Skip to content

Commit

Permalink
[JavaScript] Fix anonymous class instantiation
Browse files Browse the repository at this point in the history
Fixes #4125

This commit adds "future reserved words" from [1] to prevent them being
scoped as normal identifiers which fixes anonymous class instantiations
in TypeScript.

They are also marked reserved in JavaScript as specified as such by [1],
and `implements` is scoped illegal in class declarations.

[1]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#keywords
  • Loading branch information
deathaxe committed Dec 31, 2024
1 parent 737d31d commit 2722de8
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 16 deletions.
62 changes: 57 additions & 5 deletions JavaScript/JavaScript.sublime-syntax
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,56 @@ variables:
# https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar
reserved_word: |-
(?x:
break|case|catch|class|const|continue|debugger|default|delete|do|else|
export|extends|finally|for|function|if|import|in|instanceof|new|return|
super|switch|this|throw|try|typeof|var|void|while|with|yield|
enum|
null|true|false
# standard mode
break
| case
| catch
| class
| const
| continue
| debugger
| default
| delete
| do
| else
| export
| extends
| false
| finally
| for
| function
| if
| import
| in
| instanceof
| new
| null
| return
| super
| switch
| this
| throw
| true
| try
| typeof
| var
| void
| while
| with
# strict mode
# | let # currently allowed as variable
| static
| yield
# async code
| await
# future reserved words
| enum
| implements
| interface
| package
| private
| protected
| public
){{identifier_break}}
non_reserved_identifier: (?:(?!{{reserved_word}}){{identifier_name}})
Expand Down Expand Up @@ -1563,6 +1608,7 @@ contexts:
set:
- class-meta
- class-body
- class-implements
- class-extends
- class-name

Expand Down Expand Up @@ -1622,6 +1668,12 @@ contexts:
- inherited-class-expression-begin
- include: else-pop

class-implements:
- match: implements{{identifier_break}}
scope: invalid.illegal.unexpected-token.js
pop: 1
- include: else-pop

inherited-class-name:
- match: '{{non_reserved_identifier}}{{left_expression_end_lookahead}}'
scope: entity.other.inherited-class.js
Expand Down
12 changes: 1 addition & 11 deletions JavaScript/TypeScript.sublime-syntax
Original file line number Diff line number Diff line change
Expand Up @@ -263,17 +263,7 @@ contexts:
- ts-abstract-class
- expression-statement

class:
- match: class{{identifier_break}}
scope: keyword.declaration.class.js
set:
- class-meta
- class-body
- ts-class-implements
- class-extends
- class-name

ts-class-implements:
class-implements:
- match: implements{{identifier_break}}
scope: storage.modifier.implements.js
set: ts-inherited-class-expression-list
Expand Down
10 changes: 10 additions & 0 deletions JavaScript/tests/syntax_test_js.js
Original file line number Diff line number Diff line change
Expand Up @@ -1111,6 +1111,16 @@ var instance = new Constructor(param1, param2)
var obj = new function() {}();
// ^^^^^^^^ keyword.declaration.function
var obj = new class extends Foo {}();
// ^^^^^ keyword.declaration.class.js
// ^^^^^^^ storage.modifier.extends.js
// ^^^ entity.other.inherited-class.js
var obj = new class implements IFoo {}();
// ^^^^^ keyword.declaration.class.js
// ^^^^^^^^^^ invalid.illegal.unexpected-token.js
// ^^^^ - entity.other
var obj2 = new class Foo{}();
// ^^^^^ keyword.declaration.class
Expand Down
49 changes: 49 additions & 0 deletions JavaScript/tests/syntax_test_typescript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1680,3 +1680,52 @@ type T = Foo | ... 100 more ... | Bar;
// ^^^^^^^ meta.function-call
// ^^ keyword.operator.type
// ^^ meta.function-call.arguments

// instantiations

new class extends Bar {};
// ^^^ keyword.operator.word.new.js
// ^^^^^^^^^^^^^^^^^^^^^ meta.function-call.constructor.js
// ^^^^^^^^^^^^^^^^^^^^ meta.class.js
// ^^^^^ keyword.declaration.class.js
// ^^^^^^^ storage.modifier.extends.js
// ^^^ entity.other.inherited-class.js
// ^^ meta.block.js
// ^ punctuation.section.block.begin.js
// ^ punctuation.section.block.end.js

new class Foo extends Bar {};
// ^^^ keyword.operator.word.new.js
// ^^^^^^^^^^^^^^^^^^^^^^^^^ meta.function-call.constructor.js
// ^^^^^^^^^^^^^^^^^^^^^^^^ meta.class.js
// ^^^^^ keyword.declaration.class.js
// ^^^ entity.name.class.js
// ^^^^^^^ storage.modifier.extends.js
// ^^^ entity.other.inherited-class.js
// ^^ meta.block.js
// ^ punctuation.section.block.begin.js
// ^ punctuation.section.block.end.js

new class implements IBar {};
// ^^^ keyword.operator.word.new.js
// ^^^^^^^^^^^^^^^^^^^^^^^^^ meta.function-call.constructor.js
// ^^^^^^^^^^^^^^^^^^^^^^^^ meta.class.js
// ^^^^^ keyword.declaration.class.js
// ^^^^^^^^^^ storage.modifier.implements.js
// ^^^^ entity.other.inherited-class.js
// ^^ meta.block.js
// ^ punctuation.section.block.begin.js
// ^ punctuation.section.block.end.js

new class Foo implements IBar {};
// ^^^ keyword.operator.word.new.js
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.function-call.constructor.js
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.class.js
// ^^^^^ keyword.declaration.class.js
// ^^^ entity.name.class.js
// ^^^^^^^^^^ storage.modifier.implements.js
// ^^^^ entity.other.inherited-class.js
// ^^ meta.block.js
// ^ punctuation.section.block.begin.js
// ^ punctuation.section.block.end.js
// ^ punctuation.terminator.statement.js

0 comments on commit 2722de8

Please sign in to comment.