Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Performance improvements #515

Merged
merged 2 commits into from
Apr 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/Meziantou.Analyzer/Internals/CompilationExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace Meziantou.Analyzer;

internal static class CompilationExtensions
{
#if ROSLYN_3_8
public static ImmutableArray<INamedTypeSymbol> GetTypesByMetadataName(this Compilation compilation, string typeMetadataName)
{
var result = ImmutableArray.CreateBuilder<INamedTypeSymbol>();
Expand All @@ -30,6 +31,7 @@ public static ImmutableArray<INamedTypeSymbol> GetTypesByMetadataName(this Compi

return result.ToImmutable();
}
#endif

// Copy from https://github.com/dotnet/roslyn/blob/d2ff1d83e8fde6165531ad83f0e5b1ae95908289/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/CompilationExtensions.cs#L11-L68
/// <summary>
Expand Down
118 changes: 1 addition & 117 deletions src/Meziantou.Analyzer/Internals/MethodSymbolExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System.Collections.Immutable;
using System.Linq;
using System.Linq;
using Microsoft.CodeAnalysis;

namespace Meziantou.Analyzer;
Expand Down Expand Up @@ -79,119 +78,4 @@ public static bool IsUnitTestMethod(this IMethodSymbol methodSymbol)

return false;
}

public static bool HasOverloadWithAdditionalParameterOfType(
this IMethodSymbol methodSymbol,
Compilation compilation,
params ITypeSymbol?[] additionalParameterTypes)
{
return FindOverloadWithAdditionalParameterOfType(methodSymbol, compilation, additionalParameterTypes) != null;
}

public static bool HasOverloadWithAdditionalParameterOfType(
this IMethodSymbol methodSymbol,
IOperation currentOperation,
params ITypeSymbol?[] additionalParameterTypes)
{
if (currentOperation.SemanticModel == null)
return false;

return FindOverloadWithAdditionalParameterOfType(methodSymbol, currentOperation.SemanticModel.Compilation, syntaxNode: currentOperation.Syntax, includeObsoleteMethods: false, additionalParameterTypes) != null;
}

private static IMethodSymbol? FindOverloadWithAdditionalParameterOfType(
this IMethodSymbol methodSymbol,
Compilation compilation,
params ITypeSymbol?[] additionalParameterTypes)
{
return FindOverloadWithAdditionalParameterOfType(methodSymbol, compilation, includeObsoleteMethods: false, additionalParameterTypes);
}

public static IMethodSymbol? FindOverloadWithAdditionalParameterOfType(
this IMethodSymbol methodSymbol,
Compilation compilation,
bool includeObsoleteMethods,
params ITypeSymbol?[] additionalParameterTypes)
{
return FindOverloadWithAdditionalParameterOfType(methodSymbol, compilation, syntaxNode: null, includeObsoleteMethods, additionalParameterTypes);
}

public static IMethodSymbol? FindOverloadWithAdditionalParameterOfType(
this IMethodSymbol methodSymbol,
IOperation operation,
bool includeObsoleteMethods,
params ITypeSymbol?[] additionalParameterTypes)
{
if (operation.SemanticModel == null)
return null;

return FindOverloadWithAdditionalParameterOfType(methodSymbol, operation.SemanticModel.Compilation, operation.Syntax, includeObsoleteMethods, additionalParameterTypes);
}

public static IMethodSymbol? FindOverloadWithAdditionalParameterOfType(
this IMethodSymbol methodSymbol,
Compilation compilation,
SyntaxNode? syntaxNode,
bool includeObsoleteMethods,
params ITypeSymbol?[] additionalParameterTypes)
{
if (additionalParameterTypes == null)
return null;

additionalParameterTypes = additionalParameterTypes.Where(type => type != null).ToArray();
if (additionalParameterTypes.Length == 0)
return null;

ImmutableArray<ISymbol> members;
if (syntaxNode != null)
{
var semanticModel = compilation.GetSemanticModel(syntaxNode.SyntaxTree);
members = semanticModel.LookupSymbols(syntaxNode.GetLocation().SourceSpan.End, methodSymbol.ContainingType, methodSymbol.Name, includeReducedExtensionMethods: true);
}
else
{
members = methodSymbol.ContainingType.GetMembers(methodSymbol.Name);
}

return members.OfType<IMethodSymbol>()
.FirstOrDefault(member => (includeObsoleteMethods || !member.IsObsolete(compilation)) && HasSimilarParameters(methodSymbol, member, additionalParameterTypes));
}

public static bool IsObsolete(this IMethodSymbol methodSymbol, Compilation compilation)
{
var obsoleteAttribute = compilation?.GetBestTypeByMetadataName("System.ObsoleteAttribute");
if (obsoleteAttribute == null)
return false;

return methodSymbol.HasAttribute(obsoleteAttribute);
}

public static bool HasSimilarParameters(this IMethodSymbol methodSymbol, IMethodSymbol otherMethod, params ITypeSymbol?[] additionalParameterTypes)
{
if (methodSymbol.IsEqualTo(otherMethod))
return false;

if (additionalParameterTypes.Any(type => type == null))
{
additionalParameterTypes = additionalParameterTypes.WhereNotNull().ToArray();
}

var methodParameters = methodSymbol.Parameters.Select(p => p.Type).ToList();
var otherMethodParameters = otherMethod.Parameters.Select(p => p.Type).ToList();

if (otherMethodParameters.Count - methodParameters.Count != additionalParameterTypes.Length)
return false;

foreach (var param in methodParameters)
{
otherMethodParameters.Remove(param);
}

foreach (var param in additionalParameterTypes)
{
otherMethodParameters.Remove(param!);
}

return otherMethodParameters.Count == 0;
}
}
187 changes: 187 additions & 0 deletions src/Meziantou.Analyzer/Internals/OverloadFinder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis;

