+**FIXED — `as`/`keyof`/`is`/`infer`/`satisfies`/`asserts` (the contextual-operator class).** These are keywords in operator/type position but valid identifiers elsewhere (`const as = 1`); the flat keyword match over-scoped the identifier uses. Unlike `from` (one mechanism), these were scoped by **four at once** — the flat keyword group, the per-keyword `keyword.operator.expression.as` begin rule, the `type-inner` injection, and the structural type rules — which is why a first naive attempt (de-keywording only the flat group) failed: it didn't fix `const as` AND regressed `keyof`/`is`/`infer` in type position to `entity.name.type`. The landed fix reconciles all four: the keyword.operator.expression group is split into reserved operators (`typeof`/`new`/`void`/`delete`/`instanceof` — unconditional) vs contextual operators (a positional guard `(?=\s+OPERAND|\s*$)` — keyword iff followed by whitespace+an operand, or at EOL for multiline casts), the per-keyword begin rules get the same guard, and type-inner re-injects both so type-position uses keep keyword scope. The contextual set is derived agnostically from the grammar's `not(...)` reserved guards (no hardcoded words). Now `const as/keyof/is = 1` → `variable`, `x as T`/`as (A|B)`/`import * as ns`/`keyof T`/`p is T`/`infer U` → keyword — matching official. `of` as an identifier (`const of = 1`) — **the official grammar mis-scopes it too**, so a both-wrong case, not a gap.
0 commit comments