From de82492e97b62f34b6473de3a452762a20b40172 Mon Sep 17 00:00:00 2001 From: camc314 <18101008+camc314@users.noreply.github.com> Date: Wed, 25 Dec 2024 12:24:32 +0000 Subject: [PATCH] fix(parser): report syntax errors for missing constructor implementations (#8081) --- crates/oxc_semantic/src/builder.rs | 8 ++ crates/oxc_semantic/src/checker/typescript.rs | 20 +++++ .../coverage/snapshots/parser_typescript.snap | 74 ++++++++++++++++--- 3 files changed, 93 insertions(+), 9 deletions(-) diff --git a/crates/oxc_semantic/src/builder.rs b/crates/oxc_semantic/src/builder.rs index b1959b9f839f9..9503630f0f2ff 100644 --- a/crates/oxc_semantic/src/builder.rs +++ b/crates/oxc_semantic/src/builder.rs @@ -306,6 +306,14 @@ impl<'a> SemanticBuilder<'a> { self.errors.borrow_mut().push(error); } + pub(crate) fn in_declare_scope(&self) -> bool { + self.source_type.is_typescript_definition() + || self + .scope + .ancestors(self.current_scope_id) + .any(|scope_id| self.scope.get_flags(scope_id).is_ts_module_block()) + } + fn create_ast_node(&mut self, kind: AstKind<'a>) { let mut flags = self.current_node_flags; if self.build_jsdoc && self.jsdoc.retrieve_attached_jsdoc(&kind) { diff --git a/crates/oxc_semantic/src/checker/typescript.rs b/crates/oxc_semantic/src/checker/typescript.rs index ea4f2ed4502da..cf79788ce3ea8 100644 --- a/crates/oxc_semantic/src/checker/typescript.rs +++ b/crates/oxc_semantic/src/checker/typescript.rs @@ -1,5 +1,6 @@ use std::borrow::Cow; +use itertools::Itertools; use rustc_hash::FxHashMap; use oxc_ast::{ast::*, AstKind}; @@ -330,6 +331,10 @@ fn abstract_elem_in_concrete_class(is_property: bool, span: Span) -> OxcDiagnost .with_label(span) } +fn constructor_implementation_missing(span: Span) -> OxcDiagnostic { + OxcDiagnostic::error("Constructor implementation is missing.").with_label(span) +} + pub fn check_class<'a>(class: &Class<'a>, ctx: &SemanticBuilder<'a>) { if !class.r#abstract { for elem in &class.body.body { @@ -339,6 +344,21 @@ pub fn check_class<'a>(class: &Class<'a>, ctx: &SemanticBuilder<'a>) { } } } + + if !class.r#declare && !ctx.in_declare_scope() { + for (a, b) in class.body.body.iter().map(Some).chain(vec![None]).tuple_windows() { + if let Some(ClassElement::MethodDefinition(a)) = a { + if a.kind.is_constructor() + && a.value.r#type == FunctionType::TSEmptyBodyFunctionExpression + && b.map_or(true, |b| { + b.method_definition_kind().map_or(true, |kind| !kind.is_constructor()) + }) + { + ctx.error(constructor_implementation_missing(a.key.span())); + } + } + } + } } fn abstract_element_cannot_have_initializer( diff --git a/tasks/coverage/snapshots/parser_typescript.snap b/tasks/coverage/snapshots/parser_typescript.snap index c0fde9d6f4c20..c57359438d437 100644 --- a/tasks/coverage/snapshots/parser_typescript.snap +++ b/tasks/coverage/snapshots/parser_typescript.snap @@ -3,17 +3,13 @@ commit: d85767ab parser_typescript Summary: AST Parsed : 6494/6503 (99.86%) Positive Passed: 6483/6503 (99.69%) -Negative Passed: 1241/5747 (21.59%) -Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/ClassDeclaration10.ts -Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/ClassDeclaration11.ts +Negative Passed: 1249/5747 (21.73%) Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/ClassDeclaration13.ts -Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/ClassDeclaration14.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/ClassDeclaration15.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/ClassDeclaration21.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/ClassDeclaration22.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/ClassDeclaration24.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/ClassDeclaration25.ts -Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/ClassDeclaration8.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/ClassDeclaration9.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/ExportAssignment7.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/ExportAssignment8.ts @@ -3723,11 +3719,8 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ec Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/ClassDeclarations/parserClass1.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/ClassDeclarations/parserClass2.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/ClassDeclarations/parserClassDeclaration1.ts -Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/ClassDeclarations/parserClassDeclaration10.ts -Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/ClassDeclarations/parserClassDeclaration11.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/ClassDeclarations/parserClassDeclaration12.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/ClassDeclarations/parserClassDeclaration13.ts -Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/ClassDeclarations/parserClassDeclaration14.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/ClassDeclarations/parserClassDeclaration15.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/ClassDeclarations/parserClassDeclaration18.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/ClassDeclarations/parserClassDeclaration2.ts @@ -3740,7 +3733,6 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ec Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/ClassDeclarations/parserClassDeclaration5.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/ClassDeclarations/parserClassDeclaration6.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/ClassDeclarations/parserClassDeclaration7.ts -Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/ClassDeclarations/parserClassDeclaration8.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/ClassDeclarations/parserClassDeclaration9.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/ComputedPropertyNames/parserES5ComputedPropertyName1.ts Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/ComputedPropertyNames/parserES5ComputedPropertyName10.ts @@ -4826,6 +4818,30 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/salsa/private · ──────────────── ╰──── + × Constructor implementation is missing. + ╭─[typescript/tests/cases/compiler/ClassDeclaration10.ts:2:4] + 1 │ class C { + 2 │ constructor(); + · ─────────── + 3 │ foo(); + ╰──── + + × Constructor implementation is missing. + ╭─[typescript/tests/cases/compiler/ClassDeclaration11.ts:2:4] + 1 │ class C { + 2 │ constructor(); + · ─────────── + 3 │ foo() { } + ╰──── + + × Constructor implementation is missing. + ╭─[typescript/tests/cases/compiler/ClassDeclaration14.ts:3:4] + 2 │ foo(); + 3 │ constructor(); + · ─────────── + 4 │ } + ╰──── + × TS(1248): A class member cannot have the 'const' keyword. ╭─[typescript/tests/cases/compiler/ClassDeclaration26.ts:2:18] 1 │ class C { @@ -4844,6 +4860,14 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/salsa/private ╰──── help: Try insert a semicolon here + × Constructor implementation is missing. + ╭─[typescript/tests/cases/compiler/ClassDeclaration8.ts:2:3] + 1 │ class C { + 2 │ constructor(); + · ─────────── + 3 │ } + ╰──── + × TS(1248): A class member cannot have the 'const' keyword. ╭─[typescript/tests/cases/compiler/ClassDeclarationWithInvalidConstOnPropertyDeclaration.ts:2:16] 1 │ class AtomicNumbers { @@ -20249,6 +20273,38 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/salsa/private · ╰── `,` expected ╰──── + × Constructor implementation is missing. + ╭─[typescript/tests/cases/conformance/parser/ecmascript5/ClassDeclarations/parserClassDeclaration10.ts:2:4] + 1 │ class C { + 2 │ constructor(); + · ─────────── + 3 │ foo(); + ╰──── + + × Constructor implementation is missing. + ╭─[typescript/tests/cases/conformance/parser/ecmascript5/ClassDeclarations/parserClassDeclaration11.ts:2:4] + 1 │ class C { + 2 │ constructor(); + · ─────────── + 3 │ foo() { } + ╰──── + + × Constructor implementation is missing. + ╭─[typescript/tests/cases/conformance/parser/ecmascript5/ClassDeclarations/parserClassDeclaration14.ts:3:4] + 2 │ foo(); + 3 │ constructor(); + · ─────────── + 4 │ } + ╰──── + + × Constructor implementation is missing. + ╭─[typescript/tests/cases/conformance/parser/ecmascript5/ClassDeclarations/parserClassDeclaration8.ts:2:3] + 1 │ class C { + 2 │ constructor(); + · ─────────── + 3 │ } + ╰──── + × TS(1164): Computed property names are not allowed in enums. ╭─[typescript/tests/cases/conformance/parser/ecmascript5/ComputedPropertyNames/parserES5ComputedPropertyName6.ts:2:4] 1 │ enum E {