mirror of
https://github.com/tree-sitter/tree-sitter.git
synced 2026-09-10 07:26:23 -04:00
fix(generate): include aliased supertypes in node-types.json
Aliasing a supertype makes the aliased occurrence appear as a regular node in the syntax tree. For example, ``` alias($.expression, $.expression_target) ``` can produce: ``` (expression_target (identifier)) ``` Previously, node-types generation skipped the source supertype entirely. It could reference `expression_target` as a field or child type without emitting a corresponding top-level entry for it. Continue emitting the canonical `expression` entry with its `subtypes`, but pass aliased appearances through regular node generation so their children and fields are recorded and merged normally.
This commit is contained in:
parent
54a46eb49b
commit
7bd12334c4
|
|
@ -829,8 +829,8 @@ fn build_supertype_entries(
|
|||
subtype_map
|
||||
}
|
||||
|
||||
/// Add JSON entries for visible non-supertype rules, merged into every name
|
||||
/// they can appear under.
|
||||
/// Add JSON entries for visible non-supertype rules and aliased supertypes (treated
|
||||
/// as regular, concrete nodes), merged into every regular name they can appear under.
|
||||
#[cfg(feature = "load")]
|
||||
#[expect(
|
||||
clippy::too_many_arguments,
|
||||
|
|
@ -849,27 +849,29 @@ fn build_regular_entries(
|
|||
let empty = BTreeSet::new();
|
||||
for (i, info) in variable_info.iter().enumerate() {
|
||||
let symbol = Symbol::non_terminal(i);
|
||||
if syntax_grammar.supertype_symbols.contains(&symbol)
|
||||
|| syntax_grammar.variables_to_inline.contains(&symbol)
|
||||
{
|
||||
// Inlined symbols don't have their own node-types entries.
|
||||
if syntax_grammar.variables_to_inline.contains(&symbol) {
|
||||
continue;
|
||||
}
|
||||
let is_supertype = syntax_grammar.supertype_symbols.contains(&symbol);
|
||||
let variable = &syntax_grammar.variables[i];
|
||||
|
||||
// If a rule is aliased under multiple names, then its information
|
||||
// contributes to multiple entries in the final JSON.
|
||||
for alias in aliases_by_symbol.get(&symbol).unwrap_or(&empty) {
|
||||
let kind;
|
||||
let is_named;
|
||||
if let Some(alias) = alias {
|
||||
kind = &alias.value;
|
||||
is_named = alias.is_named;
|
||||
} else if variable.kind.is_visible() {
|
||||
kind = &variable.name;
|
||||
is_named = variable.kind == VariableType::Named;
|
||||
} else {
|
||||
// The canonical supertype is emitted separately with its subtypes.
|
||||
// An alias of that supertype is treated as a regular, visible node
|
||||
// and handled here.
|
||||
if is_supertype && alias.is_none() {
|
||||
continue;
|
||||
}
|
||||
let (kind, is_named) = if let Some(alias) = alias {
|
||||
(&alias.value, alias.is_named)
|
||||
} else if variable.kind.is_visible() {
|
||||
(&variable.name, variable.kind == VariableType::Named)
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
|
||||
// There may already be an entry with this name, because multiple
|
||||
// rules may be aliased with the same name.
|
||||
|
|
@ -1774,6 +1776,52 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_node_types_with_aliased_supertype() {
|
||||
let mut pool = RulePool::default();
|
||||
let expression = named(&mut pool, "_expression");
|
||||
let document = alias(&mut pool, expression, "expression_target", true);
|
||||
let expression = named(&mut pool, "identifier");
|
||||
let identifier = pattern(&mut pool, "[a-z]+");
|
||||
let expression_name = pool.intern("_expression");
|
||||
let expression_target_name = pool.intern("expression_target");
|
||||
let identifier_name = pool.intern("identifier");
|
||||
let node_types = get_node_types(InputGrammar {
|
||||
supertype_names: vec![expression_name],
|
||||
variables: vec![
|
||||
Variable {
|
||||
name: pool.intern("document"),
|
||||
root: document,
|
||||
},
|
||||
Variable {
|
||||
name: expression_name,
|
||||
root: expression,
|
||||
},
|
||||
Variable {
|
||||
name: identifier_name,
|
||||
root: identifier,
|
||||
},
|
||||
],
|
||||
pool,
|
||||
..Default::default()
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let alias = node_types
|
||||
.iter()
|
||||
.find(|node_type| node_type.kind == expression_target_name)
|
||||
.expect("the aliased supertype should have its own node-types entry");
|
||||
assert!(alias.named);
|
||||
assert!(alias.subtypes.is_none());
|
||||
assert_eq!(
|
||||
alias.children.as_ref().unwrap().types,
|
||||
[NodeTypeRef {
|
||||
kind: identifier_name,
|
||||
named: true,
|
||||
}]
|
||||
);
|
||||
}
|
||||
|
||||
/// A supertype whose only child is a hidden external token
|
||||
/// xgust not cause generation to panic. The subtype map must
|
||||
/// skip entries with empty subtypes to avoid a lookup failure
|
||||
|
|
|
|||
Loading…
Reference in a new issue