feat(es/lints): Support module/script mode in duplicate_bindings (#3880)

Co-authored-by: Donny/강동윤 <kdy1997.dev@gmail.com>
This commit is contained in:
magic-akari 2022-03-07 13:49:39 +08:00 committed by GitHub
parent c7d21da458
commit 0181fbe63b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 205 additions and 48 deletions

View File

@ -94,9 +94,7 @@ jobs:
# Download three.js
git clone --depth 1 https://github.com/mrdoob/three.js.git -b r117 tests/integration/three-js/repo
swc --sync tests/integration/three-js/repo/ -d tests/integration/three-js/build/
# swc tests/integration/three-js/repo/src/ -d tests/integration/three-js/repo/build/
# swc tests/integration/three-js/repo/test/unit/**/*.js -d tests/integration/three-js/repo/test/unit/build/
swc -C isModule=unknown --sync tests/integration/three-js/repo/ -d tests/integration/three-js/build/
(cd tests/integration/three-js/build/test && qunit -r failonlyreporter unit/three.source.unit.js)

View File

@ -62,7 +62,7 @@ fn fixture(input: PathBuf) {
handler,
&Options {
swcrc: true,
is_module: IsModule::Bool(true),
is_module: IsModule::Unknown,
..Default::default()
},

View File

@ -1,2 +0,0 @@
const a = 5;
let a = 2;

View File

@ -1,12 +0,0 @@
error: Duplicate binding
|
2 | let a = 2;
| ^
|
note: a was declared at here
|
1 | const a = 5;
| ^

View File

@ -0,0 +1,10 @@
function foo() {}
const foo = 1; // error
function bar() {}
var bar; // error
function baz() {}
function baz() {} // error
export {};

View File

@ -0,0 +1,36 @@
error: Duplicate binding
|
2 | const foo = 1; // error
| ^^^
|
note: foo was declared at here
|
1 | function foo() {}
| ^^^
error: Duplicate binding
|
5 | var bar; // error
| ^^^
|
note: bar was declared at here
|
4 | function bar() {}
| ^^^
error: Duplicate binding
|
8 | function baz() {} // error
| ^^^
|
note: baz was declared at here
|
7 | function baz() {}
| ^^^

View File

@ -0,0 +1,4 @@
import { hi } from "foo";
function foo() {}
function foo() {}

View File

@ -0,0 +1,12 @@
error: Duplicate binding
|
4 | function foo() {}
| ^^^
|
note: foo was declared at here
|
3 | function foo() {}
| ^^^

View File

@ -0,0 +1,11 @@
const a = 5;
let a = 2; // error
const b = 1;
var b = 2; // error
let c = 3;
var c = 4; // error
var d = 1;
var d = 1; // ok

View File

@ -0,0 +1,36 @@
error: Duplicate binding
|
2 | let a = 2; // error
| ^
|
note: a was declared at here
|
1 | const a = 5;
| ^
error: Duplicate binding
|
5 | var b = 2; // error
| ^
|
note: b was declared at here
|
4 | const b = 1;
| ^
error: Duplicate binding
|
8 | var c = 4; // error
| ^
|
note: c was declared at here
|
7 | let c = 3;
| ^

View File

@ -0,0 +1,8 @@
function foo() {}
const foo = 1; // error
function bar() {}
var bar; // ok
function baz() {}
function baz() {} // ok

View File

@ -0,0 +1,12 @@
error: Duplicate binding
|
2 | const foo = 1; // error
| ^^^
|
note: foo was declared at here
|
1 | function foo() {}
| ^^^

View File

@ -1,3 +1,15 @@
error: Duplicate binding
|
56 | var a = 0;
| ^
|
note: a was declared at here
|
36 | let a; for (let i in {}) { (function() { a; }); a = 1; }
| ^
error: Duplicate binding
|

View File

@ -1,7 +1,10 @@
use std::collections::hash_map::Entry;
use ahash::AHashSet;
use swc_common::{collections::AHashMap, errors::HANDLER, Span};
use swc_common::{
collections::{AHashMap, AHashSet},
errors::HANDLER,
Span,
};
use swc_ecma_ast::*;
use swc_ecma_utils::ident::IdentLike;
use swc_ecma_visit::{noop_visit_type, Visit, VisitWith};
@ -12,41 +15,61 @@ pub fn duplicate_bindings() -> Box<dyn Rule> {
visitor_rule(DuplicateBindings::default())
}
#[derive(Debug, Default, Clone, Copy)]
struct BindingInfo {
span: Span,
unique: bool,
}
#[derive(Debug, Default)]
struct DuplicateBindings {
bindings: AHashMap<Id, Span>,
bindings: AHashMap<Id, BindingInfo>,
type_bindings: AHashSet<Id>,
var_decl_kind: Option<VarDeclKind>,
is_pat_decl: bool,
is_module: bool,
}
impl DuplicateBindings {
/// Add a binding.
fn add(&mut self, id: &Ident, check_for_var_kind: bool) {
if check_for_var_kind {
if let Some(VarDeclKind::Var) = self.var_decl_kind {
return;
}
}
fn add(&mut self, id: &Ident, unique: bool) {
match self.bindings.entry(id.to_id()) {
Entry::Occupied(mut prev) => {
HANDLER.with(|handler| {
handler
.struct_span_err(id.span, "Duplicate binding")
.span_note(*prev.get(), &format!("{} was declared at here", id.sym))
.emit();
});
if unique || prev.get().unique {
HANDLER.with(|handler| {
handler
.struct_span_err(id.span, "Duplicate binding")
.span_note(prev.get().span, &format!("{} was declared at here", id.sym))
.emit();
});
}
// Next span.
*prev.get_mut() = id.span;
if unique || !prev.get().unique {
*prev.get_mut() = BindingInfo {
span: id.span,
unique,
}
}
}
Entry::Vacant(e) => {
e.insert(id.span);
e.insert(BindingInfo {
span: id.span,
unique,
});
}
}
}
/// `const` or `let`
fn is_unique_var_kind(&self) -> bool {
matches!(
self.var_decl_kind,
Some(VarDeclKind::Const) | Some(VarDeclKind::Let)
)
}
}
impl Visit for DuplicateBindings {
@ -57,6 +80,7 @@ impl Visit for DuplicateBindings {
type_bindings: &mut self.type_bindings,
});
self.is_module = true;
m.visit_children_with(self);
}
@ -72,12 +96,12 @@ impl Visit for DuplicateBindings {
p.visit_children_with(self);
if self.is_pat_decl {
self.add(&p.key, true);
self.add(&p.key, self.is_unique_var_kind());
}
}
fn visit_class_decl(&mut self, d: &ClassDecl) {
self.add(&d.ident, false);
self.add(&d.ident, true);
d.visit_children_with(self);
}
@ -97,7 +121,7 @@ impl Visit for DuplicateBindings {
fn visit_fn_decl(&mut self, d: &FnDecl) {
if d.function.body.is_some() {
self.add(&d.ident, false);
self.add(&d.ident, self.is_module);
}
d.visit_children_with(self);
@ -107,7 +131,7 @@ impl Visit for DuplicateBindings {
s.visit_children_with(self);
if !self.type_bindings.contains(&s.local.to_id()) {
self.add(&s.local, false);
self.add(&s.local, true);
}
}
@ -115,7 +139,7 @@ impl Visit for DuplicateBindings {
s.visit_children_with(self);
if !s.is_type_only && !self.type_bindings.contains(&s.local.to_id()) {
self.add(&s.local, false);
self.add(&s.local, true);
}
}
@ -123,7 +147,7 @@ impl Visit for DuplicateBindings {
s.visit_children_with(self);
if !self.type_bindings.contains(&s.local.to_id()) {
self.add(&s.local, false);
self.add(&s.local, true);
}
}
@ -132,7 +156,7 @@ impl Visit for DuplicateBindings {
if let Pat::Ident(p) = p {
if self.is_pat_decl {
self.add(&p.id, true);
self.add(&p.id, self.is_unique_var_kind());
}
}
}

View File

@ -40,21 +40,19 @@ fn pass(input: PathBuf) {
let mut parser = Parser::new_from(lexer);
let mut m = parser.parse_module().unwrap();
let mut program = parser.parse_program().unwrap();
let top_level_mark = Mark::fresh(Mark::root());
if input.extension().unwrap() == "ts" || input.extension().unwrap() == "tsx" {
m.visit_mut_with(&mut ts_resolver(top_level_mark));
program.visit_mut_with(&mut ts_resolver(top_level_mark));
} else {
m.visit_mut_with(&mut resolver_with_mark(top_level_mark));
program.visit_mut_with(&mut resolver_with_mark(top_level_mark));
}
let top_level_ctxt = SyntaxContext::empty().apply_mark(top_level_mark);
let config = LintConfig::default();
let program = Program::Module(m);
let rules = all(LintParams {
program: &program,
lint_config: &config,

View File

@ -0,0 +1,10 @@
function sum(i, j) {
return i + j;
}
function sum() {
return 0;
}
console.log(sum());
console.log(sum(1, 2));