feat(linter): check duplicated bound names in ImportDeclaration

This commit is contained in:
Boshen 2023-03-14 10:41:28 +08:00
parent 89f28e9f93
commit ee31f5cc6f
No known key found for this signature in database
GPG key ID: 6AC90C77AAAA6ABC
2 changed files with 36 additions and 12 deletions

View file

@ -39,7 +39,12 @@ impl Rule for EarlyErrorJavaScript {
AstKind::RegExpLiteral(lit) => check_regexp_literal(lit, ctx),
AstKind::Directive(dir) => check_directive(dir, node, ctx),
AstKind::ModuleDeclaration(decl) => check_module_declaration(decl, node, ctx),
AstKind::ModuleDeclaration(decl) => {
check_module_declaration(decl, node, ctx);
if let ModuleDeclarationKind::ImportDeclaration(import_decl) = &decl.kind {
check_import_declaration(import_decl, ctx);
}
}
AstKind::MetaProperty(prop) => check_meta_property(prop, node, ctx),
AstKind::WithStatement(stmt) => check_with_statement(stmt, node, ctx),
@ -70,6 +75,18 @@ impl Rule for EarlyErrorJavaScript {
}
}
fn check_duplicate_bound_names<T: BoundNames>(bound_names: &T, ctx: &LintContext) {
// bound_names are usually small, a simple loop should be more performant checking with a hashmap
let mut idents = bound_names.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));
}
}
}
#[derive(Debug, Error, Diagnostic)]
#[error("Cannot use await in class static initialization block")]
#[diagnostic()]
@ -393,6 +410,13 @@ fn check_module_declaration<'a>(
}
}
fn check_import_declaration(decl: &ImportDeclaration, ctx: &LintContext) {
// ModuleItem : ImportDeclaration
// It is a Syntax Error if the BoundNames of ImportDeclaration contains any duplicate entries.
// bound_names are usually small, a simple loop should be more performant checking with a hashmap
check_duplicate_bound_names(decl, ctx);
}
fn check_meta_property<'a>(prop: &MetaProperty, node: &AstNode<'a>, ctx: &LintContext<'a>) {
#[derive(Debug, Error, Diagnostic)]
#[error("Unexpected new.target expression")]
@ -868,15 +892,7 @@ fn check_formal_parameters<'a>(
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));
}
}
check_duplicate_bound_names(params, ctx);
}
fn check_formal_parameter(param: &FormalParameter, ctx: &LintContext) {

View file

@ -1,8 +1,7 @@
Test262 Summary:
AST Parsed : 44015/44034 (99.96%)
Positive Passed: 44015/44034 (99.96%)
Negative Passed: 3907/3917 (99.74%)
Expect Syntax Error: "language/import/dup-bound-names.js"
Negative Passed: 3908/3917 (99.77%)
Expect Syntax Error: "language/import/json-invalid.js"
Expect Syntax Error: "language/import/json-named-bindings.js"
Expect Syntax Error: "language/module-code/early-dup-export-as-star-as.js"
@ -18949,6 +18948,15 @@ Expect to Parse: "language/statements/function/S14_A5_T2.js"
· ╰── Invalid Character `ⸯ`
╰────
× Identifier `x` has already been declared
╭─[language/import/dup-bound-names.js:15:1]
15 │
16 │ import { x, y as x } from 'z';
· ┬ ┬
· │ ╰── It can not be redeclared here
· ╰── `x` has already been declared here
╰────
× Keywords cannot contain escape characters
╭─[language/import/escaped-as-import-specifier.js:25:1]
25 │