@@ -31,22 +31,12 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
3131 var statement = root . FindNode ( diagnosticSpan ) ;
3232 context . RegisterCodeFix (
3333 CodeAction . Create ( VSDiagnosticsResources . SwitchDoesNotHandleAllEnumOptionsCodeFixTitle ,
34- x => AddMissingCaseAsync ( context . Document , root , statement ) ,
34+ x => AddMissingCaseAsync ( context . Document , ( CompilationUnitSyntax ) root , statement ) ,
3535 SwitchDoesNotHandleAllEnumOptionsAnalyzer . Rule . Id ) , diagnostic ) ;
3636 }
3737
38- private async Task < Solution > AddMissingCaseAsync ( Document document , SyntaxNode root , SyntaxNode statement )
38+ private async Task < Solution > AddMissingCaseAsync ( Document document , CompilationUnitSyntax root , SyntaxNode statement )
3939 {
40- var qualifier = "System." ;
41- var usingSystemDirective = ( ( CompilationUnitSyntax ) root ) . Usings . Where ( u => u . Name is IdentifierNameSyntax ) . FirstOrDefault ( u => ( ( IdentifierNameSyntax ) u . Name ) . Identifier . ValueText == "System" ) ;
42-
43- if ( usingSystemDirective != null )
44- {
45- qualifier = usingSystemDirective . Alias == null
46- ? string . Empty
47- : usingSystemDirective . Alias . Name . Identifier . ValueText + "." ;
48- }
49-
5040 var semanticModel = await document . GetSemanticModelAsync ( ) ;
5141
5242 var switchBlock = ( SwitchStatementSyntax ) statement ;
@@ -57,41 +47,35 @@ private async Task<Solution> AddMissingCaseAsync(Document document, SyntaxNode r
5747 . Select ( l => l . Value )
5848 . ToList ( ) ;
5949
60- // these are the labels like `MyEnum.EnumMember`
61- var labels = caseLabels
62- . OfType < MemberAccessExpressionSyntax > ( )
63- . Select ( l => l . Name . Identifier . ValueText )
64- . ToList ( ) ;
65-
66- // these are the labels like `EnumMember` (such as when using `using static Namespace.MyEnum;`)
67- labels . AddRange ( caseLabels . OfType < IdentifierNameSyntax > ( ) . Select ( l => l . Identifier . ValueText ) ) ;
50+ var missingLabels = GetMissingLabels ( caseLabels , enumType ) ;
6851
6952 // use simplified form if there are any in simplified form or if there are not any labels at all
70- var useSimplifiedForm = caseLabels . OfType < IdentifierNameSyntax > ( ) . Any ( ) ||
71- ! caseLabels . OfType < MemberAccessExpressionSyntax > ( ) . Any ( ) ;
72-
73- // don't create members like ".ctor"
74- var missingLabels = enumType . MemberNames . Except ( labels ) . Where ( m => ! m . StartsWith ( "." ) ) ;
53+ var useSimplifiedForm = ( caseLabels . OfType < IdentifierNameSyntax > ( ) . Any ( ) ||
54+ ! caseLabels . OfType < MemberAccessExpressionSyntax > ( ) . Any ( ) ) &&
55+ EnumIsUsingStatic ( root , enumType ) ;
7556
76- var newSections = SyntaxFactory . List ( switchBlock . Sections ) ;
57+ var qualifier = GetQualifierForException ( root ) ;
7758
7859 var notImplementedException =
7960 SyntaxFactory . ThrowStatement ( SyntaxFactory . ParseExpression ( $ " new { qualifier } NotImplementedException()") )
8061 . WithAdditionalAnnotations ( Simplifier . Annotation ) ;
62+ var statements = SyntaxFactory . List ( new List < StatementSyntax > { notImplementedException } ) ;
63+
64+ var newSections = SyntaxFactory . List ( switchBlock . Sections ) ;
8165
8266 foreach ( var label in missingLabels )
8367 {
68+ // ReSharper disable once PossibleNullReferenceException
8469 var caseLabel =
8570 SyntaxFactory . CaseSwitchLabel (
8671 SyntaxFactory . ParseExpression ( useSimplifiedForm ? $ " { label } " : $ " { enumType . Name } .{ label } ")
8772 . WithTrailingTrivia ( SyntaxFactory . ParseTrailingTrivia ( Environment . NewLine ) ) ) ;
88-
89- var statements = SyntaxFactory . List ( new List < StatementSyntax > { notImplementedException . WithAdditionalAnnotations ( Simplifier . Annotation ) } ) ;
9073
9174 var section =
9275 SyntaxFactory . SwitchSection ( SyntaxFactory . List ( new List < SwitchLabelSyntax > { caseLabel } ) , statements )
9376 . WithAdditionalAnnotations ( Formatter . Annotation ) ;
9477
78+ // ensure that the new cases are above the default case
9579 newSections = newSections . Insert ( 0 , section ) ;
9680 }
9781
@@ -103,5 +87,58 @@ private async Task<Solution> AddMissingCaseAsync(Document document, SyntaxNode r
10387 var newDocument = await Simplifier . ReduceAsync ( document . WithSyntaxRoot ( newRoot ) ) ;
10488 return newDocument . Project . Solution ;
10589 }
90+
91+ private bool EnumIsUsingStatic ( CompilationUnitSyntax root , INamedTypeSymbol enumType )
92+ {
93+ var fullyQualifiedName = enumType . Name ;
94+
95+ var containingNamespace = enumType . ContainingNamespace ;
96+ while ( ! string . IsNullOrEmpty ( containingNamespace . Name ) )
97+ {
98+ fullyQualifiedName = fullyQualifiedName . Insert ( 0 , containingNamespace . Name + "." ) ;
99+ containingNamespace = containingNamespace . ContainingNamespace ;
100+ }
101+
102+ return root . Usings . Any ( u =>
103+ {
104+ if ( ! u . StaticKeyword . IsKind ( SyntaxKind . StaticKeyword ) ) { return false ; }
105+
106+ var name = u . Name as QualifiedNameSyntax ;
107+ if ( name == null ) { return false ; }
108+
109+ return new string ( name . GetText ( ) . ToString ( ) . ToCharArray ( ) . Where ( c => ! char . IsWhiteSpace ( c ) ) . ToArray ( ) ) == fullyQualifiedName ;
110+ } ) ;
111+ }
112+
113+ private IEnumerable < string > GetMissingLabels ( List < ExpressionSyntax > caseLabels , INamedTypeSymbol enumType )
114+ {
115+ // these are the labels like `MyEnum.EnumMember`
116+ var labels = caseLabels
117+ . OfType < MemberAccessExpressionSyntax > ( )
118+ . Select ( l => l . Name . Identifier . ValueText )
119+ . ToList ( ) ;
120+
121+ // these are the labels like `EnumMember` (such as when using `using static Namespace.MyEnum;`)
122+ labels . AddRange ( caseLabels . OfType < IdentifierNameSyntax > ( ) . Select ( l => l . Identifier . ValueText ) ) ;
123+
124+ // don't create members like ".ctor"
125+ return enumType . MemberNames . Except ( labels ) . Where ( m => ! m . StartsWith ( "." ) ) ;
126+ }
127+
128+ private string GetQualifierForException ( CompilationUnitSyntax root )
129+ {
130+ var qualifier = "System." ;
131+ var usingSystemDirective =
132+ root . Usings . Where ( u => u . Name is IdentifierNameSyntax )
133+ . FirstOrDefault ( u => ( ( IdentifierNameSyntax ) u . Name ) . Identifier . ValueText == "System" ) ;
134+
135+ if ( usingSystemDirective != null )
136+ {
137+ qualifier = usingSystemDirective . Alias == null
138+ ? string . Empty
139+ : usingSystemDirective . Alias . Name . Identifier . ValueText + "." ;
140+ }
141+ return qualifier ;
142+ }
106143 }
107144}
0 commit comments