Skip to content

Commit

Permalink
feat(parser): better errors for reserved words used as identifier nam…
Browse files Browse the repository at this point in the history
…es (#6478)

## What This PR Does
Provide better error messages when a reserved word is used as a `BindingIdentifier`
```ts
var const = 1;
export enum const {}
const if = 1;
```
  • Loading branch information
DonIsaac committed Oct 13, 2024
1 parent 66dccc0 commit 8ea6b72
Show file tree
Hide file tree
Showing 5 changed files with 171 additions and 157 deletions.
8 changes: 8 additions & 0 deletions crates/oxc_parser/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,14 @@ pub fn identifier_generator(x0: &str, span1: Span) -> OxcDiagnostic {
.with_label(span1)
}

#[cold]
pub fn identifier_reserved_word(span: Span, reserved: &str) -> OxcDiagnostic {
OxcDiagnostic::error(format!(
"Identifier expected. '{reserved}' is a reserved word that cannot be used here."
))
.with_label(span)
}

#[cold]
pub fn constructor_generator(span: Span) -> OxcDiagnostic {
OxcDiagnostic::error("Constructor can't be a generator").with_label(span)
Expand Down
10 changes: 8 additions & 2 deletions crates/oxc_parser/src/js/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,14 @@ impl<'a> ParserImpl<'a> {

/// `BindingIdentifier` : Identifier
pub(crate) fn parse_binding_identifier(&mut self) -> Result<BindingIdentifier<'a>> {
if !self.cur_kind().is_binding_identifier() {
return Err(self.unexpected());
let cur = self.cur_kind();
if !cur.is_binding_identifier() {
let err = if cur.is_reserved_keyword() {
diagnostics::identifier_reserved_word(self.cur_token().span(), cur.to_str())
} else {
self.unexpected()
};
return Err(err);
}
let (span, name) = self.parse_identifier_kind(Kind::Ident);
self.check_identifier(span, &name);
Expand Down
46 changes: 23 additions & 23 deletions tasks/coverage/snapshots/parser_babel.snap
Original file line number Diff line number Diff line change
Expand Up @@ -1316,7 +1316,7 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
· ──
╰────

× Unexpected token
× Identifier expected. 'if' is a reserved word that cannot be used here.
╭─[babel/packages/babel-parser/test/fixtures/core/uncategorised/382/input.js:1:5]
1 │ var if = 42
· ──
Expand Down Expand Up @@ -1411,25 +1411,25 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
· ───────
╰────

× Unexpected token
× Identifier expected. 'if' is a reserved word that cannot be used here.
╭─[babel/packages/babel-parser/test/fixtures/core/uncategorised/397/input.js:1:12]
1 │ function t(if) { }
· ──
╰────

× Unexpected token
× Identifier expected. 'true' is a reserved word that cannot be used here.
╭─[babel/packages/babel-parser/test/fixtures/core/uncategorised/398/input.js:1:12]
1 │ function t(true) { }
· ────
╰────

× Unexpected token
× Identifier expected. 'false' is a reserved word that cannot be used here.
╭─[babel/packages/babel-parser/test/fixtures/core/uncategorised/399/input.js:1:12]
1 │ function t(false) { }
· ─────
╰────

× Unexpected token
× Identifier expected. 'null' is a reserved word that cannot be used here.
╭─[babel/packages/babel-parser/test/fixtures/core/uncategorised/400/input.js:1:12]
1 │ function t(null) { }
· ────
Expand Down Expand Up @@ -2192,7 +2192,7 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
╰────
help: for octal literals use the '0o' prefix instead

× Unexpected token
× Identifier expected. 'this' is a reserved word that cannot be used here.
╭─[babel/packages/babel-parser/test/fixtures/core/uncategorised/523/input.js:1:5]
1 │ var this = 10;
· ────
Expand Down Expand Up @@ -2669,14 +2669,14 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
╰────
help: In non-strict mode code, functions can only be declared at top level, inside a block, or as the body of an if statement

× Unexpected token
× Identifier expected. 'const' is a reserved word that cannot be used here.
╭─[babel/packages/babel-parser/test/fixtures/es2015/identifiers/invalid-escape-seq-const/input.js:1:5]
1 │ var co\u{6e}st = 123;
· ──────────
2 │
╰────

× Unexpected token
× Identifier expected. 'export' is a reserved word that cannot be used here.
╭─[babel/packages/babel-parser/test/fixtures/es2015/identifiers/invalid-escape-seq-export/input.js:1:5]
1 │ var expor\u{74} = 123;
· ───────────
Expand All @@ -2689,7 +2689,7 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
· ────────────
╰────

× Unexpected token
× Identifier expected. 'import' is a reserved word that cannot be used here.
╭─[babel/packages/babel-parser/test/fixtures/es2015/identifiers/invalid-escape-seq-import/input.js:1:5]
1 │ var \u{69}\u{6d}\u{70}\u{6f}\u{72}\u{74} = 123;
· ────────────────────────────────────
Expand Down Expand Up @@ -3331,13 +3331,13 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
2 │ return `<div class="bar">Hola</div>`;
╰────

× Unexpected token
× Identifier expected. 'debugger' is a reserved word that cannot be used here.
╭─[babel/packages/babel-parser/test/fixtures/es2015/modules/import-invalid-keyword/input.js:1:10]
1 │ import { debugger } from "foo";
· ────────
╰────

× Unexpected token
× Identifier expected. 'typeof' is a reserved word that cannot be used here.
╭─[babel/packages/babel-parser/test/fixtures/es2015/modules/import-invalid-keyword-typeof/input.js:1:10]
1 │ import { typeof } from "foo";
· ──────
Expand Down Expand Up @@ -3734,25 +3734,25 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
· ╰── `t` has already been declared here
╰────

× Unexpected token
× Identifier expected. 'super' is a reserved word that cannot be used here.
╭─[babel/packages/babel-parser/test/fixtures/es2015/uncategorised/229/input.js:1:5]
1 │ var super
· ─────
╰────

× Unexpected token
× Identifier expected. 'default' is a reserved word that cannot be used here.
╭─[babel/packages/babel-parser/test/fixtures/es2015/uncategorised/230/input.js:1:5]
1 │ var default
· ───────
╰────

× Unexpected token
× Identifier expected. 'default' is a reserved word that cannot be used here.
╭─[babel/packages/babel-parser/test/fixtures/es2015/uncategorised/231/input.js:1:5]
1 │ let default
· ───────
╰────

× Unexpected token
× Identifier expected. 'default' is a reserved word that cannot be used here.
╭─[babel/packages/babel-parser/test/fixtures/es2015/uncategorised/232/input.js:1:7]
1 │ const default = 2
· ───────
Expand Down Expand Up @@ -4244,13 +4244,13 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
╰────
help: Try insert a semicolon here

× Unexpected token
× Identifier expected. 'enum' is a reserved word that cannot be used here.
╭─[babel/packages/babel-parser/test/fixtures/es2015/uncategorised/370/input.js:1:7]
1 │ const enum = foo();
· ────
╰────

× Unexpected token
× Identifier expected. 'enum' is a reserved word that cannot be used here.
╭─[babel/packages/babel-parser/test/fixtures/es2015/uncategorised/371/input.js:1:7]
1 │ const enum = foo();
· ────
Expand Down Expand Up @@ -8378,7 +8378,7 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
· ──
╰────

× Unexpected token
× Identifier expected. 'if' is a reserved word that cannot be used here.
╭─[babel/packages/babel-parser/test/fixtures/esprima/invalid-syntax/migrated_0064/input.js:1:5]
1 │ var if = 42
· ──
Expand Down Expand Up @@ -9636,7 +9636,7 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
╰────
help: Try insert a semicolon here

× Unexpected token
× Identifier expected. 'enum' is a reserved word that cannot be used here.
╭─[babel/packages/babel-parser/test/fixtures/esprima/invalid-syntax/migrated_0277/input.js:1:12]
1 │ class A {a(enum){}}
· ────
Expand Down Expand Up @@ -10812,7 +10812,7 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
╰────
help: Try insert a semicolon here

× Unexpected token
× Identifier expected. 'if' is a reserved word that cannot be used here.
╭─[babel/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/export-invalid-type-only-keyword/input.ts:1:7]
1 │ const if = {};
· ──
Expand All @@ -10831,19 +10831,19 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
· ─────────
╰────

× Unexpected token
× Identifier expected. 'if' is a reserved word that cannot be used here.
╭─[babel/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-named-type-as-keyword/input.ts:1:18]
1 │ import { type as if } from "mod";
· ──
╰────

× Unexpected token
× Identifier expected. 'if' is a reserved word that cannot be used here.
╭─[babel/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-type-named-keywords/input.ts:1:15]
1 │ import { type if } from "./mod.js";
· ──
╰────

× Unexpected token
× Identifier expected. 'if' is a reserved word that cannot be used here.
╭─[babel/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-type-only-as-as-keyword/input.ts:1:21]
1 │ import { type as as if } from "mod";
· ──
Expand Down
Loading

0 comments on commit 8ea6b72

Please sign in to comment.