From 995e7c17672f1ec650471ab069e2ab0b812aff2c Mon Sep 17 00:00:00 2001 From: Boshen Date: Sat, 11 Mar 2023 14:06:22 +0800 Subject: [PATCH] feat(linter): bind `CatchVariable` --- crates/oxc_semantic/src/binder.rs | 27 +++++++++ crates/oxc_semantic/src/builder.rs | 3 + crates/oxc_semantic/src/symbol/mod.rs | 1 + tasks/coverage/babel.snap | 83 ++++++++++++++++++++++++--- tasks/coverage/test262.snap | 22 ++++++- tasks/coverage/typescript.snap | 44 ++++++++++++-- 6 files changed, 164 insertions(+), 16 deletions(-) diff --git a/crates/oxc_semantic/src/binder.rs b/crates/oxc_semantic/src/binder.rs index 8da52e987..3c32e1ac3 100644 --- a/crates/oxc_semantic/src/binder.rs +++ b/crates/oxc_semantic/src/binder.rs @@ -93,3 +93,30 @@ impl<'a> Binder for FormalParameters<'a> { } } } + +impl<'a> Binder for CatchClause<'a> { + fn bind(&self, builder: &mut SemanticBuilder) { + let current_scope_id = builder.scope.current_scope_id; + if let Some(param) = &self.param { + // https://tc39.es/ecma262/#sec-variablestatements-in-catch-blocks + // It is a Syntax Error if any element of the BoundNames of CatchParameter also occurs in the VarDeclaredNames of Block + // unless CatchParameter is CatchParameter : BindingIdentifier + if let BindingPatternKind::BindingIdentifier(ident) = ¶m.kind { + let includes = SymbolFlags::FunctionScopedVariable | SymbolFlags::CatchVariable; + // Overshadows declarations so redeclarator error is not reported here + let symbol_id = builder.symbols.create(ident.name.clone(), ident.span, includes); + builder.scope.current_scope_mut().variables.insert(ident.name.clone(), symbol_id); + } else { + for ident in param.bound_names() { + builder.declare_symbol( + &ident.name, + ident.span, + current_scope_id, + SymbolFlags::BlockScopedVariable | SymbolFlags::CatchVariable, + SymbolFlags::BlockScopedVariableExcludes, + ); + } + } + } + } +} diff --git a/crates/oxc_semantic/src/builder.rs b/crates/oxc_semantic/src/builder.rs index 9c3cb5fa5..7a73a2314 100644 --- a/crates/oxc_semantic/src/builder.rs +++ b/crates/oxc_semantic/src/builder.rs @@ -181,6 +181,9 @@ impl<'a> SemanticBuilder<'a> { AstKind::FormalParameters(params) => { params.bind(self); } + AstKind::CatchClause(clause) => { + clause.bind(self); + } AstKind::IdentifierReference(ident) => { self.reference_identifier(ident); } diff --git a/crates/oxc_semantic/src/symbol/mod.rs b/crates/oxc_semantic/src/symbol/mod.rs index 747b6f894..032fe2151 100644 --- a/crates/oxc_semantic/src/symbol/mod.rs +++ b/crates/oxc_semantic/src/symbol/mod.rs @@ -38,6 +38,7 @@ bitflags! { /// A block-scoped variable (let or const) const BlockScopedVariable = 1 << 1; const Class = 1 << 5; + const CatchVariable = 1 << 6; // try {} catch(catch_variable) {} const Variable = Self::FunctionScopedVariable.bits | Self::BlockScopedVariable.bits; const Value = Self::Variable.bits | Self::Class.bits; diff --git a/tasks/coverage/babel.snap b/tasks/coverage/babel.snap index c936df64c..0dc997d8a 100644 --- a/tasks/coverage/babel.snap +++ b/tasks/coverage/babel.snap @@ -1,7 +1,7 @@ Babel Summary: AST Parsed : 2056/2069 (99.37%) Positive Passed: 2056/2069 (99.37%) -Negative Passed: 965/1502 (64.25%) +Negative Passed: 972/1502 (64.71%) 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" @@ -9,10 +9,8 @@ Expect Syntax Error: "annex-b/disabled/3.1-sloppy-labeled-functions/input.js" Expect Syntax Error: "annex-b/disabled/3.2.4-duplicate-function-in-block/input.js" Expect Syntax Error: "annex-b/disabled/3.2.5-duplicate-function-in-switch/input.js" Expect Syntax Error: "annex-b/disabled/3.3-function-in-if-body/input.js" -Expect Syntax Error: "annex-b/disabled/3.4-var-redeclaration-catch-binding/input.js" Expect Syntax Error: "annex-b/disabled/3.5-for-in-initializer/input.js" Expect Syntax Error: "annex-b/enabled/3.1-sloppy-labeled-functions-if-body/input.js" -Expect Syntax Error: "annex-b/enabled/3.4-var-redeclaration-catch-binding/input.js" Expect Syntax Error: "core/categorized/invalid-fn-decl-inside-loop/input.js" Expect Syntax Error: "core/categorized/invalid-fn-decl-labeled-inside-if/input.js" Expect Syntax Error: "core/categorized/invalid-fn-decl-labeled-inside-loop/input.js" @@ -26,12 +24,7 @@ Expect Syntax Error: "core/legacy-octal/legacy-octal-after-use-strict-function/i 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/dupl-bind-catch-arr-destr/input.js" Expect Syntax Error: "core/scope/dupl-bind-catch-func/input.js" -Expect Syntax Error: "core/scope/dupl-bind-catch-let/input.js" -Expect Syntax Error: "core/scope/dupl-bind-catch-obj-destr/input.js" -Expect Syntax Error: "core/scope/dupl-bind-catch-var-arr-destr/input.js" -Expect Syntax Error: "core/scope/dupl-bind-catch-var-obj-destr/input.js" Expect Syntax Error: "core/scope/dupl-bind-class-func/input.js" Expect Syntax Error: "core/scope/dupl-bind-func-gen/input.js" Expect Syntax Error: "core/scope/dupl-bind-func-module-sloppy/input.js" @@ -649,6 +642,24 @@ Expect to Parse: "typescript/types/const-type-parameters/input.ts" 2 │ function b() {} ╰──── + × Identifier `"f"` has already been declared + ╭─[annex-b/disabled/3.4-var-redeclaration-catch-binding/input.js:1:1] + 1 │ try {} catch (e) { var e; } + 2 │ try {} catch ({ f }) { var f; } + · ┬ ┬ + · │ ╰── It can not be redeclared here + · ╰── `f` has already been declared here + ╰──── + + × Identifier `"f"` has already been declared + ╭─[annex-b/enabled/3.4-var-redeclaration-catch-binding/input.js:1:1] + 1 │ try {} catch (e) { var e; } + 2 │ try {} catch ({ f }) { var f; } + · ┬ ┬ + · │ ╰── It can not be redeclared here + · ╰── `f` has already been declared here + ╰──── + × Expect token ╭─[core/categorized/for-missing-semicolons/input.js:2:1] 2 │ var a = 1 @@ -915,6 +926,16 @@ Expect to Parse: "typescript/types/const-type-parameters/input.ts" · ╰── It can not be redeclared here ╰──── + × Identifier `"foo"` has already been declared + ╭─[core/scope/dupl-bind-catch-arr-destr/input.js:1:1] + 1 │ try { + 2 │ } catch ([foo, foo]) { + · ─┬─ ─┬─ + · │ ╰── It can not be redeclared here + · ╰── `foo` has already been declared here + 3 │ } + ╰──── + × Identifier `"foo"` has already been declared ╭─[core/scope/dupl-bind-catch-dbl-let/input.js:1:1] 1 │ let foo; try {} catch (foo) {} let foo; @@ -923,6 +944,52 @@ Expect to Parse: "typescript/types/const-type-parameters/input.ts" · ╰── `foo` has already been declared here ╰──── + × Identifier `"foo"` has already been declared + ╭─[core/scope/dupl-bind-catch-let/input.js:1:1] + 1 │ try { + 2 │ } catch (foo) { + · ─┬─ + · ╰── `foo` has already been declared here + 3 │ let foo; + · ─┬─ + · ╰── It can not be redeclared here + 4 │ } + ╰──── + + × Identifier `"foo"` has already been declared + ╭─[core/scope/dupl-bind-catch-obj-destr/input.js:1:1] + 1 │ try { + 2 │ } catch ({ a: foo, b: { c: [foo] } }) { + · ─┬─ ─┬─ + · │ ╰── It can not be redeclared here + · ╰── `foo` has already been declared here + 3 │ } + ╰──── + + × Identifier `"foo"` has already been declared + ╭─[core/scope/dupl-bind-catch-var-arr-destr/input.js:1:1] + 1 │ try { + 2 │ } catch ([foo]) { + · ─┬─ + · ╰── `foo` has already been declared here + 3 │ var foo; + · ─┬─ + · ╰── It can not be redeclared here + 4 │ } + ╰──── + + × Identifier `"foo"` has already been declared + ╭─[core/scope/dupl-bind-catch-var-obj-destr/input.js:1:1] + 1 │ try { + 2 │ } catch ({ foo }) { + · ─┬─ + · ╰── `foo` has already been declared here + 3 │ var foo; + · ─┬─ + · ╰── It can not be redeclared here + 4 │ } + ╰──── + × Identifier `"foo"` has already been declared ╭─[core/scope/dupl-bind-class-class/input.js:1:1] 1 │ class foo {}; diff --git a/tasks/coverage/test262.snap b/tasks/coverage/test262.snap index 9051f0c69..d0e49647d 100644 --- a/tasks/coverage/test262.snap +++ b/tasks/coverage/test262.snap @@ -1,7 +1,7 @@ Test262 Summary: AST Parsed : 44022/44034 (99.97%) Positive Passed: 44022/44034 (99.97%) -Negative Passed: 2694/3917 (68.78%) +Negative Passed: 2696/3917 (68.83%) Expect Syntax Error: "annexB/language/statements/for-in/const-initializer.js" Expect Syntax Error: "annexB/language/statements/for-in/let-initializer.js" Expect Syntax Error: "annexB/language/statements/for-in/strict-initializer.js" @@ -1178,9 +1178,7 @@ Expect Syntax Error: "language/statements/try/catch-parameter-boundnames-restric Expect Syntax Error: "language/statements/try/dstr/ary-ptrn-rest-init-ary.js" Expect Syntax Error: "language/statements/try/dstr/ary-ptrn-rest-init-id.js" Expect Syntax Error: "language/statements/try/dstr/ary-ptrn-rest-init-obj.js" -Expect Syntax Error: "language/statements/try/early-catch-duplicates.js" Expect Syntax Error: "language/statements/try/early-catch-function.js" -Expect Syntax Error: "language/statements/try/early-catch-lex.js" Expect Syntax Error: "language/statements/try/static-init-await-binding-invalid.js" Expect Syntax Error: "language/statements/variable/12.2.1-1gs.js" Expect Syntax Error: "language/statements/variable/12.2.1-4gs.js" @@ -24397,6 +24395,24 @@ Expect to Parse: "language/statements/class/decorator/syntax/valid/decorator-par 33 │ ╰──── + × Identifier `"x"` has already been declared + ╭─[language/statements/try/early-catch-duplicates.js:15:1] + 15 │ + 16 │ try { } catch ([x, x]) {} + · ┬ ┬ + · │ ╰── It can not be redeclared here + · ╰── `x` has already been declared here + ╰──── + + × Identifier `"x"` has already been declared + ╭─[language/statements/try/early-catch-lex.js:16:1] + 16 │ + 17 │ try { } catch (x) { let x; } + · ┬ ┬ + · │ ╰── It can not be redeclared here + · ╰── `x` has already been declared here + ╰──── + × Unexpected token ╭─[language/statements/try/optional-catch-binding-parens.js:19:1] 19 │ diff --git a/tasks/coverage/typescript.snap b/tasks/coverage/typescript.snap index 6fcf83cd2..928ee6a2c 100644 --- a/tasks/coverage/typescript.snap +++ b/tasks/coverage/typescript.snap @@ -1,7 +1,7 @@ TypeScript Summary: -AST Parsed : 2306/2338 (98.63%) -Positive Passed: 2306/2338 (98.63%) -Negative Passed: 587/2532 (23.18%) +AST Parsed : 2305/2338 (98.59%) +Positive Passed: 2305/2338 (98.59%) +Negative Passed: 589/2532 (23.26%) Expect Syntax Error: "Symbols/ES5SymbolProperty2.ts" Expect Syntax Error: "Symbols/ES5SymbolProperty6.ts" Expect Syntax Error: "additionalChecks/noPropertyAccessFromIndexSignature1.ts" @@ -998,7 +998,6 @@ Expect Syntax Error: "jsdoc/jsdocAugments_errorInExtendsExpression.ts" Expect Syntax Error: "jsdoc/jsdocAugments_nameMismatch.ts" Expect Syntax Error: "jsdoc/jsdocAugments_noExtends.ts" Expect Syntax Error: "jsdoc/jsdocAugments_notAClass.ts" -Expect Syntax Error: "jsdoc/jsdocCatchClauseWithTypeAnnotation.ts" Expect Syntax Error: "jsdoc/jsdocFunctionType.ts" Expect Syntax Error: "jsdoc/jsdocFunction_missingReturn.ts" Expect Syntax Error: "jsdoc/jsdocImplements_class.ts" @@ -1561,7 +1560,6 @@ Expect Syntax Error: "statements/for-ofStatements/ES5For-ofTypeCheck9.ts" Expect Syntax Error: "statements/forStatements/forStatementsMultipleInvalidDecl.ts" Expect Syntax Error: "statements/returnStatements/invalidReturnStatements.ts" Expect Syntax Error: "statements/switchStatements/switchStatements.ts" -Expect Syntax Error: "statements/tryStatements/catchClauseWithTypeAnnotation.ts" Expect Syntax Error: "types/any/anyAsConstructor.ts" Expect Syntax Error: "types/any/anyAsGenericFunctionCall.ts" Expect Syntax Error: "types/any/assignAnyToEveryType.ts" @@ -1947,6 +1945,19 @@ Expect Syntax Error: "types/unknown/unknownControlFlow.ts" Expect Syntax Error: "types/unknown/unknownType1.ts" Expect Syntax Error: "types/unknown/unknownType2.ts" Expect Syntax Error: "types/witness/witness.ts" +Expect to Parse: "async/es6/asyncWithVarShadowing_es6.ts" + + × Identifier `"x"` has already been declared + ╭─[async/es6/asyncWithVarShadowing_es6.ts:131:1] + 131 │ } + 132 │ catch ({ x }) { + · ┬ + · ╰── `x` has already been declared here + 133 │ var x; + · ┬ + · ╰── It can not be redeclared here + 134 │ } + ╰──── Expect to Parse: "classes/propertyMemberDeclarations/autoAccessor4.ts" × Automatic Semicolon Insertion @@ -6104,6 +6115,19 @@ Expect to Parse: "salsa/privateIdentifierExpando.ts" 4 │ } ╰──── + × Identifier `"err"` has already been declared + ╭─[jsdoc/jsdocCatchClauseWithTypeAnnotation.ts:39:1] + 39 │ try { } + 40 │ catch (err) { + · ─┬─ + · ╰── `err` has already been declared here + 41 │ /** @type {string} */ + 42 │ let err; + · ─┬─ + · ╰── It can not be redeclared here + 43 │ } + ╰──── + × Unexpected token ╭─[jsdoc/jsdocDisallowedInTypescript.ts:3:1] 3 │ // grammar error from checker @@ -8873,6 +8897,16 @@ Expect to Parse: "salsa/privateIdentifierExpando.ts" 4 │ ╰──── + × Identifier `"x"` has already been declared + ╭─[statements/tryStatements/catchClauseWithTypeAnnotation.ts:28:1] + 28 │ // minor bug: shows that the `catch` argument is skipped when checking scope + 29 │ try { } catch (x) { let x: string; } + · ┬ ┬ + · │ ╰── It can not be redeclared here + · ╰── `x` has already been declared here + 30 │ try { } catch (x) { var x: string; } + ╰──── + × Unexpected token ╭─[statements/tryStatements/invalidTryStatements.ts:1:1] 1 │ function fn() {