diff --git a/src/EditorFeatures/CSharpTest/MoveDeclarationNearReference/MoveDeclarationNearReferenceTests.cs b/src/EditorFeatures/CSharpTest/MoveDeclarationNearReference/MoveDeclarationNearReferenceTests.cs index 3559e462e54e014866159915e11318832930bc58..14df4758eebf6b3c1001908e51b7b6250a4f0ad4 100644 --- a/src/EditorFeatures/CSharpTest/MoveDeclarationNearReference/MoveDeclarationNearReferenceTests.cs +++ b/src/EditorFeatures/CSharpTest/MoveDeclarationNearReference/MoveDeclarationNearReferenceTests.cs @@ -43,6 +43,27 @@ void M() }"); } + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMoveDeclarationNearReference)] + public async Task TestMove1_TopLevelStatement() + { + await TestAsync( +@" +int [||]x; +{ + Console.WriteLine(x); +} +", +@" + +{ + + int x; + Console.WriteLine(x); +} +", + Options.Regular); + } + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMoveDeclarationNearReference)] public async Task TestMove2() { @@ -67,6 +88,25 @@ void M() }"); } + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMoveDeclarationNearReference)] + public async Task TestMove2_TopLevelStatement() + { + await TestAsync( +@" +int [||]x; +Console.WriteLine(); +Console.WriteLine(x); +", +@" + +Console.WriteLine(); + +int x; +Console.WriteLine(x); +", + Options.Regular); + } + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMoveDeclarationNearReference)] public async Task TestMove3() { @@ -101,6 +141,37 @@ void M() }"); } + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMoveDeclarationNearReference)] + public async Task TestMove3_TopLevelStatement() + { + await TestAsync( +@" +int [||]x; +Console.WriteLine(); +{ + Console.WriteLine(x); +} + +{ + Console.WriteLine(x); +} +", +@" + +Console.WriteLine(); + +int x; +{ +Console.WriteLine(x); +} + +{ + Console.WriteLine(x); +} +", + Options.Regular); + } + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMoveDeclarationNearReference)] public async Task TestMove4() { @@ -127,6 +198,29 @@ void M() }"); } + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMoveDeclarationNearReference)] + public async Task TestMove4_TopLevelStatement() + { + await TestAsync( +@" +int [||]x; +Console.WriteLine(); +{ + Console.WriteLine(x); +} +", +@" + +Console.WriteLine(); +{ + + int x; + Console.WriteLine(x); +} +", + Options.Regular); + } + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMoveDeclarationNearReference)] public async Task TestAssign1() { @@ -223,6 +317,16 @@ void M() }"); } + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMoveDeclarationNearReference)] + public async Task TestMissing1_TopLevelStatement() + { + await TestMissingInRegularAndScriptAsync( +@" +int [||]x; +Console.WriteLine(x); +"); + } + [WorkItem(538424, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/538424")] [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMoveDeclarationNearReference)] public async Task TestMissingWhenReferencedInDeclaration() @@ -255,6 +359,17 @@ static void Main() }"); } + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMoveDeclarationNearReference)] + public async Task TestMissingWhenInDeclarationGroup_TopLevelStatement() + { + await TestMissingInRegularAndScriptAsync( +@" +int [||]i = 5; +int j = 10; +Console.WriteLine(i); +"); + } + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMoveDeclarationNearReference)] [WorkItem(541475, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/541475")] public async Task Regression8190() @@ -293,6 +408,23 @@ static void Main(string[] args) }"); } + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMoveDeclarationNearReference)] + public async Task TestFormatting_TopLevelStatement() + { + await TestAsync( +@" +int [||]i = 5; Console.WriteLine(); +Console.Write(i); +", +@" + +Console.WriteLine(); + +int i = 5; Console.Write(i); +", + Options.Regular); + } + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMoveDeclarationNearReference)] public async Task TestMissingInHiddenBlock1() { @@ -330,6 +462,21 @@ void Main() }"); } + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMoveDeclarationNearReference)] + public async Task TestMissingInHiddenBlock2_TopLevelStatement() + { + await TestMissingInRegularAndScriptAsync( +@"#line default + +int [|x|] = 0; +Goo(); +#line hidden +Goo(); +#line default +Bar(x); +"); + } + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMoveDeclarationNearReference)] public async Task TestAvailableInNonHiddenBlock1() { @@ -394,6 +541,35 @@ void Main() }"); } + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMoveDeclarationNearReference)] + public async Task TestAvailableInNonHiddenBlock2_TopLevelStatement() + { + await TestAsync( +@"#line default + +int [||]x = 0; +Goo(); +#line hidden +Goo(); +#line default +Goo(); +Bar(x); +", +@"#line default + +Goo(); +#line hidden +Goo(); +#line default +Goo(); +#line default + +int x = 0; +Bar(x); +", + Options.Regular); + } + [WorkItem(545435, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545435")] [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMoveDeclarationNearReference)] public async Task TestWarnOnChangingScopes1() @@ -430,6 +606,34 @@ void Main() }"); } + [WorkItem(545435, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545435")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMoveDeclarationNearReference)] + public async Task TestWarnOnChangingScopes1_TopLevelStatement() + { + await TestAsync( +@"using System.Linq; + +var [||]@lock = new object(); +new[] { 1 }.AsParallel().ForAll((i) => { + lock (@lock) + { + } +}); +", +@"using System.Linq; + +new[] { 1 }.AsParallel().ForAll((i) => +{ + + {|Warning:var @lock = new object();|} + lock (@lock) + { + } +}); +", + Options.Regular); + } + [WorkItem(545435, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545435")] [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMoveDeclarationNearReference)] public async Task TestWarnOnChangingScopes2() @@ -467,6 +671,101 @@ void Main() }"); } + [WorkItem(545435, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545435")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMoveDeclarationNearReference)] + public async Task TestWarnOnChangingScopes2_TopLevelStatement() + { + await TestAsync( +@"using System; +using System.Linq; + +var [||]i = 0; +foreach (var v in new[] { 1 }) +{ + Console.Write(i); + i++; +} +", +@"using System; +using System.Linq; + +foreach (var v in new[] { 1 }) +{ + + {|Warning:var i = 0;|} + Console.Write(i); + i++; +} +", + Options.Regular); + } + + [WorkItem(44664, "https://github.com/dotnet/roslyn/pull/44664")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMoveDeclarationNearReference)] + public async Task TestWarnOnChangingScopes3() + { + await TestInRegularAndScriptAsync( +@"using System; +using System.Linq; + +class Program +{ + void Main() + { + var [||]i = 0; + void LocalFunction() + { + Console.Write(i); + i++; + } + } +}", +@"using System; +using System.Linq; + +class Program +{ + void Main() + { + void LocalFunction() + { + {|Warning:var i = 0;|} + Console.Write(i); + i++; + } + } +}"); + } + + [WorkItem(44664, "https://github.com/dotnet/roslyn/pull/44664")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMoveDeclarationNearReference)] + public async Task TestWarnOnChangingScopes3_TopLevelStatement() + { + await TestAsync( +@"using System; +using System.Linq; + +var [||]i = 0; +void LocalFunction() +{ + Console.Write(i); + i++; +} +", +@"using System; +using System.Linq; + +void LocalFunction() +{ + + {|Warning:var i = 0;|} + Console.Write(i); + i++; +} +", + Options.Regular); + } + [WorkItem(545840, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545840")] [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMoveDeclarationNearReference)] public async Task InsertCastIfNecessary1() @@ -1163,6 +1462,38 @@ static void Main(string[] args) }"); } + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMoveDeclarationNearReference)] + public async Task TestMergeComments07_TopLevelStatement() + { + await TestAsync( +@" +if (true) +{ +} + +// leading trivia +int [||]i = 5; +Console.WriteLine(); + +// Existing trivia +i = 0; +Console.Write(i); +", +@" +if (true) +{ +} + +Console.WriteLine(); + +// leading trivia +// Existing trivia +int i = 0; +Console.Write(i); +", + Options.Regular); + } + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMoveDeclarationNearReference)] public async Task TestMergeComments08() { @@ -1206,6 +1537,42 @@ static void Main(string[] args) }"); } + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMoveDeclarationNearReference)] + public async Task TestMergeComments08_TopLevelStatement() + { + await TestAsync( +@" +if (true) +{ +} + +// leading trivia +int [||]i = 5; +Console.WriteLine(); + +{ + // Existing trivia + i = 0; + Console.Write(i); +} +", +@" +if (true) +{ +} + +Console.WriteLine(); + +{ + // leading trivia + // Existing trivia + int i = 0; + Console.Write(i); +} +", + Options.Regular); + } + [WorkItem(21907, "https://github.com/dotnet/roslyn/issues/21907")] [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMoveDeclarationNearReference)] public async Task TestMissingOnCrossFunction1() @@ -1400,6 +1767,30 @@ void M() }"); } + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMoveDeclarationNearReference)] + public async Task TestMoveIntoSwitchSection_TopLevelStatement() + { + await TestAsync( +@" +int [||]x; +switch (true) +{ + case true: + x = 0; + break; +} +", +@" +switch (true) +{ + case true: + int x = 0; + break; +} +", + Options.Regular); + } + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMoveDeclarationNearReference)] public async Task TestUsedInMultipleSwitchSections_MoveToSwitchStatement() { @@ -1440,6 +1831,41 @@ void M() }"); } + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMoveDeclarationNearReference)] + public async Task TestUsedInMultipleSwitchSections_TopLevelStatement_MoveToSwitchStatement() + { + await TestAsync( +@" +int [||]x; +System.Console.WriteLine(); +switch (true) +{ + case true: + x = 0; + break; + case false: + x = 0; + break; +} +", +@" + +System.Console.WriteLine(); + +int x; +switch (true) +{ + case true: + x = 0; + break; + case false: + x = 0; + break; +} +", + Options.Regular); + } + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMoveDeclarationNearReference)] public async Task TestUsedInMultipleSwitchSections_CannotMove() { @@ -1461,5 +1887,23 @@ void M() } }"); } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMoveDeclarationNearReference)] + public async Task TestUsedInMultipleSwitchSections_TopLevelStatement_CannotMove() + { + await TestMissingInRegularAndScriptAsync( +@" +int [||]x; +switch (true) +{ + case true: + x = 0; + break; + case false: + x = 0; + break; +} +"); + } } } diff --git a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs index 9be5d9440c3a379bbf980c4686a9630fb2b3967e..e6d874a604db3f814d2fb4418fd2904efac76b4d 100644 --- a/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs +++ b/src/Workspaces/CSharp/Portable/CodeGeneration/CSharpSyntaxGenerator.cs @@ -2792,6 +2792,13 @@ private IEnumerable SplitAndReplace(SyntaxNode multiPartDeclaration, public override SyntaxNode InsertNodesBefore(SyntaxNode root, SyntaxNode declaration, IEnumerable newDeclarations) { + if (declaration.Parent.IsKind(SyntaxKind.GlobalStatement)) + { + // Insert global statements before this global statement + declaration = declaration.Parent; + newDeclarations = newDeclarations.Select(declaration => declaration is StatementSyntax statement ? SyntaxFactory.GlobalStatement(statement) : declaration); + } + if (root.Span.Contains(declaration.Span)) { return this.Isolate(root.TrackNodes(declaration), r => this.InsertNodesBeforeInternal(r, r.GetCurrentNode(declaration), newDeclarations)); @@ -2824,6 +2831,13 @@ private SyntaxNode InsertNodesBeforeInternal(SyntaxNode root, SyntaxNode declara public override SyntaxNode InsertNodesAfter(SyntaxNode root, SyntaxNode declaration, IEnumerable newDeclarations) { + if (declaration.Parent.IsKind(SyntaxKind.GlobalStatement)) + { + // Insert global statements before this global statement + declaration = declaration.Parent; + newDeclarations = newDeclarations.Select(declaration => declaration is StatementSyntax statement ? SyntaxFactory.GlobalStatement(statement) : declaration); + } + if (root.Span.Contains(declaration.Span)) { return this.Isolate(root.TrackNodes(declaration), r => this.InsertNodesAfterInternal(r, r.GetCurrentNode(declaration), newDeclarations)); @@ -2884,6 +2898,12 @@ public override SyntaxNode RemoveNode(SyntaxNode root, SyntaxNode node) public override SyntaxNode RemoveNode(SyntaxNode root, SyntaxNode node, SyntaxRemoveOptions options) { + if (node.Parent.IsKind(SyntaxKind.GlobalStatement)) + { + // Remove the entire global statement as part of the edit + node = node.Parent; + } + if (root.Span.Contains(node.Span)) { // node exists within normal span of the root (not in trivia) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/MoveDeclarationNearReference/AbstractMoveDeclarationNearReferenceService.State.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/MoveDeclarationNearReference/AbstractMoveDeclarationNearReferenceService.State.cs index 415580f9c54df65088157274fc3c637baa49b72a..1e1074b029aa7563224be35930b9ad407cb60e80 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/MoveDeclarationNearReference/AbstractMoveDeclarationNearReferenceService.State.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/MoveDeclarationNearReference/AbstractMoveDeclarationNearReferenceService.State.cs @@ -70,7 +70,7 @@ private class State return false; } - OutermostBlock = DeclarationStatement.Parent; + OutermostBlock = syntaxFacts.GetStatementContainer(DeclarationStatement); if (!syntaxFacts.IsExecutableBlock(OutermostBlock)) { return false;