1313using Microsoft . CodeAnalysis . CSharp ;
1414using Microsoft . CodeAnalysis . CSharp . Syntax ;
1515using Microsoft . CodeAnalysis . Diagnostics ;
16+ using System ;
1617using System . Collections . Immutable ;
17- using System . Linq ;
1818
1919namespace Neo . SmartContract . Analyzer
2020{
@@ -23,11 +23,6 @@ public class EnumMethodsUsageAnalyzer : DiagnosticAnalyzer
2323 {
2424 public const string DiagnosticId = "NC4025" ;
2525
26- private readonly string [ ] _unsupportedEnumMethods = {
27- "Format" ,
28- "GetUnderlyingType"
29- } ;
30-
3126 private static readonly DiagnosticDescriptor Rule = new (
3227 DiagnosticId ,
3328 "Unsupported Enum method is used" ,
@@ -50,16 +45,136 @@ private void AnalyzeSyntax(SyntaxNodeAnalysisContext context)
5045 {
5146 if ( context . Node is not InvocationExpressionSyntax invocationExpression ) return ;
5247
53- var methodSymbol = context . SemanticModel . GetSymbolInfo ( invocationExpression ) . Symbol as IMethodSymbol ;
54-
55- if ( methodSymbol is not { ContainingType . SpecialType : SpecialType . System_Enum } ||
56- ! _unsupportedEnumMethods . Contains ( methodSymbol . Name ) ) return ;
48+ if ( context . SemanticModel . GetSymbolInfo ( invocationExpression ) . Symbol is not IMethodSymbol methodSymbol ||
49+ methodSymbol . ContainingType . SpecialType != SpecialType . System_Enum ||
50+ IsSupportedEnumMethod ( methodSymbol ) )
51+ return ;
5752
5853 var diagnostic = Diagnostic . Create ( Rule ,
5954 invocationExpression . GetLocation ( ) ,
6055 methodSymbol . Name ) ;
6156
6257 context . ReportDiagnostic ( diagnostic ) ;
6358 }
59+
60+ private static bool IsSupportedEnumMethod ( IMethodSymbol method )
61+ {
62+ if ( ! method . IsStatic )
63+ {
64+ return method . Name switch
65+ {
66+ nameof ( Enum . ToString ) => method . Parameters . Length == 0 ,
67+ nameof ( Enum . HasFlag ) => IsSpecialParameter ( method , 0 , SpecialType . System_Enum ) &&
68+ method . Parameters . Length == 1 ,
69+ _ => false
70+ } ;
71+ }
72+
73+ if ( method . IsGenericMethod )
74+ return IsSupportedGenericMethod ( method ) ;
75+
76+ return method . Name switch
77+ {
78+ nameof ( Enum . Parse ) => IsParseSignature ( method ) ,
79+ nameof ( Enum . TryParse ) => IsTryParseSignature ( method ) ,
80+ nameof ( Enum . GetNames ) or nameof ( Enum . GetValues ) => method . Parameters . Length == 1 && IsSystemTypeParameter ( method , 0 ) ,
81+ nameof ( Enum . IsDefined ) => method . Parameters . Length == 2 &&
82+ IsSystemTypeParameter ( method , 0 ) &&
83+ ( IsSpecialParameter ( method , 1 , SpecialType . System_Object ) ||
84+ IsSpecialParameter ( method , 1 , SpecialType . System_String ) ) ,
85+ nameof ( Enum . GetName ) => method . Parameters . Length == 2 &&
86+ IsSystemTypeParameter ( method , 0 ) &&
87+ IsSpecialParameter ( method , 1 , SpecialType . System_Object ) ,
88+ _ => false
89+ } ;
90+ }
91+
92+ private static bool IsSupportedGenericMethod ( IMethodSymbol method )
93+ {
94+ if ( method . TypeArguments . Length != 1 )
95+ return false ;
96+
97+ var enumType = method . TypeArguments [ 0 ] ;
98+ return method . Name switch
99+ {
100+ nameof ( Enum . Parse ) => IsGenericParseSignature ( method ) ,
101+ nameof ( Enum . TryParse ) => IsGenericTryParseSignature ( method , enumType ) ,
102+ nameof ( Enum . GetNames ) or nameof ( Enum . GetValues ) => method . Parameters . Length == 0 ,
103+ nameof ( Enum . GetName ) => method . Parameters . Length == 1 &&
104+ IsParameter ( method , 0 , enumType ) ,
105+ _ => false
106+ } ;
107+ }
108+
109+ private static bool IsParseSignature ( IMethodSymbol method )
110+ {
111+ return method . Parameters . Length is 2 or 3 &&
112+ IsSystemTypeParameter ( method , 0 ) &&
113+ IsSpecialParameter ( method , 1 , SpecialType . System_String ) &&
114+ ( method . Parameters . Length == 2 ||
115+ IsSpecialParameter ( method , 2 , SpecialType . System_Boolean ) ) ;
116+ }
117+
118+ private static bool IsTryParseSignature ( IMethodSymbol method )
119+ {
120+ return method . Parameters . Length is 3 or 4 &&
121+ IsSystemTypeParameter ( method , 0 ) &&
122+ IsSpecialParameter ( method , 1 , SpecialType . System_String ) &&
123+ ( method . Parameters . Length == 3 ||
124+ IsSpecialParameter ( method , 2 , SpecialType . System_Boolean ) ) &&
125+ IsSpecialParameter (
126+ method ,
127+ method . Parameters . Length - 1 ,
128+ SpecialType . System_Object ,
129+ RefKind . Out ) ;
130+ }
131+
132+ private static bool IsGenericParseSignature ( IMethodSymbol method )
133+ {
134+ return method . Parameters . Length is 1 or 2 &&
135+ IsSpecialParameter ( method , 0 , SpecialType . System_String ) &&
136+ ( method . Parameters . Length == 1 ||
137+ IsSpecialParameter ( method , 1 , SpecialType . System_Boolean ) ) ;
138+ }
139+
140+ private static bool IsGenericTryParseSignature ( IMethodSymbol method , ITypeSymbol enumType )
141+ {
142+ return method . Parameters . Length is 2 or 3 &&
143+ IsSpecialParameter ( method , 0 , SpecialType . System_String ) &&
144+ ( method . Parameters . Length == 2 ||
145+ IsSpecialParameter ( method , 1 , SpecialType . System_Boolean ) ) &&
146+ IsParameter ( method , method . Parameters . Length - 1 , enumType , RefKind . Out ) ;
147+ }
148+
149+ private static bool IsSystemTypeParameter ( IMethodSymbol method , int index )
150+ {
151+ if ( index >= method . Parameters . Length || method . Parameters [ index ] . RefKind != RefKind . None )
152+ return false ;
153+
154+ return method . Parameters [ index ] . Type is INamedTypeSymbol { Name : "Type" } type &&
155+ type . ContainingNamespace . ToDisplayString ( ) == "System" ;
156+ }
157+
158+ private static bool IsSpecialParameter (
159+ IMethodSymbol method ,
160+ int index ,
161+ SpecialType type ,
162+ RefKind refKind = RefKind . None )
163+ {
164+ return index < method . Parameters . Length &&
165+ method . Parameters [ index ] . RefKind == refKind &&
166+ method . Parameters [ index ] . Type . SpecialType == type ;
167+ }
168+
169+ private static bool IsParameter (
170+ IMethodSymbol method ,
171+ int index ,
172+ ITypeSymbol type ,
173+ RefKind refKind = RefKind . None )
174+ {
175+ return index < method . Parameters . Length &&
176+ method . Parameters [ index ] . RefKind == refKind &&
177+ SymbolEqualityComparer . Default . Equals ( method . Parameters [ index ] . Type , type ) ;
178+ }
64179 }
65180}
0 commit comments