feat(linter): check FormalParameters in javascript

This commit is contained in:
Boshen 2023-03-13 23:12:42 +08:00
parent 040e41240b
commit 7269d32346
No known key found for this signature in database
GPG key ID: 6AC90C77AAAA6ABC
4 changed files with 883 additions and 77 deletions

View file

@ -1,5 +1,9 @@
#[allow(clippy::wildcard_imports)]
use oxc_ast::{ast::*, syntax_directed_operations::PropName, AstKind, Atom, ModuleKind, Span};
use oxc_ast::{
ast::*,
syntax_directed_operations::{BoundNames, IsSimpleParameterList, PropName},
AstKind, Atom, ModuleKind, Span,
};
use oxc_diagnostics::{
miette::{self, Diagnostic},
thiserror::{self, Error},
@ -44,6 +48,8 @@ impl Rule for EarlyErrorJavaScript {
AstKind::Super(sup) => check_super(sup, node, ctx),
AstKind::Property(prop) => check_property(prop, ctx),
AstKind::FormalParameters(params) => check_formal_parameters(params, node, ctx),
AstKind::ObjectExpression(expr) => check_object_expression(expr, ctx),
AstKind::BinaryExpression(expr) => check_binary_expression(expr, ctx),
AstKind::LogicalExpression(expr) => check_logical_expression(expr, ctx),
@ -688,6 +694,34 @@ fn check_property(prop: &Property, ctx: &LintContext) {
}
}
fn check_formal_parameters<'a>(
params: &FormalParameters,
node: &AstNode<'a>,
ctx: &LintContext<'a>,
) {
if params.is_empty() {
return;
}
// Note: all other cases forbid duplicate parameter names.
if params.kind == FormalParameterKind::FormalParameter
&& !ctx.strict_mode(node)
&& params.is_simple_parameter_list()
{
return;
}
// bound_names are usually small, a simple loop should be more performant checking with a hashmap
let mut idents = params.bound_names();
idents.sort_unstable_by_key(|ident| ident.name.as_str());
for i in 1..idents.len() {
let ident = &idents[i - 1];
if let Some(found) = idents[i..].iter().find(|i| i.name == ident.name) {
ctx.diagnostic(Redeclaration(ident.name.clone(), ident.span, found.span));
}
}
}
fn check_object_expression(obj_expr: &ObjectExpression, ctx: &LintContext) {
// ObjectLiteral : { PropertyDefinitionList }
// It is a Syntax Error if PropertyNameList of PropertyDefinitionList contains any duplicate entries for "__proto__"

View file

@ -1,7 +1,7 @@
Babel Summary:
AST Parsed : 2051/2069 (99.13%)
Positive Passed: 2051/2069 (99.13%)
Negative Passed: 1095/1502 (72.90%)
Negative Passed: 1108/1502 (73.77%)
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"
@ -89,7 +89,6 @@ Expect Syntax Error: "core/uncategorised/544/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"
Expect Syntax Error: "es2015/class-methods/disallow-duplicate-method-params/input.js"
Expect Syntax Error: "es2015/destructuring/binding-arguments-strict/input.js"
Expect Syntax Error: "es2015/destructuring/binding-eval/input.js"
Expect Syntax Error: "es2015/destructuring/error-operator-for-default/input.js"
@ -112,7 +111,6 @@ Expect Syntax Error: "es2015/statements/label-invalid-func-strict/input.js"
Expect Syntax Error: "es2015/uncategorised/.191/input.js"
Expect Syntax Error: "es2015/uncategorised/.335/input.js"
Expect Syntax Error: "es2015/uncategorised/.343/input.js"
Expect Syntax Error: "es2015/uncategorised/166/input.js"
Expect Syntax Error: "es2015/uncategorised/220/input.js"
Expect Syntax Error: "es2015/uncategorised/227/input.js"
Expect Syntax Error: "es2015/uncategorised/228/input.js"
@ -124,10 +122,7 @@ Expect Syntax Error: "es2015/uncategorised/244/input.js"
Expect Syntax Error: "es2015/uncategorised/245/input.js"
Expect Syntax Error: "es2015/uncategorised/246/input.js"
Expect Syntax Error: "es2015/uncategorised/247/input.js"
Expect Syntax Error: "es2015/uncategorised/248/input.js"
Expect Syntax Error: "es2015/uncategorised/249/input.js"
Expect Syntax Error: "es2015/uncategorised/280/input.js"
Expect Syntax Error: "es2015/uncategorised/281/input.js"
Expect Syntax Error: "es2015/uncategorised/289/input.js"
Expect Syntax Error: "es2015/uncategorised/296/input.js"
Expect Syntax Error: "es2015/uncategorised/297/input.js"
@ -166,12 +161,6 @@ 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-array-binding-pattern/invalid-dup-param/input.js"
Expect Syntax Error: "esprima/es2015-array-pattern/dupe-param-1/input.js"
Expect Syntax Error: "esprima/es2015-array-pattern/dupe-param-2/input.js"
Expect Syntax Error: "esprima/es2015-array-pattern/dupe-param-3/input.js"
Expect Syntax Error: "esprima/es2015-arrow-function/complex-rest-in-arrow-not-allowed/input.js"
Expect Syntax Error: "esprima/es2015-arrow-function/invalid-duplicated-params/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"
Expect Syntax Error: "esprima/es2015-generator/.generator-parameter-binding-property-reserved/input.js"
@ -208,8 +197,6 @@ Expect Syntax Error: "esprima/invalid-syntax/migrated_0088/input.js"
Expect Syntax Error: "esprima/invalid-syntax/migrated_0089/input.js"
Expect Syntax Error: "esprima/invalid-syntax/migrated_0090/input.js"
Expect Syntax Error: "esprima/invalid-syntax/migrated_0091/input.js"
Expect Syntax Error: "esprima/invalid-syntax/migrated_0092/input.js"
Expect Syntax Error: "esprima/invalid-syntax/migrated_0093/input.js"
Expect Syntax Error: "esprima/invalid-syntax/migrated_0094/input.js"
Expect Syntax Error: "esprima/invalid-syntax/migrated_0100/input.js"
Expect Syntax Error: "esprima/invalid-syntax/migrated_0101/input.js"
@ -2165,6 +2152,16 @@ Expect to Parse: "typescript/types/const-type-parameters/input.ts"
╰────
help: Try insert a semicolon here
× Identifier `a` has already been declared
╭─[es2015/class-methods/disallow-duplicate-method-params/input.js:1:1]
1 │ class Foo {
2 │ bar(a, a) {}
· ┬ ┬
· │ ╰── It can not be redeclared here
· ╰── `a` has already been declared here
3 │ }
╰────
× Automatic Semicolon Insertion
╭─[es2015/class-methods/disallow-literal-async/input.js:1:1]
1 │ class A {
@ -2954,6 +2951,14 @@ Expect to Parse: "typescript/types/const-type-parameters/input.ts"
· ─
╰────
× Identifier `a` has already been declared
╭─[es2015/uncategorised/166/input.js:1:1]
1 │ function x(a, { a }){}
· ┬ ┬
· │ ╰── It can not be redeclared here
· ╰── `a` has already been declared here
╰────
× Unexpected end of file
╭─[es2015/uncategorised/198/input.js:1:1]
1 │ 0o
@ -3202,6 +3207,14 @@ Expect to Parse: "typescript/types/const-type-parameters/input.ts"
· ───
╰────
× Identifier `a` has already been declared
╭─[es2015/uncategorised/248/input.js:1:1]
1 │ "use strict"; (a, a) => 42
· ┬ ┬
· │ ╰── It can not be redeclared here
· ╰── `a` has already been declared here
╰────
× Empty parenthesized expression
╭─[es2015/uncategorised/250/input.js:1:1]
1 │ () <= 42
@ -3310,6 +3323,22 @@ Expect to Parse: "typescript/types/const-type-parameters/input.ts"
· ─
╰────
× Identifier `a` has already been declared
╭─[es2015/uncategorised/280/input.js:1:1]
1 │ "use strict"; function x(a, { a }){}
· ┬ ┬
· │ ╰── It can not be redeclared here
· ╰── `a` has already been declared here
╰────
× Identifier `a` has already been declared
╭─[es2015/uncategorised/281/input.js:1:1]
1 │ "use strict"; function x({ b: { a } }, [{ b: { a } }]){}
· ┬ ┬
· │ ╰── It can not be redeclared here
· ╰── `a` has already been declared here
╰────
× Rest element must be last element
╭─[es2015/uncategorised/283/input.js:1:1]
1 │ (...a, b) => {}
@ -6204,6 +6233,41 @@ Expect to Parse: "typescript/types/const-type-parameters/input.ts"
· ╰── Unexpected trailing comma after rest element
╰────
× Identifier `b` has already been declared
╭─[esprima/es2015-array-binding-pattern/invalid-dup-param/input.js:1:1]
1 │ ([a,[b],...b])=>0;
· ┬ ┬
· │ ╰── It can not be redeclared here
· ╰── `b` has already been declared here
╰────
× Identifier `a` has already been declared
╭─[esprima/es2015-array-pattern/dupe-param-1/input.js:1:1]
1 │ "use strict";
2 │ function a([a,a]){ }
· ┬ ┬
· │ ╰── It can not be redeclared here
· ╰── `a` has already been declared here
╰────
× Identifier `a` has already been declared
╭─[esprima/es2015-array-pattern/dupe-param-2/input.js:1:1]
1 │ "use strict";
2 │ function a([a,...a]){ }
· ┬ ┬
· │ ╰── It can not be redeclared here
· ╰── `a` has already been declared here
╰────
× Identifier `a` has already been declared
╭─[esprima/es2015-array-pattern/dupe-param-3/input.js:1:1]
1 │ "use strict";
2 │ function a([{a},...a]){ }
· ┬ ┬
· │ ╰── It can not be redeclared here
· ╰── `a` has already been declared here
╰────
× Expect token
╭─[esprima/es2015-array-pattern/with-default-catch-param-fail/input.js:1:1]
1 │ try { } catch ([a] = []) { }
@ -6225,6 +6289,22 @@ Expect to Parse: "typescript/types/const-type-parameters/input.ts"
· ╰── Rest element must be last element
╰────
× Identifier `a` has already been declared
╭─[esprima/es2015-arrow-function/complex-rest-in-arrow-not-allowed/input.js:1:1]
1 │ (a,...[a]) => 0;
· ┬ ┬
· │ ╰── It can not be redeclared here
· ╰── `a` has already been declared here
╰────
× Identifier `x` has already been declared
╭─[esprima/es2015-arrow-function/invalid-duplicated-params/input.js:1:1]
1 │ (x, x) => y;
· ┬ ┬
· │ ╰── It can not be redeclared here
· ╰── `x` has already been declared here
╰────
× Line terminator not permitted before arrow
╭─[esprima/es2015-arrow-function/invalid-line-terminator-arrow/input.js:1:1]
1 │ ()
@ -7655,6 +7735,22 @@ Expect to Parse: "typescript/types/const-type-parameters/input.ts"
· ╰── Expect `}` here, but found `EOF`
╰────
× Identifier `a` has already been declared
╭─[esprima/invalid-syntax/migrated_0092/input.js:1:1]
1 │ (a, a) => 42
· ┬ ┬
· │ ╰── It can not be redeclared here
· ╰── `a` has already been declared here
╰────
× Identifier `a` has already been declared
╭─[esprima/invalid-syntax/migrated_0093/input.js:1:1]
1 │ "use strict"; (a, a) => 42
· ┬ ┬
· │ ╰── It can not be redeclared here
· ╰── `a` has already been declared here
╰────
× Empty parenthesized expression
╭─[esprima/invalid-syntax/migrated_0095/input.js:1:1]
1 │ () <= 42

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
TypeScript Summary:
AST Parsed : 2305/2338 (98.59%)
Positive Passed: 2305/2338 (98.59%)
Negative Passed: 655/2532 (25.87%)
Negative Passed: 657/2532 (25.95%)
Expect Syntax Error: "Symbols/ES5SymbolProperty2.ts"
Expect Syntax Error: "Symbols/ES5SymbolProperty6.ts"
Expect Syntax Error: "additionalChecks/noPropertyAccessFromIndexSignature1.ts"
@ -472,7 +472,6 @@ Expect Syntax Error: "es6/destructuring/destructuringInFunctionType.ts"
Expect Syntax Error: "es6/destructuring/destructuringObjectBindingPatternAndAssignment4.ts"
Expect Syntax Error: "es6/destructuring/destructuringParameterDeclaration1ES5.ts"
Expect Syntax Error: "es6/destructuring/destructuringParameterDeclaration1ES5iterable.ts"
Expect Syntax Error: "es6/destructuring/destructuringParameterDeclaration1ES6.ts"
Expect Syntax Error: "es6/destructuring/destructuringParameterDeclaration3ES5.ts"
Expect Syntax Error: "es6/destructuring/destructuringParameterDeclaration3ES5iterable.ts"
Expect Syntax Error: "es6/destructuring/destructuringParameterDeclaration3ES6.ts"
@ -1608,7 +1607,6 @@ Expect Syntax Error: "types/nonPrimitive/nonPrimitiveStrictNull.ts"
Expect Syntax Error: "types/nonPrimitive/nonPrimitiveUnionIntersection.ts"
Expect Syntax Error: "types/objectTypeLiteral/callSignatures/callSignatureWithOptionalParameterAndInitializer.ts"
Expect Syntax Error: "types/objectTypeLiteral/callSignatures/callSignaturesThatDifferOnlyByReturnType2.ts"
Expect Syntax Error: "types/objectTypeLiteral/callSignatures/callSignaturesWithDuplicateParameters.ts"
Expect Syntax Error: "types/objectTypeLiteral/callSignatures/callSignaturesWithParameterInitializers.ts"
Expect Syntax Error: "types/objectTypeLiteral/callSignatures/constructSignatureWithAccessibilityModifiersOnParameters.ts"
Expect Syntax Error: "types/objectTypeLiteral/callSignatures/constructSignatureWithAccessibilityModifiersOnParameters2.ts"
@ -4800,6 +4798,26 @@ Expect to Parse: "salsa/privateIdentifierExpando.ts"
3 │ var {i}: string | number = { i: 2 };
╰────
× Identifier `number` has already been declared
╭─[es6/destructuring/destructuringParameterDeclaration1ES6.ts:96:1]
96 │
97 │ function e6({x: [number, number, number]}) { } // error, duplicate identifier;
· ───┬── ───┬──
· │ ╰── It can not be redeclared here
· ╰── `number` has already been declared here
98 │
╰────
× Identifier `number` has already been declared
╭─[es6/destructuring/destructuringParameterDeclaration1ES6.ts:96:1]
96 │
97 │ function e6({x: [number, number, number]}) { } // error, duplicate identifier;
· ───┬── ───┬──
· │ ╰── It can not be redeclared here
· ╰── `number` has already been declared here
98 │
╰────
× Expect token
╭─[es6/destructuring/destructuringParameterDeclaration2.ts:6:1]
6 │ function a0([a, b, [[c]]]: [number, number, string[][]]) { }
@ -11400,6 +11418,146 @@ Expect to Parse: "salsa/privateIdentifierExpando.ts"
13 │ var f8 = <T>(private x: T, public y: T) => { }
╰────
× Identifier `x` has already been declared
╭─[types/objectTypeLiteral/callSignatures/callSignaturesWithDuplicateParameters.ts:5:1]
5 │ var f2 = function (x, x) { }
6 │ var f3 = (x, x) => { }
· ┬ ┬
· │ ╰── It can not be redeclared here
· ╰── `x` has already been declared here
7 │ var f4 = <T>(x: T, x: T) => { }
╰────
× Identifier `x` has already been declared
╭─[types/objectTypeLiteral/callSignatures/callSignaturesWithDuplicateParameters.ts:6:1]
6 │ var f3 = (x, x) => { }
7 │ var f4 = <T>(x: T, x: T) => { }
· ┬ ┬
· │ ╰── It can not be redeclared here
· ╰── `x` has already been declared here
8 │
╰────
× Identifier `x` has already been declared
╭─[types/objectTypeLiteral/callSignatures/callSignaturesWithDuplicateParameters.ts:11:1]
11 │ var f6 = function (x: string, x: number) { }
12 │ var f7 = (x: string, x: number) => { }
· ┬ ┬
· │ ╰── It can not be redeclared here
· ╰── `x` has already been declared here
13 │ var f8 = <T>(x: T, y: T) => { }
╰────
× Identifier `x` has already been declared
╭─[types/objectTypeLiteral/callSignatures/callSignaturesWithDuplicateParameters.ts:15:1]
15 │ class C {
16 │ foo(x, x) { }
· ┬ ┬
· │ ╰── It can not be redeclared here
· ╰── `x` has already been declared here
17 │ foo2(x: number, x: string) { }
╰────
× Identifier `x` has already been declared
╭─[types/objectTypeLiteral/callSignatures/callSignaturesWithDuplicateParameters.ts:16:1]
16 │ foo(x, x) { }
17 │ foo2(x: number, x: string) { }
· ┬ ┬
· │ ╰── It can not be redeclared here
· ╰── `x` has already been declared here
18 │ foo3<T>(x: T, x: T) { }
╰────
× Identifier `x` has already been declared
╭─[types/objectTypeLiteral/callSignatures/callSignaturesWithDuplicateParameters.ts:17:1]
17 │ foo2(x: number, x: string) { }
18 │ foo3<T>(x: T, x: T) { }
· ┬ ┬
· │ ╰── It can not be redeclared here
· ╰── `x` has already been declared here
19 │ }
╰────
× Identifier `x` has already been declared
╭─[types/objectTypeLiteral/callSignatures/callSignaturesWithDuplicateParameters.ts:21:1]
21 │ interface I {
22 │ (x, x);
· ┬ ┬
· │ ╰── It can not be redeclared here
· ╰── `x` has already been declared here
23 │ (x: string, x: number);
╰────
× Identifier `x` has already been declared
╭─[types/objectTypeLiteral/callSignatures/callSignaturesWithDuplicateParameters.ts:22:1]
22 │ (x, x);
23 │ (x: string, x: number);
· ┬ ┬
· │ ╰── It can not be redeclared here
· ╰── `x` has already been declared here
24 │ foo(x, x);
╰────
× Identifier `x` has already been declared
╭─[types/objectTypeLiteral/callSignatures/callSignaturesWithDuplicateParameters.ts:23:1]
23 │ (x: string, x: number);
24 │ foo(x, x);
· ┬ ┬
· │ ╰── It can not be redeclared here
· ╰── `x` has already been declared here
25 │ foo(x: number, x: string);
╰────
× Identifier `x` has already been declared
╭─[types/objectTypeLiteral/callSignatures/callSignaturesWithDuplicateParameters.ts:24:1]
24 │ foo(x, x);
25 │ foo(x: number, x: string);
· ┬ ┬
· │ ╰── It can not be redeclared here
· ╰── `x` has already been declared here
26 │ foo3<T>(x: T, x: T);
╰────
× Identifier `x` has already been declared
╭─[types/objectTypeLiteral/callSignatures/callSignaturesWithDuplicateParameters.ts:25:1]
25 │ foo(x: number, x: string);
26 │ foo3<T>(x: T, x: T);
· ┬ ┬
· │ ╰── It can not be redeclared here
· ╰── `x` has already been declared here
27 │ }
╰────
× Identifier `x` has already been declared
╭─[types/objectTypeLiteral/callSignatures/callSignaturesWithDuplicateParameters.ts:29:1]
29 │ var a: {
30 │ foo(x, x);
· ┬ ┬
· │ ╰── It can not be redeclared here
· ╰── `x` has already been declared here
31 │ foo2(x: number, x: string);
╰────
× Identifier `x` has already been declared
╭─[types/objectTypeLiteral/callSignatures/callSignaturesWithDuplicateParameters.ts:30:1]
30 │ foo(x, x);
31 │ foo2(x: number, x: string);
· ┬ ┬
· │ ╰── It can not be redeclared here
· ╰── `x` has already been declared here
32 │ };
╰────
× Identifier `x` has already been declared
╭─[types/objectTypeLiteral/callSignatures/callSignaturesWithDuplicateParameters.ts:36:1]
36 │ a: function foo(x: number, x: string) { },
37 │ b: <T>(x: T, x: T) => { }
· ┬ ┬
· │ ╰── It can not be redeclared here
· ╰── `x` has already been declared here
38 │ }
╰────
× Automatic Semicolon Insertion
╭─[types/objectTypeLiteral/callSignatures/callSignaturesWithParameterInitializers2.ts:19:1]
19 │ var b = {