mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +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> {
|
impl<'a> Gen for VariableDeclarator<'a> {
|
||||||
fn gen(&self, p: &mut Codegen, ctx: Context) {
|
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 {
|
if let Some(init) = &self.init {
|
||||||
p.print_soft_space();
|
p.print_soft_space();
|
||||||
p.print_equal();
|
p.print_equal();
|
||||||
|
|
@ -2585,12 +2596,7 @@ impl<'a> Gen for PrivateIdentifier<'a> {
|
||||||
|
|
||||||
impl<'a> Gen for BindingPattern<'a> {
|
impl<'a> Gen for BindingPattern<'a> {
|
||||||
fn gen(&self, p: &mut Codegen, ctx: Context) {
|
fn gen(&self, p: &mut Codegen, ctx: Context) {
|
||||||
match &self.kind {
|
self.kind.print(p, ctx);
|
||||||
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),
|
|
||||||
}
|
|
||||||
if self.optional {
|
if self.optional {
|
||||||
p.print_str("?");
|
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> {
|
impl<'a> Gen for ObjectPattern<'a> {
|
||||||
fn gen(&self, p: &mut Codegen, ctx: Context) {
|
fn gen(&self, p: &mut Codegen, ctx: Context) {
|
||||||
p.add_source_mapping(self.span.start);
|
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 id = self.ast.binding_pattern(id, type_annotation, false);
|
||||||
let declarations =
|
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((
|
||||||
Some(self.ast.variable_declaration(
|
Some(self.ast.variable_declaration(
|
||||||
|
|
|
||||||
|
|
@ -54,27 +54,53 @@ 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<'_>) {
|
pub fn check_variable_declarator(decl: &VariableDeclarator, ctx: &SemanticBuilder<'_>) {
|
||||||
// Check for `let x?: number;`
|
// Check for `let x?: number;`
|
||||||
if decl.id.optional {
|
if decl.id.optional {
|
||||||
// NOTE: BindingPattern spans cover the identifier _and_ the type annotation.
|
// 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
|
let ty = decl
|
||||||
.id
|
.id
|
||||||
.type_annotation
|
.type_annotation
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|ty| ty.type_annotation.span())
|
.map(|ty| ty.type_annotation.span())
|
||||||
.map(|span| &ctx.source_text[span]);
|
.map(|span| &ctx.source_text[span]);
|
||||||
|
if let Some(span) = find_char(decl.span, ctx.source_text, '?') {
|
||||||
ctx.error(unexpected_optional(span, ty));
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn required_parameter_after_optional_parameter(span: Span) -> OxcDiagnostic {
|
fn required_parameter_after_optional_parameter(span: Span) -> OxcDiagnostic {
|
||||||
|
|
|
||||||
|
|
@ -148,6 +148,14 @@ impl<'a> Traverse<'a> for Transformer<'a> {
|
||||||
self.x0_typescript.enter_arrow_function_expression(arrow, ctx);
|
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>) {
|
fn enter_binding_pattern(&mut self, pat: &mut BindingPattern<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||||
self.x0_typescript.enter_binding_pattern(pat, ctx);
|
self.x0_typescript.enter_binding_pattern(pat, ctx);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -165,6 +165,14 @@ impl<'a> Traverse<'a> for TypeScriptAnnotations<'a> {
|
||||||
expr.return_type = None;
|
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>) {
|
fn enter_binding_pattern(&mut self, pat: &mut BindingPattern<'a>, _ctx: &mut TraverseCtx<'a>) {
|
||||||
pat.type_annotation = None;
|
pat.type_annotation = None;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -94,6 +94,14 @@ impl<'a> Traverse<'a> for TypeScript<'a> {
|
||||||
self.annotations.enter_arrow_function_expression(expr, ctx);
|
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>) {
|
fn enter_binding_pattern(&mut self, pat: &mut BindingPattern<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||||
self.annotations.enter_binding_pattern(pat, ctx);
|
self.annotations.enter_binding_pattern(pat, ctx);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ commit: a709f989
|
||||||
parser_typescript Summary:
|
parser_typescript Summary:
|
||||||
AST Parsed : 6469/6479 (99.85%)
|
AST Parsed : 6469/6479 (99.85%)
|
||||||
Positive Passed: 6456/6479 (99.65%)
|
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/ClassDeclaration10.ts
|
||||||
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/ClassDeclaration11.ts
|
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/ClassDeclaration11.ts
|
||||||
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/ClassDeclaration13.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/controlFlowOptionalChain3.tsx
|
||||||
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/controlFlow/controlFlowTypeofObject.ts
|
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/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/dependentDestructuredVariables.ts
|
||||||
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/controlFlow/exhaustiveSwitchStatements1.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
|
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;
|
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 `!`
|
× Expected `,` but found `!`
|
||||||
╭─[typescript/tests/cases/conformance/controlFlow/definiteAssignmentAssertionsWithObjectShortHand.ts:2:16]
|
╭─[typescript/tests/cases/conformance/controlFlow/definiteAssignmentAssertionsWithObjectShortHand.ts:2:16]
|
||||||
1 │ const a: string | undefined = 'ff';
|
1 │ const a: string | undefined = 'ff';
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue