feat(linter): check Identifier in javascript

This commit is contained in:
Boshen 2023-03-09 14:09:40 +08:00
parent 4a582f0487
commit 4ea7ac373d
8 changed files with 2434 additions and 303 deletions

64
Cargo.lock generated
View file

@ -932,6 +932,7 @@ dependencies = [
"oxc_macros",
"oxc_parser",
"oxc_semantic",
"phf",
"serde_json",
]
@ -1048,6 +1049,48 @@ dependencies = [
"sha2",
]
[[package]]
name = "phf"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "928c6535de93548188ef63bb7c4036bd415cd8f36ad25af44b9789b2ee72a48c"
dependencies = [
"phf_macros",
"phf_shared",
]
[[package]]
name = "phf_generator"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1181c94580fa345f50f19d738aaa39c0ed30a600d95cb2d3e23f94266f14fbf"
dependencies = [
"phf_shared",
"rand",
]
[[package]]
name = "phf_macros"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92aacdc5f16768709a569e913f7451034034178b05bdc8acda226659a3dccc66"
dependencies = [
"phf_generator",
"phf_shared",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "phf_shared"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1fb5f6f826b772a8d4c0394209441e7d37cbbb967ae9c7e0e8134365c9ee676"
dependencies = [
"siphasher",
]
[[package]]
name = "pico-args"
version = "0.5.0"
@ -1072,6 +1115,21 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
[[package]]
name = "rayon"
version = "1.7.0"
@ -1284,6 +1342,12 @@ version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "420acb44afdae038210c99e69aae24109f32f15500aa708e81d46c9f29d55fcf"
[[package]]
name = "siphasher"
version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de"
[[package]]
name = "smawk"
version = "0.3.1"

View file

@ -18,6 +18,7 @@ oxc_semantic = { path = "../oxc_semantic" }
lazy_static = { workspace = true }
serde_json = { workspace = true }
indextree = { workspace = true }
phf = { version = "0.11", features = ["macros"] }
[dev_dependencies]
oxc_allocator = { path = "../oxc_allocator" }

View file

@ -0,0 +1,13 @@
use phf::{phf_set, Set};
pub const STRICT_MODE_NAMES: Set<&'static str> = phf_set! {
"implements",
"interface",
"let",
"package",
"private",
"protected",
"public",
"static",
"yield",
};

View file

@ -3,6 +3,7 @@
#[cfg(test)]
mod tester;
mod ast_util;
mod context;
mod fixer;
pub mod rule;

View file

