Description
ProjectPathViolationDetector.GetViolationsFrom() and ProjectTagViolationDetector.GetViolationsFrom() both iterate over all rules in the outer loop and all references in the inner loop, resulting in O(rules × references) complexity.
Unlike AssemblyNameViolationDetector (which has an experimental O(n) path using dictionary lookups), these detectors have no optimized path — the GetViolationsFromExperimental() just delegates back to the same nested loop.
Affected Files
src/ReferenceCop/Detectors/ProjectPathViolationDetector.cs — GetViolationsFrom()
src/ReferenceCop/Detectors/ProjectTagViolationDetector.cs — GetViolationsFrom()
Impact
For projects with many references (e.g., 50+ project references) and many rules, the nested iteration becomes costly, especially during MSBuild where this runs per-project.
Suggested Optimization
For ProjectTagViolationDetector: Pre-filter rules that match the current project's tag (only one fromProjectTag can match). Then use a HashSet or dictionary keyed by ToProjectTag for O(1) lookup per reference:
var matchingRules = this.rules.Where(r => r.FromProjectTag == fromProjectTag).ToList();
var blockedTags = new HashSet<string>(matchingRules.Select(r => r.ToProjectTag));
foreach (var referenceContext in references)
{
var toTag = this.projectTagProvider.GetProjectTag(referenceContext.Reference);
if (blockedTags.Contains(toTag) && !referenceContext.IsWarningSuppressed)
{
var rule = matchingRules.First(r => r.ToProjectTag == toTag);
yield return new Violation(rule, referenceContext.Reference);
}
}
For ProjectPathViolationDetector: Similar approach — pre-filter rules matching the current project's path prefix, then check references linearly against only the matching subset.
Description
ProjectPathViolationDetector.GetViolationsFrom()andProjectTagViolationDetector.GetViolationsFrom()both iterate over all rules in the outer loop and all references in the inner loop, resulting in O(rules × references) complexity.Unlike
AssemblyNameViolationDetector(which has an experimental O(n) path using dictionary lookups), these detectors have no optimized path — theGetViolationsFromExperimental()just delegates back to the same nested loop.Affected Files
src/ReferenceCop/Detectors/ProjectPathViolationDetector.cs—GetViolationsFrom()src/ReferenceCop/Detectors/ProjectTagViolationDetector.cs—GetViolationsFrom()Impact
For projects with many references (e.g., 50+ project references) and many rules, the nested iteration becomes costly, especially during MSBuild where this runs per-project.
Suggested Optimization
For ProjectTagViolationDetector: Pre-filter rules that match the current project's tag (only one
fromProjectTagcan match). Then use a HashSet or dictionary keyed byToProjectTagfor O(1) lookup per reference:For ProjectPathViolationDetector: Similar approach — pre-filter rules matching the current project's path prefix, then check references linearly against only the matching subset.