From 76599265ecd2299b439894afef09f5eddc02b183 Mon Sep 17 00:00:00 2001 From: paulomorgado <470455+paulomorgado@users.noreply.github.com> Date: Sat, 10 Aug 2019 21:53:49 +0100 Subject: [PATCH] workaround for ImmutableArray --- ...InsteadOfCountMethodWhenAvailable.Fixer.cs | 6 - ...opertyInsteadOfCountMethodWhenAvailable.cs | 181 ++++++++++-------- ...yInsteadOfCountMethodWhenAvailableTests.cs | 23 +++ 3 files changed, 127 insertions(+), 83 deletions(-) diff --git a/src/Microsoft.NetCore.Analyzers/Core/Performance/UsePropertyInsteadOfCountMethodWhenAvailable.Fixer.cs b/src/Microsoft.NetCore.Analyzers/Core/Performance/UsePropertyInsteadOfCountMethodWhenAvailable.Fixer.cs index dc795a00e2..d6ceeb8774 100644 --- a/src/Microsoft.NetCore.Analyzers/Core/Performance/UsePropertyInsteadOfCountMethodWhenAvailable.Fixer.cs +++ b/src/Microsoft.NetCore.Analyzers/Core/Performance/UsePropertyInsteadOfCountMethodWhenAvailable.Fixer.cs @@ -1,18 +1,12 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; -using System.Collections; -using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; -using System.Linq.Expressions; using System.Threading; using System.Threading.Tasks; -using Analyzer.Utilities; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.Formatting; diff --git a/src/Microsoft.NetCore.Analyzers/Core/Performance/UsePropertyInsteadOfCountMethodWhenAvailable.cs b/src/Microsoft.NetCore.Analyzers/Core/Performance/UsePropertyInsteadOfCountMethodWhenAvailable.cs index 3193067371..8744b7f84e 100644 --- a/src/Microsoft.NetCore.Analyzers/Core/Performance/UsePropertyInsteadOfCountMethodWhenAvailable.cs +++ b/src/Microsoft.NetCore.Analyzers/Core/Performance/UsePropertyInsteadOfCountMethodWhenAvailable.cs @@ -1,16 +1,12 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Collections; using System.Collections.Immutable; -using System.Diagnostics; using System.Linq; using Analyzer.Utilities; -using Analyzer.Utilities.Extensions; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; -using Microsoft.NetCore.Analyzers; namespace Microsoft.NetCore.Analyzers.Performance { @@ -71,10 +67,7 @@ private static void OnCompilationStart(CompilationStartAnalysisContext context) { var operationActionsContext = new UsePropertyInsteadOfCountMethodWhenAvailableAnalyzer.OperationActionsContext( context.Compilation, - enumerableType, - WellKnownTypes.ICollection(context.Compilation)?.GetMembers(CountPropertyName).OfType().SingleOrDefault(), - WellKnownTypes.GenericICollection(context.Compilation)?.GetMembers(CountPropertyName).OfType().SingleOrDefault(), - WellKnownTypes.ImmutableArray(context.Compilation)); + enumerableType); var operationActionsHandler = context.Compilation.Language == LanguageNames.CSharp ? (OperationActionsHandler)new CSharpOperationActionsHandler(operationActionsContext) @@ -86,22 +79,114 @@ private static void OnCompilationStart(CompilationStartAnalysisContext context) } } + private void AnalyzeInvocationOperation(OperationAnalysisContext obj) + { + throw new NotImplementedException(); + } + private sealed class OperationActionsContext { - public OperationActionsContext(Compilation compilation, INamedTypeSymbol enumerableType, IPropertySymbol collectionCountProperty, IPropertySymbol collectionOfTCountProperty, INamedTypeSymbol immutableArrayType) + private /*readonly*/ Lazy _immutableArrayType; + private readonly Lazy _iCollectionCountProperty; + private readonly Lazy _iCollectionOfType; + private readonly Lazy _iCollectionOfTCountProperty; + + public OperationActionsContext(Compilation compilation, INamedTypeSymbol enumerableType) { Compilation = compilation; EnumerableType = enumerableType; - CollectionCountProperty = collectionCountProperty; - CollectionOfTCountProperty = collectionOfTCountProperty; - ImmutableArrayType = immutableArrayType; + _immutableArrayType = new Lazy(() => Compilation.GetTypeByMetadataName("System.Collections.Immutable.ImmutableArray`1"), true); + _iCollectionCountProperty = new Lazy(() => WellKnownTypes.ICollection(Compilation)?.GetMembers(CountPropertyName).OfType().Single(), true); + _iCollectionOfType = new Lazy(() => WellKnownTypes.GenericICollection(Compilation), true); + _iCollectionOfTCountProperty = new Lazy(() => ICollectionOfTType?.GetMembers(CountPropertyName).OfType().Single(), true); + } + + internal Compilation Compilation { get; } + + internal INamedTypeSymbol EnumerableType { get; } + + internal IPropertySymbol ICollectionCountProperty => _iCollectionCountProperty.Value; + + internal IPropertySymbol ICollectionOfTCountProperty => _iCollectionOfTCountProperty.Value; + + internal INamedTypeSymbol ICollectionOfTType => _iCollectionOfType.Value; + + internal INamedTypeSymbol ImmutableArrayType => _immutableArrayType.Value; + + internal bool IsImmutableArrayType(ITypeSymbol typeSymbol) + { + if (typeSymbol is INamedTypeSymbol namedTypeSymbol && namedTypeSymbol.ConstructedFrom is INamedTypeSymbol constructedFrom) + { + if (ImmutableArrayType is null && + constructedFrom.MetadataName.ToString().Equals("ImmutableArray`1", StringComparison.Ordinal) && + constructedFrom.ContainingNamespace.ToString().Equals("System.Collections.Immutable", StringComparison.Ordinal)) + { + _immutableArrayType = new Lazy(() => constructedFrom, true); + return true; + } + + return constructedFrom.Equals(ImmutableArrayType); + } + + return false; } - public Compilation Compilation { get; } - public INamedTypeSymbol EnumerableType { get; } - public IPropertySymbol CollectionCountProperty { get; } - public IPropertySymbol CollectionOfTCountProperty { get; } - public INamedTypeSymbol ImmutableArrayType { get; } + internal bool IsICollectionImplementation(ITypeSymbol invocationTarget) + => this.ICollectionCountProperty is object && + invocationTarget.FindImplementationForInterfaceMember(this.ICollectionCountProperty) is IPropertySymbol countProperty && + !countProperty.ExplicitInterfaceImplementations.Any(); + + internal bool IsICollectionOfTImplementation(ITypeSymbol invocationTarget) + { + if (ICollectionOfTType is null) + { + return false; + } + + if (isCollectionOfTInterface(invocationTarget)) + { + return true; + } + + if (invocationTarget.TypeKind == TypeKind.Interface) + { + if (invocationTarget.GetMembers(CountPropertyName).OfType().Any()) + { + return false; + } + + foreach (var @interface in invocationTarget.AllInterfaces) + { + if (@interface.OriginalDefinition is INamedTypeSymbol originalInterfaceDefinition && + isCollectionOfTInterface(originalInterfaceDefinition)) + { + return true; + } + } + } + else + { + foreach (var @interface in invocationTarget.AllInterfaces) + { + if (@interface.OriginalDefinition is INamedTypeSymbol originalInterfaceDefinition && + isCollectionOfTInterface(originalInterfaceDefinition)) + { + if (invocationTarget.FindImplementationForInterfaceMember(@interface.GetMembers(CountPropertyName)[0]) is IPropertySymbol propertyImplementation && + !propertyImplementation.ExplicitInterfaceImplementations.Any()) + { + return true; + } + } + } + } + + return false; + + bool isCollectionOfTInterface(ITypeSymbol type) + { + return type.OriginalDefinition?.Equals(this.ICollectionOfTType) ?? false; + } + } } /// @@ -140,75 +225,17 @@ internal void AnalyzeInvocationOperation(OperationAnalysisContext context) private string GetReplacementProperty(ITypeSymbol invocationTarget) { - if (invocationTarget.TypeKind == TypeKind.Array) - { - return LengthPropertyName; - } - - if (invocationTarget.OriginalDefinition is ITypeSymbol originalDefinition && - originalDefinition.MetadataName.ToString().Equals(typeof(ImmutableArray<>).Name, StringComparison.Ordinal) && - originalDefinition.ContainingNamespace.ToString().Equals(typeof(ImmutableArray<>).Namespace, StringComparison.Ordinal)) + if ((invocationTarget.TypeKind == TypeKind.Array) || Context.IsImmutableArrayType(invocationTarget)) { return LengthPropertyName; } - if (invocationTarget.FindImplementationForInterfaceMember(this.Context.CollectionCountProperty) is IPropertySymbol countProperty && - !countProperty.ExplicitInterfaceImplementations.Any()) - { - return CountPropertyName; - } - - if (findImplementationForCollectionOfTInterfaceCountProperty(invocationTarget)) + if (Context.IsICollectionImplementation(invocationTarget) || Context.IsICollectionOfTImplementation(invocationTarget)) { return CountPropertyName; } return null; - - bool findImplementationForCollectionOfTInterfaceCountProperty(ITypeSymbol invocationTarget) - { - if (isCollectionOfTInterface(invocationTarget)) - { - return true; - } - - if (invocationTarget.TypeKind == TypeKind.Interface) - { - if (invocationTarget.GetMembers(CountPropertyName).OfType().Any()) - { - return false; - } - - foreach (var @interface in invocationTarget.AllInterfaces) - { - if (@interface.OriginalDefinition is INamedTypeSymbol originalInterfaceDefinition && - isCollectionOfTInterface(originalInterfaceDefinition)) - { - return true; - } - } - } - else - { - foreach (var @interface in invocationTarget.AllInterfaces) - { - if (@interface.OriginalDefinition is INamedTypeSymbol originalInterfaceDefinition && - isCollectionOfTInterface(originalInterfaceDefinition)) - { - if (invocationTarget.FindImplementationForInterfaceMember(@interface.GetMembers(CountPropertyName)[0]) is IPropertySymbol propertyImplementation && - !propertyImplementation.ExplicitInterfaceImplementations.Any()) - { - return true; - } - } - } - } - - return false; - - bool isCollectionOfTInterface(ITypeSymbol type) - => type.OriginalDefinition?.Equals(this.Context.CollectionOfTCountProperty.ContainingSymbol) ?? false; - } } } diff --git a/src/Microsoft.NetCore.Analyzers/UnitTests/Performance/UsePropertyInsteadOfCountMethodWhenAvailableTests.cs b/src/Microsoft.NetCore.Analyzers/UnitTests/Performance/UsePropertyInsteadOfCountMethodWhenAvailableTests.cs index 31524ed4f3..32cb97aabf 100644 --- a/src/Microsoft.NetCore.Analyzers/UnitTests/Performance/UsePropertyInsteadOfCountMethodWhenAvailableTests.cs +++ b/src/Microsoft.NetCore.Analyzers/UnitTests/Performance/UsePropertyInsteadOfCountMethodWhenAvailableTests.cs @@ -7,6 +7,7 @@ using System.Dynamic; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Operations; +using Microsoft.NetCore.Analyzers.Runtime; using Xunit; using VerifyCS = Test.Utilities.CSharpCodeFixVerifier< Microsoft.NetCore.Analyzers.Performance.UsePropertyInsteadOfCountMethodWhenAvailableAnalyzer, @@ -19,6 +20,28 @@ namespace Microsoft.NetCore.Analyzers.Performance.UnitTests { public static partial class UsePropertyInsteadOfCountMethodWhenAvailableTests { + [Fact] + public static Task CSharp_ImmutableArray_Tests() + => VerifyCS.VerifyCodeFixAsync( + $@"using System; +using System.Linq; +public static class C +{{ + public static System.Collections.Immutable.ImmutableArray GetData() => default; + public static int M() => {{|{UsePropertyInsteadOfCountMethodWhenAvailableAnalyzer.RuleId}:GetData().Count()|}}; + public static int N() => {{|{UsePropertyInsteadOfCountMethodWhenAvailableAnalyzer.RuleId}:GetData().Count()|}}; +}} +", + $@"using System; +using System.Linq; +public static class C +{{ + public static System.Collections.Immutable.ImmutableArray GetData() => default; + public static int M() => GetData().Length; + public static int N() => GetData().Length; +}} +"); + [Theory] [InlineData("string[]", nameof(Array.Length))] [InlineData("System.Collections.Immutable.ImmutableArray", nameof(ImmutableArray.Length))]