@ -5,7 +5,7 @@ use oxc_diagnostics::{
thiserror::Error,
};
use crate::{context::LintContext, rule::Rule, AstNode};
use crate::{ast_util::STRICT_MODE_NAMES, context::LintContext, rule::Rule, AstNode};
#[derive(Debug, Default, Clone)]
pub struct EarlyErrorJavaScript;
@ -14,6 +14,13 @@ impl Rule for EarlyErrorJavaScript {
#[allow(clippy::single_match)]
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
match node.get().kind() {
AstKind::BindingIdentifier(ident) => {
check_identifier(&ident.name, ident.span, node, ctx)
}
AstKind::IdentifierReference(ident) => {
check_identifier(&ident.name, ident.span, node, ctx)
}
AstKind::LabelIdentifier(ident) => check_identifier(&ident.name, ident.span, node, ctx),
AstKind::PrivateIdentifier(ident) => check_private_identifier(ident, node, ctx),
AstKind::NumberLiteral(lit) => check_number_literal(lit, node, ctx),
AstKind::StringLiteral(lit) => check_string_literal(lit, node, ctx),
@ -36,6 +43,37 @@ struct Redeclaration(
#[label("It can not be redeclared here")] Span,
);
#[derive(Debug, Error, Diagnostic)]
#[error("The keyword '{0:?}' is reserved")]
#[diagnostic()]
struct ReservedKeyword(Atom, #[label] Span);
fn check_identifier<'a>(name: &Atom, span: Span, node: &AstNode<'a>, ctx: &LintContext<'a>) {
// if span.ctx.has_ambient() {
// return None;
// }
// It is a Syntax Error if this production has an [Await] parameter.
// if *name == "await" && span.ctx.has_await() {
// return Some(Diagnostic::IdentifierAsync("await", span.range()));
// }
// It is a Syntax Error if the goal symbol of the syntactic grammar is Module and the StringValue of IdentifierName is "await".
if *name == "await" && ctx.source_type().is_module() {
return ctx.diagnostic(ReservedKeyword(name.clone(), span));
}
// It is a Syntax Error if this production has a [Yield] parameter.
// if *name == "yield" && span.ctx.has_yield() {
// return Some(Diagnostic::IdentifierGenerator("yield", span.range()));
// }
// It is a Syntax Error if this phrase is contained in strict mode code and the StringValue of IdentifierName is: "implements", "interface", "let", "package", "private", "protected", "public", "static", or "yield".
if ctx.strict_mode(node) && STRICT_MODE_NAMES.contains(name.as_str()) {
return ctx.diagnostic(ReservedKeyword(name.clone(), span));
}
}
fn check_private_identifier<'a>(
ident: &PrivateIdentifier,
node: &AstNode<'a>,

View file

@ -1,7 +1,7 @@
Babel Summary:
AST Parsed : 2056/2069 (99.37%)
Positive Passed: 2056/2069 (99.37%)
Negative Passed: 912/1502 (60.72%)
Negative Passed: 922/1502 (61.38%)
Expect Syntax Error: "annex-b/disabled/1.1-html-comments-close/input.js"
Expect Syntax Error: "annex-b/disabled/3.1-sloppy-labeled-functions-if-body/input.js"
Expect Syntax Error: "annex-b/disabled/3.1-sloppy-labeled-functions-multiple-labels/input.js"
@ -116,7 +116,6 @@ Expect Syntax Error: "core/uncategorised/520/input.js"
Expect Syntax Error: "core/uncategorised/521/input.js"
Expect Syntax Error: "core/uncategorised/522/input.js"
Expect Syntax Error: "core/uncategorised/544/input.js"
Expect Syntax Error: "core/uncategorised/545/input.js"
Expect Syntax Error: "core/uncategorised/550/input.js"
Expect Syntax Error: "core/uncategorised/552/input.js"
Expect Syntax Error: "es2015/class-methods/direct-super-in-object-method/input.js"
@ -216,11 +215,6 @@ Expect Syntax Error: "es2015/uncategorised/297/input.js"
Expect Syntax Error: "es2015/uncategorised/332/input.js"
Expect Syntax Error: "es2015/uncategorised/334/input.js"
Expect Syntax Error: "es2015/uncategorised/349/input.js"
Expect Syntax Error: "es2015/uncategorised/359/input.js"
Expect Syntax Error: "es2015/uncategorised/361/input.js"
Expect Syntax Error: "es2015/uncategorised/363/input.js"
Expect Syntax Error: "es2015/uncategorised/365/input.js"
Expect Syntax Error: "es2015/uncategorised/367/input.js"
Expect Syntax Error: "es2015/yield/function-name-function-declaration-inside-generator/input.js"
Expect Syntax Error: "es2015/yield/function-name-generator-expression/input.js"
Expect Syntax Error: "es2015/yield/function-name-strict-body/input.js"
@ -344,7 +338,6 @@ Expect Syntax Error: "esprima/es2015-yield/invalid-yield-generator-arrow-paramet
Expect Syntax Error: "esprima/es2015-yield/invalid-yield-generator-arrow-parameters/input.js"
Expect Syntax Error: "esprima/es2015-yield/invalid-yield-generator-catch/input.js"
Expect Syntax Error: "esprima/es2015-yield/invalid-yield-generator-declaration/input.js"
Expect Syntax Error: "esprima/es2015-yield/invalid-yield-generator-export-default/input.js"
Expect Syntax Error: "esprima/es2015-yield/invalid-yield-generator-expression-name/input.js"
Expect Syntax Error: "esprima/es2015-yield/invalid-yield-generator-expression-parameter/input.js"
Expect Syntax Error: "esprima/es2015-yield/invalid-yield-generator-expression-rest/input.js"
@ -454,7 +447,6 @@ Expect Syntax Error: "esprima/invalid-syntax/migrated_0249/input.js"
Expect Syntax Error: "esprima/invalid-syntax/migrated_0250/input.js"
Expect Syntax Error: "esprima/invalid-syntax/migrated_0260/input.js"
Expect Syntax Error: "esprima/invalid-syntax/migrated_0274/input.js"
Expect Syntax Error: "esprima/invalid-syntax/migrated_0278/input.js"
Expect Syntax Error: "esprima/statement-if/.migrated_0003/input.js"
Expect Syntax Error: "esprima/statement-iteration/.migrated_0021/input.js"
Expect Syntax Error: "esprima/statement-iteration/.pattern-in-for-in/input.js"
@ -487,7 +479,6 @@ Expect Syntax Error: "typescript/class/generator-method-with-modifiers/input.ts"
Expect Syntax Error: "typescript/class/index-signature-errors/input.ts"
Expect Syntax Error: "typescript/class/invalid-modifiers-order/input.ts"
Expect Syntax Error: "typescript/class/method-readonly/input.ts"
Expect Syntax Error: "typescript/class/modifier-name-parameters/input.ts"
Expect Syntax Error: "typescript/class/modifiers-incompatible/input.ts"
Expect Syntax Error: "typescript/class/modifiers-index-signatures/input.ts"
Expect Syntax Error: "typescript/class/modifiers-invalid-order/input.ts"
@ -531,7 +522,6 @@ Expect Syntax Error: "typescript/interface/get-set-invalid-reset-parameter/input
Expect Syntax Error: "typescript/interface/invalid-modifiers-method-babel-7/input.ts"
Expect Syntax Error: "typescript/interface/invalid-modifiers-method/input.ts"
Expect Syntax Error: "typescript/interface/invalid-modifiers-property/input.ts"
Expect Syntax Error: "typescript/interface/new-line/input.ts"
Expect Syntax Error: "typescript/module-namespace/top-level-await/input.ts"
Expect Syntax Error: "typescript/regression/keyword-qualified-type-disallowed/input.ts"
Expect Syntax Error: "typescript/scope/redeclaration-class-class/input.ts"
@ -1792,6 +1782,12 @@ Expect to Parse: "typescript/types/const-type-parameters/input.ts"
· ╰── const declaration need an initializer
╰────
× The keyword '"public"' is reserved
╭─[core/uncategorised/545/input.js:1:1]
1 │ const { public } = foo();
· ──────
╰────
× Unexpected token
╭─[core/uncategorised/554/input.js:1:1]
1 │ var a = 0123.;
@ -2896,6 +2892,36 @@ Expect to Parse: "typescript/types/const-type-parameters/input.ts"
· ─
╰────
× The keyword '"await"' is reserved
╭─[es2015/uncategorised/359/input.js:1:1]
1 │ const await = foo();
· ─────
╰────
× The keyword '"await"' is reserved
╭─[es2015/uncategorised/361/input.js:1:1]
1 │ const { await } = foo();
· ─────
╰────
× The keyword '"await"' is reserved
╭─[es2015/uncategorised/363/input.js:1:1]
1 │ function foo({ await }) {}
· ─────
╰────
× The keyword '"await"' is reserved
╭─[es2015/uncategorised/365/input.js:1:1]
1 │ function await() {}
· ─────
╰────
× The keyword '"await"' is reserved
╭─[es2015/uncategorised/367/input.js:1:1]
1 │ class await {}
· ─────
╰────
× Unexpected token
╭─[es2015/uncategorised/368/input.js:1:1]
1 │ enum = foo();
@ -5598,6 +5624,12 @@ Expect to Parse: "typescript/types/const-type-parameters/input.ts"
· ╰── Unterminated string
╰────
× The keyword '"yield"' is reserved
╭─[esprima/es2015-yield/invalid-yield-generator-export-default/input.js:1:1]
1 │ export default function *yield() {}
· ─────
╰────
× Unexpected token
╭─[esprima/es2015-yield/invalid-yield-generator-member-expression/input.js:1:1]
1 │ function *g() { return yield.x; }
@ -7038,6 +7070,12 @@ Expect to Parse: "typescript/types/const-type-parameters/input.ts"
· ────
╰────
× The keyword '"static"' is reserved
╭─[esprima/invalid-syntax/migrated_0278/input.js:1:1]
1 │ class A {static [static](){};}
· ──────
╰────
× A 'set' accessor function argument must not be a rest parameter
╭─[esprima/rest-parameter/invalid-setter-rest/input.js:1:1]
1 │ x = { set f(...y) {} }
@ -7347,6 +7385,30 @@ Expect to Parse: "typescript/types/const-type-parameters/input.ts"
2 │ }
╰────
× The keyword '"private"' is reserved
╭─[typescript/class/modifier-name-parameters/input.ts:1:1]
1 │ class Foo {
2 │ constructor(private, public, static) {
· ───────
3 │ }
╰────
× The keyword '"public"' is reserved
╭─[typescript/class/modifier-name-parameters/input.ts:1:1]
1 │ class Foo {
2 │ constructor(private, public, static) {
· ──────
3 │ }
╰────
× The keyword '"static"' is reserved
╭─[typescript/class/modifier-name-parameters/input.ts:1:1]
1 │ class Foo {
2 │ constructor(private, public, static) {
· ──────
3 │ }
╰────
× Automatic Semicolon Insertion
╭─[typescript/class/optional-async-error/input.js:2:1]
2 │ class A extends B {
@ -7516,6 +7578,13 @@ Expect to Parse: "typescript/types/const-type-parameters/input.ts"
╰────
help: Try insert a semicolon here
× The keyword '"interface"' is reserved
╭─[typescript/interface/new-line/input.ts:1:1]
1 │ interface
· ─────────
2 │ F
╰────
× Automatic Semicolon Insertion
╭─[typescript/module-namespace/module-declare-new-line/input.ts:5:1]
5 │

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
TypeScript Summary:
AST Parsed : 2310/2338 (98.80%)
Positive Passed: 2310/2338 (98.80%)
Negative Passed: 565/2531 (22.32%)
AST Parsed : 2308/2338 (98.72%)
Positive Passed: 2308/2338 (98.72%)
Negative Passed: 576/2531 (22.76%)
Expect Syntax Error: "Symbols/ES5SymbolProperty2.ts"
Expect Syntax Error: "Symbols/ES5SymbolProperty6.ts"
Expect Syntax Error: "additionalChecks/noPropertyAccessFromIndexSignature1.ts"
@ -881,12 +881,6 @@ Expect Syntax Error: "externalModules/multipleExportDefault3.ts"
Expect Syntax Error: "externalModules/multipleExportDefault4.ts"
Expect Syntax Error: "externalModules/multipleExportDefault5.ts"
Expect Syntax Error: "externalModules/multipleExportDefault6.ts"
Expect Syntax Error: "externalModules/topLevelAwaitErrors.12.ts"
Expect Syntax Error: "externalModules/topLevelAwaitErrors.2.ts"
Expect Syntax Error: "externalModules/topLevelAwaitErrors.3.ts"
Expect Syntax Error: "externalModules/topLevelAwaitErrors.4.ts"
Expect Syntax Error: "externalModules/topLevelAwaitErrors.5.ts"
Expect Syntax Error: "externalModules/topLevelAwaitErrors.6.ts"
Expect Syntax Error: "externalModules/topLevelAwaitNonModule.ts"
Expect Syntax Error: "externalModules/typeOnly/exportSpecifiers_js.ts"
Expect Syntax Error: "externalModules/typeOnly/preserveValueImports_module.ts"
@ -1321,8 +1315,6 @@ Expect Syntax Error: "parser/ecmascript5/RegressionTests/parser509698.ts"
Expect Syntax Error: "parser/ecmascript5/RegressionTests/parser536727.ts"
Expect Syntax Error: "parser/ecmascript5/RegressionTests/parser553699.ts"
Expect Syntax Error: "parser/ecmascript5/RegressionTests/parser618973.ts"
Expect Syntax Error: "parser/ecmascript5/RegressionTests/parser642331.ts"
Expect Syntax Error: "parser/ecmascript5/RegressionTests/parser642331_1.ts"
Expect Syntax Error: "parser/ecmascript5/RegressionTests/parserTernaryAndCommaOperators1.ts"
Expect Syntax Error: "parser/ecmascript5/RegularExpressions/parseRegularExpressionMixedWithComments.ts"
Expect Syntax Error: "parser/ecmascript5/RegularExpressions/parserRegularExpression1.ts"
@ -1470,10 +1462,7 @@ Expect Syntax Error: "parser/ecmascript6/ComputedPropertyNames/parserComputedPro
Expect Syntax Error: "parser/ecmascript6/ComputedPropertyNames/parserComputedPropertyName31.ts"
Expect Syntax Error: "parser/ecmascript6/ComputedPropertyNames/parserComputedPropertyName32.ts"
Expect Syntax Error: "parser/ecmascript6/ComputedPropertyNames/parserComputedPropertyName34.ts"
Expect Syntax Error: "parser/ecmascript6/ComputedPropertyNames/parserComputedPropertyName36.ts"
Expect Syntax Error: "parser/ecmascript6/ComputedPropertyNames/parserComputedPropertyName37.ts"
Expect Syntax Error: "parser/ecmascript6/ComputedPropertyNames/parserComputedPropertyName38.ts"
Expect Syntax Error: "parser/ecmascript6/ComputedPropertyNames/parserComputedPropertyName39.ts"
Expect Syntax Error: "parser/ecmascript6/ComputedPropertyNames/parserComputedPropertyName4.ts"
Expect Syntax Error: "parser/ecmascript6/ComputedPropertyNames/parserComputedPropertyName40.ts"
Expect Syntax Error: "parser/ecmascript6/ComputedPropertyNames/parserComputedPropertyName41.ts"
@ -2193,6 +2182,38 @@ Expect to Parse: "expressions/typeSatisfaction/typeSatisfaction_propertyValueCon
· ╰── Expected a semicolon or an implicit semicolon after a statement, but found none
╰────
help: Try insert a semicolon here
Expect to Parse: "externalModules/topLevelAwait.2.ts"
× The keyword '"await"' is reserved
╭─[externalModules/topLevelAwait.2.ts:3:1]
3 │
4 │ declare namespace foo { const await: any; }
· ─────
5 │
╰────
× The keyword '"await"' is reserved
╭─[externalModules/topLevelAwait.2.ts:6:1]
6 │ // await allowed in import=namespace when not a module
7 │ import await = foo.await;
· ─────
╰────
Expect to Parse: "externalModules/topLevelAwait.3.ts"
× The keyword '"await"' is reserved
╭─[externalModules/topLevelAwait.3.ts:6:1]
6 │ export {};
7 │ declare const await: any;
· ─────
8 │ declare class C extends await {}
╰────
× The keyword '"await"' is reserved
╭─[externalModules/topLevelAwait.3.ts:7:1]
7 │ declare const await: any;
8 │ declare class C extends await {}
· ─────
╰────
Expect to Parse: "jsdoc/declarations/jsDeclarationsNonIdentifierInferredNames.ts"
× Unexpected token
@ -5766,6 +5787,58 @@ Expect to Parse: "salsa/privateIdentifierExpando.ts"
11 │
╰────
× The keyword '"await"' is reserved
╭─[externalModules/topLevelAwaitErrors.12.ts:4:1]
4 │ export {};
5 │ declare namespace foo { const await: any; }
· ─────
6 │
╰────
× The keyword '"await"' is reserved
╭─[externalModules/topLevelAwaitErrors.12.ts:7:1]
7 │ // await disallowed in import=namespace when in a module
8 │ import await = foo.await;
· ─────
╰────
× The keyword '"await"' is reserved
╭─[externalModules/topLevelAwaitErrors.2.ts:6:1]
6 │ // reparse variable name as await should fail
7 │ var await = 1;
· ─────
╰────
× The keyword '"await"' is reserved
╭─[externalModules/topLevelAwaitErrors.3.ts:6:1]
6 │ // reparse binding pattern as await should fail
7 │ var {await} = {await:1};
· ─────
╰────
× The keyword '"await"' is reserved
╭─[externalModules/topLevelAwaitErrors.4.ts:6:1]
6 │ // reparse binding pattern as await should fail
7 │ var [await] = [1];
· ─────
╰────
× The keyword '"await"' is reserved
╭─[externalModules/topLevelAwaitErrors.5.ts:4:1]
4 │ // await in exported class name should fail
5 │ export class await {
· ─────
6 │ }
╰────
× The keyword '"await"' is reserved
╭─[externalModules/topLevelAwaitErrors.6.ts:4:1]
4 │ // await in exported function name should fail
5 │ export function await() {
· ─────
6 │ }
╰────
× Expect token
╭─[externalModules/typeOnly/exportDeclaration_missingBraces.ts:13:1]
13 │ namespace ns {
@ -7097,6 +7170,22 @@ Expect to Parse: "salsa/privateIdentifierExpando.ts"
╰────
help: Try insert a semicolon here
× The keyword '"static"' is reserved
╭─[parser/ecmascript5/RegressionTests/parser642331.ts:1:1]
1 │ class test {
2 │ constructor (static) { }
· ──────
3 │ }
╰────
× The keyword '"static"' is reserved
╭─[parser/ecmascript5/RegressionTests/parser642331_1.ts:3:1]
3 │ class test {
4 │ constructor (static) { }
· ──────
5 │ }
╰────
× Automatic Semicolon Insertion
╭─[parser/ecmascript5/RegressionTests/parser645086_1.ts:1:1]
1 │ var v = /[]/]/
@ -7762,6 +7851,30 @@ Expect to Parse: "salsa/privateIdentifierExpando.ts"
4 │ }
╰────
× The keyword '"public"' is reserved
╭─[parser/ecmascript6/ComputedPropertyNames/parserComputedPropertyName36.ts:2:1]
2 │ class C {
3 │ [public ]: string;
· ──────
4 │ }
╰────
× The keyword '"public"' is reserved
╭─[parser/ecmascript6/ComputedPropertyNames/parserComputedPropertyName38.ts:2:1]
2 │ class C {
3 │ [public]() { }
· ──────
4 │ }
╰────
× The keyword '"public"' is reserved
╭─[parser/ecmascript6/ComputedPropertyNames/parserComputedPropertyName39.ts:3:1]
3 │ class C {
4 │ [public]() { }
· ──────
5 │ }
╰────
× Expect token
╭─[parser/ecmascript6/ComputedPropertyNames/parserComputedPropertyName5.ts:1:1]
1 │ //@target: ES6