|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable disable
using System;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Test.Utilities;
using Xunit;
using Xunit.Abstractions;
namespace Microsoft.CodeAnalysis.CSharp.UnitTests
{
public sealed class IncrementalParsingTests(ITestOutputHelper output) : ParsingTests(output)
{
private CSharpParseOptions GetOptions(string[] defines)
{
return new CSharpParseOptions(languageVersion: LanguageVersion.CSharp3, preprocessorSymbols: defines);
}
private SyntaxTree Parse(string text, params string[] defines)
{
var options = this.GetOptions(defines);
var itext = SourceText.From(text);
return SyntaxFactory.ParseSyntaxTree(itext, options);
}
private SyntaxTree Parse(string text, LanguageVersion languageVersion)
{
var options = new CSharpParseOptions(languageVersion: languageVersion);
var itext = SourceText.From(text);
return SyntaxFactory.ParseSyntaxTree(itext, options);
}
private SyntaxTree Parse6(string text)
=> Parse(text, LanguageVersion.CSharp6);
private SyntaxTree ParsePreview(string text)
=> Parse(text, LanguageVersion.Preview);
[Fact]
public void TestChangeClassNameWithNonMatchingMethod()
{
var text = "class goo { void m() { } }";
var oldTree = this.Parse(text);
var newTree = oldTree.WithReplaceFirst("goo", "bar");
Assert.Equal(0, oldTree.GetCompilationUnitRoot().Errors().Length);
Assert.Equal(0, newTree.GetCompilationUnitRoot().Errors().Length);
var diffs = SyntaxDifferences.GetRebuiltNodes(oldTree, newTree);
TestDiffsInOrder(diffs,
SyntaxKind.CompilationUnit,
SyntaxKind.ClassDeclaration,
SyntaxKind.IdentifierToken);
}
[Fact]
public void TestExclamationExclamation()
{
var text = @"#nullable enable
public class C {
public void M(string? x !!) {
}
}";
var oldTree = this.ParsePreview(text);
var newTree = oldTree.WithReplaceFirst("?", "");
oldTree.GetDiagnostics().Verify(
// (4,30): error CS1003: Syntax error, ',' expected
// public void M(string? x !!) {
Diagnostic(ErrorCode.ERR_SyntaxError, "!").WithArguments(",").WithLocation(4, 30));
newTree.GetDiagnostics().Verify(
// (4,29): error CS1003: Syntax error, ',' expected
// public void M(string x !!) {
Diagnostic(ErrorCode.ERR_SyntaxError, "!").WithArguments(",").WithLocation(4, 29));
var diffs = SyntaxDifferences.GetRebuiltNodes(oldTree, newTree);
TestDiffsInOrder(diffs,
SyntaxKind.CompilationUnit,
SyntaxKind.ClassDeclaration,
SyntaxKind.MethodDeclaration,
SyntaxKind.ParameterList,
SyntaxKind.Parameter,
SyntaxKind.PredefinedType,
SyntaxKind.StringKeyword,
SyntaxKind.IdentifierToken);
}
[Fact]
public void TestChangeClassNameToNotMatchConstructor()
{
var text = "class goo { goo() { } }";
var oldTree = this.Parse(text);
var newTree = oldTree.WithReplaceFirst("goo", "bar");
Assert.Equal(0, oldTree.GetCompilationUnitRoot().Errors().Length);
Assert.Equal(0, newTree.GetCompilationUnitRoot().Errors().Length);
var diffs = SyntaxDifferences.GetRebuiltNodes(oldTree, newTree);
TestDiffsInOrder(diffs,
SyntaxKind.CompilationUnit,
SyntaxKind.ClassDeclaration,
SyntaxKind.IdentifierToken,
SyntaxKind.MethodDeclaration,
SyntaxKind.PredefinedType,
SyntaxKind.VoidKeyword);
}
private static void TestDiffsInOrder(ImmutableArray<SyntaxNodeOrToken> diffs, params SyntaxKind[] kinds)
{
Assert.InRange(diffs.Length, 0, kinds.Length);
int diffI = 0;
foreach (var kind in kinds)
{
if (diffI < diffs.Length && diffs[diffI].IsKind(kind))
{
diffI++;
}
}
// all diffs must be consumed.
Assert.Equal(diffI, diffs.Length);
}
[Fact]
public void TestChangeClassNameToMatchConstructor()
{
var text = "class goo { bar() { } }";
var oldTree = this.Parse(text);
var newTree = oldTree.WithReplaceFirst("goo", "bar");
Assert.Equal(0, oldTree.GetCompilationUnitRoot().Errors().Length);
Assert.Equal(0, newTree.GetCompilationUnitRoot().Errors().Length);
var diffs = SyntaxDifferences.GetRebuiltNodes(oldTree, newTree);
TestDiffsInOrder(diffs,
SyntaxKind.CompilationUnit,
SyntaxKind.ClassDeclaration,
SyntaxKind.IdentifierToken,
SyntaxKind.ConstructorDeclaration);
}
[Fact]
public void TestChangeClassNameToNotMatchDestructor()
{
var text = "class goo { ~goo() { } }";
var oldTree = this.Parse(text);
var newTree = oldTree.WithReplaceFirst("goo", "bar");
Assert.Equal(0, oldTree.GetCompilationUnitRoot().Errors().Length);
Assert.Equal(0, newTree.GetCompilationUnitRoot().Errors().Length);
var diffs = SyntaxDifferences.GetRebuiltNodes(oldTree, newTree);
TestDiffsInOrder(diffs,
SyntaxKind.CompilationUnit,
SyntaxKind.ClassDeclaration,
SyntaxKind.IdentifierToken);
}
[Fact]
public void TestChangeClassNameToMatchDestructor()
{
var text = "class goo { ~bar() { } }";
var oldTree = this.Parse(text);
var newTree = oldTree.WithReplaceFirst("goo", "bar");
Assert.Equal(0, oldTree.GetCompilationUnitRoot().Errors().Length);
Assert.Equal(0, newTree.GetCompilationUnitRoot().Errors().Length);
var diffs = SyntaxDifferences.GetRebuiltNodes(oldTree, newTree);
TestDiffsInOrder(diffs,
SyntaxKind.CompilationUnit,
SyntaxKind.ClassDeclaration,
SyntaxKind.IdentifierToken);
}
[Fact]
public void TestChangeFromClassToInterface()
{
var interfaceKeyword = SyntaxFactory.ParseToken("interface"); // prime the memoizer
var text = "class goo { public void m() { } }";
var oldTree = this.Parse(text);
var newTree = oldTree.WithReplaceFirst("class", "interface");
Assert.Equal(0, oldTree.GetCompilationUnitRoot().Errors().Length);
Assert.Equal(0, newTree.GetCompilationUnitRoot().Errors().Length);
var diffs = SyntaxDifferences.GetRebuiltNodes(oldTree, newTree);
TestDiffsInOrder(diffs,
SyntaxKind.CompilationUnit,
SyntaxKind.InterfaceDeclaration,
SyntaxKind.InterfaceKeyword);
}
[Fact]
public void TestChangeFromClassToStruct()
{
var interfaceKeyword = SyntaxFactory.ParseToken("struct"); // prime the memoizer
var text = "class goo { public void m() { } }";
var oldTree = this.Parse(text);
var newTree = oldTree.WithReplaceFirst("class", "struct");
Assert.Equal(0, oldTree.GetCompilationUnitRoot().Errors().Length);
Assert.Equal(0, newTree.GetCompilationUnitRoot().Errors().Length);
var diffs = SyntaxDifferences.GetRebuiltNodes(oldTree, newTree);
TestDiffsInOrder(diffs,
SyntaxKind.CompilationUnit,
SyntaxKind.StructDeclaration,
SyntaxKind.StructKeyword);
}
[Fact]
public void TestChangeMethodName()
{
var text = "class c { void goo(a x, b y) { } }";
var oldTree = this.Parse(text);
var newTree = oldTree.WithReplaceFirst("goo", "bar");
Assert.Equal(0, oldTree.GetCompilationUnitRoot().Errors().Length);
Assert.Equal(0, newTree.GetCompilationUnitRoot().Errors().Length);
var diffs = SyntaxDifferences.GetRebuiltNodes(oldTree, newTree);
TestDiffsInOrder(diffs,
SyntaxKind.CompilationUnit,
SyntaxKind.ClassDeclaration,
SyntaxKind.MethodDeclaration,
SyntaxKind.PredefinedType,
SyntaxKind.IdentifierToken);
}
[Fact]
public void TestChangeIfCondition()
{
var text = @"
#if GOO
class goo { void M() { } }
#endif
";
var oldTree = this.Parse(text, "GOO", "BAR");
var newTree = oldTree.WithReplaceFirst("GOO", "BAR");
Assert.Equal(0, oldTree.GetCompilationUnitRoot().Errors().Length);
Assert.Equal(0, newTree.GetCompilationUnitRoot().Errors().Length);
var diffs = SyntaxDifferences.GetRebuiltNodes(oldTree, newTree);
TestDiffsInOrder(diffs,
SyntaxKind.CompilationUnit,
SyntaxKind.ClassDeclaration,
SyntaxKind.ClassKeyword,
SyntaxKind.EndOfFileToken);
}
[Fact]
public void TestChangeDefine()
{
var text = @"
#define GOO
#if GOO||BAR
class goo { void M() { } }
#endif
";
var oldTree = this.Parse(text);
var newTree = oldTree.WithReplaceFirst("GOO", "BAR");
Assert.Equal(0, oldTree.GetCompilationUnitRoot().Errors().Length);
Assert.Equal(0, newTree.GetCompilationUnitRoot().Errors().Length);
var diffs = SyntaxDifferences.GetRebuiltNodes(oldTree, newTree);
TestDiffsInOrder(diffs,
SyntaxKind.CompilationUnit,
SyntaxKind.ClassDeclaration,
SyntaxKind.ClassKeyword,
SyntaxKind.EndOfFileToken);
}
[Fact]
public void TestChangeDefineAndIfElse()
{
var text = @"
#define GOO
#if GOO
class C { void M() { } }
#else
class C { void N() { } }
#endif
";
var oldTree = this.Parse(text);
var newTree = oldTree.WithReplaceFirst("GOO", "BAR");
Assert.Equal(0, oldTree.GetCompilationUnitRoot().Errors().Length);
Assert.Equal(0, newTree.GetCompilationUnitRoot().Errors().Length);
var diffs = SyntaxDifferences.GetRebuiltNodes(oldTree, newTree);
TestDiffsInOrder(diffs,
SyntaxKind.CompilationUnit,
SyntaxKind.ClassDeclaration,
SyntaxKind.ClassKeyword,
SyntaxKind.IdentifierToken,
SyntaxKind.MethodDeclaration,
SyntaxKind.PredefinedType,
SyntaxKind.IdentifierToken,
SyntaxKind.ParameterList,
SyntaxKind.Block,
SyntaxKind.EndOfFileToken);
}
[Fact]
public void TestAddLineDirective()
{
var text = @"
class C { void M() { } }
";
var oldTree = this.Parse(text);
var newTree = oldTree.WithInsertAt(0, "#line 100\r\n");
Assert.Equal(0, oldTree.GetCompilationUnitRoot().Errors().Length);
Assert.Equal(0, newTree.GetCompilationUnitRoot().Errors().Length);
var diffs = SyntaxDifferences.GetRebuiltNodes(oldTree, newTree);
TestDiffsInOrder(diffs,
SyntaxKind.CompilationUnit,
SyntaxKind.ClassDeclaration,
SyntaxKind.ClassKeyword);
}
[Fact]
public void TestRemoveLineDirective()
{
var text = @"
#line 10
class C { void M() { } }
";
var oldTree = this.Parse(text);
var newTree = oldTree.WithRemoveFirst("#line 10");
Assert.Equal(0, oldTree.GetCompilationUnitRoot().Errors().Length);
Assert.Equal(0, newTree.GetCompilationUnitRoot().Errors().Length);
var diffs = SyntaxDifferences.GetRebuiltNodes(oldTree, newTree);
TestDiffsInOrder(diffs,
SyntaxKind.CompilationUnit,
SyntaxKind.ClassDeclaration,
SyntaxKind.ClassKeyword);
}
[Fact]
public void TestRemoveEndRegionDirective()
{
var text = @"
#if true
class A { void a() { } }
#region
class B { void b() { } }
#endregion
class C { void c() { } }
#endif
";
var oldTree = this.Parse(text);
Assert.Equal(0, oldTree.GetCompilationUnitRoot().Errors().Length);
var oldDirectives = oldTree.GetCompilationUnitRoot().GetDirectives();
Assert.Equal(4, oldDirectives.Count);
Assert.Equal(SyntaxKind.IfDirectiveTrivia, oldDirectives[0].Kind());
Assert.Equal(SyntaxKind.RegionDirectiveTrivia, oldDirectives[1].Kind());
Assert.Equal(SyntaxKind.EndRegionDirectiveTrivia, oldDirectives[2].Kind());
Assert.Equal(SyntaxKind.EndIfDirectiveTrivia, oldDirectives[3].Kind());
var newTree = oldTree.WithRemoveFirst("#endregion");
var errors = newTree.GetCompilationUnitRoot().Errors();
Assert.Equal(2, errors.Length);
Assert.Equal((int)ErrorCode.ERR_EndRegionDirectiveExpected, errors[0].Code);
Assert.Equal((int)ErrorCode.ERR_EndRegionDirectiveExpected, errors[1].Code);
var newDirectives = newTree.GetCompilationUnitRoot().GetDirectives();
Assert.Equal(3, newDirectives.Count);
Assert.Equal(SyntaxKind.IfDirectiveTrivia, newDirectives[0].Kind());
Assert.Equal(SyntaxKind.RegionDirectiveTrivia, newDirectives[1].Kind());
Assert.Equal(SyntaxKind.BadDirectiveTrivia, newDirectives[2].Kind());
var diffs = SyntaxDifferences.GetRebuiltNodes(oldTree, newTree);
TestDiffsInOrder(diffs,
SyntaxKind.CompilationUnit,
SyntaxKind.ClassDeclaration, // class declaration on edge before change
SyntaxKind.MethodDeclaration,
SyntaxKind.PredefinedType,
SyntaxKind.Block,
SyntaxKind.ClassDeclaration, // class declaration on edge after change
SyntaxKind.ClassKeyword, // edge of change and directives different
SyntaxKind.EndOfFileToken); // directives different (endif becomes bad-directive)
}
[Fact]
public void TestAddEndRegionDirective()
{
var text = @"
#if true
class A { void a() { } }
#region
class B { void b() { } }
class C { void c() { } }
#endif
";
var oldTree = this.Parse(text);
var errors = oldTree.GetCompilationUnitRoot().Errors();
Assert.Equal(2, errors.Length);
Assert.Equal((int)ErrorCode.ERR_EndRegionDirectiveExpected, errors[0].Code);
Assert.Equal((int)ErrorCode.ERR_EndRegionDirectiveExpected, errors[1].Code);
var oldDirectives = oldTree.GetCompilationUnitRoot().GetDirectives();
Assert.Equal(3, oldDirectives.Count);
Assert.Equal(SyntaxKind.IfDirectiveTrivia, oldDirectives[0].Kind());
Assert.Equal(SyntaxKind.RegionDirectiveTrivia, oldDirectives[1].Kind());
Assert.Equal(SyntaxKind.BadDirectiveTrivia, oldDirectives[2].Kind());
var newTree = oldTree.WithInsertBefore("class C", "#endregion\r\n");
errors = newTree.GetCompilationUnitRoot().Errors();
Assert.Equal(0, errors.Length);
var newDirectives = newTree.GetCompilationUnitRoot().GetDirectives();
Assert.Equal(4, newDirectives.Count);
Assert.Equal(SyntaxKind.IfDirectiveTrivia, newDirectives[0].Kind());
Assert.Equal(SyntaxKind.RegionDirectiveTrivia, newDirectives[1].Kind());
Assert.Equal(SyntaxKind.EndRegionDirectiveTrivia, newDirectives[2].Kind());
Assert.Equal(SyntaxKind.EndIfDirectiveTrivia, newDirectives[3].Kind());
var diffs = SyntaxDifferences.GetRebuiltNodes(oldTree, newTree);
TestDiffsInOrder(diffs,
SyntaxKind.CompilationUnit,
SyntaxKind.ClassDeclaration, // class declaration on edge before change
SyntaxKind.MethodDeclaration,
SyntaxKind.PredefinedType,
SyntaxKind.Block,
SyntaxKind.ClassDeclaration, // class declaration on edge after change
SyntaxKind.ClassKeyword, // edge of change and directives different
SyntaxKind.EndOfFileToken); // directives different (endif becomes bad-directive)
}
[Fact]
public void TestGlobalStatementToStatementChange()
{
var text = @";a * b";
var oldTree = SyntaxFactory.ParseSyntaxTree(text, options: TestOptions.Script);
var newTree = oldTree.WithInsertAt(0, "{ ");
var diffs = SyntaxDifferences.GetRebuiltNodes(oldTree, newTree);
TestDiffsInOrder(diffs,
SyntaxKind.CompilationUnit,
SyntaxKind.GlobalStatement,
SyntaxKind.Block,
SyntaxKind.OpenBraceToken,
SyntaxKind.EmptyStatement,
SyntaxKind.LocalDeclarationStatement,
SyntaxKind.VariableDeclaration,
SyntaxKind.PointerType,
SyntaxKind.IdentifierName,
SyntaxKind.VariableDeclarator,
SyntaxKind.SemicolonToken, // missing
SyntaxKind.CloseBraceToken); // missing
}
[Fact]
public void TestStatementToGlobalStatementChange()
{
var text = @"{; a * b; }";
var oldTree = SyntaxFactory.ParseSyntaxTree(text, options: TestOptions.Script);
var newTree = oldTree.WithRemoveAt(0, 1);
var diffs = SyntaxDifferences.GetRebuiltNodes(oldTree, newTree);
TestDiffsInOrder(diffs,
SyntaxKind.CompilationUnit,
SyntaxKind.GlobalStatement,
SyntaxKind.EmptyStatement,
SyntaxKind.GlobalStatement,
SyntaxKind.ExpressionStatement,
SyntaxKind.MultiplyExpression,
SyntaxKind.IdentifierName,
SyntaxKind.IdentifierName,
SyntaxKind.SemicolonToken);
}
[Fact]
public void TestAttributeToCollectionExpression1()
{
var source = @"
using System;
class C
{
void M()
{
[A] Method();
}
}
";
var tree = SyntaxFactory.ParseSyntaxTree(source);
Assert.True(tree.GetRoot().DescendantNodesAndSelf().Any(n => n is AttributeSyntax));
Assert.False(tree.GetRoot().DescendantNodesAndSelf().Any(n => n is CollectionExpressionSyntax));
var text = tree.GetText();
var span = new TextSpan(source.IndexOf("]") + 1, length: 1);
var change = new TextChange(span, ".");
text = text.WithChanges(change);
tree = tree.WithChangedText(text);
var fullTree = SyntaxFactory.ParseSyntaxTree(text.ToString());
Assert.False(tree.GetRoot().DescendantNodesAndSelf().Any(n => n is AttributeSyntax));
Assert.True(tree.GetRoot().DescendantNodesAndSelf().Any(n => n is CollectionExpressionSyntax));
WalkTreeAndVerify(tree.GetCompilationUnitRoot(), fullTree.GetCompilationUnitRoot());
}
[Fact]
public void TestCollectionExpressionToAttribute1()
{
var source = @"
using System;
class C
{
void M()
{
[A].Method();
}
}
";
var tree = SyntaxFactory.ParseSyntaxTree(source);
Assert.False(tree.GetRoot().DescendantNodesAndSelf().Any(n => n is AttributeSyntax));
Assert.True(tree.GetRoot().DescendantNodesAndSelf().Any(n => n is CollectionExpressionSyntax));
var text = tree.GetText();
var span = new TextSpan(source.IndexOf("."), length: 1);
var change = new TextChange(span, " ");
text = text.WithChanges(change);
tree = tree.WithChangedText(text);
var fullTree = SyntaxFactory.ParseSyntaxTree(text.ToString());
Assert.True(tree.GetRoot().DescendantNodesAndSelf().Any(n => n is AttributeSyntax));
Assert.False(tree.GetRoot().DescendantNodesAndSelf().Any(n => n is CollectionExpressionSyntax));
WalkTreeAndVerify(tree.GetCompilationUnitRoot(), fullTree.GetCompilationUnitRoot());
}
[Fact]
public void TestLocalFunctionCollectionVsAccessParsing()
{
var source = """
using System;
class C
{
void M()
{
var v = a ? b?[() =>
{
var v = whatever();
int LocalFunc()
{
var v = a ? [b] : c;
}
var v = whatever();
}] : d;
}
}
""";
var tree = SyntaxFactory.ParseSyntaxTree(source);
Assert.Empty(tree.GetDiagnostics());
var localFunc1 = tree.GetRoot().DescendantNodesAndSelf().Single(n => n is LocalFunctionStatementSyntax);
var innerConditionalExpr1 = localFunc1.DescendantNodesAndSelf().Single(n => n is ConditionalExpressionSyntax);
var text = tree.GetText();
var prefix = "var v = a ? b?[() =>";
var suffix = "] : d;";
var prefixSpan = new TextSpan(source.IndexOf(prefix), length: prefix.Length);
var suffixSpan = new TextSpan(source.IndexOf(suffix), length: suffix.Length);
text = text.WithChanges(new TextChange(prefixSpan, ""), new TextChange(suffixSpan, ""));
tree = tree.WithChangedText(text);
Assert.Empty(tree.GetDiagnostics());
var fullTree = SyntaxFactory.ParseSyntaxTree(text.ToString());
Assert.Empty(fullTree.GetDiagnostics());
var localFunc2 = tree.GetRoot().DescendantNodesAndSelf().Single(n => n is LocalFunctionStatementSyntax);
var innerConditionalExpr2 = localFunc2.DescendantNodesAndSelf().Single(n => n is ConditionalExpressionSyntax);
WalkTreeAndVerify(tree.GetCompilationUnitRoot(), fullTree.GetCompilationUnitRoot());
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74456")]
public void TestCollectionExpressionSpreadVsDeletingTopLevelBrace()
{
const string valueSetterLine = "x[1] = 312;";
var initialSource = $$"""
public class Program
{
public void M2()
{
if (true)
{
{
if (true)
{
{{valueSetterLine}}
}
}
}
if (true)
{
y = [.. z];
}
}
}
""";
var initialTree = SyntaxFactory.ParseSyntaxTree(initialSource);
// Initial code is fully legal and should have no parse errors.
Assert.Empty(initialTree.GetDiagnostics());
// Delete '{' (and end of line) before 'values[1] = 312;'
var initialText = initialTree.GetText();
var valueSetterLinePosition = initialSource.IndexOf(valueSetterLine);
var initialLines = initialText.Lines;
int valueSetterLineIndex = initialLines.IndexOf(valueSetterLinePosition);
var openBraceLine = initialText.Lines[valueSetterLineIndex - 1];
Assert.EndsWith("{", openBraceLine.ToString());
var withOpenBraceDeletedText = initialText.WithChanges(new TextChange(openBraceLine.SpanIncludingLineBreak, ""));
var withOpenBraceDeletedTree = initialTree.WithChangedText(withOpenBraceDeletedText);
// Deletion of the open brace causes the method body to close early with the close brace before the `if`
// statement. This will lead to a ton of cascading errors for what follows. In particular, the `[.. values]`
// will be parsed as a very broken attribute on an incomplete member.
{
UsingTree(withOpenBraceDeletedTree,
// (13,9): error CS1519: Invalid token 'if' in class, record, struct, or interface member declaration
// if (true)
Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "if").WithArguments("if").WithLocation(13, 9),
// (13,13): error CS1031: Type expected
// if (true)
Diagnostic(ErrorCode.ERR_TypeExpected, "true").WithLocation(13, 13),
// (13,13): error CS8124: Tuple must contain at least two elements.
// if (true)
Diagnostic(ErrorCode.ERR_TupleTooFewElements, "true").WithLocation(13, 13),
// (13,13): error CS1026: ) expected
// if (true)
Diagnostic(ErrorCode.ERR_CloseParenExpected, "true").WithLocation(13, 13),
// (13,13): error CS1519: Invalid token 'true' in class, record, struct, or interface member declaration
// if (true)
Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "true").WithArguments("true").WithLocation(13, 13),
// (15,15): error CS1519: Invalid token '=' in class, record, struct, or interface member declaration
// y = [.. z];
Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "=").WithArguments("=").WithLocation(15, 15),
// (15,15): error CS1519: Invalid token '=' in class, record, struct, or interface member declaration
// y = [.. z];
Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "=").WithArguments("=").WithLocation(15, 15),
// (15,18): error CS1001: Identifier expected
// y = [.. z];
Diagnostic(ErrorCode.ERR_IdentifierExpected, ".").WithLocation(15, 18),
// (15,19): error CS1001: Identifier expected
// y = [.. z];
Diagnostic(ErrorCode.ERR_IdentifierExpected, ".").WithLocation(15, 19),
// (15,23): error CS1519: Invalid token ';' in class, record, struct, or interface member declaration
// y = [.. z];
Diagnostic(ErrorCode.ERR_InvalidMemberDecl, ";").WithArguments(";").WithLocation(15, 23),
// (17,5): error CS1022: Type or namespace definition, or end-of-file expected
// }
Diagnostic(ErrorCode.ERR_EOFExpected, "}").WithLocation(17, 5),
// (18,1): error CS1022: Type or namespace definition, or end-of-file expected
// }
Diagnostic(ErrorCode.ERR_EOFExpected, "}").WithLocation(18, 1));
N(SyntaxKind.CompilationUnit);
{
N(SyntaxKind.ClassDeclaration);
{
N(SyntaxKind.PublicKeyword);
N(SyntaxKind.ClassKeyword);
N(SyntaxKind.IdentifierToken, "Program");
N(SyntaxKind.OpenBraceToken);
N(SyntaxKind.MethodDeclaration);
{
N(SyntaxKind.PublicKeyword);
N(SyntaxKind.PredefinedType);
{
N(SyntaxKind.VoidKeyword);
}
N(SyntaxKind.IdentifierToken, "M2");
N(SyntaxKind.ParameterList);
{
N(SyntaxKind.OpenParenToken);
N(SyntaxKind.CloseParenToken);
}
N(SyntaxKind.Block);
{
N(SyntaxKind.OpenBraceToken);
N(SyntaxKind.IfStatement);
{
N(SyntaxKind.IfKeyword);
N(SyntaxKind.OpenParenToken);
N(SyntaxKind.TrueLiteralExpression);
{
N(SyntaxKind.TrueKeyword);
}
N(SyntaxKind.CloseParenToken);
N(SyntaxKind.Block);
{
N(SyntaxKind.OpenBraceToken);
N(SyntaxKind.Block);
{
N(SyntaxKind.OpenBraceToken);
N(SyntaxKind.IfStatement);
{
N(SyntaxKind.IfKeyword);
N(SyntaxKind.OpenParenToken);
N(SyntaxKind.TrueLiteralExpression);
{
N(SyntaxKind.TrueKeyword);
}
N(SyntaxKind.CloseParenToken);
N(SyntaxKind.ExpressionStatement);
{
N(SyntaxKind.SimpleAssignmentExpression);
{
N(SyntaxKind.ElementAccessExpression);
{
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "x");
}
N(SyntaxKind.BracketedArgumentList);
{
N(SyntaxKind.OpenBracketToken);
N(SyntaxKind.Argument);
{
N(SyntaxKind.NumericLiteralExpression);
{
N(SyntaxKind.NumericLiteralToken, "1");
}
}
N(SyntaxKind.CloseBracketToken);
}
}
N(SyntaxKind.EqualsToken);
N(SyntaxKind.NumericLiteralExpression);
{
N(SyntaxKind.NumericLiteralToken, "312");
}
}
N(SyntaxKind.SemicolonToken);
}
}
N(SyntaxKind.CloseBraceToken);
}
N(SyntaxKind.CloseBraceToken);
}
}
N(SyntaxKind.CloseBraceToken);
}
}
// Here is where we go off the rails. This corresponds to the `if (true) ...` part after the method
N(SyntaxKind.IncompleteMember);
{
N(SyntaxKind.TupleType);
{
N(SyntaxKind.OpenParenToken);
M(SyntaxKind.TupleElement);
{
M(SyntaxKind.IdentifierName);
{
M(SyntaxKind.IdentifierToken);
}
}
M(SyntaxKind.CommaToken);
M(SyntaxKind.TupleElement);
{
M(SyntaxKind.IdentifierName);
{
M(SyntaxKind.IdentifierToken);
}
}
M(SyntaxKind.CloseParenToken);
}
}
// this corresponds to 'y' in 'y = [.. z];'
N(SyntaxKind.IncompleteMember);
{
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "y");
}
}
// This corresponds to `[.. z]` which parser thinks is an attribute with an invalid dotted name.
N(SyntaxKind.IncompleteMember);
{
N(SyntaxKind.AttributeList);
{
N(SyntaxKind.OpenBracketToken);
N(SyntaxKind.Attribute);
{
N(SyntaxKind.QualifiedName);
{
N(SyntaxKind.QualifiedName);
{
M(SyntaxKind.IdentifierName);
{
M(SyntaxKind.IdentifierToken);
}
N(SyntaxKind.DotToken);
M(SyntaxKind.IdentifierName);
{
M(SyntaxKind.IdentifierToken);
}
}
N(SyntaxKind.DotToken);
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "z");
}
}
}
N(SyntaxKind.CloseBracketToken);
}
}
N(SyntaxKind.CloseBraceToken);
}
N(SyntaxKind.EndOfFileToken);
}
EOF();
}
// Now delete '}' after 'values[1] = 312;'. This should result in no diagnostics.
//
// Note: because we deleted the end of line after the '{', the line that is now on the line where `values...` was
// will be the line that was originally after it (the } line).
var withOpenBraceDeletedLines = withOpenBraceDeletedText.Lines;
var closeBraceLine = withOpenBraceDeletedLines[valueSetterLineIndex];
Assert.EndsWith("}", closeBraceLine.ToString());
var withCloseBraceDeletedText = withOpenBraceDeletedText.WithChanges(new TextChange(closeBraceLine.SpanIncludingLineBreak, ""));
var withCloseBraceDeletedTree = withOpenBraceDeletedTree.WithChangedText(withCloseBraceDeletedText);
Assert.Empty(withCloseBraceDeletedTree.GetDiagnostics());
var fullTree = SyntaxFactory.ParseSyntaxTree(withCloseBraceDeletedText.ToString());
Assert.Empty(fullTree.GetDiagnostics());
WalkTreeAndVerify(withCloseBraceDeletedTree.GetCompilationUnitRoot(), fullTree.GetCompilationUnitRoot());
}
#region "Regression"
#if false
[Fact]
public void DevDiv3599()
{
var text =
@"class B {
#if false
#endif
}
";
var newText =
@"class B
{
private class E
{
}
#if false
#endif
}
";
var oldTree = this.Parse(text);
Assert.Equal(text, oldTree.GetCompilationUnitRoot().ToFullString());
Assert.Equal(0, oldTree.GetCompilationUnitRoot().Errors().Count);
var oldDirectives = oldTree.GetCompilationUnitRoot().GetDirectives();
Assert.Equal(2, oldDirectives.Count);
Assert.Equal(SyntaxKind.IfDirectiveTrivia, oldDirectives[0].Kind);
Assert.Equal(SyntaxKind.EndIfDirectiveTrivia, oldDirectives[1].Kind);
var newTree = oldTree.WithChange(SourceText.From(newText),
new TextChangeRange(new TextSpan(7, 0), 16),
new TextChangeRange(new TextSpan(8, 0), 13),
new TextChangeRange(new TextSpan(9, 0), 7)); //this is the tricky one - it occurs before the trailing trivia of the closing brace
//this is the line that fails without the fix to DevDiv #3599 - there's extra text because of a blender error
Assert.Equal(newText, newTree.GetCompilationUnitRoot().ToFullString());
Assert.Equal(0, newTree.GetCompilationUnitRoot().Errors().Count);
var newDirectives = newTree.GetCompilationUnitRoot().GetDirectives();
Assert.Equal(2, oldDirectives.Count);
Assert.Equal(SyntaxKind.IfDirectiveTrivia, oldDirectives[0].Kind);
Assert.Equal(SyntaxKind.EndIfDirectiveTrivia, oldDirectives[1].Kind);
var diffs = SyntaxDifferences.GetRebuiltNodes(oldTree, newTree);
Assert.Equal(8, diffs.Count);
Assert.Equal(SyntaxKind.CompilationUnit, // Everything - different because a descendant is different
Assert.Equal(SyntaxKind.ClassDeclaration, // class B - different because a descendant is different
//class keyword is reused
Assert.Equal(SyntaxKind.IdentifierToken, // B - different because there a change immediately afterward
//open brace is reused
Assert.Equal(SyntaxKind.ClassDeclaration, // class E - different because it's inserted
Assert.Equal(SyntaxKind.PrivateKeyword, // private - different because it's inserted
Assert.Equal(SyntaxKind.IdentifierToken, // E - different because it's inserted
Assert.Equal(SyntaxKind.OpenBraceToken, // { - different because it's inserted
Assert.Equal(SyntaxKind.CloseBraceToken, // } - different because it's inserted
//close brace is reused
//eof is reused
}
#endif
[Fact]
public void Bug892212()
{
// prove that this incremental change can occur without exception!
var text = "/";
var startTree = SyntaxFactory.ParseSyntaxTree(text);
var newTree = startTree.WithInsertAt(1, "/");
var fullText = newTree.GetCompilationUnitRoot().ToFullString();
Assert.Equal("//", fullText);
}
#if false
[WorkItem(896260, "Personal")]
[Fact]//(Skip = "Bug")]
public void RemovePartialFromClassWithIncorrectSpan()
{
var test = @"partial class C{}";
var resultString = "class C{}";
var startTree = SyntaxTree.Parse(test);
var finalString = startTree.GetCompilationUnitRoot().ToString();
var incrementalChange = new TextChange(startTree.Text, SourceText.From(resultString), new TextChangeRange[] { new TextChangeRange(new TextSpan(0, 7), 0) }); // NOTE: The string length here is a bit too short for the change
var newTree = startTree.WithChange(incrementalChange);
var output = newTree.GetCompilationUnitRoot().ToString();
Assert.Equal(output, resultString);
}
#endif
#if false // can no longer specify an incorrect range
[Fact]
public void Bug896260()
{
var test = @"partial class C{}";
var startTree = SyntaxTree.ParseText(test);
var finalString = startTree.GetCompilationUnitRoot().ToString();
Exception e = null;
try
{
// NOTE: The string length here is a bit too short for the change
var newTree = startTree.WithChange(SourceText.From("class C{}"), new TextChangeRange[] { new TextChangeRange(new TextSpan(0, 7), 0) });
}
catch (Exception x)
{
e = x;
}
Assert.NotNull(e);
}
#endif
[Fact]
public void Bug896262()
{
var text = SourceText.From(@"partial class C{}");
var startTree = SyntaxFactory.ParseSyntaxTree(text);
var finalString = startTree.GetCompilationUnitRoot().ToFullString();
var newText = text.WithChanges(new TextChange(new TextSpan(0, 8), ""));
var newTree = startTree.WithChangedText(newText);
var finalText = newTree.GetCompilationUnitRoot().ToFullString();
Assert.Equal(newText.ToString(), finalText);
}
[WorkItem(536457, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536457")]
[Fact]
public void RemovePartialFromClassWithCorrectSpan()
{
var text = SourceText.From(@"partial class C{}");
var startTree = SyntaxFactory.ParseSyntaxTree(text);
var finalString = startTree.GetCompilationUnitRoot().ToFullString();
var newText = text.WithChanges(new TextChange(new TextSpan(0, 8), ""));
var newTree = startTree.WithChangedText(newText);
var output = newTree.GetCompilationUnitRoot().ToFullString();
Assert.Equal(newText.ToString(), output);
}
[WorkItem(536519, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536519")]
[Fact]
public void AddTopLevelMemberErrorDifference()
{
SourceText oldText = SourceText.From(@"
using System;
public d");
SyntaxTree incrementalTree, parsedTree;
CharByCharIncrementalParse(oldText, 'e', out incrementalTree, out parsedTree);
// The bug is that the errors are currently different
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(536520, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536520")]
[Fact]
public void AddIncompleteStatementErrorDifference()
{
SourceText oldText = SourceText.From(@"
public class Test
{
static int Main()
{
");
SyntaxTree incrementalTree, parsedTree;
CharByCharIncrementalParse(oldText, 'r', out incrementalTree, out parsedTree);
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(536523, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536523")]
[Fact]
public void DifferentNumberOfErrorsForNonCompletedBlock()
{
SourceText oldText = SourceText.From(@"
public class Test
{
static int Main()
{
return 1;
");
SyntaxTree incrementalTree, parsedTree;
CharByCharIncrementalParse(oldText, '}', out incrementalTree, out parsedTree);
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(536649, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536649")]
[Fact]
public void AddingCharacterOnErrorWithExtern()
{
SourceText oldText = SourceText.From(@"
class C
{
public extern C();
static int Main ()
");
char newCharacter = '{';
SyntaxTree incrementalTree, parsedTree;
CharByCharIncrementalParse(oldText, newCharacter, out incrementalTree, out parsedTree);
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(536650, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536650")]
[Fact]
public void ErrorWithExtraModifiers()
{
SourceText oldText = SourceText.From(@"
class MyClass {
internal internal const in");
char newCharacter = 't';
SyntaxTree incrementalTree, parsedTree;
CharByCharIncrementalParse(oldText, newCharacter, out incrementalTree, out parsedTree);
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(536651, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536651")]
[Fact]
public void CommentsCauseDifferentErrorStrings()
{
SourceText oldText = SourceText.From(@"
class A
{
static public int Main ()
{
double d = new double(1); /");
char newCharacter = '/';
SyntaxTree incrementalTree, parsedTree;
CharByCharIncrementalParse(oldText, newCharacter, out incrementalTree, out parsedTree);
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(536652, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536652")]
[Fact]
public void ErrorModifierOnClass()
{
SourceText oldText = SourceText.From(@"
protected class My");
char newCharacter = 'C';
SyntaxTree incrementalTree, parsedTree;
CharByCharIncrementalParse(oldText, newCharacter, out incrementalTree, out parsedTree);
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(536653, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536653")]
[Fact]
public void ErrorPartialClassWithNoBody()
{
SourceText oldText = SourceText.From(@"
public partial clas");
char newCharacter = 's';
SyntaxTree incrementalTree, parsedTree;
CharByCharIncrementalParse(oldText, newCharacter, out incrementalTree, out parsedTree);
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(536654, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536654")]
[Fact]
public void ErrorConstKeywordInMethodName()
{
SourceText oldText = SourceText.From(@" class A
{
protected virtual void Finalize const () { }
}
class B");
char newCharacter = ' ';
SyntaxTree incrementalTree, parsedTree;
CharByCharIncrementalParse(oldText, newCharacter, out incrementalTree, out parsedTree);
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(536655, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536655")]
[Fact]
public void ErrorWithOperatorDeclaration()
{
SourceText oldText = SourceText.From(@"public class TestClass
{
public static TestClass operator ++");
char newCharacter = '(';
SyntaxTree incrementalTree, parsedTree;
CharByCharIncrementalParse(oldText, newCharacter, out incrementalTree, out parsedTree);
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(536661, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536661")]
[Fact]
public void ErrorWithNestedTypeInNew()
{
SourceText oldText = SourceText.From(@"using System;
class Test {
static public int Main(String[] args) {
AbstractBase b = new AbstractBase.");
char newCharacter = 'I';
SyntaxTree incrementalTree, parsedTree;
CharByCharIncrementalParse(oldText, newCharacter, out incrementalTree, out parsedTree);
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(536662, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536662")]
[Fact]
public void ErrorWithInvalidMethodName()
{
SourceText oldText = SourceText.From(@"public class MyClass {
int -goo(");
char newCharacter = ')';
SyntaxTree incrementalTree, parsedTree;
CharByCharIncrementalParse(oldText, newCharacter, out incrementalTree, out parsedTree);
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(536524, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536524")]
[Fact]
public void AddingAFieldInIncompleteClass()
{
SourceText oldText = SourceText.From(@"
public class Test
{
");
SyntaxTree incrementalTree, parsedTree;
CharByCharIncrementalParse(oldText, 'C', out incrementalTree, out parsedTree);
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(903526, "DevDiv/Personal")]
[Fact]
public void AddingTryBlockToMethodOneCharAtTime()
{
SourceText startingText = SourceText.From(@"
public class Test
{
void Goo() {} // Point
}");
SyntaxTree syntaxTree = SyntaxFactory.ParseSyntaxTree(startingText);
// Insert a try/catch block inside the method, one char at a time
foreach (char c in "try{}catch{}")
{
syntaxTree = syntaxTree.WithInsertBefore("} // Point", c.ToString());
}
Assert.Equal(0, syntaxTree.GetCompilationUnitRoot().Errors().Length);
}
[WorkItem(903526, "DevDiv/Personal")]
[Fact]
public void AddingIfBlockToMethodOneCharAtTime()
{
SourceText startingText = SourceText.From(@"
public class Test
{
void Goo() {} // Point
}");
SyntaxTree syntaxTree = SyntaxFactory.ParseSyntaxTree(startingText);
foreach (char c in "if(true){}else{}")
{
syntaxTree = syntaxTree.WithInsertBefore("} // Point", c.ToString());
}
Assert.Equal(0, syntaxTree.GetCompilationUnitRoot().Errors().Length);
}
[Fact]
public void AddingWhileBlockToMethodOneCharAtTime()
{
SourceText startingText = SourceText.From(@"
public class Test
{
void Goo() {} // Point
}");
SyntaxTree syntaxTree = SyntaxFactory.ParseSyntaxTree(startingText);
foreach (char c in "while(true){}")
{
syntaxTree = syntaxTree.WithInsertBefore("} // Point", c.ToString());
}
Assert.Equal(0, syntaxTree.GetCompilationUnitRoot().Errors().Length);
}
[WorkItem(536563, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536563")]
[Fact]
public void CommentOutClassKeyword()
{
SourceText oldText = SourceText.From(@"class MyClass
{
private enum E {zero, one, two, three};
public const E test = E.two;
public static int Main()
{
return 1;
}
}");
int locationOfChange = 0, widthOfChange = 5;
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
CommentOutText(oldText, locationOfChange, widthOfChange, out incrementalTree, out parsedTree);
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(536565, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536565")]
[Fact]
public void CommentOutOpeningCurlyOnPrivateDeclaration()
{
SourceText oldText = SourceText.From(@"
private class B{ public class MyClass
{
private enum E {zero, one, two, three};
public const E test = E.two;
public static int Main()
{
return 1;
}
}}");
int locationOfChange = 42, widthOfChange = 1;
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
CommentOutText(oldText, locationOfChange, widthOfChange, out incrementalTree, out parsedTree);
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(536567, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536567")]
[Fact]
public void CommentOutBracesOnMethodDeclaration()
{
SourceText oldText = SourceText.From(@"
private class B{ private class MyClass
{
private enum E {zero, one, two, three};
public const E test = E.two;
public int Main()
{
return 1;
}
}}");
int locationOfChange = 139, widthOfChange = 2;
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
CommentOutText(oldText, locationOfChange, widthOfChange, out incrementalTree, out parsedTree);
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(536568, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536568")]
[Fact]
public void CommentOutEventKeyword()
{
SourceText oldText = SourceText.From(@"interface IGoo
{
event EventHandler E { add { } remove { } }
}
class Test
{
public static int Main()
{
return 1;
}
}");
int locationOfChange = 20, widthOfChange = 6;
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
CommentOutText(oldText, locationOfChange, widthOfChange, out incrementalTree, out parsedTree);
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(536571, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536571")]
[Fact]
public void CommentOutEventAccessor()
{
SourceText oldText = SourceText.From(@"interface IGoo
{
event EventHandler E { add { } remove { } }
}
class Test
{
public static int Main()
{
return 1;
}
}");
int locationOfChange = 43, widthOfChange = 3;
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
CommentOutText(oldText, locationOfChange, widthOfChange, out incrementalTree, out parsedTree);
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(536573, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536573")]
[Fact]
public void CommentOutDotInUsingAlias()
{
SourceText oldText = SourceText.From(@"using System.Runtime.CompilerServices;
[assembly:InternalsVisibleTo(a)]
class A
{
public int x = 0;
static int Main()
{
return 0;
}
}
");
int locationOfChange = 12, widthOfChange = 1;
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
CommentOutText(oldText, locationOfChange, widthOfChange, out incrementalTree, out parsedTree);
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(536577, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536577")]
[Fact]
public void CommentOutThisInIndexer()
{
SourceText oldText = SourceText.From(@"class A
{
int MyInter.this[int i] {
get {
return intI + 1;
}
set {
intI = value + 1;
}
}
}
");
int locationOfChange = 26, widthOfChange = 4;
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
CommentOutText(oldText, locationOfChange, widthOfChange, out incrementalTree, out parsedTree);
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(536578, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536578")]
[Fact]
public void CommentOutReturnStatementInProperty()
{
SourceText oldText = SourceText.From(@"public class MyClass {
int this[] {
get {
return intI;
}
set {
intI = value;
}
}
}
");
int locationOfChange = 51, widthOfChange = 7;
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
CommentOutText(oldText, locationOfChange, widthOfChange, out incrementalTree, out parsedTree);
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(905311, "DevDiv/Personal")]
[Fact]
public void AddSemicolonInForLoop()
{
SourceText oldText = SourceText.From(@"public class MyClass {
void goo()
{
for (int i = 0
}
}
");
int locationOfInsert = oldText.ToString().IndexOf('0') + 1;
SyntaxTree oldTree = SyntaxFactory.ParseSyntaxTree(oldText);
// The bug was that this would simply assert
SyntaxTree newTree = oldTree.WithInsertAt(locationOfInsert, ";");
}
[WorkItem(536635, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536635")]
[Fact]
public void AddSemicolonAfterStartOfVerbatimString()
{
var oldText = @"class A
{
string s = @
}
";
var oldTree = SyntaxFactory.ParseSyntaxTree(oldText);
var newTree = oldTree.WithInsertAt(oldText.IndexOf('@'), ";");
}
[WorkItem(536717, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536717")]
[Fact]
public void AddReturnWithTriviaAtStart()
{
string oldText = @"0;
}
}";
string diffText = "return ";
// Get the Original parse tree
SyntaxTree origTree = SyntaxFactory.ParseSyntaxTree(oldText);
// Get the tree after incremental parse after applying the change
SyntaxTree incrTree = origTree.WithInsertAt(0, diffText);
string newText = diffText + oldText;
// Get the full parse tree with the applied change
SyntaxTree fullTree = SyntaxFactory.ParseSyntaxTree(newText);
CompareIncToFullParseErrors(incrTree, fullTree);
}
[WorkItem(536728, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536728")]
[Fact]
public void CommentClassWithGTandGTEOperator()
{
// the token in question is now converted to skipped text so this check is no longer applicable
#if false
SourceText oldText = SourceText.From(@"class Test
{
static bool Test()
{
if (b21 >>= b22)
{
}
}
}
");
int locationOfChange = 0, widthOfChange = 5;
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
// This function will update "class" to "/*class*/" in oldText
CommentOutText(oldText, locationOfChange, widthOfChange, out incrementalTree, out parsedTree);
// Verify if the >>= operator in the incremental parse tree is actually 2 separate tokens (> and >=)
Assert.Equal(SyntaxKind.GreaterThanToken, incrementalTree.GetCompilationUnitRoot().ChildNodesAndTokens()[2].ChildNodesAndTokens()[8].Kind);
Assert.Equal(SyntaxKind.GreaterThanEqualsToken, incrementalTree.GetCompilationUnitRoot().ChildNodesAndTokens()[2].ChildNodesAndTokens()[9].Kind);
// The full parse tree should also have the above tree structure for the >>= operator
Assert.Equal(SyntaxKind.GreaterThanToken, parsedTree.GetCompilationUnitRoot().ChildNodesAndTokens()[2].ChildNodesAndTokens()[8].Kind);
Assert.Equal(SyntaxKind.GreaterThanEqualsToken, parsedTree.GetCompilationUnitRoot().ChildNodesAndTokens()[2].ChildNodesAndTokens()[9].Kind);
// Serialize the parse trees and compare the incremental parse tree against the full parse tree
// Assert.Equal( parsedTree.GetCompilationUnitRoot().ToXml().ToString(), incrementalTree.GetCompilationUnitRoot().ToXml().ToString());
Assert.True(parsedTree.GetCompilationUnitRoot().IsEquivalentTo(incrementalTree.GetCompilationUnitRoot()));
#endif
}
[WorkItem(536730, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536730")]
[Fact]
public void CodeWithDollarSign()
{
SourceText oldText = SourceText.From(@"class filesystem{
po$i$;
}");
int locationOfChange = 0, widthOfChange = 5;
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
// This function will update "class" to "/*class*/" in oldText
CommentOutText(oldText, locationOfChange, widthOfChange, out incrementalTree, out parsedTree);
// Verify when you roundtrip the text from the full parse with change should match the text from the incremental parse with change
// The bug is that the "$" sign was being swallowed on the incremental parse
Assert.Equal(parsedTree.GetCompilationUnitRoot().ToFullString(), incrementalTree.GetCompilationUnitRoot().ToFullString());
}
[WorkItem(536731, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536731")]
[Fact]
public void CommentCodeInGOTOStatement()
{
SourceText oldText = SourceText.From(@"class CSTR020mod{ public static void CSTR020() { ON ERROR GOTO ErrorTrap; } }");
int locationOfChange = oldText.ToString().IndexOf("ON", StringComparison.Ordinal), widthOfChange = 2;
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
// This function will update "ON" to "/*ON*/" in oldText
CommentOutText(oldText, locationOfChange, widthOfChange, out incrementalTree, out parsedTree);
// Verify that the errors from the fully parsed tree with the change and the incrementally parsed tree are the same
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(536734, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536734")]
[Fact]
public void CommentConstInConstDeclError()
{
SourceText oldText = SourceText.From(@"class A
{
const byte X4var As Byte = 55;
}
");
int locationOfChange = oldText.ToString().IndexOf("const", StringComparison.Ordinal), widthOfChange = 5;
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
// This function will update "const" to "/*const*/" in oldText
CommentOutText(oldText, locationOfChange, widthOfChange, out incrementalTree, out parsedTree);
// Verify that the errors from the fully parsed tree with the change and the incrementally parsed tree are the same
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(536738, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536738")]
[Fact]
public void CommentClassWithDelegateDecl()
{
SourceText oldText = SourceText.From(@"public class DynClassDrived
{
protected delegate void ProtectedDel(dynamic d);
}
");
int locationOfChange = oldText.ToString().IndexOf("class", StringComparison.Ordinal), widthOfChange = 5;
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
var oldTree = SyntaxFactory.ParseSyntaxTree(oldText);
// This function will update "class" to "/*class*/" in oldText
CommentOutText(oldText, locationOfChange, widthOfChange, out incrementalTree, out parsedTree);
// Verify that the errors from the fully parsed tree with the change and the incrementally parsed tree are the same
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(536738, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536738")]
[Fact]
public void CommentCloseBraceInPropertyDecl()
{
SourceText oldText = SourceText.From(@"public class MemberClass
{
public MyStruct[] Property_MyStructArr { get; set; }
public MyEnum[] Property_MyEnumArr { set; private get; }
}
");
int locationOfChange = oldText.ToString().IndexOf('}'), widthOfChange = 5;
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
// This function will update the first closing brace in property declaration Property_MyStructArr "}" to "/*}*/" in oldText
CommentOutText(oldText, locationOfChange, widthOfChange, out incrementalTree, out parsedTree);
// Verify that the errors from the fully parsed tree with the change and the incrementally parsed tree are the same
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[Fact]
public void CommentCloseBraceInInitOnlyPropertyDecl()
{
SourceText oldText = SourceText.From(@"public class MemberClass
{
public MyStruct[] Property_MyStructArr { get; init; }
public MyEnum[] Property_MyEnumArr { init; private get; }
}
");
int locationOfChange = oldText.ToString().IndexOf('}'), widthOfChange = 5;
// This function will update the first closing brace in property declaration Property_MyStructArr "}" to "/*}*/" in oldText
CommentOutText(oldText, locationOfChange, widthOfChange, out SyntaxTree incrementalTree, out SyntaxTree parsedTree);
// Verify that the errors from the fully parsed tree with the change and the incrementally parsed tree are the same
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(536739, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536739")]
[Fact]
public void CommentFixedInIllegalArrayDecl()
{
SourceText oldText = SourceText.From(@"class Test
{
unsafe struct A
{
public fixed byte Array[dy[""Test""]];
}
}");
int locationOfChange = oldText.ToString().IndexOf("fixed", StringComparison.Ordinal), widthOfChange = 5;
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
// This function will update "fixed" to "/*fixed*/" in oldText above
CommentOutText(oldText, locationOfChange, widthOfChange, out incrementalTree, out parsedTree);
// Verify that the errors from the fully parsed tree with the change and the incrementally parsed tree are the same
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(536788, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536788")]
[Fact]
public void CommentGlobalUsedAsAlias()
{
SourceText oldText = SourceText.From(
@"using @global=System.Int32;
class Test
{
}
");
int locationOfChange = oldText.ToString().IndexOf("@global", StringComparison.Ordinal), widthOfChange = 7;
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
// This function will update "@global" to "/*@global*/" in oldText
CommentOutText(oldText, locationOfChange, widthOfChange, out incrementalTree, out parsedTree);
// Verify if the fully parsed tree and the incrementally parse tree have the same number of children
Assert.Equal(parsedTree.GetCompilationUnitRoot().ChildNodesAndTokens().Count, incrementalTree.GetCompilationUnitRoot().ChildNodesAndTokens().Count);
// Verify if the children of the trees are of the same kind
for (int i = 0; i < parsedTree.GetCompilationUnitRoot().ChildNodesAndTokens().Count; i++)
{
Assert.Equal(parsedTree.GetCompilationUnitRoot().ChildNodesAndTokens()[i].Kind(), incrementalTree.GetCompilationUnitRoot().ChildNodesAndTokens()[i].Kind());
}
}
[WorkItem(536789, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536789")]
[Fact]
public void CommentUsingStmtGlobalUsedAsAlias()
{
SourceText oldText = SourceText.From(
@"using @global=System.Int32;
class Test
{
static int Main()
{
return (@global) 0;
}
}
");
string txtToCmnt = @"using @global=System.Int32;";
int locationOfChange = oldText.ToString().IndexOf(txtToCmnt, StringComparison.Ordinal), widthOfChange = txtToCmnt.Length;
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
// This function will update "using @global=System.Int32;" to "/*using @global=System.Int32;*/" in oldText
CommentOutText(oldText, locationOfChange, widthOfChange, out incrementalTree, out parsedTree);
// Verify that the errors from the fully parsed tree with the change and the incrementally parsed tree are the same
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(536790, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536790")]
[Fact]
public void CmntMainInCodeWithGlobalQualifierInUnsafe()
{
SourceText oldText = SourceText.From(
@"class Test
{
unsafe static int Main()
{
global::System.Int32* p = stackalloc global::System.Int32[5];
}
}
");
string txtToCmnt = @"Main";
int locationOfChange = oldText.ToString().IndexOf(txtToCmnt, StringComparison.Ordinal), widthOfChange = txtToCmnt.Length;
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
// This function will update "Main" to "/*Main*/" in oldText
CommentOutText(oldText, locationOfChange, widthOfChange, out incrementalTree, out parsedTree);
// Verify that the errors from the fully parsed tree with the change and the incrementally parsed tree are the same
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(536842, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536842"), WorkItem(543452, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543452")]
[Fact]
public void DelegateDeclInvalidCastException()
{
SourceText oldText = SourceText.From(
@" public delegate void MyDelegate01(dynamic d, int n);
[System.CLSCompliant(false)]
");
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
// This function will add the ' ' character to the end of oldText
// The bug is that when you do the incremental parse with the change an InvalidCastException is thrown at runtime.
CharByCharIncrementalParse(oldText, ' ', out incrementalTree, out parsedTree);
// Verify the incrementalTree text and the fully parsed tree text matches
Assert.Equal(parsedTree.GetText().ToString(), incrementalTree.GetText().ToString());
// Verify that the errors from the fully parsed tree with the change and the incrementally parsed tree are the same
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(536843, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536843")]
[Fact]
public void KeyExistsArgumentException()
{
SourceText oldText = SourceText.From(
@" public abstract class AbstractCompiler : ICompiler
{
protected virtual IDictionary GetOptions()
{
foreach (string parameter in parameters.Split(' '))
{
if (true)
{
string[] parts = parameter.Remove(0, 1).Split(':');
string key = parts[0].ToLower();
if (true)
{
}
if (true)
{
}
else if (false)
{
}
}
}
}
protected virtual TargetType GetTargetType(IDictionary options)
{
}
}
");
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
// This function will add the ' ' character to the end of oldText
// The bug is that when you do the incremental parse with the change an ArgumentException is thrown at runtime.
CharByCharIncrementalParse(oldText, ' ', out incrementalTree, out parsedTree);
// Verify the incrementalTree text and the fully parsed tree text matches
Assert.Equal(parsedTree.GetText().ToString(), incrementalTree.GetText().ToString());
// Verify that the errors from the fully parsed tree with the change and the incrementally parsed tree are the same
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(536849, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536849")]
[Fact]
public void QueryExprWithKeywordsAsVariablesAndIncompleteJoin()
{
SourceText oldText = SourceText.From(
@"class Test {
static void Main()
{
var q =
from string @params in ( @foreach/9)
join");
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
// This function will add the ' ' character to the end of oldText
CharByCharIncrementalParse(oldText, ' ', out incrementalTree, out parsedTree);
// Verify that the errors from the fully parsed tree with the change and the incrementally parsed tree are the same
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(536865, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536865")]
[Fact]
public void IncompleteGenericTypeParamVarDecl()
{
SourceText oldText = SourceText.From(
@"public class Test
{
public static int Main()
{
C<B, A");
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
// This function will add the '>' character to the end of oldText
CharByCharIncrementalParse(oldText, '>', out incrementalTree, out parsedTree);
// Verify that the errors from the fully parsed tree with the change and the incrementally parsed tree are the same
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(536866, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536866")]
[Fact]
public void IncompleteArglistMethodInvocation()
{
SourceText oldText = SourceText.From(
@"public class Test
{
public static void Run()
{
testvar.Test(__arglist(10l, 1");
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
// This function will add the '2' character to the end of oldText
CharByCharIncrementalParse(oldText, '2', out incrementalTree, out parsedTree);
// Verify that the errors from the fully parsed tree with the change and the incrementally parsed tree are the same
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(536867, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536867")]
[Fact]
public void IncompleteErrorExtensionMethodDecl()
{
SourceText oldText = SourceText.From(
@"public static class Extensions
{
public static this Goo(int i, this string str)");
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
// This function will add the ' ' character to the end of oldText
// The bug is that the Incremental Parser throws a NullReferenceException
CharByCharIncrementalParse(oldText, ' ', out incrementalTree, out parsedTree);
// Verify the incrementalTree text and the fully parsed tree text matches
Assert.Equal(parsedTree.GetText().ToString(), incrementalTree.GetText().ToString());
// Verify that the errors from the fully parsed tree with the change and the incrementally parsed tree are the same
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(536868, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536868")]
[Fact]
public void IncompleteErrorLambdaExpr()
{
SourceText oldText = SourceText.From(
@"public class Program
{
public static int Main()
{
D[] a2 = new [] {(int x)=");
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
// This function will add the '>' character to the end of oldText
CharByCharIncrementalParse(oldText, '>', out incrementalTree, out parsedTree);
// Verify that the errors from the fully parsed tree with the change and the incrementally parsed tree are the same
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(536871, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536871")]
[Fact]
public void IncompleteCodeFollowingXmlDocStyleComment()
{
SourceText oldText = SourceText.From(
@"class C
{
/// =>
");
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
// This function will add the 's' character to the end of oldText
CharByCharIncrementalParse(oldText, 's', out incrementalTree, out parsedTree);
// Verify that the errors from the fully parsed tree with the change and the incrementally parsed tree are the same
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(536897, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536897")]
[Fact]
public void IncompleteNamespaceFollowingExternError()
{
SourceText oldText = SourceText.From(
@"using C1 = extern;
namespace N");
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
// This function will add the '1' character to the end of oldText
CharByCharIncrementalParse(oldText, '1', out incrementalTree, out parsedTree);
// Verify that the errors from the fully parsed tree with the change and the incrementally parsed tree are the same
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(536898, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536898")]
[Fact]
public void IncompleteConditionWithJaggedArrayAccess()
{
SourceText oldText = SourceText.From(
@"class A
{
public static int Main()
{
if (arr[2][3l] =");
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
// This function will add the '=' character to the end of oldText
CharByCharIncrementalParse(oldText, '=', out incrementalTree, out parsedTree);
// Verify that the errors from the fully parsed tree with the change and the incrementally parsed tree are the same
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(536899, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536899")]
[Fact]
public void TrailingCommentFollowingAttributesInsideMethod()
{
SourceText oldText = SourceText.From(
@"public class goo
{
public static int Goo
{
[method:A][goo:A]/");
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
var oldTree = SyntaxFactory.ParseSyntaxTree(oldText);
// This function will add the '/' character to the end of oldText
CharByCharIncrementalParse(oldText, '/', out incrementalTree, out parsedTree);
// Verify that the first child node of the root is equivalent between incremental tree and full parse tree
Assert.Equal(parsedTree.GetCompilationUnitRoot().ChildNodesAndTokens()[0].AsNode().ToFullString(), incrementalTree.GetCompilationUnitRoot().ChildNodesAndTokens()[0].AsNode().ToFullString());
}
[WorkItem(536901, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536901")]
[Fact]
public void SpecialAttribNameWithDoubleAtToken()
{
SourceText oldText = SourceText.From(
@"[@@X");
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
// This function will add the ']' character to the end of oldText
CharByCharIncrementalParse(oldText, ']', out incrementalTree, out parsedTree);
// Verify that the errors from the fully parsed tree with the change and the incrementally parsed tree are the same
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(536903, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536903")]
[Fact]
public void AssertForAttributeWithGenericType()
{
SourceText oldText = SourceText.From(
@"[Goo<i");
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
// This function will add the 'n' character to the end of oldText
// The bug is that an assert is thrown when you perform the incremental parse with the change
CharByCharIncrementalParse(oldText, 'n', out incrementalTree, out parsedTree);
// Verify that the errors from the fully parsed tree with the change and the incrementally parsed tree are the same
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(539056, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/539056")]
[Fact]
public void AssertOnTypingColonInGenericTypeConstraint()
{
SourceText oldText = SourceText.From(
@"class Meta<T>:imeta<T> where T");
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
// This function will add the ':' character to the end of oldText
// The bug is that an assert is thrown when you perform the incremental parse with the change
CharByCharIncrementalParse(oldText, ':', out incrementalTree, out parsedTree);
// Verify that the errors from the fully parsed tree with the change and the incrementally parsed tree are the same
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(536904, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536904")]
[Fact]
public void ArithmeticExprWithLongConstant()
{
SourceText oldText = SourceText.From(
@"public class arith0018
{
public static void Main()
{
long l1 = 1l/0");
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
// This function will add the 'l' character to the end of oldText
CharByCharIncrementalParse(oldText, 'l', out incrementalTree, out parsedTree);
// Verify that the errors from the fully parsed tree with the change and the incrementally parsed tree are the same
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(536913, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536913")]
[Fact]
public void AddClassKeywordWithAnonymousMethodThrowsIndexOutOfRangeException()
{
SourceText oldText = SourceText.From(
@"Production<V, T>
{
private readonly T epsilon=default(T);
public Production(T epsilon, Function<SomeType<object>, object> action, V variable, SomeType<object> someType)
{
((VoidDelegate)delegate
{
someType.Iterate(delegate(object o)
{
System.Console.WriteLine(((BoolDelegate)delegate { return object.Equals(o, this.epsilon); })());
});
})();
}
}");
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
// This function will add the "class " text to the start of oldText
// The bug is that the incremental parser was throwing an IndexOutofRangeException
TokenByTokenBottomUp(oldText, "class ", out incrementalTree, out parsedTree);
// Verify the incrementalTree roundtrip text is the same as parsedTree roundtrip text
Assert.Equal(parsedTree.GetCompilationUnitRoot().ToFullString(), incrementalTree.GetCompilationUnitRoot().ToFullString());
// Verify that the errors from the fully parsed tree with the change and the incrementally parsed tree are the same
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(536914, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536914")]
[Fact]
public void AddClassKeywordWithParamsModifierInAnonymousMethod()
{
SourceText oldText = SourceText.From(
@"Test
{
static int Goo()
{
Dele f = delegate(params int[] a) { return 1;};
}
}
");
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
// This function will add the "class " text to the start of oldText
TokenByTokenBottomUp(oldText, "class ", out incrementalTree, out parsedTree);
// Verify that the errors from the fully parsed tree with the change and the incrementally parsed tree are the same
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(536916, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536916")]
[Fact]
public void AddEqualTokenBeforeLongConst()
{
SourceText oldText = SourceText.From(
@"3l;
");
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
// This function will add the "=" text to the start of oldText
TokenByTokenBottomUp(oldText, "= ", out incrementalTree, out parsedTree);
// Verify that the errors from the fully parsed tree with the change and the incrementally parsed tree are the same
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(536917, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536917")]
[Fact]
public void AddEqualTokenBeforeHugeConst()
{
SourceText oldText = SourceText.From(
@"18446744073709551616;
");
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
// This function will add the "=" text to the start of oldText
TokenByTokenBottomUp(oldText, "=", out incrementalTree, out parsedTree);
// Verify that the errors from the fully parsed tree with the change and the incrementally parsed tree are the same
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(536616, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536616")]
[Fact]
public void AddEndTagToXmlDocComment()
{
SourceText oldText = SourceText.From(
@"class c1
{
/// <Summary>
/// <
");
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
TokenByTokenBottomUp(oldText, "/", out incrementalTree, out parsedTree);
// Verify that the errors from the fully parsed tree with the change and the incrementally parsed tree are the same
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(537888, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537888")]
[Fact]
public void AddClassKeywordToCodeWithConstructorAndProperty()
{
SourceText oldText = SourceText.From(
@"IntVector
{
public IntVector(int length)
{
}
public int Length
{
get
{
return 1;
}
}
}
");
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
// This function will add the "class " text to the start of oldText
TokenByTokenBottomUp(oldText, "class ", out incrementalTree, out parsedTree);
// Verify that the errors from the fully parsed tree with the change and the incrementally parsed tree are the same
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(537890, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537890")]
[Fact]
public void AddCurlyBracesToIncompleteCode()
{
SourceText oldText = SourceText.From(
@" int[][] arr;
if (arr[1][1] == 0)
return 0;
else
return 1;
}
}
");
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
// This function will add the "{ " text to the start of oldText
TokenByTokenBottomUp(oldText, "{ ", out incrementalTree, out parsedTree);
// Verify that the errors from the fully parsed tree with the change and the incrementally parsed tree are the same
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(537891, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537891")]
[Fact]
public void AddOpenParenToIncompleteMethodDeclBeforeDestructor()
{
SourceText oldText = SourceText.From(
@"string s) {}
~Widget() {}
");
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
// This function will add the "(" text to the start of oldText
TokenByTokenBottomUp(oldText, "(", out incrementalTree, out parsedTree);
// Verify that the errors from the fully parsed tree with the change and the incrementally parsed tree are the same
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(538977, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/538977")]
[Fact]
public void AddTokenToIncompleteQueryExpr()
{
SourceText oldText = SourceText.From(
@"equals abstract select new { };
");
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
// This function will add the "i " text to the start of oldText
TokenByTokenBottomUp(oldText, "i ", out incrementalTree, out parsedTree);
// Verify that the errors from the fully parsed tree with the change and the incrementally parsed tree are the same
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(536986, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536986")]
[Fact]
public void IncompleteGenericInterfaceImplementation()
{
SourceText oldText = SourceText.From(
@"class GenInt : IGenX<int[]>, IGenY<int>
{
string IGenX<int[]>.m");
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
// This function will add the '(' character to the end of oldText
CharByCharIncrementalParse(oldText, '(', out incrementalTree, out parsedTree);
// Verify that the errors from the fully parsed tree with the change and the incrementally parsed tree are the same
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(536988, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536988")]
[Fact]
public void IncompleteIndexerDecl()
{
SourceText oldText = SourceText.From(
@"public class Test
{
int this[ params int [] args, i");
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
// This function will add the 'n' character to the end of oldText
CharByCharIncrementalParse(oldText, 'n', out incrementalTree, out parsedTree);
// Verify that the errors from the fully parsed tree with the change and the incrementally parsed tree are the same
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(536990, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536990")]
[Fact]
public void IncompleteGenericVarDeclWithUnderscore()
{
SourceText oldText = SourceText.From(
@"public class Test
{
public static int Main()
{
cT ct = _class.TestT<cT, cU");
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
// This function will add the '>' character to the end of oldText
CharByCharIncrementalParse(oldText, '>', out incrementalTree, out parsedTree);
// Verify that the errors from the fully parsed tree with the change and the incrementally parsed tree are the same
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(536991, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/536991")]
[Fact]
public void IncompleteUnsafeArrayInit()
{
SourceText oldText = SourceText.From(
@"unsafe class Test
{
unsafe void*[] A = {(void*");
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
// This function will add the ')' character to the end of oldText
CharByCharIncrementalParse(oldText, ')', out incrementalTree, out parsedTree);
// Verify that the errors from the fully parsed tree with the change and the incrementally parsed tree are the same
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(537012, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537012")]
[Fact]
public void RemoveClassIdentifierTokenWithDelegDecl()
{
SourceText oldText = SourceText.From(
@"class Test
{
static int Goo()
{
Dele f = delegate(params int[] a) { return 1;};
}
}");
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
string textToRemove = "Test";
int locationOfChange = oldText.ToString().IndexOf(textToRemove, StringComparison.Ordinal);
int widthOfChange = textToRemove.Length;
// This function will remove the "Test" token from oldText
RemoveText(oldText, locationOfChange, widthOfChange, out incrementalTree, out parsedTree);
// Verify that the errors from the fully parsed tree with the change and the incrementally parsed tree are the same
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(537889, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537889")]
[Fact]
public void RemoveBracesInExtensionIndexer()
{
SourceText oldText = SourceText.From(
@"public static class Extensions
{
public static int this(this int x)[int index1] { get { return 9; } }
public static int Main()
{
return 0;
}
}");
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
string textToRemove = ")[";
int locationOfChange = oldText.ToString().IndexOf(textToRemove, StringComparison.Ordinal);
int widthOfChange = textToRemove.Length;
// This function will remove the ")[" token from oldText
RemoveText(oldText, locationOfChange, widthOfChange, out incrementalTree, out parsedTree);
// Verify that the errors from the fully parsed tree with the change and the incrementally parsed tree are the same
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(537892, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537892")]
[Fact]
public void RemoveParensInMethodDeclContainingPartialKeyword()
{
SourceText oldText = SourceText.From(
@"static int Main()
{
partial");
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
string textToRemove = "()";
int locationOfChange = oldText.ToString().IndexOf(textToRemove, StringComparison.Ordinal);
int widthOfChange = textToRemove.Length;
// This function will remove the "()" text from oldText
RemoveText(oldText, locationOfChange, widthOfChange, out incrementalTree, out parsedTree);
// Verify that the errors from the fully parsed tree with the change and the incrementally parsed tree are the same
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(537020, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537020")]
[Fact]
public void IncompleteGlobalQualifierExplInterfaceImpl()
{
SourceText oldText = SourceText.From(
@"class Test : N1.I1
{
int global::N1.");
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
// This function will add the 'I' character to the end of oldText
CharByCharIncrementalParse(oldText, 'I', out incrementalTree, out parsedTree);
// Verify that the fully parsed tree is structurally equivalent to the incrementally parsed tree
CompareTreeEquivalence(parsedTree.GetCompilationUnitRoot(), incrementalTree.GetCompilationUnitRoot());
}
[WorkItem(537033, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537033")]
[Fact]
public void RemoveParensInGetEnumeratorWithPropertyAccess()
{
SourceText oldText = SourceText.From(
@"public class QueueProducerConsumer
{
public IEnumerator<T> GetEnumerator()
{
while (true)
{
if (!value.HasValue)
{
}
}
}
}
");
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
string textToRemove = "()";
int locationOfChange = oldText.ToString().IndexOf(textToRemove, StringComparison.Ordinal);
int widthOfChange = textToRemove.Length;
// This function will remove the "()" text from oldText
RemoveText(oldText, locationOfChange, widthOfChange, out incrementalTree, out parsedTree);
// Verify that the fully parsed tree is structurally equivalent to the incrementally parsed tree
CompareTreeEquivalence(parsedTree.GetCompilationUnitRoot(), incrementalTree.GetCompilationUnitRoot());
}
[WorkItem(537053, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537053")]
[Fact]
public void RemoveReturnTypeOnProperty()
{
SourceText oldText = SourceText.From(
@"public class Test
{
public int Prop
{
set
{
D d = delegate
{
Validator(value);
};
}
}
}
");
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
string textToRemove = "int";
int locationOfChange = oldText.ToString().IndexOf(textToRemove, StringComparison.Ordinal);
int widthOfChange = textToRemove.Length;
// This function will remove the "int" text from oldText
RemoveText(oldText, locationOfChange, widthOfChange, out incrementalTree, out parsedTree);
// Verify that the fully parsed tree is structurally equivalent to the incrementally parsed tree
CompareTreeEquivalence(parsedTree.GetCompilationUnitRoot(), incrementalTree.GetCompilationUnitRoot());
}
[WorkItem(538975, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/538975")]
[Fact]
public void RemoveTypeOnArrayInParameterWithMethodDeclError()
{
SourceText oldText = SourceText.From(
@"public class A
{
public void static Main(string[] args)
{
}
}
");
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
string textToRemove = "string";
int locationOfChange = oldText.ToString().IndexOf(textToRemove, StringComparison.Ordinal);
int widthOfChange = textToRemove.Length;
// This function will remove the "string" text from oldText
RemoveText(oldText, locationOfChange, widthOfChange, out incrementalTree, out parsedTree);
// Verify that the errors from the fully parsed tree with the change and the incrementally parsed tree are the same
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(537054, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537054")]
[Fact]
public void RemoveReturnTypeOnGenericMethodWithTypeParamConstraint()
{
SourceText oldText = SourceText.From(
@"class Test
{
public static int M<U>() where U : IDisposable, new()
{
}
}");
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
string textToRemove = "int";
int locationOfChange = oldText.ToString().IndexOf(textToRemove, StringComparison.Ordinal);
int widthOfChange = textToRemove.Length;
// This function will remove the "int" text from oldText
RemoveText(oldText, locationOfChange, widthOfChange, out incrementalTree, out parsedTree);
// Verify that the fully parsed tree is structurally equivalent to the incrementally parsed tree
CompareTreeEquivalence(parsedTree.GetCompilationUnitRoot(), incrementalTree.GetCompilationUnitRoot());
}
[WorkItem(537084, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537084")]
[Fact]
public void RemoveNamespaceQualifierFromTypeInIfCondition()
{
SourceText oldText = SourceText.From(
@"public class Driver
{
public void AddValidations()
{
if (typeof(K) is System.ValueType)
{
}
}
}");
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
string textToRemove = "System";
int locationOfChange = oldText.ToString().IndexOf(textToRemove, StringComparison.Ordinal);
int widthOfChange = textToRemove.Length;
// This function will remove the "System" text from oldText
// The bug is that "Debug.Assert" was thrown by the Incremental Parser with this change
RemoveText(oldText, locationOfChange, widthOfChange, out incrementalTree, out parsedTree);
// Verify that the fully parsed tree is structurally equivalent to the incrementally parsed tree
CompareTreeEquivalence(parsedTree.GetCompilationUnitRoot(), incrementalTree.GetCompilationUnitRoot());
}
[WorkItem(537092, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537092")]
[Fact]
public void RemoveMethodNameWithLambdaExprInMethodBody()
{
SourceText oldText = SourceText.From(
@"class C
{
static int Main()
{
M((x, y) => new Pair<int,double>());
}
}");
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
string textToRemove = "Main";
int locationOfChange = oldText.ToString().IndexOf(textToRemove, StringComparison.Ordinal);
int widthOfChange = textToRemove.Length;
// This function will remove the "Main" text from oldText
RemoveText(oldText, locationOfChange, widthOfChange, out incrementalTree, out parsedTree);
// Verify that the errors from the fully parsed tree with the change and the incrementally parsed tree are the same
CompareIncToFullParseErrors(incrementalTree, parsedTree);
// Verify that the fully parsed tree is structurally equivalent to the incrementally parsed tree
CompareTreeEquivalence(parsedTree.GetCompilationUnitRoot(), incrementalTree.GetCompilationUnitRoot());
}
[WorkItem(538978, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/538978")]
[Fact]
public void RemoveInitializerOnDeclStatementWithErrors()
{
SourceText oldText = SourceText.From(
@"x public static string S = null;
");
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
string textToRemove = "null";
int locationOfChange = oldText.ToString().IndexOf(textToRemove, StringComparison.Ordinal);
int widthOfChange = textToRemove.Length;
// This function will remove the "null" text from oldText
RemoveText(oldText, locationOfChange, widthOfChange, out incrementalTree, out parsedTree);
// Verify that the errors from the fully parsed tree with the change and the incrementally parsed tree are the same
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(537116, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537116")]
[Fact]
public void CmntEndIfInMethodDecl()
{
SourceText oldText = SourceText.From(
@"class Referenced
{
#if PUBLIC
public
#else
internal
#endif
static RecordNotFound Method(){}
}
//<Code>");
string txtToCmnt = @"internal
#endif
static RecordNotFound Method(){}";
int locationOfChange = oldText.ToString().IndexOf(txtToCmnt, StringComparison.Ordinal), widthOfChange = txtToCmnt.Length;
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
// This function will comment out the txtToCmnt in oldText
CommentOutText(oldText, locationOfChange, widthOfChange, out incrementalTree, out parsedTree);
// Verify that the errors from the fully parsed tree with the change and the incrementally parsed tree are the same
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(537125, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537125")]
[Fact]
public void CmntAnonTypeInQueryExpr()
{
SourceText oldText = SourceText.From(
@"public class QueryExpressionTest
{
public static int Main()
{
var query7 = from a in b join delegate in c on d equals delegate select new { e, delegate };
var query13 = from delegate in f join g in h on delegate equals i select delegate;
}
}");
string txtToCmnt = @"select new { e, delegate }";
int locationOfChange = oldText.ToString().IndexOf(txtToCmnt, StringComparison.Ordinal), widthOfChange = txtToCmnt.Length;
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
// This function will comment out the txtToCmnt in oldText
CommentOutText(oldText, locationOfChange, widthOfChange, out incrementalTree, out parsedTree);
// Verify that the errors from the fully parsed tree with the change and the incrementally parsed tree are the same
CompareIncToFullParseErrors(incrementalTree, parsedTree);
}
[WorkItem(537180, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537180")]
[Fact]
public void CmntParamsInExtProperty()
{
SourceText oldText = SourceText.From(
@"public static class Extensions
{
public static int Goo2(this int x ) { set { var1 = value; } }
}");
string txtToCmnt = @"(this int x )";
int locationOfChange = oldText.ToString().IndexOf(txtToCmnt, StringComparison.Ordinal), widthOfChange = txtToCmnt.Length;
SyntaxTree incrementalTree;
SyntaxTree parsedTree;
// This function will comment out the txtToCmnt in oldText
CommentOutText(oldText, locationOfChange, widthOfChange, out incrementalTree, out parsedTree);
// Verify that the fully parsed tree is structurally equivalent to the incrementally parsed tree
CompareTreeEquivalence(parsedTree.GetCompilationUnitRoot(), incrementalTree.GetCompilationUnitRoot());
}
[WorkItem(537533, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/537533")]
[Fact]
public void MultiCommentInserts()
{
var str = @"using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main(string[] args)
{
// abc
// 123
// def
if (true) { } else { }
}
}";
var text = SourceText.From(str);
var tree = SyntaxFactory.ParseSyntaxTree(text);
var text2 = text.WithChanges(
new TextChange(new TextSpan(str.IndexOf(" abc", StringComparison.Ordinal), 0), "//"),
new TextChange(new TextSpan(str.IndexOf(" 123", StringComparison.Ordinal), 0), "//"),
new TextChange(new TextSpan(str.IndexOf(" def", StringComparison.Ordinal), 0), "//"));
var parsedTree = SyntaxFactory.ParseSyntaxTree(text2);
var reparsedTree = tree.WithChangedText(text2);
CompareTreeEquivalence(parsedTree.GetCompilationUnitRoot(), reparsedTree.GetCompilationUnitRoot());
}
[WorkItem(542236, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542236")]
[Fact]
public void InsertOpenBraceBeforeCodes()
{
SourceText oldText = SourceText.From(@"
this.I = i;
};
}");
var startTree = SyntaxFactory.ParseSyntaxTree(oldText);
// first make certain this text round trips
Assert.Equal(oldText.ToString(), startTree.GetCompilationUnitRoot().ToFullString());
var newText = oldText.WithChanges(new TextChange(new TextSpan(0, 0), "{"));
var reparsedTree = startTree.WithChangedText(newText);
var parsedTree = SyntaxFactory.ParseSyntaxTree(newText);
CompareIncToFullParseErrors(reparsedTree, parsedTree);
}
[WorkItem(6676, "https://github.com/dotnet/roslyn/issues/6676")]
[Fact]
public void InsertExpressionStatementWithoutSemicolonBefore()
{
SourceText oldText = SourceText.From(@"System.Console.WriteLine(true)
");
var startTree = SyntaxFactory.ParseSyntaxTree(oldText, options: TestOptions.Script);
startTree.GetDiagnostics().Verify();
var newText = oldText.WithChanges(new TextChange(new TextSpan(0, 0), @"System.Console.WriteLine(false)
"));
AssertEx.AreEqual(@"System.Console.WriteLine(false)
System.Console.WriteLine(true)
",
newText.ToString());
var reparsedTree = startTree.WithChangedText(newText);
var parsedTree = SyntaxFactory.ParseSyntaxTree(newText, options: TestOptions.Script);
parsedTree.GetDiagnostics().Verify(
// (1,32): error CS1002: ; expected
// System.Console.WriteLine(false)
Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(1, 32));
CompareIncToFullParseErrors(reparsedTree, parsedTree);
}
[WorkItem(6676, "https://github.com/dotnet/roslyn/issues/6676")]
[Fact]
public void InsertExpressionStatementWithoutSemicolonAfter()
{
SourceText oldText = SourceText.From(@"System.Console.WriteLine(true)
");
var startTree = SyntaxFactory.ParseSyntaxTree(oldText, options: TestOptions.Script);
startTree.GetDiagnostics().Verify();
var newText = oldText.WithInsertAt(
oldText.Length,
@"System.Console.WriteLine(false)
");
AssertEx.Equal(@"System.Console.WriteLine(true)
System.Console.WriteLine(false)
", newText.ToString());
var reparsedTree = startTree.WithChangedText(newText);
var parsedTree = SyntaxFactory.ParseSyntaxTree(newText, options: TestOptions.Script);
parsedTree.GetDiagnostics().Verify(
// (1,31): error CS1002: ; expected
// System.Console.WriteLine(true)
Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(1, 31));
CompareIncToFullParseErrors(reparsedTree, parsedTree);
}
[WorkItem(6676, "https://github.com/dotnet/roslyn/issues/6676")]
[Fact]
public void MakeEmbeddedExpressionStatementWithoutSemicolon()
{
SourceText oldText = SourceText.From(@"System.Console.WriteLine(true)
");
var startTree = SyntaxFactory.ParseSyntaxTree(oldText, options: TestOptions.Script);
startTree.GetDiagnostics().Verify();
var newText = oldText.WithChanges(new TextChange(new TextSpan(0, 0), @"if (false)
"));
AssertEx.Equal(@"if (false)
System.Console.WriteLine(true)
", newText.ToString());
var reparsedTree = startTree.WithChangedText(newText);
var parsedTree = SyntaxFactory.ParseSyntaxTree(newText, options: TestOptions.Script);
parsedTree.GetDiagnostics().Verify(
// (2,31): error CS1002: ; expected
// System.Console.WriteLine(true)
Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(2, 31));
CompareIncToFullParseErrors(reparsedTree, parsedTree);
}
[WorkItem(531404, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/531404")]
[Fact]
public void AppendDisabledText()
{
var text =
@"class SmallDictionary
{
public void Add(int key, int value)
{
int hash = key + value;
#if DEBUG";
var originalTree = this.Parse(text);
var changedTree = originalTree.WithInsertAt(text.Length, "\r\n hash++;");
var parsedTree = this.Parse(changedTree.GetCompilationUnitRoot().ToFullString());
Assert.Equal(
parsedTree.GetCompilationUnitRoot().EndOfFileToken.FullSpan,
changedTree.GetCompilationUnitRoot().EndOfFileToken.FullSpan);
}
[Fact, WorkItem(531614, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/531614")]
public void IncrementalParseStopAtEscapeBackSlash()
{
var text1 = @"using System;
class Program
{
static void Main()
{
";
var text2 = @" Console.WriteLine(""\'\0\a\b\";
var comp = CSharpTestBase.CreateCompilation(SyntaxFactory.ParseSyntaxTree(String.Empty));
var oldTree = comp.SyntaxTrees.First();
var oldIText = oldTree.GetText();
var span = new TextSpan(oldIText.Length, 0);
var change = new TextChange(span, text1);
var newIText = oldIText.WithChanges(change);
var newTree = oldTree.WithChangedText(newIText);
var fullTree = SyntaxFactory.ParseSyntaxTree(newIText.ToString(), options: newTree.Options);
var fullText = fullTree.GetCompilationUnitRoot().ToFullString();
var incText = newTree.GetCompilationUnitRoot().ToFullString();
Assert.Equal(fullText.Length, incText.Length);
Assert.Equal(fullText, incText);
//
oldTree = newTree;
oldIText = oldTree.GetText();
span = new TextSpan(oldIText.Length, 0);
change = new TextChange(span, text2);
newIText = oldIText.WithChanges(change);
newTree = oldTree.WithChangedText(newIText);
fullTree = SyntaxFactory.ParseSyntaxTree(newIText.ToString(), options: newTree.Options);
fullText = fullTree.GetCompilationUnitRoot().ToFullString();
incText = newTree.GetCompilationUnitRoot().ToFullString();
Assert.Equal(fullText.Length, incText.Length);
Assert.Equal(fullText, incText);
}
[Fact, WorkItem(552741, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/552741")]
public void IncrementalParseTopDownCommentOutLines()
{
var text = @"// <Title> Query Expression syntax </Title>
// <Description>
// from, join, on, equals, into, let, orderby, ascending, descending, group, by
// @-with contextual keywords parseable as a type or identifier in a query expression
// Various combinations
// </Description>
// <RelatedBugs></RelatedBugs>
//<Expects status=success></Expects>
// <Code>
using System;
using System.Linq;
public class from { }
public class join { }
public class on { }
public class equals { }
public class into { }
public class let { }
public class orderby : let { }
public class ascending : orderby, descending { }
public interface descending { }
public class group { }
public class by { }
public class QueryExpressionTest
{
public static int Main()
{
var array02a = new[] { new join(), new join(), new join() } as object[];
var array02b = new[] { new join(), new join(), new join() } as object[];
var query02 = from i in array02a join j in array02b on (@from)i equals (@from)j select new { i, j };
var array03a = new[] { new on(), new on(), new on() } as object[];
var array03b = new[] { new on(), new on(), new on() } as object[];
var query03 = from @on i in array03a join j in array03b on i equals (@on)j select new { i, j };
var array04a = new[] { new equals(), new equals(), new equals() } as object[];
return 0;
}
}
";
var currTree = SyntaxFactory.ParseSyntaxTree(text);
var currIText = currTree.GetText();
var items = text.Split('\n');
int currLen = 0;
foreach (var item in items)
{
var span = new TextSpan(currLen, 0);
var change = new TextChange(span, "// ");
currLen += item.Length + 3;
currIText = currIText.WithChanges(change);
currTree = currTree.WithChangedText(currIText);
var fullTree = SyntaxFactory.ParseSyntaxTree(currIText.ToString());
int incCount = currTree.GetCompilationUnitRoot().ChildNodesAndTokens().Count;
int fullCount = fullTree.GetCompilationUnitRoot().ChildNodesAndTokens().Count;
WalkTreeAndVerify(currTree.GetCompilationUnitRoot(), fullTree.GetCompilationUnitRoot());
}
}
[Fact, WorkItem(552741, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/552741")]
public void IncrementalParseStatementAfterQuery()
{
var text = @"
using System.Linq;
class equals
{
static void Main(string[] args)
{
equals[] a;
var q = from x in args select x;
a = new[] { new equals() };
}
}
";
var currTree = SyntaxFactory.ParseSyntaxTree(text);
var currIText = currTree.GetText();
// Insert "// " before the "x" in "select x"; the next statement becomes part of the query.
var span = new TextSpan(text.LastIndexOf('x'), 0);
var change = new TextChange(span, "// ");
currIText = currIText.WithChanges(change);
currTree = currTree.WithChangedText(currIText);
var fullTree = SyntaxFactory.ParseSyntaxTree(currIText.ToString());
WalkTreeAndVerify(currTree.GetCompilationUnitRoot(), fullTree.GetCompilationUnitRoot());
}
[Fact, WorkItem(529260, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/529260")]
public void DoNotReuseAnnotatedNodes()
{
var text = @"
class C { }
class D { }
";
Func<SyntaxTree, GreenNode> extractGreenClassC = tree =>
tree.GetCompilationUnitRoot().Members.First().Green;
// Check reuse after a trivial change in an unannotated tree.
{
var oldTree = SyntaxFactory.ParseSyntaxTree(text);
var newTree = oldTree.WithInsertAt(text.Length, " ");
// Class declaration is reused.
Assert.Same(extractGreenClassC(oldTree), extractGreenClassC(newTree));
}
// Check reuse after a trivial change in an annotated tree.
{
var tempTree = SyntaxFactory.ParseSyntaxTree(text);
var tempRoot = tempTree.GetRoot();
var tempToken = tempRoot.DescendantTokens().First(t => t.Kind() == SyntaxKind.IdentifierToken);
var oldRoot = tempRoot.ReplaceToken(tempToken, tempToken.WithAdditionalAnnotations(new SyntaxAnnotation()));
Assert.True(oldRoot.ContainsAnnotations, "Should contain annotations.");
Assert.Equal(text, oldRoot.ToFullString());
var oldTree = SyntaxFactory.SyntaxTree(oldRoot, options: tempTree.Options, path: tempTree.FilePath);
var newTree = oldTree.WithInsertAt(text.Length, " ");
var oldClassC = extractGreenClassC(oldTree);
var newClassC = extractGreenClassC(newTree);
Assert.True(oldClassC.ContainsAnnotations, "Should contain annotations");
Assert.False(newClassC.ContainsAnnotations, "Annotations should have been removed.");
// Class declaration is not reused...
Assert.NotSame(oldClassC, newClassC);
// ...even though the text is the same.
Assert.Equal(oldClassC.ToFullString(), newClassC.ToFullString());
var oldToken = ((Syntax.InternalSyntax.ClassDeclarationSyntax)oldClassC).Identifier;
var newToken = ((Syntax.InternalSyntax.ClassDeclarationSyntax)newClassC).Identifier;
Assert.True(oldToken.ContainsAnnotations, "Should contain annotations");
Assert.False(newToken.ContainsAnnotations, "Annotations should have been removed.");
// Token is not reused...
Assert.NotSame(oldToken, newToken);
// ...even though the text is the same.
Assert.Equal(oldToken.ToFullString(), newToken.ToFullString());
}
}
[Fact]
[WorkItem(658496, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/658496")]
public void DontReuseLambdaParameterAsMethodParameter()
{
var items = new string[]
{
"a b.c*/ d => {e(f =>",
"/*",
};
var oldText = SourceText.From(items[0]);
var oldTree = SyntaxFactory.ParseSyntaxTree(oldText); // f is a simple lambda parameter
var change = new TextChange(new TextSpan(0, 0), items[1]); // Prepend
var newText = oldText.WithChanges(change); // f is a method decl parameter
var incrTree = oldTree.WithChangedText(newText);
var fullTree = SyntaxFactory.ParseSyntaxTree(newText);
Assert.Equal(
fullTree.GetDiagnostics().Select(d => d.ToString()),
incrTree.GetDiagnostics().Select(d => d.ToString()));
WalkTreeAndVerify(incrTree.GetRoot(), fullTree.GetRoot());
}
[Fact]
public void TestRescanInterpolatedString()
{
var interfaceKeyword = SyntaxFactory.ParseToken("interface"); // prime the memoizer
var text = @"class goo { public void m() { string s = $""{1} world"" ; } }";
var oldTree = this.Parse6(text);
var newTree = oldTree.WithReplaceFirst(@"world"" ", @"world"" ");
Assert.Equal(0, oldTree.GetCompilationUnitRoot().Errors().Length);
Assert.Equal(0, newTree.GetCompilationUnitRoot().Errors().Length);
}
[Fact]
public void Goo()
{
var oldText = SourceText.From(@"
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
class Program
{
static void Main(string[] args)
{
}
protected abstract int Stuff { get; }
}
class G: Program
{
protected override int Stuff
{
get
{
throw new NotImplementedException();
}
}
}");
var newText = SourceText.From(@"
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
class Program
{
static void Main(string[] args)
{
}
protected abstract int Stuff { get; }
}
class G: Program
{
protected override int Stuff =>
{
get
{
throw new NotImplementedException();
}
}
}
");
var oldTree = SyntaxFactory.ParseSyntaxTree(oldText);
var newTree = oldTree.WithChangedText(newText);
WalkTreeAndVerify(newTree.GetCompilationUnitRoot(), SyntaxFactory.ParseSyntaxTree(newText).GetCompilationUnitRoot());
}
[WorkItem(23272, "https://github.com/dotnet/roslyn/issues/23272")]
[Fact]
public void AddAccessibilityToNullableArray()
{
var source =
@"class A { }
class B
{
A[]? F;
}";
var tree = SyntaxFactory.ParseSyntaxTree(source);
var text = tree.GetText();
var span = new TextSpan(source.IndexOf(" A[]?"), 0);
var change = new TextChange(span, "p");
text = text.WithChanges(change);
tree = tree.WithChangedText(text);
var fullTree = SyntaxFactory.ParseSyntaxTree(text.ToString());
WalkTreeAndVerify(tree.GetCompilationUnitRoot(), fullTree.GetCompilationUnitRoot());
}
[Fact]
[WorkItem(37663, "https://github.com/dotnet/roslyn/issues/37663")]
public void AssemblyAttributeBeforeNamespace()
{
var src = @"
using System;
using System.Linq;
[assembly:]
namespace N
{ }";
var tree = SyntaxFactory.ParseSyntaxTree(src);
var text = tree.GetText();
var span = new TextSpan(src.IndexOf(":"), 1);
var change = new TextChange(span, "");
text = text.WithChanges(change);
tree = tree.WithChangedText(text);
var fullTree = SyntaxFactory.ParseSyntaxTree(text.ToString());
WalkTreeAndVerify(tree.GetCompilationUnitRoot(), fullTree.GetCompilationUnitRoot());
}
[WorkItem(37665, "https://github.com/dotnet/roslyn/issues/37665")]
[Fact]
public void AddBracketInUsingDirective()
{
var source =
@"using System;
namespace NS
{
class A { }
}
";
var tree = SyntaxFactory.ParseSyntaxTree(source);
var text = tree.GetText();
var span = new TextSpan(source.IndexOf(";"), 0);
var change = new TextChange(span, "[");
text = text.WithChanges(change);
tree = tree.WithChangedText(text);
var fullTree = SyntaxFactory.ParseSyntaxTree(text.ToString());
WalkTreeAndVerify(tree.GetCompilationUnitRoot(), fullTree.GetCompilationUnitRoot());
}
[WorkItem(37665, "https://github.com/dotnet/roslyn/issues/37665")]
[Fact]
public void AddAttributeAfterUsingDirective()
{
var source =
@"using System;
namespace NS
{
class A { }
}
";
var tree = SyntaxFactory.ParseSyntaxTree(source);
var text = tree.GetText();
var span = new TextSpan(source.IndexOf(";") + 1, 0);
var change = new TextChange(span, "[Obsolete]");
text = text.WithChanges(change);
tree = tree.WithChangedText(text);
var fullTree = SyntaxFactory.ParseSyntaxTree(text.ToString());
WalkTreeAndVerify(tree.GetCompilationUnitRoot(), fullTree.GetCompilationUnitRoot());
}
[WorkItem(37665, "https://github.com/dotnet/roslyn/issues/37665")]
[Fact]
public void AddTrailingModifierInUsingDirective()
{
var source =
@"using System;
namespace NS
{
class A { }
}
";
var tree = SyntaxFactory.ParseSyntaxTree(source);
var text = tree.GetText();
var span = new TextSpan(source.IndexOf(";") + 1, 0);
var change = new TextChange(span, "public");
text = text.WithChanges(change);
tree = tree.WithChangedText(text);
var fullTree = SyntaxFactory.ParseSyntaxTree(text.ToString());
WalkTreeAndVerify(tree.GetCompilationUnitRoot(), fullTree.GetCompilationUnitRoot());
}
[WorkItem(37665, "https://github.com/dotnet/roslyn/issues/37665")]
[Fact]
public void AddTrailingModifierInUsingDirective_2()
{
var source =
@"using System;publi
namespace NS
{
class A { }
}
";
var tree = SyntaxFactory.ParseSyntaxTree(source);
var text = tree.GetText();
var substring = "publi";
var span = new TextSpan(source.IndexOf(substring) + substring.Length, 0);
var change = new TextChange(span, "c");
text = text.WithChanges(change);
tree = tree.WithChangedText(text);
var fullTree = SyntaxFactory.ParseSyntaxTree(text.ToString());
WalkTreeAndVerify(tree.GetCompilationUnitRoot(), fullTree.GetCompilationUnitRoot());
}
[Fact]
public void Statement_EditAttributeList_01()
{
var source = @"
class C
{
void M()
{
[Attr]
void local1() { };
}
}
";
var tree = SyntaxFactory.ParseSyntaxTree(source);
var text = tree.GetText();
var substring = "Attr";
var span = new TextSpan(source.IndexOf(substring) + substring.Length, 0);
var change = new TextChange(span, "1, Attr2");
text = text.WithChanges(change);
tree = tree.WithChangedText(text);
var fullTree = SyntaxFactory.ParseSyntaxTree(text.ToString());
WalkTreeAndVerify(tree.GetCompilationUnitRoot(), fullTree.GetCompilationUnitRoot());
}
[Fact]
public void Statement_EditAttributeList_02()
{
var source = @"
class C
{
void M()
{
[Attr1]
Method1();
}
}
";
var tree = SyntaxFactory.ParseSyntaxTree(source);
var text = tree.GetText();
var substring = @"Attr1";
var span = new TextSpan(source.IndexOf(substring) + substring.Length, 0);
var change = new TextChange(span, ", Attr2");
text = text.WithChanges(change);
tree = tree.WithChangedText(text);
var fullTree = SyntaxFactory.ParseSyntaxTree(text.ToString());
WalkTreeAndVerify(tree.GetCompilationUnitRoot(), fullTree.GetCompilationUnitRoot());
}
[Fact]
public void Statement_AddAttributeList()
{
var source = @"
class C
{
void M()
{
[Attr1]
void local1() { };
}
}
";
var tree = SyntaxFactory.ParseSyntaxTree(source);
var text = tree.GetText();
var substring = @"[Attr1]";
var span = new TextSpan(source.IndexOf(substring) + substring.Length, 0);
var change = new TextChange(span, " [Attr2]");
text = text.WithChanges(change);
tree = tree.WithChangedText(text);
var fullTree = SyntaxFactory.ParseSyntaxTree(text.ToString());
WalkTreeAndVerify(tree.GetCompilationUnitRoot(), fullTree.GetCompilationUnitRoot());
}
[Fact]
public void EditStatementWithAttributes_01()
{
var source = @"
class C
{
void M()
{
[Attr1]
void local1() { Method(); };
}
}
";
var tree = SyntaxFactory.ParseSyntaxTree(source);
var text = tree.GetText();
var substring = @"Method(";
var span = new TextSpan(source.IndexOf(substring) + substring.Length, 0);
var change = new TextChange(span, "Arg");
text = text.WithChanges(change);
tree = tree.WithChangedText(text);
var fullTree = SyntaxFactory.ParseSyntaxTree(text.ToString());
WalkTreeAndVerify(tree.GetCompilationUnitRoot(), fullTree.GetCompilationUnitRoot());
}
[Fact]
public void EditStatementWithAttributes_02()
{
var source = @"
class C
{
void M()
{
[Attr1]
Method1();
}
}
";
var tree = SyntaxFactory.ParseSyntaxTree(source);
var text = tree.GetText();
var substring = @"Method";
var span = new TextSpan(source.IndexOf(substring) + substring.Length, 1);
var change = new TextChange(span, "2");
text = text.WithChanges(change);
tree = tree.WithChangedText(text);
var fullTree = SyntaxFactory.ParseSyntaxTree(text.ToString());
WalkTreeAndVerify(tree.GetCompilationUnitRoot(), fullTree.GetCompilationUnitRoot());
}
[Theory]
[InlineData("[Attr] () => { }")]
[InlineData("[Attr] x => x")]
[InlineData("([Attr] x) => x")]
[InlineData("([Attr] int x) => x")]
public void Lambda_EditAttributeList(string lambdaExpression)
{
var source =
$@"class Program
{{
static void Main()
{{
F({lambdaExpression});
}}
}}";
var tree = SyntaxFactory.ParseSyntaxTree(source);
var text = tree.GetText();
var substring = "Attr";
var span = new TextSpan(source.IndexOf(substring) + substring.Length, 0);
var change = new TextChange(span, "1, Attr2");
text = text.WithChanges(change);
tree = tree.WithChangedText(text);
var fullTree = SyntaxFactory.ParseSyntaxTree(text.ToString());
WalkTreeAndVerify(tree.GetCompilationUnitRoot(), fullTree.GetCompilationUnitRoot());
}
[Theory]
[InlineData("() => { }", "() => { }")]
[InlineData("x => x", "x => x")]
[InlineData("(x) => x", "x) => x")]
[InlineData("(int x) => x", "int x) => x")]
public void Lambda_AddFirstAttributeList(string lambdaExpression, string substring)
{
var source =
$@"class Program
{{
static void Main()
{{
F({lambdaExpression});
}}
}}";
var tree = SyntaxFactory.ParseSyntaxTree(source);
var text = tree.GetText();
var span = new TextSpan(source.IndexOf(substring), 0);
var change = new TextChange(span, "[Attr]");
text = text.WithChanges(change);
tree = tree.WithChangedText(text);
var fullTree = SyntaxFactory.ParseSyntaxTree(text.ToString());
WalkTreeAndVerify(tree.GetCompilationUnitRoot(), fullTree.GetCompilationUnitRoot());
}
[Theory]
[InlineData("[Attr1] () => { }")]
[InlineData("[Attr1] x => x")]
[InlineData("([Attr1] x) => x")]
[InlineData("([Attr1] int x) => x")]
public void Lambda_AddSecondAttributeList(string lambdaExpression)
{
var source =
$@"class Program
{{
static void Main()
{{
F({lambdaExpression});
}}
}}";
var tree = SyntaxFactory.ParseSyntaxTree(source);
var text = tree.GetText();
var substring = @"[Attr1]";
var span = new TextSpan(source.IndexOf(substring) + substring.Length, 0);
var change = new TextChange(span, " [Attr2]");
text = text.WithChanges(change);
tree = tree.WithChangedText(text);
var fullTree = SyntaxFactory.ParseSyntaxTree(text.ToString());
WalkTreeAndVerify(tree.GetCompilationUnitRoot(), fullTree.GetCompilationUnitRoot());
}
[Theory]
[InlineData("[Attr] () => { }")]
[InlineData("[Attr] x => x")]
[InlineData("([Attr] x) => x")]
[InlineData("([Attr] int x) => x")]
public void Lambda_RemoveAttributeList(string lambdaExpression)
{
var source =
$@"class Program
{{
static void Main()
{{
F({lambdaExpression});
}}
}}";
var tree = SyntaxFactory.ParseSyntaxTree(source);
var text = tree.GetText();
var substring = "[Attr] ";
var span = new TextSpan(source.IndexOf(substring) + substring.Length, 0);
var change = new TextChange(span, "");
text = text.WithChanges(change);
tree = tree.WithChangedText(text);
var fullTree = SyntaxFactory.ParseSyntaxTree(text.ToString());
WalkTreeAndVerify(tree.GetCompilationUnitRoot(), fullTree.GetCompilationUnitRoot());
}
[Fact]
public void EditGlobalStatementWithAttributes_01()
{
var source = @"
[Attr]
x.y();
";
var tree = SyntaxFactory.ParseSyntaxTree(source);
var text = tree.GetText();
var substring = @"x.y";
var span = new TextSpan(source.IndexOf(substring) + substring.Length, 0);
var change = new TextChange(span, ".z");
text = text.WithChanges(change);
tree = tree.WithChangedText(text);
var fullTree = SyntaxFactory.ParseSyntaxTree(text.ToString());
WalkTreeAndVerify(tree.GetCompilationUnitRoot(), fullTree.GetCompilationUnitRoot());
}
[Fact]
public void EditGlobalStatementWithAttributes_02()
{
var source = @"
[Attr]
if (b) { }
";
var tree = SyntaxFactory.ParseSyntaxTree(source);
var text = tree.GetText();
var substring = @"if (b) { }";
var span = new TextSpan(source.IndexOf(substring) + substring.Length, 0);
var change = new TextChange(span, " if (c) { }");
text = text.WithChanges(change);
tree = tree.WithChangedText(text);
var fullTree = SyntaxFactory.ParseSyntaxTree(text.ToString());
WalkTreeAndVerify(tree.GetCompilationUnitRoot(), fullTree.GetCompilationUnitRoot());
}
[Fact]
public void EditGlobalStatementWithAttributes_03()
{
var source = @"
[Attr]
if (b) { }
";
var tree = SyntaxFactory.ParseSyntaxTree(source);
var text = tree.GetText();
var substring = @"if (b) { }";
var span = new TextSpan(source.IndexOf(substring) + substring.Length, 0);
var change = new TextChange(span, " else (c) { }");
text = text.WithChanges(change);
tree = tree.WithChangedText(text);
var fullTree = SyntaxFactory.ParseSyntaxTree(text.ToString());
WalkTreeAndVerify(tree.GetCompilationUnitRoot(), fullTree.GetCompilationUnitRoot());
}
[Fact]
public void EditNestedStatementWithAttributes_01()
{
var source = "{ [Goo]Goo(); [Goo]Goo(); [Goo]Goo(); }";
var tree = SyntaxFactory.ParseSyntaxTree(source);
var text = tree.GetText();
var span = new TextSpan(start: 0, length: 1); // delete first character
var change = new TextChange(span, "");
text = text.WithChanges(change);
tree = tree.WithChangedText(text);
var fullTree = SyntaxFactory.ParseSyntaxTree(text.ToString());
WalkTreeAndVerify(tree.GetCompilationUnitRoot(), fullTree.GetCompilationUnitRoot());
}
[Fact]
public void EditNestedStatementWithAttributes_02()
{
var source = "{ [Goo]Goo(); [Goo]Goo(); [Goo]Goo(); }";
var tree = SyntaxFactory.ParseSyntaxTree(source);
var text = tree.GetText();
var span = new TextSpan(start: 0, length: 0);
var change = new TextChange(span, "{ ");
text = text.WithChanges(change);
tree = tree.WithChangedText(text);
var fullTree = SyntaxFactory.ParseSyntaxTree(text.ToString());
WalkTreeAndVerify(tree.GetCompilationUnitRoot(), fullTree.GetCompilationUnitRoot());
}
[Fact]
public void EditNestedStatementWithAttributes_03()
{
var source = "class C { void M() { Goo[Goo] [Goo]if(Goo) { } } }";
var tree = SyntaxFactory.ParseSyntaxTree(source);
var text = tree.GetText();
var substring = "Goo[Goo]";
var span = new TextSpan(start: source.IndexOf(substring), length: 3); // Goo[Goo] -> [Goo]
var change = new TextChange(span, "");
text = text.WithChanges(change);
tree = tree.WithChangedText(text);
var fullTree = SyntaxFactory.ParseSyntaxTree(text.ToString());
WalkTreeAndVerify(tree.GetCompilationUnitRoot(), fullTree.GetCompilationUnitRoot());
}
[Fact]
[WorkItem(62126, "https://github.com/dotnet/roslyn/issues/62126")]
public void StartAttributeOnABlock()
{
var source = @"
using System;
switch (getVirtualKey())
{
case VirtualKey.Up or VirtualKey.Down or VirtualKey.Left or VirtualKey.Right:
{
}
}
// A local function to simulate get operation.
static VirtualKey getVirtualKey() => VirtualKey.Up;
enum VirtualKey
{
Up,
Down,
Left,
Right
}
";
var tree = SyntaxFactory.ParseSyntaxTree(source);
var text = tree.GetText();
var span = new TextSpan(start: source.IndexOf(":") + 1, length: 0);
var change = new TextChange(span, "[");
text = text.WithChanges(change);
tree = tree.WithChangedText(text);
var fullTree = SyntaxFactory.ParseSyntaxTree(text.ToString());
WalkTreeAndVerify(tree.GetCompilationUnitRoot(), fullTree.GetCompilationUnitRoot());
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/76439")]
public void InKeywordInsideAForBlock()
{
var source = """
void Main()
{
for (int i = 0; i < n; i++)
{
}
}
""";
var tree = SyntaxFactory.ParseSyntaxTree(source);
var text = tree.GetText();
// Update all the 'i's in the for-loop to be 'in' instead.
var position1 = source.IndexOf("i =") + 1;
var position2 = source.IndexOf("i <") + 1;
var position3 = source.IndexOf("i++") + 1;
text = text.WithChanges(
new TextChange(new TextSpan(position1, 0), "n"),
new TextChange(new TextSpan(position2, 0), "n"),
new TextChange(new TextSpan(position3, 0), "n"));
Assert.Equal("""
void Main()
{
for (int in = 0; in < n; in++)
{
}
}
""", text.ToString());
tree = tree.WithChangedText(text);
var fullTree = SyntaxFactory.ParseSyntaxTree(text.ToString());
WalkTreeAndVerify(tree.GetCompilationUnitRoot(), fullTree.GetCompilationUnitRoot());
}
#endregion
#region Helper functions
private void WalkTreeAndVerify(SyntaxNodeOrToken incNode, SyntaxNodeOrToken fullNode)
{
var incChildren = incNode.ChildNodesAndTokens();
var fullChildren = fullNode.ChildNodesAndTokens();
Assert.Equal(incChildren.Count, fullChildren.Count);
for (int i = 0; i < incChildren.Count; i++)
{
var incChild = incChildren[i];
var fullChild = fullChildren[i];
WalkTreeAndVerify(incChild, fullChild);
}
}
private static void CommentOutText(SourceText oldText, int locationOfChange, int widthOfChange, out SyntaxTree incrementalTree, out SyntaxTree parsedTree)
{
var newText = oldText.WithChanges(
new TextChange[] {
new TextChange(new TextSpan(locationOfChange, 0), "/*"),
new TextChange(new TextSpan(locationOfChange + widthOfChange, 0), "*/")
});
var tree = SyntaxFactory.ParseSyntaxTree(oldText);
incrementalTree = tree.WithChangedText(newText);
parsedTree = SyntaxFactory.ParseSyntaxTree(newText);
}
private static void RemoveText(SourceText oldText, int locationOfChange, int widthOfChange, out SyntaxTree incrementalTree, out SyntaxTree parsedTree)
{
var newText = oldText.WithChanges(new TextChange(new TextSpan(locationOfChange, widthOfChange), ""));
var tree = SyntaxFactory.ParseSyntaxTree(oldText);
incrementalTree = tree.WithChangedText(newText);
parsedTree = SyntaxFactory.ParseSyntaxTree(newText);
}
private void CompareIncToFullParseErrors(SyntaxTree incrementalTree, SyntaxTree parsedTree)
{
var pd = parsedTree.GetDiagnostics();
var id = incrementalTree.GetDiagnostics();
Assert.Equal(pd.Count(), id.Count());
for (int i = 0; i < id.Count(); i++)
{
Assert.Equal(pd.ElementAt(i).Inspect(), id.ElementAt(i).Inspect());
}
ParentChecker.CheckParents(parsedTree.GetCompilationUnitRoot(), parsedTree);
ParentChecker.CheckParents(incrementalTree.GetCompilationUnitRoot(), incrementalTree);
}
private static void CharByCharIncrementalParse(SourceText oldText, char newChar, out SyntaxTree incrementalTree, out SyntaxTree parsedTree)
{
var startTree = SyntaxFactory.ParseSyntaxTree(oldText);
// first make certain this text round trips
Assert.Equal(oldText.ToString(), startTree.GetCompilationUnitRoot().ToFullString());
var newText = oldText.WithChanges(new TextChange(new TextSpan(oldText.Length, 0), newChar.ToString()));
incrementalTree = startTree.WithChangedText(newText);
parsedTree = SyntaxFactory.ParseSyntaxTree(newText);
}
private static void TokenByTokenBottomUp(SourceText oldText, string token, out SyntaxTree incrementalTree, out SyntaxTree parsedTree)
{
var startTree = SyntaxFactory.ParseSyntaxTree(oldText);
SourceText newText = SourceText.From(token + oldText.ToString());
incrementalTree = startTree.WithInsertAt(0, token);
parsedTree = SyntaxFactory.ParseSyntaxTree(newText);
}
private static void CompareTreeEquivalence(SyntaxNodeOrToken parsedTreeNode, SyntaxNodeOrToken incrementalTreeNode)
{
Assert.Equal(parsedTreeNode.Kind(), incrementalTreeNode.Kind());
Assert.Equal(parsedTreeNode.ChildNodesAndTokens().Count, incrementalTreeNode.ChildNodesAndTokens().Count);
for (int i = 0; i < parsedTreeNode.ChildNodesAndTokens().Count; i++)
{
CompareTreeEquivalence(parsedTreeNode.ChildNodesAndTokens()[i], incrementalTreeNode.ChildNodesAndTokens()[i]);
}
}
#endregion
}
}
|