Repository navigation
fix!: reject try statements without handlers, stray commas, and function or class expression statements - #164
Merged
Conversation
…ion or class expression statements Require a catch or finally clause in try statements, allow no elision in object literals, object patterns, and call or new arguments, and lex `function` and `class` at a declaration start as declaration keywords so that an expression statement cannot begin with them. BREAKING CHANGE: inputs that ECMAScript rejects in these ways now produce trees with ERROR or MISSING nodes, and error recovery around them changes. Node types, field names, and trees of valid programs are unchanged. Co-authored-by: WillBooster (Claude Code) <agent@willbooster.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
The shipped parsers (both
tree-sitter-javascript.wasmandtree-sitter-javascript_script.wasm) parsed several inputs that ECMAScript rejects as SyntaxErrors into trees with noERRORorMISSINGnode, for exampletry { a(); },({ a: 1,, b: 2 }),f(,),const { a,, b } = o;,function () { return 1; },class {}, andfunction f() {}.x;as statements. The fork's maintenance scope requires invalid input to yieldERRORorMISSINGnodes.Requirements
try { a(); }(a try statement with neither catch nor finally) yields anERRORorMISSINGnode in the Module and Script grammars.ERRORorMISSINGnode in object literals (({ a: 1,, b: 2 }),({,})), call and new arguments (f(1,, 2),f(,),new T(,)), and object patterns (const { a,, b } = o;).[1,,2],const [a,,b] = x;), and a single trailing comma stays valid where ECMAScript allows it (f(1,),({ a: 1, }),const { a, } = o;).functionorclass(e.g.function () { return 1; },class {}) yields anERRORorMISSINGnode.(function () {})(),(class {}),x = function () {}, and, in the Module grammar,export default function () {}/export default class {}keep parsing to the same correct trees.functionandclassis enforced by the external scanner, because an LR grammar can exclude these keywords from the start of an expression statement only by duplicating the whole left spine of the expression grammar.Customer Summary
Parsing these invalid programs now reports syntax errors instead of returning error-free trees. Valid programs parse exactly as before.
Technical Summary
try_statement(grammar.js) requires acatch_clause, afinally_clause, or both.objectandargumentsuse a newtrailingCommaSephelper (one or more items, then an optional trailing comma) instead ofcommaSep(optional(...)).object_patternlists properties separated by single commas, optionally followed by a comma and a rest pattern.arrayandarray_patternare unchanged._declaration_function_keywordand_declaration_class_keyword, are aliased tofunctionandclassin the named branches offunction_declaration,generator_function_declaration(including their async forms), andclass_declaration. The scanner (scan_declaration_keywordinsrc/scanner.c) emits them whenever a declaration may start there and the word is exactlyfunctionorclass, so the parser cannot take the function or class expression path at a statement start. This rejects nameless functions and classes,function f() {}.x;, and@dec class {}as statements, while keepingexport function,export default function () {}, and labeled orif-body function declarations (Annex B) valid. The scanner skips this during error recovery.Node types and field names are unchanged (
src/node-types.jsondid not change). The commit is marked breaking because error-recovery trees change for the inputs above.What to check hardest: the scanner reaches
scan_declaration_keywordthroughscan_contextual_keyword, i.e. after the existing automatic-semicolon and statement-boundary decisions. The differential test against Acorn/Babel (200,000 generated programs, 0 mismatches) and the corpus support that no valid declaration or expression start is affected.Testing
bun run verify: passed.bun run test(whole node and browser suite, including corpus, Wasm corpus, grammar extensions, examples, performance, and the Acorn/Babel differential test): 34 files, 177 tests passed.test/unit/syntaxRestrictions.test.ts: for both grammars, the invalid inputs above have errors, and their valid look-alikes parse to the expected trees.Function callsto wrap its function expression in parentheses (as a statement,function(x, y) {}(a, b);is invalid), andExtra complex literals in expressionsto its new error-recovery tree. Updatedtest/unit/defaultExports.test.ts, which expected nameless declarations withoutexport defaultto parse without errors.Notes
export { , }andimport { , } from "m"still parse without errors (export_clauseandnamed_importsusecommaSep(...), optional(',')). This PR leaves them unchanged.src/scanner.cfunctions that this PR does not modify (scan_automatic_semicolon,scan_after_line_break,follows_yield_operand,scan_resource_binding,tree_sitter_javascript_external_scanner_scan,scan_let,scan_default_trivia). The file-levelfileNcssallowance is raised from 976 to 992 for the added helper.