Skip to content

Add MA0002.report_only_non_ordinal option - #1283

Merged
meziantou merged 2 commits into
mainfrom
claude/ma0002-ordinal-option-65a4e6
Aug 14, 2026
Merged

meziantou merged 2 commits into
mainfrom
claude/ma0002-ordinal-option-65a4e6

Conversation

@meziantou

Copy link
Copy Markdown
Owner

What

Adds a new opt-in EditorConfig option MA0002.report_only_non_ordinal (default false, so existing behavior is unchanged).

MA0002 currently reports whenever a string-keyed collection or LINQ operation has an IEqualityComparer<string>/IComparer<string> overload but none is supplied — even when the default comparison is already ordinal (and therefore safe), e.g. new HashSet<string>(), new Dictionary<string,int>(), ToDictionary, ToImmutableDictionary.

When MA0002.report_only_non_ordinal = true, the analyzer suppresses diagnostics for a curated, exhaustive list of known-ordinal (equality-default) APIs while still reporting the genuinely risky culture-sensitive ordering APIs (SortedDictionary, SortedSet, SortedList, OrderBy, ThenBy, Order, ...), which default to Comparer<string>.Default (= StringComparer.CurrentCulture).

Known-ordinal set (suppressed when the option is on)

  • Types (constructors / collection expressions): HashSet, Dictionary, ConcurrentDictionary, OrderedDictionary, ImmutableDictionary, ImmutableHashSet, FrozenDictionary, FrozenSet.
  • Methods: LINQ equality methods (Distinct, DistinctBy, Contains, Except, ExceptBy, Intersect, IntersectBy, Union, UnionBy, GroupBy, GroupJoin, Join, LeftJoin, RightJoin, SequenceEqual, ToDictionary, ToHashSet, ToLookup, CountBy, AggregateBy) and immutable/frozen factories (Create, CreateBuilder, CreateRange, CreateRangeWithOverwrite, ToImmutableDictionary, ToImmutableHashSet, ToFrozenDictionary, ToFrozenSet).

The method names are scoped to their BCL container types (Enumerable, Queryable, ImmutableDictionary, ImmutableHashSet, FrozenDictionary, FrozenSet), so the Sorted* factories that share names like Create/CreateRange keep reporting. The list was verified by reflecting over the actual net10.0 assemblies rather than by guessing.

Because this is an allow-list (only suppress what is known-ordinal), the only failure mode is under-suppression — a culture-sensitive API can never be silently hidden.

Behavior change to note

Meziantou.Framework.Assertions.Assert was previously unconditionally excluded from MA0002. It is now folded into the option: reported by default and suppressed only when report_only_non_ordinal = true. This means Assert methods with a comparer overload start being reported by default.

Tests / docs

  • Added analyzer tests for the option: suppressed known-ordinal cases (types, LINQ, collection expression, immutable factories, Assert) and still-reported ordering cases (SortedDictionary, OrderBy, Order, ImmutableSortedDictionary.Create).
  • Updated docs/Rules/MA0002.md and regenerated docs/README.md.
  • MA0002 tests pass on default Roslyn and on roslyn4.2 / roslyn4.14.

MA0002 reports whenever a string-keyed collection or LINQ operation has an
IEqualityComparer<string>/IComparer<string> overload but none is supplied,
even when the default comparison is already ordinal (and therefore safe).

Add an opt-in `MA0002.report_only_non_ordinal` option (default false, so
behavior is unchanged). When enabled, diagnostics are suppressed for a curated,
exhaustive list of known-ordinal (equality-default) APIs while culture-sensitive
ordering APIs (SortedDictionary, OrderBy, ...) keep being reported.

Known-ordinal types: HashSet, Dictionary, ConcurrentDictionary, OrderedDictionary,
ImmutableDictionary, ImmutableHashSet, FrozenDictionary, FrozenSet.
Known-ordinal methods: LINQ equality methods (Distinct, Contains, GroupBy,
ToDictionary, Union, Except, Intersect, Join/LeftJoin/RightJoin, SequenceEqual,
CountBy, AggregateBy, *By variants) and immutable/frozen factories (Create,
CreateBuilder, CreateRange, ToImmutableDictionary, ToFrozenSet, ...), scoped to
their BCL container types so Sorted* factories sharing the same names keep
reporting.

Meziantou.Framework.Assertions.Assert is folded into the option: previously it
was unconditionally excluded; it is now reported by default and suppressed only
when the option is enabled.
@meziantou
meziantou enabled auto-merge (squash) August 14, 2026 23:25
@meziantou
meziantou merged commit 45060b8 into main Aug 14, 2026
11 checks passed
@meziantou
meziantou deleted the claude/ma0002-ordinal-option-65a4e6 branch August 14, 2026 23:32
This was referenced Aug 15, 2026
This was referenced Sep 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant