Description
ReferenceEvaluationContextFactory.Create() checks for warning suppression using noWarnCodes.Contains(Violation.ViolationSeverityWarningCode), where noWarnCodes is IEnumerable<string>. This performs O(n) linear enumeration via LINQ each time.
In the Roslyn analyzer path (ReferenceCopAnalyzer.AnalyzeCompilation), noWarnCodes is the IEnumerable<string> from NoWarnAssembliesProvider.GetNoWarnByAssembly(), which returns arrays from .Split().Select().ToArray(). The Contains call re-enumerates this array each time.
In the MSBuild path (ReferenceCopTask.Execute), noWarnCodes comes from ProjectReferenceInfo.NoWarn, which is similarly an enumerable from .Split().Select().
Affected Files
src/ReferenceCop/Detectors/ReferenceEvaluationContextFactory.cs — Create(), line 22 (.Contains() call)
src/ReferenceCop.Roslyn/Providers/NoWarnAssembliesProvider.cs — produces IEnumerable<string> values
src/ReferenceCop.MSBuild/Providers/MSBuildProjectMetadataProvider.cs — produces IEnumerable<string> from split
Impact
- Per-reference cost:
Contains on IEnumerable is O(n) per call. While individual NoWarn lists are typically small (1-5 codes), this runs for every reference in the compilation. With 100+ references, the overhead accumulates.
- Unnecessary enumeration: The LINQ
Contains extension method enumerates the sequence each time instead of using a set-based lookup.
- Multiple scans: If additional suppression codes are added in the future (e.g., checking both
RC0001 and RC0002), each check re-enumerates.
Suggested Optimization
Use a HashSet<string> for the NoWarn codes, either at the provider level or at the factory level:
Option 1 — At the provider level (preferred, change once):
// In NoWarnAssembliesProvider:
public Dictionary<string, HashSet<string>> GetNoWarnByAssembly(string noWarnAssembliesString)
{
// ...
var noWarnCodes = new HashSet<string>(
noWarnCodesString.Split(NoWarnCodesSeparator, StringSplitOptions.RemoveEmptyEntries)
.Select(code => code.Trim()),
StringComparer.OrdinalIgnoreCase);
result[assemblyName] = noWarnCodes;
}
Option 2 — Pre-compute the boolean (simplest):
public static ReferenceEvaluationContext<T> Create<T>(T reference, IEnumerable<string> noWarnCodes = null)
{
bool isSuppressed = noWarnCodes?.Any(c =>
c == Violation.ViolationSeverityWarningCode) ?? false;
return new ReferenceEvaluationContext<T>(reference, isSuppressed);
}
Since there are currently only two violation codes (RC0001, RC0002), and only RC0002 is checked, the boolean pre-compute is the simplest and most effective fix.
Description
ReferenceEvaluationContextFactory.Create()checks for warning suppression usingnoWarnCodes.Contains(Violation.ViolationSeverityWarningCode), wherenoWarnCodesisIEnumerable<string>. This performs O(n) linear enumeration via LINQ each time.In the Roslyn analyzer path (
ReferenceCopAnalyzer.AnalyzeCompilation),noWarnCodesis theIEnumerable<string>fromNoWarnAssembliesProvider.GetNoWarnByAssembly(), which returns arrays from.Split().Select().ToArray(). TheContainscall re-enumerates this array each time.In the MSBuild path (
ReferenceCopTask.Execute),noWarnCodescomes fromProjectReferenceInfo.NoWarn, which is similarly an enumerable from.Split().Select().Affected Files
src/ReferenceCop/Detectors/ReferenceEvaluationContextFactory.cs—Create(), line 22 (.Contains()call)src/ReferenceCop.Roslyn/Providers/NoWarnAssembliesProvider.cs— producesIEnumerable<string>valuessrc/ReferenceCop.MSBuild/Providers/MSBuildProjectMetadataProvider.cs— producesIEnumerable<string>from splitImpact
ContainsonIEnumerableis O(n) per call. While individual NoWarn lists are typically small (1-5 codes), this runs for every reference in the compilation. With 100+ references, the overhead accumulates.Containsextension method enumerates the sequence each time instead of using a set-based lookup.RC0001andRC0002), each check re-enumerates.Suggested Optimization
Use a
HashSet<string>for the NoWarn codes, either at the provider level or at the factory level:Option 1 — At the provider level (preferred, change once):
Option 2 — Pre-compute the boolean (simplest):
Since there are currently only two violation codes (
RC0001,RC0002), and onlyRC0002is checked, the boolean pre-compute is the simplest and most effective fix.