From 233b8d46f9ba672a3aaeb6cea845abab0ba525c3 Mon Sep 17 00:00:00 2001 From: Collin Alpert Date: Thu, 14 Mar 2024 00:09:57 +0100 Subject: [PATCH 1/2] Do not suggest passing IFormatProvider to certain Convert methods --- .../Runtime/SpecifyIFormatProvider.cs | 26 +- .../Runtime/SpecifyIFormatProviderTests.cs | 530 ++++++++++++++++++ 2 files changed, 553 insertions(+), 3 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/SpecifyIFormatProvider.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/SpecifyIFormatProvider.cs index d634edbc7c..5bb0d61baa 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/SpecifyIFormatProvider.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/SpecifyIFormatProvider.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; @@ -147,8 +148,6 @@ protected override void InitializeWorker(CompilationStartAnalysisContext context GetParameterInfo(stringType), GetParameterInfo(objectType, isArray: true, arrayRank: 1, isParams: true)); - var currentCultureProperty = cultureInfoType.GetMembers("CurrentCulture").OfType().FirstOrDefault(); - var invariantCultureProperty = cultureInfoType.GetMembers("InvariantCulture").OfType().FirstOrDefault(); var currentUICultureProperty = cultureInfoType.GetMembers("CurrentUICulture").OfType().FirstOrDefault(); var installedUICultureProperty = cultureInfoType.GetMembers("InstalledUICulture").OfType().FirstOrDefault(); @@ -165,6 +164,27 @@ protected override void InitializeWorker(CompilationStartAnalysisContext context var guidParseMethods = guidType?.GetMembers("Parse") ?? ImmutableArray.Empty; + var convertType = typeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemConvert); + var superfluousConvertToStringFormatProviderOverloads = convertType?.GetMembers(nameof(Convert.ToString)) + .OfType() + .Where(m => m.IsStatic + && m.Parameters is [{ Type.SpecialType: SpecialType.System_String or SpecialType.System_Boolean or SpecialType.System_Char }, var possibleFormatProvider] + && possibleFormatProvider.Type.Equals(iformatProviderType, SymbolEqualityComparer.Default)) ?? []; + var superfluousConvertToToCharFormatProviderOverloads = convertType?.GetMembers(nameof(Convert.ToChar)) + .OfType() + .Where(m => m.IsStatic + && m.Parameters is [{ Type.SpecialType: SpecialType.System_String }, var possibleFormatProvider] + && possibleFormatProvider.Type.Equals(iformatProviderType, SymbolEqualityComparer.Default)) ?? []; + var superfluousConvertToBooleanFormatProviderOverloads = convertType?.GetMembers(nameof(Convert.ToBoolean)) + .OfType() + .Where(m => m.IsStatic + && m.Parameters is [{ Type.SpecialType: SpecialType.System_String }, var possibleFormatProvider] + && possibleFormatProvider.Type.Equals(iformatProviderType, SymbolEqualityComparer.Default)) ?? []; + ImmutableHashSet superfluousFormatProviderOverloads = superfluousConvertToStringFormatProviderOverloads + .Concat(superfluousConvertToToCharFormatProviderOverloads) + .Concat(superfluousConvertToBooleanFormatProviderOverloads) + .ToImmutableHashSet(SymbolEqualityComparer.Default); + #endregion context.RegisterOperationAction(oaContext => @@ -224,7 +244,7 @@ protected override void InitializeWorker(CompilationStartAnalysisContext context // Sample message for IFormatProviderAlternateRule: Because the behavior of Convert.ToInt64(string) could vary based on the current user's locale settings, // replace this call in IFormatProviderStringTest.TestMethod() with a call to Convert.ToInt64(string, IFormatProvider). - if (correctOverload != null) + if (correctOverload != null && !superfluousFormatProviderOverloads.Contains(correctOverload)) { oaContext.ReportDiagnostic( invocationExpression.Syntax.CreateDiagnostic( diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/SpecifyIFormatProviderTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/SpecifyIFormatProviderTests.cs index e5d11d438b..d07a781968 100644 --- a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/SpecifyIFormatProviderTests.cs +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/SpecifyIFormatProviderTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.Testing; using Test.Utilities; using Xunit; @@ -1652,6 +1653,535 @@ public void M({valueType}? x) {{ return VerifyCS.VerifyAnalyzerAsync(code); } + [Theory, WorkItem(7154, "https://github.com/dotnet/roslyn-analyzers/issues/7154")] + [InlineData("bool")] + [InlineData("char")] + [InlineData("string")] + [InlineData("string?")] + public Task CA1305_ConvertToString_NoDiagnostic(string type) + { + var source = $$""" + using System; + + #nullable enable + class Test + { + void M({{type}} value) + { + var x = Convert.ToString(value); + } + } + """; + + return new VerifyCS.Test + { + TestCode = source, + LanguageVersion = LanguageVersion.CSharp8 + }.RunAsync(); + } + + [Theory, WorkItem(7154, "https://github.com/dotnet/roslyn-analyzers/issues/7154")] + [InlineData("object")] + [InlineData("object?")] + [InlineData("sbyte")] + [InlineData("byte")] + [InlineData("short")] + [InlineData("ushort")] + [InlineData("int")] + [InlineData("uint")] + [InlineData("long")] + [InlineData("ulong")] + [InlineData("float")] + [InlineData("double")] + [InlineData("decimal")] + [InlineData("DateTime")] + public Task CA1305_ConvertToString_Diagnostic(string type) + { + var source = $$""" + using System; + + #nullable enable + class Test + { + void M({{type}} value) + { + var x = {|#0:Convert.ToString(value)|}; + } + } + """; + var expectedDiagnostic = new DiagnosticResult(SpecifyIFormatProviderAnalyzer.IFormatProviderAlternateStringRule) + .WithLocation(0) + .WithArguments($"Convert.ToString({type.TrimEnd('?')})", $"Test.M({type.TrimEnd('?')})", $"Convert.ToString({type.TrimEnd('?')}, IFormatProvider)"); + + return new VerifyCS.Test + { + TestCode = source, + ExpectedDiagnostics = { expectedDiagnostic }, + LanguageVersion = LanguageVersion.CSharp8 + }.RunAsync(); + } + + [Fact, WorkItem(7154, "https://github.com/dotnet/roslyn-analyzers/issues/7154")] + public Task CA1305_ConvertToChar_NoDiagnostic() + { + const string source = """ + using System; + + class Test + { + void M(string value) + { + var x = Convert.ToChar(value); + } + } + """; + + return VerifyCS.VerifyAnalyzerAsync(source); + } + + [Theory, WorkItem(7154, "https://github.com/dotnet/roslyn-analyzers/issues/7154")] + [InlineData("object")] + [InlineData("object?")] + public Task CA1305_ConvertToChar_Diagnostic(string type) + { + var source = $$""" + using System; + + #nullable enable + class Test + { + void M({{type}} value) + { + var x = {|#0:Convert.ToChar(value)|}; + } + } + """; + var expectedDiagnostic = new DiagnosticResult(SpecifyIFormatProviderAnalyzer.IFormatProviderAlternateRule) + .WithLocation(0) + .WithArguments($"Convert.ToChar({type.TrimEnd('?')})", $"Test.M({type.TrimEnd('?')})", $"Convert.ToChar({type.TrimEnd('?')}, IFormatProvider)"); + + return new VerifyCS.Test + { + TestCode = source, + ExpectedDiagnostics = { expectedDiagnostic }, + LanguageVersion = LanguageVersion.CSharp8 + }.RunAsync(); + } + + [Theory, WorkItem(7154, "https://github.com/dotnet/roslyn-analyzers/issues/7154")] + [InlineData("string")] + [InlineData("string?")] + public Task CA1305_ConvertToBoolean_NoDiagnostic(string type) + { + var source = $$""" + using System; + + #nullable enable + class Test + { + void M({{type}} value) + { + var x = Convert.ToBoolean(value); + } + } + """; + + return new VerifyCS.Test + { + TestCode = source, + LanguageVersion = LanguageVersion.CSharp8 + }.RunAsync(); + } + + [Theory, WorkItem(7154, "https://github.com/dotnet/roslyn-analyzers/issues/7154")] + [InlineData("object")] + [InlineData("object?")] + public Task CA1305_ConvertToBoolean_Diagnostic(string type) + { + var source = $$""" + using System; + + #nullable enable + class Test + { + void M({{type}} value) + { + var x = {|#0:Convert.ToBoolean(value)|}; + } + } + """; + var expectedDiagnostic = new DiagnosticResult(SpecifyIFormatProviderAnalyzer.IFormatProviderAlternateRule) + .WithLocation(0) + .WithArguments($"Convert.ToBoolean({type.TrimEnd('?')})", $"Test.M({type.TrimEnd('?')})", $"Convert.ToBoolean({type.TrimEnd('?')}, IFormatProvider)"); + + return new VerifyCS.Test + { + TestCode = source, + ExpectedDiagnostics = { expectedDiagnostic }, + LanguageVersion = LanguageVersion.CSharp8 + }.RunAsync(); + } + + [Theory, WorkItem(7154, "https://github.com/dotnet/roslyn-analyzers/issues/7154")] + [InlineData("object")] + [InlineData("object?")] + [InlineData("string")] + public Task CA1305_ConvertToSByte_Diagnostic(string type) + { + var source = $$""" + using System; + + #nullable enable + class Test + { + void M({{type}} value) + { + var x = {|#0:Convert.ToSByte(value)|}; + } + } + """; + var expectedDiagnostic = new DiagnosticResult(SpecifyIFormatProviderAnalyzer.IFormatProviderAlternateRule) + .WithLocation(0) + .WithArguments($"Convert.ToSByte({type.TrimEnd('?')})", $"Test.M({type.TrimEnd('?')})", $"Convert.ToSByte({type.TrimEnd('?')}, IFormatProvider)"); + + return new VerifyCS.Test + { + TestCode = source, + ExpectedDiagnostics = { expectedDiagnostic }, + LanguageVersion = LanguageVersion.CSharp8 + }.RunAsync(); + } + + [Theory, WorkItem(7154, "https://github.com/dotnet/roslyn-analyzers/issues/7154")] + [InlineData("object")] + [InlineData("object?")] + [InlineData("string?")] + public Task CA1305_ConvertToByte_Diagnostic(string type) + { + var source = $$""" + using System; + + #nullable enable + class Test + { + void M({{type}} value) + { + var x = {|#0:Convert.ToByte(value)|}; + } + } + """; + var expectedDiagnostic = new DiagnosticResult(SpecifyIFormatProviderAnalyzer.IFormatProviderAlternateRule) + .WithLocation(0) + .WithArguments($"Convert.ToByte({type.TrimEnd('?')})", $"Test.M({type.TrimEnd('?')})", $"Convert.ToByte({type.TrimEnd('?')}, IFormatProvider)"); + + return new VerifyCS.Test + { + TestCode = source, + ExpectedDiagnostics = { expectedDiagnostic }, + LanguageVersion = LanguageVersion.CSharp8 + }.RunAsync(); + } + + [Theory, WorkItem(7154, "https://github.com/dotnet/roslyn-analyzers/issues/7154")] + [InlineData("object")] + [InlineData("object?")] + [InlineData("string?")] + public Task CA1305_ConvertToInt16_Diagnostic(string type) + { + var source = $$""" + using System; + + #nullable enable + class Test + { + void M({{type}} value) + { + var x = {|#0:Convert.ToInt16(value)|}; + } + } + """; + var expectedDiagnostic = new DiagnosticResult(SpecifyIFormatProviderAnalyzer.IFormatProviderAlternateRule) + .WithLocation(0) + .WithArguments($"Convert.ToInt16({type.TrimEnd('?')})", $"Test.M({type.TrimEnd('?')})", $"Convert.ToInt16({type.TrimEnd('?')}, IFormatProvider)"); + + return new VerifyCS.Test + { + TestCode = source, + ExpectedDiagnostics = { expectedDiagnostic }, + LanguageVersion = LanguageVersion.CSharp8 + }.RunAsync(); + } + + [Theory, WorkItem(7154, "https://github.com/dotnet/roslyn-analyzers/issues/7154")] + [InlineData("object")] + [InlineData("object?")] + [InlineData("string?")] + public Task CA1305_ConvertToUInt16_Diagnostic(string type) + { + var source = $$""" + using System; + + #nullable enable + class Test + { + void M({{type}} value) + { + var x = {|#0:Convert.ToUInt16(value)|}; + } + } + """; + var expectedDiagnostic = new DiagnosticResult(SpecifyIFormatProviderAnalyzer.IFormatProviderAlternateRule) + .WithLocation(0) + .WithArguments($"Convert.ToUInt16({type.TrimEnd('?')})", $"Test.M({type.TrimEnd('?')})", $"Convert.ToUInt16({type.TrimEnd('?')}, IFormatProvider)"); + + return new VerifyCS.Test + { + TestCode = source, + ExpectedDiagnostics = { expectedDiagnostic }, + LanguageVersion = LanguageVersion.CSharp8 + }.RunAsync(); + } + + [Theory, WorkItem(7154, "https://github.com/dotnet/roslyn-analyzers/issues/7154")] + [InlineData("object")] + [InlineData("object?")] + [InlineData("string?")] + public Task CA1305_ConvertToInt32_Diagnostic(string type) + { + var source = $$""" + using System; + + #nullable enable + class Test + { + void M({{type}} value) + { + var x = {|#0:Convert.ToInt32(value)|}; + } + } + """; + var expectedDiagnostic = new DiagnosticResult(SpecifyIFormatProviderAnalyzer.IFormatProviderAlternateRule) + .WithLocation(0) + .WithArguments($"Convert.ToInt32({type.TrimEnd('?')})", $"Test.M({type.TrimEnd('?')})", $"Convert.ToInt32({type.TrimEnd('?')}, IFormatProvider)"); + + return new VerifyCS.Test + { + TestCode = source, + ExpectedDiagnostics = { expectedDiagnostic }, + LanguageVersion = LanguageVersion.CSharp8 + }.RunAsync(); + } + + [Theory, WorkItem(7154, "https://github.com/dotnet/roslyn-analyzers/issues/7154")] + [InlineData("object")] + [InlineData("object?")] + [InlineData("string?")] + public Task CA1305_ConvertToUInt32_Diagnostic(string type) + { + var source = $$""" + using System; + + #nullable enable + class Test + { + void M({{type}} value) + { + var x = {|#0:Convert.ToUInt32(value)|}; + } + } + """; + var expectedDiagnostic = new DiagnosticResult(SpecifyIFormatProviderAnalyzer.IFormatProviderAlternateRule) + .WithLocation(0) + .WithArguments($"Convert.ToUInt32({type.TrimEnd('?')})", $"Test.M({type.TrimEnd('?')})", $"Convert.ToUInt32({type.TrimEnd('?')}, IFormatProvider)"); + + return new VerifyCS.Test + { + TestCode = source, + ExpectedDiagnostics = { expectedDiagnostic }, + LanguageVersion = LanguageVersion.CSharp8 + }.RunAsync(); + } + + [Theory, WorkItem(7154, "https://github.com/dotnet/roslyn-analyzers/issues/7154")] + [InlineData("object")] + [InlineData("object?")] + [InlineData("string?")] + public Task CA1305_ConvertToInt64_Diagnostic(string type) + { + var source = $$""" + using System; + + #nullable enable + class Test + { + void M({{type}} value) + { + var x = {|#0:Convert.ToInt64(value)|}; + } + } + """; + var expectedDiagnostic = new DiagnosticResult(SpecifyIFormatProviderAnalyzer.IFormatProviderAlternateRule) + .WithLocation(0) + .WithArguments($"Convert.ToInt64({type.TrimEnd('?')})", $"Test.M({type.TrimEnd('?')})", $"Convert.ToInt64({type.TrimEnd('?')}, IFormatProvider)"); + + return new VerifyCS.Test + { + TestCode = source, + ExpectedDiagnostics = { expectedDiagnostic }, + LanguageVersion = LanguageVersion.CSharp8 + }.RunAsync(); + } + + [Theory, WorkItem(7154, "https://github.com/dotnet/roslyn-analyzers/issues/7154")] + [InlineData("object")] + [InlineData("object?")] + [InlineData("string?")] + public Task CA1305_ConvertToUInt64_Diagnostic(string type) + { + var source = $$""" + using System; + + #nullable enable + class Test + { + void M({{type}} value) + { + var x = {|#0:Convert.ToUInt64(value)|}; + } + } + """; + var expectedDiagnostic = new DiagnosticResult(SpecifyIFormatProviderAnalyzer.IFormatProviderAlternateRule) + .WithLocation(0) + .WithArguments($"Convert.ToUInt64({type.TrimEnd('?')})", $"Test.M({type.TrimEnd('?')})", $"Convert.ToUInt64({type.TrimEnd('?')}, IFormatProvider)"); + + return new VerifyCS.Test + { + TestCode = source, + ExpectedDiagnostics = { expectedDiagnostic }, + LanguageVersion = LanguageVersion.CSharp8 + }.RunAsync(); + } + + [Theory, WorkItem(7154, "https://github.com/dotnet/roslyn-analyzers/issues/7154")] + [InlineData("object")] + [InlineData("object?")] + [InlineData("string?")] + public Task CA1305_ConvertToSingle_Diagnostic(string type) + { + var source = $$""" + using System; + + #nullable enable + class Test + { + void M({{type}} value) + { + var x = {|#0:Convert.ToSingle(value)|}; + } + } + """; + var expectedDiagnostic = new DiagnosticResult(SpecifyIFormatProviderAnalyzer.IFormatProviderAlternateRule) + .WithLocation(0) + .WithArguments($"Convert.ToSingle({type.TrimEnd('?')})", $"Test.M({type.TrimEnd('?')})", $"Convert.ToSingle({type.TrimEnd('?')}, IFormatProvider)"); + + return new VerifyCS.Test + { + TestCode = source, + ExpectedDiagnostics = { expectedDiagnostic }, + LanguageVersion = LanguageVersion.CSharp8 + }.RunAsync(); + } + + [Theory, WorkItem(7154, "https://github.com/dotnet/roslyn-analyzers/issues/7154")] + [InlineData("object")] + [InlineData("object?")] + [InlineData("string?")] + public Task CA1305_ConvertToDouble_Diagnostic(string type) + { + var source = $$""" + using System; + + #nullable enable + class Test + { + void M({{type}} value) + { + var x = {|#0:Convert.ToDouble(value)|}; + } + } + """; + var expectedDiagnostic = new DiagnosticResult(SpecifyIFormatProviderAnalyzer.IFormatProviderAlternateRule) + .WithLocation(0) + .WithArguments($"Convert.ToDouble({type.TrimEnd('?')})", $"Test.M({type.TrimEnd('?')})", $"Convert.ToDouble({type.TrimEnd('?')}, IFormatProvider)"); + + return new VerifyCS.Test + { + TestCode = source, + ExpectedDiagnostics = { expectedDiagnostic }, + LanguageVersion = LanguageVersion.CSharp8 + }.RunAsync(); + } + + [Theory, WorkItem(7154, "https://github.com/dotnet/roslyn-analyzers/issues/7154")] + [InlineData("object")] + [InlineData("object?")] + [InlineData("string?")] + public Task CA1305_ConvertToDecimal_Diagnostic(string type) + { + var source = $$""" + using System; + + #nullable enable + class Test + { + void M({{type}} value) + { + var x = {|#0:Convert.ToDecimal(value)|}; + } + } + """; + var expectedDiagnostic = new DiagnosticResult(SpecifyIFormatProviderAnalyzer.IFormatProviderAlternateRule) + .WithLocation(0) + .WithArguments($"Convert.ToDecimal({type.TrimEnd('?')})", $"Test.M({type.TrimEnd('?')})", $"Convert.ToDecimal({type.TrimEnd('?')}, IFormatProvider)"); + + return new VerifyCS.Test + { + TestCode = source, + ExpectedDiagnostics = { expectedDiagnostic }, + LanguageVersion = LanguageVersion.CSharp8 + }.RunAsync(); + } + + [Theory, WorkItem(7154, "https://github.com/dotnet/roslyn-analyzers/issues/7154")] + [InlineData("object")] + [InlineData("object?")] + [InlineData("string?")] + public Task CA1305_ConvertToDateTime_Diagnostic(string type) + { + var source = $$""" + using System; + + #nullable enable + class Test + { + void M({{type}} value) + { + var x = {|#0:Convert.ToSingle(value)|}; + } + } + """; + var expectedDiagnostic = new DiagnosticResult(SpecifyIFormatProviderAnalyzer.IFormatProviderAlternateRule) + .WithLocation(0) + .WithArguments($"Convert.ToSingle({type.TrimEnd('?')})", $"Test.M({type.TrimEnd('?')})", $"Convert.ToSingle({type.TrimEnd('?')}, IFormatProvider)"); + + return new VerifyCS.Test + { + TestCode = source, + ExpectedDiagnostics = { expectedDiagnostic }, + LanguageVersion = LanguageVersion.CSharp8 + }.RunAsync(); + } + private DiagnosticResult GetIFormatProviderAlternateStringRuleCSharpResultAt(int line, int column, string arg1, string arg2, string arg3) => #pragma warning disable RS0030 // Do not use banned APIs VerifyCS.Diagnostic(SpecifyIFormatProviderAnalyzer.IFormatProviderAlternateStringRule) From 65a06970482c400afb703838077d26e246134431 Mon Sep 17 00:00:00 2001 From: Collin Alpert Date: Fri, 30 Aug 2024 10:47:46 +0200 Subject: [PATCH 2/2] Implement PR suggestions --- .../Runtime/SpecifyIFormatProvider.cs | 36 +++++++++---------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/SpecifyIFormatProvider.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/SpecifyIFormatProvider.cs index 8de8d9bdeb..d77a027a24 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/SpecifyIFormatProvider.cs +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/SpecifyIFormatProvider.cs @@ -153,25 +153,23 @@ protected override void InitializeWorker(CompilationStartAnalysisContext context var guidParseMethods = guidType?.GetMembers("Parse") ?? ImmutableArray.Empty; var convertType = typeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemConvert); - var superfluousConvertToStringFormatProviderOverloads = convertType?.GetMembers(nameof(Convert.ToString)) - .OfType() - .Where(m => m.IsStatic - && m.Parameters is [{ Type.SpecialType: SpecialType.System_String or SpecialType.System_Boolean or SpecialType.System_Char }, var possibleFormatProvider] - && possibleFormatProvider.Type.Equals(iformatProviderType, SymbolEqualityComparer.Default)) ?? []; - var superfluousConvertToToCharFormatProviderOverloads = convertType?.GetMembers(nameof(Convert.ToChar)) - .OfType() - .Where(m => m.IsStatic - && m.Parameters is [{ Type.SpecialType: SpecialType.System_String }, var possibleFormatProvider] - && possibleFormatProvider.Type.Equals(iformatProviderType, SymbolEqualityComparer.Default)) ?? []; - var superfluousConvertToBooleanFormatProviderOverloads = convertType?.GetMembers(nameof(Convert.ToBoolean)) - .OfType() - .Where(m => m.IsStatic - && m.Parameters is [{ Type.SpecialType: SpecialType.System_String }, var possibleFormatProvider] - && possibleFormatProvider.Type.Equals(iformatProviderType, SymbolEqualityComparer.Default)) ?? []; - ImmutableHashSet superfluousFormatProviderOverloads = superfluousConvertToStringFormatProviderOverloads - .Concat(superfluousConvertToToCharFormatProviderOverloads) - .Concat(superfluousConvertToBooleanFormatProviderOverloads) - .ToImmutableHashSet(SymbolEqualityComparer.Default); + ImmutableHashSet superfluousFormatProviderOverloads = ImmutableHashSet.Empty; + if (convertType != null) + { + superfluousFormatProviderOverloads = convertType.GetMembers(nameof(Convert.ToString)) + .OfType() + .Where(m => m.Parameters is [{ Type.SpecialType: SpecialType.System_String or SpecialType.System_Boolean or SpecialType.System_Char }, var possibleFormatProvider] + && possibleFormatProvider.Type.Equals(iformatProviderType, SymbolEqualityComparer.Default)).ToImmutableHashSet(); + superfluousFormatProviderOverloads = superfluousFormatProviderOverloads + .Add(convertType.GetMembers(nameof(Convert.ToChar)) + .OfType() + .First(m => m.Parameters is [{ Type.SpecialType: SpecialType.System_String }, var possibleFormatProvider] + && possibleFormatProvider.Type.Equals(iformatProviderType, SymbolEqualityComparer.Default))) + .Add(convertType.GetMembers(nameof(Convert.ToBoolean)) + .OfType() + .First(m => m.Parameters is [{ Type.SpecialType: SpecialType.System_String }, var possibleFormatProvider] + && possibleFormatProvider.Type.Equals(iformatProviderType, SymbolEqualityComparer.Default))); + } #endregion