| File: CallAnalysis\Arrays.cs | Web Access |
| Project: src\src\Analyzers\Microsoft.Analyzers.Extra\Microsoft.Analyzers.Extra.csproj (Microsoft.Analyzers.Extra) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; namespace Microsoft.Extensions.ExtraAnalyzers.CallAnalysis; /// <summary> /// Recommends replacing dictionaries and sets indexed by [enum|byte|sbyte] with simple arrays instead. /// </summary> internal sealed class Arrays { private static readonly string[] _collectionTypes = new[] { "System.Collections.Generic.Dictionary`2", "System.Collections.Generic.HashSet`1", "System.Collections.Generic.SortedDictionary`2", "System.Collections.Generic.SortedSet`1", "System.Collections.Immutable.ImmutableDictionary`2", "System.Collections.Immutable.ImmutableHashSet`1", "System.Collections.Immutable.ImmutableSortedDictionary`2", "System.Collections.Immutable.ImmutableSortedSet`1", "System.Collections.Frozen.FrozenDictionary`2", "System.Collections.Frozen.FrozenSet`1", }; private static readonly Dictionary<string, string[]> _collectionFactories = new() { ["System.Collections.Immutable.ImmutableDictionary"] = new[] { "Create", "CreateRange", }, ["System.Collections.Immutable.ImmutableHashSet"] = new[] { "Create", "CreateRange", }, ["System.Collections.Immutable.ImmutableSortedDictionary"] = new[] { "Create", "CreateRange", }, ["System.Collections.Immutable.ImmutableSortedSet"] = new[] { "Create", "CreateRange", }, ["System.Collections.Immutable.ImmutableDictionary`2+Builder"] = new[] { "ToImmutable", }, ["System.Collections.Immutable.ImmutableHashSet`1+Builder"] = new[] { "ToImmutable", }, ["System.Collections.Immutable.ImmutableSortedDictionary`2+Builder"] = new[] { "ToImmutable", }, ["System.Collections.Immutable.ImmutableSortedSet`1+Builder"] = new[] { "ToImmutable", }, }; public Arrays(CallAnalyzer.Registrar reg) { reg.RegisterConstructors(_collectionTypes, HandleConstructor); reg.RegisterMethods(_collectionFactories, HandleMethod); var freezer = reg.Compilation.GetTypeByMetadataName("System.Collections.Frozen.FrozenDictionary"); if (freezer != null) { foreach (var method in freezer.GetMembers("ToFrozenDictionary").OfType<IMethodSymbol>().Where(m => m.TypeParameters.Length == 2)) { reg.RegisterMethod(method, HandleMethod); } } freezer = reg.Compilation.GetTypeByMetadataName("System.Collections.Frozen.FrozenSet"); if (freezer != null) { foreach (var method in freezer.GetMembers("ToFrozenSet").OfType<IMethodSymbol>().Where(m => m.TypeParameters.Length == 1)) { reg.RegisterMethod(method, HandleMethod); } } static void HandleMethod(OperationAnalysisContext context, IInvocationOperation op) => HandleSuspectType(context, (INamedTypeSymbol)op.TargetMethod.ReturnType, op.Syntax.GetLocation()); static void HandleConstructor(OperationAnalysisContext context, IObjectCreationOperation op) => HandleSuspectType(context, (INamedTypeSymbol)op.Type!, op.Syntax.GetLocation()); static void HandleSuspectType(OperationAnalysisContext context, INamedTypeSymbol type, Location loc) { var keyType = type.TypeArguments[0]; if (keyType.TypeKind == TypeKind.Enum || keyType.SpecialType == SpecialType.System_Byte || keyType.SpecialType == SpecialType.System_SByte) { if (keyType.TypeKind == TypeKind.Enum) { var flagsAttr = context.Compilation.GetTypeByMetadataName("System.FlagsAttribute"); if (keyType.GetAttributes().Any(a => a.AttributeClass != null && SymbolEqualityComparer.Default.Equals(a.AttributeClass, flagsAttr))) { // not for [Flags] enums return; } } var valueType = keyType; if (type.TypeArguments.Length == 2) { valueType = type.TypeArguments[1]; } var diagnostic = Diagnostic.Create(DiagDescriptors.Arrays, loc, valueType.ToDisplayString(), type.ToDisplayString()); context.ReportDiagnostic(diagnostic); } } } }