mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 12:19:15 +00:00
feat(semantic): check for JSDoc types in TS type annotations (#6042)
Closes #5982
This commit is contained in:
parent
93575cd09e
commit
8b2e9aa744
3 changed files with 210 additions and 3 deletions
|
|
@ -105,6 +105,7 @@ pub fn check<'a>(node: &AstNode<'a>, ctx: &SemanticBuilder<'a>) {
|
|||
AstKind::VariableDeclarator(decl) => ts::check_variable_declarator(decl, ctx),
|
||||
AstKind::SimpleAssignmentTarget(target) => ts::check_simple_assignment_target(target, ctx),
|
||||
AstKind::TSInterfaceDeclaration(decl) => ts::check_ts_interface_declaration(decl, ctx),
|
||||
AstKind::TSTypeAnnotation(annot) => ts::check_ts_type_annotation(annot, ctx),
|
||||
AstKind::TSTypeParameterDeclaration(declaration) => {
|
||||
ts::check_ts_type_parameter_declaration(declaration, ctx);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,47 @@ pub fn check_ts_type_parameter_declaration(
|
|||
ctx.error(empty_type_parameter_list(declaration.span));
|
||||
}
|
||||
}
|
||||
/// '?' at the end of a type is not valid TypeScript syntax. Did you mean to write 'number | null | undefined'?(17019)
|
||||
#[allow(clippy::needless_pass_by_value)]
|
||||
fn jsdoc_type_in_annotation(
|
||||
modifier: char,
|
||||
is_start: bool,
|
||||
span: Span,
|
||||
suggested_type: Cow<str>,
|
||||
) -> OxcDiagnostic {
|
||||
let (code, start_or_end) = if is_start { ("17020", "start") } else { ("17019", "end") };
|
||||
|
||||
ts_error(
|
||||
code,
|
||||
format!("'{modifier}' at the {start_or_end} of a type is not valid TypeScript syntax.",),
|
||||
)
|
||||
.with_label(span)
|
||||
.with_help(format!("Did you mean to write '{suggested_type}'?"))
|
||||
}
|
||||
|
||||
pub fn check_ts_type_annotation(annotation: &TSTypeAnnotation<'_>, ctx: &SemanticBuilder<'_>) {
|
||||
let (modifier, is_start, span_with_illegal_modifier) = match &annotation.type_annotation {
|
||||
TSType::JSDocNonNullableType(ty) => ('!', !ty.postfix, ty.span()),
|
||||
TSType::JSDocNullableType(ty) => ('?', !ty.postfix, ty.span()),
|
||||
_ => {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let valid_type_span = if is_start {
|
||||
span_with_illegal_modifier.shrink_left(1)
|
||||
} else {
|
||||
span_with_illegal_modifier.shrink_right(1)
|
||||
};
|
||||
|
||||
let suggestion = if modifier == '?' {
|
||||
Cow::Owned(format!("{} | null | undefined", &ctx.source_text[valid_type_span]))
|
||||
} else {
|
||||
Cow::Borrowed(&ctx.source_text[valid_type_span])
|
||||
};
|
||||
|
||||
ctx.error(jsdoc_type_in_annotation(modifier, is_start, span_with_illegal_modifier, suggestion));
|
||||
}
|
||||
|
||||
/// Initializers are not allowed in ambient contexts. ts(1039)
|
||||
fn initializer_in_ambient_context(init_span: Span) -> OxcDiagnostic {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ commit: a709f989
|
|||
parser_typescript Summary:
|
||||
AST Parsed : 6469/6479 (99.85%)
|
||||
Positive Passed: 6458/6479 (99.68%)
|
||||
Negative Passed: 1226/5715 (21.45%)
|
||||
Negative Passed: 1228/5715 (21.49%)
|
||||
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
|
||||
|
|
@ -1663,8 +1663,6 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/parseAssertE
|
|||
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/parseCommaSeparatedNewlineNumber.ts
|
||||
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/parseCommaSeparatedNewlineString.ts
|
||||
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/parseImportAttributesError.ts
|
||||
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/parseInvalidNonNullableTypes.ts
|
||||
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/parseInvalidNullableTypes.ts
|
||||
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/parseJsxExtends2.ts
|
||||
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/parseTypes.ts
|
||||
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/partialDiscriminatedUnionMemberHasGoodError.ts
|
||||
|
|
@ -10185,6 +10183,173 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/salsa/private
|
|||
╰────
|
||||
help: Try insert a semicolon here
|
||||
|
||||
× TS(17019): '!' at the end of a type is not valid TypeScript syntax.
|
||||
╭─[typescript/tests/cases/compiler/parseInvalidNonNullableTypes.ts:1:30]
|
||||
1 │ function f1(a: string): a is string! {
|
||||
· ───────
|
||||
2 │ return true;
|
||||
╰────
|
||||
help: Did you mean to write 'string'?
|
||||
|
||||
× TS(17020): '!' at the start of a type is not valid TypeScript syntax.
|
||||
╭─[typescript/tests/cases/compiler/parseInvalidNonNullableTypes.ts:5:30]
|
||||
4 │
|
||||
5 │ function f2(a: string): a is !string {
|
||||
· ───────
|
||||
6 │ return true;
|
||||
╰────
|
||||
help: Did you mean to write 'string'?
|
||||
|
||||
× TS(17019): '!' at the end of a type is not valid TypeScript syntax.
|
||||
╭─[typescript/tests/cases/compiler/parseInvalidNonNullableTypes.ts:9:16]
|
||||
8 │
|
||||
9 │ function f3(a: string!) {}
|
||||
· ───────
|
||||
10 │ function f4(a: number!) {}
|
||||
╰────
|
||||
help: Did you mean to write 'string'?
|
||||
|
||||
× TS(17019): '!' at the end of a type is not valid TypeScript syntax.
|
||||
╭─[typescript/tests/cases/compiler/parseInvalidNonNullableTypes.ts:10:16]
|
||||
9 │ function f3(a: string!) {}
|
||||
10 │ function f4(a: number!) {}
|
||||
· ───────
|
||||
11 │
|
||||
╰────
|
||||
help: Did you mean to write 'number'?
|
||||
|
||||
× TS(17020): '!' at the start of a type is not valid TypeScript syntax.
|
||||
╭─[typescript/tests/cases/compiler/parseInvalidNonNullableTypes.ts:12:16]
|
||||
11 │
|
||||
12 │ function f5(a: !string) {}
|
||||
· ───────
|
||||
13 │ function f6(a: !number) {}
|
||||
╰────
|
||||
help: Did you mean to write 'string'?
|
||||
|
||||
× TS(17020): '!' at the start of a type is not valid TypeScript syntax.
|
||||
╭─[typescript/tests/cases/compiler/parseInvalidNonNullableTypes.ts:13:16]
|
||||
12 │ function f5(a: !string) {}
|
||||
13 │ function f6(a: !number) {}
|
||||
· ───────
|
||||
14 │
|
||||
╰────
|
||||
help: Did you mean to write 'number'?
|
||||
|
||||
× TS(17019): '!' at the end of a type is not valid TypeScript syntax.
|
||||
╭─[typescript/tests/cases/compiler/parseInvalidNonNullableTypes.ts:15:16]
|
||||
14 │
|
||||
15 │ function f7(): string! {}
|
||||
· ───────
|
||||
16 │ function f8(): !string {}
|
||||
╰────
|
||||
help: Did you mean to write 'string'?
|
||||
|
||||
× TS(17020): '!' at the start of a type is not valid TypeScript syntax.
|
||||
╭─[typescript/tests/cases/compiler/parseInvalidNonNullableTypes.ts:16:16]
|
||||
15 │ function f7(): string! {}
|
||||
16 │ function f8(): !string {}
|
||||
· ───────
|
||||
17 │
|
||||
╰────
|
||||
help: Did you mean to write 'string'?
|
||||
|
||||
× TS(17019): '!' at the end of a type is not valid TypeScript syntax.
|
||||
╭─[typescript/tests/cases/compiler/parseInvalidNonNullableTypes.ts:19:10]
|
||||
18 │ const a = 1 as any!;
|
||||
19 │ const b: number! = 1;
|
||||
· ───────
|
||||
20 │
|
||||
╰────
|
||||
help: Did you mean to write 'number'?
|
||||
|
||||
× TS(17020): '!' at the start of a type is not valid TypeScript syntax.
|
||||
╭─[typescript/tests/cases/compiler/parseInvalidNonNullableTypes.ts:22:10]
|
||||
21 │ const c = 1 as !any;
|
||||
22 │ const d: !number = 1;
|
||||
· ───────
|
||||
╰────
|
||||
help: Did you mean to write 'number'?
|
||||
|
||||
× TS(17020): '?' at the start of a type is not valid TypeScript syntax.
|
||||
╭─[typescript/tests/cases/compiler/parseInvalidNullableTypes.ts:1:30]
|
||||
1 │ function f1(a: string): a is ?string {
|
||||
· ───────
|
||||
2 │ return true;
|
||||
╰────
|
||||
help: Did you mean to write 'string | null | undefined'?
|
||||
|
||||
× TS(17019): '?' at the end of a type is not valid TypeScript syntax.
|
||||
╭─[typescript/tests/cases/compiler/parseInvalidNullableTypes.ts:5:16]
|
||||
4 │
|
||||
5 │ function f2(a: string?) {}
|
||||
· ───────
|
||||
6 │ function f3(a: number?) {}
|
||||
╰────
|
||||
help: Did you mean to write 'string | null | undefined'?
|
||||
|
||||
× TS(17019): '?' at the end of a type is not valid TypeScript syntax.
|
||||
╭─[typescript/tests/cases/compiler/parseInvalidNullableTypes.ts:6:16]
|
||||
5 │ function f2(a: string?) {}
|
||||
6 │ function f3(a: number?) {}
|
||||
· ───────
|
||||
7 │
|
||||
╰────
|
||||
help: Did you mean to write 'number | null | undefined'?
|
||||
|
||||
× TS(17020): '?' at the start of a type is not valid TypeScript syntax.
|
||||
╭─[typescript/tests/cases/compiler/parseInvalidNullableTypes.ts:11:25]
|
||||
10 │
|
||||
11 │ function f6(a: string): ?string {
|
||||
· ───────
|
||||
12 │ return true;
|
||||
╰────
|
||||
help: Did you mean to write 'string | null | undefined'?
|
||||
|
||||
× TS(17019): '?' at the end of a type is not valid TypeScript syntax.
|
||||
╭─[typescript/tests/cases/compiler/parseInvalidNullableTypes.ts:16:10]
|
||||
15 │ const a = 1 as any?;
|
||||
16 │ const b: number? = 1;
|
||||
· ───────
|
||||
17 │
|
||||
╰────
|
||||
help: Did you mean to write 'number | null | undefined'?
|
||||
|
||||
× TS(17019): '?' at the end of a type is not valid TypeScript syntax.
|
||||
╭─[typescript/tests/cases/compiler/parseInvalidNullableTypes.ts:21:8]
|
||||
20 │
|
||||
21 │ let e: unknown?;
|
||||
· ────────
|
||||
22 │ let f: never?;
|
||||
╰────
|
||||
help: Did you mean to write 'unknown | null | undefined'?
|
||||
|
||||
× TS(17019): '?' at the end of a type is not valid TypeScript syntax.
|
||||
╭─[typescript/tests/cases/compiler/parseInvalidNullableTypes.ts:22:8]
|
||||
21 │ let e: unknown?;
|
||||
22 │ let f: never?;
|
||||
· ──────
|
||||
23 │ let g: void?;
|
||||
╰────
|
||||
help: Did you mean to write 'never | null | undefined'?
|
||||
|
||||
× TS(17019): '?' at the end of a type is not valid TypeScript syntax.
|
||||
╭─[typescript/tests/cases/compiler/parseInvalidNullableTypes.ts:23:8]
|
||||
22 │ let f: never?;
|
||||
23 │ let g: void?;
|
||||
· ─────
|
||||
24 │ let h: undefined?;
|
||||
╰────
|
||||
help: Did you mean to write 'void | null | undefined'?
|
||||
|
||||
× TS(17019): '?' at the end of a type is not valid TypeScript syntax.
|
||||
╭─[typescript/tests/cases/compiler/parseInvalidNullableTypes.ts:24:8]
|
||||
23 │ let g: void?;
|
||||
24 │ let h: undefined?;
|
||||
· ──────────
|
||||
╰────
|
||||
help: Did you mean to write 'undefined | null | undefined'?
|
||||
|
||||
× Unexpected token
|
||||
╭─[typescript/tests/cases/compiler/parseJsxElementInUnaryExpressionNoCrash1.ts:1:4]
|
||||
1 │ ~< <
|
||||
|
|
|
|||
Loading…
Reference in a new issue