diff --git a/src/Analysis/Engine/Impl/AnalysisHashSet.cs b/src/Analysis/Engine/Impl/AnalysisHashSet.cs index a612be922..65143d677 100644 --- a/src/Analysis/Engine/Impl/AnalysisHashSet.cs +++ b/src/Analysis/Engine/Impl/AnalysisHashSet.cs @@ -275,7 +275,19 @@ public bool SetEquals(IAnalysisSet other) { return true; } + // Only access Count once to prevent wasted time in bucket locks. + var lCount = Count; + var rCount = other.Count; + + if (lCount == 0 && rCount == 0) { + return true; + } + if (Comparer == other.Comparer) { + if (lCount != rCount) { + return false; + } + // Quick check for any unmatched hashcodes. // This can conclusively prove the sets are not equal, but cannot // prove equality. @@ -290,17 +302,16 @@ public bool SetEquals(IAnalysisSet other) { } } - var otherHc = new HashSet(other, _comparer); - foreach (var key in this) { - if (!otherHc.Remove(key)) { - return false; - } - } - if (otherHc.Any()) { + if (lCount == 0 || rCount == 0) { return false; } - return true; + if (lCount == 1 && rCount == 1) { + return _comparer.Equals(this.First(), other.First()); + } + + var hs = new HashSet(other, _comparer); + return hs.SetEquals(this); } /// diff --git a/src/Analysis/Engine/Impl/AnalysisSet.cs b/src/Analysis/Engine/Impl/AnalysisSet.cs index efd8a5a38..821f8ba7c 100644 --- a/src/Analysis/Engine/Impl/AnalysisSet.cs +++ b/src/Analysis/Engine/Impl/AnalysisSet.cs @@ -534,6 +534,8 @@ public static bool ContainsAny(this IAnalysisSet set, IAnalysisSet values) { sealed class ObjectComparer : IEqualityComparer, IEqualityComparer { public static readonly ObjectComparer Instance = new ObjectComparer(); + private ObjectComparer() { } + public bool Equals(AnalysisValue x, AnalysisValue y) { #if FULL_VALIDATION if (x != null && y != null) { @@ -574,7 +576,7 @@ sealed class UnionComparer : IEqualityComparer, IEqualityComparer public readonly int Strength; - public UnionComparer(int strength = 0) { + private UnionComparer(int strength = 0) { Strength = strength; } diff --git a/src/Analysis/Engine/Impl/Intellisense/AnalysisQueue.cs b/src/Analysis/Engine/Impl/Intellisense/AnalysisQueue.cs index 143e66ed8..085572ac4 100644 --- a/src/Analysis/Engine/Impl/Intellisense/AnalysisQueue.cs +++ b/src/Analysis/Engine/Impl/Intellisense/AnalysisQueue.cs @@ -206,7 +206,7 @@ public async Task Handler(CancellationToken cancellationToken) { } private sealed class QueueItemComparer : IEqualityComparer { - public static IEqualityComparer Instance { get; } = new QueueItemComparer(); + public static readonly IEqualityComparer Instance = new QueueItemComparer(); private QueueItemComparer() { } public bool Equals(QueueItem x, QueueItem y) => Equals(x.Key, y.Key); diff --git a/src/Analysis/Engine/Impl/LocationInfo.cs b/src/Analysis/Engine/Impl/LocationInfo.cs index c34854ea6..78eac9caa 100644 --- a/src/Analysis/Engine/Impl/LocationInfo.cs +++ b/src/Analysis/Engine/Impl/LocationInfo.cs @@ -83,9 +83,13 @@ public bool Equals(ILocationInfo other) { /// Provides an IEqualityComparer that compares line, column and project entries. By /// default locations are equaitable based upon only line/project entry. /// - public static IEqualityComparer FullComparer { get; } = new FullLocationComparer(); + public static IEqualityComparer FullComparer => FullLocationComparer.Instance; sealed class FullLocationComparer : IEqualityComparer, IEqualityComparer { + public static readonly FullLocationComparer Instance = new FullLocationComparer(); + + private FullLocationComparer() { } + public bool Equals(LocationInfo x, LocationInfo y) => Equals(x, y); public bool Equals(ILocationInfo x, ILocationInfo y) { return x.StartLine == y.StartLine && diff --git a/src/Analysis/Engine/Impl/OverloadResult.cs b/src/Analysis/Engine/Impl/OverloadResult.cs index b7f9f2a1e..9af018318 100644 --- a/src/Analysis/Engine/Impl/OverloadResult.cs +++ b/src/Analysis/Engine/Impl/OverloadResult.cs @@ -425,8 +425,8 @@ internal ParameterResult GetParameterResultFromParameterInfo(IParameterInfo para } class OverloadResultComparer : EqualityComparer { - public static IEqualityComparer Instance = new OverloadResultComparer(false); - public static IEqualityComparer WeakInstance = new OverloadResultComparer(true); + public static readonly IEqualityComparer Instance = new OverloadResultComparer(false); + public static readonly IEqualityComparer WeakInstance = new OverloadResultComparer(true); private readonly bool _weak; diff --git a/src/Analysis/Engine/Impl/PythonAnalyzer.cs b/src/Analysis/Engine/Impl/PythonAnalyzer.cs index c8b90d753..d6654ba58 100644 --- a/src/Analysis/Engine/Impl/PythonAnalyzer.cs +++ b/src/Analysis/Engine/Impl/PythonAnalyzer.cs @@ -921,7 +921,9 @@ private AggregateProjectEntry GetAggregateWorker(IProjectEntry[] all) { } class AggregateComparer : IEqualityComparer { - public static AggregateComparer Instance = new AggregateComparer(); + public static readonly AggregateComparer Instance = new AggregateComparer(); + + private AggregateComparer() { } public bool Equals(IProjectEntry[] x, IProjectEntry[] y) { if (x.Length != y.Length) { diff --git a/src/Analysis/Engine/Impl/Values/ClassInfo.cs b/src/Analysis/Engine/Impl/Values/ClassInfo.cs index ef6e14ec1..b313f334f 100644 --- a/src/Analysis/Engine/Impl/Values/ClassInfo.cs +++ b/src/Analysis/Engine/Impl/Values/ClassInfo.cs @@ -831,6 +831,10 @@ public static IAnalysisSet GetMemberFromMroNoReferences(IEnumerable 0) { + break; + } } return result; }