Skip to content

Fix MA0018 reporting one diagnostic per event accessor - #1505

Merged
meziantou merged 1 commit into
mainfrom
feature/ma0018-event-duplicates-6b177c
Sep 12, 2026
Merged

meziantou merged 1 commit into
mainfrom
feature/ma0018-event-duplicates-6b177c

Conversation

@meziantou

Copy link
Copy Markdown
Owner

What changed

DoNotDeclareStaticMembersOnGenericTypes (MA0018) skipped PropertyGet and PropertySet accessors so that a static property is reported once, through its IPropertySymbol. The event accessor kinds were missing from that filter, so MethodKind.EventAdd and MethodKind.EventRemove are now skipped too. The comment, which said "skip properties" while the code skips accessors, was corrected.

Why

For a static event, GetMembers() returns the IEventSymbol and its add_ / remove_ accessors, all three public and static, so all three were reported:

public class Sample<T>
{
    public static event EventHandler MyEvent;
}

produced three identical diagnostics at the same location:

/0/Test0.cs(4,38): info MA0018: Do not declare static members on generic types
/0/Test0.cs(4,38): info MA0018: Do not declare static members on generic types
/0/Test0.cs(4,38): info MA0018: Do not declare static members on generic types

With explicitly written add / remove accessors the three diagnostics land on three different spans, so the IDE does not even deduplicate them.

Notes for reviewers

  • Using member.IsImplicitlyDeclared instead would not be enough: accessors written explicitly are not implicit.
  • The compiler-generated backing field of a field-like event is private, so IsVisibleOutsideOfAssembly() already excluded it — it never contributed a diagnostic.
  • Two tests were added to DoNotDeclareStaticMembersOnGenericTypesTests: a field-like static event and one with explicit add / remove accessors (the case with distinct spans). Temporarily reverting the analyzer change makes exactly those two fail, so they do pin the defect.
  • All 11 tests in the class pass on roslyn4.8, 4.14, 5.0, 5.6 and 5.9.
  • dotnet run --project src/DocumentationGenerator exits 0 with no markdown changes. docs/Rules/MA0018.md needs no update: the documented behavior is unchanged, this only removes duplicates.

`Analyze` skipped `PropertyGet` and `PropertySet` accessors so that a static
property is reported once, through its `IPropertySymbol`, but the event
accessor kinds were missing from that filter. For a static event,
`GetMembers()` returns the `IEventSymbol` and its `add_`/`remove_` accessors,
all three public and static, so all three were reported. With explicitly
written accessors the three diagnostics land on three different spans, so the
IDE does not even deduplicate them.

Add `EventAdd` and `EventRemove` to the skip list. `IsImplicitlyDeclared`
would not work here: accessors written explicitly are not implicit.
@meziantou
meziantou merged commit dbebdcd into main Sep 12, 2026
13 checks passed
@meziantou
meziantou deleted the feature/ma0018-event-duplicates-6b177c branch September 12, 2026 20:17
This was referenced Sep 12, 2026
This was referenced Sep 24, 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