提交 1b0cf5c7 编写于 作者: C CyrusNajmabadi 提交者: GitHub

Merge pull request #15850 from CyrusNajmabadi/mergeMarkers

Provide a better experience editing a file with merge-markers in it.
......@@ -5992,6 +5992,15 @@ internal class CSharpResources {
}
}
/// <summary>
/// Looks up a localized string similar to Merge conflict marker encountered.
/// </summary>
internal static string ERR_Merge_conflict_marker_encountered {
get {
return ResourceManager.GetString("ERR_Merge_conflict_marker_encountered", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Name &apos;{0}&apos; exceeds the maximum length allowed in metadata..
/// </summary>
......
......@@ -2285,6 +2285,9 @@ If such a class is used as a base class and if the deriving class defines a dest
<data name="ERR_OpenEndedComment" xml:space="preserve">
<value>End-of-file found, '*/' expected</value>
</data>
<data name="ERR_Merge_conflict_marker_encountered" xml:space="preserve">
<value>Merge conflict marker encountered</value>
</data>
<data name="ERR_OvlOperatorExpected" xml:space="preserve">
<value>Overloadable operator expected</value>
</data>
......
......@@ -1458,5 +1458,7 @@ internal enum ErrorCode
ERR_TypeForwardedToMultipleAssemblies = 8206,
ERR_ExpressionTreeContainsDiscard = 8207,
#endregion more stragglers for C# 7
ERR_Merge_conflict_marker_encountered = 8300,
}
}
}
\ No newline at end of file
......@@ -928,7 +928,7 @@ private bool ScanInteger()
TextWindow.AdvanceChar();
}
return start < TextWindow.Position;
return start < TextWindow.Position;
}
// Allows underscores in integers, except at beginning and end
......@@ -2293,12 +2293,160 @@ private void LexSyntaxTrivia(bool afterFirstToken, bool isTrailing, ref SyntaxLi
{
return;
}
// Note: we specifically do not look for the >>>>>>> pattern as the start of
// a conflict marker trivia. That's because *technically* (albeit unlikely)
// >>>>>>> could be the end of a very generic construct. So, instead, we only
// recognize >>>>>>> as we are scanning the trivia after a ======= marker
// (which can never be part of legal code).
// case '>':
case '=':
case '<':
if (!isTrailing)
{
if (IsConflictMarkerTrivia())
{
this.LexConflictMarkerTrivia(ref triviaList);
break;
}
}
return;
default:
return;
}
}
}
// All conflict markers consist of the same character repeated seven times. If it is
// a <<<<<<< or >>>>>>> marker then it is also followed by a space.
private static readonly int s_conflictMarkerLength = "<<<<<<<".Length;
private bool IsConflictMarkerTrivia()
{
var position = TextWindow.Position;
var text = TextWindow.Text;
if (position == 0 || SyntaxFacts.IsNewLine(text[position - 1]))
{
var firstCh = text[position];
Debug.Assert(firstCh == '<' || firstCh == '=' || firstCh == '>');
if ((position + s_conflictMarkerLength) <= text.Length)
{
for (int i = 0, n = s_conflictMarkerLength; i < n; i++)
{
if (text[position + i] != firstCh)
{
return false;
}
}
if (firstCh == '=')
{
return true;
}
return (position + s_conflictMarkerLength) < text.Length &&
text[position + s_conflictMarkerLength] == ' ';
}
}
return false;
}
private void LexConflictMarkerTrivia(ref SyntaxListBuilder triviaList)
{
this.Start();
this.AddError(TextWindow.Position, s_conflictMarkerLength,
ErrorCode.ERR_Merge_conflict_marker_encountered);
var startCh = this.TextWindow.PeekChar();
// First create a trivia from the start of this merge conflict marker to the
// end of line/file (whichever comes first).
LexConflictMarkerHeader(ref triviaList);
// Now add the newlines as the next trivia.
LexConflictMarkerEndOfLine(ref triviaList);
// Now, if it was an ======= marker, then also created a DisabledText trivia for
// the contents of the file after it, up until the next >>>>>>> marker we see.
if (startCh == '=')
{
LexConflictMarkerDisabledText(ref triviaList);
}
}
private SyntaxListBuilder LexConflictMarkerDisabledText(ref SyntaxListBuilder triviaList)
{
// Consume everything from the start of the mid-conflict marker to the start of the next
// end-conflict marker.
this.Start();
var hitEndConflictMarker = false;
while (true)
{
var ch = this.TextWindow.PeekChar();
if (ch == SlidingTextWindow.InvalidCharacter)
{
break;
}
// If we hit the end-conflict marker, then lex it out at this point.
if (ch == '>' && IsConflictMarkerTrivia())
{
hitEndConflictMarker = true;
break;
}
this.TextWindow.AdvanceChar();
}
if (this.TextWindow.Width > 0)
{
this.AddTrivia(SyntaxFactory.DisabledText(TextWindow.GetText(false)), ref triviaList);
}
if (hitEndConflictMarker)
{
LexConflictMarkerTrivia(ref triviaList);
}
return triviaList;
}
private void LexConflictMarkerEndOfLine(ref SyntaxListBuilder triviaList)
{
this.Start();
while (SyntaxFacts.IsNewLine(this.TextWindow.PeekChar()))
{
this.TextWindow.AdvanceChar();
}
if (this.TextWindow.Width > 0)
{
this.AddTrivia(SyntaxFactory.EndOfLine(TextWindow.GetText(false)), ref triviaList);
}
}
private void LexConflictMarkerHeader(ref SyntaxListBuilder triviaList)
{
while (true)
{
var ch = this.TextWindow.PeekChar();
if (ch == SlidingTextWindow.InvalidCharacter || SyntaxFacts.IsNewLine(ch))
{
break;
}
this.TextWindow.AdvanceChar();
}
this.AddTrivia(SyntaxFactory.ConflictMarker(TextWindow.GetText(false)), ref triviaList);
}
private void AddTrivia(CSharpSyntaxNode trivia, ref SyntaxListBuilder list)
{
if (this.HasErrors)
......
......@@ -78,13 +78,7 @@ public void Dispose()
}
}
public SourceText Text
{
get
{
return _text;
}
}
public SourceText Text => _text;
/// <summary>
/// The current absolute position in the text file.
......
......@@ -158,6 +158,7 @@ Microsoft.CodeAnalysis.CSharp.Syntax.WhenClauseSyntax.WhenKeyword.get -> Microso
Microsoft.CodeAnalysis.CSharp.Syntax.WhenClauseSyntax.WithCondition(Microsoft.CodeAnalysis.CSharp.Syntax.ExpressionSyntax condition) -> Microsoft.CodeAnalysis.CSharp.Syntax.WhenClauseSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.WhenClauseSyntax.WithWhenKeyword(Microsoft.CodeAnalysis.SyntaxToken whenKeyword) -> Microsoft.CodeAnalysis.CSharp.Syntax.WhenClauseSyntax
Microsoft.CodeAnalysis.CSharp.SyntaxKind.CasePatternSwitchLabel = 9009 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind
Microsoft.CodeAnalysis.CSharp.SyntaxKind.ConflictMarkerTrivia = 8564 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind
Microsoft.CodeAnalysis.CSharp.SyntaxKind.ConstantPattern = 9002 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind
Microsoft.CodeAnalysis.CSharp.SyntaxKind.DeclarationExpression = 9040 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind
Microsoft.CodeAnalysis.CSharp.SyntaxKind.DeclarationPattern = 9000 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind
......
......@@ -97,6 +97,9 @@ internal static SyntaxTrivia Comment(string text)
}
}
internal static SyntaxTrivia ConflictMarker(string text)
=> SyntaxTrivia.Create(SyntaxKind.ConflictMarkerTrivia, text);
internal static SyntaxTrivia DisabledText(string text)
{
return SyntaxTrivia.Create(SyntaxKind.DisabledTextTrivia, text);
......
......@@ -263,6 +263,7 @@ public enum SyntaxKind : ushort
ReferenceDirectiveTrivia = 8561,
BadDirectiveTrivia = 8562,
SkippedTokensTrivia = 8563,
ConflictMarkerTrivia = 8564,
// xml nodes (for xml doc comment structure)
XmlElement = 8574,
......
......@@ -214,6 +214,7 @@ public static bool IsTrivia(SyntaxKind kind)
case SyntaxKind.MultiLineDocumentationCommentTrivia:
case SyntaxKind.DisabledTextTrivia:
case SyntaxKind.DocumentationCommentExteriorTrivia:
case SyntaxKind.ConflictMarkerTrivia:
return true;
default:
return IsPreprocessorDirective(kind);
......
......@@ -4,14 +4,11 @@
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Test.Utilities;
using Xunit;
using InternalSyntax = Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax;
using Microsoft.CodeAnalysis.CSharp.Syntax.InternalSyntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
namespace Microsoft.CodeAnalysis.CSharp.UnitTests
{
......@@ -3170,5 +3167,358 @@ private static string ToHexString(decimal d)
{
return string.Join("", decimal.GetBits(d).Select(word => string.Format("{0:x8}", word)));
}
[Fact]
public void TestGreaterThanConflictMarkerNotConsumedWhenLegalInGeneric()
{
var source = @"
class C
{
void M()
{
var v = new List<List<List<List<List<List<List<int
>>>>>>>
();
}
}";
var tree = CSharpSyntaxTree.ParseText(source);
var diagnostics = tree.GetDiagnostics();
Assert.Empty(diagnostics);
Assert.False(tree.GetRoot().DescendantTokens().Any(
t => t.LeadingTrivia.Any(SyntaxKind.ConflictMarkerTrivia) ||
t.TrailingTrivia.Any(SyntaxKind.ConflictMarkerTrivia)));
}
[Fact]
public void TestLessThanConflictMarker1()
{
// Needs to be followed by a space.
var token = Lex("<<<<<<<").First();
Assert.Equal(SyntaxKind.LessThanLessThanToken, token.Kind());
// Has to be the start of a line.
token = Lex(" <<<<<<<").First();
Assert.Equal(SyntaxKind.LessThanLessThanToken, token.Kind());
Assert.True(token.HasLeadingTrivia);
Assert.True(token.LeadingTrivia.Single().Kind() == SyntaxKind.WhitespaceTrivia);
// Has to have at least seven characters.
token = Lex("<<<<<< ").First();
Assert.Equal(SyntaxKind.LessThanLessThanToken, token.Kind());
// Start of line, seven characters, ends with space.
token = Lex("<<<<<<< ").First();
Assert.Equal(SyntaxKind.EndOfFileToken, token.Kind());
Assert.True(token.HasLeadingTrivia);
var trivia = token.LeadingTrivia.Single();
Assert.True(trivia.Kind() == SyntaxKind.ConflictMarkerTrivia);
Assert.True(trivia.ContainsDiagnostics);
var errors = trivia.Errors();
Assert.Equal(1, errors.Length);
Assert.Equal((int)ErrorCode.ERR_Merge_conflict_marker_encountered, errors[0].Code);
}
[Fact]
public void TestLessThanConflictMarker2()
{
var token = Lex("{\r\n<<<<<<<").Skip(1).First();
Assert.Equal(SyntaxKind.LessThanLessThanToken, token.Kind());
token = Lex("{\r\n <<<<<<<").Skip(1).First();
Assert.Equal(SyntaxKind.LessThanLessThanToken, token.Kind());
Assert.True(token.HasLeadingTrivia);
Assert.True(token.LeadingTrivia.Single().Kind() == SyntaxKind.WhitespaceTrivia);
token = Lex("{\r\n<<<<<< ").Skip(1).First();
Assert.Equal(SyntaxKind.LessThanLessThanToken, token.Kind());
token = Lex("{\r\n<<<<<<< ").Skip(1).First();
Assert.Equal(SyntaxKind.EndOfFileToken, token.Kind());
Assert.True(token.HasLeadingTrivia);
var trivia = token.LeadingTrivia.Single();
Assert.True(trivia.Kind() == SyntaxKind.ConflictMarkerTrivia);
Assert.True(trivia.SpanStart == 3);
Assert.True(trivia.Span.Length == 8);
Assert.True(trivia.ContainsDiagnostics);
var errors = trivia.Errors();
Assert.Equal(1, errors.Length);
Assert.Equal((int)ErrorCode.ERR_Merge_conflict_marker_encountered, errors[0].Code);
}
[Fact]
public void TestGreaterThanConflictMarker1()
{
// Less-than's should never be merged as conflict markers, except if they follow
// an ======= region
var token = Lex(">>>>>>>").First();
Assert.Equal(SyntaxKind.GreaterThanToken, token.Kind());
token = Lex(" >>>>>>>").First();
Assert.Equal(SyntaxKind.GreaterThanToken, token.Kind());
Assert.True(token.HasLeadingTrivia);
Assert.True(token.LeadingTrivia.Single().Kind() == SyntaxKind.WhitespaceTrivia);
token = Lex(">>>>>> ").First();
Assert.Equal(SyntaxKind.GreaterThanToken, token.Kind());
token = Lex(">>>>>>> ").First();
Assert.Equal(SyntaxKind.GreaterThanToken, token.Kind());
Assert.False(token.ContainsDiagnostics);
}
[Fact]
public void TestGreaterThanConflictMarker2()
{
// Less-than's should never be merged as conflict markers, except if they follow
// an ======= region
var token = Lex("{\r\n>>>>>>>").Skip(1).First();
Assert.Equal(SyntaxKind.GreaterThanToken, token.Kind());
token = Lex("{\r\n >>>>>>>").Skip(1).First();
Assert.Equal(SyntaxKind.GreaterThanToken, token.Kind());
Assert.True(token.HasLeadingTrivia);
Assert.True(token.LeadingTrivia.Single().Kind() == SyntaxKind.WhitespaceTrivia);
token = Lex("{\r\n>>>>>> ").Skip(1).First();
Assert.Equal(SyntaxKind.GreaterThanToken, token.Kind());
token = Lex("{\r\n>>>>>>> ").Skip(1).First();
Assert.Equal(SyntaxKind.GreaterThanToken, token.Kind());
Assert.False(token.ContainsDiagnostics);
}
[Fact]
public void TestEqualsConflictMarker1()
{
// Has to be the start of a line.
var token = Lex(" =======").First();
Assert.Equal(SyntaxKind.EqualsEqualsToken, token.Kind());
Assert.True(token.HasLeadingTrivia);
Assert.True(token.LeadingTrivia.Single().Kind() == SyntaxKind.WhitespaceTrivia);
// Has to have at least seven characters.
token = Lex("====== ").First();
Assert.Equal(SyntaxKind.EqualsEqualsToken, token.Kind());
// Start of line, seven characters
token = Lex("=======").First();
Assert.Equal(SyntaxKind.EndOfFileToken, token.Kind());
Assert.True(token.HasLeadingTrivia);
Assert.True(token.LeadingTrivia.Single().Kind() == SyntaxKind.ConflictMarkerTrivia);
// Start of line, seven characters
token = Lex("======= trailing chars").First();
Assert.Equal(SyntaxKind.EndOfFileToken, token.Kind());
Assert.True(token.HasLeadingTrivia);
var trivia = token.LeadingTrivia.Single();
Assert.True(trivia.Kind() == SyntaxKind.ConflictMarkerTrivia);
Assert.Equal(trivia.Span.Length, 22);
Assert.True(trivia.ContainsDiagnostics);
var errors = trivia.Errors();
Assert.Equal(1, errors.Length);
Assert.Equal((int)ErrorCode.ERR_Merge_conflict_marker_encountered, errors[0].Code);
token = Lex("======= Trailing\r\ndisabled text").First();
Assert.Equal(SyntaxKind.EndOfFileToken, token.Kind());
Assert.True(token.HasLeadingTrivia);
Assert.Equal(3, token.LeadingTrivia.Count);
trivia = token.LeadingTrivia[0];
Assert.True(trivia.Kind() == SyntaxKind.ConflictMarkerTrivia);
Assert.Equal(trivia.Span.Length, 16);
Assert.True(trivia.ContainsDiagnostics);
errors = trivia.Errors();
Assert.Equal(1, errors.Length);
Assert.Equal((int)ErrorCode.ERR_Merge_conflict_marker_encountered, errors[0].Code);
trivia = token.LeadingTrivia[1];
Assert.True(trivia.Kind() == SyntaxKind.EndOfLineTrivia);
Assert.Equal(trivia.Span.Start, 16);
Assert.Equal(trivia.Span.Length, 2);
trivia = token.LeadingTrivia[2];
Assert.True(trivia.Kind() == SyntaxKind.DisabledTextTrivia);
Assert.Equal(trivia.Span.Start, 18);
Assert.Equal(trivia.Span.Length, 13);
token = Lex("======= Trailing\r\ndisabled text\r\n>>>> still disabled").First();
Assert.Equal(SyntaxKind.EndOfFileToken, token.Kind());
Assert.True(token.HasLeadingTrivia);
Assert.Equal(3, token.LeadingTrivia.Count);
trivia = token.LeadingTrivia[0];
Assert.True(trivia.Kind() == SyntaxKind.ConflictMarkerTrivia);
Assert.Equal(trivia.Span.Length, 16);
Assert.True(trivia.ContainsDiagnostics);
errors = trivia.Errors();
Assert.Equal(1, errors.Length);
Assert.Equal((int)ErrorCode.ERR_Merge_conflict_marker_encountered, errors[0].Code);
trivia = token.LeadingTrivia[1];
Assert.True(trivia.Kind() == SyntaxKind.EndOfLineTrivia);
Assert.Equal(trivia.Span.Length, 2);
trivia = token.LeadingTrivia[2];
Assert.True(trivia.Kind() == SyntaxKind.DisabledTextTrivia);
Assert.Equal(trivia.Span.Length, 34);
token = Lex("======= Trailing\r\ndisabled text\r\n>>>>>>> Actually the end").First();
Assert.Equal(SyntaxKind.EndOfFileToken, token.Kind());
Assert.True(token.HasLeadingTrivia);
Assert.Equal(token.LeadingTrivia.Count, 4);
var trivia1 = token.LeadingTrivia[0];
Assert.True(trivia1.Kind() == SyntaxKind.ConflictMarkerTrivia);
Assert.Equal(trivia1.Span.Length, 16);
Assert.True(trivia1.ContainsDiagnostics);
errors = trivia1.Errors();
Assert.Equal(1, errors.Length);
Assert.Equal((int)ErrorCode.ERR_Merge_conflict_marker_encountered, errors[0].Code);
var trivia2 = token.LeadingTrivia[1];
Assert.True(trivia2.Kind() == SyntaxKind.EndOfLineTrivia);
Assert.Equal(trivia2.Span.Start, 16);
Assert.Equal(trivia2.Span.Length, 2);
var trivia3 = token.LeadingTrivia[2];
Assert.True(trivia3.Kind() == SyntaxKind.DisabledTextTrivia);
Assert.Equal(trivia3.Span.Start, 18);
Assert.Equal(trivia3.Span.Length, 15);
var trivia4 = token.LeadingTrivia[3];
Assert.True(trivia4.Kind() == SyntaxKind.ConflictMarkerTrivia);
Assert.Equal(trivia4.Span.Start, 33);
Assert.Equal(trivia4.Span.Length, 24);
Assert.True(trivia4.ContainsDiagnostics);
errors = trivia4.Errors();
Assert.Equal(1, errors.Length);
Assert.Equal((int)ErrorCode.ERR_Merge_conflict_marker_encountered, errors[0].Code);
token = Lex("======= Trailing\r\n>>>>>>> Actually the end").First();
Assert.Equal(SyntaxKind.EndOfFileToken, token.Kind());
Assert.True(token.HasLeadingTrivia);
Assert.Equal(token.LeadingTrivia.Count, 3);
trivia1 = token.LeadingTrivia[0];
Assert.True(trivia1.Kind() == SyntaxKind.ConflictMarkerTrivia);
Assert.Equal(trivia1.Span.Length, 16);
Assert.True(trivia1.ContainsDiagnostics);
errors = trivia1.Errors();
Assert.Equal(1, errors.Length);
Assert.Equal((int)ErrorCode.ERR_Merge_conflict_marker_encountered, errors[0].Code);
trivia2 = token.LeadingTrivia[1];
Assert.True(trivia2.Kind() == SyntaxKind.EndOfLineTrivia);
Assert.Equal(trivia2.Span.Start, 16);
Assert.Equal(trivia2.Span.Length, 2);
trivia3 = token.LeadingTrivia[2];
Assert.True(trivia3.Kind() == SyntaxKind.ConflictMarkerTrivia);
Assert.Equal(trivia3.Span.Start, 18);
Assert.Equal(trivia3.Span.Length, 24);
Assert.True(trivia3.ContainsDiagnostics);
errors = trivia3.Errors();
Assert.Equal(1, errors.Length);
Assert.Equal((int)ErrorCode.ERR_Merge_conflict_marker_encountered, errors[0].Code);
}
[Fact]
public void TestEqualsConflictMarker2()
{
// Has to be the start of a line.
var token = Lex("{\r\n =======").Skip(1).First();
Assert.Equal(SyntaxKind.EqualsEqualsToken, token.Kind());
Assert.True(token.HasLeadingTrivia);
Assert.True(token.LeadingTrivia.Single().Kind() == SyntaxKind.WhitespaceTrivia);
// Has to have at least seven characters.
token = Lex("{\r\n====== ").Skip(1).First();
Assert.Equal(SyntaxKind.EqualsEqualsToken, token.Kind());
// Start of line, seven characters
token = Lex("{\r\n=======").Skip(1).First();
Assert.Equal(SyntaxKind.EndOfFileToken, token.Kind());
Assert.True(token.HasLeadingTrivia);
Assert.True(token.LeadingTrivia.Single().Kind() == SyntaxKind.ConflictMarkerTrivia);
// Start of line, seven characters
token = Lex("{\r\n======= trailing chars").Skip(1).First();
Assert.Equal(SyntaxKind.EndOfFileToken, token.Kind());
Assert.True(token.HasLeadingTrivia);
var trivia = token.LeadingTrivia.Single();
Assert.True(trivia.Kind() == SyntaxKind.ConflictMarkerTrivia);
Assert.Equal(trivia.Span.Length, 22);
token = Lex("{\r\n======= Trailing\r\ndisabled text").Skip(1).First();
Assert.Equal(SyntaxKind.EndOfFileToken, token.Kind());
Assert.True(token.HasLeadingTrivia);
Assert.Equal(3, token.LeadingTrivia.Count);
trivia = token.LeadingTrivia[0];
Assert.True(trivia.Kind() == SyntaxKind.ConflictMarkerTrivia);
Assert.Equal(trivia.Span.Length, 16);
trivia = token.LeadingTrivia[1];
Assert.True(trivia.Kind() == SyntaxKind.EndOfLineTrivia);
Assert.Equal(trivia.Span.Start, 19);
Assert.Equal(trivia.Span.Length, 2);
trivia = token.LeadingTrivia[2];
Assert.True(trivia.Kind() == SyntaxKind.DisabledTextTrivia);
Assert.Equal(trivia.Span.Start, 21);
Assert.Equal(trivia.Span.Length, 13);
token = Lex("{\r\n======= Trailing\r\ndisabled text\r\n>>>> still disabled").Skip(1).First();
Assert.Equal(SyntaxKind.EndOfFileToken, token.Kind());
Assert.True(token.HasLeadingTrivia);
Assert.Equal(3, token.LeadingTrivia.Count);
trivia = token.LeadingTrivia[0];
Assert.True(trivia.Kind() == SyntaxKind.ConflictMarkerTrivia);
Assert.Equal(trivia.Span.Length, 16);
trivia = token.LeadingTrivia[1];
Assert.True(trivia.Kind() == SyntaxKind.EndOfLineTrivia);
Assert.Equal(trivia.Span.Length, 2);
trivia = token.LeadingTrivia[2];
Assert.True(trivia.Kind() == SyntaxKind.DisabledTextTrivia);
Assert.Equal(trivia.Span.Length, 34);
token = Lex("{\r\n======= Trailing\r\ndisabled text\r\n>>>>>>> Actually the end").Skip(1).First();
Assert.Equal(SyntaxKind.EndOfFileToken, token.Kind());
Assert.True(token.HasLeadingTrivia);
Assert.Equal(token.LeadingTrivia.Count, 4);
var trivia1 = token.LeadingTrivia[0];
Assert.True(trivia1.Kind() == SyntaxKind.ConflictMarkerTrivia);
Assert.Equal(trivia1.Span.Length, 16);
var trivia2 = token.LeadingTrivia[1];
Assert.True(trivia2.Kind() == SyntaxKind.EndOfLineTrivia);
Assert.Equal(trivia2.Span.Start, 19);
Assert.Equal(trivia2.Span.Length, 2);
var trivia3 = token.LeadingTrivia[2];
Assert.True(trivia3.Kind() == SyntaxKind.DisabledTextTrivia);
Assert.Equal(trivia3.Span.Start, 21);
Assert.Equal(trivia3.Span.Length, 15);
var trivia4 = token.LeadingTrivia[3];
Assert.True(trivia4.Kind() == SyntaxKind.ConflictMarkerTrivia);
Assert.Equal(trivia4.Span.Start, 36);
Assert.Equal(trivia4.Span.Length, 24);
}
}
}
......@@ -413,4 +413,4 @@ public bool IsEquivalentTo(SyntaxTrivia trivia)
(UnderlyingNode != null && trivia.UnderlyingNode != null && UnderlyingNode.IsEquivalentTo(trivia.UnderlyingNode));
}
}
}
}
\ No newline at end of file
......@@ -1724,6 +1724,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
ERR_PublicSignNetModule = 37282
ERR_BadAssemblyName = 37283
ERR_Merge_conflict_marker_encountered = 37284
'// WARNINGS BEGIN HERE
WRN_UseOfObsoleteSymbol2 = 40000
WRN_InvalidOverrideDueToTupleNames2 = 40001
......
......@@ -35,6 +35,7 @@ Microsoft.CodeAnalysis.VisualBasic.Syntax.TypedTupleElementSyntax
Microsoft.CodeAnalysis.VisualBasic.Syntax.TypedTupleElementSyntax.Type() -> Microsoft.CodeAnalysis.VisualBasic.Syntax.TypeSyntax
Microsoft.CodeAnalysis.VisualBasic.Syntax.TypedTupleElementSyntax.Update(type As Microsoft.CodeAnalysis.VisualBasic.Syntax.TypeSyntax) -> Microsoft.CodeAnalysis.VisualBasic.Syntax.TypedTupleElementSyntax
Microsoft.CodeAnalysis.VisualBasic.Syntax.TypedTupleElementSyntax.WithType(type As Microsoft.CodeAnalysis.VisualBasic.Syntax.TypeSyntax) -> Microsoft.CodeAnalysis.VisualBasic.Syntax.TypedTupleElementSyntax
Microsoft.CodeAnalysis.VisualBasic.SyntaxKind.ConflictMarkerTrivia = 792 -> Microsoft.CodeAnalysis.VisualBasic.SyntaxKind
Microsoft.CodeAnalysis.VisualBasic.SyntaxKind.NamedTupleElement = 791 -> Microsoft.CodeAnalysis.VisualBasic.SyntaxKind
Microsoft.CodeAnalysis.VisualBasic.SyntaxKind.TupleExpression = 788 -> Microsoft.CodeAnalysis.VisualBasic.SyntaxKind
Microsoft.CodeAnalysis.VisualBasic.SyntaxKind.TupleType = 789 -> Microsoft.CodeAnalysis.VisualBasic.SyntaxKind
......@@ -65,6 +66,7 @@ Overrides Microsoft.CodeAnalysis.VisualBasic.VisualBasicSyntaxRewriter.VisitName
Overrides Microsoft.CodeAnalysis.VisualBasic.VisualBasicSyntaxRewriter.VisitTupleExpression(node As Microsoft.CodeAnalysis.VisualBasic.Syntax.TupleExpressionSyntax) -> Microsoft.CodeAnalysis.SyntaxNode
Overrides Microsoft.CodeAnalysis.VisualBasic.VisualBasicSyntaxRewriter.VisitTupleType(node As Microsoft.CodeAnalysis.VisualBasic.Syntax.TupleTypeSyntax) -> Microsoft.CodeAnalysis.SyntaxNode
Overrides Microsoft.CodeAnalysis.VisualBasic.VisualBasicSyntaxRewriter.VisitTypedTupleElement(node As Microsoft.CodeAnalysis.VisualBasic.Syntax.TypedTupleElementSyntax) -> Microsoft.CodeAnalysis.SyntaxNode
Shared Microsoft.CodeAnalysis.VisualBasic.SyntaxFactory.ConflictMarkerTrivia(text As String) -> Microsoft.CodeAnalysis.SyntaxTrivia
Shared Microsoft.CodeAnalysis.VisualBasic.SyntaxFactory.NamedTupleElement(identifier As Microsoft.CodeAnalysis.SyntaxToken) -> Microsoft.CodeAnalysis.VisualBasic.Syntax.NamedTupleElementSyntax
Shared Microsoft.CodeAnalysis.VisualBasic.SyntaxFactory.NamedTupleElement(identifier As Microsoft.CodeAnalysis.SyntaxToken, asClause As Microsoft.CodeAnalysis.VisualBasic.Syntax.SimpleAsClauseSyntax) -> Microsoft.CodeAnalysis.VisualBasic.Syntax.NamedTupleElementSyntax
Shared Microsoft.CodeAnalysis.VisualBasic.SyntaxFactory.NamedTupleElement(identifier As String) -> Microsoft.CodeAnalysis.VisualBasic.Syntax.NamedTupleElementSyntax
......
......@@ -66,6 +66,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax
' The following table classifies the first &H180 Unicode characters.
' R and r are marked as COMPLEX so that quick-scanning doesn't stop after "REM".
' # is marked complex as it may start directives.
' < = > are complex because they might start a merge conflict marker.
' PERF: Use UShort instead of CharFlags so the compiler can use array literal initialization.
' The most natural type choice, Enum arrays, are not blittable due to a CLR limitation.
Private Shared ReadOnly s_charProperties As UShort() = {
......@@ -77,7 +78,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax
CharFlags.White, CharFlags.Complex, CharFlags.Complex, CharFlags.Complex, CharFlags.TypeChar, CharFlags.TypeChar, CharFlags.TypeChar, CharFlags.Complex,
CharFlags.Punct, CharFlags.Punct, CharFlags.CompoundPunctStart, CharFlags.CompoundPunctStart, CharFlags.Punct, CharFlags.CompoundPunctStart, CharFlags.Punct, CharFlags.CompoundPunctStart,
CharFlags.Digit, CharFlags.Digit, CharFlags.Digit, CharFlags.Digit, CharFlags.Digit, CharFlags.Digit, CharFlags.Digit, CharFlags.Digit,
CharFlags.Digit, CharFlags.Digit, CharFlags.Complex, CharFlags.Complex, CharFlags.CompoundPunctStart, CharFlags.Punct, CharFlags.CompoundPunctStart, CharFlags.Punct, _
CharFlags.Digit, CharFlags.Digit, CharFlags.Complex, CharFlags.Complex, CharFlags.Complex, CharFlags.Complex, CharFlags.Complex, CharFlags.Punct, _
_
CharFlags.TypeChar, CharFlags.Letter, CharFlags.Letter, CharFlags.Letter, CharFlags.Letter, CharFlags.Letter, CharFlags.Letter, CharFlags.Letter,
CharFlags.Letter, CharFlags.Letter, CharFlags.Letter, CharFlags.Letter, CharFlags.Letter, CharFlags.Letter, CharFlags.Letter, CharFlags.Letter,
......
......@@ -611,7 +611,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax
' optimization for a common case
' the ASCII range between ': and ~ , with exception of except "'", "_" and R cannot start trivia
If ch > ":"c AndAlso ch <= "~"c AndAlso ch <> "'"c AndAlso ch <> "_"c AndAlso ch <> "R"c AndAlso ch <> "r"c Then
If ch > ":"c AndAlso
ch <= "~"c AndAlso
ch <> "'"c AndAlso
ch <> "_"c AndAlso
ch <> "R"c AndAlso
ch <> "r"c AndAlso
ch <> "<"c AndAlso
ch <> "="c AndAlso
ch <> ">"c Then
Return Nothing
End If
......@@ -641,6 +649,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax
If StartsDirective(0) Then
Return TryScanDirective(tList)
End If
If IsConflictMarkerTrivia() Then
ScanConflictMarker(tList)
Return True
End If
End If
Dim ch = Peek()
......@@ -676,6 +689,107 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Syntax.InternalSyntax
Return False
End Function
' All conflict markers consist of the same character repeated seven times. If it Is
' a <<<<<<< Or >>>>>>> marker then it Is also followed by a space.
Private Shared ReadOnly s_conflictMarkerLength As Integer = "<<<<<<<".Length
Private Function IsConflictMarkerTrivia() As Boolean
If CanGet() Then
Dim ch = Peek()
If ch = "<"c OrElse ch = ">"c OrElse ch = "="c Then
Dim position = _lineBufferOffset
Dim text = _buffer
If position = 0 OrElse SyntaxFacts.IsNewLine(text(position - 1)) Then
Dim firstCh = _buffer(position)
If (position + s_conflictMarkerLength) <= text.Length Then
For i = 0 To s_conflictMarkerLength - 1
If text(position + i) <> firstCh Then
Return False
End If
Next
If firstCh = "="c Then
Return True
End If
Return (position + s_conflictMarkerLength) < text.Length AndAlso
text(position + s_conflictMarkerLength) = " "c
End If
End If
End If
End If
Return False
End Function
Private Sub ScanConflictMarker(tList As SyntaxListBuilder)
Dim startCh = Peek()
' First create a trivia from the start of this merge conflict marker to the
' end of line/file (whicever comes first).
ScanConflictMarkerHeader(tList)
' Now add the newlines as the next trivia.
ScanConflictMarkerEndOfLine(tList)
If startCh = "="c Then
' Consume everything from the start of the mid-conflict marker to the start of the next
' end-conflict marker.
ScanConflictMarkerDisabledText(tList)
End If
End Sub
Private Sub ScanConflictMarkerDisabledText(tList As SyntaxListBuilder)
Dim start = _lineBufferOffset
While CanGet()
Dim ch = Peek()
If ch = ">"c AndAlso IsConflictMarkerTrivia() Then
Exit While
End If
AdvanceChar()
End While
Dim width = _lineBufferOffset - start
If width > 0 Then
tList.Add(SyntaxFactory.DisabledTextTrivia(GetText(start, width)))
End If
End Sub
Private Sub ScanConflictMarkerEndOfLine(tList As SyntaxListBuilder)
Dim start = _lineBufferOffset
While CanGet() AndAlso SyntaxFacts.IsNewLine(Peek())
AdvanceChar()
End While
Dim width = _lineBufferOffset - start
If width > 0 Then
tList.Add(SyntaxFactory.EndOfLineTrivia(GetText(start, width)))
End If
End Sub
Private Sub ScanConflictMarkerHeader(tList As SyntaxListBuilder)
Dim start = _lineBufferOffset
While CanGet()
Dim ch = Peek()
If SyntaxFacts.IsNewLine(ch) Then
Exit While
End If
AdvanceChar()
End While
Dim trivia = SyntaxFactory.ConflictMarkerTrivia(GetText(start, _lineBufferOffset - start))
trivia = DirectCast(trivia.SetDiagnostics({ErrorFactory.ErrorInfo(ERRID.ERR_Merge_conflict_marker_encountered)}), SyntaxTrivia)
tList.Add(trivia)
End Sub
' check for '''(~')
Private Function StartsXmlDoc(Here As Integer) As Boolean
Return _options.DocumentationMode >= DocumentationMode.Parse AndAlso
......
......@@ -9394,6 +9394,9 @@ Unify Enum declarations with other block type declarations.
<node-kind name="CommentTrivia">
<description>Represents a comment.</description>
</node-kind>
<node-kind name="ConflictMarkerTrivia">
<description>Trivia created when merge conflict markers (like "&lt;&lt;&lt;&lt;&lt;&lt;&lt;") are detected in source code.</description>
</node-kind>
<node-kind name="LineContinuationTrivia" token-text="_&#13;&#10;">
<description>Represents an explicit line continuation character at the end of a line, i.e., _</description>
</node-kind>
......
......@@ -3193,6 +3193,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
''' Represents an element of a tuple type supplying element name and optionally a type.
''' </summary>
NamedTupleElement = 791
End Enum
''' <summary>
''' Trivia created when merge conflict markers (like "&lt;&lt;&lt;&lt;&lt;&lt;&lt;") are detected in source code
''' </summary>
ConflictMarkerTrivia = 792
End Enum
End Namespace
......@@ -6900,6 +6900,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
End Get
End Property
'''<summary>
''' Looks up a localized string similar to Merge conflict marker encountered.
'''</summary>
Friend ReadOnly Property ERR_Merge_conflict_marker_encountered() As String
Get
Return ResourceManager.GetString("ERR_Merge_conflict_marker_encountered", resourceCulture)
End Get
End Property
'''<summary>
''' Looks up a localized string similar to &apos;{0}&apos; is a module and cannot be referenced as an assembly..
'''</summary>
......
......@@ -5464,4 +5464,7 @@
<data name="ERR_TypeForwardedToMultipleAssemblies" xml:space="preserve">
<value>Module '{0}' in assembly '{1}' is forwarding the type '{2}' to multiple assemblies: '{3}' and '{4}'.</value>
</data>
<data name="ERR_Merge_conflict_marker_encountered" xml:space="preserve">
<value>Merge conflict marker encountered</value>
</data>
</root>
\ No newline at end of file
......@@ -42,13 +42,13 @@ Public Class QuickTokenTableTests
ShouldBeGoodQuickToken(" ( 8")
ShouldBeGoodQuickToken(" , a")
ShouldBeGoodQuickToken(" ( 8")
ShouldBeGoodQuickToken("a=")
ShouldBeGoodQuickToken("ab$=")
ShouldBeGoodQuickToken(" a =")
ShouldBeGoodQuickToken(" =a")
ShouldBeGoodQuickToken("a,")
ShouldBeGoodQuickToken("ab$,")
ShouldBeGoodQuickToken(" a ,")
ShouldBeGoodQuickToken(" ,a")
ShouldBeGoodQuickToken("> x")
ShouldBeGoodQuickToken("< A")
ShouldBeGoodQuickToken("} x")
ShouldBeGoodQuickToken("{ A")
ShouldBeGoodQuickToken(". F")
ShouldBeGoodQuickToken("+ a")
End Sub
......
......@@ -56,6 +56,332 @@ Public Class ScannerTests
Return tokens
End Function
<Fact>
Public Sub TestLessThanConflictMarker1()
' Needs to be followed by a space.
Dim token = SyntaxFactory.ParseTokens("<<<<<<<").First()
Assert.Equal(SyntaxKind.LessThanLessThanToken, token.Kind())
' Has to be the start of a line.
token = SyntaxFactory.ParseTokens(" <<<<<<<").First()
Assert.Equal(SyntaxKind.LessThanLessThanToken, token.Kind())
Assert.True(token.HasLeadingTrivia)
Assert.True(token.LeadingTrivia.Single().Kind() = SyntaxKind.WhitespaceTrivia)
' Has to have at least seven characters.
token = SyntaxFactory.ParseTokens("<<<<<< ").First()
Assert.Equal(SyntaxKind.LessThanLessThanToken, token.Kind())
' Start of line, seven characters, ends with space.
token = SyntaxFactory.ParseTokens("<<<<<<< ").First()
Assert.Equal(SyntaxKind.EndOfFileToken, token.Kind())
Assert.True(token.HasLeadingTrivia)
Dim trivia = token.LeadingTrivia.Single()
Assert.True(trivia.Kind() = SyntaxKind.ConflictMarkerTrivia)
Assert.True(trivia.ContainsDiagnostics)
Dim err = trivia.Errors().First
Assert.Equal(ERRID.ERR_Merge_conflict_marker_encountered, err.Code)
End Sub
<Fact>
Public Sub TestLessThanConflictMarker2()
Dim token = SyntaxFactory.ParseTokens("{" & vbCrLf & "<<<<<<<").Skip(2).First()
Assert.Equal(SyntaxKind.LessThanLessThanToken, token.Kind())
token = SyntaxFactory.ParseTokens("{" & vbCrLf & " <<<<<<<").Skip(2).First()
Assert.Equal(SyntaxKind.LessThanLessThanToken, token.Kind())
Assert.True(token.HasLeadingTrivia)
Assert.True(token.LeadingTrivia.Single().Kind() = SyntaxKind.WhitespaceTrivia)
token = SyntaxFactory.ParseTokens("{" & vbCrLf & "<<<<<< ").Skip(2).First()
Assert.Equal(SyntaxKind.LessThanLessThanToken, token.Kind())
token = SyntaxFactory.ParseTokens("{" & vbCrLf & "<<<<<<< ").Skip(2).First()
Assert.Equal(SyntaxKind.EndOfFileToken, token.Kind())
Assert.True(token.HasLeadingTrivia)
Dim trivia = token.LeadingTrivia.Single()
Assert.True(trivia.Kind() = SyntaxKind.ConflictMarkerTrivia)
Assert.True(trivia.SpanStart = 3)
Assert.True(trivia.Span.Length = 8)
Assert.True(trivia.ContainsDiagnostics)
Dim err = trivia.Errors().First
Assert.Equal(ERRID.ERR_Merge_conflict_marker_encountered, err.Code)
End Sub
<Fact>
Public Sub TestGreaterThanConflictMarker1()
' Needs to be followed by a space.
Dim token = SyntaxFactory.ParseTokens(">>>>>>>").First()
Assert.Equal(SyntaxKind.GreaterThanGreaterThanToken, token.Kind())
' Has to be the start of a line.
token = SyntaxFactory.ParseTokens(" >>>>>>>").First()
Assert.Equal(SyntaxKind.GreaterThanGreaterThanToken, token.Kind())
Assert.True(token.HasLeadingTrivia)
Assert.True(token.LeadingTrivia.Single().Kind() = SyntaxKind.WhitespaceTrivia)
' Has to have at least seven characters.
token = SyntaxFactory.ParseTokens(">>>>>> ").First()
Assert.Equal(SyntaxKind.GreaterThanGreaterThanToken, token.Kind())
' Start of line, seven characters, ends with space.
token = SyntaxFactory.ParseTokens(">>>>>>> ").First()
Assert.Equal(SyntaxKind.EndOfFileToken, token.Kind())
Assert.True(token.HasLeadingTrivia)
Dim trivia = token.LeadingTrivia.Single()
Assert.True(trivia.Kind() = SyntaxKind.ConflictMarkerTrivia)
Assert.True(trivia.ContainsDiagnostics)
Dim err = trivia.Errors().First
Assert.Equal(ERRID.ERR_Merge_conflict_marker_encountered, err.Code)
End Sub
<Fact>
Public Sub TestGreaterThanConflictMarker2()
Dim token = SyntaxFactory.ParseTokens("{" & vbCrLf & ">>>>>>>").Skip(2).First()
Assert.Equal(SyntaxKind.GreaterThanGreaterThanToken, token.Kind())
token = SyntaxFactory.ParseTokens("{" & vbCrLf & " >>>>>>>").Skip(2).First()
Assert.Equal(SyntaxKind.GreaterThanGreaterThanToken, token.Kind())
Assert.True(token.HasLeadingTrivia)
Assert.True(token.LeadingTrivia.Single().Kind() = SyntaxKind.WhitespaceTrivia)
token = SyntaxFactory.ParseTokens("{" & vbCrLf & ">>>>>> ").Skip(2).First()
Assert.Equal(SyntaxKind.GreaterThanGreaterThanToken, token.Kind())
token = SyntaxFactory.ParseTokens("{" & vbCrLf & ">>>>>>> ").Skip(2).First()
Assert.Equal(SyntaxKind.EndOfFileToken, token.Kind())
Assert.True(token.HasLeadingTrivia)
Dim trivia = token.LeadingTrivia.Single()
Assert.True(trivia.Kind() = SyntaxKind.ConflictMarkerTrivia)
Assert.True(trivia.SpanStart = 3)
Assert.True(trivia.Span.Length = 8)
Assert.True(trivia.ContainsDiagnostics)
Dim err = trivia.Errors().First
Assert.Equal(ERRID.ERR_Merge_conflict_marker_encountered, err.Code)
End Sub
<Fact>
Public Sub TestEqualsConflictMarker1()
' Has to be the start of a line.
Dim token = SyntaxFactory.ParseTokens(" =======").First()
Assert.Equal(SyntaxKind.EqualsToken, token.Kind())
Assert.True(token.HasLeadingTrivia)
Assert.True(token.LeadingTrivia.Single().Kind() = SyntaxKind.WhitespaceTrivia)
' Has to have at least seven characters.
token = SyntaxFactory.ParseTokens("====== ").First()
Assert.Equal(SyntaxKind.EqualsToken, token.Kind())
' Start of line, seven characters
token = SyntaxFactory.ParseTokens("=======").First()
Assert.Equal(SyntaxKind.EndOfFileToken, token.Kind())
Assert.True(token.HasLeadingTrivia)
Assert.True(token.LeadingTrivia.Single().Kind() = SyntaxKind.ConflictMarkerTrivia)
' Start of line, seven characters
token = SyntaxFactory.ParseTokens("======= trailing chars").First()
Assert.Equal(SyntaxKind.EndOfFileToken, token.Kind())
Assert.True(token.HasLeadingTrivia)
Dim trivia = token.LeadingTrivia.Single()
Assert.True(trivia.Kind() = SyntaxKind.ConflictMarkerTrivia)
Assert.Equal(trivia.Span.Length, 22)
Assert.True(trivia.ContainsDiagnostics)
Dim err = trivia.Errors().First
Assert.Equal(ERRID.ERR_Merge_conflict_marker_encountered, err.Code)
token = SyntaxFactory.ParseTokens("======= Trailing" & vbCrLf & "disabled text").First()
Assert.Equal(SyntaxKind.EndOfFileToken, token.Kind())
Assert.True(token.HasLeadingTrivia)
Assert.Equal(3, token.LeadingTrivia.Count)
trivia = token.LeadingTrivia(0)
Assert.True(trivia.Kind() = SyntaxKind.ConflictMarkerTrivia)
Assert.Equal(trivia.Span.Length, 16)
Assert.True(trivia.ContainsDiagnostics)
err = trivia.Errors().First
Assert.Equal(ERRID.ERR_Merge_conflict_marker_encountered, err.Code)
trivia = token.LeadingTrivia(1)
Assert.True(trivia.Kind() = SyntaxKind.EndOfLineTrivia)
Assert.Equal(trivia.Span.Start, 16)
Assert.Equal(trivia.Span.Length, 2)
trivia = token.LeadingTrivia(2)
Assert.True(trivia.Kind() = SyntaxKind.DisabledTextTrivia)
Assert.Equal(trivia.Span.Start, 18)
Assert.Equal(trivia.Span.Length, 13)
token = SyntaxFactory.ParseTokens("======= Trailing" & vbCrLf & "disabled text" & vbCrLf & ">>>> still disabled").First()
Assert.Equal(SyntaxKind.EndOfFileToken, token.Kind())
Assert.True(token.HasLeadingTrivia)
Assert.Equal(3, token.LeadingTrivia.Count)
trivia = token.LeadingTrivia(0)
Assert.True(trivia.Kind() = SyntaxKind.ConflictMarkerTrivia)
Assert.Equal(trivia.Span.Length, 16)
Assert.True(trivia.ContainsDiagnostics)
err = trivia.Errors().First
Assert.Equal(ERRID.ERR_Merge_conflict_marker_encountered, err.Code)
trivia = token.LeadingTrivia(1)
Assert.True(trivia.Kind() = SyntaxKind.EndOfLineTrivia)
Assert.Equal(trivia.Span.Length, 2)
trivia = token.LeadingTrivia(2)
Assert.True(trivia.Kind() = SyntaxKind.DisabledTextTrivia)
Assert.Equal(trivia.Span.Length, 34)
token = SyntaxFactory.ParseTokens("======= Trailing" & vbCrLf & "disabled text" & vbCrLf & ">>>>>>> Actually the end").First()
Assert.Equal(SyntaxKind.EndOfFileToken, token.Kind())
Assert.True(token.HasLeadingTrivia)
Assert.Equal(token.LeadingTrivia.Count, 4)
Dim trivia1 = token.LeadingTrivia(0)
Assert.True(trivia1.Kind() = SyntaxKind.ConflictMarkerTrivia)
Assert.Equal(trivia1.Span.Length, 16)
Assert.True(trivia1.ContainsDiagnostics)
err = trivia1.Errors().First
Assert.Equal(ERRID.ERR_Merge_conflict_marker_encountered, err.Code)
Dim trivia2 = token.LeadingTrivia(1)
Assert.True(trivia2.Kind() = SyntaxKind.EndOfLineTrivia)
Assert.Equal(trivia2.Span.Start, 16)
Assert.Equal(trivia2.Span.Length, 2)
Dim trivia3 = token.LeadingTrivia(2)
Assert.True(trivia3.Kind() = SyntaxKind.DisabledTextTrivia)
Assert.Equal(trivia3.Span.Start, 18)
Assert.Equal(trivia3.Span.Length, 15)
Dim trivia4 = token.LeadingTrivia(3)
Assert.True(trivia4.Kind() = SyntaxKind.ConflictMarkerTrivia)
Assert.Equal(trivia4.Span.Start, 33)
Assert.Equal(trivia4.Span.Length, 24)
Assert.True(trivia4.ContainsDiagnostics)
err = trivia4.Errors().First
Assert.Equal(ERRID.ERR_Merge_conflict_marker_encountered, err.Code)
End Sub
<Fact>
Public Sub TestEqualsConflictMarker2()
' Has to be the start of a line.
Dim token = SyntaxFactory.ParseTokens("{" & vbCrLf & " =======").Skip(2).First()
Assert.Equal(SyntaxKind.EqualsToken, token.Kind())
Assert.True(token.HasLeadingTrivia)
Assert.True(token.LeadingTrivia.Single().Kind() = SyntaxKind.WhitespaceTrivia)
' Has to have at least seven characters.
token = SyntaxFactory.ParseTokens("{" & vbCrLf & "====== ").Skip(2).First()
Assert.Equal(SyntaxKind.EqualsToken, token.Kind())
' Start of line, seven characters
token = SyntaxFactory.ParseTokens("{" & vbCrLf & "=======").Skip(2).First()
Assert.Equal(SyntaxKind.EndOfFileToken, token.Kind())
Assert.True(token.HasLeadingTrivia)
Dim trivia = token.LeadingTrivia.Single()
Assert.True(trivia.Kind() = SyntaxKind.ConflictMarkerTrivia)
Assert.True(trivia.ContainsDiagnostics)
Dim err = trivia.Errors().First
Assert.Equal(ERRID.ERR_Merge_conflict_marker_encountered, err.Code)
' Start of line, seven characters
token = SyntaxFactory.ParseTokens("{" & vbCrLf & "======= trailing chars").Skip(2).First()
Assert.Equal(SyntaxKind.EndOfFileToken, token.Kind())
Assert.True(token.HasLeadingTrivia)
trivia = token.LeadingTrivia.Single()
Assert.True(trivia.Kind() = SyntaxKind.ConflictMarkerTrivia)
Assert.Equal(trivia.Span.Length, 22)
Assert.True(trivia.ContainsDiagnostics)
err = trivia.Errors().First
Assert.Equal(ERRID.ERR_Merge_conflict_marker_encountered, err.Code)
token = SyntaxFactory.ParseTokens("{" & vbCrLf & "======= Trailing" & vbCrLf & "disabled text").Skip(2).First()
Assert.Equal(SyntaxKind.EndOfFileToken, token.Kind())
Assert.True(token.HasLeadingTrivia)
Assert.Equal(3, token.LeadingTrivia.Count)
trivia = token.LeadingTrivia(0)
Assert.True(trivia.Kind() = SyntaxKind.ConflictMarkerTrivia)
Assert.Equal(trivia.Span.Length, 16)
Assert.True(trivia.ContainsDiagnostics)
err = trivia.Errors().First
Assert.Equal(ERRID.ERR_Merge_conflict_marker_encountered, err.Code)
trivia = token.LeadingTrivia(1)
Assert.True(trivia.Kind() = SyntaxKind.EndOfLineTrivia)
Assert.Equal(trivia.Span.Start, 19)
Assert.Equal(trivia.Span.Length, 2)
trivia = token.LeadingTrivia(2)
Assert.True(trivia.Kind() = SyntaxKind.DisabledTextTrivia)
Assert.Equal(trivia.Span.Start, 21)
Assert.Equal(trivia.Span.Length, 13)
token = SyntaxFactory.ParseTokens("{" & vbCrLf & "======= Trailing" & vbCrLf & "disabled text" & vbCrLf & ">>>> still disabled").Skip(2).First()
Assert.Equal(SyntaxKind.EndOfFileToken, token.Kind())
Assert.True(token.HasLeadingTrivia)
Assert.Equal(3, token.LeadingTrivia.Count)
trivia = token.LeadingTrivia(0)
Assert.True(trivia.Kind() = SyntaxKind.ConflictMarkerTrivia)
Assert.Equal(trivia.Span.Length, 16)
Assert.True(trivia.ContainsDiagnostics)
err = trivia.Errors().First
Assert.Equal(ERRID.ERR_Merge_conflict_marker_encountered, err.Code)
trivia = token.LeadingTrivia(1)
Assert.True(trivia.Kind() = SyntaxKind.EndOfLineTrivia)
Assert.Equal(trivia.Span.Length, 2)
trivia = token.LeadingTrivia(2)
Assert.True(trivia.Kind() = SyntaxKind.DisabledTextTrivia)
Assert.Equal(trivia.Span.Length, 34)
token = SyntaxFactory.ParseTokens("{" & vbCrLf & "======= Trailing" & vbCrLf & "disabled text" & vbCrLf & ">>>>>>> Actually the end").Skip(2).First()
Assert.Equal(SyntaxKind.EndOfFileToken, token.Kind())
Assert.True(token.HasLeadingTrivia)
Assert.Equal(token.LeadingTrivia.Count, 4)
Dim trivia1 = token.LeadingTrivia(0)
Assert.True(trivia1.Kind() = SyntaxKind.ConflictMarkerTrivia)
Assert.Equal(trivia1.Span.Length, 16)
Assert.True(trivia1.ContainsDiagnostics)
err = trivia1.Errors().First
Assert.Equal(ERRID.ERR_Merge_conflict_marker_encountered, err.Code)
Dim trivia2 = token.LeadingTrivia(1)
Assert.True(trivia2.Kind() = SyntaxKind.EndOfLineTrivia)
Assert.Equal(trivia2.Span.Start, 19)
Assert.Equal(trivia2.Span.Length, 2)
Dim trivia3 = token.LeadingTrivia(2)
Assert.True(trivia3.Kind() = SyntaxKind.DisabledTextTrivia)
Assert.Equal(trivia3.Span.Start, 21)
Assert.Equal(trivia3.Span.Length, 15)
Dim trivia4 = token.LeadingTrivia(3)
Assert.True(trivia4.Kind() = SyntaxKind.ConflictMarkerTrivia)
Assert.Equal(trivia4.Span.Start, 36)
Assert.Equal(trivia4.Span.Length, 24)
Assert.True(trivia4.ContainsDiagnostics)
err = trivia4.Errors().First
Assert.Equal(ERRID.ERR_Merge_conflict_marker_encountered, err.Code)
End Sub
<Fact>
Public Sub Scanner_EndOfText()
Dim tk = ScanOnce("")
......
......@@ -3977,5 +3977,38 @@ public async Task TupleLiteralWithNames()
Number("2"),
Punctuation.CloseParen);
}
[Fact, Trait(Traits.Feature, Traits.Features.Classification)]
public async Task TestConflictMarkers1()
{
await TestAsync(
@"class C
{
<<<<<<< Start
public void Foo();
=======
public void Bar();
>>>>>>> End
}",
Keyword("class"),
Class("C"),
Punctuation.OpenCurly,
Comment("<<<<<<< Start"),
Keyword("public"),
Keyword("void"),
Identifier("Foo"),
Punctuation.OpenParen,
Punctuation.CloseParen,
Punctuation.Semicolon,
Comment("======="),
Keyword("public"),
Keyword("void"),
Identifier("Bar"),
Punctuation.OpenParen,
Punctuation.CloseParen,
Punctuation.Semicolon,
Comment(">>>>>>> End"),
Punctuation.CloseCurly);
}
}
}
......@@ -3857,5 +3857,32 @@ End Region ' Stuff",
TextSpan.FromBounds(28, 36),
Comment("' Stuff"))
End Function
<Fact, Trait(Traits.Feature, Traits.Features.Classification)>
Public Async Function TestConflictMarkers1() As Task
Await TestAsync(
"interface I
<<<<<<< Start
sub Foo()
=======
sub Bar()
>>>>>>> End
end interface",
Keyword("interface"),
[Interface]("I"),
Comment("<<<<<<< Start"),
Keyword("sub"),
Identifier("Foo"),
Punctuation.OpenParen,
Punctuation.CloseParen,
Comment("======="),
Keyword("sub"),
Identifier("Bar"),
Punctuation.OpenParen,
Punctuation.CloseParen,
Comment(">>>>>>> End"),
Keyword("end"),
Keyword("interface"))
End Function
End Class
End Namespace
End Namespace
\ No newline at end of file
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Classification;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Shared.Collections;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp.Classification
{
......@@ -157,12 +149,12 @@ private void ClassifyTriviaList(SyntaxTriviaList list)
return;
}
ClassifyTrivia(trivia);
ClassifyTrivia(trivia, list);
}
while (enumerator.MoveNext());
}
private void ClassifyTrivia(SyntaxTrivia trivia)
private void ClassifyTrivia(SyntaxTrivia trivia, SyntaxTriviaList triviaList)
{
switch (trivia.Kind())
{
......@@ -173,7 +165,7 @@ private void ClassifyTrivia(SyntaxTrivia trivia)
return;
case SyntaxKind.DisabledTextTrivia:
AddClassification(trivia, ClassificationTypeNames.ExcludedCode);
ClassifyDisabledText(trivia, triviaList);
return;
case SyntaxKind.SkippedTokensTrivia:
......@@ -189,6 +181,10 @@ private void ClassifyTrivia(SyntaxTrivia trivia)
AddClassification(trivia, ClassificationTypeNames.XmlDocCommentDelimiter);
return;
case SyntaxKind.ConflictMarkerTrivia:
ClassifyConflictMarker(trivia);
return;
case SyntaxKind.IfDirectiveTrivia:
case SyntaxKind.ElifDirectiveTrivia:
case SyntaxKind.ElseDirectiveTrivia:
......@@ -223,5 +219,30 @@ private void ClassifySkippedTokens(SkippedTokensTriviaSyntax skippedTokens)
ClassifyToken(tk);
}
}
private void ClassifyConflictMarker(SyntaxTrivia trivia)
{
AddClassification(trivia, ClassificationTypeNames.Comment);
}
private void ClassifyDisabledText(SyntaxTrivia trivia, SyntaxTriviaList triviaList)
{
var index = triviaList.IndexOf(trivia);
if (index >= 2 &&
triviaList[index - 1].Kind() == SyntaxKind.EndOfLineTrivia &&
triviaList[index - 2].Kind() == SyntaxKind.ConflictMarkerTrivia)
{
// for the ======== add a comment for the first line, and then lex all
// subsequent lines up until the end of the conflict marker.
foreach (var token in SyntaxFactory.ParseTokens(text: trivia.ToFullString(), initialTokenPosition: trivia.SpanStart))
{
ClassifyToken(token);
}
}
else
{
AddClassification(trivia, ClassificationTypeNames.ExcludedCode);
}
}
}
}
}
\ No newline at end of file
......@@ -173,6 +173,7 @@ public static bool IsInNonUserCode(this SyntaxTree syntaxTree, int position, Can
{
return
syntaxTree.IsEntirelyWithinNonUserCodeComment(position, cancellationToken) ||
syntaxTree.IsEntirelyWithinConflictMarker(position, cancellationToken) ||
syntaxTree.IsEntirelyWithinStringOrCharLiteral(position, cancellationToken) ||
syntaxTree.IsInInactiveRegion(position, cancellationToken);
}
......@@ -302,6 +303,20 @@ public static bool IsEntirelyWithinCrefSyntax(this SyntaxTree syntaxTree, int po
return false;
}
public static bool IsEntirelyWithinConflictMarker(
this SyntaxTree syntaxTree, int position, CancellationToken cancellationToken)
{
var trivia = syntaxTree.FindTriviaAndAdjustForEndOfFile(position, cancellationToken);
if (trivia.Kind() == SyntaxKind.EndOfLineTrivia)
{
// Check if we're on the newline right at the end of a comment
trivia = trivia.GetPreviousTrivia(syntaxTree, cancellationToken);
}
return trivia.Kind() == SyntaxKind.ConflictMarkerTrivia;
}
public static bool IsEntirelyWithinTopLevelSingleLineComment(
this SyntaxTree syntaxTree, int position, CancellationToken cancellationToken)
{
......
......@@ -124,6 +124,10 @@ private static void Analyze(SyntaxTriviaList list, ref AnalysisResult result)
{
result.HasSkippedOrDisabledText = true;
}
else if (trivia.Kind() == SyntaxKind.ConflictMarkerTrivia)
{
result.HasConflictMarker = true;
}
else
{
Contract.ThrowIfFalse(SyntaxFacts.IsPreprocessorDirective(trivia.Kind()));
......@@ -201,6 +205,7 @@ internal struct AnalysisResult
internal bool HasSkippedTokens { get; set; }
internal bool HasSkippedOrDisabledText { get; set; }
internal bool HasConflictMarker { get; set; }
internal bool HasComments { get; set; }
internal bool HasPreprocessor { get; set; }
......
......@@ -94,12 +94,12 @@ public override TriviaData Create(SyntaxToken token1, SyntaxToken token2)
private bool ContainsOnlyWhitespace(Analyzer.AnalysisResult result)
{
if (result.HasComments || result.HasPreprocessor || result.HasSkippedTokens || result.HasSkippedOrDisabledText)
{
return false;
}
return true;
return
!result.HasComments &&
!result.HasPreprocessor &&
!result.HasSkippedTokens &&
!result.HasSkippedOrDisabledText &&
!result.HasConflictMarker;
}
private TriviaData GetWhitespaceOnlyTriviaInfo(SyntaxToken token1, SyntaxToken token2, Analyzer.AnalysisResult result)
......
......@@ -91,18 +91,18 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Classification
End Sub
Private Sub ClassifyTrivia(token As SyntaxToken)
For Each trivia In token.LeadingTrivia
_cancellationToken.ThrowIfCancellationRequested()
ClassifyTrivia(trivia)
Next
ClassifyTrivia(token.LeadingTrivia)
ClassifyTrivia(token.TrailingTrivia)
End Sub
For Each trivia In token.TrailingTrivia
Public Sub ClassifyTrivia(triviaList As SyntaxTriviaList)
For Each trivia In triviaList
_cancellationToken.ThrowIfCancellationRequested()
ClassifyTrivia(trivia)
ClassifyTrivia(trivia, triviaList)
Next
End Sub
Private Sub ClassifyTrivia(trivia As SyntaxTrivia)
Private Sub ClassifyTrivia(trivia As SyntaxTrivia, triviaList As SyntaxTriviaList)
If trivia.HasStructure Then
Select Case trivia.GetStructure().Kind
Case SyntaxKind.DocumentationCommentTrivia
......@@ -129,14 +129,60 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Classification
ElseIf trivia.Kind = SyntaxKind.CommentTrivia Then
AddClassification(trivia, ClassificationTypeNames.Comment)
ElseIf trivia.Kind = SyntaxKind.DisabledTextTrivia Then
AddClassification(trivia, ClassificationTypeNames.ExcludedCode)
ClassifyDisabledText(trivia, triviaList)
ElseIf trivia.Kind = SyntaxKind.ColonTrivia Then
AddClassification(trivia, ClassificationTypeNames.Punctuation)
ElseIf trivia.Kind = SyntaxKind.LineContinuationTrivia Then
AddClassification(New TextSpan(trivia.SpanStart, 1), ClassificationTypeNames.Punctuation)
ElseIf trivia.Kind = SyntaxKind.ConflictMarkerTrivia Then
ClassifyConflictMarker(trivia)
End If
End Sub
Private Sub ClassifyConflictMarker(trivia As SyntaxTrivia)
AddClassification(trivia, ClassificationTypeNames.Comment)
End Sub
Private Sub ClassifyDisabledText(trivia As SyntaxTrivia, triviaList As SyntaxTriviaList)
Dim index = triviaList.IndexOf(trivia)
If index >= 2 AndAlso
triviaList(index - 1).Kind() = SyntaxKind.EndOfLineTrivia AndAlso
triviaList(index - 2).Kind() = SyntaxKind.ConflictMarkerTrivia Then
For Each token In SyntaxFactory.ParseTokens(trivia.ToFullString(), initialTokenPosition:=trivia.SpanStart)
ClassifyToken(token)
Next
Else
AddClassification(trivia, ClassificationTypeNames.ExcludedCode)
End If
End Sub
Private Sub ClassifyDisabledMergeCode(trivia As SyntaxTrivia, triviaText As String)
Dim equalsLineLength = 0
Dim length = triviaText.Length
While equalsLineLength < length
If SyntaxFacts.IsNewLine(triviaText(equalsLineLength)) Then
Exit While
End If
equalsLineLength += 1
End While
AddClassification(New TextSpan(trivia.SpanStart, equalsLineLength), ClassificationTypeNames.Comment)
' Now lex out all the tokens in the rest of the trivia text.
Dim tokens = SyntaxFactory.ParseTokens(
text:=triviaText,
offset:=equalsLineLength,
initialTokenPosition:=trivia.SpanStart + equalsLineLength)
For Each token In tokens
ClassifyToken(token)
Next
End Sub
Private Sub ClassifySkippedTokens(skippedTokens As SkippedTokensTriviaSyntax)
If Not _textSpan.OverlapsWith(skippedTokens.Span) Then
Return
......
......@@ -55,6 +55,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Formatting
AnalyzeLineContinuation(trivia, result)
ElseIf trivia.Kind = SyntaxKind.ColonTrivia Then
result.HasColonTrivia = True
ElseIf trivia.Kind = SyntaxKind.ConflictMarkerTrivia Then
result.HasConflictMarker = True
Else
Contract.ThrowIfFalse(SyntaxFacts.IsPreprocessorDirective(trivia.Kind))
......@@ -133,6 +135,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Formatting
Friend Property HasComments() As Boolean
Friend Property HasPreprocessor() As Boolean
Friend Property HasConflictMarker() As Boolean
Friend Property HasOnlyOneSpaceBeforeLineContinuation() As Boolean
Friend Property HasLineContinuation() As Boolean
......
......@@ -136,19 +136,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Formatting
Not result.HasColonTrivia AndAlso
Not result.HasPreprocessor AndAlso
Not result.HasSkippedOrDisabledText AndAlso
Not result.HasUnknownWhitespace
Not result.HasUnknownWhitespace AndAlso
Not result.HasConflictMarker
End Function
Private Function ContainsOnlyWhitespace(result As Analyzer.AnalysisResult) As Boolean
If result.HasComments OrElse
result.HasColonTrivia OrElse
result.HasPreprocessor OrElse
result.HasSkippedOrDisabledText OrElse
result.HasLineContinuation Then
Return False
End If
Return True
Return Not result.HasComments AndAlso
Not result.HasColonTrivia AndAlso
Not result.HasPreprocessor AndAlso
Not result.HasSkippedOrDisabledText AndAlso
Not result.HasLineContinuation AndAlso
Not result.HasConflictMarker
End Function
Private Function GetWhitespaceOnlyTriviaInfo(token1 As SyntaxToken, token2 As SyntaxToken, result As Analyzer.AnalysisResult) As TriviaData
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册