diff --git a/crates/oxc_parser/src/diagnostics.rs b/crates/oxc_parser/src/diagnostics.rs index 43050332baeb4..d7af4084946fc 100644 --- a/crates/oxc_parser/src/diagnostics.rs +++ b/crates/oxc_parser/src/diagnostics.rs @@ -701,6 +701,15 @@ pub fn cannot_appear_on_a_type_parameter(modifier: &Modifier) -> OxcDiagnostic { .with_label(modifier.span) } +#[cold] +pub fn can_only_appear_on_a_type_parameter_of_a_class_interface_or_type_alias( + modifier: ModifierKind, + span: Span, +) -> OxcDiagnostic { + ts_error("1274", format!("'{modifier}' modifier can only appear on a type parameter of a class, interface or type alias.")) + .with_label(span) +} + pub fn cannot_appear_on_a_parameter(modifier: &Modifier) -> OxcDiagnostic { ts_error("1090", format!("'{}' modifier cannot appear on a parameter.", modifier.kind)) .with_label(modifier.span) diff --git a/crates/oxc_parser/src/js/class.rs b/crates/oxc_parser/src/js/class.rs index 001921a67a5da..25ac2f4a2f192 100644 --- a/crates/oxc_parser/src/js/class.rs +++ b/crates/oxc_parser/src/js/class.rs @@ -317,8 +317,16 @@ impl<'a> ParserImpl<'a> { } fn parse_class_element_name(&mut self, modifiers: &Modifiers<'a>) -> (PropertyKey<'a>, bool) { - if let Some(modifier) = modifiers.iter().find(|m| m.kind == ModifierKind::Const) { - self.error(diagnostics::const_class_member(modifier.span)); + for modifier in modifiers.iter() { + match modifier.kind { + ModifierKind::Const => { + self.error(diagnostics::const_class_member(modifier.span)); + } + ModifierKind::In | ModifierKind::Out => { + self.error(diagnostics::can_only_appear_on_a_type_parameter_of_a_class_interface_or_type_alias(modifier.kind, modifier.span)); + } + _ => {} + } } match self.cur_kind() { Kind::PrivateIdentifier => { diff --git a/crates/oxc_semantic/src/checker/typescript.rs b/crates/oxc_semantic/src/checker/typescript.rs index 09c6d2fd03b97..4700888ed8a26 100644 --- a/crates/oxc_semantic/src/checker/typescript.rs +++ b/crates/oxc_semantic/src/checker/typescript.rs @@ -14,8 +14,39 @@ fn ts_error>>(code: &'static str, message: M) -> OxcDi OxcDiagnostic::error(message).with_error_code("TS", code) } +fn can_only_appear_on_a_type_parameter_of_a_class_interface_or_type_alias( + modifier: &str, + span: Span, +) -> OxcDiagnostic { + ts_error("1274", format!("'{modifier}' modifier can only appear on a type parameter of a class, interface or type alias.")) + .with_label(span) +} + pub fn check_ts_type_parameter<'a>(param: &TSTypeParameter<'a>, ctx: &SemanticBuilder<'a>) { check_type_name_is_reserved(¶m.name, ctx, "Type parameter"); + if param.r#in || param.out { + let is_allowed_node = matches!( + // skip parent TSTypeParameterDeclaration + ctx.nodes.ancestor_kinds(ctx.current_node_id).nth(1), + Some( + AstKind::TSInterfaceDeclaration(_) + | AstKind::Class(_) + | AstKind::TSTypeAliasDeclaration(_) + ) + ); + if !is_allowed_node { + if param.r#in { + ctx.error(can_only_appear_on_a_type_parameter_of_a_class_interface_or_type_alias( + "in", param.span, + )); + } + if param.out { + ctx.error(can_only_appear_on_a_type_parameter_of_a_class_interface_or_type_alias( + "out", param.span, + )); + } + } + } } /// '?' at the end of a type is not valid TypeScript syntax. Did you mean to write 'number | null | undefined'?(17019) diff --git a/tasks/coverage/snapshots/parser_babel.snap b/tasks/coverage/snapshots/parser_babel.snap index e21e83160f673..7edb7ad5ce584 100644 --- a/tasks/coverage/snapshots/parser_babel.snap +++ b/tasks/coverage/snapshots/parser_babel.snap @@ -14878,6 +14878,38 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc ╰──── help: Remove the duplicate modifier. + × TS(1274): 'in' modifier can only appear on a type parameter of a class, interface or type alias. + ╭─[babel/packages/babel-parser/test/fixtures/typescript/types/variance-annotations/input.ts:104:5] + 103 │ class C { + 104 │ in a = 0; // Error + · ── + 105 │ out b = 0; // Error + ╰──── + + × TS(1274): 'out' modifier can only appear on a type parameter of a class, interface or type alias. + ╭─[babel/packages/babel-parser/test/fixtures/typescript/types/variance-annotations/input.ts:105:5] + 104 │ in a = 0; // Error + 105 │ out b = 0; // Error + · ─── + 106 │ } + ╰──── + + × TS(1274): 'in' modifier can only appear on a type parameter of a class, interface or type alias. + ╭─[babel/packages/babel-parser/test/fixtures/typescript/types/variance-annotations/input.ts:100:21] + 99 │ + 100 │ declare function f1(x: T): void; // Error + · ──── + 101 │ declare function f2(): T; // Error + ╰──── + + × TS(1274): 'out' modifier can only appear on a type parameter of a class, interface or type alias. + ╭─[babel/packages/babel-parser/test/fixtures/typescript/types/variance-annotations/input.ts:101:21] + 100 │ declare function f1(x: T): void; // Error + 101 │ declare function f2(): T; // Error + · ───── + 102 │ + ╰──── + × TS(1273): 'public' modifier cannot be used on a type parameter. ╭─[babel/packages/babel-parser/test/fixtures/typescript/types/variance-annotations-babel-7/input.ts:95:10] 94 │ @@ -14904,6 +14936,38 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc ╰──── help: Remove the duplicate modifier. + × TS(1274): 'in' modifier can only appear on a type parameter of a class, interface or type alias. + ╭─[babel/packages/babel-parser/test/fixtures/typescript/types/variance-annotations-babel-7/input.ts:104:5] + 103 │ class C { + 104 │ in a = 0; // Error + · ── + 105 │ out b = 0; // Error + ╰──── + + × TS(1274): 'out' modifier can only appear on a type parameter of a class, interface or type alias. + ╭─[babel/packages/babel-parser/test/fixtures/typescript/types/variance-annotations-babel-7/input.ts:105:5] + 104 │ in a = 0; // Error + 105 │ out b = 0; // Error + · ─── + 106 │ } + ╰──── + + × TS(1274): 'in' modifier can only appear on a type parameter of a class, interface or type alias. + ╭─[babel/packages/babel-parser/test/fixtures/typescript/types/variance-annotations-babel-7/input.ts:100:21] + 99 │ + 100 │ declare function f1(x: T): void; // Error + · ──── + 101 │ declare function f2(): T; // Error + ╰──── + + × TS(1274): 'out' modifier can only appear on a type parameter of a class, interface or type alias. + ╭─[babel/packages/babel-parser/test/fixtures/typescript/types/variance-annotations-babel-7/input.ts:101:21] + 100 │ declare function f1(x: T): void; // Error + 101 │ declare function f2(): T; // Error + · ───── + 102 │ + ╰──── + × Unexpected token. Did you mean `{'>'}` or `>`? ╭─[babel/packages/babel-parser/test/fixtures/typescript/types/variance-annotations-with-jsx/input.tsx:2:11] 1 │ // valid JSX diff --git a/tasks/coverage/snapshots/parser_typescript.snap b/tasks/coverage/snapshots/parser_typescript.snap index 5e2f7b2920566..92e37c4006029 100644 --- a/tasks/coverage/snapshots/parser_typescript.snap +++ b/tasks/coverage/snapshots/parser_typescript.snap @@ -27683,6 +27683,38 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/parser/ecmasc ╰──── help: Remove the duplicate modifier. + × TS(1274): 'in' modifier can only appear on a type parameter of a class, interface or type alias. + ╭─[typescript/tests/cases/conformance/types/typeParameters/typeParameterLists/varianceAnnotations.ts:104:5] + 103 │ class C { + 104 │ in a = 0; // Error + · ── + 105 │ out b = 0; // Error + ╰──── + + × TS(1274): 'out' modifier can only appear on a type parameter of a class, interface or type alias. + ╭─[typescript/tests/cases/conformance/types/typeParameters/typeParameterLists/varianceAnnotations.ts:105:5] + 104 │ in a = 0; // Error + 105 │ out b = 0; // Error + · ─── + 106 │ } + ╰──── + + × TS(1274): 'in' modifier can only appear on a type parameter of a class, interface or type alias. + ╭─[typescript/tests/cases/conformance/types/typeParameters/typeParameterLists/varianceAnnotations.ts:100:21] + 99 │ + 100 │ declare function f1(x: T): void; // Error + · ──── + 101 │ declare function f2(): T; // Error + ╰──── + + × TS(1274): 'out' modifier can only appear on a type parameter of a class, interface or type alias. + ╭─[typescript/tests/cases/conformance/types/typeParameters/typeParameterLists/varianceAnnotations.ts:101:21] + 100 │ declare function f1(x: T): void; // Error + 101 │ declare function f2(): T; // Error + · ───── + 102 │ + ╰──── + × Identifier expected. 'in' is a reserved word that cannot be used here. ╭─[typescript/tests/cases/conformance/types/typeParameters/typeParameterLists/varianceAnnotationsWithCircularlyReferencesError.ts:1:12] 1 │ type T1 = T1 // Error: circularly references