Skip to content

Commit 5045f87

Browse files
author
mukunda katta
committed
Improve label reference diagnostics
1 parent f350b52 commit 5045f87

7 files changed

Lines changed: 485 additions & 0 deletions

File tree

src/compiler/checker.ts

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53030,6 +53030,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
5303053030
let current: Node = node;
5303153031
while (current) {
5303253032
if (isFunctionLikeOrClassStaticBlockDeclaration(current)) {
53033+
if (node.label && !hasContainingLabel(node, current)) {
53034+
const label = findLabelInCurrentFunction(node);
53035+
if (label && label.pos > node.pos) {
53036+
return grammarErrorOnNodeWithRelatedLabel(node, Diagnostics.A_label_cannot_be_referenced_prior_to_its_declared_location, label);
53037+
}
53038+
}
53039+
5303353040
return grammarErrorOnNode(node, Diagnostics.Jump_target_cannot_cross_function_boundary);
5303453041
}
5303553042

@@ -53080,6 +53087,64 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
5308053087
}
5308153088
}
5308253089

53090+
function hasContainingLabel(node: BreakOrContinueStatement, boundary: Node): boolean {
53091+
Debug.assert(!!node.label);
53092+
53093+
for (let current = boundary.parent; current; current = current.parent) {
53094+
if (
53095+
current.kind === SyntaxKind.LabeledStatement &&
53096+
current.pos <= boundary.pos &&
53097+
boundary.end <= current.end &&
53098+
(current as LabeledStatement).label.escapedText === node.label.escapedText
53099+
) {
53100+
return true;
53101+
}
53102+
}
53103+
53104+
return false;
53105+
}
53106+
53107+
function findLabelInCurrentFunction(node: BreakOrContinueStatement): LabeledStatement | undefined {
53108+
Debug.assert(!!node.label);
53109+
53110+
for (let current: Node = node; current && !isFunctionLikeOrClassStaticBlockDeclaration(current); current = current.parent) {
53111+
if (!current.parent) {
53112+
break;
53113+
}
53114+
const statements = getStatementContainerStatements(current.parent);
53115+
if (statements) {
53116+
const label = find(statements, statement => statement.kind === SyntaxKind.LabeledStatement && (statement as LabeledStatement).label.escapedText === node.label!.escapedText) as LabeledStatement | undefined;
53117+
if (label) {
53118+
return label;
53119+
}
53120+
}
53121+
}
53122+
53123+
return undefined;
53124+
}
53125+
53126+
function getStatementContainerStatements(node: Node): NodeArray<Statement> | undefined {
53127+
switch (node.kind) {
53128+
case SyntaxKind.SourceFile:
53129+
return (node as SourceFile).statements;
53130+
case SyntaxKind.Block:
53131+
case SyntaxKind.ModuleBlock:
53132+
return (node as Block | ModuleBlock).statements;
53133+
case SyntaxKind.CaseClause:
53134+
case SyntaxKind.DefaultClause:
53135+
return (node as CaseOrDefaultClause).statements;
53136+
}
53137+
}
53138+
53139+
function grammarErrorOnNodeWithRelatedLabel(node: BreakOrContinueStatement, message: DiagnosticMessage, label: LabeledStatement): boolean {
53140+
const sourceFile = getSourceFileOfNode(node);
53141+
if (!hasParseDiagnostics(sourceFile)) {
53142+
addRelatedInfo(error(node, message), createDiagnosticForNode(label.label, Diagnostics._0_is_declared_here, idText(label.label)));
53143+
return true;
53144+
}
53145+
return false;
53146+
}
53147+
5308353148
function checkGrammarBindingElement(node: BindingElement) {
5308453149
if (node.dotDotDotToken) {
5308553150
const elements = node.parent.elements;

src/compiler/diagnosticMessages.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,10 @@
307307
"category": "Error",
308308
"code": 1107
309309
},
310+
"A label cannot be referenced prior to its declared location.": {
311+
"category": "Error",
312+
"code": 18001
313+
},
310314
"A 'return' statement can only be used within a function body.": {
311315
"category": "Error",
312316
"code": 1108
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
labelReferenceDiagnostics.ts(2,5): error TS18001: A label cannot be referenced prior to its declared location.
2+
labelReferenceDiagnostics.ts(8,5): error TS18001: A label cannot be referenced prior to its declared location.
3+
labelReferenceDiagnostics.ts(14,5): error TS1107: Jump target cannot cross function boundary.
4+
labelReferenceDiagnostics.ts(18,5): error TS1107: Jump target cannot cross function boundary.
5+
labelReferenceDiagnostics.ts(23,9): error TS1107: Jump target cannot cross function boundary.
6+
labelReferenceDiagnostics.ts(30,5): error TS1107: Jump target cannot cross function boundary.
7+
labelReferenceDiagnostics.ts(37,9): error TS1107: Jump target cannot cross function boundary.
8+
labelReferenceDiagnostics.ts(44,9): error TS1107: Jump target cannot cross function boundary.
9+
10+
11+
==== labelReferenceDiagnostics.ts (8 errors) ====
12+
function breakToLaterLabel() {
13+
break target;
14+
~~~~~~~~~~~~~
15+
!!! error TS18001: A label cannot be referenced prior to its declared location.
16+
!!! related TS2728 labelReferenceDiagnostics.ts:3:5: 'target' is declared here.
17+
target:
18+
while (true) {}
19+
}
20+
21+
function continueToLaterLabel() {
22+
continue target;
23+
~~~~~~~~~~~~~~~~
24+
!!! error TS18001: A label cannot be referenced prior to its declared location.
25+
!!! related TS2728 labelReferenceDiagnostics.ts:9:5: 'target' is declared here.
26+
target:
27+
while (true) {}
28+
}
29+
30+
function breakToMissingLabel() {
31+
break target;
32+
~~~~~~~~~~~~~
33+
!!! error TS1107: Jump target cannot cross function boundary.
34+
}
35+
36+
function breakToFunctionNameLabel() {
37+
break breakToFunctionNameLabel;
38+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
39+
!!! error TS1107: Jump target cannot cross function boundary.
40+
}
41+
42+
function continueToMissingLabel() {
43+
while (true) {
44+
continue target;
45+
~~~~~~~~~~~~~~~~
46+
!!! error TS1107: Jump target cannot cross function boundary.
47+
}
48+
}
49+
50+
function breakToNonEnclosingLabel() {
51+
target:
52+
console.log("target");
53+
break target;
54+
~~~~~~~~~~~~~
55+
!!! error TS1107: Jump target cannot cross function boundary.
56+
}
57+
58+
function continueToNonEnclosingLabel() {
59+
target:
60+
while (true) {}
61+
while (true) {
62+
continue target;
63+
~~~~~~~~~~~~~~~~
64+
!!! error TS1107: Jump target cannot cross function boundary.
65+
}
66+
}
67+
68+
target:
69+
while (true) {
70+
function crossesFunctionBoundary() {
71+
break target;
72+
~~~~~~~~~~~~~
73+
!!! error TS1107: Jump target cannot cross function boundary.
74+
}
75+
}
76+
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
//// [tests/cases/compiler/labelReferenceDiagnostics.ts] ////
2+
3+
//// [labelReferenceDiagnostics.ts]
4+
function breakToLaterLabel() {
5+
break target;
6+
target:
7+
while (true) {}
8+
}
9+
10+
function continueToLaterLabel() {
11+
continue target;
12+
target:
13+
while (true) {}
14+
}
15+
16+
function breakToMissingLabel() {
17+
break target;
18+
}
19+
20+
function breakToFunctionNameLabel() {
21+
break breakToFunctionNameLabel;
22+
}
23+
24+
function continueToMissingLabel() {
25+
while (true) {
26+
continue target;
27+
}
28+
}
29+
30+
function breakToNonEnclosingLabel() {
31+
target:
32+
console.log("target");
33+
break target;
34+
}
35+
36+
function continueToNonEnclosingLabel() {
37+
target:
38+
while (true) {}
39+
while (true) {
40+
continue target;
41+
}
42+
}
43+
44+
target:
45+
while (true) {
46+
function crossesFunctionBoundary() {
47+
break target;
48+
}
49+
}
50+
51+
52+
//// [labelReferenceDiagnostics.js]
53+
"use strict";
54+
function breakToLaterLabel() {
55+
break target;
56+
target: while (true) { }
57+
}
58+
function continueToLaterLabel() {
59+
continue target;
60+
target: while (true) { }
61+
}
62+
function breakToMissingLabel() {
63+
break target;
64+
}
65+
function breakToFunctionNameLabel() {
66+
break breakToFunctionNameLabel;
67+
}
68+
function continueToMissingLabel() {
69+
while (true) {
70+
continue target;
71+
}
72+
}
73+
function breakToNonEnclosingLabel() {
74+
target: console.log("target");
75+
break target;
76+
}
77+
function continueToNonEnclosingLabel() {
78+
target: while (true) { }
79+
while (true) {
80+
continue target;
81+
}
82+
}
83+
target: while (true) {
84+
function crossesFunctionBoundary() {
85+
break target;
86+
}
87+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
//// [tests/cases/compiler/labelReferenceDiagnostics.ts] ////
2+
3+
=== labelReferenceDiagnostics.ts ===
4+
function breakToLaterLabel() {
5+
>breakToLaterLabel : Symbol(breakToLaterLabel, Decl(labelReferenceDiagnostics.ts, 0, 0))
6+
7+
break target;
8+
target:
9+
while (true) {}
10+
}
11+
12+
function continueToLaterLabel() {
13+
>continueToLaterLabel : Symbol(continueToLaterLabel, Decl(labelReferenceDiagnostics.ts, 4, 1))
14+
15+
continue target;
16+
target:
17+
while (true) {}
18+
}
19+
20+
function breakToMissingLabel() {
21+
>breakToMissingLabel : Symbol(breakToMissingLabel, Decl(labelReferenceDiagnostics.ts, 10, 1))
22+
23+
break target;
24+
}
25+
26+
function breakToFunctionNameLabel() {
27+
>breakToFunctionNameLabel : Symbol(breakToFunctionNameLabel, Decl(labelReferenceDiagnostics.ts, 14, 1))
28+
29+
break breakToFunctionNameLabel;
30+
}
31+
32+
function continueToMissingLabel() {
33+
>continueToMissingLabel : Symbol(continueToMissingLabel, Decl(labelReferenceDiagnostics.ts, 18, 1))
34+
35+
while (true) {
36+
continue target;
37+
}
38+
}
39+
40+
function breakToNonEnclosingLabel() {
41+
>breakToNonEnclosingLabel : Symbol(breakToNonEnclosingLabel, Decl(labelReferenceDiagnostics.ts, 24, 1))
42+
43+
target:
44+
console.log("target");
45+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
46+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
47+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
48+
49+
break target;
50+
}
51+
52+
function continueToNonEnclosingLabel() {
53+
>continueToNonEnclosingLabel : Symbol(continueToNonEnclosingLabel, Decl(labelReferenceDiagnostics.ts, 30, 1))
54+
55+
target:
56+
while (true) {}
57+
while (true) {
58+
continue target;
59+
}
60+
}
61+
62+
target:
63+
while (true) {
64+
function crossesFunctionBoundary() {
65+
>crossesFunctionBoundary : Symbol(crossesFunctionBoundary, Decl(labelReferenceDiagnostics.ts, 41, 14))
66+
67+
break target;
68+
}
69+
}
70+

0 commit comments

Comments
 (0)