Skip to content

Commit 3cd0e9f

Browse files
Merge branch 'unterminator' into taggedSigHelpAtEnd
Conflicts: src/compiler/parser.ts src/services/signatureHelp.ts
2 parents 46991b7 + 0e122b5 commit 3cd0e9f

File tree

7 files changed

+28
-62
lines changed

7 files changed

+28
-62
lines changed

src/compiler/checker.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -5302,15 +5302,15 @@ module ts {
53025302
var templateExpression = <TemplateExpression>tagExpression.template;
53035303
var lastSpan = lastOrUndefined(templateExpression.templateSpans);
53045304
Debug.assert(lastSpan !== undefined); // we should always have at least one span.
5305-
callIsIncomplete = lastSpan.literal.kind === SyntaxKind.Missing || isUnterminatedTemplateEnd(lastSpan.literal);
5305+
callIsIncomplete = lastSpan.literal.kind === SyntaxKind.Missing || !!lastSpan.literal.isUnterminated;
53065306
}
53075307
else {
53085308
// If the template didn't end in a backtick, or its beginning occurred right prior to EOF,
53095309
// then this might actually turn out to be a TemplateHead in the future;
53105310
// so we consider the call to be incomplete.
53115311
var templateLiteral = <LiteralExpression>tagExpression.template;
53125312
Debug.assert(templateLiteral.kind === SyntaxKind.NoSubstitutionTemplateLiteral);
5313-
callIsIncomplete = isUnterminatedTemplateEnd(templateLiteral);
5313+
callIsIncomplete = !!templateLiteral.isUnterminated;
53145314
}
53155315
}
53165316
else {

src/compiler/parser.ts

+4-20
Original file line numberDiff line numberDiff line change
@@ -809,26 +809,6 @@ module ts {
809809
return SyntaxKind.FirstTriviaToken <= token && token <= SyntaxKind.LastTriviaToken;
810810
}
811811

812-
export function isUnterminatedTemplateEnd(node: LiteralExpression) {
813-
Debug.assert(isTemplateLiteralKind(node.kind));
814-
var sourceText = getSourceFileOfNode(node).text;
815-
816-
// If we're not at the EOF, we know we must be terminated.
817-
if (node.end !== sourceText.length) {
818-
return false;
819-
}
820-
821-
// The literal can only be unterminated if it is a template tail or a no-sub template.
822-
if (node.kind !== SyntaxKind.TemplateTail && node.kind !== SyntaxKind.NoSubstitutionTemplateLiteral) {
823-
return false;
824-
}
825-
826-
// If we didn't end in a backtick, we must still be in the middle of a template literal,
827-
// but if it's the *initial* backtick (whereby the token is 1 char long), then it's unclosed.
828-
var width = node.end - getTokenPosOfNode(node);
829-
return width < 2 || sourceText.charCodeAt(node.end - 1) !== CharacterCodes.backtick;
830-
}
831-
832812
export function isModifier(token: SyntaxKind): boolean {
833813
switch (token) {
834814
case SyntaxKind.PublicKeyword:
@@ -1545,6 +1525,10 @@ module ts {
15451525
var text = scanner.getTokenValue();
15461526
node.text = internName ? internIdentifier(text) : text;
15471527

1528+
if (scanner.isUnterminated()) {
1529+
node.isUnterminated = true;
1530+
}
1531+
15481532
var tokenPos = scanner.getTokenPos();
15491533
nextToken();
15501534
finishNode(node);

src/compiler/scanner.ts

+10
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ module ts {
2222
hasPrecedingLineBreak(): boolean;
2323
isIdentifier(): boolean;
2424
isReservedWord(): boolean;
25+
isUnterminated(): boolean;
2526
reScanGreaterToken(): SyntaxKind;
2627
reScanSlashToken(): SyntaxKind;
2728
reScanTemplateToken(): SyntaxKind;
@@ -470,6 +471,7 @@ module ts {
470471
var token: SyntaxKind;
471472
var tokenValue: string;
472473
var precedingLineBreak: boolean;
474+
var tokenUnterminated: boolean;
473475

474476
function error(message: DiagnosticMessage): void {
475477
if (onError) {
@@ -553,6 +555,7 @@ module ts {
553555
while (true) {
554556
if (pos >= len) {
555557
result += text.substring(start, pos);
558+
tokenUnterminated = true;
556559
error(Diagnostics.Unterminated_string_literal);
557560
break;
558561
}
@@ -570,6 +573,7 @@ module ts {
570573
}
571574
if (isLineBreak(ch)) {
572575
result += text.substring(start, pos);
576+
tokenUnterminated = true;
573577
error(Diagnostics.Unterminated_string_literal);
574578
break;
575579
}
@@ -593,6 +597,7 @@ module ts {
593597
while (true) {
594598
if (pos >= len) {
595599
contents += text.substring(start, pos);
600+
tokenUnterminated = true;
596601
error(Diagnostics.Unterminated_template_literal);
597602
resultingToken = startedWithBacktick ? SyntaxKind.NoSubstitutionTemplateLiteral : SyntaxKind.TemplateTail;
598603
break;
@@ -756,6 +761,7 @@ module ts {
756761
function scan(): SyntaxKind {
757762
startPos = pos;
758763
precedingLineBreak = false;
764+
tokenUnterminated = false;
759765
while (true) {
760766
tokenPos = pos;
761767
if (pos >= len) {
@@ -912,6 +918,7 @@ module ts {
912918
continue;
913919
}
914920
else {
921+
tokenUnterminated = !commentClosed;
915922
return token = SyntaxKind.MultiLineCommentTrivia;
916923
}
917924
}
@@ -1069,12 +1076,14 @@ module ts {
10691076
// If we reach the end of a file, or hit a newline, then this is an unterminated
10701077
// regex. Report error and return what we have so far.
10711078
if (p >= len) {
1079+
tokenUnterminated = true;
10721080
error(Diagnostics.Unterminated_regular_expression_literal)
10731081
break;
10741082
}
10751083

10761084
var ch = text.charCodeAt(p);
10771085
if (isLineBreak(ch)) {
1086+
tokenUnterminated = true;
10781087
error(Diagnostics.Unterminated_regular_expression_literal)
10791088
break;
10801089
}
@@ -1167,6 +1176,7 @@ module ts {
11671176
hasPrecedingLineBreak: () => precedingLineBreak,
11681177
isIdentifier: () => token === SyntaxKind.Identifier || token > SyntaxKind.LastReservedWord,
11691178
isReservedWord: () => token >= SyntaxKind.FirstReservedWord && token <= SyntaxKind.LastReservedWord,
1179+
isUnterminated: () => tokenUnterminated,
11701180
reScanGreaterToken,
11711181
reScanSlashToken,
11721182
reScanTemplateToken,

src/compiler/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,7 @@ module ts {
467467
// For a NumericLiteral, the stored value is the toString() representation of the number. For example 1, 1.00, and 1e0 are all stored as just "1".
468468
export interface LiteralExpression extends Expression {
469469
text: string;
470+
isUnterminated?: boolean;
470471
}
471472

472473
export interface TemplateExpression extends Expression {

src/services/services.ts

+7-36
Original file line numberDiff line numberDiff line change
@@ -2497,47 +2497,21 @@ module ts {
24972497
}
24982498

24992499
function isInStringOrRegularExpressionOrTemplateLiteral(previousToken: Node): boolean {
2500-
if (previousToken.kind === SyntaxKind.StringLiteral || isTemplateLiteralKind(previousToken.kind)) {
2500+
if (previousToken.kind === SyntaxKind.StringLiteral
2501+
|| previousToken.kind === SyntaxKind.RegularExpressionLiteral
2502+
|| isTemplateLiteralKind(previousToken.kind)) {
25012503
// The position has to be either: 1. entirely within the token text, or
2502-
// 2. at the end position, and the string literal is not terminated
2503-
2504+
// 2. at the end position of an unterminated token.
25042505
var start = previousToken.getStart();
25052506
var end = previousToken.getEnd();
25062507

25072508
if (start < position && position < end) {
25082509
return true;
25092510
}
25102511
else if (position === end) {
2511-
var width = end - start;
2512-
var text = previousToken.getSourceFile().text;
2513-
2514-
// If the token is a single character, or its second-to-last charcter indicates an escape code,
2515-
// then we can immediately say that we are in the middle of an unclosed string.
2516-
if (width <= 1 || text.charCodeAt(end - 2) === CharacterCodes.backslash) {
2517-
return true;
2518-
}
2519-
2520-
// Now check if the last character is a closing character for the token.
2521-
switch (previousToken.kind) {
2522-
case SyntaxKind.StringLiteral:
2523-
case SyntaxKind.NoSubstitutionTemplateLiteral:
2524-
return text.charCodeAt(start) !== text.charCodeAt(end - 1);
2525-
2526-
case SyntaxKind.TemplateHead:
2527-
case SyntaxKind.TemplateMiddle:
2528-
return text.charCodeAt(end - 1) !== CharacterCodes.openBrace
2529-
|| text.charCodeAt(end - 2) !== CharacterCodes.$;
2530-
2531-
case SyntaxKind.TemplateTail:
2532-
return text.charCodeAt(end - 1) !== CharacterCodes.backtick;
2533-
}
2534-
2535-
return false;
2512+
return !!(<LiteralExpression>previousToken).isUnterminated;
25362513
}
25372514
}
2538-
else if (previousToken.kind === SyntaxKind.RegularExpressionLiteral) {
2539-
return previousToken.getStart() < position && position < previousToken.getEnd();
2540-
}
25412515

25422516
return false;
25432517
}
@@ -5579,7 +5553,7 @@ module ts {
55795553
if (token === SyntaxKind.StringLiteral) {
55805554
// Check to see if we finished up on a multiline string literal.
55815555
var tokenText = scanner.getTokenText();
5582-
if (tokenText.length > 0 && tokenText.charCodeAt(tokenText.length - 1) === CharacterCodes.backslash) {
5556+
if (scanner.isUnterminated()) {
55835557
var quoteChar = tokenText.charCodeAt(0);
55845558
result.finalLexState = quoteChar === CharacterCodes.doubleQuote
55855559
? EndOfLineState.InDoubleQuoteStringLiteral
@@ -5588,10 +5562,7 @@ module ts {
55885562
}
55895563
else if (token === SyntaxKind.MultiLineCommentTrivia) {
55905564
// Check to see if the multiline comment was unclosed.
5591-
var tokenText = scanner.getTokenText()
5592-
if (!(tokenText.length > 3 && // need to avoid catching '/*/'
5593-
tokenText.charCodeAt(tokenText.length - 2) === CharacterCodes.asterisk &&
5594-
tokenText.charCodeAt(tokenText.length - 1) === CharacterCodes.slash)) {
5565+
if (scanner.isUnterminated()) {
55955566
result.finalLexState = EndOfLineState.InMultiLineCommentTrivia;
55965567
}
55975568
}

src/services/signatureHelp.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -295,8 +295,8 @@ module ts.SignatureHelp {
295295
var tagExpression = <TaggedTemplateExpression>templateExpression.parent;
296296
Debug.assert(templateExpression.kind === SyntaxKind.TemplateExpression);
297297

298-
// If we're just after a template tail, don't show signature help.
299-
if (node.kind === SyntaxKind.TemplateTail && !isInsideTemplateLiteral(<LiteralExpression>node, position)) {
298+
// If we're just after a template tail, don't show signature help.
299+
if (node.kind === SyntaxKind.TemplateTail && !isInsideTemplateLiteral(<LiteralExpression>node, position)) {
300300
return undefined;
301301
}
302302

src/services/utilities.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ module ts {
322322
}
323323

324324
export function isInsideTemplateLiteral(node: LiteralExpression, position: number) {
325-
return (node.getStart() < position && position < node.getEnd())
326-
|| (isUnterminatedTemplateEnd(node) && position === node.getEnd());
325+
return isTemplateLiteralKind(node.kind)
326+
&& (node.getStart() < position && position < node.getEnd()) || (!!node.isUnterminated && position === node.getEnd());
327327
}
328328
}

0 commit comments

Comments
 (0)