From bb25ae4f28bfc02be26f3f50fb481d537a30c18f Mon Sep 17 00:00:00 2001 From: Mario Pistrich Date: Wed, 16 Aug 2023 00:24:27 +0200 Subject: [PATCH] CA2263: Prefer generic overload when type is known This analyzer detects when a `System.Type` overload is called when a suitable generic overload is available. To validate if a generic overload is applicable, the arity, parameter count, containing symbol of the invocation (to avoid endless loops), return type, argument types and type constraints(using speculative binding) are checked. The fixer removes unnecessary casts and parentheses. --- .../CSharpPreferGenericOverloads.Fixer.cs | 90 + .../Usage/CSharpPreferGenericOverloads.cs | 65 + .../Core/AnalyzerReleases.Unshipped.md | 1 + .../MicrosoftNetCoreAnalyzersResources.resx | 12 + .../Usage/PreferGenericOverloads.Fixer.cs | 55 + .../Usage/PreferGenericOverloads.cs | 212 ++ .../MicrosoftNetCoreAnalyzersResources.cs.xlf | 20 + .../MicrosoftNetCoreAnalyzersResources.de.xlf | 20 + .../MicrosoftNetCoreAnalyzersResources.es.xlf | 20 + .../MicrosoftNetCoreAnalyzersResources.fr.xlf | 20 + .../MicrosoftNetCoreAnalyzersResources.it.xlf | 20 + .../MicrosoftNetCoreAnalyzersResources.ja.xlf | 20 + .../MicrosoftNetCoreAnalyzersResources.ko.xlf | 20 + .../MicrosoftNetCoreAnalyzersResources.pl.xlf | 20 + ...crosoftNetCoreAnalyzersResources.pt-BR.xlf | 20 + .../MicrosoftNetCoreAnalyzersResources.ru.xlf | 20 + .../MicrosoftNetCoreAnalyzersResources.tr.xlf | 20 + ...osoftNetCoreAnalyzersResources.zh-Hans.xlf | 20 + ...osoftNetCoreAnalyzersResources.zh-Hant.xlf | 20 + .../Microsoft.CodeAnalysis.NetAnalyzers.md | 12 + .../Microsoft.CodeAnalysis.NetAnalyzers.sarif | 38 + src/NetAnalyzers/RulesMissingDocumentation.md | 1 + .../Usage/PreferGenericOverloadsTests.cs | 2409 +++++++++++++++++ .../BasicPreferGenericOverloads.Fixer.vb | 81 + .../Usage/BasicPreferGenericOverloads.vb | 58 + .../DiagnosticCategoryAndIdRanges.txt | 2 +- 26 files changed, 3295 insertions(+), 1 deletion(-) create mode 100644 src/NetAnalyzers/CSharp/Microsoft.NetCore.Analyzers/Usage/CSharpPreferGenericOverloads.Fixer.cs create mode 100644 src/NetAnalyzers/CSharp/Microsoft.NetCore.Analyzers/Usage/CSharpPreferGenericOverloads.cs create mode 100644 src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Usage/PreferGenericOverloads.Fixer.cs create mode 100644 src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Usage/PreferGenericOverloads.cs create mode 100644 src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Usage/PreferGenericOverloadsTests.cs create mode 100644 src/NetAnalyzers/VisualBasic/Microsoft.NetCore.Analyzers/Usage/BasicPreferGenericOverloads.Fixer.vb create mode 100644 src/NetAnalyzers/VisualBasic/Microsoft.NetCore.Analyzers/Usage/BasicPreferGenericOverloads.vb diff --git a/src/NetAnalyzers/CSharp/Microsoft.NetCore.Analyzers/Usage/CSharpPreferGenericOverloads.Fixer.cs b/src/NetAnalyzers/CSharp/Microsoft.NetCore.Analyzers/Usage/CSharpPreferGenericOverloads.Fixer.cs new file mode 100644 index 0000000000..4349b23d00 --- /dev/null +++ b/src/NetAnalyzers/CSharp/Microsoft.NetCore.Analyzers/Usage/CSharpPreferGenericOverloads.Fixer.cs @@ -0,0 +1,90 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. + +using System.Composition; +using System.Threading; +using System.Threading.Tasks; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Operations; +using Microsoft.CodeAnalysis.Simplification; +using Microsoft.NetCore.Analyzers.Usage; +using static Microsoft.NetCore.Analyzers.Usage.PreferGenericOverloadsAnalyzer; + +namespace Microsoft.NetCore.CSharp.Analyzers.Usage +{ + /// + /// CA2263: + /// + [ExportCodeFixProvider(LanguageNames.CSharp), Shared] + public sealed class CSharpPreferGenericOverloadsFixer : PreferGenericOverloadsFixer + { + protected override async Task ReplaceWithGenericCallAsync(Document document, IInvocationOperation invocation, CancellationToken cancellationToken) + { + if (!RuntimeTypeInvocationContext.TryGetContext(invocation, out var invocationContext)) + { + return document; + } + + var modifiedInvocationSyntax = CSharpPreferGenericOverloadsAnalyzer.GetModifiedInvocationSyntax(invocationContext); + + if (modifiedInvocationSyntax is not InvocationExpressionSyntax invocationExpressionSyntax) + { + return document; + } + + // Analyzers are not allowed to have a reference to Simplifier, so add the additional annotation here instead. + invocationExpressionSyntax = invocationExpressionSyntax.WithExpression(invocationExpressionSyntax.Expression.WithAdditionalAnnotations(Simplifier.Annotation)); + + var editor = await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(false); + + if (invocationContext.Parent is IConversionOperation conversionOperation + && invocationContext.Parent.Syntax is CastExpressionSyntax castExpressionSyntax + && invocationContext.SemanticModel is not null) + { + var typeInfo = invocationContext.SemanticModel.GetSpeculativeTypeInfo( + invocationContext.Syntax.SpanStart, + invocationExpressionSyntax, + SpeculativeBindingOption.BindAsExpression); + + if (typeInfo.ConvertedType.IsAssignableTo(conversionOperation.Type, invocationContext.SemanticModel.Compilation)) + { + // Add a simplifier annotation to the parent to remove no longer needed parenthesis. + if (castExpressionSyntax.Parent is ParenthesizedExpressionSyntax parenthesizedExpressionSyntax) + { + editor.ReplaceNode( + parenthesizedExpressionSyntax, + parenthesizedExpressionSyntax + .ReplaceNode( + castExpressionSyntax, + castExpressionSyntax.Expression + .ReplaceNode(invocationContext.Syntax, invocationExpressionSyntax) + .WithTriviaFrom(castExpressionSyntax)) + .WithAdditionalAnnotations(Simplifier.Annotation)); + } + else + { + editor.ReplaceNode( + castExpressionSyntax, + castExpressionSyntax.Expression + .ReplaceNode(invocationContext.Syntax, invocationExpressionSyntax) + .WithTriviaFrom(castExpressionSyntax)); + } + } + else + { + editor.ReplaceNode(invocationContext.Syntax, invocationExpressionSyntax); + } + } + else + { + editor.ReplaceNode(invocationContext.Syntax, invocationExpressionSyntax); + } + + return document.WithSyntaxRoot(editor.GetChangedRoot()); + } + } +} diff --git a/src/NetAnalyzers/CSharp/Microsoft.NetCore.Analyzers/Usage/CSharpPreferGenericOverloads.cs b/src/NetAnalyzers/CSharp/Microsoft.NetCore.Analyzers/Usage/CSharpPreferGenericOverloads.cs new file mode 100644 index 0000000000..c9ada5415a --- /dev/null +++ b/src/NetAnalyzers/CSharp/Microsoft.NetCore.Analyzers/Usage/CSharpPreferGenericOverloads.cs @@ -0,0 +1,65 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. + +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.NetCore.Analyzers.Usage; +using Microsoft.CodeAnalysis.Operations; + +namespace Microsoft.NetCore.CSharp.Analyzers.Usage +{ + /// + /// CA2263: + /// + [DiagnosticAnalyzer(LanguageNames.CSharp)] + public sealed class CSharpPreferGenericOverloadsAnalyzer : PreferGenericOverloadsAnalyzer + { + protected sealed override bool TryGetModifiedInvocationSyntax(RuntimeTypeInvocationContext invocationContext, [NotNullWhen(true)] out SyntaxNode? modifiedInvocationSyntax) + { + modifiedInvocationSyntax = GetModifiedInvocationSyntax(invocationContext); + + return modifiedInvocationSyntax is not null; + } + + // Expose as internal static to allow the fixer to also call this method. + internal static SyntaxNode? GetModifiedInvocationSyntax(RuntimeTypeInvocationContext invocationContext) + { + if (invocationContext.Syntax is not InvocationExpressionSyntax invocationSyntax) + { + return null; + } + + var typeArgumentsSyntax = invocationContext.TypeArguments.Select(t => SyntaxFactory.ParseTypeName(t.ToDisplayString())); + var otherArgumentsSyntax = invocationContext.OtherArguments + .Where(a => a.ArgumentKind != ArgumentKind.DefaultValue) + .Select(a => a.Syntax) + .OfType(); + var methodNameSyntax = + SyntaxFactory.GenericName( + SyntaxFactory.Identifier(invocationContext.Method.Name), + SyntaxFactory.TypeArgumentList(SyntaxFactory.SeparatedList(typeArgumentsSyntax))); + var modifiedInvocationExpression = invocationSyntax.Expression; + + if (modifiedInvocationExpression is MemberAccessExpressionSyntax memberAccessExpressionSyntax) + { + modifiedInvocationExpression = memberAccessExpressionSyntax.WithName(methodNameSyntax); + } + else if (modifiedInvocationExpression is IdentifierNameSyntax identifierNameSyntax) + { + modifiedInvocationExpression = methodNameSyntax; + } + else + { + return null; + } + + return invocationSyntax + .WithExpression(modifiedInvocationExpression) + .WithArgumentList(SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(otherArgumentsSyntax))) + .WithTriviaFrom(invocationSyntax); + } + } +} diff --git a/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md b/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md index a4b1a7ebbd..d1ecf3bd2b 100644 --- a/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md +++ b/src/NetAnalyzers/Core/AnalyzerReleases.Unshipped.md @@ -24,6 +24,7 @@ CA1862 | Performance | Info | RecommendCaseInsensitiveStringComparison, [Documen CA1863 | Performance | Hidden | UseCompositeFormatAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1862) CA1864 | Performance | Info | PreferDictionaryTryAddValueOverGuardedAddAnalyzer, [Documentation](https://docs.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1864) CA2021 | Reliability | Warning | DoNotCallEnumerableCastOrOfTypeWithIncompatibleTypesAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2021) +CA2263 | Usage | Info | PreferGenericOverloadsAnalyzer, [Documentation](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2263) ### Removed Rules diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx index 01eac786fd..668e1d335f 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/MicrosoftNetCoreAnalyzersResources.resx @@ -1967,6 +1967,18 @@ Widening and user defined conversions are not supported with generic types. Use correct type parameter + + Use generic overload + + + Using a generic overload is preferable to the 'System.Type' overload when the type is known, promoting cleaner and more type-safe code with improved compile-time checks. + + + Prefer the generic overload '{0}' instead of '{1}' + + + Prefer generic overload when type is known + Use 'Clear()' diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Usage/PreferGenericOverloads.Fixer.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Usage/PreferGenericOverloads.Fixer.cs new file mode 100644 index 0000000000..40fd7e4997 --- /dev/null +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Usage/PreferGenericOverloads.Fixer.cs @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. + +using System.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; +using Analyzer.Utilities; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Operations; +using static Microsoft.NetCore.Analyzers.Usage.PreferGenericOverloadsAnalyzer; + +namespace Microsoft.NetCore.Analyzers.Usage +{ + /// + /// CA2263: + /// + public abstract class PreferGenericOverloadsFixer : CodeFixProvider + { + public sealed override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create(RuleId); + + public sealed override FixAllProvider GetFixAllProvider() + { + return WellKnownFixAllProviders.BatchFixer; + } + + public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + var root = await context.Document.GetRequiredSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + var node = root.FindNode(context.Span, getInnermostNodeForTie: true); + + if (node is null) + { + return; + } + + var semanticModel = await context.Document.GetRequiredSemanticModelAsync(context.CancellationToken).ConfigureAwait(false); + var operation = semanticModel.GetOperation(node, context.CancellationToken); + + if (operation is not IInvocationOperation invocation) + { + return; + } + + var codeAction = CodeAction.Create( + MicrosoftNetCoreAnalyzersResources.PreferGenericOverloadsCodeFixTitle, + ct => ReplaceWithGenericCallAsync(context.Document, invocation, ct), + nameof(MicrosoftNetCoreAnalyzersResources.PreferGenericOverloadsCodeFixTitle)); + + context.RegisterCodeFix(codeAction, context.Diagnostics); + } + + protected abstract Task ReplaceWithGenericCallAsync(Document document, IInvocationOperation invocation, CancellationToken cancellationToken); + } +} diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Usage/PreferGenericOverloads.cs b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Usage/PreferGenericOverloads.cs new file mode 100644 index 0000000000..dd28e1a194 --- /dev/null +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Usage/PreferGenericOverloads.cs @@ -0,0 +1,212 @@ +// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. + +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Threading; +using Analyzer.Utilities; +using Analyzer.Utilities.Extensions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; + +namespace Microsoft.NetCore.Analyzers.Usage +{ + using static MicrosoftNetCoreAnalyzersResources; + + /// + /// CA2263: + /// + public abstract class PreferGenericOverloadsAnalyzer : DiagnosticAnalyzer + { + internal const string RuleId = "CA2263"; + + internal static readonly DiagnosticDescriptor Rule = DiagnosticDescriptorHelper.Create( + RuleId, + CreateLocalizableResourceString(nameof(PreferGenericOverloadsTitle)), + CreateLocalizableResourceString(nameof(PreferGenericOverloadsMessage)), + DiagnosticCategory.Usage, + RuleLevel.IdeSuggestion, + CreateLocalizableResourceString(nameof(PreferGenericOverloadsDescription)), + isPortedFxCopRule: false, + isDataflowRule: false); + + public sealed override ImmutableArray SupportedDiagnostics { get; } = ImmutableArray.Create(Rule); + + public override void Initialize(AnalysisContext context) + { + context.EnableConcurrentExecution(); + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); + context.RegisterCompilationStartAction(context => context.RegisterOperationAction(AnalyzeInvocation, OperationKind.Invocation)); + } + + private void AnalyzeInvocation(OperationAnalysisContext context) + { + if (!RuntimeTypeInvocationContext.TryGetContext((IInvocationOperation)context.Operation, out var invocationContext)) + { + return; + } + + // Get all methods on the containing type with the same name as the original invocation that are applicable generic overloads. + var genericInvocation = invocationContext.Method.ContainingType + .GetMembers(invocationContext.Method.Name) + .OfType() + .Where(IsApplicableGenericOverload) + .FirstOrDefault(); + + if (genericInvocation is not null) + { + context.ReportDiagnostic(invocationContext.Invocation.CreateDiagnostic( + Rule, + genericInvocation.ToDisplayString(), + invocationContext.Method.ToDisplayString())); + } + + // A generic overload is applicable iff: + // 1. The arity is the same as the type parameters of the original invocation + // 2. The parameters count is the same as the other arguments of the original invocation + // 3. It is not the same as the containing symbol containing the original invocation + // This is to prevent cases where the generic method forwards to a non generic one, e.g. Foo() calls Foo(typeof(T)). + // Without this condition we would create an infinite loop as we would replace Foo(typeof(T)) with Foo(). + // 4. The return type is assignable to the original return type. We do not check the return type for expression statements. + // 5. All other arguments of the original invocation are assignable to the parameters of the method. + // 6. Speculative binding of the new invocation succeeds; this is to check if any type parameter constraints are violated. + bool IsApplicableGenericOverload(IMethodSymbol method) + { + // Reduce method if original method was reduced. + if (invocationContext.Method.ReducedFrom is not null) + { + method = invocationContext.ReduceExtensionMethodOrOriginal(method, context.Compilation, context.CancellationToken); + } + + if (method.Arity != invocationContext.TypeArguments.Length || + method.Parameters.Length != invocationContext.OtherArguments.Length || + SymbolEqualityComparer.Default.Equals(method, context.ContainingSymbol)) + { + return false; + } + + var genericMethod = method.Construct(invocationContext.TypeArguments.ToArray()); + + if (!invocationContext.IsReturnTypeCompatible(genericMethod, context.Compilation) || + !invocationContext.AreOtherArgumentsCompatible(genericMethod, context.Compilation) || + !TryGetModifiedInvocationSyntax(invocationContext, out var modifiedInvocationSyntax)) + { + return false; + } + + var speculativeSymbolInfo = invocationContext.SemanticModel?.GetSpeculativeSymbolInfo( + invocationContext.Syntax.SpanStart, + modifiedInvocationSyntax, + SpeculativeBindingOption.BindAsExpression); + + // Check if the expression was bound successfully. + if (speculativeSymbolInfo?.Symbol is not IMethodSymbol boundMethod) + { + return false; + } + + // Reduce the constructed method if the bound method is reduced to be able to compare them. + if (boundMethod.ReducedFrom is not null) + { + genericMethod = invocationContext.ReduceExtensionMethodOrOriginal(genericMethod, context.Compilation, context.CancellationToken); + } + + // Check if the speculative symbol was bound to the same method. + // This prevents cases where we bind to a overload that was ruled out before (e.g. it is the same as the containing symbol). + return SymbolEqualityComparer.Default.Equals(boundMethod, genericMethod) + && SymbolEqualityComparer.Default.Equals(boundMethod.ReturnType, genericMethod.ReturnType); + } + } + + // Make the context internal to be also usable in the fixer. + protected internal sealed class RuntimeTypeInvocationContext + { + private RuntimeTypeInvocationContext( + IInvocationOperation invocation, + ImmutableArray typeArguments, + ImmutableArray otherArguments) + { + Invocation = invocation; + TypeArguments = typeArguments; + OtherArguments = otherArguments; + } + + public static bool TryGetContext(IInvocationOperation invocation, [NotNullWhen(true)] out RuntimeTypeInvocationContext? invocationContext) + { + invocationContext = default; + + var argumentsInParameterOrder = invocation.Arguments.GetArgumentsInParameterOrder(); + var typeOfArguments = argumentsInParameterOrder.WhereAsArray(a => a.Value is ITypeOfOperation); + + // Bail out if there is no argument using the typeof operator. + if (typeOfArguments.Length == 0) + { + return false; + } + + // Split arguments into type arguments and other arguments passed to a potential generic overload. + var typeArguments = typeOfArguments + .Select(a => a.Value) + .OfType() + .Select(t => t.TypeOperand) + .Where(t => t is not INamedTypeSymbol { IsUnboundGenericType: true }) + .ToImmutableArray(); + var otherArguments = argumentsInParameterOrder.RemoveRange(typeOfArguments); + + invocationContext = new RuntimeTypeInvocationContext(invocation, typeArguments, otherArguments); + + return true; + } + + public IInvocationOperation Invocation { get; } + public ImmutableArray TypeArguments { get; } + public ImmutableArray OtherArguments { get; } + public SemanticModel? SemanticModel => Invocation.SemanticModel; + public IMethodSymbol Method => Invocation.TargetMethod; + public SyntaxNode Syntax => Invocation.Syntax; + public IOperation? Parent => Invocation.Parent; + + public IMethodSymbol ReduceExtensionMethodOrOriginal(IMethodSymbol method, Compilation compilation, CancellationToken cancellationToken) + { + if (method.IsExtensionMethod) + { + var receiverType = Invocation.GetReceiverType(compilation, false, cancellationToken); + + if (receiverType is not null) + { + return method.ReduceExtensionMethod(receiverType) ?? method; + } + } + + return method; + } + + public bool IsReturnTypeCompatible(IMethodSymbol method, Compilation compilation) + { + // We do not care if we change the return type if it is an expression statement. + if (Parent is IExpressionStatementOperation) + { + return true; + } + + return method.ReturnType.IsAssignableTo(Method.ReturnType, compilation); + } + + public bool AreOtherArgumentsCompatible(IMethodSymbol method, Compilation compilation) + { + for (int i = 0; i < OtherArguments.Length; i++) + { + if (!OtherArguments[i].Value.WalkDownConversion().Type.IsAssignableTo(method.Parameters[i].Type, compilation)) + { + return false; + } + } + + return true; + } + } + + protected abstract bool TryGetModifiedInvocationSyntax(RuntimeTypeInvocationContext invocationContext, [NotNullWhen(true)] out SyntaxNode? modifiedInvocationSyntax); + } +} diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf index 624885ff9e..a16f3f868b 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.cs.xlf @@ -2123,6 +2123,26 @@ Obecné přetypování (IL unbox.any) používané sekvencí vrácenou metodou E Preferovat metodu IDictionary.TryGetValue(TKey, out TValue) + + Use generic overload + Use generic overload + + + + Using a generic overload is preferable to the 'System.Type' overload when the type is known, promoting cleaner and more type-safe code with improved compile-time checks. + Using a generic overload is preferable to the 'System.Type' overload when the type is known, promoting cleaner and more type-safe code with improved compile-time checks. + + + + Prefer the generic overload '{0}' instead of '{1}' + Prefer the generic overload '{0}' instead of '{1}' + + + + Prefer generic overload when type is known + Prefer generic overload when type is known + + Replace with 'HashData' method Nahraďte metodou HashData. diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf index 216cd38324..5b5ed0edaf 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.de.xlf @@ -2123,6 +2123,26 @@ Erweiterungen und benutzerdefinierte Konvertierungen werden bei generischen Type Methode „IDictionary.TryGetValue(TKey, out TValue)“ bevorzugen + + Use generic overload + Use generic overload + + + + Using a generic overload is preferable to the 'System.Type' overload when the type is known, promoting cleaner and more type-safe code with improved compile-time checks. + Using a generic overload is preferable to the 'System.Type' overload when the type is known, promoting cleaner and more type-safe code with improved compile-time checks. + + + + Prefer the generic overload '{0}' instead of '{1}' + Prefer the generic overload '{0}' instead of '{1}' + + + + Prefer generic overload when type is known + Prefer generic overload when type is known + + Replace with 'HashData' method Ersetzen Sie dies durch die Methode „HashData“. diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf index cacd29161e..7eff6351ff 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.es.xlf @@ -2123,6 +2123,26 @@ La ampliación y las conversiones definidas por el usuario no se admiten con tip Preferir el método "IDictionary.TryGetValue(TKey, out TValue)" + + Use generic overload + Use generic overload + + + + Using a generic overload is preferable to the 'System.Type' overload when the type is known, promoting cleaner and more type-safe code with improved compile-time checks. + Using a generic overload is preferable to the 'System.Type' overload when the type is known, promoting cleaner and more type-safe code with improved compile-time checks. + + + + Prefer the generic overload '{0}' instead of '{1}' + Prefer the generic overload '{0}' instead of '{1}' + + + + Prefer generic overload when type is known + Prefer generic overload when type is known + + Replace with 'HashData' method Reemplazar por el método 'HashData' diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf index a336bc908c..d3f3954633 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.fr.xlf @@ -2123,6 +2123,26 @@ Les conversions étendues et définies par l’utilisateur ne sont pas prises en Préférer la méthode 'IDictionary.TryGetValue(TKey, out TValue)' + + Use generic overload + Use generic overload + + + + Using a generic overload is preferable to the 'System.Type' overload when the type is known, promoting cleaner and more type-safe code with improved compile-time checks. + Using a generic overload is preferable to the 'System.Type' overload when the type is known, promoting cleaner and more type-safe code with improved compile-time checks. + + + + Prefer the generic overload '{0}' instead of '{1}' + Prefer the generic overload '{0}' instead of '{1}' + + + + Prefer generic overload when type is known + Prefer generic overload when type is known + + Replace with 'HashData' method Remplacer par la méthode "HashData" diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf index 042c2dbccb..6492cf392f 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.it.xlf @@ -2123,6 +2123,26 @@ L'ampliamento e le conversioni definite dall'utente non sono supportate con tipi Preferisci il metodo 'IDictionary.TryGetValue(TKey, out TValue)' + + Use generic overload + Use generic overload + + + + Using a generic overload is preferable to the 'System.Type' overload when the type is known, promoting cleaner and more type-safe code with improved compile-time checks. + Using a generic overload is preferable to the 'System.Type' overload when the type is known, promoting cleaner and more type-safe code with improved compile-time checks. + + + + Prefer the generic overload '{0}' instead of '{1}' + Prefer the generic overload '{0}' instead of '{1}' + + + + Prefer generic overload when type is known + Prefer generic overload when type is known + + Replace with 'HashData' method Sostituire con il metodo 'HashData' diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf index e4a770a9a2..26cb0ad507 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ja.xlf @@ -2123,6 +2123,26 @@ Enumerable.OfType<T> で使用されるジェネリック型チェック ( 'IDictionary.TryGetValue(TKey, out TValue)' メソッドを優先します + + Use generic overload + Use generic overload + + + + Using a generic overload is preferable to the 'System.Type' overload when the type is known, promoting cleaner and more type-safe code with improved compile-time checks. + Using a generic overload is preferable to the 'System.Type' overload when the type is known, promoting cleaner and more type-safe code with improved compile-time checks. + + + + Prefer the generic overload '{0}' instead of '{1}' + Prefer the generic overload '{0}' instead of '{1}' + + + + Prefer generic overload when type is known + Prefer generic overload when type is known + + Replace with 'HashData' method 'HashData' メソッドに置き換える diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf index bcf12ee730..53ad443675 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ko.xlf @@ -2123,6 +2123,26 @@ Enumerable.OfType<T>에서 사용하는 제네릭 형식 검사(C# 'is' 'IDictionary.TryGetValue(TKey, out TValue)' 메서드 선호 + + Use generic overload + Use generic overload + + + + Using a generic overload is preferable to the 'System.Type' overload when the type is known, promoting cleaner and more type-safe code with improved compile-time checks. + Using a generic overload is preferable to the 'System.Type' overload when the type is known, promoting cleaner and more type-safe code with improved compile-time checks. + + + + Prefer the generic overload '{0}' instead of '{1}' + Prefer the generic overload '{0}' instead of '{1}' + + + + Prefer generic overload when type is known + Prefer generic overload when type is known + + Replace with 'HashData' method 'HashData' 메소드로 대체 diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf index 93ce237ccf..fff27d7b96 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pl.xlf @@ -2123,6 +2123,26 @@ Konwersje poszerzane i zdefiniowane przez użytkownika nie są obsługiwane w pr Preferuj metodę „IDictionary.TryGetValue(TKey, out TValue)” + + Use generic overload + Use generic overload + + + + Using a generic overload is preferable to the 'System.Type' overload when the type is known, promoting cleaner and more type-safe code with improved compile-time checks. + Using a generic overload is preferable to the 'System.Type' overload when the type is known, promoting cleaner and more type-safe code with improved compile-time checks. + + + + Prefer the generic overload '{0}' instead of '{1}' + Prefer the generic overload '{0}' instead of '{1}' + + + + Prefer generic overload when type is known + Prefer generic overload when type is known + + Replace with 'HashData' method Zamień na metodę „HashData” diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pt-BR.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pt-BR.xlf index 15d69fc4ee..5a1cbe7140 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pt-BR.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.pt-BR.xlf @@ -2123,6 +2123,26 @@ Ampliação e conversões definidas pelo usuário não são compatíveis com tip Prefira o método 'IDictionary.TryGetValue(TKey, out TValue)' + + Use generic overload + Use generic overload + + + + Using a generic overload is preferable to the 'System.Type' overload when the type is known, promoting cleaner and more type-safe code with improved compile-time checks. + Using a generic overload is preferable to the 'System.Type' overload when the type is known, promoting cleaner and more type-safe code with improved compile-time checks. + + + + Prefer the generic overload '{0}' instead of '{1}' + Prefer the generic overload '{0}' instead of '{1}' + + + + Prefer generic overload when type is known + Prefer generic overload when type is known + + Replace with 'HashData' method Substituir pelo método “HashData” diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf index 158d3a0b3d..bee7973adf 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.ru.xlf @@ -2123,6 +2123,26 @@ Widening and user defined conversions are not supported with generic types.Предпочитать метод "IDictionary.TryGetValue(TKey, out TValue)" + + Use generic overload + Use generic overload + + + + Using a generic overload is preferable to the 'System.Type' overload when the type is known, promoting cleaner and more type-safe code with improved compile-time checks. + Using a generic overload is preferable to the 'System.Type' overload when the type is known, promoting cleaner and more type-safe code with improved compile-time checks. + + + + Prefer the generic overload '{0}' instead of '{1}' + Prefer the generic overload '{0}' instead of '{1}' + + + + Prefer generic overload when type is known + Prefer generic overload when type is known + + Replace with 'HashData' method Заменить методом HashData diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf index 82848fdd27..4385be230d 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.tr.xlf @@ -2123,6 +2123,26 @@ Genel türlerde genişletme ve kullanıcı tanımlı dönüştürmeler desteklen 'IDictionary.TryGetValue(TKey, out TValue)' yöntemini tercih edin + + Use generic overload + Use generic overload + + + + Using a generic overload is preferable to the 'System.Type' overload when the type is known, promoting cleaner and more type-safe code with improved compile-time checks. + Using a generic overload is preferable to the 'System.Type' overload when the type is known, promoting cleaner and more type-safe code with improved compile-time checks. + + + + Prefer the generic overload '{0}' instead of '{1}' + Prefer the generic overload '{0}' instead of '{1}' + + + + Prefer generic overload when type is known + Prefer generic overload when type is known + + Replace with 'HashData' method 'HashData' yöntemiyle değiştir diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.zh-Hans.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.zh-Hans.xlf index 02d8aca805..0d50147a6d 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.zh-Hans.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.zh-Hans.xlf @@ -2123,6 +2123,26 @@ Enumerable.OfType<T> 使用的泛型类型检查 (C# 'is' operator/IL 'isi 首选 “IDictionary.TryGetValue(TKey, out TValue)” 方法 + + Use generic overload + Use generic overload + + + + Using a generic overload is preferable to the 'System.Type' overload when the type is known, promoting cleaner and more type-safe code with improved compile-time checks. + Using a generic overload is preferable to the 'System.Type' overload when the type is known, promoting cleaner and more type-safe code with improved compile-time checks. + + + + Prefer the generic overload '{0}' instead of '{1}' + Prefer the generic overload '{0}' instead of '{1}' + + + + Prefer generic overload when type is known + Prefer generic overload when type is known + + Replace with 'HashData' method 替换为“HashData”方法 diff --git a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.zh-Hant.xlf b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.zh-Hant.xlf index e5a66cd4a6..c3bf1cc20f 100644 --- a/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.zh-Hant.xlf +++ b/src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/xlf/MicrosoftNetCoreAnalyzersResources.zh-Hant.xlf @@ -2123,6 +2123,26 @@ Enumerable.OfType<T> 使用的一般型別檢查 (C# 'is' operator/IL 'isi 建議使用 'IDictionary.TryGetValue(TKey,out TValue)' 方法 + + Use generic overload + Use generic overload + + + + Using a generic overload is preferable to the 'System.Type' overload when the type is known, promoting cleaner and more type-safe code with improved compile-time checks. + Using a generic overload is preferable to the 'System.Type' overload when the type is known, promoting cleaner and more type-safe code with improved compile-time checks. + + + + Prefer the generic overload '{0}' instead of '{1}' + Prefer the generic overload '{0}' instead of '{1}' + + + + Prefer generic overload when type is known + Prefer generic overload when type is known + + Replace with 'HashData' method 以 'HashData' 方法取代 diff --git a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md index ef63ae6f98..da26bc1532 100644 --- a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md +++ b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.md @@ -2550,6 +2550,18 @@ The ConfigureAwaitOptions.SuppressThrowing is only supported with the non-generi |CodeFix|False| --- +## [CA2263](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2263): Prefer generic overload when type is known + +Using a generic overload is preferable to the 'System.Type' overload when the type is known, promoting cleaner and more type-safe code with improved compile-time checks. + +|Item|Value| +|-|-| +|Category|Usage| +|Enabled|True| +|Severity|Info| +|CodeFix|True| +--- + ## [CA2300](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2300): Do not use insecure deserializer BinaryFormatter The method '{0}' is insecure when deserializing untrusted data. If you need to instead detect BinaryFormatter deserialization without a SerializationBinder set, then disable rule CA2300, and enable rules CA2301 and CA2302. diff --git a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif index b80746b7b8..80b1d732ca 100644 --- a/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif +++ b/src/NetAnalyzers/Microsoft.CodeAnalysis.NetAnalyzers.sarif @@ -514,6 +514,25 @@ ] } }, + "CA2263": { + "id": "CA2263", + "shortDescription": "Prefer generic overload when type is known", + "fullDescription": "Using a generic overload is preferable to the 'System.Type' overload when the type is known, promoting cleaner and more type-safe code with improved compile-time checks.", + "defaultLevel": "note", + "helpUri": "https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2263", + "properties": { + "category": "Usage", + "isEnabledByDefault": true, + "typeName": "CSharpPreferGenericOverloadsAnalyzer", + "languages": [ + "C#" + ], + "tags": [ + "Telemetry", + "EnabledRuleInAggressiveMode" + ] + } + }, "CA2352": { "id": "CA2352", "shortDescription": "Unsafe DataSet or DataTable in serializable type can be vulnerable to remote code execution attacks", @@ -6624,6 +6643,25 @@ ] } }, + "CA2263": { + "id": "CA2263", + "shortDescription": "Prefer generic overload when type is known", + "fullDescription": "Using a generic overload is preferable to the 'System.Type' overload when the type is known, promoting cleaner and more type-safe code with improved compile-time checks.", + "defaultLevel": "note", + "helpUri": "https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2263", + "properties": { + "category": "Usage", + "isEnabledByDefault": true, + "typeName": "BasicPreferGenericOverloadsAnalyzer", + "languages": [ + "Visual Basic" + ], + "tags": [ + "Telemetry", + "EnabledRuleInAggressiveMode" + ] + } + }, "CA2352": { "id": "CA2352", "shortDescription": "Unsafe DataSet or DataTable in serializable type can be vulnerable to remote code execution attacks", diff --git a/src/NetAnalyzers/RulesMissingDocumentation.md b/src/NetAnalyzers/RulesMissingDocumentation.md index eb030b9cc5..456ae72d35 100644 --- a/src/NetAnalyzers/RulesMissingDocumentation.md +++ b/src/NetAnalyzers/RulesMissingDocumentation.md @@ -16,3 +16,4 @@ CA1867 | | Cache and reuse 'JsonSerializerOptions' instances | CA2021 | | Do not call Enumerable.Cast\ or Enumerable.OfType\ with incompatible types | CA2261 | | Do not use ConfigureAwaitOptions.SuppressThrowing with Task\ | +CA2263 | | Prefer generic overload when type is known | diff --git a/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Usage/PreferGenericOverloadsTests.cs b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Usage/PreferGenericOverloadsTests.cs new file mode 100644 index 0000000000..126f9fddd9 --- /dev/null +++ b/src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Usage/PreferGenericOverloadsTests.cs @@ -0,0 +1,2409 @@ +// 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.Testing; +using Xunit; +using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< + Microsoft.NetCore.CSharp.Analyzers.Usage.CSharpPreferGenericOverloadsAnalyzer, + Microsoft.NetCore.CSharp.Analyzers.Usage.CSharpPreferGenericOverloadsFixer>; +using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier< + Microsoft.NetCore.VisualBasic.Analyzers.Usage.BasicPreferGenericOverloadsAnalyzer, + Microsoft.NetCore.VisualBasic.Analyzers.Usage.BasicPreferGenericOverloadsFixer>; + +namespace Microsoft.NetCore.Analyzers.Usage.UnitTests +{ + public class PreferGenericOverloadsTests + { + [Fact] + public async Task NoTypeArgument_NoDiagnostic_CS() + { + string source = """ + class C + { + void M(int x) {} + void M() {} + + void Test() + { + M(0); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task RuntimeTypeArgument_NoDiagnostic_CS() + { + string source = """ + class C + { + void M(System.Type type) {} + void M() {} + + void Test() + { + M(GetType()); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task StaticClassAsTypeArgument_NoDiagnostic_CS() + { + string source = """ + static class C + { + static void M(System.Type type) {} + static void M() {} + + static void Test() + { + M(typeof(C)); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task UnboundGenericTypeArgument_NoDiagnostic_CS() + { + string source = """ + class ViolatingType {} + + class C + { + void M(System.Type type) {} + void M() {} + + void Test() + { + M(typeof(ViolatingType<>)); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task WrongArity_NoDiagnostic_CS() + { + string source = """ + class C + { + void M(System.Type type1, System.Type type2) {} + void M() {} + void M() {} + + void Test() + { + M(typeof(C), typeof(C)); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task WrongParameterCount_NoDiagnostic_CS() + { + string source = """ + class C + { + void M(System.Type type, int x) {} + void M() {} + void M(int x, int y) {} + + void Test() + { + M(typeof(C), 0); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task WrongParameterType_NoDiagnostic_CS() + { + string source = """ + class C + { + void M(System.Type type, int x) {} + void M(string x) {} + + void Test() + { + M(typeof(C), 0); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task WrongParameterTypeWithOneMatching_NoDiagnostic_CS() + { + string source = """ + class C + { + void M(System.Type type, int x, string y) {} + void M(string x, string y) {} + + void Test() + { + M(typeof(C), 0, string.Empty); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task SameAsContainingSymbol_NoDiagnostic_CS() + { + string source = """ + class C + { + object M(System.Type type, object x) { return x; } + T M(T x) { return (T)M(typeof(T), x); } + } + """; + + await VerifyCS.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task ViolatesValueTypeConstraint_NoDiagnostic_CS() + { + string source = """ + class ViolatingType {} + + class C + { + void M(System.Type type) {} + void M() where T : struct {} + + void Test() + { + M(typeof(ViolatingType)); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task SatisfiesValueTypeConstraint_OffersFixer_CS() + { + string source = """ + struct ValidType {} + + class C + { + void M(System.Type type) {} + void M() where T : struct {} + + void Test() + { + [|M(typeof(ValidType))|]; + } + } + """; + + string fixedSource = """ + struct ValidType {} + + class C + { + void M(System.Type type) {} + void M() where T : struct {} + + void Test() + { + M(); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task ViolatesReferenceTypeConstraint_NoDiagnostic_CS() + { + string source = """ + struct ViolatingType {} + + class C + { + void M(System.Type type) {} + void M() where T : class {} + + void Test() + { + M(typeof(ViolatingType)); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task SatisfiesReferenceTypeConstraint_OffersFixer_CS() + { + string source = """ + class ValidType {} + + class C + { + void M(System.Type type) {} + void M() where T : class {} + + void Test() + { + [|M(typeof(ValidType))|]; + } + } + """; + + string fixedSource = """ + class ValidType {} + + class C + { + void M(System.Type type) {} + void M() where T : class {} + + void Test() + { + M(); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task ViolatesUnmanagedTypeConstraint_NoDiagnostic_CS() + { + string source = """ + class ViolatingType {} + + class C + { + void M(System.Type type) {} + void M() where T : unmanaged {} + + void Test() + { + M(typeof(ViolatingType)); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task SatisfiesUnmanagedTypeConstraint_OffersFixer_CS() + { + string source = """ + struct ValidType {} + + class C + { + void M(System.Type type) {} + void M() where T : unmanaged {} + + void Test() + { + [|M(typeof(ValidType))|]; + } + } + """; + + string fixedSource = """ + struct ValidType {} + + class C + { + void M(System.Type type) {} + void M() where T : unmanaged {} + + void Test() + { + M(); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task ViolatesConstructorConstraint_NoDiagnostic_CS() + { + string source = """ + class ViolatingType + { + private ViolatingType() {} + } + + class C + { + void M(System.Type type) {} + void M() where T : new() {} + + void Test() + { + M(typeof(ViolatingType)); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task SatisfiesConstructorConstraint_OffersFixer_CS() + { + string source = """ + class ValidType + { + public ValidType() {} + } + + class C + { + void M(System.Type type) {} + void M() where T : new() {} + + void Test() + { + [|M(typeof(ValidType))|]; + } + } + """; + + string fixedSource = """ + class ValidType + { + public ValidType() {} + } + + class C + { + void M(System.Type type) {} + void M() where T : new() {} + + void Test() + { + M(); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task ViolatesTypeConstraint_NoDiagnostic_CS() + { + string source = """ + class ViolatingType {} + + class C + { + void M(System.Type type) {} + void M() where T : C {} + + void Test() + { + M(typeof(ViolatingType)); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task SatisfiesTypeConstraint_OffersFixer_CS() + { + string source = """ + class ValidType : C {} + + class C + { + void M(System.Type type) {} + void M() where T : C {} + + void Test() + { + [|M(typeof(ValidType))|]; + } + } + """; + + string fixedSource = """ + class ValidType : C {} + + class C + { + void M(System.Type type) {} + void M() where T : C {} + + void Test() + { + M(); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task SingleTypeArgument_OffersFixer_CS() + { + string source = """ + class C + { + void M(System.Type type) {} + void M() {} + + void Test() + { + [|M(typeof(C))|]; + } + } + """; + + string fixedSource = """ + class C + { + void M(System.Type type) {} + void M() {} + + void Test() + { + M(); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task MultipleTypeArguments_OffersFixer_CS() + { + string source = """ + class A {} + class B {} + + class C + { + void M(System.Type type1, System.Type type2, System.Type type3) {} + void M() {} + + void Test() + { + [|M(typeof(A), typeof(B), typeof(C))|]; + } + } + """; + + string fixedSource = """ + class A {} + class B {} + + class C + { + void M(System.Type type1, System.Type type2, System.Type type3) {} + void M() {} + + void Test() + { + M(); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task SingleTypeArgumentWithOtherArgument_OffersFixer_CS() + { + string source = """ + class C + { + void M(System.Type type, int x) {} + void M(int x) {} + + void Test() + { + [|M(typeof(C), 0)|]; + } + } + """; + + string fixedSource = """ + class C + { + void M(System.Type type, int x) {} + void M(int x) {} + + void Test() + { + M(0); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task MultipleTypeArgumentsWithOtherArgument_OffersFixer_CS() + { + string source = """ + class A {} + class B {} + + class C + { + void M(System.Type type1, System.Type type2, System.Type type3, int x) {} + void M(int x) {} + + void Test() + { + [|M(typeof(A), typeof(B), typeof(C), 0)|]; + } + } + """; + + string fixedSource = """ + class A {} + class B {} + + class C + { + void M(System.Type type1, System.Type type2, System.Type type3, int x) {} + void M(int x) {} + + void Test() + { + M(0); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task SingleTypeArgumentWithOtherArguments_OffersFixer_CS() + { + string source = """ + class C + { + void M(System.Type type, int x, string y, object z) {} + void M(int x, string y, object z) {} + + void Test() + { + [|M(typeof(C), 0, "Test", this)|]; + } + } + """; + + string fixedSource = """ + class C + { + void M(System.Type type, int x, string y, object z) {} + void M(int x, string y, object z) {} + + void Test() + { + M(0, "Test", this); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task MultipleTypeArgumentsWithOtherArguments_OffersFixer_CS() + { + string source = """ + class A {} + class B {} + + class C + { + void M(System.Type type1, System.Type type2, System.Type type3, int x, string y, object z) {} + void M(int x, string y, object z) {} + + void Test() + { + [|M(typeof(A), typeof(B), typeof(C), 0, "Test", this)|]; + } + } + """; + + string fixedSource = """ + class A {} + class B {} + + class C + { + void M(System.Type type1, System.Type type2, System.Type type3, int x, string y, object z) {} + void M(int x, string y, object z) {} + + void Test() + { + M(0, "Test", this); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task SingleTypeArgumentWithOtherGenericArgument_OffersFixer_CS() + { + string source = """ + class C + { + void M(System.Type type, object x) {} + void M(T x) {} + + void Test() + { + [|M(typeof(C), new C())|]; + } + } + """; + + string fixedSource = """ + class C + { + void M(System.Type type, object x) {} + void M(T x) {} + + void Test() + { + M(new C()); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task MultipleTypeArgumentsWithOtherGenericArgument_OffersFixer_CS() + { + string source = """ + class A {} + class B {} + + class C + { + void M(System.Type type1, System.Type type2, System.Type type3, object x) {} + void M(T1 x) {} + + void Test() + { + [|M(typeof(A), typeof(B), typeof(C), new A())|]; + } + } + """; + + string fixedSource = """ + class A {} + class B {} + + class C + { + void M(System.Type type1, System.Type type2, System.Type type3, object x) {} + void M(T1 x) {} + + void Test() + { + M(new A()); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task TypeParameterNotFirst_OffersFixer_CS() + { + string source = """ + class C + { + void M(int x, System.Type type) {} + void M(int x) {} + + void Test() + { + [|M(0, typeof(C))|]; + } + } + """; + + string fixedSource = """ + class C + { + void M(int x, System.Type type) {} + void M(int x) {} + + void Test() + { + M(0); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task ExtensionMethod_OffersFixer_CS() + { + string source = """ + public static class CExtensions + { + public static void M(this C c, System.Type type) {} + public static void M(this C c) {} + } + + public class C + { + void Test() + { + [|new C().M(typeof(C))|]; + } + } + """; + + string fixedSource = """ + public static class CExtensions + { + public static void M(this C c, System.Type type) {} + public static void M(this C c) {} + } + + public class C + { + void Test() + { + new C().M(); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task ExtensionMethodCalledDirectly_OffersFixer_CS() + { + string source = """ + public static class CExtensions + { + public static void M(this C c, System.Type type) {} + public static void M(this C c) {} + } + + public class C + { + void Test() + { + [|CExtensions.M(this, typeof(C))|]; + } + } + """; + + string fixedSource = """ + public static class CExtensions + { + public static void M(this C c, System.Type type) {} + public static void M(this C c) {} + } + + public class C + { + void Test() + { + CExtensions.M(this); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task TypeAlias_OffersFixer_CS() + { + string source = """ + using A = C; + + class C + { + void M(System.Type type, object x) {} + void M(T x) {} + + void Test() + { + [|M(typeof(A), new A())|]; + } + } + """; + + string fixedSource = """ + using A = C; + + class C + { + void M(System.Type type, object x) {} + void M(T x) {} + + void Test() + { + M(new A()); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task OptionalParameters_OffersFixer_CS() + { + string source = """ + class C + { + void M(System.Type type, int x, int y = 1, int z = 2) {} + void M(int x, int y = 1, int z = 2) {} + + void Test() + { + [|M(typeof(C), 0, 5)|]; + } + } + """; + + string fixedSource = """ + class C + { + void M(System.Type type, int x, int y = 1, int z = 2) {} + void M(int x, int y = 1, int z = 2) {} + + void Test() + { + M(0, 5); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task StaticMethods_OffersFixer_CS() + { + string source = """ + class C + { + static void M(System.Type type, object x) {} + static void M(T x) {} + + void Test() + { + [|M(typeof(C), this)|]; + } + } + """; + + string fixedSource = """ + class C + { + static void M(System.Type type, object x) {} + static void M(T x) {} + + void Test() + { + M(this); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task StaticMethodsInStaticClass_OffersFixer_CS() + { + string source = """ + public static class StaticClass + { + public static void M(System.Type type, object x) {} + public static void M(T x) {} + } + + class C + { + void Test() + { + [|StaticClass.M(typeof(C), this)|]; + } + } + """; + + string fixedSource = """ + public static class StaticClass + { + public static void M(System.Type type, object x) {} + public static void M(T x) {} + } + + class C + { + void Test() + { + StaticClass.M(this); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task StaticMethodsInStaticClassWithNamespace_OffersFixer_CS() + { + string source = """ + namespace TestNamespace + { + public static class StaticClass + { + public static void M(System.Type type, object x) {} + public static void M(T x) {} + } + } + + class C + { + void Test() + { + [|TestNamespace.StaticClass.M(typeof(C), this)|]; + } + } + """; + + string fixedSource = """ + namespace TestNamespace + { + public static class StaticClass + { + public static void M(System.Type type, object x) {} + public static void M(T x) {} + } + } + + class C + { + void Test() + { + TestNamespace.StaticClass.M(this); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task ReturnTypeIsNotCompatible_NoDiagnostic_CS() + { + string source = """ + class C + { + void Test() + { + System.Collections.Immutable.ImmutableHashSet x = System.Collections.Immutable.ImmutableHashSet.Create(typeof(C)); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task ReturnTypeIsIgnoredForExpressionStatement_OffersFixer_CS() + { + string source = """ + class C + { + void Test() + { + [|System.Collections.Immutable.ImmutableHashSet.Create(typeof(C))|]; + } + } + """; + + string fixedSource = """ + class C + { + void Test() + { + System.Collections.Immutable.ImmutableHashSet.Create(); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task UnnecessaryCastIsRemoved_OffersFixer_CS() + { + string source = """ + class C + { + object M(System.Type type, object x) { return x; } + T M(T x) { return x; } + + C Test() + { + return (C)[|M(typeof(C), this)|]; + } + } + """; + + string fixedSource = """ + class C + { + object M(System.Type type, object x) { return x; } + T M(T x) { return x; } + + C Test() + { + return M(this); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task NeededCastIsPreserved_OffersFixer_CS() + { + string source = """ + class C + { + object M(System.Type type, object x) { return x; } + object M(T x) { return x; } + + C Test() + { + return (C)[|M(typeof(C), this)|]; + } + } + """; + + string fixedSource = """ + class C + { + object M(System.Type type, object x) { return x; } + object M(T x) { return x; } + + C Test() + { + return (C)[|M(this)|]; + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task UnaryPostfixOperatorIsPreserved_OffersFixer_CS() + { + string source = """ + #nullable enable + + using System; + using System.Reflection; + + class C + { + object? M(System.Type type, object x) { return x; } + T? M(T x) { return x; } + + C Test() + { + var a = (Func)[|typeof(C).GetMethod("M", BindingFlags.Instance | BindingFlags.NonPublic)!.CreateDelegate(typeof(Func))|]; + return (C)[|M(typeof(C), this)|]!; + } + } + """; + + string fixedSource = """ + #nullable enable + + using System; + using System.Reflection; + + class C + { + object? M(System.Type type, object x) { return x; } + T? M(T x) { return x; } + + C Test() + { + var a = typeof(C).GetMethod("M", BindingFlags.Instance | BindingFlags.NonPublic)!.CreateDelegate>(); + return M(this)!; + } + } + """; + + var test = new VerifyCS.Test + { + TestCode = source, + FixedCode = fixedSource, + LanguageVersion = CodeAnalysis.CSharp.LanguageVersion.CSharp9, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50 + }; + + await test.RunAsync(); + } + + [Fact] + public async Task NullConditionalMemberAccessOperatorIsPreserved_OffersFixer_CS() + { + string source = """ + #nullable enable + + class C + { + object? M(System.Type type, object x) { return x; } + T? M(T x) { return x; } + + C? Test() + { + return ((C?)[|M(typeof(C), this)|])?.Other(); + } + + C Other() { return new C(); } + } + """; + + string fixedSource = """ + #nullable enable + + class C + { + object? M(System.Type type, object x) { return x; } + T? M(T x) { return x; } + + C? Test() + { + return M(this)?.Other(); + } + + C Other() { return new C(); } + } + """; + + var test = new VerifyCS.Test + { + TestCode = source, + FixedCode = fixedSource, + LanguageVersion = CodeAnalysis.CSharp.LanguageVersion.CSharp9 + }; + + await test.RunAsync(); + } + + [Fact] + public async Task NamedParametersArePreserved_OffersFixer_CS() + { + string source = """ + class C + { + void M(System.Type type, int x) {} + void M(int x) {} + + void Test() + { + [|M(x: 0, type: typeof(C))|]; + } + } + """; + + string fixedSource = """ + class C + { + void M(System.Type type, int x) {} + void M(int x) {} + + void Test() + { + M(x: 0); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task TriviaIsPreserved_OffersFixer_CS() + { + string source = """ + class C + { + void M(System.Type type) {} + void M() {} + + void Test() + { + // reticulates the splines + [|M(typeof(C))|]; + } + } + """; + + string fixedSource = """ + class C + { + void M(System.Type type) {} + void M() {} + + void Test() + { + // reticulates the splines + M(); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task TriviaIsPreservedWhenCastIsRemoved_OffersFixer_CS() + { + string source = """ + class C + { + object M(System.Type type, object x) { return x; } + T M(T x) { return x; } + + C M() + { + // reticulates the splines + return (C)[|M(typeof(C), this)|]; + } + } + """; + + string fixedSource = """ + class C + { + object M(System.Type type, object x) { return x; } + T M(T x) { return x; } + + C M() + { + // reticulates the splines + return M(this); + } + } + """; + + await VerifyCS.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task NoTypeArgument_NoDiagnostic_VB() + { + string source = """ + Class C + Sub M(x as Integer) : End Sub + Sub M(Of T)() : End Sub + + Sub Test() + M(0) + End Sub + End Class + """; + + await VerifyVB.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task RuntimeTypeArgument_NoDiagnostic_VB() + { + string source = """ + Class C + Sub M(type as System.Type) : End Sub + Sub M(Of T)() : End Sub + + Sub Test() + M(Me.GetType()) + End Sub + End Class + """; + + await VerifyVB.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task UnboundGenericTypeArgument_NoDiagnostic_VB() + { + string source = """ + Class ViolatingType(Of T) : End Class + + Class C + Sub M(type as System.Type) : End Sub + Sub M(Of T)() : End Sub + + Sub Test() + M(GetType(ViolatingType(Of ))) + End Sub + End Class + """; + + await VerifyVB.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task WrongArity_NoDiagnostic_VB() + { + string source = """ + Class C + Sub M(type1 as System.Type, type2 as System.Type) : End Sub + Sub M(Of T1)() : End Sub + Sub M(Of T1, T2, T3)() : End Sub + + Sub Test() + M(GetType(C), GetType(C)) + End Sub + End Class + """; + + await VerifyVB.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task WrongParameterCount_NoDiagnostic_VB() + { + string source = """ + Class C + Sub M(type as System.Type, x as Integer) : End Sub + Sub M(Of T)() : End Sub + Sub M(Of T)(x as Integer, y as Integer) : End Sub + + Sub Test() + M(GetType(C), 0) + End Sub + End Class + """; + + await VerifyVB.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task WrongParameterType_NoDiagnostic_VB() + { + string source = """ + Class C + Sub M(type as System.Type, x as Integer) : End Sub + Sub M(Of T)(x as String) : End Sub + + Sub Test() + M(GetType(C), 0) + End Sub + End Class + """; + + await VerifyVB.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task WrongParameterTypeWithOneMatching_NoDiagnostic_VB() + { + string source = """ + Class C + Sub M(type as System.Type, x as Integer, y as String) : End Sub + Sub M(Of T)(x as String, y as String) : End Sub + + Sub Test() + M(GetType(C), 0, String.Empty) + End Sub + End Class + """; + + await VerifyVB.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task SameAsContainingSymbol_NoDiagnostic_VB() + { + string source = """ + Class C + Function M(type as System.Type, x as Object) as Object + Return x + End Function + + Function M(Of T)(x as Object) as T + Return CType(M(GetType(T), x), T) + End Function + End Class + """; + + await VerifyVB.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task ViolatesValueTypeConstraint_NoDiagnostic_VB() + { + string source = """ + Class ViolatingType : End Class + + Class C + Sub M(type as System.Type) : End Sub + Sub M(Of T as Structure)() : End Sub + + Sub Test() + M(GetType(ViolatingType)) + End Sub + End Class + """; + + await VerifyVB.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task SatisfiesValueTypeConstraint_OffersFixer_VB() + { + string source = """ + Structure ValidType : End Structure + + Class C + Sub M(type as System.Type) : End Sub + Sub M(Of T as Structure)() : End Sub + + Sub Test() + [|M(GetType(ValidType))|] + End Sub + End Class + """; + + string fixedSource = """ + Structure ValidType : End Structure + + Class C + Sub M(type as System.Type) : End Sub + Sub M(Of T as Structure)() : End Sub + + Sub Test() + M(Of ValidType)() + End Sub + End Class + """; + + await VerifyVB.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task ViolatesReferenceTypeConstraint_NoDiagnostic_VB() + { + string source = """ + Structure ViolatingType : End Structure + + Class C + Sub M(type as System.Type) : End Sub + Sub M(Of T as Class)() : End Sub + + Sub Test() + M(GetType(ViolatingType)) + End Sub + End Class + """; + + await VerifyVB.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task SatisfiesReferenceTypeConstraint_OffersFixer_VB() + { + string source = """ + Class ValidType : End Class + + Class C + Sub M(type as System.Type) : End Sub + Sub M(Of T as Class)() : End Sub + + Sub Test() + [|M(GetType(ValidType))|] + End Sub + End Class + """; + + string fixedSource = """ + Class ValidType : End Class + + Class C + Sub M(type as System.Type) : End Sub + Sub M(Of T as Class)() : End Sub + + Sub Test() + M(Of ValidType)() + End Sub + End Class + """; + + await VerifyVB.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task ViolatesConstructorConstraint_NoDiagnostic_VB() + { + string source = """ + Class ViolatingType + Private Sub New() : End Sub + End Class + + Class C + Sub M(type as System.Type) : End Sub + Sub M(Of T as New)() : End Sub + + Sub Test() + M(GetType(ViolatingType)) + End Sub + End Class + """; + + await VerifyVB.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task SatisfiesConstructorConstraint_OffersFixer_VB() + { + string source = """ + Class ValidType + Public Sub New() : End Sub + End Class + + Class C + Sub M(type as System.Type) : End Sub + Sub M(Of T as New)() : End Sub + + Sub Test() + [|M(GetType(ValidType))|] + End Sub + End Class + """; + + string fixedSource = """ + Class ValidType + Public Sub New() : End Sub + End Class + + Class C + Sub M(type as System.Type) : End Sub + Sub M(Of T as New)() : End Sub + + Sub Test() + M(Of ValidType)() + End Sub + End Class + """; + + await VerifyVB.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task ViolatesTypeConstraint_NoDiagnostic_VB() + { + string source = """ + Class ViolatingType : End Class + + Class C + Sub M(type as System.Type) : End Sub + Sub M(Of T as C)() : End Sub + + Sub Test() + M(GetType(ViolatingType)) + End Sub + End Class + """; + + await VerifyVB.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task SatisfiesTypeConstraint_OffersFixer_VB() + { + string source = """ + Class ValidType + Inherits C + End Class + + Class C + Sub M(type as System.Type) : End Sub + Sub M(Of T as C)() : End Sub + + Sub Test() + [|M(GetType(ValidType))|] + End Sub + End Class + """; + + string fixedSource = """ + Class ValidType + Inherits C + End Class + + Class C + Sub M(type as System.Type) : End Sub + Sub M(Of T as C)() : End Sub + + Sub Test() + M(Of ValidType)() + End Sub + End Class + """; + + await VerifyVB.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task SingleTypeArgument_OffersFixer_VB() + { + string source = """ + Class C + Sub M(type as System.Type) : End Sub + Sub M(Of T)() : End Sub + + Sub Test() + [|M(GetType(C))|] + End Sub + End Class + """; + + string fixedSource = """ + Class C + Sub M(type as System.Type) : End Sub + Sub M(Of T)() : End Sub + + Sub Test() + M(Of C)() + End Sub + End Class + """; + + await VerifyVB.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task MultipleTypeArguments_OffersFixer_VB() + { + string source = """ + Class A : End Class + Class B : End Class + + Class C + Sub M(type1 as System.Type, type2 as System.Type, type3 as System.Type) : End Sub + Sub M(Of T1, T2, T3)() : End Sub + + Sub Test() + [|M(GetType(A), GetType(B), GetType(C))|] + End Sub + End Class + """; + + string fixedSource = """ + Class A : End Class + Class B : End Class + + Class C + Sub M(type1 as System.Type, type2 as System.Type, type3 as System.Type) : End Sub + Sub M(Of T1, T2, T3)() : End Sub + + Sub Test() + M(Of A, B, C)() + End Sub + End Class + """; + + await VerifyVB.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task SingleTypeArgumentWithOtherArgument_OffersFixer_VB() + { + string source = """ + Class C + Sub M(type as System.Type, x as Integer) : End Sub + Sub M(Of T)(x as Integer) : End Sub + + Sub Test() + [|M(GetType(C), 0)|] + End Sub + End Class + """; + + string fixedSource = """ + Class C + Sub M(type as System.Type, x as Integer) : End Sub + Sub M(Of T)(x as Integer) : End Sub + + Sub Test() + M(Of C)(0) + End Sub + End Class + """; + + await VerifyVB.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task MultipleTypeArgumentsWithOtherArgument_OffersFixer_VB() + { + string source = """ + Class A : End Class + Class B : End Class + + Class C + Sub M(type1 as System.Type, type2 as System.Type, type3 as System.Type, x as Integer) : End Sub + Sub M(Of T1, T2, T3)(x as Integer) : End Sub + + Sub Test() + [|M(GetType(A), GetType(B), GetType(C), 0)|] + End Sub + End Class + """; + + string fixedSource = """ + Class A : End Class + Class B : End Class + + Class C + Sub M(type1 as System.Type, type2 as System.Type, type3 as System.Type, x as Integer) : End Sub + Sub M(Of T1, T2, T3)(x as Integer) : End Sub + + Sub Test() + M(Of A, B, C)(0) + End Sub + End Class + """; + + await VerifyVB.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task SingleTypeArgumentWithOtherArguments_OffersFixer_VB() + { + string source = """ + Class C + Sub M(type as System.Type, x as Integer, y as String, z as Object) : End Sub + Sub M(Of T)(x as Integer, y as String, z as Object) : End Sub + + Sub Test() + [|M(GetType(C), 0, "Test", Me)|] + End Sub + End Class + """; + + string fixedSource = """ + Class C + Sub M(type as System.Type, x as Integer, y as String, z as Object) : End Sub + Sub M(Of T)(x as Integer, y as String, z as Object) : End Sub + + Sub Test() + M(Of C)(0, "Test", Me) + End Sub + End Class + """; + + await VerifyVB.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task MultipleTypeArgumentsWithOtherArguments_OffersFixer_VB() + { + string source = """ + Class A : End Class + Class B : End Class + + Class C + Sub M(type1 as System.Type, type2 as System.Type, type3 as System.Type, x as Integer, y as String, z as Object) : End Sub + Sub M(Of T1, T2, T3)(x as Integer, y as String, z as Object) : End Sub + + Sub Test() + [|M(GetType(A), GetType(B), GetType(C), 0, "Test", Me)|] + End Sub + End Class + """; + + string fixedSource = """ + Class A : End Class + Class B : End Class + + Class C + Sub M(type1 as System.Type, type2 as System.Type, type3 as System.Type, x as Integer, y as String, z as Object) : End Sub + Sub M(Of T1, T2, T3)(x as Integer, y as String, z as Object) : End Sub + + Sub Test() + M(Of A, B, C)(0, "Test", Me) + End Sub + End Class + """; + + await VerifyVB.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task SingleTypeArgumentWithOtherGenericArgument_OffersFixer_VB() + { + string source = """ + Class C + Sub M(type as System.Type, x as Object) : End Sub + Sub M(Of T)(x as T) : End Sub + + Sub Test() + [|M(GetType(C), new C())|] + End Sub + End Class + """; + + string fixedSource = """ + Class C + Sub M(type as System.Type, x as Object) : End Sub + Sub M(Of T)(x as T) : End Sub + + Sub Test() + M(new C()) + End Sub + End Class + """; + + await VerifyVB.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task MultipleTypeArgumentsWithOtherGenericArgument_OffersFixer_VB() + { + string source = """ + Class A : End Class + Class B : End Class + + Class C + Sub M(type1 as System.Type, type2 as System.Type, type3 as System.Type, x as Object) : End Sub + Sub M(Of T1, T2, T3)(x as T1) : End Sub + + Sub Test() + [|M(GetType(A), GetType(B), GetType(C), new A())|] + End Sub + End Class + """; + + string fixedSource = """ + Class A : End Class + Class B : End Class + + Class C + Sub M(type1 as System.Type, type2 as System.Type, type3 as System.Type, x as Object) : End Sub + Sub M(Of T1, T2, T3)(x as T1) : End Sub + + Sub Test() + M(Of A, B, C)(new A()) + End Sub + End Class + """; + + await VerifyVB.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task TypeParameterNotFirst_OffersFixer_VB() + { + string source = """ + Class C + Sub M(x as Integer, type as System.Type) : End Sub + Sub M(Of T)(x as Integer) : End Sub + + Sub Test() + [|M(0, GetType(C))|] + End Sub + End Class + """; + + string fixedSource = """ + Class C + Sub M(x as Integer, type as System.Type) : End Sub + Sub M(Of T)(x as Integer) : End Sub + + Sub Test() + M(Of C)(0) + End Sub + End Class + """; + + await VerifyVB.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task ExtensionMethod_OffersFixer_VB() + { + string source = """ + Module CExtensions + + Public Sub M(c as C, type as System.Type) : End Sub + + + Public Sub M(Of T)(C as C) : End Sub + End Module + + Class C + Sub Test() + Dim c as C = new C() + [|c.M(GetType(C))|] + End Sub + End Class + """; + + string fixedSource = """ + Module CExtensions + + Public Sub M(c as C, type as System.Type) : End Sub + + + Public Sub M(Of T)(C as C) : End Sub + End Module + + Class C + Sub Test() + Dim c as C = new C() + c.M(Of C)() + End Sub + End Class + """; + + await VerifyVB.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task ExtensionMethodCalledDirectly_OffersFixer_VB() + { + string source = """ + Module CExtensions + + Public Sub M(c as C, type as System.Type) : End Sub + + + Public Sub M(Of T)(C as C) : End Sub + End Module + + Class C + Sub Test() + Dim c as C = new C() + [|CExtensions.M(c, GetType(C))|] + End Sub + End Class + """; + + string fixedSource = """ + Module CExtensions + + Public Sub M(c as C, type as System.Type) : End Sub + + + Public Sub M(Of T)(C as C) : End Sub + End Module + + Class C + Sub Test() + Dim c as C = new C() + CExtensions.M(Of C)(c) + End Sub + End Class + """; + + await VerifyVB.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task TypeAlias_OffersFixer_VB() + { + string source = """ + Imports A = C + + Class C + Sub M(type as System.Type) : End Sub + Sub M(Of T)() : End Sub + + Sub Test() + [|M(GetType(A))|] + End Sub + End Class + """; + + string fixedSource = """ + Imports A = C + + Class C + Sub M(type as System.Type) : End Sub + Sub M(Of T)() : End Sub + + Sub Test() + M(Of A)() + End Sub + End Class + """; + + await VerifyVB.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task OptionalParameters_OffersFixer_VB() + { + string source = """ + Class C + Sub M(type as System.Type, x as Integer, Optional y as Integer = 1, Optional z as Integer = 2) : End Sub + Sub M(Of T)(x as Integer, Optional y as Integer = 1, Optional z as Integer = 2) : End Sub + + Sub Test() + [|M(GetType(C), 0, 5)|] + End Sub + End Class + """; + + string fixedSource = """ + Class C + Sub M(type as System.Type, x as Integer, Optional y as Integer = 1, Optional z as Integer = 2) : End Sub + Sub M(Of T)(x as Integer, Optional y as Integer = 1, Optional z as Integer = 2) : End Sub + + Sub Test() + M(Of C)(0, 5) + End Sub + End Class + """; + + await VerifyVB.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task StaticMethods_OffersFixer_VB() + { + string source = """ + Class C + Shared Sub M(type as System.Type) : End Sub + Shared Sub M(Of T)() : End Sub + + Sub Test() + [|M(GetType(C))|] + End Sub + End Class + """; + + string fixedSource = """ + Class C + Shared Sub M(type as System.Type) : End Sub + Shared Sub M(Of T)() : End Sub + + Sub Test() + M(Of C)() + End Sub + End Class + """; + + await VerifyVB.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task StaticMethodsWithNamespace_OffersFixer_VB() + { + string source = """ + Namespace TestNamespace + Module TestModule + Sub M(type as System.Type) : End Sub + Sub M(Of T)() : End Sub + End Module + End Namespace + + Class C + Sub Test() + [|TestNamespace.M(GetType(C))|] + End Sub + End Class + """; + + string fixedSource = """ + Namespace TestNamespace + Module TestModule + Sub M(type as System.Type) : End Sub + Sub M(Of T)() : End Sub + End Module + End Namespace + + Class C + Sub Test() + TestNamespace.M(Of C)() + End Sub + End Class + """; + + await VerifyVB.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task ReturnTypeIsNotCompatible_NoDiagnostic_VB() + { + string source = """ + Class C + Sub Test() + Dim x as System.Collections.Immutable.ImmutableHashSet(Of System.Type) = System.Collections.Immutable.ImmutableHashSet.Create(GetType(C)) + End Sub + End Class + """; + + await VerifyVB.VerifyCodeFixAsync(source, source); + } + + [Fact] + public async Task ReturnTypeIsIgnoredForExpressionStatement_OffersFixer_VB() + { + string source = """ + Class C + Sub Test() + [|System.Collections.Immutable.ImmutableHashSet.Create(GetType(C))|] + End Sub + End Class + """; + + string fixedSource = """ + Class C + Sub Test() + System.Collections.Immutable.ImmutableHashSet.Create(Of C)() + End Sub + End Class + """; + + await VerifyVB.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task UnnecessaryCastIsRemoved_OffersFixer_VB() + { + string source = """ + Class C + Function M(type as System.Type, x as Object) as Object + Return x + End Function + + Function M(Of T)(x as T) as T + Return x + End Function + + Sub Test() + Dim x As C = CType([|M(GetType(C), Me)|], C) + Dim y As C = DirectCast([|M(GetType(C), Me)|], C) + Dim z As C = TryCast([|M(GetType(C), Me)|], C) + End Sub + End Class + """; + + string fixedSource = """ + Class C + Function M(type as System.Type, x as Object) as Object + Return x + End Function + + Function M(Of T)(x as T) as T + Return x + End Function + + Sub Test() + Dim x As C = M(Me) + Dim y As C = M(Me) + Dim z As C = M(Me) + End Sub + End Class + """; + + await VerifyVB.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task NeededCastIsPreserved_OffersFixer_VB() + { + string source = """ + Class C + Function M(type as System.Type, x as Object) as Object + Return x + End Function + + Function M(Of T)(x as T) as Object + Return x + End Function + + Sub Test() + Dim x As C = CType([|M(GetType(C), Me)|], C) + Dim y As C = DirectCast([|M(GetType(C), Me)|], C) + Dim z As C = TryCast([|M(GetType(C), Me)|], C) + End Sub + End Class + """; + + string fixedSource = """ + Class C + Function M(type as System.Type, x as Object) as Object + Return x + End Function + + Function M(Of T)(x as T) as Object + Return x + End Function + + Sub Test() + Dim x As C = CType(M(Me), C) + Dim y As C = DirectCast(M(Me), C) + Dim z As C = TryCast(M(Me), C) + End Sub + End Class + """; + + await VerifyVB.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task NamedParametersArePreserved_OffersFixer_VB() + { + string source = """ + Class C + Sub M(type as System.Type, x as Integer) : End Sub + Sub M(Of T)(x as Integer) : End Sub + + Sub Test() + [|M(x:=0, type:=GetType(C))|] + End Sub + End Class + """; + + string fixedSource = """ + Class C + Sub M(type as System.Type, x as Integer) : End Sub + Sub M(Of T)(x as Integer) : End Sub + + Sub Test() + M(Of C)(x:=0) + End Sub + End Class + """; + + await VerifyVB.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task TriviaIsPreserved_OffersFixer_VB() + { + string source = """ + Class C + Sub M(type as System.Type, x as Integer) : End Sub + Sub M(Of T)(x as Integer) : End Sub + + Sub Test() + ' reticulates the splines + [|M(GetType(C), 0)|] + End Sub + End Class + """; + + string fixedSource = """ + Class C + Sub M(type as System.Type, x as Integer) : End Sub + Sub M(Of T)(x as Integer) : End Sub + + Sub Test() + ' reticulates the splines + M(Of C)(0) + End Sub + End Class + """; + + await VerifyVB.VerifyCodeFixAsync(source, fixedSource); + } + + [Fact] + public async Task TriviaIsPreservedWhenCastIsRemoved_OffersFixer_VB() + { + string source = """ + Class C + Function M(type as System.Type, x as Object) as Object + Return x + End Function + + Function M(Of T)(x as T) as T + Return x + End Function + + Sub Test() + ' reticulates the splines + Dim x As C = CType([|M(GetType(C), Me)|], C) + End Sub + End Class + """; + + string fixedSource = """ + Class C + Function M(type as System.Type, x as Object) as Object + Return x + End Function + + Function M(Of T)(x as T) as T + Return x + End Function + + Sub Test() + ' reticulates the splines + Dim x As C = M(Me) + End Sub + End Class + """; + + await VerifyVB.VerifyCodeFixAsync(source, fixedSource); + } + } +} diff --git a/src/NetAnalyzers/VisualBasic/Microsoft.NetCore.Analyzers/Usage/BasicPreferGenericOverloads.Fixer.vb b/src/NetAnalyzers/VisualBasic/Microsoft.NetCore.Analyzers/Usage/BasicPreferGenericOverloads.Fixer.vb new file mode 100644 index 0000000000..ecdc91b17f --- /dev/null +++ b/src/NetAnalyzers/VisualBasic/Microsoft.NetCore.Analyzers/Usage/BasicPreferGenericOverloads.Fixer.vb @@ -0,0 +1,81 @@ +' Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. + +Imports System.Composition +Imports System.Threading +Imports Analyzer.Utilities.Extensions +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.CodeFixes +Imports Microsoft.CodeAnalysis.Editing +Imports Microsoft.CodeAnalysis.Operations +Imports Microsoft.CodeAnalysis.Simplification +Imports Microsoft.CodeAnalysis.VisualBasic +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax +Imports Microsoft.NetCore.Analyzers.Usage +Imports Microsoft.NetCore.Analyzers.Usage.PreferGenericOverloadsAnalyzer + +Namespace Microsoft.NetCore.VisualBasic.Analyzers.Usage + ''' + ''' CA2263: + ''' + + Public NotInheritable Class BasicPreferGenericOverloadsFixer + Inherits PreferGenericOverloadsFixer + + Protected Overrides Async Function ReplaceWithGenericCallAsync(document As Document, + invocation As IInvocationOperation, + cancellationToken As CancellationToken) As Task(Of Document) + Dim invocationContext As RuntimeTypeInvocationContext = Nothing + + If Not RuntimeTypeInvocationContext.TryGetContext(invocation, invocationContext) Then + Return document + End If + + Dim modifiedInvocationSyntax = BasicPreferGenericOverloadsAnalyzer.GetModifiedInvocationSyntax(invocationContext) + + If TypeOf modifiedInvocationSyntax IsNot InvocationExpressionSyntax Then + Return document + End If + + ' Analyzers are not allowed to have a reference to Simplifier, so add the additional annotation here instead. + Dim invocationExpressionSyntax = CType(modifiedInvocationSyntax, InvocationExpressionSyntax) + invocationExpressionSyntax = invocationExpressionSyntax.WithExpression(invocationExpressionSyntax.Expression.WithAdditionalAnnotations(Simplifier.Annotation)) + + Dim editor = Await DocumentEditor.CreateAsync(document, cancellationToken).ConfigureAwait(False) + + If TypeOf invocationContext.Parent Is IConversionOperation And + TypeOf invocationContext.Parent.Syntax Is CastExpressionSyntax And + invocationContext.SemanticModel IsNot Nothing Then + Dim conversionOperation = CType(invocationContext.Parent, IConversionOperation) + Dim castExpressionSyntax = CType(invocationContext.Parent.Syntax, CastExpressionSyntax) + Dim typeInfo = invocationContext.Invocation.SemanticModel.GetSpeculativeTypeInfo(invocationContext.Syntax.SpanStart, + invocationExpressionSyntax, + SpeculativeBindingOption.BindAsExpression) + If typeInfo.ConvertedType.IsAssignableTo(conversionOperation.Type, invocationContext.SemanticModel.Compilation) Then + ' Add a simplifier annotation to the parent to remove no longer needed parentheses. + If TypeOf castExpressionSyntax.Parent Is ParenthesizedExpressionSyntax Then + Dim parenthesizedExpressionSyntax = CType(castExpressionSyntax.Parent, ParenthesizedExpressionSyntax) + + editor.ReplaceNode(parenthesizedExpressionSyntax, + parenthesizedExpressionSyntax _ + .ReplaceNode(castExpressionSyntax, + castExpressionSyntax.Expression _ + .ReplaceNode(invocationContext.Syntax, invocationExpressionSyntax) _ + .WithTriviaFrom(castExpressionSyntax)) _ + .WithAdditionalAnnotations(Simplifier.Annotation)) + Else + editor.ReplaceNode(castExpressionSyntax, + castExpressionSyntax.Expression _ + .ReplaceNode(invocationContext.Syntax, invocationExpressionSyntax) _ + .WithTriviaFrom(castExpressionSyntax)) + End If + Else + editor.ReplaceNode(invocationContext.Syntax, invocationExpressionSyntax) + End If + Else + editor.ReplaceNode(invocationContext.Syntax, invocationExpressionSyntax) + End If + + Return document.WithSyntaxRoot(editor.GetChangedRoot()) + End Function + End Class +End Namespace diff --git a/src/NetAnalyzers/VisualBasic/Microsoft.NetCore.Analyzers/Usage/BasicPreferGenericOverloads.vb b/src/NetAnalyzers/VisualBasic/Microsoft.NetCore.Analyzers/Usage/BasicPreferGenericOverloads.vb new file mode 100644 index 0000000000..18977aab50 --- /dev/null +++ b/src/NetAnalyzers/VisualBasic/Microsoft.NetCore.Analyzers/Usage/BasicPreferGenericOverloads.vb @@ -0,0 +1,58 @@ +' Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. + +Imports System.Diagnostics.CodeAnalysis +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.VisualBasic +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax +Imports Microsoft.NetCore.Analyzers.Usage + +Namespace Microsoft.NetCore.VisualBasic.Analyzers.Usage + ''' + ''' CA2263: + ''' + + Public NotInheritable Class BasicPreferGenericOverloadsAnalyzer + Inherits PreferGenericOverloadsAnalyzer + + Protected Overrides Function TryGetModifiedInvocationSyntax(invocationContext As RuntimeTypeInvocationContext, + ByRef modifiedInvocationSyntax As SyntaxNode) As Boolean + modifiedInvocationSyntax = GetModifiedInvocationSyntax(invocationContext) + + Return modifiedInvocationSyntax IsNot Nothing + End Function + + ' Expose as friend shared to allow the fixer to also call this method. + Friend Shared Function GetModifiedInvocationSyntax(invocationContext As RuntimeTypeInvocationContext) As SyntaxNode + If TypeOf invocationContext.Syntax IsNot InvocationExpressionSyntax Then + Return Nothing + End If + + Dim invocationSyntax = CType(invocationContext.Syntax, InvocationExpressionSyntax) + Dim typeArgumentSyntax = invocationContext.TypeArguments.Select(Function(t) SyntaxFactory.ParseTypeName(t.ToDisplayString())) + Dim otherArgumentsSyntax = invocationContext.OtherArguments _ + .Where(Function(a) a.ArgumentKind <> Operations.ArgumentKind.DefaultValue) _ + .Select(Function(a) a.Syntax) _ + .OfType(Of ArgumentSyntax) + Dim methodNameSyntax = + SyntaxFactory.GenericName(SyntaxFactory.Identifier(invocationContext.Method.Name), + SyntaxFactory.TypeArgumentList(typeArgumentSyntax.ToArray())) + Dim modifiedInvocationExpression = invocationSyntax.Expression + + If TypeOf modifiedInvocationExpression Is MemberAccessExpressionSyntax Then + Dim memberAccessExpressionSyntax = CType(modifiedInvocationExpression, MemberAccessExpressionSyntax) + modifiedInvocationExpression = memberAccessExpressionSyntax.WithName(methodNameSyntax) + ElseIf TypeOf modifiedInvocationExpression Is IdentifierNameSyntax Then + Dim identifierNameSyntax = CType(modifiedInvocationExpression, IdentifierNameSyntax) + modifiedInvocationExpression = methodNameSyntax + Else + Return Nothing + End If + + Return invocationSyntax _ + .WithExpression(modifiedInvocationExpression) _ + .WithArgumentList(SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(otherArgumentsSyntax))) _ + .WithTriviaFrom(invocationSyntax) + End Function + End Class +End Namespace diff --git a/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt b/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt index 06f507268f..b21dc7b062 100644 --- a/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt +++ b/src/Utilities/Compiler/DiagnosticCategoryAndIdRanges.txt @@ -14,7 +14,7 @@ Globalization: CA2101, CA1300-CA1311 Mobility: CA1600-CA1601 Performance: HA, CA1800-CA1869 Security: CA2100-CA2153, CA2300-CA2330, CA3000-CA3147, CA5300-CA5405 -Usage: CA1801, CA1806, CA1816, CA2200-CA2209, CA2211-CA2261 +Usage: CA1801, CA1806, CA1816, CA2200-CA2209, CA2211-CA2263 Naming: CA1700-CA1727 Interoperability: CA1400-CA1422 Maintainability: CA1500-CA1513