mirror of
https://github.com/swc-project/swc.git
synced 2024-11-23 09:38:16 +03:00
fix(es/lints): Fix incorrect duplicate binding error (#3194)
swc_ecma_transforms_base: - `resolver`: Fix resolving of `const` and `let`. swc_ecma_lints: - `duplicate_bindings`: Don't mark ambient function declararions as a binding. (Closes #3193)
This commit is contained in:
parent
e2b5a31882
commit
913c82a2ab
4
Cargo.lock
generated
4
Cargo.lock
generated
@ -2998,8 +2998,12 @@ dependencies = [
|
||||
"swc_atoms",
|
||||
"swc_common",
|
||||
"swc_ecma_ast",
|
||||
"swc_ecma_codegen",
|
||||
"swc_ecma_parser",
|
||||
"swc_ecma_transforms_base",
|
||||
"swc_ecma_utils",
|
||||
"swc_ecma_visit",
|
||||
"testing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -17,3 +17,9 @@ swc_common = {version = "0.16.0", path = "../swc_common"}
|
||||
swc_ecma_ast = {version = "0.62.0", path = "../swc_ecma_ast"}
|
||||
swc_ecma_utils = {version = "0.59.0", path = "../swc_ecma_utils"}
|
||||
swc_ecma_visit = {version = "0.48.0", path = "../swc_ecma_visit"}
|
||||
|
||||
[dev-dependencies]
|
||||
swc_ecma_codegen = {version = "0.86.0", path = "../swc_ecma_codegen"}
|
||||
swc_ecma_parser = {version = "0.84.0", path = "../swc_ecma_parser"}
|
||||
swc_ecma_transforms_base = {version = "0.52.0", path = "../swc_ecma_transforms_base"}
|
||||
testing = {version = "0.17.0", path = "../testing"}
|
||||
|
@ -77,7 +77,9 @@ impl Visit for DuplicateBindings {
|
||||
}
|
||||
|
||||
fn visit_fn_decl(&mut self, d: &FnDecl) {
|
||||
self.add(&d.ident, false);
|
||||
if d.function.body.is_some() {
|
||||
self.add(&d.ident, false);
|
||||
}
|
||||
|
||||
d.visit_children_with(self);
|
||||
}
|
||||
|
57
crates/swc_ecma_lints/tests/fixture.rs
Normal file
57
crates/swc_ecma_lints/tests/fixture.rs
Normal file
@ -0,0 +1,57 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use swc_common::input::SourceFileInput;
|
||||
use swc_ecma_ast::EsVersion;
|
||||
use swc_ecma_lints::rules::all;
|
||||
use swc_ecma_parser::{lexer::Lexer, Parser, Syntax};
|
||||
use swc_ecma_transforms_base::resolver::resolver;
|
||||
use swc_ecma_utils::HANDLER;
|
||||
use swc_ecma_visit::VisitMutWith;
|
||||
|
||||
#[testing::fixture("tests/pass/**/input.js")]
|
||||
#[testing::fixture("tests/pass/**/input.ts")]
|
||||
fn pass(input: PathBuf) {
|
||||
testing::run_test(false, |cm, handler| {
|
||||
let fm = cm.load_file(&input).unwrap();
|
||||
|
||||
let lexer = Lexer::new(
|
||||
if input.extension().unwrap() == "ts" {
|
||||
Syntax::Typescript(swc_ecma_parser::TsConfig {
|
||||
..Default::default()
|
||||
})
|
||||
} else if input.extension().unwrap() == "tsx" {
|
||||
Syntax::Typescript(swc_ecma_parser::TsConfig {
|
||||
tsx: true,
|
||||
..Default::default()
|
||||
})
|
||||
} else {
|
||||
Syntax::Es(swc_ecma_parser::EsConfig {
|
||||
..Default::default()
|
||||
})
|
||||
},
|
||||
EsVersion::latest(),
|
||||
SourceFileInput::from(&*fm),
|
||||
None,
|
||||
);
|
||||
|
||||
let mut parser = Parser::new_from(lexer);
|
||||
|
||||
let mut m = parser.parse_module().unwrap();
|
||||
m.visit_mut_with(&mut resolver());
|
||||
|
||||
let rules = all();
|
||||
|
||||
HANDLER.set(&handler, || {
|
||||
for mut rule in rules {
|
||||
rule.lint_module(&m);
|
||||
}
|
||||
});
|
||||
|
||||
if handler.has_errors() {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
}
|
3
crates/swc_ecma_lints/tests/pass/issue-3193/1/input.ts
Normal file
3
crates/swc_ecma_lints/tests/pass/issue-3193/1/input.ts
Normal file
@ -0,0 +1,3 @@
|
||||
function test();
|
||||
function test(a, b) {
|
||||
}
|
25
crates/swc_ecma_lints/tests/pass/vercel/1/input.js
Normal file
25
crates/swc_ecma_lints/tests/pass/vercel/1/input.js
Normal file
@ -0,0 +1,25 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var n = t(3957);
|
||||
function o(e) {
|
||||
const r = e.nextUrl;
|
||||
|
||||
|
||||
if ('/log' !== r.pathname) {
|
||||
|
||||
|
||||
if ('/throw-error-internal' === r.pathname) {
|
||||
function r() {
|
||||
return e();
|
||||
}
|
||||
try {
|
||||
r();
|
||||
} catch (o) {
|
||||
console.error(o);
|
||||
}
|
||||
return new Promise((e, r) => r(new Error('oh no!')));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -5,11 +5,12 @@ use swc_common::{collections::AHashSet, Mark, SyntaxContext};
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_utils::{find_ids, Id};
|
||||
use swc_ecma_visit::{as_folder, noop_visit_mut_type, Fold, VisitMut, VisitMutWith};
|
||||
use tracing::{debug, span, Level};
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
const LOG: bool = false;
|
||||
const LOG: bool = false && cfg!(debug_assertions);
|
||||
|
||||
/// See [resolver_with_mark] for docs.
|
||||
pub fn resolver() -> impl 'static + Fold + VisitMut {
|
||||
@ -119,6 +120,14 @@ impl<'a> Scope<'a> {
|
||||
declared_types: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_declared(&self, symbol: &JsWord) -> bool {
|
||||
if self.declared_symbols.contains(symbol) {
|
||||
return true;
|
||||
}
|
||||
|
||||
self.parent.map_or(false, |p| p.is_declared(symbol))
|
||||
}
|
||||
}
|
||||
|
||||
/// # Phases
|
||||
@ -211,8 +220,8 @@ impl<'a> Resolver<'a> {
|
||||
/// Modifies a binding identifier.
|
||||
fn modify(&mut self, ident: &mut Ident, kind: Option<VarDeclKind>) {
|
||||
if cfg!(debug_assertions) && LOG {
|
||||
eprintln!(
|
||||
"resolver: Binding (type = {}) {}{:?} {:?}",
|
||||
debug!(
|
||||
"Binding (type = {}) {}{:?} {:?}",
|
||||
self.in_type,
|
||||
ident.sym,
|
||||
ident.span.ctxt(),
|
||||
@ -233,7 +242,7 @@ impl<'a> Resolver<'a> {
|
||||
} else {
|
||||
let span = ident.span.apply_mark(mark);
|
||||
if cfg!(debug_assertions) && LOG {
|
||||
eprintln!("\t-> {:?}", span.ctxt());
|
||||
debug!("\t-> {:?}", span.ctxt());
|
||||
}
|
||||
span
|
||||
};
|
||||
@ -317,7 +326,7 @@ impl<'a> Resolver<'a> {
|
||||
} else {
|
||||
let span = ident.span.apply_mark(mark);
|
||||
if cfg!(debug_assertions) && LOG {
|
||||
eprintln!("\t-> {:?}", span.ctxt());
|
||||
debug!("\t-> {:?}", span.ctxt());
|
||||
}
|
||||
span
|
||||
};
|
||||
@ -615,6 +624,12 @@ impl<'a> VisitMut for Resolver<'a> {
|
||||
}
|
||||
|
||||
fn visit_mut_expr(&mut self, expr: &mut Expr) {
|
||||
let _span = if LOG {
|
||||
Some(span!(Level::ERROR, "visit_mut_expr").entered())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
self.in_type = false;
|
||||
let old = self.ident_type;
|
||||
self.ident_type = IdentType::Ref;
|
||||
@ -726,8 +741,8 @@ impl<'a> VisitMut for Resolver<'a> {
|
||||
let Ident { span, sym, .. } = i;
|
||||
|
||||
if cfg!(debug_assertions) && LOG {
|
||||
eprintln!(
|
||||
"resolver: IdentRef (type = {}) {}{:?}",
|
||||
debug!(
|
||||
"IdentRef (type = {}) {}{:?}",
|
||||
self.in_type,
|
||||
sym,
|
||||
span.ctxt()
|
||||
@ -742,12 +757,12 @@ impl<'a> VisitMut for Resolver<'a> {
|
||||
let span = span.apply_mark(mark);
|
||||
|
||||
if cfg!(debug_assertions) && LOG {
|
||||
eprintln!("\t -> {:?}", span.ctxt());
|
||||
debug!("\t -> {:?}", span.ctxt());
|
||||
}
|
||||
i.span = span;
|
||||
} else {
|
||||
if cfg!(debug_assertions) && LOG {
|
||||
eprintln!("\t -> Unresolved");
|
||||
debug!("\t -> Unresolved");
|
||||
}
|
||||
|
||||
let mark = {
|
||||
@ -771,7 +786,7 @@ impl<'a> VisitMut for Resolver<'a> {
|
||||
let span = span.apply_mark(mark);
|
||||
|
||||
if cfg!(debug_assertions) && LOG {
|
||||
eprintln!("\t -> {:?}", span.ctxt());
|
||||
debug!("\t -> {:?}", span.ctxt());
|
||||
}
|
||||
|
||||
i.span = span;
|
||||
@ -917,8 +932,20 @@ impl<'a> VisitMut for Resolver<'a> {
|
||||
}
|
||||
|
||||
fn visit_mut_stmts(&mut self, stmts: &mut Vec<Stmt>) {
|
||||
let _span = if LOG {
|
||||
Some(span!(Level::ERROR, "visit_mut_stmts").entered())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Phase 1: Handle hoisting
|
||||
{
|
||||
let _span = if LOG {
|
||||
Some(span!(Level::ERROR, "hoist").entered())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut hoister = Hoister {
|
||||
resolver: self,
|
||||
kind: None,
|
||||
@ -1386,6 +1413,21 @@ impl VisitMut for Hoister<'_, '_> {
|
||||
if self.catch_param_decls.contains(&node.ident.sym) {
|
||||
return;
|
||||
}
|
||||
let _span = if LOG {
|
||||
Some(span!(Level::ERROR, "Hoister.visit_mut_fn_decl").entered())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if self.in_block {
|
||||
// If we are in nested block, and variable named `foo` is declared, we should
|
||||
// ignore function foo while handling upper scopes.
|
||||
let i = node.ident.clone();
|
||||
|
||||
if self.resolver.current.is_declared(&i.sym) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
self.resolver.in_type = false;
|
||||
self.resolver
|
||||
|
@ -0,0 +1,8 @@
|
||||
{
|
||||
let a = 1;
|
||||
const b = 1;
|
||||
var c = 1;
|
||||
}
|
||||
console.log(a);
|
||||
console.log(b);
|
||||
console.log(c);
|
@ -0,0 +1,7 @@
|
||||
{
|
||||
let a__2 = 1;
|
||||
const b__2 = 1;
|
||||
var c = 1;
|
||||
}console.log(a);
|
||||
console.log(b);
|
||||
console.log(c);
|
@ -0,0 +1,23 @@
|
||||
|
||||
var n = t(3957);
|
||||
function o(e) {
|
||||
const r = e.nextUrl;
|
||||
|
||||
|
||||
if ('/log' !== r.pathname) {
|
||||
|
||||
|
||||
if ('/throw-error-internal' === r.pathname) {
|
||||
function r() {
|
||||
return e();
|
||||
}
|
||||
try {
|
||||
r();
|
||||
} catch (o) {
|
||||
console.error(o);
|
||||
}
|
||||
return new Promise((e, r) => r(new Error('oh no!')));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
var n = t(3957);
|
||||
function o(e__2) {
|
||||
const r__2 = e__2.nextUrl;
|
||||
if ('/log' !== r__2.pathname) {
|
||||
if ('/throw-error-internal' === r__2.pathname) {
|
||||
function r__3() {
|
||||
return e__2();
|
||||
}
|
||||
try {
|
||||
r__3();
|
||||
} catch (o__4) {
|
||||
console.error(o__4);
|
||||
}
|
||||
return new Promise((e__5, r__5)=>r__5(new Error('oh no!'))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user