From 7d5ad7d94f2ed6046dac006267915be969f9ee16 Mon Sep 17 00:00:00 2001 From: camc314 <18101008+camc314@users.noreply.github.com> Date: Tue, 22 Apr 2025 02:40:47 +0000 Subject: [PATCH] feat(parser): report error when `import type { type }` is used (#10528) --- crates/oxc_parser/src/diagnostics.rs | 7 +++++++ crates/oxc_parser/src/js/module.rs | 24 ++++++++++++++++------ tasks/coverage/snapshots/parser_babel.snap | 9 ++++++-- 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/crates/oxc_parser/src/diagnostics.rs b/crates/oxc_parser/src/diagnostics.rs index 44717e1f7dce1..37295eee822f8 100644 --- a/crates/oxc_parser/src/diagnostics.rs +++ b/crates/oxc_parser/src/diagnostics.rs @@ -577,6 +577,13 @@ pub fn accessibility_modifier_on_private_property(modifier: &Modifier) -> OxcDia .with_label(modifier.span) } +/// TS(2206) +#[cold] +pub fn type_modifier_on_named_type_import(span: Span) -> OxcDiagnostic { + ts_error("2206", "The 'type' modifier cannot be used on a named import when 'import type' is used on its import statement.") + .with_label(span) +} + /// TS(2207) #[cold] pub fn type_modifier_on_named_type_export(span: Span) -> OxcDiagnostic { diff --git a/crates/oxc_parser/src/js/module.rs b/crates/oxc_parser/src/js/module.rs index 1531195fbb99a..9ee2af3fb1fe1 100644 --- a/crates/oxc_parser/src/js/module.rs +++ b/crates/oxc_parser/src/js/module.rs @@ -80,7 +80,7 @@ impl<'a> ParserImpl<'a> { // import "source" None } else { - Some(self.parse_import_declaration_specifiers()?) + Some(self.parse_import_declaration_specifiers(import_kind)?) }; let source = self.parse_literal_string()?; @@ -103,6 +103,7 @@ impl<'a> ParserImpl<'a> { // Full Syntax: fn parse_import_declaration_specifiers( &mut self, + import_kind: ImportOrExportKind, ) -> Result>> { let mut specifiers = self.ast.vec(); // import defaultExport from "module-name"; @@ -114,7 +115,7 @@ impl<'a> ParserImpl<'a> { Kind::Star => specifiers.push(self.parse_import_namespace_specifier()?), // import defaultExport, { export1 [ , [...] ] } from "module-name"; Kind::LCurly => { - let mut import_specifiers = self.parse_import_specifiers()?; + let mut import_specifiers = self.parse_import_specifiers(import_kind)?; specifiers.append(&mut import_specifiers); } _ => return Err(self.unexpected()), @@ -125,7 +126,7 @@ impl<'a> ParserImpl<'a> { specifiers.push(self.parse_import_namespace_specifier()?); // import { export1 , export2 as alias2 , [...] } from "module-name"; } else if self.at(Kind::LCurly) { - let mut import_specifiers = self.parse_import_specifiers()?; + let mut import_specifiers = self.parse_import_specifiers(import_kind)?; specifiers.append(&mut import_specifiers); } @@ -152,14 +153,17 @@ impl<'a> ParserImpl<'a> { } // import { export1 , export2 as alias2 , [...] } from "module-name"; - fn parse_import_specifiers(&mut self) -> Result>> { + fn parse_import_specifiers( + &mut self, + import_kind: ImportOrExportKind, + ) -> Result>> { self.expect(Kind::LCurly)?; let list = self.context(Context::empty(), self.ctx, |p| { p.parse_delimited_list( Kind::RCurly, Kind::Comma, /* trailing_separator */ true, - Self::parse_import_specifier, + |parser| parser.parse_import_specifier(import_kind), ) })?; self.expect(Kind::RCurly)?; @@ -456,7 +460,10 @@ impl<'a> ParserImpl<'a> { // ImportSpecifier : // ImportedBinding // ModuleExportName as ImportedBinding - pub(crate) fn parse_import_specifier(&mut self) -> Result> { + pub(crate) fn parse_import_specifier( + &mut self, + parent_import_kind: ImportOrExportKind, + ) -> Result> { let specifier_span = self.start_span(); let peek_kind = self.peek_kind(); let mut import_kind = ImportOrExportKind::Value; @@ -474,6 +481,11 @@ impl<'a> ParserImpl<'a> { } } + // `import type { type bar } from 'foo';` + if parent_import_kind == ImportOrExportKind::Type && import_kind == ImportOrExportKind::Type + { + self.error(diagnostics::type_modifier_on_named_type_import(self.cur_token().span())); + } if import_kind == ImportOrExportKind::Type { self.bump_any(); } diff --git a/tasks/coverage/snapshots/parser_babel.snap b/tasks/coverage/snapshots/parser_babel.snap index bccfca6488db3..eec5fa00ffeb6 100644 --- a/tasks/coverage/snapshots/parser_babel.snap +++ b/tasks/coverage/snapshots/parser_babel.snap @@ -3,7 +3,7 @@ commit: 578ac4df parser_babel Summary: AST Parsed : 2303/2322 (99.18%) Positive Passed: 2282/2322 (98.28%) -Negative Passed: 1556/1673 (93.01%) +Negative Passed: 1557/1673 (93.07%) Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/core/categorized/invalid-startindex-and-startline-specified-without-startcolumn/input.js Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/core/categorized/startline-and-startcolumn-specified/input.js Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/core/categorized/startline-specified/input.js @@ -112,7 +112,6 @@ Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/ty Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/type-arguments/empty-type-ref-babel-7/input.ts Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/type-arguments/instantiation-expression-property-access/input.ts Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/type-arguments/instantiation-expression-property-access-babel-7/input.ts -Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-type-in-type/input.ts Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/types/const-type-parameters-invalid/input.ts Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/types/const-type-parameters-invalid-babel-7/input.ts Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/types/import-type-declaration-error/input.ts @@ -12966,6 +12965,12 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc · ── ╰──── + × TS(2206): The 'type' modifier cannot be used on a named import when 'import type' is used on its import statement. + ╭─[babel/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-type-in-type/input.ts:1:15] + 1 │ import type { type foo } from "foo"; + · ──── + ╰──── + × Identifier expected. 'if' is a reserved word that cannot be used here. ╭─[babel/packages/babel-parser/test/fixtures/typescript/type-only-import-export-specifiers/import-invalid-type-named-keywords/input.ts:1:15] 1 │ import { type if } from "./mod.js";