From 89b4d16f9fa2d2b6353e2bc4f1c2daf0e5891c70 Mon Sep 17 00:00:00 2001 From: NewellClark Date: Fri, 14 May 2021 15:39:48 -0400 Subject: [PATCH 1/3] Add failing tests --- .../Runtime/SpecifyIFormatProviderTests.cs | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/SpecifyIFormatProviderTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/SpecifyIFormatProviderTests.cs index 77e964fbae..15d19c8f82 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/SpecifyIFormatProviderTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/SpecifyIFormatProviderTests.cs @@ -945,6 +945,36 @@ End Class "); } + [Fact] + public async Task CA1305_NullableInvariantTypes_NoDiagnostic() + { + await VerifyCS.VerifyAnalyzerAsync(@" +using System; +public class SomeClass +{ + private char? _char; + private bool? _bool; + private Guid? _guid; + + public string SomeMethod() + { + return _char.ToString() + _bool.ToString() + _guid.ToString() + _guid.ToString(""D""); + } +}"); + + await VerifyVB.VerifyAnalyzerAsync(@" +Imports System +Public Class SomeClass + Private _char As Char? + Private _bool As Boolean? + Private _guid As Guid? + + Public Function SomeMethod() As String + Return _char.ToString() & _bool.ToString() & _guid.ToString() & _guid.ToString(""D"") + End Function +End Class"); + } + [Theory, WorkItem(3507, "https://github.com/dotnet/roslyn-analyzers/issues/3507")] [InlineData("DateTime")] [InlineData("DateTimeOffset")] From b1c8ce533a438aca3c7de972d2bec1e156375539 Mon Sep 17 00:00:00 2001 From: NewellClark Date: Fri, 14 May 2021 16:14:23 -0400 Subject: [PATCH 2/3] Fix false positive on nullable invariant types --- .../Runtime/SpecifyIFormatProvider.cs | 7 +++++++ .../Runtime/SpecifyIFormatProviderTests.cs | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/SpecifyIFormatProvider.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/SpecifyIFormatProvider.cs index da5d624249..fd9a8838b8 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/SpecifyIFormatProvider.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/SpecifyIFormatProvider.cs @@ -86,12 +86,19 @@ protected override void InitializeWorker(CompilationStartAnalysisContext context var charType = context.Compilation.GetSpecialType(SpecialType.System_Char); var boolType = context.Compilation.GetSpecialType(SpecialType.System_Boolean); var guidType = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemGuid); + var nullableTType = context.Compilation.GetSpecialType(SpecialType.System_Nullable_T); + INamedTypeSymbol? GetNullableType(INamedTypeSymbol? typeSymbol) => typeSymbol is not null ? nullableTType?.Construct(typeSymbol) : null; var builder = ImmutableHashSet.CreateBuilder(); + builder.AddIfNotNull(charType); + builder.AddIfNotNull(GetNullableType(charType)); builder.AddIfNotNull(boolType); + builder.AddIfNotNull(GetNullableType(boolType)); builder.AddIfNotNull(stringType); builder.AddIfNotNull(guidType); + builder.AddIfNotNull(GetNullableType(guidType)); + var invariantToStringTypes = builder.ToImmutableHashSet(); var dateTimeType = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemDateTime); diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/SpecifyIFormatProviderTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/SpecifyIFormatProviderTests.cs index 15d19c8f82..29eff5c638 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/SpecifyIFormatProviderTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/SpecifyIFormatProviderTests.cs @@ -958,7 +958,7 @@ public class SomeClass public string SomeMethod() { - return _char.ToString() + _bool.ToString() + _guid.ToString() + _guid.ToString(""D""); + return _char.ToString() + _bool.ToString() + _guid.ToString(); } }"); @@ -970,7 +970,7 @@ Private _bool As Boolean? Private _guid As Guid? Public Function SomeMethod() As String - Return _char.ToString() & _bool.ToString() & _guid.ToString() & _guid.ToString(""D"") + Return _char.ToString() & _bool.ToString() & _guid.ToString() End Function End Class"); } From 3a7a6550e04bebe28d0a48fd8a7b8d3b6bcb3330 Mon Sep 17 00:00:00 2001 From: NewellClark Date: Mon, 17 May 2021 13:56:28 -0400 Subject: [PATCH 3/3] Simplify analyzer --- .../Runtime/SpecifyIFormatProvider.cs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/SpecifyIFormatProvider.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/SpecifyIFormatProvider.cs index fd9a8838b8..0d9ed2434a 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/SpecifyIFormatProvider.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/SpecifyIFormatProvider.cs @@ -86,19 +86,12 @@ protected override void InitializeWorker(CompilationStartAnalysisContext context var charType = context.Compilation.GetSpecialType(SpecialType.System_Char); var boolType = context.Compilation.GetSpecialType(SpecialType.System_Boolean); var guidType = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemGuid); - var nullableTType = context.Compilation.GetSpecialType(SpecialType.System_Nullable_T); - INamedTypeSymbol? GetNullableType(INamedTypeSymbol? typeSymbol) => typeSymbol is not null ? nullableTType?.Construct(typeSymbol) : null; var builder = ImmutableHashSet.CreateBuilder(); - builder.AddIfNotNull(charType); - builder.AddIfNotNull(GetNullableType(charType)); builder.AddIfNotNull(boolType); - builder.AddIfNotNull(GetNullableType(boolType)); builder.AddIfNotNull(stringType); builder.AddIfNotNull(guidType); - builder.AddIfNotNull(GetNullableType(guidType)); - var invariantToStringTypes = builder.ToImmutableHashSet(); var dateTimeType = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemDateTime); @@ -271,7 +264,7 @@ private static bool IsValidToStringCall(IInvocationOperation invocationOperation return false; } - if (invariantToStringTypes.Contains(targetMethod.ContainingType)) + if (invariantToStringTypes.Contains(UnwrapNullableValueTypes(targetMethod.ContainingType))) { return true; } @@ -296,6 +289,15 @@ private static bool IsValidToStringCall(IInvocationOperation invocationOperation } return false; + + // Local functions + + static INamedTypeSymbol UnwrapNullableValueTypes(INamedTypeSymbol typeSymbol) + { + if (typeSymbol.IsNullableValueType() && typeSymbol.TypeArguments[0] is INamedTypeSymbol nullableTypeArgument) + return nullableTypeArgument; + return typeSymbol; + } } } }