Skip to content

Commit 4a59cd7

Browse files
Copilotjakebailey
andauthored
Reject import assert in parser and checker, remove checker assert/with distinction (microsoft#2775)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com>
1 parent 09d6f15 commit 4a59cd7

18 files changed

+527
-312
lines changed

internal/checker/checker.go

Lines changed: 15 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3224,9 +3224,7 @@ func (c *Checker) checkImportType(node *ast.Node) {
32243224
func (c *Checker) getResolutionModeOverride(node *ast.ImportAttributes, reportErrors bool) core.ResolutionMode {
32253225
if len(node.Attributes.Nodes) != 1 {
32263226
if reportErrors {
3227-
c.grammarErrorOnNode(node.AsNode(), core.IfElse(node.Token == ast.KindWithKeyword,
3228-
diagnostics.Type_import_attributes_should_have_exactly_one_key_resolution_mode_with_value_import_or_require,
3229-
diagnostics.Type_import_assertions_should_have_exactly_one_key_resolution_mode_with_value_import_or_require))
3227+
c.grammarErrorOnNode(node.AsNode(), diagnostics.Type_import_attributes_should_have_exactly_one_key_resolution_mode_with_value_import_or_require)
32303228
}
32313229
return core.ResolutionModeNone
32323230
}
@@ -3236,9 +3234,7 @@ func (c *Checker) getResolutionModeOverride(node *ast.ImportAttributes, reportEr
32363234
}
32373235
if elem.Name().Text() != "resolution-mode" {
32383236
if reportErrors {
3239-
c.grammarErrorOnNode(elem.Name(), core.IfElse(node.Token == ast.KindWithKeyword,
3240-
diagnostics.X_resolution_mode_is_the_only_valid_key_for_type_import_attributes,
3241-
diagnostics.X_resolution_mode_is_the_only_valid_key_for_type_import_assertions))
3237+
c.grammarErrorOnNode(elem.Name(), diagnostics.X_resolution_mode_is_the_only_valid_key_for_type_import_attributes)
32423238
}
32433239
return core.ResolutionModeNone
32443240
}
@@ -5203,14 +5199,11 @@ func (c *Checker) checkExternalImportOrExportDeclaration(node *ast.Node) bool {
52035199
if !ast.IsImportEqualsDeclaration(node) {
52045200
attributes := ast.GetImportAttributes(node)
52055201
if attributes != nil {
5206-
diagnostic := core.IfElse(attributes.AsImportAttributes().Token == ast.KindWithKeyword,
5207-
diagnostics.Import_attribute_values_must_be_string_literal_expressions,
5208-
diagnostics.Import_assertion_values_must_be_string_literal_expressions)
52095202
hasError := false
52105203
for _, attr := range attributes.AsImportAttributes().Attributes.Nodes {
52115204
if !ast.IsStringLiteral(attr.AsImportAttribute().Value) {
52125205
hasError = true
5213-
c.error(attr.AsImportAttribute().Value, diagnostic)
5206+
c.error(attr.AsImportAttribute().Value, diagnostics.Import_attribute_values_must_be_string_literal_expressions)
52145207
}
52155208
}
52165209
return !hasError
@@ -5256,40 +5249,24 @@ func (c *Checker) checkImportAttributes(declaration *ast.Node) {
52565249
}
52575250
isTypeOnly := ast.IsExclusivelyTypeOnlyImportOrExport(declaration)
52585251
override := c.getResolutionModeOverride(node.AsImportAttributes(), isTypeOnly)
5259-
isImportAttributes := node.AsImportAttributes().Token == ast.KindWithKeyword
52605252
if isTypeOnly && override != core.ResolutionModeNone {
5261-
return // Other grammar checks do not apply to type-only imports with resolution mode assertions
5253+
return // Other grammar checks do not apply to type-only imports with resolution mode attributes
52625254
}
52635255

52645256
if !c.moduleKind.SupportsImportAttributes() {
5265-
if isImportAttributes {
5266-
c.grammarErrorOnNode(node, diagnostics.Import_attributes_are_only_supported_when_the_module_option_is_set_to_esnext_node18_node20_nodenext_or_preserve)
5267-
} else {
5268-
c.grammarErrorOnNode(node, diagnostics.Import_assertions_are_only_supported_when_the_module_option_is_set_to_esnext_node18_node20_nodenext_or_preserve)
5269-
}
5270-
return
5271-
}
5272-
5273-
if core.ModuleKindNode20 <= c.moduleKind && c.moduleKind <= core.ModuleKindNodeNext && !isImportAttributes {
5274-
c.grammarErrorOnNode(node, diagnostics.Import_assertions_have_been_replaced_by_import_attributes_Use_with_instead_of_assert)
5257+
c.grammarErrorOnNode(node, diagnostics.Import_attributes_are_only_supported_when_the_module_option_is_set_to_esnext_node18_node20_nodenext_or_preserve)
52755258
return
52765259
}
52775260

52785261
if moduleSpecifier := getModuleSpecifierFromNode(declaration); moduleSpecifier != nil {
52795262
if c.getEmitSyntaxForModuleSpecifierExpression(moduleSpecifier) == core.ModuleKindCommonJS {
5280-
if isImportAttributes {
5281-
c.grammarErrorOnNode(node, diagnostics.Import_attributes_are_not_allowed_on_statements_that_compile_to_CommonJS_require_calls)
5282-
} else {
5283-
c.grammarErrorOnNode(node, diagnostics.Import_assertions_are_not_allowed_on_statements_that_compile_to_CommonJS_require_calls)
5284-
}
5263+
c.grammarErrorOnNode(node, diagnostics.Import_attributes_are_not_allowed_on_statements_that_compile_to_CommonJS_require_calls)
52855264
return
52865265
}
52875266
}
52885267

52895268
if isTypeOnly {
5290-
c.grammarErrorOnNode(node, core.IfElse(isImportAttributes,
5291-
diagnostics.Import_attributes_cannot_be_used_with_type_only_imports_or_exports,
5292-
diagnostics.Import_assertions_cannot_be_used_with_type_only_imports_or_exports))
5269+
c.grammarErrorOnNode(node, diagnostics.Import_attributes_cannot_be_used_with_type_only_imports_or_exports)
52935270
return
52945271
}
52955272
if override != core.ResolutionModeNone {
@@ -8063,6 +8040,14 @@ func (c *Checker) checkImportCallExpression(node *ast.Node) *Type {
80638040
if importCallOptionsType != c.emptyObjectType {
80648041
c.checkTypeAssignableTo(optionsType, c.getNullableType(importCallOptionsType, TypeFlagsUndefined), args[1], nil)
80658042
}
8043+
if ast.IsObjectLiteralExpression(args[1]) {
8044+
for _, prop := range args[1].AsObjectLiteralExpression().Properties.Nodes {
8045+
if ast.IsPropertyAssignment(prop) && ast.IsIdentifier(prop.Name()) && prop.Name().Text() == "assert" {
8046+
c.error(prop.Name(), diagnostics.Import_assertions_have_been_replaced_by_import_attributes_Use_with_instead_of_assert)
8047+
break
8048+
}
8049+
}
8050+
}
80668051
}
80678052
// resolveExternalModuleName will return undefined if the moduleReferenceExpression is not a string literal
80688053
moduleSymbol := c.resolveExternalModuleName(node, specifier, false /*ignoreErrors*/)

internal/parser/parser.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2428,6 +2428,9 @@ func (p *Parser) parseModuleExportName(disallowKeywords bool) (node *ast.Node, n
24282428

24292429
func (p *Parser) tryParseImportAttributes() *ast.Node {
24302430
if p.token == ast.KindWithKeyword || (p.token == ast.KindAssertKeyword && !p.hasPrecedingLineBreak()) {
2431+
if p.token == ast.KindAssertKeyword {
2432+
p.parseErrorAtCurrentToken(diagnostics.Import_assertions_have_been_replaced_by_import_attributes_Use_with_instead_of_assert)
2433+
}
24312434
return p.parseImportAttributes(p.token, false /*skipKeyword*/)
24322435
}
24332436
return nil
@@ -2492,6 +2495,9 @@ func (p *Parser) parseExportDeclaration(pos int, jsdoc jsdocScannerInfo, modifie
24922495
}
24932496
}
24942497
if moduleSpecifier != nil && (p.token == ast.KindWithKeyword || p.token == ast.KindAssertKeyword) && !p.hasPrecedingLineBreak() {
2498+
if p.token == ast.KindAssertKeyword {
2499+
p.parseErrorAtCurrentToken(diagnostics.Import_assertions_have_been_replaced_by_import_attributes_Use_with_instead_of_assert)
2500+
}
24952501
attributes = p.parseImportAttributes(p.token, false /*skipKeyword*/)
24962502
}
24972503
p.parseSemicolon()
@@ -2963,6 +2969,9 @@ func (p *Parser) parseImportType() *ast.Node {
29632969
p.parseExpected(ast.KindOpenBraceToken)
29642970
currentToken := p.token
29652971
if currentToken == ast.KindWithKeyword || currentToken == ast.KindAssertKeyword {
2972+
if currentToken == ast.KindAssertKeyword {
2973+
p.parseErrorAtCurrentToken(diagnostics.Import_assertions_have_been_replaced_by_import_attributes_Use_with_instead_of_assert)
2974+
}
29662975
p.nextToken()
29672976
} else {
29682977
p.parseErrorAtCurrentToken(diagnostics.X_0_expected, scanner.TokenToString(ast.KindWithKeyword))
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/a.ts(1,35): error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.
2+
/b.ts(1,37): error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.
3+
/c.ts(1,51): error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.
4+
5+
6+
==== /a.ts (1 errors) ====
7+
import json from "./package.json" assert { type: "json" };
8+
~~~~~~
9+
!!! error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.
10+
11+
==== /b.ts (1 errors) ====
12+
import * as data from "./data.json" assert { type: "json" };
13+
~~~~~~
14+
!!! error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.
15+
16+
==== /c.ts (1 errors) ====
17+
export { default as config } from "./config.json" assert { type: "json" };
18+
~~~~~~
19+
!!! error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.
20+
21+
==== /package.json (0 errors) ====
22+
{}
23+
24+
==== /data.json (0 errors) ====
25+
{}
26+
27+
==== /config.json (0 errors) ====
28+
{}
29+

testdata/baselines/reference/submodule/compiler/importAssertionsDeprecated.errors.txt.diff

Lines changed: 0 additions & 33 deletions
This file was deleted.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/a.ts(2,35): error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.
2+
/b.ts(1,37): error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.
3+
/c.ts(1,51): error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.
4+
5+
6+
==== /a.ts (1 errors) ====
7+
// With ignoreDeprecations: "6.0", import assertions should not produce a deprecation error.
8+
import json from "./package.json" assert { type: "json" };
9+
~~~~~~
10+
!!! error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.
11+
12+
==== /b.ts (1 errors) ====
13+
import * as data from "./data.json" assert { type: "json" };
14+
~~~~~~
15+
!!! error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.
16+
17+
==== /c.ts (1 errors) ====
18+
export { default as config } from "./config.json" assert { type: "json" };
19+
~~~~~~
20+
!!! error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.
21+
22+
==== /package.json (0 errors) ====
23+
{}
24+
25+
==== /data.json (0 errors) ====
26+
{}
27+
28+
==== /config.json (0 errors) ====
29+
{}
30+
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
--- old.importAssertionsDeprecatedIgnored.errors.txt
2+
+++ new.importAssertionsDeprecatedIgnored.errors.txt
3+
@@= skipped -0, +0 lines =@@
4+
-<no content>
5+
+/a.ts(2,35): error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.
6+
+/b.ts(1,37): error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.
7+
+/c.ts(1,51): error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.
8+
+
9+
+
10+
+==== /a.ts (1 errors) ====
11+
+ // With ignoreDeprecations: "6.0", import assertions should not produce a deprecation error.
12+
+ import json from "./package.json" assert { type: "json" };
13+
+ ~~~~~~
14+
+!!! error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.
15+
+
16+
+==== /b.ts (1 errors) ====
17+
+ import * as data from "./data.json" assert { type: "json" };
18+
+ ~~~~~~
19+
+!!! error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.
20+
+
21+
+==== /c.ts (1 errors) ====
22+
+ export { default as config } from "./config.json" assert { type: "json" };
23+
+ ~~~~~~
24+
+!!! error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.
25+
+
26+
+==== /package.json (0 errors) ====
27+
+ {}
28+
+
29+
+==== /data.json (0 errors) ====
30+
+ {}
31+
+
32+
+==== /config.json (0 errors) ====
33+
+ {}
34+
+
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/main.ts(1,30): error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.
2+
/main.ts(2,30): error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.
3+
/main.ts(4,31): error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.
4+
/main.ts(5,31): error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.
5+
6+
7+
==== /types.d.ts (0 errors) ====
8+
export interface MyType { x: string }
9+
10+
==== /main.ts (4 errors) ====
11+
type A = import("./types", { assert: { "resolution-mode": "import" } }).MyType;
12+
~~~~~~
13+
!!! error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.
14+
type B = import("./types", { assert: { "resolution-mode": "require" } }).MyType;
15+
~~~~~~
16+
!!! error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.
17+
18+
const a = import("./types", { assert: { "resolution-mode": "import" } });
19+
~~~~~~
20+
!!! error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.
21+
const b = import("./types", { assert: { "resolution-mode": "require" } });
22+
~~~~~~
23+
!!! error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.
24+

testdata/baselines/reference/submodule/compiler/importTypeAssertionDeprecation.errors.txt.diff

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,21 @@
33
@@= skipped -0, +0 lines =@@
44
-/main.ts(1,38): error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.
55
-/main.ts(2,38): error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.
6-
-/main.ts(4,31): error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.
7-
-/main.ts(5,31): error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.
8-
-
9-
-
10-
-==== /types.d.ts (0 errors) ====
11-
- export interface MyType { x: string }
12-
-
13-
-==== /main.ts (4 errors) ====
14-
- type A = import("./types", { assert: { "resolution-mode": "import" } }).MyType;
6+
+/main.ts(1,30): error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.
7+
+/main.ts(2,30): error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.
8+
/main.ts(4,31): error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.
9+
/main.ts(5,31): error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.
10+
11+
@@= skipped -8, +8 lines =@@
12+
13+
==== /main.ts (4 errors) ====
14+
type A = import("./types", { assert: { "resolution-mode": "import" } }).MyType;
1515
- ~
16-
-!!! error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.
17-
- type B = import("./types", { assert: { "resolution-mode": "require" } }).MyType;
16+
+ ~~~~~~
17+
!!! error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.
18+
type B = import("./types", { assert: { "resolution-mode": "require" } }).MyType;
1819
- ~
19-
-!!! error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.
20-
-
21-
- const a = import("./types", { assert: { "resolution-mode": "import" } });
22-
- ~~~~~~
23-
-!!! error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.
24-
- const b = import("./types", { assert: { "resolution-mode": "require" } });
25-
- ~~~~~~
26-
-!!! error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.
27-
-
28-
+<no content>
20+
+ ~~~~~~
21+
!!! error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.
22+
23+
const a = import("./types", { assert: { "resolution-mode": "import" } });
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/main.ts(2,30): error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.
2+
/main.ts(3,30): error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.
3+
/main.ts(5,31): error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.
4+
/main.ts(6,31): error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.
5+
6+
7+
==== /types.d.ts (0 errors) ====
8+
export interface MyType { x: string }
9+
10+
==== /main.ts (4 errors) ====
11+
// With ignoreDeprecations: "6.0", import type assertions should not produce a deprecation error.
12+
type A = import("./types", { assert: { "resolution-mode": "import" } }).MyType;
13+
~~~~~~
14+
!!! error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.
15+
type B = import("./types", { assert: { "resolution-mode": "require" } }).MyType;
16+
~~~~~~
17+
!!! error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.
18+
19+
const a = import("./types", { assert: { "resolution-mode": "import" } });
20+
~~~~~~
21+
!!! error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.
22+
const b = import("./types", { assert: { "resolution-mode": "require" } });
23+
~~~~~~
24+
!!! error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.
25+

0 commit comments

Comments
 (0)