| File: ImmutableArrayBoxingAnalyzer.cs | Web Access |
| Project: src\roslyn\src\RoslynAnalyzers\Roslyn.Diagnostics.Analyzers\Core\Roslyn.Diagnostics.Analyzers.csproj (Roslyn.Diagnostics.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.Immutable; using System.Linq; using Analyzer.Utilities; using Analyzer.Utilities.Extensions; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; namespace Roslyn.Diagnostics.Analyzers { using static RoslynDiagnosticsAnalyzersResources; /// <summary> /// RS0066: <inheritdoc cref="ImmutableArrayBoxingTitle"/> /// </summary> #pragma warning disable RS1004 // Recommend adding language support to diagnostic analyzer [DiagnosticAnalyzer(LanguageNames.CSharp)] #pragma warning restore RS1004 // Recommend adding language support to diagnostic analyzer public class ImmutableArrayBoxingAnalyzer : DiagnosticAnalyzer { internal static readonly DiagnosticDescriptor Rule = new( RoslynDiagnosticIds.ImmutableArrayBoxingRuleId, CreateLocalizableResourceString(nameof(ImmutableArrayBoxingTitle)), CreateLocalizableResourceString(nameof(ImmutableArrayBoxingMessage)), DiagnosticCategory.RoslynDiagnosticsPerformance, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: CreateLocalizableResourceString(nameof(ImmutableArrayBoxingDescription)), helpLinkUri: null, customTags: WellKnownDiagnosticTagsExtensions.Telemetry); public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule); public override void Initialize(AnalysisContext context) { context.EnableConcurrentExecution(); context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); context.RegisterCompilationStartAction(context => { var immutableArray = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemCollectionsImmutableImmutableArray1); if (immutableArray is null) return; var readOnlyListExtensions = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemCollectionsGenericReadOnlyListExtensions); if (readOnlyListExtensions is null) return; var enumerableExtensions = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemCollectionsGenericEnumerableExtensions); if (enumerableExtensions is null) return; context.RegisterOperationAction( context => AnalyzeInvocation(context, immutableArray, readOnlyListExtensions, enumerableExtensions), OperationKind.Invocation); }); } private static void AnalyzeInvocation( OperationAnalysisContext context, INamedTypeSymbol immutableArrayType, INamedTypeSymbol readOnlyListExtensionsType, INamedTypeSymbol enumerableExtensionsType) { var invocation = (IInvocationOperation)context.Operation; var targetMethod = invocation.TargetMethod.ReducedFrom ?? invocation.TargetMethod; var isReadOnlyListExtensions = Equals(targetMethod.OriginalDefinition.ContainingType, readOnlyListExtensionsType); var isEnumerableExtensions = Equals(targetMethod.OriginalDefinition.ContainingType, enumerableExtensionsType); if (!isReadOnlyListExtensions && !isEnumerableExtensions) return; var instance = invocation.Instance ?? invocation.Arguments.FirstOrDefault()?.Value; if (instance is not IConversionOperation conversionOperation) return; if (conversionOperation.Type?.TypeKind != TypeKind.Interface) return; if (conversionOperation.Operand.Type is not INamedTypeSymbol operandType || !Equals(operandType.OriginalDefinition, immutableArrayType)) { return; } var typeName = isReadOnlyListExtensions ? WellKnownTypeNames.SystemCollectionsGenericReadOnlyListExtensions : WellKnownTypeNames.SystemCollectionsGenericEnumerableExtensions; context.ReportDiagnostic(instance.CreateDiagnostic(Rule, $"{typeName}.{targetMethod.Name}")); } } }