Skip to content

Commit 7df3a40

Browse files
Make the compiler resilient to encountering merge conflict markers in a source code file.
1 parent 997aadb commit 7df3a40

8 files changed

Lines changed: 138 additions & 3 deletions

File tree

src/compiler/diagnosticInformationMap.generated.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ module ts {
144144
Array_element_destructuring_pattern_expected: { code: 1181, category: DiagnosticCategory.Error, key: "Array element destructuring pattern expected." },
145145
A_destructuring_declaration_must_have_an_initializer: { code: 1182, category: DiagnosticCategory.Error, key: "A destructuring declaration must have an initializer." },
146146
Destructuring_declarations_are_not_allowed_in_ambient_contexts: { code: 1183, category: DiagnosticCategory.Error, key: "Destructuring declarations are not allowed in ambient contexts." },
147+
Merge_conflict_marker_encountered: { code: 1184, category: DiagnosticCategory.Error, key: "Merge conflict marker encountered." },
147148
Duplicate_identifier_0: { code: 2300, category: DiagnosticCategory.Error, key: "Duplicate identifier '{0}'." },
148149
Initializer_of_instance_member_variable_0_cannot_reference_identifier_1_declared_in_the_constructor: { code: 2301, category: DiagnosticCategory.Error, key: "Initializer of instance member variable '{0}' cannot reference identifier '{1}' declared in the constructor." },
149150
Static_members_cannot_reference_class_type_parameters: { code: 2302, category: DiagnosticCategory.Error, key: "Static members cannot reference class type parameters." },

src/compiler/diagnosticMessages.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,10 @@
568568
"category": "Error",
569569
"code": 1183
570570
},
571+
"Merge conflict marker encountered.": {
572+
"category": "Error",
573+
"code": 1184
574+
},
571575

