Skip to content

Commit 37fc78f

Browse files
committed
gen-tm: tolerate zero-width guards before a declaration name; reject reserved type-alias names
gen-tm's declaration detector (keyword + Ident -> entity.name.*) only skipped non-keyword literals between the keyword and the name, so a zero-width guard like `['type', not(reserved), Ident]` made it bail and the type name lost its entity.name.type scope (~21 type-name/typeparam tokens mis-scoped, 99.3%->95.8%). Skip `not`/`sameLine` markers too — they consume no token and aren't part of the `keyword name` highlight pattern. With that, the type-alias name can be reserved-guarded without regressing highlighting: `type void = X` is now rejected (TS2457) while `type string = X`, `type Foo<T> = T`, `class type {}` still parse. (tmLanguage output is byte-identical to before — the gen-tm fix exactly offsets the guard.) FP 80->79, FN stays 0 (3376/3376), TP unchanged, highlighter 99.3% unchanged. sanity 15/15, agnostic 5/5, refactor-guard 112/112, smoke pass.
1 parent 54bac8a commit 37fc78f

4 files changed

Lines changed: 9 additions & 3 deletions

File tree

examples/lezer/highlight.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import { styleTags, tags as t } from "@lezer/highlight";
33
// Contextual definition-name positions (give these their own node names):
44
// after "class" → name node should be styled t.typeName (entity.name.type)
55
// after "interface" → name node should be styled t.typeName (entity.name.type)
6-
// after "type" → name node should be styled t.typeName (entity.name.type)
76
// after "enum" → name node should be styled t.typeName (entity.name.type)
87
// after "namespace" → name node should be styled t.typeName (entity.name.type)
98

