Description
PatternMatchComparer implements IEqualityComparer<string> but its GetHashCode method uses the default string.GetHashCode(). This violates the IEqualityComparer contract: when Equals(x, y) returns true, GetHashCode(x) must equal GetHashCode(y).
Specifically, PatternMatchComparer.Equals("*", "SomeAssembly") returns true, but "*".GetHashCode() != "SomeAssembly".GetHashCode(). This means any dictionary using this comparer cannot perform correct O(1) lookups for wildcard patterns.
Affected Files
src/ReferenceCop/Comparers/PatternMatchComparer.cs — GetHashCode() (line 12)
src/ReferenceCop/Detectors/AssemblyNameViolationDetector.cs — rules dictionary uses this comparer (line 8)
Impact
- Broken invariant: The
Dictionary<string, Rule> in AssemblyNameViolationDetector uses PatternMatchComparer as its equality comparer. Dictionary lookups via TryGetValue or indexer for wildcard keys will silently fail to find matches because hash codes differ.
- Hidden by current usage: The current
GetViolationsFrom() iterates this.rules via foreach (treating the dictionary as IEnumerable<KeyValuePair>), which bypasses hash-based lookup entirely — masking the bug. But it also means the Dictionary provides zero performance benefit over a List<KeyValuePair>.
- The
experimental path is affected: GetViolationsFromExperimental uses exactMatchRules.TryGetValue() which works for exact matches (correct hash codes), but the original rules dictionary remains broken for any consumers that might try to look up by key.
Suggested Fix
Since PatternMatchComparer supports wildcards, it cannot produce consistent hash codes for all equal pairs. Two options:
- Replace the Dictionary with a List for rule storage (since it is only iterated, never looked up by key):
private readonly List<KeyValuePair<string, ReferenceCopConfig.Rule>> rules;
This makes the actual data structure match how it is used and avoids the misleading Dictionary type.
- If dictionary lookup is needed, use a constant hash code (trades O(1) for correctness):
public int GetHashCode(string obj) => 0; // Forces bucket collision, but maintains contract
This is correct but makes the dictionary degenerate to O(n). Option 1 is preferred since the dictionary is never used as a dictionary.
Description
PatternMatchComparerimplementsIEqualityComparer<string>but itsGetHashCodemethod uses the defaultstring.GetHashCode(). This violates theIEqualityComparercontract: whenEquals(x, y)returns true,GetHashCode(x)must equalGetHashCode(y).Specifically,
PatternMatchComparer.Equals("*", "SomeAssembly")returnstrue, but"*".GetHashCode() != "SomeAssembly".GetHashCode(). This means any dictionary using this comparer cannot perform correct O(1) lookups for wildcard patterns.Affected Files
src/ReferenceCop/Comparers/PatternMatchComparer.cs—GetHashCode()(line 12)src/ReferenceCop/Detectors/AssemblyNameViolationDetector.cs—rulesdictionary uses this comparer (line 8)Impact
Dictionary<string, Rule>inAssemblyNameViolationDetectorusesPatternMatchCompareras its equality comparer. Dictionary lookups viaTryGetValueor indexer for wildcard keys will silently fail to find matches because hash codes differ.GetViolationsFrom()iteratesthis.rulesviaforeach(treating the dictionary asIEnumerable<KeyValuePair>), which bypasses hash-based lookup entirely — masking the bug. But it also means the Dictionary provides zero performance benefit over aList<KeyValuePair>.experimentalpath is affected:GetViolationsFromExperimentalusesexactMatchRules.TryGetValue()which works for exact matches (correct hash codes), but the originalrulesdictionary remains broken for any consumers that might try to look up by key.Suggested Fix
Since
PatternMatchComparersupports wildcards, it cannot produce consistent hash codes for all equal pairs. Two options:This makes the actual data structure match how it is used and avoids the misleading
Dictionarytype.This is correct but makes the dictionary degenerate to O(n). Option 1 is preferred since the dictionary is never used as a dictionary.