572576
"Duplicate identifier '{0}'.": {
573577
"category": "Error",

src/compiler/scanner.ts

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -333,10 +333,14 @@ module ts {
333333
var ch = text.charCodeAt(pos);
334334
switch (ch) {
335335
case CharacterCodes.carriageReturn:
336-
if (text.charCodeAt(pos + 1) === CharacterCodes.lineFeed) pos++;
336+
if (text.charCodeAt(pos + 1) === CharacterCodes.lineFeed) {
337+
pos++;
338+
}
337339
case CharacterCodes.lineFeed:
338340
pos++;
339-
if (stopAfterLineBreak) return pos;
341+
if (stopAfterLineBreak) {
342+
return pos;
343+
}
340344
continue;
341345
case CharacterCodes.tab:
342346
case CharacterCodes.verticalTab:
@@ -367,6 +371,14 @@ module ts {
367371
continue;
368372
}
369373
break;
374+
case CharacterCodes.lessThan:
375+
case CharacterCodes.equals:
376+
case CharacterCodes.greaterThan:
377+
if (isConflictMarkerTrivia(text, pos)) {
378+
pos = scanConflictMarkerTrivia(text, pos);
379+
continue;
380+
}
381+
// fall through
370382
default:
371383
if (ch > CharacterCodes.maxAsciiCharacter && (isWhiteSpace(ch) || isLineBreak(ch))) {
372384
pos++;
@@ -378,6 +390,39 @@ module ts {
378390
}
379391
}
380392

393+
function isConflictMarkerTrivia(text: string, pos: number) {
394+
// Conflict markers must be at the start of a line.
395+
if (pos > 0 && isLineBreak(text.charCodeAt(pos - 1))) {
396+
var ch = text.charCodeAt(pos);
397+
398+
// All conflict markers the same character repeated seven times. If it is a
399+
// <<<<<<< or >>>>>>> marker then it is also followd by a space.
400+
var markerLength = "<<<<<<<".length;
401+
402+
if ((pos + markerLength) < text.length) {
403+
for (var i = 0, n = markerLength; i < n; i++) {
404+
if (text.charCodeAt(pos + i) !== ch) {
405+
return false;
406+
}
407+
}
408+
409+
return ch === CharacterCodes.equals ||
410+
text.charCodeAt(pos + markerLength) === CharacterCodes.space;
411+
}
412+
}
413+
414+
return false;
415+
}
416+
417+
function scanConflictMarkerTrivia(text: string, pos: number) {
418+
var len = text.length;
419+
while (pos < len && !isLineBreak(text.charCodeAt(pos))) {
420+
pos++;
421+
}
422+
423+
return pos;
424+
}
425+
381426
// Extract comments from the given source text starting at the given position. If trailing is false, whitespace is skipped until
382427
// the first line break and comments between that location and the next token are returned. If trailing is true, comments occurring
383428
// between the given position and the next line break are returned. The return value is an array containing a TextRange for each
@@ -1010,6 +1055,17 @@ module ts {
10101055
case CharacterCodes.semicolon:
10111056
return pos++, token = SyntaxKind.SemicolonToken;
10121057
case CharacterCodes.lessThan:
1058+
if (isConflictMarkerTrivia(text, pos)) {
1059+
error(Diagnostics.Merge_conflict_marker_encountered);
1060+
pos = scanConflictMarkerTrivia(text, pos);
1061+
if (skipTrivia) {
1062+
continue;
1063+
}
1064+
else {
1065+
return token = SyntaxKind.ConflictMarkerTrivia;
1066+
}
1067+
}
1068+
10131069
if (text.charCodeAt(pos + 1) === CharacterCodes.lessThan) {
10141070
if (text.charCodeAt(pos + 2) === CharacterCodes.equals) {
10151071
return pos += 3, token = SyntaxKind.LessThanLessThanEqualsToken;
@@ -1021,6 +1077,17 @@ module ts {
10211077
}
10221078
return pos++, token = SyntaxKind.LessThanToken;
10231079
case CharacterCodes.equals:
1080+
if (isConflictMarkerTrivia(text, pos)) {
1081+
error(Diagnostics.Merge_conflict_marker_encountered);
1082+
pos = scanConflictMarkerTrivia(text, pos);
1083+
if (skipTrivia) {
1084+
continue;
1085+
}
1086+
else {
1087+
return token = SyntaxKind.ConflictMarkerTrivia;
1088+
}
1089+
}
1090+
10241091
if (text.charCodeAt(pos + 1) === CharacterCodes.equals) {
10251092
if (text.charCodeAt(pos + 2) === CharacterCodes.equals) {
10261093
return pos += 3, token = SyntaxKind.EqualsEqualsEqualsToken;
@@ -1032,6 +1099,17 @@ module ts {
10321099
}
10331100
return pos++, token = SyntaxKind.EqualsToken;
10341101
case CharacterCodes.greaterThan:
1102+
if (isConflictMarkerTrivia(text, pos)) {
1103+
error(Diagnostics.Merge_conflict_marker_encountered);
1104+
pos = scanConflictMarkerTrivia(text, pos);
1105+
if (skipTrivia) {
1106+
continue;
1107+
}
1108+
else {
1109+
return token = SyntaxKind.ConflictMarkerTrivia;
1110+
}
1111+
}
1112+
10351113
return pos++, token = SyntaxKind.GreaterThanToken;
10361114
case CharacterCodes.question:
10371115
return pos++, token = SyntaxKind.QuestionToken;

src/compiler/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ module ts {
1515
MultiLineCommentTrivia,
1616
NewLineTrivia,
1717
WhitespaceTrivia,
18+
ConflictMarkerTrivia,
1819
// Literals
1920
NumericLiteral,
2021
StringLiteral,
@@ -266,7 +267,7 @@ module ts {
266267
FirstToken = Unknown,
267268
LastToken = TypeKeyword,
268269
FirstTriviaToken = SingleLineCommentTrivia,
269-
LastTriviaToken = WhitespaceTrivia,
270+
LastTriviaToken = ConflictMarkerTrivia,
270271
FirstLiteralToken = NumericLiteral,
271272
LastLiteralToken = NoSubstitutionTemplateLiteral,
272273
FirstTemplateToken = NoSubstitutionTemplateLiteral,

src/services/services.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5780,6 +5780,7 @@ module ts {
57805780
return TokenClass.StringLiteral;
57815781
case SyntaxKind.RegularExpressionLiteral:
57825782
return TokenClass.RegExpLiteral;
5783+
case SyntaxKind.ConflictMarkerTrivia:
57835784
case SyntaxKind.MultiLineCommentTrivia:
57845785
case SyntaxKind.SingleLineCommentTrivia:
57855786
return TokenClass.Comment;
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
tests/cases/compiler/conflictMarkerTrivia1.ts(2,1): error TS1184: Merge conflict marker encountered.
2+
tests/cases/compiler/conflictMarkerTrivia1.ts(4,1): error TS1184: Merge conflict marker encountered.
3+
tests/cases/compiler/conflictMarkerTrivia1.ts(6,1): error TS1184: Merge conflict marker encountered.
4+
tests/cases/compiler/conflictMarkerTrivia1.ts(3,5): error TS2300: Duplicate identifier 'v'.
5+
tests/cases/compiler/conflictMarkerTrivia1.ts(5,5): error TS2300: Duplicate identifier 'v'.
6+
7+
8+
==== tests/cases/compiler/conflictMarkerTrivia1.ts (5 errors) ====
9+
class C {
10+
<<<<<<< HEAD
11+
12+
!!! error TS1184: Merge conflict marker encountered.
13+
v = 1;
14+
~
15+
!!! error TS2300: Duplicate identifier 'v'.
16+
=======
17+
18+
!!! error TS1184: Merge conflict marker encountered.
19+
v = 2;
20+
~
21+
!!! error TS2300: Duplicate identifier 'v'.
22+
>>>>>>> Branch-a
23+
24+
!!! error TS1184: Merge conflict marker encountered.
25+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
class C {
2+
<<<<<<< HEAD
3+
v = 1;
4+
=======
5+
v = 2;
6+
>>>>>>> Branch-a
7+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////class C {
4+
////<<<<<<< HEAD
5+
//// v = 1;
6+
////=======
7+
////v = 2;
8+
////>>>>>>> Branch - a
9+
////}
10+
11+
format.document();
12+
verify.currentFileContentIs("class C {\r\n\
13+
<<<<<<< HEAD\r\n\
14+
v = 1;\r\n\
15+
=======\r\n\
16+
v = 2;\r\n\
17+
>>>>>>> Branch - a\r\n\
18+
}");

0 commit comments

Comments
 (0)