fix(semantic,codegen,transformer): handle definite ! operator in variable declarator (#6019)

closes #5999
This commit is contained in:
Boshen 2024-09-24 08:32:05 +00:00
parent a88504cefb
commit c8682e9fcb
7 changed files with 110 additions and 20 deletions

View file

@ -588,7 +588,18 @@ impl<'a> Gen for VariableDeclaration<'a> {
impl<'a> Gen for VariableDeclarator<'a> {
fn gen(&self, p: &mut Codegen, ctx: Context) {
self.id.print(p, ctx);
self.id.kind.print(p, ctx);
if self.definite {
p.print_char(b'!');
}
if self.id.optional {
p.print_str("?");
}
if let Some(type_annotation) = &self.id.type_annotation {
p.print_colon();
p.print_soft_space();
type_annotation.print(p, ctx);
}
if let Some(init) = &self.init {
p.print_soft_space();
p.print_equal();
@ -2585,12 +2596,7 @@ impl<'a> Gen for PrivateIdentifier<'a> {
impl<'a> Gen for BindingPattern<'a> {
fn gen(&self, p: &mut Codegen, ctx: Context) {
match &self.kind {
BindingPatternKind::BindingIdentifier(ident) => ident.print(p, ctx),
BindingPatternKind::ObjectPattern(pattern) => pattern.print(p, ctx),
BindingPatternKind::ArrayPattern(pattern) => pattern.print(p, ctx),
BindingPatternKind::AssignmentPattern(pattern) => pattern.print(p, ctx),
}
self.kind.print(p, ctx);
if self.optional {
p.print_str("?");
}
@ -2602,6 +2608,17 @@ impl<'a> Gen for BindingPattern<'a> {
}
}
impl<'a> Gen for BindingPatternKind<'a> {
fn gen(&self, p: &mut Codegen, ctx: Context) {
match self {
BindingPatternKind::BindingIdentifier(ident) => ident.print(p, ctx),
BindingPatternKind::ObjectPattern(pattern) => pattern.print(p, ctx),
BindingPatternKind::ArrayPattern(pattern) => pattern.print(p, ctx),
BindingPatternKind::AssignmentPattern(pattern) => pattern.print(p, ctx),
}
}
}
impl<'a> Gen for ObjectPattern<'a> {
fn gen(&self, p: &mut Codegen, ctx: Context) {
p.add_source_mapping(self.span.start);

View file

@ -67,7 +67,7 @@ impl<'a> IsolatedDeclarations<'a> {
let id = self.ast.binding_pattern(id, type_annotation, false);
let declarations =
self.ast.vec1(self.ast.variable_declarator(SPAN, kind, id, None, true));
self.ast.vec1(self.ast.variable_declarator(SPAN, kind, id, None, false));
Some((
Some(self.ast.variable_declaration(

View file

@ -54,26 +54,52 @@ fn unexpected_optional(span: Span, type_annotation: Option<&str>) -> OxcDiagnost
}
}
#[allow(clippy::cast_possible_truncation)]
#[expect(clippy::cast_possible_truncation)]
fn find_char(span: Span, source_text: &str, c: char) -> Option<Span> {
let Some(offset) = span.source_text(source_text).find(c) else {
debug_assert!(
false,
"Flag {c} not found in source text. This is likely indicates a bug in the parser.",
);
return None;
};
let offset = span.start + offset as u32;
Some(Span::new(offset, offset))
}
pub fn check_variable_declarator(decl: &VariableDeclarator, ctx: &SemanticBuilder<'_>) {
// Check for `let x?: number;`
if decl.id.optional {
// NOTE: BindingPattern spans cover the identifier _and_ the type annotation.
let start = decl.id.span().start;
let Some(offset) = ctx.source_text[start as usize..].find('?') else {
debug_assert!(false, "Optional flag not found in source text. This is likely indicates a bug in the parser.");
return;
};
let offset = start + offset as u32;
let span = Span::new(offset, offset);
let ty = decl
.id
.type_annotation
.as_ref()
.map(|ty| ty.type_annotation.span())
.map(|span| &ctx.source_text[span]);
ctx.error(unexpected_optional(span, ty));
if let Some(span) = find_char(decl.span, ctx.source_text, '?') {
ctx.error(unexpected_optional(span, ty));
}
}
if decl.definite {
// Check for `let x!: number = 1;`
// ^
let Some(span) = find_char(decl.span, ctx.source_text, '!') else { return };
if decl.init.is_some() {
let error = ts_error(
"1263",
"Declarations with initializers cannot also have definite assignment assertions.",
)
.with_label(span);
ctx.error(error);
} else if decl.id.type_annotation.is_none() {
let error = ts_error(
"1264",
"Declarations with definite assignment assertions must also have type annotations.",
)
.with_label(span);
ctx.error(error);
}
}
}

View file

@ -148,6 +148,14 @@ impl<'a> Traverse<'a> for Transformer<'a> {
self.x0_typescript.enter_arrow_function_expression(arrow, ctx);
}
fn enter_variable_declarator(
&mut self,
decl: &mut VariableDeclarator<'a>,
ctx: &mut TraverseCtx<'a>,
) {
self.x0_typescript.enter_variable_declarator(decl, ctx);
}
fn enter_binding_pattern(&mut self, pat: &mut BindingPattern<'a>, ctx: &mut TraverseCtx<'a>) {
self.x0_typescript.enter_binding_pattern(pat, ctx);
}

View file

@ -165,6 +165,14 @@ impl<'a> Traverse<'a> for TypeScriptAnnotations<'a> {
expr.return_type = None;
}
fn enter_variable_declarator(
&mut self,
decl: &mut VariableDeclarator<'a>,
_ctx: &mut TraverseCtx<'a>,
) {
decl.definite = false;
}
fn enter_binding_pattern(&mut self, pat: &mut BindingPattern<'a>, _ctx: &mut TraverseCtx<'a>) {
pat.type_annotation = None;

View file

@ -94,6 +94,14 @@ impl<'a> Traverse<'a> for TypeScript<'a> {
self.annotations.enter_arrow_function_expression(expr, ctx);
}
fn enter_variable_declarator(
&mut self,
decl: &mut VariableDeclarator<'a>,
ctx: &mut TraverseCtx<'a>,
) {
self.annotations.enter_variable_declarator(decl, ctx);
}
fn enter_binding_pattern(&mut self, pat: &mut BindingPattern<'a>, ctx: &mut TraverseCtx<'a>) {
self.annotations.enter_binding_pattern(pat, ctx);
}

View file

@ -3,7 +3,7 @@ commit: a709f989
parser_typescript Summary:
AST Parsed : 6469/6479 (99.85%)
Positive Passed: 6456/6479 (99.65%)
Negative Passed: 1225/5715 (21.43%)
Negative Passed: 1226/5715 (21.45%)
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
@ -2542,7 +2542,6 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/controlFl
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/controlFlow/controlFlowOptionalChain3.tsx
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/controlFlow/controlFlowTypeofObject.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/controlFlow/controlFlowWhileStatement.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/controlFlow/definiteAssignmentAssertions.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/controlFlow/dependentDestructuredVariables.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/controlFlow/exhaustiveSwitchStatements1.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/controlFlow/neverReturningFunctions1.ts
@ -14917,6 +14916,30 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/salsa/private
164 │ get p2(): asserts this is string;
╰────
× TS(1264): Declarations with definite assignment assertions must also have type annotations.
╭─[typescript/tests/cases/conformance/controlFlow/definiteAssignmentAssertions.ts:69:10]
68 │ function f4() {
69 │ let a!;
· ▲
70 │ let b! = 1;
╰────
× TS(1263): Declarations with initializers cannot also have definite assignment assertions.
╭─[typescript/tests/cases/conformance/controlFlow/definiteAssignmentAssertions.ts:70:10]
69 │ let a!;
70 │ let b! = 1;
· ▲
71 │ let c!: number = 1;
╰────
× TS(1263): Declarations with initializers cannot also have definite assignment assertions.
╭─[typescript/tests/cases/conformance/controlFlow/definiteAssignmentAssertions.ts:71:10]
70 │ let b! = 1;
71 │ let c!: number = 1;
· ▲
72 │ }
╰────
× Expected `,` but found `!`
╭─[typescript/tests/cases/conformance/controlFlow/definiteAssignmentAssertionsWithObjectShortHand.ts:2:16]
1 │ const a: string | undefined = 'ff';