@@ -97,16 +97,19 @@ protected override void InitializeWorker(CompilationStartAnalysisContext context
9797 var boolType = context . Compilation . GetSpecialType ( SpecialType . System_Boolean ) ;
9898 var guidType = context . Compilation . GetOrCreateTypeByMetadataName ( WellKnownTypeNames . SystemGuid ) ;
9999
100- var builder = ImmutableHashSet . CreateBuilder < INamedTypeSymbol > ( ) ;
101- builder . AddIfNotNull ( charType ) ;
102- builder . AddIfNotNull ( boolType ) ;
103- builder . AddIfNotNull ( stringType ) ;
104- builder . AddIfNotNull ( guidType ) ;
105- var invariantToStringTypes = builder . ToImmutableHashSet ( ) ;
100+ var nullableT = context . Compilation . GetSpecialType ( SpecialType . System_Nullable_T ) ;
101+ var invariantToStringMethodsBuilder = ImmutableHashSet . CreateBuilder < IMethodSymbol > ( ) ;
102+ AddValidToStringMethods ( invariantToStringMethodsBuilder , nullableT , charType ) ;
103+ AddValidToStringMethods ( invariantToStringMethodsBuilder , nullableT , boolType ) ;
104+ AddValidToStringMethods ( invariantToStringMethodsBuilder , nullableT , stringType ) ;
105+ AddValidToStringMethods ( invariantToStringMethodsBuilder , nullableT , guidType ) ;
106+ var invariantToStringMethods = invariantToStringMethodsBuilder . ToImmutable ( ) ;
106107
107- var dateTimeType = context . Compilation . GetOrCreateTypeByMetadataName ( WellKnownTypeNames . SystemDateTime ) ;
108- var dateTimeOffsetType = context . Compilation . GetOrCreateTypeByMetadataName ( WellKnownTypeNames . SystemDateTimeOffset ) ;
109- var timeSpanType = context . Compilation . GetOrCreateTypeByMetadataName ( WellKnownTypeNames . SystemTimeSpan ) ;
108+ var dateTimeToStringFormatMethod = GetToStringWithFormatStringParameter ( context . Compilation . GetOrCreateTypeByMetadataName ( WellKnownTypeNames . SystemDateTime ) ) ;
109+
110+ var dateTimeOffsetToStringFormatMethod = GetToStringWithFormatStringParameter ( context . Compilation . GetOrCreateTypeByMetadataName ( WellKnownTypeNames . SystemDateTimeOffset ) ) ;
111+
112+ var timeSpanToStringFormatMethod = GetToStringWithFormatStringParameter ( context . Compilation . GetOrCreateTypeByMetadataName ( WellKnownTypeNames . SystemTimeSpan ) ) ;
110113
111114 var stringFormatMembers = stringType . GetMembers ( "Format" ) . OfType < IMethodSymbol > ( ) ;
112115
@@ -145,6 +148,9 @@ protected override void InitializeWorker(CompilationStartAnalysisContext context
145148 var installedUICulturePropertyOfComputerInfoType = computerInfoType ? . GetMembers ( "InstalledUICulture" ) . OfType < IPropertySymbol > ( ) . FirstOrDefault ( ) ;
146149
147150 var obsoleteAttributeType = context . Compilation . GetOrCreateTypeByMetadataName ( WellKnownTypeNames . SystemObsoleteAttribute ) ;
151+
152+ var guidParseMethods = guidType ? . GetMembers ( "Parse" ) ?? ImmutableArray < ISymbol > . Empty ;
153+
148154 #endregion
149155
150156 context . RegisterOperationAction ( oaContext =>
@@ -157,8 +163,8 @@ protected override void InitializeWorker(CompilationStartAnalysisContext context
157163 targetMethod . ContainingType . IsErrorType ( ) ||
158164 ( activatorType != null && activatorType . Equals ( targetMethod . ContainingType ) ) ||
159165 ( resourceManagerType != null && resourceManagerType . Equals ( targetMethod . ContainingType ) ) ||
160- IsValidToStringCall ( invocationExpression , invariantToStringTypes , dateTimeType , dateTimeOffsetType , timeSpanType ) ||
161- IsValidParseCall ( invocationExpression , guidType ) )
166+ IsValidToStringCall ( invocationExpression , invariantToStringMethods , dateTimeToStringFormatMethod , dateTimeOffsetToStringFormatMethod , timeSpanToStringFormatMethod ) ||
167+ IsValidParseCall ( invocationExpression , guidParseMethods ) )
162168 {
163169 return ;
164170 }
@@ -280,6 +286,25 @@ protected override void InitializeWorker(CompilationStartAnalysisContext context
280286 } , OperationKind . Invocation ) ;
281287 }
282288
289+ private static IMethodSymbol ? GetToStringWithFormatStringParameter ( INamedTypeSymbol ? type )
290+ {
291+ return type ? . GetMembers ( "ToString" ) . OfType < IMethodSymbol > ( ) . FirstOrDefault ( s => s . Parameters is [ { Type . SpecialType : SpecialType . System_String } ] ) ;
292+ }
293+
294+ private static void AddValidToStringMethods ( ImmutableHashSet < IMethodSymbol > . Builder validToStringMethodsBuilder , INamedTypeSymbol nullableT , INamedTypeSymbol ? type )
295+ {
296+ if ( type is null )
297+ {
298+ return ;
299+ }
300+
301+ validToStringMethodsBuilder . AddRange ( GetToStringMethods ( type ) ) ;
302+ validToStringMethodsBuilder . AddRange ( GetToStringMethods ( nullableT . Construct ( type ) ) ) ;
303+
304+ static IEnumerable < IMethodSymbol > GetToStringMethods ( INamedTypeSymbol namedTypeSymbol )
305+ => namedTypeSymbol . GetMembers ( "ToString" ) . OfType < IMethodSymbol > ( ) . WhereNotNull ( ) ;
306+ }
307+
283308 private static IEnumerable < int > GetIndexesOfParameterType ( IMethodSymbol targetMethod , INamedTypeSymbol formatProviderType )
284309 {
285310 return targetMethod . Parameters
@@ -293,17 +318,11 @@ private static ParameterInfo GetParameterInfo(INamedTypeSymbol type, bool isArra
293318 return ParameterInfo . GetParameterInfo ( type , isArray , arrayRank , isParams ) ;
294319 }
295320
296- private static bool IsValidToStringCall ( IInvocationOperation invocationOperation , ImmutableHashSet < INamedTypeSymbol > invariantToStringTypes ,
297- INamedTypeSymbol ? dateTimeType , INamedTypeSymbol ? dateTimeOffsetType , INamedTypeSymbol ? timeSpanType )
321+ private static bool IsValidToStringCall ( IInvocationOperation invocationOperation , ImmutableHashSet < IMethodSymbol > validToStringMethods ,
322+ IMethodSymbol ? dateTimeToStringFormatMethod , IMethodSymbol ? dateTimeOffsetToStringFormatMethod , IMethodSymbol ? timeSpanToStringFormatMethod )
298323 {
299324 var targetMethod = invocationOperation . TargetMethod ;
300-
301- if ( targetMethod . Name != "ToString" )
302- {
303- return false ;
304- }
305-
306- if ( invariantToStringTypes . Contains ( UnwrapNullableValueTypes ( targetMethod . ContainingType ) ) )
325+ if ( validToStringMethods . Contains ( targetMethod ) )
307326 {
308327 return true ;
309328 }
@@ -316,44 +335,20 @@ private static bool IsValidToStringCall(IInvocationOperation invocationOperation
316335 }
317336
318337 // Handle invariant format specifiers, see https://github.com/dotnet/roslyn-analyzers/issues/3507
319- if ( ( dateTimeType != null && targetMethod . ContainingType . Equals ( dateTimeType ) ) ||
320- ( dateTimeOffsetType != null && targetMethod . ContainingType . Equals ( dateTimeOffsetType ) ) )
338+ if ( targetMethod . Equals ( dateTimeToStringFormatMethod , SymbolEqualityComparer . Default ) || targetMethod . Equals ( dateTimeOffsetToStringFormatMethod , SymbolEqualityComparer . Default ) )
321339 {
322340 return s_dateInvariantFormats . Contains ( format ) ;
323341 }
324342
325- if ( timeSpanType != null && targetMethod . ContainingType . Equals ( timeSpanType ) )
343+ if ( targetMethod . Equals ( timeSpanToStringFormatMethod , SymbolEqualityComparer . Default ) )
326344 {
327345 return format == "c" ;
328346 }
329347
330348 return false ;
331-
332- // Local functions
333-
334- static INamedTypeSymbol UnwrapNullableValueTypes ( INamedTypeSymbol typeSymbol )
335- {
336- if ( typeSymbol . IsNullableValueType ( ) && typeSymbol . TypeArguments [ 0 ] is INamedTypeSymbol nullableTypeArgument )
337- return nullableTypeArgument ;
338- return typeSymbol ;
339- }
340349 }
341350
342- private static bool IsValidParseCall ( IInvocationOperation invocationOperation , INamedTypeSymbol ? guidType )
343- {
344- var targetMethod = invocationOperation . TargetMethod ;
345-
346- if ( targetMethod . Name != "Parse" )
347- {
348- return false ;
349- }
350-
351- if ( targetMethod . ContainingType . Equals ( guidType ) )
352- {
353- return true ;
354- }
355-
356- return false ;
357- }
351+ private static bool IsValidParseCall ( IInvocationOperation invocationOperation , ImmutableArray < ISymbol > guidParseMethods )
352+ => guidParseMethods . Contains ( invocationOperation . TargetMethod ) ;
358353 }
359354}
0 commit comments