From f866781f49ba6ce3f8f1d0b12f0b4c0a11f3ce15 Mon Sep 17 00:00:00 2001 From: DonIsaac <22823424+DonIsaac@users.noreply.github.com> Date: Wed, 25 Sep 2024 02:51:17 +0000 Subject: [PATCH] feat(semantic): check for type annotations on left side of `for..in` and `for..of` iterators (#6043) --- crates/oxc_semantic/src/checker/mod.rs | 2 + crates/oxc_semantic/src/checker/typescript.rs | 24 ++++ .../coverage/snapshots/parser_typescript.snap | 107 ++++++++++++++++-- 3 files changed, 126 insertions(+), 7 deletions(-) diff --git a/crates/oxc_semantic/src/checker/mod.rs b/crates/oxc_semantic/src/checker/mod.rs index ab5baf1d63372..a21d5e9bbc07c 100644 --- a/crates/oxc_semantic/src/checker/mod.rs +++ b/crates/oxc_semantic/src/checker/mod.rs @@ -53,10 +53,12 @@ pub fn check<'a>(node: &AstNode<'a>, ctx: &SemanticBuilder<'a>) { AstKind::ForInStatement(stmt) => { js::check_function_declaration(&stmt.body, false, ctx); js::check_for_statement_left(&stmt.left, true, node, ctx); + ts::check_for_statement_left(&stmt.left, true, ctx); } AstKind::ForOfStatement(stmt) => { js::check_function_declaration(&stmt.body, false, ctx); js::check_for_statement_left(&stmt.left, false, node, ctx); + ts::check_for_statement_left(&stmt.left, false, ctx); } AstKind::WhileStatement(WhileStatement { body, .. }) | AstKind::DoWhileStatement(DoWhileStatement { body, .. }) diff --git a/crates/oxc_semantic/src/checker/typescript.rs b/crates/oxc_semantic/src/checker/typescript.rs index 86940d94eac65..ada15a0f2b37c 100644 --- a/crates/oxc_semantic/src/checker/typescript.rs +++ b/crates/oxc_semantic/src/checker/typescript.rs @@ -479,3 +479,27 @@ pub fn check_object_property(prop: &ObjectProperty, ctx: &SemanticBuilder<'_>) { } } } + +/// The left-hand side of a 'for...of' statement cannot use a type annotation. (2483) +fn type_annotation_in_for_left(span: Span, is_for_in: bool) -> OxcDiagnostic { + let for_of_or_in = if is_for_in { "for...in" } else { "for...of" }; + ts_error( + "2483", + format!( + "The left-hand side of a '{for_of_or_in}' statement cannot use a type annotation.", + ), + ).with_label(span).with_help("This iterator's type will be inferred from the iterable. You can safely remove the type annotation.") +} + +pub fn check_for_statement_left(left: &ForStatementLeft, is_for_in: bool, ctx: &SemanticBuilder) { + let ForStatementLeft::VariableDeclaration(decls) = left else { + return; + }; + + for decl in &decls.declarations { + if decl.id.type_annotation.is_some() { + let span = decl.id.span(); + ctx.error(type_annotation_in_for_left(span, is_for_in)); + } + } +} diff --git a/tasks/coverage/snapshots/parser_typescript.snap b/tasks/coverage/snapshots/parser_typescript.snap index 06b1c60713dcf..e7191d8b6ff15 100644 --- a/tasks/coverage/snapshots/parser_typescript.snap +++ b/tasks/coverage/snapshots/parser_typescript.snap @@ -3,7 +3,7 @@ commit: a709f989 parser_typescript Summary: AST Parsed : 6469/6479 (99.85%) Positive Passed: 6458/6479 (99.68%) -Negative Passed: 1228/5715 (21.49%) +Negative Passed: 1234/5715 (21.59%) Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/ClassDeclaration10.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/ClassDeclaration11.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/ClassDeclaration13.ts @@ -854,10 +854,8 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/firstMatchRe Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/fixTypeParameterInSignatureWithRestParameters.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/fixingTypeParametersRepeatedly2.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/flatArrayNoExcessiveStackDepth.ts -Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/forIn.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/forIn2.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/forInStatement2.ts -Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/forInStatement4.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/forInStatement7.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/forInStrictNullChecksNoError.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/forwardDeclaredCommonTypes01.ts @@ -3850,14 +3848,12 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ec Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserES5ForOfStatement15.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserES5ForOfStatement16.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserES5ForOfStatement20.ts -Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserES5ForOfStatement5.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserES5ForOfStatement8.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserES5ForOfStatement9.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserEmptyStatement1.d.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserExpressionStatement1.d.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserForInStatement1.d.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserForInStatement4.ts -Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserForInStatement5.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserForInStatement8.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserForStatement1.d.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserForStatement2.ts @@ -3962,7 +3958,6 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ec Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript6/Iterators/parserForOfStatement15.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript6/Iterators/parserForOfStatement16.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript6/Iterators/parserForOfStatement20.ts -Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript6/Iterators/parserForOfStatement5.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript6/Iterators/parserForOfStatement8.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript6/Iterators/parserForOfStatement9.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/pedantic/noUncheckedIndexedAccess.ts @@ -4071,7 +4066,6 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/statement Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/statements/for-inStatements/for-inStatementsDestructuring2.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/statements/for-inStatements/for-inStatementsDestructuring3.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/statements/for-inStatements/for-inStatementsDestructuring4.ts -Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/statements/for-inStatements/for-inStatementsInvalid.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/statements/for-ofStatements/ES5For-of17.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/statements/for-ofStatements/ES5For-of26.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/statements/for-ofStatements/ES5For-of27.ts @@ -7901,6 +7895,24 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/salsa/private 30 │ } ╰──── + × TS(2483): The left-hand side of a 'for...in' statement cannot use a type annotation. + ╭─[typescript/tests/cases/compiler/forIn.ts:2:10] + 1 │ var arr = null; + 2 │ for (var i:number in arr) { // error + · ──────── + 3 │ var x1 = arr[i]; + ╰──── + help: This iterator's type will be inferred from the iterable. You can safely remove the type annotation. + + × TS(2483): The left-hand side of a 'for...in' statement cannot use a type annotation. + ╭─[typescript/tests/cases/compiler/forInStatement4.ts:2:10] + 1 │ var expr: any; + 2 │ for (var a: number in expr) { + · ───────── + 3 │ } + ╰──── + help: This iterator's type will be inferred from the iterable. You can safely remove the type annotation. + × Identifier `foo3` has already been declared ╭─[typescript/tests/cases/compiler/funClodule.ts:15:10] 14 │ @@ -21644,6 +21656,14 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/salsa/private 2 │ } ╰──── + × TS(2483): The left-hand side of a 'for...of' statement cannot use a type annotation. + ╭─[typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserES5ForOfStatement5.ts:1:10] + 1 │ for (var a: number of X) { + · ───────── + 2 │ } + ╰──── + help: This iterator's type will be inferred from the iterable. You can safely remove the type annotation. + × Only a single declaration is allowed in a `for...of` statement ╭─[typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserES5ForOfStatement6.ts:1:6] 1 │ for (var a = 1, b = 2 of X) { @@ -21658,6 +21678,22 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/salsa/private 2 │ } ╰──── + × TS(2483): The left-hand side of a 'for...of' statement cannot use a type annotation. + ╭─[typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserES5ForOfStatement7.ts:1:10] + 1 │ for (var a: number = 1, b: string = "" of X) { + · ───────── + 2 │ } + ╰──── + help: This iterator's type will be inferred from the iterable. You can safely remove the type annotation. + + × TS(2483): The left-hand side of a 'for...of' statement cannot use a type annotation. + ╭─[typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserES5ForOfStatement7.ts:1:25] + 1 │ for (var a: number = 1, b: string = "" of X) { + · ───────── + 2 │ } + ╰──── + help: This iterator's type will be inferred from the iterable. You can safely remove the type annotation. + × Unexpected token ╭─[typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserForInStatement2.ts:1:10] 1 │ for (var in X) { @@ -21672,6 +21708,14 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/salsa/private 2 │ } ╰──── + × TS(2483): The left-hand side of a 'for...in' statement cannot use a type annotation. + ╭─[typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserForInStatement5.ts:1:10] + 1 │ for (var a: number in X) { + · ───────── + 2 │ } + ╰──── + help: This iterator's type will be inferred from the iterable. You can safely remove the type annotation. + × Only a single declaration is allowed in a `for...in` statement ╭─[typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserForInStatement6.ts:1:6] 1 │ for (var a = 1, b = 2 in X) { @@ -21686,6 +21730,22 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/salsa/private 2 │ } ╰──── + × TS(2483): The left-hand side of a 'for...in' statement cannot use a type annotation. + ╭─[typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserForInStatement7.ts:1:10] + 1 │ for (var a: number = 1, b: string = "" in X) { + · ───────── + 2 │ } + ╰──── + help: This iterator's type will be inferred from the iterable. You can safely remove the type annotation. + + × TS(2483): The left-hand side of a 'for...in' statement cannot use a type annotation. + ╭─[typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserForInStatement7.ts:1:25] + 1 │ for (var a: number = 1, b: string = "" in X) { + · ───────── + 2 │ } + ╰──── + help: This iterator's type will be inferred from the iterable. You can safely remove the type annotation. + × Unexpected token ╭─[typescript/tests/cases/conformance/parser/ecmascript5/Statements/parserForStatement4.ts:1:6] 1 │ for (a = 1 in b) { @@ -22151,6 +22211,14 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/salsa/private 2 │ } ╰──── + × TS(2483): The left-hand side of a 'for...of' statement cannot use a type annotation. + ╭─[typescript/tests/cases/conformance/parser/ecmascript6/Iterators/parserForOfStatement5.ts:1:10] + 1 │ for (var a: number of X) { + · ───────── + 2 │ } + ╰──── + help: This iterator's type will be inferred from the iterable. You can safely remove the type annotation. + × Only a single declaration is allowed in a `for...of` statement ╭─[typescript/tests/cases/conformance/parser/ecmascript6/Iterators/parserForOfStatement6.ts:1:6] 1 │ for (var a = 1, b = 2 of X) { @@ -22165,6 +22233,22 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/salsa/private 2 │ } ╰──── + × TS(2483): The left-hand side of a 'for...of' statement cannot use a type annotation. + ╭─[typescript/tests/cases/conformance/parser/ecmascript6/Iterators/parserForOfStatement7.ts:1:10] + 1 │ for (var a: number = 1, b: string = "" of X) { + · ───────── + 2 │ } + ╰──── + help: This iterator's type will be inferred from the iterable. You can safely remove the type annotation. + + × TS(2483): The left-hand side of a 'for...of' statement cannot use a type annotation. + ╭─[typescript/tests/cases/conformance/parser/ecmascript6/Iterators/parserForOfStatement7.ts:1:25] + 1 │ for (var a: number = 1, b: string = "" of X) { + · ───────── + 2 │ } + ╰──── + help: This iterator's type will be inferred from the iterable. You can safely remove the type annotation. + × Expected `,` but found `?` ╭─[typescript/tests/cases/conformance/parser/ecmascript6/ShorthandPropertyAssignment/parserShorthandPropertyAssignment1.ts:3:11] 2 │ var name:any, id: any; @@ -23101,6 +23185,15 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/salsa/private 38 │ } ╰──── + × TS(2483): The left-hand side of a 'for...in' statement cannot use a type annotation. + ╭─[typescript/tests/cases/conformance/statements/for-inStatements/for-inStatementsInvalid.ts:10:10] + 9 │ + 10 │ for (var idx : number in {}) { } + · ──────────── + 11 │ + ╰──── + help: This iterator's type will be inferred from the iterable. You can safely remove the type annotation. + × Unexpected token ╭─[typescript/tests/cases/conformance/statements/for-ofStatements/ES5For-of12.ts:1:6] 1 │ for ([""] of [[""]]) { }