From 8d99a15ac922bf0a65c8fbbcef40c85687d591c3 Mon Sep 17 00:00:00 2001 From: Boshen Date: Fri, 2 Feb 2024 14:13:10 +0800 Subject: [PATCH] feat(semantic): report error on optional variable declaration in TypeScript (#2261) closes #2253 closes #2255 --- crates/oxc_semantic/src/checker/typescript.rs | 17 ++++++- tasks/coverage/misc/fail/oxc-2253.ts | 7 +++ tasks/coverage/parser_misc.snap | 48 ++++++++++++++++++- 3 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 tasks/coverage/misc/fail/oxc-2253.ts diff --git a/crates/oxc_semantic/src/checker/typescript.rs b/crates/oxc_semantic/src/checker/typescript.rs index 80d58e252..8ab930191 100644 --- a/crates/oxc_semantic/src/checker/typescript.rs +++ b/crates/oxc_semantic/src/checker/typescript.rs @@ -16,9 +16,8 @@ impl EarlyErrorTypeScript { pub fn run<'a>(node: &AstNode<'a>, ctx: &SemanticBuilder<'a>) { let kind = node.kind(); - // should be removed when add more matches. - #[allow(clippy::single_match)] match kind { + AstKind::VariableDeclarator(decl) => check_variable_declarator(decl, ctx), AstKind::SimpleAssignmentTarget(target) => check_simple_assignment_target(target, ctx), AstKind::FormalParameters(params) => check_formal_parameters(params, ctx), _ => {} @@ -26,6 +25,20 @@ impl EarlyErrorTypeScript { } } +#[allow(clippy::cast_possible_truncation)] +fn check_variable_declarator(decl: &VariableDeclarator, ctx: &SemanticBuilder<'_>) { + #[derive(Debug, Error, Diagnostic)] + #[error("Unexpected `?` operator")] + #[diagnostic()] + struct UnexpectedOptional(#[label] Span); + if decl.id.optional { + let start = decl.id.span().end; + let Some(offset) = ctx.source_text[start as usize..].find('?') else { return }; + let offset = start + offset as u32; + ctx.error(UnexpectedOptional(Span::new(offset, offset))); + } +} + fn check_formal_parameters(params: &FormalParameters, ctx: &SemanticBuilder<'_>) { if !params.is_empty() && params.kind == FormalParameterKind::Signature { check_duplicate_bound_names(params, ctx); diff --git a/tasks/coverage/misc/fail/oxc-2253.ts b/tasks/coverage/misc/fail/oxc-2253.ts new file mode 100644 index 000000000..a20f8cd52 --- /dev/null +++ b/tasks/coverage/misc/fail/oxc-2253.ts @@ -0,0 +1,7 @@ +const a? = "A" +const [b]? = ["B"] +const { c }? = { c: "C" } + +const d ? = "A" +const [e, f] ? = ["B"] +const { g, h } ? = { c: "C" } diff --git a/tasks/coverage/parser_misc.snap b/tasks/coverage/parser_misc.snap index 6ab51d5d9..c3ae38e03 100644 --- a/tasks/coverage/parser_misc.snap +++ b/tasks/coverage/parser_misc.snap @@ -1,7 +1,7 @@ parser_misc Summary: AST Parsed : 10/10 (100.00%) Positive Passed: 10/10 (100.00%) -Negative Passed: 5/5 (100.00%) +Negative Passed: 6/6 (100.00%) × Unexpected token ╭─[fail/oxc-169.js:1:1] 1 │ 1<(V=82< @@ -23,6 +23,52 @@ Negative Passed: 5/5 (100.00%) 3 │ } ╰──── + × Unexpected `?` operator + ╭─[fail/oxc-2253.ts:1:1] + 1 │ const a? = "A" + · ▲ + 2 │ const [b]? = ["B"] + ╰──── + + × Unexpected `?` operator + ╭─[fail/oxc-2253.ts:1:1] + 1 │ const a? = "A" + 2 │ const [b]? = ["B"] + · ▲ + 3 │ const { c }? = { c: "C" } + ╰──── + + × Unexpected `?` operator + ╭─[fail/oxc-2253.ts:2:1] + 2 │ const [b]? = ["B"] + 3 │ const { c }? = { c: "C" } + · ▲ + 4 │ + ╰──── + + × Unexpected `?` operator + ╭─[fail/oxc-2253.ts:4:1] + 4 │ + 5 │ const d ? = "A" + · ▲ + 6 │ const [e, f] ? = ["B"] + ╰──── + + × Unexpected `?` operator + ╭─[fail/oxc-2253.ts:5:1] + 5 │ const d ? = "A" + 6 │ const [e, f] ? = ["B"] + · ▲ + 7 │ const { g, h } ? = { c: "C" } + ╰──── + + × Unexpected `?` operator + ╭─[fail/oxc-2253.ts:6:1] + 6 │ const [e, f] ? = ["B"] + 7 │ const { g, h } ? = { c: "C" } + · ▲ + ╰──── + × Empty parenthesized expression ╭─[fail/oxc-232.js:1:1] 1 │ x = (/* a */)