| File: MetaAnalyzers\PreferIsKindAnalyzer.cs | Web Access |
| Project: src\roslyn\src\RoslynAnalyzers\Microsoft.CodeAnalysis.Analyzers\Core\Microsoft.CodeAnalysis.Analyzers.csproj (Microsoft.CodeAnalysis.Analyzers) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System.Collections.Generic; using System.Collections.Immutable; using Analyzer.Utilities; using Analyzer.Utilities.Extensions; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; namespace Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers { using static CodeAnalysisDiagnosticsResources; /// <summary> /// RS1034: <inheritdoc cref="PreferIsKindTitle"/> /// </summary> [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] public sealed class PreferIsKindAnalyzer : DiagnosticAnalyzer { internal static readonly DiagnosticDescriptor Rule = new( DiagnosticIds.PreferIsKindRuleId, CreateLocalizableResourceString(nameof(PreferIsKindTitle)), CreateLocalizableResourceString(nameof(PreferIsKindMessage)), DiagnosticCategory.MicrosoftCodeAnalysisPerformance, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: CreateLocalizableResourceString(nameof(PreferIsKindDescription)), helpLinkUri: null, customTags: WellKnownDiagnosticTagsExtensions.Telemetry); public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(Rule); public override void Initialize(AnalysisContext context) { context.EnableConcurrentExecution(); context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); context.RegisterCompilationStartAction(context => { // Kind() methods // Microsoft.CodeAnalysis.CSharp.CSharpExtensions.Kind // Microsoft.CodeAnalysis.VisualBasic.VisualBasicExtensions.Kind // // IsKind() methods // Microsoft.CodeAnalysis.CSharpExtensions.IsKind // Microsoft.CodeAnalysis.VisualBasicExtensions.IsKind Dictionary<INamedTypeSymbol, INamedTypeSymbol> containingTypeMap = []; if (context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisCSharpCSharpExtensions) is { } csharpKindExtensions && context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisCSharpExtensions) is { } csharpIsKindExtensions) { containingTypeMap[csharpKindExtensions] = csharpIsKindExtensions; } if (context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisVisualBasicVisualBasicExtensions) is { } vbKindExtensions && context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisVisualBasicExtensions) is { } vbIsKindExtensions) { containingTypeMap[vbKindExtensions] = vbIsKindExtensions; } if (containingTypeMap.Count > 0) { context.RegisterOperationAction(context => HandleBinaryOperation(context, containingTypeMap), OperationKind.Binary); } }); } private static void HandleBinaryOperation(OperationAnalysisContext context, Dictionary<INamedTypeSymbol, INamedTypeSymbol> containingTypeMap) { var operation = (IBinaryOperation)context.Operation; if (operation.OperatorKind is not (BinaryOperatorKind.Equals or BinaryOperatorKind.NotEquals)) { return; } var possibleInvocation = operation.LeftOperand.WalkDownConversion(); if (possibleInvocation is IConditionalAccessOperation conditionalAccess) { // We don't currently report on Nullable<SyntaxToken>. If we'll report that in the future, the codefix must behave correctly. if (conditionalAccess.Operation.Type!.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T) { return; } possibleInvocation = conditionalAccess.WhenNotNull; } if (possibleInvocation is IInvocationOperation { TargetMethod: { Name: "Kind", ContainingType: var containingType } } && containingTypeMap.TryGetValue(containingType, out _)) { context.ReportDiagnostic(operation.LeftOperand.CreateDiagnostic(Rule)); } } } }