提交 f943b1ba 编写于 作者: A Abraham Hosch

Rewrite PopulateSwitch diagnostic and fixes. Fix bug in generator that causes...

Rewrite PopulateSwitch diagnostic and fixes.  Fix bug in generator that causes crashes when generating a default case for VB.NET switches.
上级 7eee68b6
......@@ -183,8 +183,8 @@
<Compile Include="CodeActions\Preview\PreviewTests.cs" />
<Compile Include="CodeActions\ReplaceMethodWithProperty\ReplaceMethodWithPropertyTests.cs" />
<Compile Include="Diagnostics\InvokeDelegateWithConditionalAccess\InvokeDelegateWithConditionalAccessTests.cs" />
<Compile Include="Diagnostics\PopulateSwitch\PopulateSwitchTests.cs" />
<Compile Include="Diagnostics\PopulateSwitch\PopulateSwitchTests_FixAllTests.cs" />
<Compile Include="PopulateSwitch\PopulateSwitchTests.cs" />
<Compile Include="PopulateSwitch\PopulateSwitchTests_FixAllTests.cs" />
<Compile Include="Diagnostics\Suppression\RemoveSuppressionTests.cs" />
<Compile Include="Diagnostics\Suppression\SuppressionTest_FixMultipleTests.cs" />
<Compile Include="Diagnostics\UseAutoProperty\UseAutoPropertyTests.cs" />
......
......@@ -14,7 +14,7 @@ public partial class PopulateSwitchTests : AbstractCSharpDiagnosticProviderBased
internal override Tuple<DiagnosticAnalyzer, CodeFixProvider> CreateDiagnosticProviderAndFixer(Workspace workspace)
{
return new Tuple<DiagnosticAnalyzer, CodeFixProvider>(
new CSharpPopulateSwitchDiagnosticAnalyzer(), new PopulateSwitchCodeFixProvider());
new CSharpPopulateSwitchDiagnosticAnalyzer(), new CSharpPopulateSwitchCodeFixProvider());
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsPopulateSwitch)]
......@@ -33,13 +33,13 @@ class MyClass
void Method()
{
var e = MyEnum.Fizz;
switch (e)
switch ([|e|])
{
[|case MyEnum.Fizz:
case MyEnum.Fizz:
case MyEnum.Buzz:
case MyEnum.FizzBuzz:
default:
break;|]
break;
}
}
}
......@@ -63,12 +63,12 @@ class MyClass
void Method()
{
var e = MyEnum.Fizz;
switch (e)
switch ([|e|])
{
[|case MyEnum.Fizz:
case MyEnum.Fizz:
case MyEnum.Buzz:
case MyEnum.FizzBuzz:
break;|]
break;
}
}
}
......@@ -117,11 +117,11 @@ class MyClass
void Method()
{
var e = MyEnum.Fizz;
switch (e)
switch ([|e|])
{
[|case MyEnum.Fizz:
case MyEnum.Fizz:
case MyEnum.Buzz:
break;|]
break;
}
}
}
......@@ -171,13 +171,13 @@ class MyClass
void Method()
{
var e = MyEnum.Fizz;
switch (e)
switch ([|e|])
{
[|case MyEnum.Fizz:
case MyEnum.Fizz:
case MyEnum.Buzz:
break;
default:
break;|]
break;
}
}
}
......@@ -227,11 +227,11 @@ class MyClass
void Method()
{
var e = MyEnum.Fizz;
switch (e)
switch ([|e|])
{
[|case MyEnum.Fizz:
case MyEnum.Fizz:
case MyEnum.Buzz:
break;|]
break;
}
}
}
......@@ -281,12 +281,12 @@ class MyClass
void Method()
{
var e = MyEnum.Fizz;
switch (e)
switch ([|e|])
{
[|case MyEnum.Fizz:
case MyEnum.Fizz:
case MyEnum.Buzz:
default:
break;|]
break;
}
}
}
......@@ -335,13 +335,13 @@ class MyClass
void Method()
{
var e = MyEnum.Fizz;
switch (e)
switch ([|e|])
{
[|default:
default:
break;
case MyEnum.Fizz:
case MyEnum.Buzz:
break;|]
break;
}
}
}
......@@ -391,9 +391,8 @@ class MyClass
void Method()
{
var e = MyEnum.Fizz;
switch (e)
switch ([|e|])
{
[||]
}
}
}
......@@ -441,9 +440,9 @@ class MyClass
void Method()
{
var e = Append;
switch (e)
switch ([|e|])
{
[|case CreateNew:
case CreateNew:
break;
case Create:
break;
......@@ -456,7 +455,7 @@ void Method()
case Append:
break;
default:
break;|]
break;
}
}
}
......@@ -477,9 +476,9 @@ class MyClass
void Method()
{
var e = Append;
switch (e)
switch ([|e|])
{
[|case CreateNew:
case CreateNew:
break;
case OpenOrCreate:
break;
......@@ -492,7 +491,7 @@ void Method()
case Create:
break;
default:
break;|]
break;
}
}
}
......@@ -513,9 +512,9 @@ class MyClass
void Method()
{
var e = Append;
switch (e)
switch ([|e|])
{
[|case CreateNew:
case CreateNew:
break;
case Create:
break;
......@@ -524,7 +523,7 @@ void Method()
case OpenOrCreate:
break;
default:
break;|]
break;
}
}
}
......@@ -549,9 +548,9 @@ void Method()
break;
case OpenOrCreate:
break;
case Append:
case System.IO.FileMode.Truncate:
break;
case Truncate:
case System.IO.FileMode.Append:
break;
default:
break;
......@@ -575,8 +574,8 @@ class MyClass
void Method()
{
var e = Append;
switch (e)
{[||]
switch ([|e|])
{
}
}
}
......@@ -593,17 +592,17 @@ void Method()
var e = Append;
switch (e)
{
case CreateNew:
case System.IO.FileMode.CreateNew:
break;
case Create:
case System.IO.FileMode.Create:
break;
case Open:
case System.IO.FileMode.Open:
break;
case OpenOrCreate:
case System.IO.FileMode.OpenOrCreate:
break;
case Truncate:
case System.IO.FileMode.Truncate:
break;
case Append:
case System.IO.FileMode.Append:
break;
default:
break;
......@@ -632,12 +631,12 @@ class MyClass
void Method()
{
var e = MyEnum.Fizz;
switch (e)
switch ([|e|])
{
[|case MyEnum.Fizz:
case MyEnum.Fizz:
case MyEnum.Buzz:
default:
break;|]
break;
}
}
}
......@@ -663,12 +662,12 @@ class MyClass
void Method()
{
var e = MyEnum.Fizz;
switch (e)
switch ([|e|])
{
[|case MyEnum.Fizz:
case MyEnum.Fizz:
case MyEnum.Buzz:
default:
break;|]
break;
}
}
}
......@@ -693,12 +692,12 @@ class MyClass
void Method()
{
var e = MyEnum.Fizz;
switch (e)
switch ([|e|])
{
[|case MyEnum.Fizz:
case MyEnum.Fizz:
case MyEnum.Buzz:
default:
break;|]
break;
}
}
}
......@@ -723,12 +722,12 @@ class MyClass
void Method()
{
var e = MyEnum.Fizz;
switch (e)
switch ([|e|])
{
[|case MyEnum.Fizz:
case MyEnum.Fizz:
case MyEnum.Buzz:
default:
break;|]
break;
}
}
}
......@@ -753,11 +752,11 @@ class MyClass
void Method()
{
var e = MyEnum.Fizz;
switch (e)
switch ([|e|])
{
[|case MyEnum.Fizz:
case MyEnum.Fizz:
case MyEnum.Buzz:
break;|]
break;
}
}
}
......@@ -809,11 +808,11 @@ enum MyEnum
void Method()
{
var e = MyEnum.Fizz;
switch (e)
switch ([|e|])
{
[|case MyEnum.Fizz:
case MyEnum.Fizz:
case MyEnum.Buzz:
break;|]
break;
}
}
}
......@@ -861,12 +860,12 @@ class MyClass
void Method()
{
var e = ""test"";
switch (e)
switch ([|e|])
{
[|case ""test1"":
case ""test1"":
case ""test1"":
default:
break;|]
break;
}
}
}
......
......@@ -26,12 +26,12 @@ class MyClass1
void Method()
{
var e = MyEnum.Fizz;
switch (e)
switch ({|FixAllInDocument:e|})
{
{|FixAllInDocument:case MyEnum.Fizz:
case MyEnum.Fizz:
case MyEnum.Buzz:
case MyEnum.FizzBuzz:
break;|}
break;
}
switch (e)
{
......@@ -190,12 +190,12 @@ class MyClass1
void Method()
{
var e = MyEnum.Fizz;
switch (e)
switch ({|FixAllInProject:e|})
{
{|FixAllInProject:case MyEnum.Fizz:
case MyEnum.Fizz:
case MyEnum.Buzz:
case MyEnum.FizzBuzz:
break;|}
break;
}
}
}
......@@ -340,12 +340,12 @@ class MyClass1
void Method()
{
var e = MyEnum1.Fizz;
switch (e)
switch ({|FixAllInSolution:e|})
{
{|FixAllInSolution:case MyEnum1.Fizz:
case MyEnum1.Fizz:
case MyEnum1.Buzz:
case MyEnum1.FizzBuzz:
break;|}
break;
}
}
}
......
......@@ -202,8 +202,8 @@
<Compile Include="Diagnostics\MakeMethodSynchronous\MakeMethodSynchronousTests.vb" />
<Compile Include="Diagnostics\MoveToTopOfFile\MoveToTopOfFileTests.vb" />
<Compile Include="Diagnostics\OverloadBase\OverloadBaseTests.vb" />
<Compile Include="Diagnostics\PopulateSwitch\PopulateSwitchTests.vb" />
<Compile Include="Diagnostics\PopulateSwitch\PopulateSwitchTests_FixAllTests.vb" />
<Compile Include="PopulateSwitch\PopulateSwitchTests.vb" />
<Compile Include="PopulateSwitch\PopulateSwitchTests_FixAllTests.vb" />
<Compile Include="Diagnostics\RemoveUnnecessaryCast\RemoveUnnecessaryCastTests.vb" />
<Compile Include="Diagnostics\RemoveUnnecessaryCast\RemoveUnnecessaryCastTests_FixAllTests.vb" />
<Compile Include="Diagnostics\RemoveUnnecessaryImports\RemoveUnnecessaryImportsTests.vb" />
......
......@@ -8,7 +8,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics.Popula
Inherits AbstractVisualBasicDiagnosticProviderBasedUserDiagnosticTest
Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace) As Tuple(Of DiagnosticAnalyzer, CodeFixProvider)
Return New Tuple(Of DiagnosticAnalyzer, CodeFixProvider)(New VisualBasicPopulateSwitchDiagnosticAnalyzer(), New PopulateSwitchCodeFixProvider())
Return New Tuple(Of DiagnosticAnalyzer, CodeFixProvider)(New VisualBasicPopulateSwitchDiagnosticAnalyzer(), New VisualBasicPopulateSwitchCodeFixProvider())
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsPopulateSwitch)>
......@@ -23,15 +23,15 @@ End Enum
Class Foo
Sub Bar()
Dim e = MyEnum.Fizz
Select Case e
[|Case MyEnum.Fizz
Select Case [|e|]
Case MyEnum.Fizz
Exit Select
Case MyEnum.Buzz
Exit Select
Case MyEnum.FizzBuzz
Exit Select
Case Else
Exit Select|]
Exit Select
End Select
End Sub
End Class
......@@ -39,7 +39,7 @@ End Class
Await TestMissingAsync(markup)
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsPopulateSwitch)>
Public Async Function AllMembersExist_NotElse() As Task
Dim markup =
......@@ -52,13 +52,13 @@ End Enum
Class Foo
Sub Bar()
Dim e = MyEnum.Fizz
Select Case e
[|Case MyEnum.Fizz
Select Case [|e|]
Case MyEnum.Fizz
Exit Select
Case MyEnum.Buzz
Exit Select
Case MyEnum.FizzBuzz
Exit Select|]
Exit Select
End Select
End Sub
End Class
......@@ -90,7 +90,7 @@ End Class
Await TestAsync(markup, expected, compareTokens:=False)
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsPopulateSwitch)>
Public Async Function NotAllMembersExist_NotElse() As Task
Dim markup =
......@@ -103,11 +103,11 @@ End Enum
Class Foo
Sub Bar()
Dim e = MyEnum.Fizz
Select Case e
[|Case MyEnum.Fizz
Select Case [|e|]
Case MyEnum.Fizz
Exit Select
Case MyEnum.Buzz
Exit Select|]
Exit Select
End Select
End Sub
End Class
......@@ -139,7 +139,7 @@ End Class
Await TestAsync(markup, expected, compareTokens:=False)
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsPopulateSwitch)>
Public Async Function NotAllMembersExist_WithElse() As Task
Dim markup =
......@@ -152,13 +152,13 @@ End Enum
Class Foo
Sub Bar()
Dim e = MyEnum.Fizz
Select Case e
[|Case MyEnum.Fizz
Select Case [|e|]
Case MyEnum.Fizz
Exit Select
Case MyEnum.Buzz
Exit Select
Case Else
Exit Select|]
Exit Select
End Select
End Sub
End Class
......@@ -190,7 +190,7 @@ End Class
Await TestAsync(markup, expected, compareTokens:=False)
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsPopulateSwitch)>
Public Async Function NotAllMembersExist_NotElse_EnumHasExplicitType() As Task
Dim markup =
......@@ -203,11 +203,11 @@ End Enum
Class Foo
Sub Bar()
Dim e = MyEnum.Fizz
Select Case e
[|Case MyEnum.Fizz
Select Case [|e|]
Case MyEnum.Fizz
Exit Select
Case MyEnum.Buzz
Exit Select|]
Exit Select
End Select
End Sub
End Class
......@@ -239,7 +239,7 @@ End Class
Await TestAsync(markup, expected, compareTokens:=False)
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsPopulateSwitch)>
Public Async Function NotAllMembersExist_WithMembersAndElseInBlock_NewValuesAboveElseBlock() As Task
Dim markup =
......@@ -252,12 +252,12 @@ End Enum
Class Foo
Sub Bar()
Dim e = MyEnum.Fizz
Select Case e
[|Case MyEnum.Fizz
Select Case [|e|]
Case MyEnum.Fizz
Exit Select
Case MyEnum.Buzz
Case Else
Exit Select|]
Exit Select
End Select
End Sub
End Class
......@@ -301,7 +301,7 @@ End Enum
Class Foo
Sub Bar()
Dim e = MyEnum.Fizz
Select Case e[||]
Select Case [|e|]
End Select
End Sub
End Class
......@@ -330,7 +330,7 @@ Class Foo
End Sub
End Class
</File>
Await TestAsync(markup, expected, compareTokens:=False)
End Function
......@@ -347,8 +347,8 @@ End Enum
Class Foo
Sub Bar()
Dim e = CreateNew
Select Case e
[|Case CreateNew
Select Case [|e|]
Case CreateNew
Exit Select
Case Create
Exit Select
......@@ -361,12 +361,12 @@ Class Foo
Case Append
Exit Select
Case Else
Exit Select|]
Exit Select
End Select
End Sub
End Class
</File>
Await TestMissingAsync(markup)
End Function
......@@ -383,8 +383,8 @@ End Enum
Class Foo
Sub Bar()
Dim e = CreateNew
Select Case e
[|Case Truncate
Select Case [|e|]
Case Truncate
Exit Select
Case Append
Exit Select
......@@ -397,12 +397,12 @@ Class Foo
Case Create
Exit Select
Case Else
Exit Select|]
Exit Select
End Select
End Sub
End Class
</File>
Await TestMissingAsync(markup)
End Function
......@@ -419,21 +419,21 @@ End Enum
Class Foo
Sub Bar()
Dim e = CreateNew
Select Case e
[|Case CreateNew
Select Case [|e|]
Case CreateNew
Exit Select
Case Create
Exit Select
Case Open
Exit Select
Case Else
Exit Select|]
Exit Select
End Select
End Sub
End Class
</File>
Dim expected =
Dim expected =
<File>
Imports System.IO.FileMode
Enum MyEnum
......@@ -451,11 +451,11 @@ Class Foo
Exit Select
Case Open
Exit Select
Case System.IO.FileMode.Append
Case System.IO.FileMode.OpenOrCreate
Exit Select
Case System.IO.FileMode.Truncate
Exit Select
Case System.IO.FileMode.OpenOrCreate
Case System.IO.FileMode.Append
Exit Select
Case Else
Exit Select
......@@ -463,7 +463,7 @@ Class Foo
End Sub
End Class
</File>
Await TestAsync(markup, expected, compareTokens:=False)
End Function
......@@ -480,14 +480,14 @@ End Enum
Class Foo
Sub Bar()
Dim e = CreateNew
Select Case e
[||]
Select Case [|e|]
End Select
End Sub
End Class
</File>
Dim expected =
Dim expected =
<File>
Imports System.IO.FileMode
Enum MyEnum
......@@ -517,10 +517,10 @@ Class Foo
End Sub
End Class
</File>
Await TestAsync(markup, expected, compareTokens:=False)
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsPopulateSwitch)>
Public Async Function NotAllMembersExist_EnumIsFlags() As Task
Dim markup =
......@@ -535,11 +535,11 @@ End Enum
Class Foo
Sub Bar()
Dim e = MyEnum.Fizz
Select Case e
[|Case MyEnum.Fizz
Select Case [|e|]
Case MyEnum.Fizz
Exit Select
Case MyEnum.Buzz
Exit Select|]
Exit Select
End Select
End Sub
End Class
......@@ -547,7 +547,7 @@ End Class
Await TestMissingAsync(markup)
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsPopulateSwitch)>
Public Async Function NotAllMembersExist_EnumIsFlagsAttribute() As Task
Dim markup =
......@@ -562,11 +562,11 @@ End Enum
Class Foo
Sub Bar()
Dim e = MyEnum.Fizz
Select Case e
[|Case MyEnum.Fizz
Select Case [|e|]
Case MyEnum.Fizz
Exit Select
Case MyEnum.Buzz
Exit Select|]
Exit Select
End Select
End Sub
End Class
......@@ -574,7 +574,7 @@ End Class
Await TestMissingAsync(markup)
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsPopulateSwitch)>
Public Async Function NotAllMembersExist_EnumIsFullyQualifiedSystemFlags() As Task
Dim markup =
......@@ -588,11 +588,11 @@ End Enum
Class Foo
Sub Bar()
Dim e = MyEnum.Fizz
Select Case e
[|Case MyEnum.Fizz
Select Case [|e|]
Case MyEnum.Fizz
Exit Select
Case MyEnum.Buzz
Exit Select|]
Exit Select
End Select
End Sub
End Class
......@@ -600,7 +600,7 @@ End Class
Await TestMissingAsync(markup)
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsPopulateSwitch)>
Public Async Function NotAllMembersExist_EnumIsFullyQualifiedSystemFlagsAttribute() As Task
Dim markup =
......@@ -614,11 +614,11 @@ End Enum
Class Foo
Sub Bar()
Dim e = MyEnum.Fizz
Select Case e
[|Case MyEnum.Fizz
Select Case [|e|]
Case MyEnum.Fizz
Exit Select
Case MyEnum.Buzz
Exit Select|]
Exit Select
End Select
End Sub
End Class
......@@ -626,7 +626,7 @@ End Class
Await TestMissingAsync(markup)
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsPopulateSwitch)>
Public Async Function NotAllMembersExist_EnumHasNonFlagsAttribute() As Task
Dim markup =
......@@ -640,11 +640,11 @@ End Enum
Class Foo
Sub Bar()
Dim e = MyEnum.Fizz
Select Case e
[|Case MyEnum.Fizz
Select Case [|e|]
Case MyEnum.Fizz
Exit Select
Case MyEnum.Buzz
Exit Select|]
Exit Select
End Select
End Sub
End Class
......@@ -677,7 +677,7 @@ End Class
Await TestAsync(markup, expected, compareTokens:=False)
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsPopulateSwitch)>
Public Async Function NotAllMembersExist_EnumIsNested() As Task
Dim markup =
......@@ -690,11 +690,11 @@ Class Foo
End Enum
Sub Bar()
Dim e = MyEnum.Fizz
Select Case e
[|Case MyEnum.Fizz
Select Case [|e|]
Case MyEnum.Fizz
Exit Select
Case MyEnum.Buzz
Exit Select|]
Exit Select
End Select
End Sub
End Class
......@@ -715,7 +715,7 @@ Class Foo
Exit Select
Case MyEnum.Buzz
Exit Select
Case Foo.MyEnum.FizzBuzz
Case MyEnum.FizzBuzz
Exit Select
Case Else
Exit Select
......@@ -726,7 +726,7 @@ End Class
Await TestAsync(markup, expected, compareTokens:=False)
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsPopulateSwitch)>
Public Async Function NotAllMembersExist_SwitchIsNotEnum() As Task
Dim markup =
......@@ -734,11 +734,11 @@ End Class
Class Foo
Sub Bar()
Dim e = "Test"
Select Case e
[|Case "Fizz"
Select Case [|e|]
Case "Fizz"
Exit Select
Case Test"
Exit Select|]
Exit Select
End Select
End Sub
End Class
......
......@@ -17,13 +17,13 @@ End Enum
Class Foo
Sub Bar()
Dim e = MyEnum1.Fizz
Select Case e
{|FixAllInDocument:Case MyEnum1.Fizz
Select Case {|FixAllInDocument:e|}
Case MyEnum1.Fizz
Exit Select
Case MyEnum1.Buzz
Exit Select
Case Else
Exit Select|}
Exit Select
End Select
Select Case e
......@@ -163,7 +163,7 @@ End Class]]>
</Document>
</Project>
</Workspace>.ToString()
Await TestAsync(input, expected, compareTokens:=False, fixAllActionEquivalenceKey:=Nothing)
End Function
......@@ -182,13 +182,13 @@ End Enum
Class Foo
Sub Bar()
Dim e = MyEnum1.Fizz
Select Case e
{|FixAllInProject:Case MyEnum1.Fizz
Select Case {|FixAllInProject:e|}
Case MyEnum1.Fizz
Exit Select
Case MyEnum1.Buzz
Exit Select
Case MyEnum1.FizzBuzz
Exit Select|}
Exit Select
End Select
End Sub
End Class]]>
......@@ -310,7 +310,7 @@ End Class]]>
</Document>
</Project>
</Workspace>.ToString()
Await TestAsync(input, expected, compareTokens:=False, fixAllActionEquivalenceKey:=Nothing)
End Function
......@@ -329,11 +329,11 @@ End Enum
Class Foo
Sub Bar()
Dim e = MyEnum1.Fizz
Select Case e
{|FixAllInSolution:Case MyEnum1.Fizz
Select Case {|FixAllInSolution:e|}
Case MyEnum1.Fizz
Exit Select
Case MyEnum1.Buzz
Exit Select|}
Exit Select
End Select
End Sub
End Class]]>
......@@ -453,7 +453,7 @@ End Class]]>
</Document>
</Project>
</Workspace>.ToString()
Await TestAsync(input, expected, compareTokens:=False, fixAllActionEquivalenceKey:=Nothing)
End Function
End Class
......
......@@ -80,8 +80,6 @@
<Compile Include="CodeFixes\ImplementInterface\ImplementInterfaceCodeFixProvider.cs" />
<Compile Include="CodeFixes\Iterator\CSharpAddYieldCodeFixProvider.cs" />
<Compile Include="CodeFixes\Iterator\CSharpChangeToIEnumerableCodeFixProvider.cs" />
<Compile Include="CodeFixes\PopulateSwitch\PopulateSwitchCodeFixProvider.cs" />
<Compile Include="CodeFixes\PopulateSwitch\PopulateSwitchCodeFixProvider.PopulateSwitchFixAllProvider.cs" />
<Compile Include="CodeFixes\RemoveUnnecessaryCast\RemoveUnnecessaryCastCodeFixProvider.RemoveUnnecessaryCastFixAllProvider.cs" />
<Compile Include="CodeFixes\RemoveUnnecessaryCast\RemoveUnnecessaryCastCodeFixProvider.cs" />
<Compile Include="CodeFixes\RemoveUnnecessaryUsings\RemoveUnnecessaryUsingsCodeFixProvider.cs" />
......@@ -267,7 +265,7 @@
<DesignTime>True</DesignTime>
<DependentUpon>CSharpFeaturesResources.resx</DependentUpon>
</Compile>
<Compile Include="Diagnostics\Analyzers\CSharpPopulateSwitchDiagnosticAnalyzer.cs" />
<Compile Include="PopulateSwitch\CSharpPopulateSwitchDiagnosticAnalyzer.cs" />
<Compile Include="Diagnostics\Analyzers\CSharpUnboundIdentifiersDiagnosticAnalyzer.cs" />
<Compile Include="Diagnostics\Analyzers\CSharpRemoveUnnecessaryCastDiagnosticAnalyzer.cs" />
<Compile Include="Diagnostics\Analyzers\CSharpRemoveUnnecessaryImportsDiagnosticAnalyzer.cs" />
......@@ -352,6 +350,7 @@
<Compile Include="Organizing\Organizers\OperatorDeclarationOrganizer.cs" />
<Compile Include="Organizing\Organizers\PropertyDeclarationOrganizer.cs" />
<Compile Include="Organizing\Organizers\StructDeclarationOrganizer.cs" />
<Compile Include="PopulateSwitch\CSharpPopulateSwitchCodeFixProvider.cs" />
<Compile Include="RemoveUnnecessaryImports\CSharpRemoveUnnecessaryImportsService.cs" />
<Compile Include="RemoveUnnecessaryImports\CSharpRemoveUnnecessaryImportsService.Rewriter.cs" />
<Compile Include="ReplaceMethodWithProperty\CSharpReplaceMethodWithPropertyService.cs" />
......
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace Microsoft.CodeAnalysis.CSharp.CodeFixes.PopulateSwitch
{
internal partial class PopulateSwitchCodeFixProvider : CodeFixProvider
{
private class PopulateSwitchFixAllProvider : BatchSimplificationFixAllProvider
{
internal static new readonly PopulateSwitchFixAllProvider Instance = new PopulateSwitchFixAllProvider();
protected override SyntaxNode GetNodeToSimplify(SyntaxNode root, SemanticModel model, Diagnostic diagnostic, Workspace workspace, out string codeActionId, CancellationToken cancellationToken)
{
codeActionId = null;
return GetSwitchStatementNode(root, diagnostic.Location.SourceSpan);
}
protected override bool NeedsParentFixup
{
get
{
return true;
}
}
protected override async Task<Document> AddSimplifyAnnotationsAsync(Document document, SyntaxNode nodeToSimplify, CancellationToken cancellationToken)
{
var switchBlock = nodeToSimplify as SwitchStatementSyntax;
if (switchBlock == null)
{
return null;
}
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
return await AddMissingSwitchLabelsAsync(model, document, root, switchBlock).ConfigureAwait(false);
}
}
}
}
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Collections.Generic;
using System.Composition;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Simplification;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.CSharp.CodeFixes.PopulateSwitch
{
[ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.AddLabelsToSwitch), Shared]
[ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.PopulateSwitch), Shared]
[ExtensionOrder(After = PredefinedCodeFixProviderNames.ImplementInterface)]
internal partial class PopulateSwitchCodeFixProvider : CodeFixProvider
internal class CSharpPopulateSwitchCodeFixProvider : AbstractPopulateSwitchCodeFixProvider
{
public override ImmutableArray<string> FixableDiagnosticIds
{
get { return ImmutableArray.Create(IDEDiagnosticIds.PopulateSwitchDiagnosticId); }
}
public sealed override FixAllProvider GetFixAllProvider()
{
return PopulateSwitchFixAllProvider.Instance;
}
private static SwitchStatementSyntax GetSwitchStatementNode(SyntaxNode root, TextSpan span)
protected override SyntaxNode GetSwitchStatementNode(SyntaxNode root, TextSpan span)
{
var token = root.FindToken(span.Start);
if (!token.Span.IntersectsWith(span))
{
return null;
}
return (SwitchStatementSyntax)root.FindNode(span);
var switchExpression = (ExpressionSyntax)root.FindNode(span);
return (SwitchStatementSyntax) switchExpression.Parent;
}
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
protected override SyntaxNode GetSwitchExpression(SyntaxNode node)
{
var document = context.Document;
var span = context.Span;
var cancellationToken = context.CancellationToken;
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var node = GetSwitchStatementNode(root, span);
if (node == null)
{
return;
}
context.RegisterCodeFix(
new MyCodeAction(
CSharpFeaturesResources.AddSwitchLabels,
(c) => AddMissingSwitchLabelsAsync(model, document, root, node)),
context.Diagnostics);
var switchStatement = (SwitchStatementSyntax)node;
return switchStatement.Expression;
}
private static async Task<Document> AddMissingSwitchLabelsAsync(SemanticModel model, Document document, SyntaxNode root, SwitchStatementSyntax switchBlock)
protected override List<string> GetMissingLabels(SyntaxNode node, SemanticModel model, INamedTypeSymbol enumType, out bool containsDefaultLabel)
{
var enumType = (INamedTypeSymbol)model.GetTypeInfo(switchBlock.Expression).Type;
var fullyQualifiedEnumType = enumType.ToDisplayString();
var containsDefaultLabel = false;
var caseLabels = new List<ExpressionSyntax>();
foreach (var section in switchBlock.Sections)
{
foreach (var label in section.Labels)
{
var caseLabel = label as CaseSwitchLabelSyntax;
if (caseLabel != null)
{
caseLabels.Add(caseLabel.Value);
continue;
}
if (label.IsKind(SyntaxKind.DefaultSwitchLabel))
{
containsDefaultLabel = true;
}
}
}
var missingLabels = GetMissingLabels(model, caseLabels, enumType);
var breakStatement = SyntaxFactory.BreakStatement();
var statements = SyntaxFactory.List(new List<StatementSyntax> { breakStatement });
var newSections = SyntaxFactory.List(switchBlock.Sections);
foreach (var label in missingLabels)
{
// If an existing simplified label exists, it means we can assume that works already and do it ourselves as well (ergo: there is a static using)
var caseLabel =
SyntaxFactory.CaseSwitchLabel(
SyntaxFactory.ParseExpression($"{fullyQualifiedEnumType}.{label}")
.WithTrailingTrivia(SyntaxFactory.ParseTrailingTrivia(Environment.NewLine)));
var section =
SyntaxFactory.SwitchSection(SyntaxFactory.List(new List<SwitchLabelSyntax> { caseLabel }), statements)
.WithAdditionalAnnotations(Formatter.Annotation);
// ensure that the new cases are above the last section if a default case exists, but below all other sections
if (containsDefaultLabel)
{
// this will not give an index-out-of-bounds error because we know there is at least one case block
newSections = newSections.Insert(switchBlock.Sections.Count - 1, section);
}
else
{
newSections = newSections.Add(section);
}
}
if (!containsDefaultLabel)
{
newSections = newSections.Add(SyntaxFactory.SwitchSection(SyntaxFactory.List(
new List<SwitchLabelSyntax> { SyntaxFactory.DefaultSwitchLabel() }), statements));
}
var newNode = switchBlock.WithSections(newSections).WithAdditionalAnnotations(Formatter.Annotation, Simplifier.Annotation);
var newRoot = root.ReplaceNode(switchBlock, newNode);
return await Simplifier.ReduceAsync(document.WithSyntaxRoot(newRoot)).ConfigureAwait(false);
}
var caseLabels = GetCaseLabels((SwitchStatementSyntax)node, out containsDefaultLabel);
private static IEnumerable<string> GetMissingLabels(SemanticModel model, List<ExpressionSyntax> caseLabels, INamedTypeSymbol enumType)
{
var symbols = new List<ISymbol>();
foreach (var label in caseLabels)
{
......@@ -185,12 +89,47 @@ private static IEnumerable<string> GetMissingLabels(SemanticModel model, List<Ex
return missingLabels;
}
private class MyCodeAction : CodeAction.DocumentChangeAction
protected override int InsertPosition(List<SyntaxNode> sections)
{
return sections.Count - 1;
}
protected override List<SyntaxNode> GetSwitchSections(SyntaxNode node)
{
var switchBlock = (SwitchStatementSyntax)node;
return new List<SyntaxNode>(switchBlock.Sections);
}
protected override SyntaxNode NewSwitchNode(SyntaxNode node, List<SyntaxNode> sections)
{
var switchBlock = (SwitchStatementSyntax)node;
return switchBlock.WithSections(SyntaxFactory.List(sections))
.WithAdditionalAnnotations(Formatter.Annotation, Simplifier.Annotation);
}
private List<ExpressionSyntax> GetCaseLabels(SwitchStatementSyntax switchBlock, out bool containsDefaultLabel)
{
public MyCodeAction(string title, Func<CancellationToken, Task<Document>> createChangedDocument) :
base(title, createChangedDocument)
containsDefaultLabel = false;
var caseLabels = new List<ExpressionSyntax>();
foreach (var section in switchBlock.Sections)
{
foreach (var label in section.Labels)
{
var caseLabel = label as CaseSwitchLabelSyntax;
if (caseLabel != null)
{
caseLabels.Add(caseLabel.Value);
}
if (label.IsKind(SyntaxKind.DefaultSwitchLabel))
{
containsDefaultLabel = true;
}
}
}
return caseLabels;
}
}
}
using System.Collections.Immutable;
using System.Threading;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Diagnostics.PopulateSwitch;
using Microsoft.CodeAnalysis.Text;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System.Collections.Generic;
using System.Collections.Immutable;
namespace Microsoft.CodeAnalysis.CSharp.Diagnostics.PopulateSwitch
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
internal sealed class CSharpPopulateSwitchDiagnosticAnalyzer : PopulateSwitchDiagnosticAnalyzerBase<SyntaxKind>
internal sealed class CSharpPopulateSwitchDiagnosticAnalyzer : AbstractPopulateSwitchDiagnosticAnalyzerBase<SyntaxKind>
{
private static readonly ImmutableArray<SyntaxKind> s_kindsOfInterest = ImmutableArray.Create(SyntaxKind.SwitchStatement);
protected override ImmutableArray<SyntaxKind> SyntaxKindsOfInterest { get; } = s_kindsOfInterest;
public override ImmutableArray<SyntaxKind> SyntaxKindsOfInterest
protected override SyntaxNode GetExpression(SyntaxNode node)
{
get
{
return s_kindsOfInterest;
}
var switchBlock = (SwitchStatementSyntax)node;
return switchBlock.Expression;
}
protected override TextSpan GetDiagnosticSpan(SyntaxNode node)
protected override List<SyntaxNode> GetCaseLabels(SyntaxNode node, out bool hasDefaultCase)
{
var switchStatement = (SwitchStatementSyntax)node;
return switchStatement.Span;
}
hasDefaultCase = false;
protected override bool SwitchIsFullyPopulated(SemanticModel model, SyntaxNode node, CancellationToken cancellationToken)
{
var switchBlock = (SwitchStatementSyntax)node;
var enumType = model.GetTypeInfo(switchBlock.Expression).Type as INamedTypeSymbol;
if (enumType == null || enumType.TypeKind != TypeKind.Enum)
{
return true;
}
// ignore enums marked with Flags per spec: https://github.com/dotnet/roslyn/issues/6766#issuecomment-156878851
foreach (var attribute in enumType.GetAttributes())
{
var containingClass = attribute.AttributeClass.ToDisplayString();
if (containingClass == typeof(System.FlagsAttribute).FullName)
{
return true;
}
}
var caseLabels = new List<ExpressionSyntax>();
var hasDefaultCase = false;
var caseLabels = new List<SyntaxNode>();
foreach (var section in switchBlock.Sections)
{
......@@ -66,42 +41,7 @@ protected override bool SwitchIsFullyPopulated(SemanticModel model, SyntaxNode n
}
}
if (!hasDefaultCase)
{
return false;
}
var labelSymbols = new List<ISymbol>();
foreach (var label in caseLabels)
{
labelSymbols.Add(model.GetSymbolInfo(label).Symbol);
}
foreach (var member in enumType.GetMembers())
{
// skip `.ctor`
if (member.IsImplicitlyDeclared)
{
continue;
}
var switchHasSymbol = false;
foreach (var symbol in labelSymbols)
{
if (symbol == member)
{
switchHasSymbol = true;
break;
}
}
if (!switchHasSymbol)
{
return false;
}
}
return true;
return caseLabels;
}
}
}
......@@ -6,7 +6,6 @@ internal static class PredefinedCodeFixProviderNames
{
public const string AddAwait = "Add Await For Expression";
public const string AddAsync = "Add Async To Member";
public const string AddLabelsToSwitch = "Add Labels To Switch";
public const string ChangeReturnType = "Change Return Type";
public const string ChangeToYield = "Change To Yield";
public const string ConvertToAsync = "Convert To Async";
......@@ -29,6 +28,7 @@ internal static class PredefinedCodeFixProviderNames
public const string ImplementInterface = "Implement Interface";
public const string InsertMissingCast = nameof(InsertMissingCast);
public const string MoveToTopOfFile = "Move To Top Of File";
public const string PopulateSwitch = "Populate Switch";
public const string RemoveUnnecessaryCast = "Remove Unnecessary Casts";
public const string RemoveUnnecessaryImports = "Remove Unnecessary Usings or Imports";
public const string RenameTracking = "Rename Tracking";
......
......@@ -181,7 +181,7 @@
<Compile Include="Completion\SuggestionMode\SuggestionModeCompletionProvider.cs" />
<Compile Include="Diagnostics\AnalyzerHelper.cs" />
<Compile Include="Diagnostics\AbstractHostDiagnosticUpdateSource.cs" />
<Compile Include="Diagnostics\Analyzers\PopulateSwitchDiagnosticAnalyzerBase.cs" />
<Compile Include="PopulateSwitch\AbstractPopulateSwitchDiagnosticAnalyzer.cs" />
<Compile Include="Diagnostics\AnalyzerUpdateArgsId.cs" />
<Compile Include="Diagnostics\DiagnosticEventTaskScheduler.cs" />
<Compile Include="Diagnostics\DiagnosticService_UpdateSourceRegistrationService.cs" />
......@@ -254,6 +254,7 @@
<Compile Include="MakeMethodSynchronous\AbstractMakeMethodSynchronousCodeFixProvider.cs" />
<Compile Include="Navigation\NavigationOptions.cs" />
<Compile Include="Navigation\NavigationOptionsProvider.cs" />
<Compile Include="PopulateSwitch\AbstractPopulateSwitchCodeFixProvider.cs" />
<Compile Include="QuickInfo\QuickInfoUtilities.cs" />
<Compile Include="RemoveUnnecessaryImports\AbstractRemoveUnnecessaryImportsService.cs" />
<Compile Include="ReplaceMethodWithProperty\ReplaceMethodWithPropertyCodeRefactoringProvider.cs" />
......
......@@ -241,6 +241,15 @@ internal class FeaturesResources {
}
}
/// <summary>
/// Looks up a localized string similar to Add Missing Labels.
/// </summary>
internal static string AddSwitchLabels {
get {
return ResourceManager.GetString("AddSwitchLabels", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to An active statement has been removed from its original method. You must revert your changes to continue or restart the debugging session..
/// </summary>
......
......@@ -916,4 +916,7 @@ This version used in: {2}</value>
<data name="AddMissingStatements" xml:space="preserve">
<value>Add missing statements to switch</value>
</data>
<data name="AddSwitchLabels" xml:space="preserve">
<value>Add Missing Labels</value>
</data>
</root>
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.CSharp.CodeFixes.PopulateSwitch
{
internal abstract class AbstractPopulateSwitchCodeFixProvider : CodeFixProvider
{
public override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(IDEDiagnosticIds.PopulateSwitchDiagnosticId);
public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer;
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
var document = context.Document;
var span = context.Span;
var cancellationToken = context.CancellationToken;
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var node = GetSwitchStatementNode(root, span);
if (node == null)
{
return;
}
context.RegisterCodeFix(
new MyCodeAction(
FeaturesResources.AddSwitchLabels,
c => AddMissingSwitchLabelsAsync(model, document, root, node)),
context.Diagnostics);
}
protected abstract SyntaxNode GetSwitchExpression(SyntaxNode node);
protected abstract List<string> GetMissingLabels(SyntaxNode node, SemanticModel model, INamedTypeSymbol enumType,
out bool containsDefaultLabel);
protected abstract int InsertPosition(List<SyntaxNode> sections);
protected abstract List<SyntaxNode> GetSwitchSections(SyntaxNode node);
protected abstract SyntaxNode NewSwitchNode(SyntaxNode node, List<SyntaxNode> sections);
protected abstract SyntaxNode GetSwitchStatementNode(SyntaxNode root, TextSpan span);
private Task<Document> AddMissingSwitchLabelsAsync(SemanticModel model, Document document, SyntaxNode root, SyntaxNode switchNode)
{
var enumType = (INamedTypeSymbol)model.GetTypeInfo(GetSwitchExpression(switchNode)).Type;
var fullyQualifiedEnumType = enumType.ToDisplayString();
bool containsDefaultLabel;
var missingLabels = GetMissingLabels(switchNode, model, enumType, out containsDefaultLabel);
var generator = SyntaxGenerator.GetGenerator(document);
var switchExitStatement = generator.ExitSwitchStatement();
var statements = new List<SyntaxNode> { switchExitStatement };
var newSections = GetSwitchSections(switchNode);
foreach (var label in missingLabels)
{
var caseLabel = generator.DottedName($"{fullyQualifiedEnumType}.{label}");
var section = generator.SwitchSection(caseLabel, new List<SyntaxNode> { switchExitStatement });
// ensure that the new cases are above the last section if a default case exists, but below all other sections
if (containsDefaultLabel)
{
newSections.Insert(InsertPosition(newSections), section);
}
else
{
newSections.Add(section);
}
}
if (!containsDefaultLabel)
{
newSections.Add(generator.DefaultSwitchSection(statements));
}
var newNode = NewSwitchNode(switchNode, newSections);
var newRoot = root.ReplaceNode(switchNode, newNode);
return Task.FromResult(document.WithSyntaxRoot(newRoot));
}
private class MyCodeAction : CodeAction.DocumentChangeAction
{
public MyCodeAction(string title, Func<CancellationToken, Task<Document>> createChangedDocument) :
base(title, createChangedDocument)
{
}
}
}
}
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading;
namespace Microsoft.CodeAnalysis.Diagnostics.PopulateSwitch
{
internal abstract class PopulateSwitchDiagnosticAnalyzerBase<TLanguageKindEnum> : DiagnosticAnalyzer, IBuiltInAnalyzer where TLanguageKindEnum : struct
internal abstract class AbstractPopulateSwitchDiagnosticAnalyzerBase<TLanguageKindEnum> : DiagnosticAnalyzer, IBuiltInAnalyzer where TLanguageKindEnum : struct
{
private static readonly LocalizableString s_localizableTitle = new LocalizableResourceString(nameof(FeaturesResources.AddMissingStatements), FeaturesResources.ResourceManager, typeof(FeaturesResources));
private static readonly LocalizableString s_localizableMessage = new LocalizableResourceString(nameof(WorkspacesResources.PopulateSwitch), WorkspacesResources.ResourceManager, typeof(WorkspacesResources));
......@@ -14,24 +14,18 @@ internal abstract class PopulateSwitchDiagnosticAnalyzerBase<TLanguageKindEnum>
s_localizableTitle,
s_localizableMessage,
DiagnosticCategory.EditAndContinue,
DiagnosticSeverity.Hidden,
DiagnosticSeverity.Warning,
isEnabledByDefault: true,
customTags: DiagnosticCustomTags.Unnecessary);
#region Interface methods
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
{
get
{
return ImmutableArray.Create(s_descriptor);
}
}
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(s_descriptor);
public override void Initialize(AnalysisContext context)
{
context.RegisterSyntaxNodeAction(
(nodeContext) =>
nodeContext =>
{
Diagnostic diagnostic;
if (TryPopulateSwitch(nodeContext.SemanticModel, nodeContext.Node, out diagnostic, nodeContext.CancellationToken))
......@@ -42,24 +36,74 @@ public override void Initialize(AnalysisContext context)
SyntaxKindsOfInterest);
}
public abstract ImmutableArray<TLanguageKindEnum> SyntaxKindsOfInterest { get; }
protected abstract ImmutableArray<TLanguageKindEnum> SyntaxKindsOfInterest { get; }
#endregion
protected abstract bool SwitchIsFullyPopulated(SemanticModel model, SyntaxNode node, CancellationToken cancellationToken);
protected abstract TextSpan GetDiagnosticSpan(SyntaxNode node);
protected abstract SyntaxNode GetExpression(SyntaxNode node);
protected abstract List<SyntaxNode> GetCaseLabels(SyntaxNode node, out bool hasDefaultCase);
private bool SwitchIsFullyPopulated(SemanticModel model, SyntaxNode node)
{
var enumType = model.GetTypeInfo(GetExpression(node)).Type as INamedTypeSymbol;
if (enumType == null || enumType.TypeKind != TypeKind.Enum)
{
return true;
}
// ignore enums marked with Flags per spec: https://github.com/dotnet/roslyn/issues/6766#issuecomment-156878851
foreach (var attribute in enumType.GetAttributes())
{
var containingClass = attribute.AttributeClass.ToDisplayString();
if (containingClass == typeof(System.FlagsAttribute).FullName)
{
return true;
}
}
bool hasDefaultCase;
var caseLabels = GetCaseLabels(node, out hasDefaultCase);
if (!hasDefaultCase)
{
return false;
}
var labelSymbols = new List<ISymbol>();
foreach (var label in caseLabels)
{
labelSymbols.Add(model.GetSymbolInfo(label).Symbol);
}
foreach (var member in enumType.GetMembers())
{
// skip `.ctor` and `__value`
var fieldSymbol = member as IFieldSymbol;
if (fieldSymbol == null || fieldSymbol.Type.SpecialType != SpecialType.None)
{
continue;
}
if (!labelSymbols.Contains(member))
{
return false;
}
}
return true;
}
private bool TryPopulateSwitch(SemanticModel model, SyntaxNode node, out Diagnostic diagnostic, CancellationToken cancellationToken)
{
diagnostic = default(Diagnostic);
if (SwitchIsFullyPopulated(model, node, cancellationToken))
if (SwitchIsFullyPopulated(model, node))
{
return false;
}
var tree = model.SyntaxTree;
var span = GetDiagnosticSpan(node);
var span = GetExpression(node).Span;
if (tree.OverlapsHiddenPosition(span, cancellationToken))
{
return false;
......
......@@ -116,8 +116,7 @@
<Compile Include="CodeFixes\MoveToTopOfFile\MoveToTopOfFileCodeFixProvider.vb" />
<Compile Include="CodeFixes\OverloadBase\OverloadBaseCodeFixProvider.AddOverloads.vb" />
<Compile Include="CodeFixes\OverloadBase\OverloadBaseCodeFixProvider.vb" />
<Compile Include="CodeFixes\PopulateSwitch\PopulateSwitchCodeFixProvider.PopulateSwitchCodeFixAllProvider.vb" />
<Compile Include="CodeFixes\PopulateSwitch\PopulateSwitchCodeFixProvider.vb" />
<Compile Include="PopulateSwitch\VisualBasicPopulateSwitchCodeFixProvider.vb" />
<Compile Include="CodeFixes\RemoveUnnecessaryCast\RemoveUnnecessaryCastCodeFixProvider.RemoveUnnecessaryCastFixAllProvider.vb" />
<Compile Include="CodeFixes\RemoveUnnecessaryCast\RemoveUnnecessaryCastCodeFixProvider.Rewriter.vb" />
<Compile Include="CodeFixes\RemoveUnnecessaryCast\RemoveUnnecessaryCastCodeFixProvider.vb" />
......@@ -304,7 +303,7 @@
<Compile Include="Completion\KeywordRecommenders\Types\BuiltInTypesKeywordRecommender.vb" />
<Compile Include="Completion\VisualBasicCompletionRules.vb" />
<Compile Include="Completion\VisualBasicCompletionService.vb" />
<Compile Include="Diagnostics\Analyzers\VisualBasicPopulateSwitchDiagnosticAnalyzer.vb" />
<Compile Include="PopulateSwitch\VisualBasicPopulateSwitchDiagnosticAnalyzer.vb" />
<Compile Include="Diagnostics\Analyzers\VisualBasicUnboundIdentifiersDiagnosticAnalyzer.vb" />
<Compile Include="Diagnostics\Analyzers\VisualBasicRemoveUnnecessaryCastDiagnosticAnalyzer.vb" />
<Compile Include="Diagnostics\Analyzers\VisualBasicRemoveUnnecessaryImportsDiagnosticAnalyzer.vb" />
......
Imports System.Threading
Imports Microsoft.CodeAnalysis.CodeFixes
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.PopulateSwitch
Partial Friend Class PopulateSwitchCodeFixProvider
Inherits CodeFixProvider
Private Class PopulateSwitchCodeFixAllProvider
Inherits BatchSimplificationFixAllProvider
Friend Shared Shadows ReadOnly Instance As PopulateSwitchCodeFixAllProvider = New PopulateSwitchCodeFixAllProvider()
Protected Overrides Function GetNodeToSimplify(root As SyntaxNode, model As SemanticModel, diagnostic As Diagnostic, workspace As Workspace, ByRef codeActionId As String, cancellationToken As CancellationToken) As SyntaxNode
codeActionId = Nothing
Return GetSelectBlockNode(root, diagnostic.Location.SourceSpan)
End Function
Protected Overrides ReadOnly Property NeedsParentFixup As Boolean
Get
Return True
End Get
End Property
Protected Overrides Async Function AddSimplifyAnnotationsAsync(document As Document, nodeToSimplify As SyntaxNode, cancellationToken As CancellationToken) As Task(Of Document)
Dim selectBlock = TryCast(nodeToSimplify, SelectBlockSyntax)
If selectBlock Is Nothing
Return Nothing
End If
Dim root = Await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(False)
Dim model = Await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(False)
Return Await AddMissingSwitchLabelsAsync(model, document, root, selectBlock).ConfigureAwait(False)
End Function
End Class
End Class
End Namespace
\ No newline at end of file
Imports System.Collections.Immutable
Imports System.Composition
Imports System.Threading
Imports Microsoft.CodeAnalysis.CodeActions
Imports System.Composition
Imports System.Runtime.InteropServices
Imports Microsoft.CodeAnalysis.CodeFixes
Imports Microsoft.CodeAnalysis.Diagnostics
Imports Microsoft.CodeAnalysis.CSharp.CodeFixes.PopulateSwitch
Imports Microsoft.CodeAnalysis.Formatting
Imports Microsoft.CodeAnalysis.Simplification
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.PopulateSwitch
<ExportCodeFixProvider(LanguageNames.VisualBasic, Name:=PredefinedCodeFixProviderNames.AddLabelsToSwitch), [Shared]>
<ExportCodeFixProvider(LanguageNames.VisualBasic, Name:=PredefinedCodeFixProviderNames.PopulateSwitch), [Shared]>
<ExtensionOrder(After:=PredefinedCodeFixProviderNames.AddOverloads)>
Partial Friend Class PopulateSwitchCodeFixProvider
Inherits CodeFixProvider
Partial Friend Class VisualBasicPopulateSwitchCodeFixProvider
Inherits AbstractPopulateSwitchCodeFixProvider
Public NotOverridable Overrides ReadOnly Property FixableDiagnosticIds As ImmutableArray(Of String)
Get
Return ImmutableArray.Create(IDEDiagnosticIds.PopulateSwitchDiagnosticId)
End Get
End Property
Protected Overrides Function GetSwitchExpression(node As SyntaxNode) As SyntaxNode
Private Shared Function GetSelectBlockNode(root As SyntaxNode, span As TextSpan) As SelectBlockSyntax
Dim token = root.FindToken(span.Start)
If Not token.Span.IntersectsWith(span)
Return Nothing
End If
Return DirectCast(root.FindNode(span), SelectBlockSyntax)
End Function
Public NotOverridable Overrides Async Function RegisterCodeFixesAsync(context As CodeFixContext) As Task
Dim document = context.Document
Dim span = context.Span
Dim cancellationToken = context.CancellationToken
Dim root = Await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(False)
Dim model = Await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(False)
Dim node = GetSelectBlockNode(root, span)
If node Is Nothing Then
Return
End If
context.RegisterCodeFix(
New MyCodeAction(
VBFeaturesResources.AddSelectLabels,
Function(c) AddMissingSwitchLabelsAsync(model, document, root, node)),
context.Diagnostics)
End Function
Private Shared Async Function AddMissingSwitchLabelsAsync(model As SemanticModel, document As Document, root As SyntaxNode, selectBlock As SelectBlockSyntax) As Task(Of Document)
Dim enumType = DirectCast(model.GetTypeInfo(selectBlock.SelectStatement.Expression).Type, INamedTypeSymbol)
Dim fullyQualifiedEnumType = enumType.ToDisplayString()
Dim containsElseCase = False
Dim caseLabels = New List(Of ExpressionSyntax)
For Each block In selectBlock.CaseBlocks
For Each caseSyntax In block.CaseStatement.Cases
Dim simpleCaseClause = TryCast(caseSyntax, SimpleCaseClauseSyntax)
If Not simpleCaseClause Is Nothing
caseLabels.Add(simpleCaseClause.Value)
Continue For
End If
If caseSyntax.IsKind(SyntaxKind.ElseCaseClause)
containsElseCase = True
End If
Next
Next
Dim numOfBlocksWithNoStatementsWithElse = 0
If containsElseCase
' skip the `Else` block
For i = selectBlock.CaseBlocks.Count - 2 To 0 Step -1
If Not selectBlock.CaseBlocks.ElementAt(i).Statements.Count = 0
' insert the values immediately below the last item with statements
numOfBlocksWithNoStatementsWithElse = i + 1
Exit For
End If
Next
End If
Dim missingLabels = GetMissingLabels(model, caseLabels, enumType)
Dim exitSelectStatement = SyntaxFactory.ExitSelectStatement()
Dim statements = SyntaxFactory.List(New List(Of StatementSyntax) From {exitSelectStatement})
Dim newSections = SyntaxFactory.List(selectBlock.CaseBlocks)
For Each label In missingLabels
Dim caseStatement = SyntaxFactory.CaseStatement(SyntaxFactory.SimpleCaseClause(SyntaxFactory.ParseExpression(fullyQualifiedEnumType + "." + label)))
Dim block = SyntaxFactory.CaseBlock(caseStatement, statements)
' ensure that the new cases are above the block with an else case, but below all other blocks
If containsElseCase
' this will not result in an InvalidOperationException because we know there
' are at least `numOfBlocksWithNoStatementsWithElse` items in the select block
newSections = newSections.Insert(numOfBlocksWithNoStatementsWithElse, block)
Else
newSections = newSections.Add(block)
End If
Next
If Not containsElseCase
newSections = newSections.Add(SyntaxFactory.CaseElseBlock(SyntaxFactory.CaseElseStatement(SyntaxFactory.ElseCaseClause()), statements))
End If
Dim newNode = selectBlock.WithCaseBlocks(newSections).WithAdditionalAnnotations(Formatter.Annotation, Simplifier.Annotation)
Dim newRoot = root.ReplaceNode(selectBlock, newNode)
' This doesn't work, possibly because of https://github.com/dotnet/roslyn/issues/473
' It creates `Namespace.EnumName.Member`, rather than `EnumName.Member`
' Then, it immediately tells me that it can be simplified
Return Await Simplifier.ReduceAsync(document.WithSyntaxRoot(newRoot)).ConfigureAwait(False)
Dim selectBlock = DirectCast(node, SelectBlockSyntax)
Return selectBlock.SelectStatement.Expression
End Function
Private Shared Function GetMissingLabels(model As SemanticModel, caseLabels As List(Of ExpressionSyntax), enumType As INamedTypeSymbol) As IEnumerable(Of String)
Protected Overrides Function GetMissingLabels(node As SyntaxNode, model As SemanticModel, enumType As INamedTypeSymbol, <Out> ByRef containsDefaultLabel As Boolean) As List(Of String)
Dim caseLabels = GetCaseLabels(DirectCast(node, SelectBlockSyntax), containsDefaultLabel)
Dim symbols As New List(Of ISymbol)
For Each label In caseLabels
' these are the labels like `MyEnum.EnumMember`
Dim memberAccessExpression = TryCast(label, MemberAccessExpressionSyntax)
If Not memberAccessExpression Is Nothing
......@@ -136,7 +36,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.PopulateSwitch
Continue For
End If
End If
' these are the labels like `EnumMember` (such as when using `Imports Namespace.MyEnum;`)
Dim identifierName = TryCast(label, IdentifierNameSyntax)
If Not identifierName Is Nothing
......@@ -171,17 +71,67 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.PopulateSwitch
Return missingLabels
End Function
Public NotOverridable Overrides Function GetFixAllProvider() As FixAllProvider
Return PopulateSwitchCodeFixAllProvider.Instance
Protected Overrides Function InsertPosition(sections As List(Of SyntaxNode)) As Integer
Dim cases = sections.OfType(Of CaseBlockSyntax).ToList()
Dim numOfBlocksWithNoStatementsWithElse = 0
' skip the `Else` block
For i = cases.Count - 2 To 0 Step -1
If Not cases.ElementAt(i).Statements.Count = 0
' insert the values immediately below the last item with statements
numOfBlocksWithNoStatementsWithElse = i + 1
Exit For
End If
Next
Return numOfBlocksWithNoStatementsWithElse
End Function
Protected Overrides Function GetSwitchSections(node As SyntaxNode) As List(Of SyntaxNode)
Dim selectBlock = DirectCast(node, SelectBlockSyntax)
Return New List(Of SyntaxNode)(selectBlock.CaseBlocks)
End Function
Protected Overrides Function NewSwitchNode(node As SyntaxNode, sections As List(Of SyntaxNode)) As SyntaxNode
Dim selectBlock = DirectCast(node, SelectBlockSyntax)
Return selectBlock.WithCaseBlocks(SyntaxFactory.List(sections)).WithAdditionalAnnotations(Formatter.Annotation, Simplifier.Annotation)
End Function
Protected Overrides Function GetSwitchStatementNode(root As SyntaxNode, span As TextSpan) As SyntaxNode
Dim token = root.FindToken(span.Start)
If Not token.Span.IntersectsWith(span)
Return Nothing
End If
Dim selectExpression = DirectCast(root.FindNode(span), ExpressionSyntax)
Return DirectCast(selectExpression.Parent.Parent, SelectBlockSyntax)
End Function
Private Function GetCaseLabels(selectBlock As SelectBlockSyntax, <Out> ByRef containsDefaultLabel As Boolean) As List(Of ExpressionSyntax)
containsDefaultLabel = False
Dim caseLabels = New List(Of ExpressionSyntax)
For Each block In selectBlock.CaseBlocks
For Each caseSyntax In block.CaseStatement.Cases
Dim simpleCaseClause = TryCast(caseSyntax, SimpleCaseClauseSyntax)
If Not simpleCaseClause Is Nothing
caseLabels.Add(simpleCaseClause.Value)
Continue For
End If
If caseSyntax.IsKind(SyntaxKind.ElseCaseClause)
containsDefaultLabel = True
End If
Next
Next
Return caseLabels
End Function
Private Class MyCodeAction
Inherits CodeAction.DocumentChangeAction
Public Sub New(title As String, createChangedDocument As Func(Of CancellationToken, Task(Of Document)))
MyBase.New(title, createChangedDocument)
End Sub
End Class
End Class
End Namespace
\ No newline at end of file
Imports System.Collections.Immutable
Imports System.Threading
Imports System.Runtime.InteropServices
Imports Microsoft.CodeAnalysis.Diagnostics
Imports Microsoft.CodeAnalysis.Diagnostics.PopulateSwitch
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic.Diagnostics.PopulateSwitch
<DiagnosticAnalyzer(LanguageNames.VisualBasic)>
Friend NotInheritable Class VisualBasicPopulateSwitchDiagnosticAnalyzer
Inherits PopulateSwitchDiagnosticAnalyzerBase(Of SyntaxKind)
Inherits AbstractPopulateSwitchDiagnosticAnalyzerBase(Of SyntaxKind)
Private Shared ReadOnly s_kindsOfInterest As ImmutableArray(Of SyntaxKind) = ImmutableArray.Create(SyntaxKind.SelectBlock)
Public Overrides ReadOnly Property SyntaxKindsOfInterest As ImmutableArray(Of SyntaxKind)
Get
Return s_kindsOfInterest
End Get
End Property
Protected Overrides Function GetDiagnosticSpan(node As SyntaxNode) As TextSpan
Return DirectCast(node, SelectBlockSyntax).Span
End Function
Protected Overrides Function SwitchIsFullyPopulated(model As SemanticModel, node As SyntaxNode, cancellationToken As CancellationToken) As Boolean
Protected Overrides Function GetCaseLabels(node As SyntaxNode, <Out> ByRef hasDefaultCase As Boolean) As List(Of SyntaxNode)
Dim selectBlock = DirectCast(node, SelectBlockSyntax)
Dim enumType = TryCast(model.GetTypeInfo(selectBlock.SelectStatement.Expression).Type, INamedTypeSymbol)
If enumType Is Nothing OrElse Not enumType.TypeKind = TypeKind.Enum
Return True
End If
' ignore enums marked with Flags per spec: https://github.com/dotnet/roslyn/issues/6766#issuecomment-156878851
For Each attribute In enumType.GetAttributes()
Dim containingClass = attribute.AttributeClass.ToDisplayString()
If containingClass = GetType(FlagsAttribute).FullName
Return True
End If
Next
Dim caseLabels As New List(Of ExpressionSyntax)
Dim hasElseCase = False
Dim caseLabels As New List(Of SyntaxNode)
hasDefaultCase = False
For Each block In selectBlock.CaseBlocks
For Each caseClause In block.CaseStatement.Cases
......@@ -51,42 +24,21 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Diagnostics.PopulateSwitch
End If
If caseClause.IsKind(SyntaxKind.ElseCaseClause)
hasElseCase = True
hasDefaultCase = True
End If
Next
Next
If Not hasElseCase
Return False
End If
Dim labelSymbols As New List(Of ISymbol)
For Each label In caseLabels
labelSymbols.Add(model.GetSymbolInfo(label).Symbol)
Next
For Each member In enumType.GetMembers()
' skip `.ctor` and `value__`
' member.IsImplicitlyDeclared doesn't work because `FileMode.value__` is apparently explicitly declared
Dim field = TryCast(member, IFieldSymbol)
If field Is Nothing OrElse (Not field.Type.SpecialType = SpecialType.None)
Continue For
End If
Return caseLabels
End Function
Dim switchHasSymbol = False
For Each symbol In labelSymbols
If symbol Is member
switchHasSymbol = True
Exit For
End If
Next
Private Shared ReadOnly s_kindsOfInterest As ImmutableArray(Of SyntaxKind) = ImmutableArray.Create(SyntaxKind.SelectBlock)
Protected Overrides ReadOnly Property SyntaxKindsOfInterest As ImmutableArray(Of SyntaxKind) = s_kindsOfInterest
If Not switchHasSymbol
Return False
End If
Next
Protected Overrides Function GetExpression(node As SyntaxNode) As SyntaxNode
Return True
Dim selectBlock = DirectCast(node, SelectBlockSyntax)
Return selectBlock.SelectStatement.Expression
End Function
End Class
End Namespace
......@@ -384,8 +384,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration
End Function
Public Overrides Function DefaultSwitchSection(statements As IEnumerable(Of SyntaxNode)) As SyntaxNode
Return SyntaxFactory.CaseBlock(
SyntaxFactory.CaseStatement(SyntaxFactory.ElseCaseClause()),
Return SyntaxFactory.CaseElseBlock(
SyntaxFactory.CaseElseStatement(SyntaxFactory.ElseCaseClause()),
GetStatementList(statements))
End Function
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册