namespace Meziantou.Analyzer.Internals;
internal sealed class OverloadFinder
{
private readonly ITypeSymbol? _obsoleteSymbol;
private readonly Compilation _compilation;

public OverloadFinder(Compilation compilation)
{
_compilation = compilation;
_obsoleteSymbol = compilation.GetBestTypeByMetadataName("System.ObsoleteAttribute");
}

public bool HasOverloadWithAdditionalParameterOfType(
IMethodSymbol methodSymbol,
params ITypeSymbol[] additionalParameterTypes)
{
return FindOverloadWithAdditionalParameterOfType(methodSymbol, additionalParameterTypes) != null;
}

public bool HasOverloadWithAdditionalParameterOfType(
IMethodSymbol methodSymbol,
IOperation currentOperation,
params ITypeSymbol[] additionalParameterTypes)
{
if (currentOperation.SemanticModel == null)
return false;

return FindOverloadWithAdditionalParameterOfType(methodSymbol, syntaxNode: currentOperation.Syntax, includeObsoleteMethods: false, additionalParameterTypes) != null;
}

private IMethodSymbol? FindOverloadWithAdditionalParameterOfType(
IMethodSymbol methodSymbol,
params ITypeSymbol[] additionalParameterTypes)
{
return FindOverloadWithAdditionalParameterOfType(methodSymbol, includeObsoleteMethods: false, additionalParameterTypes);
}

public IMethodSymbol? FindOverloadWithAdditionalParameterOfType(
IMethodSymbol methodSymbol,
bool includeObsoleteMethods,
params ITypeSymbol[] additionalParameterTypes)
{
return FindOverloadWithAdditionalParameterOfType(methodSymbol, syntaxNode: null, includeObsoleteMethods, additionalParameterTypes);
}

public IMethodSymbol? FindOverloadWithAdditionalParameterOfType(
IMethodSymbol methodSymbol,
IOperation operation,
bool includeObsoleteMethods,
params ITypeSymbol[] additionalParameterTypes)
{
if (operation.SemanticModel == null)
return null;

return FindOverloadWithAdditionalParameterOfType(methodSymbol, operation.Syntax, includeObsoleteMethods, additionalParameterTypes);
}

public IMethodSymbol? FindOverloadWithAdditionalParameterOfType(
IMethodSymbol methodSymbol,
SyntaxNode? syntaxNode,
bool includeObsoleteMethods,
params ITypeSymbol[] additionalParameterTypes)
{
if (additionalParameterTypes == null)
return null;

additionalParameterTypes = additionalParameterTypes.Where(type => type != null).ToArray();
if (additionalParameterTypes.Length == 0)
return null;

ImmutableArray<ISymbol> members;
if (syntaxNode != null)
{
var semanticModel = _compilation.GetSemanticModel(syntaxNode.SyntaxTree);
members = semanticModel.LookupSymbols(syntaxNode.GetLocation().SourceSpan.End, methodSymbol.ContainingType, methodSymbol.Name, includeReducedExtensionMethods: true);
}
else
{
members = methodSymbol.ContainingType.GetMembers(methodSymbol.Name);
}

foreach (var member in members)
{
if (member is IMethodSymbol method)
{
if (!includeObsoleteMethods && IsObsolete(method))
continue;

if (HasSimilarParameters(methodSymbol, method, additionalParameterTypes))
return method;
}
}

return null;
}

public static bool HasSimilarParameters(IMethodSymbol method, IMethodSymbol otherMethod, params ITypeSymbol[] additionalParameterTypes)
{
if (method.IsEqualTo(otherMethod))
return false;

if (otherMethod.Parameters.Length - method.Parameters.Length != additionalParameterTypes.Length)
return false;

// Most of the time, an overload has the same order for the parameters
{
int i = 0, j = 0;
var additionalParameterIndex = 0;
while (i < method.Parameters.Length && j < method.Parameters.Length)
{
var methodParameter = method.Parameters[i];
var otherMethodParameter = otherMethod.Parameters[j];

if (methodParameter.IsEqualTo(otherMethodParameter))
{
i++;
j++;
continue;
}

if (additionalParameterIndex == additionalParameterTypes.Length)
break;

var additionalParameter = additionalParameterTypes[additionalParameterIndex];
if (methodParameter.Type.IsEqualTo(additionalParameter))
{
i++;
continue;
}

if (otherMethodParameter.Type.IsEqualTo(additionalParameter))
{
j++;
continue;
}

break;
}

if (i == method.Parameters.Length && j == otherMethod.Parameters.Length)
return true;
}

// Slower search, allows to find overload with different parameter order
{
var otherMethodParameters = otherMethod.Parameters;

foreach (var param in method.Parameters)
{
for (var i = 0; i < otherMethodParameters.Length; i++)
{
if (otherMethodParameters[i].Type.IsEqualTo(param.Type))
{
otherMethodParameters = otherMethodParameters.RemoveAt(i);
break;
}
}
}

foreach (var paramType in additionalParameterTypes)
{
for (var i = 0; i < otherMethodParameters.Length; i++)
{
if (otherMethodParameters[i].Type.IsEqualTo(paramType))
{
otherMethodParameters = otherMethodParameters.RemoveAt(i);
break;
}
}
}

return otherMethodParameters.Length == 0;
}
}

private bool IsObsolete(IMethodSymbol methodSymbol)
{
if (_obsoleteSymbol == null)
return false;

return methodSymbol.HasAttribute(_obsoleteSymbol);
}
}
1 change: 0 additions & 1 deletion src/Meziantou.Analyzer/Internals/SymbolExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis;

Expand Down
5 changes: 5 additions & 0 deletions src/Meziantou.Analyzer/Internals/TypeSymbolExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ public static bool IsOrImplements(this ITypeSymbol symbol, ITypeSymbol? interfac
if (attributeType == null)
return null;

if (attributeType.IsSealed)
{
inherits = false;
}

foreach (var attribute in symbol.GetAttributes())
{
if (inherits)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.Collections.Immutable;
using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;
Expand Down
Loading