Skip to content

Commit 7d75ca2

Browse files
authored
Merge pull request #6356 from Youssef1313/ca1305
Refactor CA1305 to compare symbols
2 parents 0ec3193 + 6edc75e commit 7d75ca2

File tree

2 files changed

+44
-66
lines changed

2 files changed

+44
-66
lines changed

src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/SpecifyIFormatProvider.cs

Lines changed: 43 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -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
}

src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/SpecifyIFormatProviderTests.cs

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -952,25 +952,8 @@ public async Task CA1305_GuidParse_NoDiagnosticsAsync()
952952
{
953953
await new VerifyCS.Test
954954
{
955-
ReferenceAssemblies = new ReferenceAssemblies(""), // workaround for lack of .NET 7 Preview 4 reference assemblies
955+
ReferenceAssemblies = ReferenceAssemblies.Net.Net70,
956956
TestCode = @"
957-
namespace System
958-
{
959-
public class Object { }
960-
public abstract class ValueType { }
961-
public struct Void { }
962-
public class String { }
963-
public interface IFormatProvider { }
964-
public struct Guid
965-
{
966-
public static Guid Parse(string s) => default;
967-
public static Guid Parse(string s, IFormatProvider provider) => default;
968-
}
969-
}
970-
namespace System.Globalization
971-
{
972-
public class CultureInfo : IFormatProvider { }
973-
}
974957
namespace Test
975958
{
976959
using System;

0 commit comments

Comments
 (0)