mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 12:19:15 +00:00
fix(semantic,codegen,transformer): handle definite ! operator in variable declarator (#6019)
closes #5999
This commit is contained in:
parent
a88504cefb
commit
c8682e9fcb
7 changed files with 110 additions and 20 deletions
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
|
|||
Loading…
Reference in a new issue