feat(linter): check NewTarget in javascript

This commit is contained in:
Boshen 2023-03-14 00:04:13 +08:00
parent 39795dbb1b
commit 76118e4901
No known key found for this signature in database
GPG key ID: 6AC90C77AAAA6ABC
6 changed files with 337 additions and 22 deletions

View file

@ -103,6 +103,11 @@ impl<'a> LintContext<'a> {
self.semantic().scopes().node_scope(node)
}
#[must_use]
pub fn scope_ancestors(&self, node: &AstNode) -> Ancestors<'_, Scope> {
self.semantic().scopes().ancestors(node.get().scope_id())
}
#[must_use]
pub fn strict_mode(&self, node: &AstNode) -> bool {
let scope = self.scope(node);

View file

@ -9,6 +9,7 @@ use oxc_diagnostics::{
thiserror::{self, Error},
Redeclaration,
};
use oxc_semantic::ScopeFlags;
use crate::{ast_util::STRICT_MODE_NAMES, context::LintContext, rule::Rule, AstNode};
@ -39,6 +40,7 @@ impl Rule for EarlyErrorJavaScript {
AstKind::Directive(dir) => check_directive(dir, node, ctx),
AstKind::ModuleDeclaration(decl) => check_module_declaration(decl, node, ctx),
AstKind::MetaProperty(prop) => check_meta_property(prop, node, ctx),
AstKind::WithStatement(stmt) => check_with_statement(stmt, node, ctx),
AstKind::SwitchStatement(stmt) => check_switch_statement(stmt, ctx),
@ -376,6 +378,64 @@ fn check_module_declaration<'a>(
}
}
fn check_meta_property<'a>(prop: &MetaProperty, node: &AstNode<'a>, ctx: &LintContext<'a>) {
#[derive(Debug, Error, Diagnostic)]
#[error("Unexpected new.target expression")]
#[diagnostic(help(
"new.target is only allowed in constructors and functions invoked using thew `new` operator"
))]
struct NewTarget(#[label] Span);
#[derive(Debug, Error, Diagnostic)]
#[error("The only valid meta property for new is new.target")]
#[diagnostic()]
struct NewTargetProperty(#[label] Span);
#[derive(Debug, Error, Diagnostic)]
#[error("Unexpected import.meta expression")]
#[diagnostic(help("import.meta is only allowed in module code"))]
struct ImportMeta(#[label] Span);
#[derive(Debug, Error, Diagnostic)]
#[error("The only valid meta property for import is import.meta")]
#[diagnostic()]
struct ImportMetaProperty(#[label] Span);
match prop.meta.name.as_str() {
"import" => {
if prop.property.name == "meta" {
if ctx.source_type().is_script() {
return ctx.diagnostic(ImportMeta(prop.span));
}
return;
}
ctx.diagnostic(ImportMetaProperty(prop.span));
}
"new" => {
if prop.property.name == "target" {
let mut in_function_scope = false;
for scope_id in ctx.scope_ancestors(node) {
let flags = ctx.scopes()[scope_id].get().flags;
// In arrow functions, new.target is inherited from the surrounding scope.
if flags.contains(ScopeFlags::Arrow) {
continue;
}
if flags.intersects(ScopeFlags::Function | ScopeFlags::ClassStaticBlock) {
in_function_scope = true;
break;
}
}
if !in_function_scope {
return ctx.diagnostic(NewTarget(prop.span));
}
return;
}
ctx.diagnostic(NewTargetProperty(prop.span));
}
_ => {}
}
}
fn check_function_declaration<'a>(kind: AstKind<'a>, node: &AstNode<'a>, ctx: &LintContext<'a>) {
#[derive(Debug, Error, Diagnostic)]
#[error("Invalid function declaration")]

View file

@ -12,7 +12,7 @@ use std::rc::Rc;
pub use builder::SemanticBuilder;
pub use node::{AstNode, AstNodes, SemanticNode};
use oxc_ast::{SourceType, Trivias};
pub use scope::{Scope, ScopeTree};
pub use scope::{Scope, ScopeFlags, ScopeTree};
pub struct Semantic<'a> {
source_type: SourceType,

View file

@ -1,7 +1,7 @@
Babel Summary:
AST Parsed : 2051/2069 (99.13%)
Positive Passed: 2051/2069 (99.13%)
Negative Passed: 1123/1502 (74.77%)
AST Parsed : 2050/2069 (99.08%)
Positive Passed: 2050/2069 (99.08%)
Negative Passed: 1133/1502 (75.43%)
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"
@ -21,8 +21,6 @@ Expect Syntax Error: "core/escape-string/numeric-escape-in-directive/input.js"
Expect Syntax Error: "core/escape-string/numeric-escape-in-property-name/input.js"
Expect Syntax Error: "core/legacy-octal/legacy-octal-after-use-strict-function/input.js"
Expect Syntax Error: "core/legacy-octal/legacy-octal-after-use-strict/input.js"
Expect Syntax Error: "core/opts/allowNewTargetOutsideFunction-false-2/input.js"
Expect Syntax Error: "core/opts/allowNewTargetOutsideFunction-false/input.js"
Expect Syntax Error: "core/scope/undecl-export-as-default/input.js"
Expect Syntax Error: "core/scope/undecl-export-as/input.js"
Expect Syntax Error: "core/scope/undecl-export-block/input.js"
@ -93,9 +91,6 @@ Expect Syntax Error: "es2015/destructuring/error-operator-for-default/input.js"
Expect Syntax Error: "es2015/for-in/strict-initializer/input.js"
Expect Syntax Error: "es2015/for-of/invalid-let-as-identifier/input.js"
Expect Syntax Error: "es2015/let/let-as-identifier-strict-fail/input.js"
Expect Syntax Error: "es2015/meta-properties/invalid-arrow-function/input.js"
Expect Syntax Error: "es2015/meta-properties/new-invalid-prop/input.js"
Expect Syntax Error: "es2015/meta-properties/new-target-invalid/input.js"
Expect Syntax Error: "es2015/modules/duplicate-export-default-and-export-as-default/input.js"
Expect Syntax Error: "es2015/modules/duplicate-export-default/input.js"
Expect Syntax Error: "es2015/modules/duplicate-named-export-class-declaration/input.js"
@ -142,10 +137,7 @@ Expect Syntax Error: "es2018/object-rest-spread/comma-after-spread-for-in/input.
Expect Syntax Error: "es2018/object-rest-spread/comma-after-spread-nested/input.js"
Expect Syntax Error: "es2018/object-rest-spread/no-pattern-in-rest-with-ts/input.js"
Expect Syntax Error: "es2018/object-rest-spread/no-pattern-in-rest/input.js"
Expect Syntax Error: "es2020/dynamic-import/direct-calls-only/input.js"
Expect Syntax Error: "es2020/dynamic-import/invalid-trailing-comma/input.js"
Expect Syntax Error: "es2020/import-meta/error-in-script/input.js"
Expect Syntax Error: "es2020/import-meta/no-other-prop-names/input.js"
Expect Syntax Error: "esprima/declaration-function/dupe-param/input.js"
Expect Syntax Error: "esprima/es2015-arrow-function/invalid-param-strict-mode/input.js"
Expect Syntax Error: "esprima/es2015-export-declaration/invalid-export-named-default/input.js"
@ -153,8 +145,6 @@ Expect Syntax Error: "esprima/es2015-generator/.generator-parameter-binding-prop
Expect Syntax Error: "esprima/es2015-identifier/.invalid_function_wait/input.js"
Expect Syntax Error: "esprima/es2015-identifier/invalid_expression_await/input.js"
Expect Syntax Error: "esprima/es2015-identifier/invalid_var_await/input.js"
Expect Syntax Error: "esprima/es2015-meta-property/invalid-new-target/input.js"
Expect Syntax Error: "esprima/es2015-meta-property/unknown-property/input.js"
Expect Syntax Error: "esprima/es2015-yield/invalid-yield-generator-declaration/input.js"
Expect Syntax Error: "esprima/es2015-yield/invalid-yield-generator-expression-name/input.js"
Expect Syntax Error: "esprima/es2015-yield/invalid-yield-generator-function-declaration/input.js"
@ -381,6 +371,23 @@ Expect Syntax Error: "typescript/types/tuple-optional-invalid/input.ts"
Expect Syntax Error: "typescript/types/tuple-required-after-labeled-optional/input.ts"
Expect Syntax Error: "typescript/types/tuple-unlabeled-spread-after-labeled/input.ts"
Expect Syntax Error: "typescript/types/tuple-unlabeled-spread-before-labeled/input.ts"
Expect to Parse: "core/opts/allowNewTargetOutsideFunction-true/input.js"
× Unexpected new.target expression
╭─[core/opts/allowNewTargetOutsideFunction-true/input.js:1:1]
1 │ const x = new.target;
· ──────────
2 │ const y = () => new.target;
╰────
help: new.target is only allowed in constructors and functions invoked using thew `new` operator
× Unexpected new.target expression
╭─[core/opts/allowNewTargetOutsideFunction-true/input.js:1:1]
1 │ const x = new.target;
2 │ const y = () => new.target;
· ──────────
╰────
help: new.target is only allowed in constructors and functions invoked using thew `new` operator
Expect to Parse: "core/scope/dupl-bind-func-var/input.js"
× Identifier `foo` has already been declared
@ -761,6 +768,20 @@ Expect to Parse: "typescript/types/const-type-parameters/input.ts"
· ╰── Expect `(` here, but found `await`
╰────
× Unexpected new.target expression
╭─[core/opts/allowNewTargetOutsideFunction-false-2/input.js:1:1]
1 │ const y = () => new.target;
· ──────────
╰────
help: new.target is only allowed in constructors and functions invoked using thew `new` operator
× Unexpected new.target expression
╭─[core/opts/allowNewTargetOutsideFunction-false/input.js:1:1]
1 │ const x = new.target;
· ──────────
╰────
help: new.target is only allowed in constructors and functions invoked using thew `new` operator
× Unexpected token
╭─[core/regression/13694-invalid-dot-bracketL-member/input.js:1:1]
1 │ a.[b]
@ -2533,6 +2554,23 @@ Expect to Parse: "typescript/types/const-type-parameters/input.ts"
3 │ }
╰────
× Unexpected new.target expression
╭─[es2015/meta-properties/invalid-arrow-function/input.js:1:1]
1 │ const A = () => {
2 │ new.target;
· ──────────
3 │ }
╰────
help: new.target is only allowed in constructors and functions invoked using thew `new` operator
× The only valid meta property for new is new.target
╭─[es2015/meta-properties/new-invalid-prop/input.js:1:1]
1 │ function f() {
2 │ new.prop
· ────────
3 │ }
╰────
× Keywords cannot contain escape characters
╭─[es2015/meta-properties/new-target-invalid-escaped-new/input.js:1:1]
1 │ function f() { n\u0065w.target; }
@ -2547,6 +2585,13 @@ Expect to Parse: "typescript/types/const-type-parameters/input.ts"
· ╰── keyword cannot contain escape characters
╰────
× Unexpected new.target expression
╭─[es2015/meta-properties/new-target-invalid/input.js:1:1]
1 │ new.target
· ──────────
╰────
help: new.target is only allowed in constructors and functions invoked using thew `new` operator
× Identifier `foo` has already been declared
╭─[es2015/modules/duplicate-named-export-destructuring10/input.js:1:1]
1 │ export function foo() {};
@ -4746,6 +4791,14 @@ Expect to Parse: "typescript/types/const-type-parameters/input.ts"
· ╰── Invalid characters after number
╰────
× The only valid meta property for import is import.meta
╭─[es2020/dynamic-import/direct-calls-only/input.js:1:1]
1 │ function failsParse() {
2 │ return import.then();
· ───────────
3 │ }
╰────
× Unexpected token
╭─[es2020/dynamic-import/invalid-arguments-spread/input.js:1:1]
1 │ import(...[1])
@ -4772,6 +4825,19 @@ Expect to Parse: "typescript/types/const-type-parameters/input.ts"
· ─
╰────
× Unexpected import.meta expression
╭─[es2020/import-meta/error-in-script/input.js:1:1]
1 │ const x = import.meta;
· ───────────
╰────
help: import.meta is only allowed in module code
× The only valid meta property for import is import.meta
╭─[es2020/import-meta/no-other-prop-names/input.js:1:1]
1 │ import.notMeta;
· ──────────────
╰────
× Invalid assignment
╭─[es2020/import-meta/not-assignable/input.js:1:1]
1 │ import.meta = true;
@ -6849,6 +6915,19 @@ Expect to Parse: "typescript/types/const-type-parameters/input.ts"
· ─
╰────
× Unexpected new.target expression
╭─[esprima/es2015-meta-property/invalid-new-target/input.js:1:1]
1 │ var x = new.target;
· ──────────
╰────
help: new.target is only allowed in constructors and functions invoked using thew `new` operator
× The only valid meta property for new is new.target
╭─[esprima/es2015-meta-property/unknown-property/input.js:1:1]
1 │ var f = function() { new.unknown_property; }
· ────────────────────
╰────
× Identifier `__proto__` has already been declared
╭─[esprima/es2015-object-initialiser/invalid-proto-getter-literal-identifier/input.js:1:1]
1 │ ({ get __proto(){}, "__proto__": null, __proto__: null, })

View file

@ -1,7 +1,7 @@
Test262 Summary:
AST Parsed : 44015/44034 (99.96%)
Positive Passed: 44015/44034 (99.96%)
Negative Passed: 3844/3917 (98.14%)
Negative Passed: 3848/3917 (98.24%)
Expect Syntax Error: "language/directive-prologue/10.1.1-2gs.js"
Expect Syntax Error: "language/directive-prologue/10.1.1-5gs.js"
Expect Syntax Error: "language/directive-prologue/10.1.1-8gs.js"
@ -22,7 +22,6 @@ Expect Syntax Error: "language/expressions/function/param-duplicated-strict-body
Expect Syntax Error: "language/expressions/function/param-duplicated-strict-body-3.js"
Expect Syntax Error: "language/expressions/function/param-eval-strict-body.js"
Expect Syntax Error: "language/expressions/generators/yield-as-generator-expression-binding-identifier.js"
Expect Syntax Error: "language/expressions/import.meta/syntax/goal-script.js"
Expect Syntax Error: "language/expressions/object/getter-body-strict-inside.js"
Expect Syntax Error: "language/expressions/object/identifier-shorthand-implements-invalid-strict-mode.js"
Expect Syntax Error: "language/expressions/object/identifier-shorthand-interface-invalid-strict-mode.js"
@ -38,8 +37,6 @@ Expect Syntax Error: "language/expressions/object/setter-param-arguments-strict-
Expect Syntax Error: "language/expressions/object/setter-param-eval-strict-inside.js"
Expect Syntax Error: "language/global-code/export.js"
Expect Syntax Error: "language/global-code/import.js"
Expect Syntax Error: "language/global-code/new.target-arrow.js"
Expect Syntax Error: "language/global-code/new.target.js"
Expect Syntax Error: "language/import/dup-bound-names.js"
Expect Syntax Error: "language/import/json-invalid.js"
Expect Syntax Error: "language/import/json-named-bindings.js"
@ -59,7 +56,6 @@ Expect Syntax Error: "language/module-code/early-dup-export-id.js"
Expect Syntax Error: "language/module-code/early-dup-export-star-as-dflt.js"
Expect Syntax Error: "language/module-code/early-export-global.js"
Expect Syntax Error: "language/module-code/early-export-unresolvable.js"
Expect Syntax Error: "language/module-code/early-new-target.js"
Expect Syntax Error: "language/statements/class/static-init-invalid-await.js"
Expect Syntax Error: "language/statements/function/enable-strict-via-body.js"
Expect Syntax Error: "language/statements/function/enable-strict-via-outer-body.js"
@ -15403,6 +15399,14 @@ Expect to Parse: "language/statements/function/S14_A5_T2.js"
· ╰── keyword cannot contain escape characters
╰────
× Unexpected import.meta expression
╭─[language/expressions/import.meta/syntax/goal-script.js:17:1]
17 │
18 │ import.meta;
· ───────────
╰────
help: import.meta is only allowed in module code
× Invalid assignment
╭─[language/expressions/import.meta/syntax/invalid-assignment-target-array-destructuring-expr.js:30:1]
30 │
@ -17792,6 +17796,23 @@ Expect to Parse: "language/statements/function/S14_A5_T2.js"
· ──
╰────
× Unexpected new.target expression
╭─[language/global-code/new.target-arrow.js:35:1]
35 │ () => {
36 │ new.target;
· ──────────
37 │ };
╰────
help: new.target is only allowed in constructors and functions invoked using thew `new` operator
× Unexpected new.target expression
╭─[language/global-code/new.target.js:19:1]
19 │
20 │ new.target;
· ──────────
╰────
help: new.target is only allowed in constructors and functions invoked using thew `new` operator
× TS1108: A 'return' statement can only be used within a function body
╭─[language/global-code/return.js:21:1]
21 │
@ -20282,6 +20303,14 @@ Expect to Parse: "language/statements/function/S14_A5_T2.js"
· ╰── It can not be redeclared here
╰────
× Unexpected new.target expression
╭─[language/module-code/early-new-target.js:15:1]
15 │
16 │ new.target;
· ──────────
╰────
help: new.target is only allowed in constructors and functions invoked using thew `new` operator
× The keyword 'public' is reserved
╭─[language/module-code/early-strict-mode.js:14:1]
14 │

View file

@ -1,7 +1,7 @@
TypeScript Summary:
AST Parsed : 2305/2338 (98.59%)
Positive Passed: 2305/2338 (98.59%)
Negative Passed: 661/2532 (26.11%)
Negative Passed: 663/2532 (26.18%)
Expect Syntax Error: "Symbols/ES5SymbolProperty2.ts"
Expect Syntax Error: "Symbols/ES5SymbolProperty6.ts"
Expect Syntax Error: "additionalChecks/noPropertyAccessFromIndexSignature1.ts"
@ -551,8 +551,6 @@ Expect Syntax Error: "es6/modules/defaultExportsCannotMerge04.ts"
Expect Syntax Error: "es6/modules/importEmptyFromModuleNotExisted.ts"
Expect Syntax Error: "es6/modules/multipleDefaultExports04.ts"
Expect Syntax Error: "es6/modules/multipleDefaultExports05.ts"
Expect Syntax Error: "es6/newTarget/invalidNewTarget.es5.ts"
Expect Syntax Error: "es6/newTarget/invalidNewTarget.es6.ts"
Expect Syntax Error: "es6/restParameters/readonlyRestParameters.ts"
Expect Syntax Error: "es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesAssignmentError.ts"
Expect Syntax Error: "es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesAssignmentErrorFromMissingIdentifier.ts"
@ -5287,6 +5285,150 @@ Expect to Parse: "salsa/privateIdentifierExpando.ts"
8 │ }
╰────
× Unexpected new.target expression
╭─[es6/newTarget/invalidNewTarget.es5.ts:1:1]
1 │ // @target: es5
2 │ const a = new.target;
· ──────────
3 │ const b = () => new.target;
╰────
help: new.target is only allowed in constructors and functions invoked using thew `new` operator
× Unexpected new.target expression
╭─[es6/newTarget/invalidNewTarget.es5.ts:2:1]
2 │ const a = new.target;
3 │ const b = () => new.target;
· ──────────
4 │
╰────
help: new.target is only allowed in constructors and functions invoked using thew `new` operator
× Unexpected new.target expression
╭─[es6/newTarget/invalidNewTarget.es5.ts:5:1]
5 │ class C {
6 │ [new.target]() { }
· ──────────
7 │ c() { return new.target; }
╰────
help: new.target is only allowed in constructors and functions invoked using thew `new` operator
× Unexpected new.target expression
╭─[es6/newTarget/invalidNewTarget.es5.ts:9:1]
9 │ set e(_) { _ = new.target; }
10 │ f = () => new.target;
· ──────────
11 │
╰────
help: new.target is only allowed in constructors and functions invoked using thew `new` operator
× Unexpected new.target expression
╭─[es6/newTarget/invalidNewTarget.es5.ts:11:1]
11 │
12 │ static [new.target]() { }
· ──────────
13 │ static g() { return new.target; }
╰────
help: new.target is only allowed in constructors and functions invoked using thew `new` operator
× Unexpected new.target expression
╭─[es6/newTarget/invalidNewTarget.es5.ts:15:1]
15 │ static set i(_) { _ = new.target; }
16 │ static j = () => new.target;
· ──────────
17 │ }
╰────
help: new.target is only allowed in constructors and functions invoked using thew `new` operator
× Unexpected new.target expression
╭─[es6/newTarget/invalidNewTarget.es5.ts:19:1]
19 │ const O = {
20 │ [new.target]: undefined,
· ──────────
21 │ k() { return new.target; },
╰────
help: new.target is only allowed in constructors and functions invoked using thew `new` operator
× Unexpected new.target expression
╭─[es6/newTarget/invalidNewTarget.es5.ts:23:1]
23 │ set m(_) { _ = new.target; },
24 │ n: new.target,
· ──────────
25 │ };
╰────
help: new.target is only allowed in constructors and functions invoked using thew `new` operator
× Unexpected new.target expression
╭─[es6/newTarget/invalidNewTarget.es6.ts:1:1]
1 │ // @target: es6
2 │ const a = new.target;
· ──────────
3 │ const b = () => new.target;
╰────
help: new.target is only allowed in constructors and functions invoked using thew `new` operator
× Unexpected new.target expression
╭─[es6/newTarget/invalidNewTarget.es6.ts:2:1]
2 │ const a = new.target;
3 │ const b = () => new.target;
· ──────────
4 │
╰────
help: new.target is only allowed in constructors and functions invoked using thew `new` operator
× Unexpected new.target expression
╭─[es6/newTarget/invalidNewTarget.es6.ts:5:1]
5 │ class C {
6 │ [new.target]() { }
· ──────────
7 │ c() { return new.target; }
╰────
help: new.target is only allowed in constructors and functions invoked using thew `new` operator
× Unexpected new.target expression
╭─[es6/newTarget/invalidNewTarget.es6.ts:9:1]
9 │ set e(_) { _ = new.target; }
10 │ f = () => new.target;
· ──────────
11 │
╰────
help: new.target is only allowed in constructors and functions invoked using thew `new` operator
× Unexpected new.target expression
╭─[es6/newTarget/invalidNewTarget.es6.ts:11:1]
11 │
12 │ static [new.target]() { }
· ──────────
13 │ static g() { return new.target; }
╰────
help: new.target is only allowed in constructors and functions invoked using thew `new` operator
× Unexpected new.target expression
╭─[es6/newTarget/invalidNewTarget.es6.ts:15:1]
15 │ static set i(_) { _ = new.target; }
16 │ static j = () => new.target;
· ──────────
17 │ }
╰────
help: new.target is only allowed in constructors and functions invoked using thew `new` operator
× Unexpected new.target expression
╭─[es6/newTarget/invalidNewTarget.es6.ts:19:1]
19 │ const O = {
20 │ [new.target]: undefined,
· ──────────
21 │ k() { return new.target; },
╰────
help: new.target is only allowed in constructors and functions invoked using thew `new` operator
× Unexpected new.target expression
╭─[es6/newTarget/invalidNewTarget.es6.ts:23:1]
23 │ set m(_) { _ = new.target; },
24 │ n: new.target,
· ──────────
25 │ };
╰────
help: new.target is only allowed in constructors and functions invoked using thew `new` operator
× Unexpected token
╭─[es6/shorthandPropertyAssignment/objectLiteralShorthandPropertiesErrorFromNotUsingIdentifier.ts:2:1]
2 │ var y = {