fix(parser): reject invalid modifiers on import aliases (#26622)

Oxc currently accepts and discards invalid modifiers on TypeScript import aliases. Validate them before constructing the declaration: `declare import X = A.B;` now reports TS1079, and `abstract import X = A.B;` reports TS1242.

Adds regression coverage for modifier combinations and exported, ambient, external, and type-only aliases. Valid aliases retain their existing behavior.
This commit is contained in:
camc314 committed 2026-09-15 07:36:38 +00:00
1 parent 957241c3cf
commit 827fdbdf52
17 files changed
+179 -18

No files matched your search

+5
View File
@@ -889,6 +889,11 @@ parser_diagnostics! {
OxcDiagnostic::error("import() requires a specifier.").with_label(span)
};
declare_modifier_on_import(span: Span) => {
ts_error("1079", "A 'declare' modifier cannot be used with an import declaration.")
.with_label(span)
};
modifier_cannot_be_used_here(modifier: Modifier, allowed: Option<ModifierKinds>) => {
OxcDiagnostic::error(format!("'{}' modifier cannot be used here.", modifier.kind))
.with_label(modifier.span())
+14
View File
@@ -689,6 +689,20 @@ impl<'a, C: Config> ParserImpl<'a, C> {
Declaration::ClassDeclaration(decl)
}
Kind::Import => {
self.verify_modifiers(
modifiers,
ModifierKinds::new([ModifierKind::Export]),
true,
|modifier, allowed| match modifier.kind {
ModifierKind::Declare => {
diagnostics::declare_modifier_on_import(modifier.span())
}
ModifierKind::Abstract => {
diagnostics::illegal_abstract_modifier(modifier.span())
}
_ => diagnostics::modifier_cannot_be_used_here(modifier, allowed),
},
);
self.bump_any();
let token = self.cur_token();
let mut import_kind = ImportOrExportKind::Value;
@@ -0,0 +1,2 @@
namespace A { export class B {} }
abstract import type X = require("external");
@@ -0,0 +1,2 @@
namespace A { export class B {} }
abstract import X = A.B;
@@ -0,0 +1,2 @@
namespace A { export class B {} }
declare abstract import X = A.B;
@@ -0,0 +1,2 @@
namespace A { export class B {} }
declare import X = require("external");
@@ -0,0 +1,2 @@
namespace A { export class B {} }
export abstract import X = A.B;
@@ -0,0 +1,2 @@
namespace A { export class B {} }
export declare import type X = require("external");
@@ -0,0 +1,2 @@
namespace A { export class B {} }
export declare import X = A.B;
@@ -0,0 +1,10 @@
namespace A { export class B {} }
import Local = A.B;
export import Exported = A.B;
import External = require("external");
export import ExportedExternal = require("external");
export import type ExportedType = require("external");
declare namespace Ambient {
import Local = A.B;
export import Exported = A.B;
}
+2 -2
View File
@@ -1,3 +1,3 @@
codegen_misc Summary:
AST Parsed : 110/110 (100.00%)
Positive Passed: 110/110 (100.00%)
AST Parsed : 111/111 (100.00%)
Positive Passed: 111/111 (100.00%)
+2 -2
View File
@@ -1,3 +1,3 @@
formatter_misc Summary:
AST Parsed : 110/110 (100.00%)
Positive Passed: 110/110 (100.00%)
AST Parsed : 111/111 (100.00%)
Positive Passed: 111/111 (100.00%)
+2 -2
View File
@@ -1,3 +1,3 @@
lexer_misc Summary:
AST Parsed : 337/337 (100.00%)
Positive Passed: 337/337 (100.00%)
AST Parsed : 345/345 (100.00%)
Positive Passed: 345/345 (100.00%)
+59 -3
View File
@@ -1,7 +1,7 @@
parser_misc Summary:
AST Parsed : 110/110 (100.00%)
Positive Passed: 110/110 (100.00%)
Negative Passed: 227/227 (100.00%)
AST Parsed : 111/111 (100.00%)
Positive Passed: 111/111 (100.00%)
Negative Passed: 234/234 (100.00%)
× TS(1275): 'accessor' modifier can only appear on a property declaration.
╭─[misc/fail/accessor-modifier-abstract-method.ts:1:29]
@@ -936,6 +936,62 @@ Negative Passed: 227/227 (100.00%)
· ╰── `from` expected
╰────
× TS(1242): 'abstract' modifier can only appear on a class, method, or property declaration.
╭─[misc/fail/import-equals-modifier-abstract-type-require.ts:2:1]
1 │ namespace A { export class B {} }
2 │ abstract import type X = require("external");
· ────────
╰────
× TS(1242): 'abstract' modifier can only appear on a class, method, or property declaration.
╭─[misc/fail/import-equals-modifier-abstract.ts:2:1]
1 │ namespace A { export class B {} }
2 │ abstract import X = A.B;
· ────────
╰────
× TS(1079): A 'declare' modifier cannot be used with an import declaration.
╭─[misc/fail/import-equals-modifier-declare-abstract.ts:2:1]
1 │ namespace A { export class B {} }
2 │ declare abstract import X = A.B;
· ───────
╰────
× TS(1242): 'abstract' modifier can only appear on a class, method, or property declaration.
╭─[misc/fail/import-equals-modifier-declare-abstract.ts:2:9]
1 │ namespace A { export class B {} }
2 │ declare abstract import X = A.B;
· ────────
╰────
× TS(1079): A 'declare' modifier cannot be used with an import declaration.
╭─[misc/fail/import-equals-modifier-declare-require.ts:2:1]
1 │ namespace A { export class B {} }
2 │ declare import X = require("external");
· ───────
╰────
× TS(1242): 'abstract' modifier can only appear on a class, method, or property declaration.
╭─[misc/fail/import-equals-modifier-export-abstract.ts:2:8]
1 │ namespace A { export class B {} }
2 │ export abstract import X = A.B;
· ────────
╰────
× TS(1079): A 'declare' modifier cannot be used with an import declaration.
╭─[misc/fail/import-equals-modifier-export-declare-type-require.ts:2:8]
1 │ namespace A { export class B {} }
2 │ export declare import type X = require("external");
· ───────
╰────
× TS(1079): A 'declare' modifier cannot be used with an import declaration.
╭─[misc/fail/import-equals-modifier-export-declare.ts:2:8]
1 │ namespace A { export class B {} }
2 │ export declare import X = A.B;
· ───────
╰────
× Identifier expected. 'this' is a reserved word that cannot be used here.
╭─[misc/fail/import-equals-this.ts:2:12]
1 │ // `this` is not valid in import equals declaration
@@ -3,7 +3,7 @@ commit: b465fdbf
parser_typescript Summary:
AST Parsed : 9783/9783 (100.00%)
Positive Passed: 9783/9783 (100.00%)
Negative Passed: 1649/2636 (62.56%)
Negative Passed: 1651/2636 (62.63%)
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/FunctionDeclaration3.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/FunctionDeclaration4.ts
@@ -202,8 +202,6 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/declarationE
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/declarationEmitUsingTypeAlias1.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/declareModifierOnImport1.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/decoratorsOnComputedProperties.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/defaultArgsInOverloads.ts
@@ -450,8 +448,6 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/importAndVar
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/importAssertionsDeprecated.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/importDeclWithClassModifiers.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/importTypeAssertionDeprecation.ts
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/importWithTrailingSlash.ts
@@ -5863,6 +5859,12 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/types/wit
╰────
help: Remove the duplicate modifier.
× TS(1079): A 'declare' modifier cannot be used with an import declaration.
╭─[typescript/tests/cases/compiler/declareModifierOnImport1.ts:1:1]
1 │ declare import a = b;
· ───────
╰────
× Expected `,` or `)` but found `:`
╭─[typescript/tests/cases/compiler/decoratorInJsFile.ts:3:10]
2 │ class SomeClass {
@@ -7774,6 +7776,33 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/types/wit
╰────
help: Wrap this with quotes
× 'public' modifier cannot be used here.
╭─[typescript/tests/cases/compiler/importDeclWithClassModifiers.ts:5:8]
4 │ }
5 │ export public import a = x.c;
· ──────
6 │ export private import b = x.c;
╰────
help: Only 'export' modifier is allowed here.
× 'private' modifier cannot be used here.
╭─[typescript/tests/cases/compiler/importDeclWithClassModifiers.ts:6:8]
5 │ export public import a = x.c;
6 │ export private import b = x.c;
· ───────
7 │ export static import c = x.c;
╰────
help: Only 'export' modifier is allowed here.
× 'static' modifier cannot be used here.
╭─[typescript/tests/cases/compiler/importDeclWithClassModifiers.ts:7:8]
6 │ export private import b = x.c;
7 │ export static import c = x.c;
· ──────
8 │ var b: a;
╰────
help: Only 'export' modifier is allowed here.
× TS(1029): 'export' modifier must precede 'declare' modifier.
╭─[typescript/tests/cases/compiler/importDeclWithDeclareModifier.ts:5:9]
4 │ }
@@ -7782,6 +7811,14 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/types/wit
6 │ var b: a;
╰────
× TS(1079): A 'declare' modifier cannot be used with an import declaration.
╭─[typescript/tests/cases/compiler/importDeclWithDeclareModifier.ts:5:1]
4 │ }
5 │ declare export import a = x.c;
· ───────
6 │ var b: a;
╰────
× TS(1029): 'export' modifier must precede 'declare' modifier.
╭─[typescript/tests/cases/compiler/importDeclWithDeclareModifierInAmbientContext.ts:6:13]
5 │ }
@@ -7793,6 +7830,14 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/types/wit
× TS(1038): A 'declare' modifier cannot be used in an already ambient context.
╭─[typescript/tests/cases/compiler/importDeclWithDeclareModifierInAmbientContext.ts:6:5]
5 │ }
6 │ declare export import a = x.c;
· ───────
7 │ var b: a;
╰────
× TS(1079): A 'declare' modifier cannot be used with an import declaration.
╭─[typescript/tests/cases/compiler/importDeclWithDeclareModifierInAmbientContext.ts:6:5]
5 │ }
6 │ declare export import a = x.c;
· ───────
7 │ var b: a;
@@ -17752,6 +17797,15 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/types/wit
╰────
help: Allowed modifiers are: declare, async
× 'accessor' modifier cannot be used here.
╭─[typescript/tests/cases/conformance/classes/propertyMemberDeclarations/autoAccessorDisallowedModifiers.ts:34:1]
33 │ accessor function F1() {}
34 │ accessor import "x";
· ────────
35 │ accessor import {} from "x";
╰────
help: Only 'export' modifier is allowed here.
× Unexpected token
╭─[typescript/tests/cases/conformance/classes/propertyMemberDeclarations/autoAccessorDisallowedModifiers.ts:34:17]
33 │ accessor function F1() {}
+10 -2
View File
@@ -1,11 +1,19 @@
semantic_misc Summary:
AST Parsed : 110/110 (100.00%)
Positive Passed: 105/110 (95.45%)
AST Parsed : 111/111 (100.00%)
Positive Passed: 105/111 (94.59%)
semantic Error: tasks/coverage/misc/pass/declare-let-private.ts
Bindings mismatch:
after transform: ScopeId(0): ["private"]
rebuilt : ScopeId(0): []
semantic Error: tasks/coverage/misc/pass/import-equals-modifiers.ts
Bindings mismatch:
after transform: ScopeId(0): ["A", "Ambient", "Exported", "ExportedExternal", "ExportedType"]
rebuilt : ScopeId(0): ["A", "Exported", "ExportedExternal"]
Symbol reference IDs mismatch for "A":
after transform: SymbolId(0): [ReferenceId(1), ReferenceId(2), ReferenceId(3), ReferenceId(6), ReferenceId(7)]
rebuilt : SymbolId(0): [ReferenceId(2), ReferenceId(3), ReferenceId(4)]
semantic Error: tasks/coverage/misc/pass/oxc-11593.ts
Scope parent mismatch:
after transform: ScopeId(2): Some(ScopeId(1))
@@ -1,3 +1,3 @@
transformer_misc Summary:
AST Parsed : 110/110 (100.00%)
Positive Passed: 110/110 (100.00%)
AST Parsed : 111/111 (100.00%)
Positive Passed: 111/111 (100.00%)