Skip to content

Commit bae0f50

Browse files
authored
Fix import assertion occurrences crash and make import assertion parsing more generous (microsoft#47535)
1 parent 04d77fe commit bae0f50

15 files changed

+154
-14
lines changed

src/compiler/checker.ts

+10
Original file line numberDiff line numberDiff line change
@@ -39786,6 +39786,16 @@ namespace ts {
3978639786
return false;
3978739787
}
3978839788
}
39789+
if (!isImportEqualsDeclaration(node) && node.assertClause) {
39790+
let hasError = false;
39791+
for (const clause of node.assertClause.elements) {
39792+
if (!isStringLiteral(clause.value)) {
39793+
hasError = true;
39794+
error(clause.value, Diagnostics.Import_assertion_values_must_be_string_literal_expressions);
39795+
}
39796+
}
39797+
return !hasError;
39798+
}
3978939799
return true;
3979039800
}
3979139801

src/compiler/diagnosticMessages.json

+4
Original file line numberDiff line numberDiff line change
@@ -3369,6 +3369,10 @@
33693369
"category": "Error",
33703370
"code": 2836
33713371
},
3372+
"Import assertion values must be string literal expressions.": {
3373+
"category": "Error",
3374+
"code": 2837
3375+
},
33723376

33733377
"Import declaration '{0}' is using private name '{1}'.": {
33743378
"category": "Error",

src/compiler/factory/nodeFactory.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -4024,7 +4024,7 @@ namespace ts {
40244024
}
40254025

40264026
// @api
4027-
function createAssertEntry(name: AssertionKey, value: StringLiteral): AssertEntry {
4027+
function createAssertEntry(name: AssertionKey, value: Expression): AssertEntry {
40284028
const node = createBaseNode<AssertEntry>(SyntaxKind.AssertEntry);
40294029
node.name = name;
40304030
node.value = value;
@@ -4033,7 +4033,7 @@ namespace ts {
40334033
}
40344034

40354035
// @api
4036-
function updateAssertEntry(node: AssertEntry, name: AssertionKey, value: StringLiteral): AssertEntry {
4036+
function updateAssertEntry(node: AssertEntry, name: AssertionKey, value: Expression): AssertEntry {
40374037
return node.name !== name
40384038
|| node.value !== value
40394039
? update(createAssertEntry(name, value), node)

src/compiler/parser.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -7281,7 +7281,7 @@ namespace ts {
72817281
const pos = getNodePos();
72827282
const name = tokenIsIdentifierOrKeyword(token()) ? parseIdentifierName() : parseLiteralLikeNode(SyntaxKind.StringLiteral) as StringLiteral;
72837283
parseExpected(SyntaxKind.ColonToken);
7284-
const value = parseLiteralLikeNode(SyntaxKind.StringLiteral) as StringLiteral;
7284+
const value = parseAssignmentExpressionOrHigher();
72857285
return finishNode(factory.createAssertEntry(name, value), pos);
72867286
}
72877287

src/compiler/types.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -3055,7 +3055,7 @@ namespace ts {
30553055
readonly kind: SyntaxKind.AssertEntry;
30563056
readonly parent: AssertClause;
30573057
readonly name: AssertionKey;
3058-
readonly value: StringLiteral;
3058+
readonly value: Expression;
30593059
}
30603060

30613061
export interface AssertClause extends Node {
@@ -7446,8 +7446,8 @@ namespace ts {
74467446
updateImportClause(node: ImportClause, isTypeOnly: boolean, name: Identifier | undefined, namedBindings: NamedImportBindings | undefined): ImportClause;
74477447
createAssertClause(elements: NodeArray<AssertEntry>, multiLine?: boolean): AssertClause;
74487448
updateAssertClause(node: AssertClause, elements: NodeArray<AssertEntry>, multiLine?: boolean): AssertClause;
7449-
createAssertEntry(name: AssertionKey, value: StringLiteral): AssertEntry;
7450-
updateAssertEntry (node: AssertEntry, name: AssertionKey, value: StringLiteral): AssertEntry;
7449+
createAssertEntry(name: AssertionKey, value: Expression): AssertEntry;
7450+
updateAssertEntry(node: AssertEntry, name: AssertionKey, value: Expression): AssertEntry;
74517451
createNamespaceImport(name: Identifier): NamespaceImport;
74527452
updateNamespaceImport(node: NamespaceImport, name: Identifier): NamespaceImport;
74537453
createNamespaceExport(name: Identifier): NamespaceExport;

src/compiler/visitorPublic.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1090,7 +1090,7 @@ namespace ts {
10901090
Debug.type<AssertEntry>(node);
10911091
return factory.updateAssertEntry(node,
10921092
nodeVisitor(node.name, visitor, isAssertionKey),
1093-
nodeVisitor(node.value, visitor, isStringLiteral));
1093+
nodeVisitor(node.value, visitor, isExpressionNode));
10941094

10951095
case SyntaxKind.ImportClause:
10961096
Debug.type<ImportClause>(node);

src/services/findAllReferences.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -526,7 +526,7 @@ namespace ts.FindAllReferences {
526526
function getTextSpan(node: Node, sourceFile: SourceFile, endNode?: Node): TextSpan {
527527
let start = node.getStart(sourceFile);
528528
let end = (endNode || node).getEnd();
529-
if (isStringLiteralLike(node)) {
529+
if (isStringLiteralLike(node) && (end - start) > 2) {
530530
Debug.assert(endNode === undefined);
531531
start += 1;
532532
end -= 1;

tests/baselines/reference/api/tsserverlibrary.d.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1652,7 +1652,7 @@ declare namespace ts {
16521652
readonly kind: SyntaxKind.AssertEntry;
16531653
readonly parent: AssertClause;
16541654
readonly name: AssertionKey;
1655-
readonly value: StringLiteral;
1655+
readonly value: Expression;
16561656
}
16571657
export interface AssertClause extends Node {
16581658
readonly kind: SyntaxKind.AssertClause;
@@ -3576,8 +3576,8 @@ declare namespace ts {
35763576
updateImportClause(node: ImportClause, isTypeOnly: boolean, name: Identifier | undefined, namedBindings: NamedImportBindings | undefined): ImportClause;
35773577
createAssertClause(elements: NodeArray<AssertEntry>, multiLine?: boolean): AssertClause;
35783578
updateAssertClause(node: AssertClause, elements: NodeArray<AssertEntry>, multiLine?: boolean): AssertClause;
3579-
createAssertEntry(name: AssertionKey, value: StringLiteral): AssertEntry;
3580-
updateAssertEntry(node: AssertEntry, name: AssertionKey, value: StringLiteral): AssertEntry;
3579+
createAssertEntry(name: AssertionKey, value: Expression): AssertEntry;
3580+
updateAssertEntry(node: AssertEntry, name: AssertionKey, value: Expression): AssertEntry;
35813581
createNamespaceImport(name: Identifier): NamespaceImport;
35823582
updateNamespaceImport(node: NamespaceImport, name: Identifier): NamespaceImport;
35833583
createNamespaceExport(name: Identifier): NamespaceExport;

tests/baselines/reference/api/typescript.d.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1652,7 +1652,7 @@ declare namespace ts {
16521652
readonly kind: SyntaxKind.AssertEntry;
16531653
readonly parent: AssertClause;
16541654
readonly name: AssertionKey;
1655-
readonly value: StringLiteral;
1655+
readonly value: Expression;
16561656
}
16571657
export interface AssertClause extends Node {
16581658
readonly kind: SyntaxKind.AssertClause;
@@ -3576,8 +3576,8 @@ declare namespace ts {
35763576
updateImportClause(node: ImportClause, isTypeOnly: boolean, name: Identifier | undefined, namedBindings: NamedImportBindings | undefined): ImportClause;
35773577
createAssertClause(elements: NodeArray<AssertEntry>, multiLine?: boolean): AssertClause;
35783578
updateAssertClause(node: AssertClause, elements: NodeArray<AssertEntry>, multiLine?: boolean): AssertClause;
3579-
createAssertEntry(name: AssertionKey, value: StringLiteral): AssertEntry;
3580-
updateAssertEntry(node: AssertEntry, name: AssertionKey, value: StringLiteral): AssertEntry;
3579+
createAssertEntry(name: AssertionKey, value: Expression): AssertEntry;
3580+
updateAssertEntry(node: AssertEntry, name: AssertionKey, value: Expression): AssertEntry;
35813581
createNamespaceImport(name: Identifier): NamespaceImport;
35823582
updateNamespaceImport(node: NamespaceImport, name: Identifier): NamespaceImport;
35833583
createNamespaceExport(name: Identifier): NamespaceExport;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
tests/cases/compiler/mod.mts(1,52): error TS2837: Import assertion values must be string literal expressions.
2+
tests/cases/compiler/mod.mts(3,52): error TS2837: Import assertion values must be string literal expressions.
3+
tests/cases/compiler/mod.mts(5,52): error TS2837: Import assertion values must be string literal expressions.
4+
tests/cases/compiler/mod.mts(7,52): error TS2837: Import assertion values must be string literal expressions.
5+
tests/cases/compiler/mod.mts(9,52): error TS2837: Import assertion values must be string literal expressions.
6+
tests/cases/compiler/mod.mts(11,66): error TS2837: Import assertion values must be string literal expressions.
7+
8+
9+
==== tests/cases/compiler/mod.mts (6 errors) ====
10+
import * as thing1 from "./mod.mjs" assert {field: 0};
11+
~
12+
!!! error TS2837: Import assertion values must be string literal expressions.
13+
14+
import * as thing2 from "./mod.mjs" assert {field: `a`};
15+
~~~
16+
!!! error TS2837: Import assertion values must be string literal expressions.
17+
18+
import * as thing3 from "./mod.mjs" assert {field: /a/g};
19+
~~~~
20+
!!! error TS2837: Import assertion values must be string literal expressions.
21+
22+
import * as thing4 from "./mod.mjs" assert {field: ["a"]};
23+
~~~~~
24+
!!! error TS2837: Import assertion values must be string literal expressions.
25+
26+
import * as thing5 from "./mod.mjs" assert {field: { a: 0 }};
27+
~~~~~~~~
28+
!!! error TS2837: Import assertion values must be string literal expressions.
29+
30+
import * as thing6 from "./mod.mjs" assert {type: "json", field: 0..toString()}
31+
~~~~~~~~~~~~~
32+
!!! error TS2837: Import assertion values must be string literal expressions.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//// [mod.mts]
2+
import * as thing1 from "./mod.mjs" assert {field: 0};
3+
4+
import * as thing2 from "./mod.mjs" assert {field: `a`};
5+
6+
import * as thing3 from "./mod.mjs" assert {field: /a/g};
7+
8+
import * as thing4 from "./mod.mjs" assert {field: ["a"]};
9+
10+
import * as thing5 from "./mod.mjs" assert {field: { a: 0 }};
11+
12+
import * as thing6 from "./mod.mjs" assert {type: "json", field: 0..toString()}
13+
14+
//// [mod.mjs]
15+
export {};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
=== tests/cases/compiler/mod.mts ===
2+
import * as thing1 from "./mod.mjs" assert {field: 0};
3+
>thing1 : Symbol(thing1, Decl(mod.mts, 0, 6))
4+
5+
import * as thing2 from "./mod.mjs" assert {field: `a`};
6+
>thing2 : Symbol(thing2, Decl(mod.mts, 2, 6))
7+
8+
import * as thing3 from "./mod.mjs" assert {field: /a/g};
9+
>thing3 : Symbol(thing3, Decl(mod.mts, 4, 6))
10+
11+
import * as thing4 from "./mod.mjs" assert {field: ["a"]};
12+
>thing4 : Symbol(thing4, Decl(mod.mts, 6, 6))
13+
14+
import * as thing5 from "./mod.mjs" assert {field: { a: 0 }};
15+
>thing5 : Symbol(thing5, Decl(mod.mts, 8, 6))
16+
>a : Symbol(a, Decl(mod.mts, 8, 52))
17+
18+
import * as thing6 from "./mod.mjs" assert {type: "json", field: 0..toString()}
19+
>thing6 : Symbol(thing6, Decl(mod.mts, 10, 6))
20+
>0..toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --))
21+
>toString : Symbol(Number.toString, Decl(lib.es5.d.ts, --, --))
22+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
=== tests/cases/compiler/mod.mts ===
2+
import * as thing1 from "./mod.mjs" assert {field: 0};
3+
>thing1 : typeof thing1
4+
>field : any
5+
6+
import * as thing2 from "./mod.mjs" assert {field: `a`};
7+
>thing2 : typeof thing1
8+
>field : any
9+
10+
import * as thing3 from "./mod.mjs" assert {field: /a/g};
11+
>thing3 : typeof thing1
12+
>field : any
13+
>/a/g : RegExp
14+
15+
import * as thing4 from "./mod.mjs" assert {field: ["a"]};
16+
>thing4 : typeof thing1
17+
>field : any
18+
>["a"] : string[]
19+
>"a" : "a"
20+
21+
import * as thing5 from "./mod.mjs" assert {field: { a: 0 }};
22+
>thing5 : typeof thing1
23+
>field : any
24+
>{ a: 0 } : { a: number; }
25+
>a : number
26+
>0 : 0
27+
28+
import * as thing6 from "./mod.mjs" assert {type: "json", field: 0..toString()}
29+
>thing6 : typeof thing1
30+
>type : any
31+
>field : any
32+
>0..toString() : string
33+
>0..toString : (radix?: number) => string
34+
>0. : 0
35+
>toString : (radix?: number) => string
36+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// @module: nodenext
2+
// @filename: mod.mts
3+
import * as thing1 from "./mod.mjs" assert {field: 0};
4+
5+
import * as thing2 from "./mod.mjs" assert {field: `a`};
6+
7+
import * as thing3 from "./mod.mjs" assert {field: /a/g};
8+
9+
import * as thing4 from "./mod.mjs" assert {field: ["a"]};
10+
11+
import * as thing5 from "./mod.mjs" assert {field: { a: 0 }};
12+
13+
import * as thing6 from "./mod.mjs" assert {type: "json", field: 0..toString()}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
// @module: nodenext
4+
////import * as react from "react" assert { cache: /**/0 };
5+
////react.Children;
6+
7+
goTo.marker();
8+
verify.occurrencesAtPositionCount(0);

0 commit comments

Comments
 (0)