Skip to content

Commit

Permalink
fix(semantic,codegen,transformer): handle definite ! operator in va…
Browse files Browse the repository at this point in the history
…riable declarator

closes #5999
  • Loading branch information
Boshen committed Sep 24, 2024
1 parent a88504c commit 6baa7f2
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 18 deletions.
31 changes: 24 additions & 7 deletions crates/oxc_codegen/src/gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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("?");
}
Expand All @@ -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);
Expand Down
46 changes: 37 additions & 9 deletions crates/oxc_semantic/src/checker/typescript.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,26 +54,54 @@ fn unexpected_optional(span: Span, type_annotation: Option<&str>) -> OxcDiagnost
}
}

fn find_char<'a>(span: Span, source_text: &str, c: char) -> Option<Span> {
// let start = decl.id.span().start;
let Some(offset) = span.source_text(source_text).find(c) else {
debug_assert!(
false,
"Flag {} not found in source text. This is likely indicates a bug in the parser.",
c
);
return None;
};
let offset = span.start + offset as u32;
Some(Span::new(offset, offset))
}

#[allow(clippy::cast_possible_truncation)]
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);
}
}
}

Expand Down
8 changes: 8 additions & 0 deletions crates/oxc_transformer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
8 changes: 8 additions & 0 deletions crates/oxc_transformer/src/typescript/annotations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
8 changes: 8 additions & 0 deletions crates/oxc_transformer/src/typescript/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
27 changes: 25 additions & 2 deletions tasks/coverage/snapshots/parser_typescript.snap
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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';
Expand Down

0 comments on commit 6baa7f2

Please sign in to comment.