|
// 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.
#nullable disable
using System;
using System.Collections.Generic;
using System.Linq;
namespace Microsoft.CodeAnalysis.Shared.Utilities;
internal sealed class SignatureComparer
{
public static readonly SignatureComparer Instance = new(SymbolEquivalenceComparer.Instance);
public static readonly SignatureComparer IgnoreAssembliesInstance = new(SymbolEquivalenceComparer.IgnoreAssembliesInstance);
private readonly SymbolEquivalenceComparer _symbolEquivalenceComparer;
private SignatureComparer(SymbolEquivalenceComparer symbolEquivalenceComparer)
=> _symbolEquivalenceComparer = symbolEquivalenceComparer;
private IEqualityComparer<IParameterSymbol> ParameterEquivalenceComparer => _symbolEquivalenceComparer.ParameterEquivalenceComparer;
private IEqualityComparer<ITypeSymbol> SignatureTypeEquivalenceComparer => _symbolEquivalenceComparer.SignatureTypeEquivalenceComparer;
public bool HaveSameSignature(ISymbol symbol1, ISymbol symbol2, bool caseSensitive)
{
// NOTE - we're deliberately using reference equality here for speed.
if (symbol1 == symbol2)
return true;
if (symbol1 == null || symbol2 == null)
return false;
if (symbol1.Kind != symbol2.Kind)
return false;
return symbol1.Kind switch
{
SymbolKind.Method => HaveSameSignature((IMethodSymbol)symbol1, (IMethodSymbol)symbol2, caseSensitive),
SymbolKind.Property => HaveSameSignature((IPropertySymbol)symbol1, (IPropertySymbol)symbol2, caseSensitive),
SymbolKind.Event => HaveSameSignature((IEventSymbol)symbol1, (IEventSymbol)symbol2, caseSensitive),
_ => true,
};
}
private static bool HaveSameSignature(IEventSymbol event1, IEventSymbol event2, bool caseSensitive)
=> IdentifiersMatch(event1.Name, event2.Name, caseSensitive);
public bool HaveSameSignature(IPropertySymbol property1, IPropertySymbol property2, bool caseSensitive)
{
if (!IdentifiersMatch(property1.Name, property2.Name, caseSensitive) ||
property1.Parameters.Length != property2.Parameters.Length ||
property1.IsIndexer != property2.IsIndexer)
{
return false;
}
return property1.Parameters.SequenceEqual(
property2.Parameters,
this.ParameterEquivalenceComparer);
}
public static bool BadPropertyAccessor(IMethodSymbol method1, IMethodSymbol method2)
{
return method1 != null &&
(method2 == null || method2.DeclaredAccessibility != Accessibility.Public);
}
public bool HaveSameSignature(IMethodSymbol method1,
IMethodSymbol method2,
bool caseSensitive,
bool compareParameterName = false,
bool isParameterCaseSensitive = false)
{
if ((method1.MethodKind == MethodKind.AnonymousFunction) !=
(method2.MethodKind == MethodKind.AnonymousFunction))
{
return false;
}
if (method1.MethodKind != MethodKind.AnonymousFunction)
{
if (!IdentifiersMatch(method1.Name, method2.Name, caseSensitive))
return false;
}
if (method1.MethodKind != method2.MethodKind ||
method1.Arity != method2.Arity)
{
return false;
}
return HaveSameSignature(method1.Parameters, method2.Parameters, compareParameterName, isParameterCaseSensitive);
}
private static bool IdentifiersMatch(string identifier1, string identifier2, bool caseSensitive)
{
return caseSensitive
? identifier1 == identifier2
: string.Equals(identifier1, identifier2, StringComparison.OrdinalIgnoreCase);
}
public bool HaveSameSignature(
IList<IParameterSymbol> parameters1,
IList<IParameterSymbol> parameters2)
{
if (parameters1.Count != parameters2.Count)
{
return false;
}
return parameters1.SequenceEqual(parameters2, this.ParameterEquivalenceComparer);
}
public bool HaveSameSignature(
IList<IParameterSymbol> parameters1,
IList<IParameterSymbol> parameters2,
bool compareParameterName,
bool isCaseSensitive)
{
if (parameters1.Count != parameters2.Count)
{
return false;
}
for (var i = 0; i < parameters1.Count; ++i)
{
if (!_symbolEquivalenceComparer.ParameterEquivalenceComparer.Equals(parameters1[i], parameters2[i], compareParameterName, isCaseSensitive))
{
return false;
}
}
return true;
}
public bool HaveSameSignatureAndConstraintsAndReturnTypeAndAccessors(ISymbol symbol1, ISymbol symbol2, bool caseSensitive)
{
// NOTE - we're deliberately using reference equality here for speed.
if (symbol1 == symbol2)
return true;
if (!HaveSameSignatureAndConstraintsAndReturnType(symbol1, symbol2, caseSensitive))
return false;
if (symbol1 is IPropertySymbol property1 && symbol2 is IPropertySymbol property2)
return HaveSameAccessors(property1, property2);
return true;
}
public bool HaveSameSignatureAndConstraintsAndReturnType(ISymbol symbol1, ISymbol symbol2, bool caseSensitive)
{
// NOTE - we're deliberately using reference equality here for speed.
if (symbol1 == symbol2)
return true;
if (!HaveSameSignature(symbol1, symbol2, caseSensitive))
return false;
switch (symbol1.Kind)
{
case SymbolKind.Method:
var method1 = (IMethodSymbol)symbol1;
var method2 = (IMethodSymbol)symbol2;
return HaveSameSignatureAndConstraintsAndReturnType(method1, method2);
case SymbolKind.Property:
var property1 = (IPropertySymbol)symbol1;
var property2 = (IPropertySymbol)symbol2;
return property1.ReturnsByRef == property2.ReturnsByRef &&
property1.ReturnsByRefReadonly == property2.ReturnsByRefReadonly &&
this.SignatureTypeEquivalenceComparer.Equals(property1.Type, property2.Type);
case SymbolKind.Event:
var ev1 = (IEventSymbol)symbol1;
var ev2 = (IEventSymbol)symbol2;
return HaveSameReturnType(ev1, ev2);
}
return true;
}
private static bool HaveSameAccessors(IPropertySymbol property1, IPropertySymbol property2)
{
if (property1.ContainingType == null ||
property1.ContainingType.TypeKind == TypeKind.Interface)
{
if (BadPropertyAccessor(property1.GetMethod, property2.GetMethod) ||
BadPropertyAccessor(property1.SetMethod, property2.SetMethod))
{
return false;
}
}
if (property2.ContainingType == null ||
property2.ContainingType.TypeKind == TypeKind.Interface)
{
if (BadPropertyAccessor(property2.GetMethod, property1.GetMethod) ||
BadPropertyAccessor(property2.SetMethod, property1.SetMethod))
{
return false;
}
}
return true;
}
private bool HaveSameSignatureAndConstraintsAndReturnType(IMethodSymbol method1, IMethodSymbol method2)
{
if (method1.ReturnsVoid != method2.ReturnsVoid ||
method1.ReturnsByRef != method2.ReturnsByRef ||
method1.ReturnsByRefReadonly != method2.ReturnsByRefReadonly)
{
return false;
}
if (!method1.ReturnsVoid && !this.SignatureTypeEquivalenceComparer.Equals(method1.ReturnType, method2.ReturnType))
return false;
for (var i = 0; i < method1.TypeParameters.Length; i++)
{
var typeParameter1 = method1.TypeParameters[i];
var typeParameter2 = method2.TypeParameters[i];
if (!HaveSameConstraints(typeParameter1, typeParameter2))
return false;
}
return true;
}
private bool HaveSameConstraints(ITypeParameterSymbol typeParameter1, ITypeParameterSymbol typeParameter2)
{
if (typeParameter1.HasConstructorConstraint != typeParameter2.HasConstructorConstraint ||
typeParameter1.HasReferenceTypeConstraint != typeParameter2.HasReferenceTypeConstraint ||
typeParameter1.HasValueTypeConstraint != typeParameter2.HasValueTypeConstraint ||
typeParameter1.AllowsRefLikeType != typeParameter2.AllowsRefLikeType)
{
return false;
}
if (typeParameter1.ConstraintTypes.Length != typeParameter2.ConstraintTypes.Length)
{
return false;
}
return typeParameter1.ConstraintTypes.SetEquals(
typeParameter2.ConstraintTypes, this.SignatureTypeEquivalenceComparer);
}
private bool HaveSameReturnType(IEventSymbol ev1, IEventSymbol ev2)
=> this.SignatureTypeEquivalenceComparer.Equals(ev1.Type, ev2.Type);
}
|