examples/tree-sitter/grammar.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ module.exports = grammar({
140140

141141
type_param: $ => choice(seq(repeat1(choice("const", "in", "out", "public", "private", "protected", "readonly")), $.ident, optional(seq("extends", $.type)), optional(seq("=", $.type))), seq(choice("const", "in", "out", "public", "private", "protected", "readonly"), choice($.ident, "in", "out"), optional(seq("extends", $.type)), optional(seq("=", $.type))), seq(choice($.ident, "in", "out"), optional(seq("extends", $.type)), optional(seq("=", $.type)))),
142142

143-
decl: $ => choice(seq(optional("async"), "function", optional("*"), field('name', $.ident), optional($.type_params), "(", optional(seq($.param, repeat(seq(",", $.param)), optional(","))), ")", optional(seq(":", $.type)), choice($.block, optional(";"))), seq("interface", field('name', $.ident), optional($.type_params), optional(seq("extends", optional(seq($.type, repeat(seq(",", $.type)), optional(","))))), "{", repeat(seq($.interface_member, optional(choice(";", ",")))), "}"), seq("type", field('name', $.ident), optional($.type_params), "=", $.type, optional(";")), seq(repeat($.decorator_expr), optional("abstract"), "class", field('name', $.ident), optional($.type_params), optional(seq("extends", $.class_heritage)), optional(seq("implements", optional(seq($.type, repeat(seq(",", $.type)), optional(","))))), "{", repeat($.class_member), "}"), seq("enum", field('name', $.ident), "{", optional(seq($.enum_member, repeat(seq(",", $.enum_member)), optional(","))), "}"), seq("declare", "function", optional("*"), field('name', $.ident), optional($.type_params), "(", optional(seq($.param, repeat(seq(",", $.param)), optional(","))), ")", optional(seq(":", $.type)), optional(";")), seq("declare", choice($.decl, $.stmt)), seq("namespace", field('name', $.ident), "{", repeat($.stmt), "}"), seq("module", choice($.string, $.ident), "{", repeat($.stmt), "}"), seq("export", choice($.decl, $.stmt)), seq("export", "default", choice(seq(optional("async"), "function", optional("*"), optional(field('name', $.ident)), optional($.type_params), "(", optional(seq($.param, repeat(seq(",", $.param)), optional(","))), ")", optional(seq(":", $.type)), choice($.block, optional(";"))), seq("abstract", "class", field('name', $.ident), optional($.type_params), optional(seq("extends", $.class_heritage)), optional(seq("implements", optional(seq($.type, repeat(seq(",", $.type)), optional(","))))), "{", repeat($.class_member), "}"), seq("abstract", "class", optional($.type_params), optional(seq("extends", $.class_heritage)), optional(seq("implements", optional(seq($.type, repeat(seq(",", $.type)), optional(","))))), "{", repeat($.class_member), "}"), seq($.expr, optional(";")))), seq("export", "*", choice(seq("from", $.string, optional(";")), seq("as", $.ident, "from", $.string, optional(";")))), seq("export", "{", optional(seq($.import_specifier, repeat(seq(",", $.import_specifier)), optional(","))), "}", optional(seq("from", $.string)), optional(";")), seq("export", "=", $.expr, optional(";")), seq("export", "type", "{", optional(seq($.import_specifier, repeat(seq(",", $.import_specifier)), optional(","))), "}", optional(seq("from", $.string)), optional(";")), seq("const", "enum", field('name', $.ident), "{", optional(seq($.enum_member, repeat(seq(",", $.enum_member)), optional(","))), "}"), seq("import", choice(seq($.import_clause, "from", $.string, optional(";")), seq("type", $.import_clause, "from", $.string, optional(";")), seq($.ident, "=", $.expr, optional(";")), seq($.string, optional(";")))), seq(repeat($.decorator_expr), "export", choice($.decl, $.stmt))),
143+
decl: $ => choice(seq(optional("async"), "function", optional("*"), field('name', $.ident), optional($.type_params), "(", optional(seq($.param, repeat(seq(",", $.param)), optional(","))), ")", optional(seq(":", $.type)), choice($.block, optional(";"))), seq("interface", field('name', $.ident), optional($.type_params), optional(seq("extends", optional(seq($.type, repeat(seq(",", $.type)), optional(","))))), "{", repeat(seq($.interface_member, optional(choice(";", ",")))), "}"), seq("type", $.ident, optional($.type_params), "=", $.type, optional(";")), seq(repeat($.decorator_expr), optional("abstract"), "class", field('name', $.ident), optional($.type_params), optional(seq("extends", $.class_heritage)), optional(seq("implements", optional(seq($.type, repeat(seq(",", $.type)), optional(","))))), "{", repeat($.class_member), "}"), seq("enum", field('name', $.ident), "{", optional(seq($.enum_member, repeat(seq(",", $.enum_member)), optional(","))), "}"), seq("declare", "function", optional("*"), field('name', $.ident), optional($.type_params), "(", optional(seq($.param, repeat(seq(",", $.param)), optional(","))), ")", optional(seq(":", $.type)), optional(";")), seq("declare", choice($.decl, $.stmt)), seq("namespace", field('name', $.ident), "{", repeat($.stmt), "}"), seq("module", choice($.string, $.ident), "{", repeat($.stmt), "}"), seq("export", choice($.decl, $.stmt)), seq("export", "default", choice(seq(optional("async"), "function", optional("*"), optional(field('name', $.ident)), optional($.type_params), "(", optional(seq($.param, repeat(seq(",", $.param)), optional(","))), ")", optional(seq(":", $.type)), choice($.block, optional(";"))), seq("abstract", "class", field('name', $.ident), optional($.type_params), optional(seq("extends", $.class_heritage)), optional(seq("implements", optional(seq($.type, repeat(seq(",", $.type)), optional(","))))), "{", repeat($.class_member), "}"), seq("abstract", "class", optional($.type_params), optional(seq("extends", $.class_heritage)), optional(seq("implements", optional(seq($.type, repeat(seq(",", $.type)), optional(","))))), "{", repeat($.class_member), "}"), seq($.expr, optional(";")))), seq("export", "*", choice(seq("from", $.string, optional(";")), seq("as", $.ident, "from", $.string, optional(";")))), seq("export", "{", optional(seq($.import_specifier, repeat(seq(",", $.import_specifier)), optional(","))), "}", optional(seq("from", $.string)), optional(";")), seq("export", "=", $.expr, optional(";")), seq("export", "type", "{", optional(seq($.import_specifier, repeat(seq(",", $.import_specifier)), optional(","))), "}", optional(seq("from", $.string)), optional(";")), seq("const", "enum", field('name', $.ident), "{", optional(seq($.enum_member, repeat(seq(",", $.enum_member)), optional(","))), "}"), seq("import", choice(seq($.import_clause, "from", $.string, optional(";")), seq("type", $.import_clause, "from", $.string, optional(";")), seq($.ident, "=", $.expr, optional(";")), seq($.string, optional(";")))), seq(repeat($.decorator_expr), "export", choice($.decl, $.stmt))),
144144

145145
interface_member: $ => choice(seq(optional("new"), optional($.type_params), "(", optional(seq($.param, repeat(seq(",", $.param)), optional(","))), ")", optional(seq(":", $.type))), seq(choice("get", "set"), $.member_name, "(", optional(seq($.param, repeat(seq(",", $.param)), optional(","))), ")", optional(seq(":", $.type))), seq(optional("static"), optional(choice("+", "-")), optional("readonly"), "[", $.ident, "in", $.type, optional(seq("as", $.type)), "]", optional(choice("+", "-")), optional("?"), ":", $.type), seq("readonly", $.member_name, optional("?"), ":", $.type), seq($.member_name, optional("?"), choice(seq(optional($.type_params), "(", optional(seq($.param, repeat(seq(",", $.param)), optional(","))), ")", optional(seq(":", $.type))), optional(seq(":", $.type)))), seq(optional("static"), optional("readonly"), "[", optional(seq($.param, repeat(seq(",", $.param)), optional(","))), "]", optional(seq(":", $.type)))),
146146

examples/typescript.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -496,7 +496,7 @@ const Decl = rule($ => [
496496
// as a declaration + arrow rather than longest-matching `function f(){}()` (IIFE).
497497
[opt('async'), 'function', opt('*'), Ident, opt(TypeParams), '(', sep(Param, ','), ')', opt(':', Type), alt(Block, opt(';'))],
498498
['interface', Ident, opt(TypeParams), opt('extends', sep(Type, ',')), '{', many(InterfaceMember, opt(alt(';', ','))), '}'],
499-
['type', Ident, opt(TypeParams), '=', Type, opt(';')],
499+
['type', notReserved, Ident, opt(TypeParams), '=', Type, opt(';')], // type-alias name can't be a reserved word (`type void = …`); contextual type keywords (`string`/`any`/…) stay valid
500500
// class decl: optional decorators + optional `abstract`. gen-tm expands the
501501
// opt()/many() to recover the `class Ident … { … }` shape for highlighting.
502502
[many(DecoratorExpr), opt('abstract'), 'class', Ident, opt(TypeParams), opt('extends', ClassHeritage), opt('implements', sep(Type, ',')), '{', many(ClassMember), '}'],

src/gen-tm.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1109,6 +1109,13 @@ function detectDeclarations(grammar: CstGrammar, tokenNames: Set<string>): DeclI
11091109
nameIdx++;
11101110
continue;
11111111
}
1112+
// Zero-width guards (`not(...)` / `sameLine`) consume no token, so they can sit
1113+
// between the keyword and the name (e.g. `'type' not(reserved) Ident`) without
1114+
// changing the `keyword name` highlight pattern — skip past them.
1115+
if (item.type === 'not' || item.type === 'sameLine') {
1116+
nameIdx++;
1117+
continue;
1118+
}
11121119
return; // unexpected item type — not a declaration pattern
11131120
}
11141121
if (nameIdx >= items.length) return;

0 commit comments

Comments
 (0)