Skip to content

Add global generic constraint support to type relationship helpers#1199

Merged
meziantou merged 1 commit into
mainfrom
meziantou-global-generic-constraints
Jun 24, 2026
Merged

Add global generic constraint support to type relationship helpers#1199
meziantou merged 1 commit into
mainfrom
meziantou-global-generic-constraints

Conversation

@meziantou

Copy link
Copy Markdown
Owner

Motivation

MA0046 (Use EventHandler to declare events) recently added support for generic type parameter constraints, but this capability was isolated to a single analyzer. Other relationship-checking helpers like IsOrInheritFrom, Implements, and InheritsFrom lack this awareness, causing them to miss or incorrectly handle cases where a type parameter is constrained to a base type or interface. This creates inconsistency across the codebase and false positives/negatives in analyzers that rely on these helpers.

Approach

Centralized generic constraint resolution: Updated all type relationship helpers in TypeSymbolExtensions to recursively resolve generic type parameter constraints:

  • InheritsFrom – now checks if constraint types inherit from the target
  • Implements – now checks if constraint types implement the target interface
  • ImplementsGenericInterface – now checks generic interface constraints
  • IsOrImplements – simplified by delegating to Implements
  • IsOrInheritFrom – combined type equality check with constraint resolution

Cycle-safe recursion: Added AnyConstraintTypeMatches helper that tracks visited type parameters using a HashSet to prevent infinite loops in recursive constraint chains.

Refactored consumers: Simplified UseEventHandlerOfTAnalyzer (MA0046) to use IsOrInheritFrom directly instead of maintaining its own constraint-traversal logic.

Changes

  • TypeSymbolExtensions.cs: Added generic constraint awareness to five core helpers; introduced cycle-safe traversal
  • UseEventHandlerOfTAnalyzer.cs: Removed local constraint recursion (14 lines → 2 lines)
  • EventsShouldHaveProperArgumentsAnalyzerTests.cs: Added regression test for constrained generic delegate arguments
  • docs/Rules/MA0046.md and docs/Rules/MA0093.md: Updated to note generic constraint support

Testing

  • All 3289 existing tests pass
  • Targeted tests pass on Roslyn 4.2 (earliest supported version)
  • New test validates that EventsShouldHaveProperArgumentsAnalyzer correctly flags null EventArgs on constrained generic delegates
  • Full test suite passes after documentation regeneration

- Update InheritsFrom, Implements, ImplementsGenericInterface, IsOrImplements, and IsOrInheritFrom to recursively resolve ITypeParameterSymbol constraints
- Include cycle-safe traversal over constraint types to prevent infinite loops
- Refactor UseEventHandlerOfTAnalyzer to use the centralized behavior instead of local constraint recursion
- Simplify IsOrImplements implementation by delegating to Implements
- Add regression test for generic type parameter constraints in EventsShouldHaveProperArgumentsAnalyzerTests
- Update MA0046 and MA0093 documentation to note generic constraint support

This ensures all analyzers that use these helpers consistently handle generic type parameters, preventing false positives and negatives across the codebase.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@meziantou meziantou merged commit 8b52c41 into main Jun 24, 2026
13 checks passed
@meziantou meziantou deleted the meziantou-global-generic-constraints branch June 24, 2026 13:49
This was referenced Jun 24, 2026
pkloehn1 added a commit to paragon-stats/paragon-stats that referenced this pull request Jun 25, 2026
Updated
[Meziantou.Analyzer](https://github.com/meziantou/Meziantou.Analyzer)
from 3.0.108 to 3.0.113.

<details>
<summary>Release notes</summary>

_Sourced from [Meziantou.Analyzer's
releases](https://github.com/meziantou/Meziantou.Analyzer/releases)._

## 3.0.113

NuGet package:
<https://www.nuget.org/packages/Meziantou.Analyzer/3.0.113>

## What's Changed
* Add global generic constraint support to type relationship helpers by
@​meziantou in meziantou/Meziantou.Analyzer#1199


**Full Changelog**:
meziantou/Meziantou.Analyzer@3.0.112...3.0.113

## 3.0.112

NuGet package:
<https://www.nuget.org/packages/Meziantou.Analyzer/3.0.112>

**Full Changelog**:
meziantou/Meziantou.Analyzer@3.0.111...3.0.112

## 3.0.111

NuGet package:
<https://www.nuget.org/packages/Meziantou.Analyzer/3.0.111>

## What's Changed
* Fix MA0206 for documented class declarations by @​meziantou in
meziantou/Meziantou.Analyzer#1198


**Full Changelog**:
meziantou/Meziantou.Analyzer@3.0.110...3.0.111

## 3.0.110

NuGet package:
<https://www.nuget.org/packages/Meziantou.Analyzer/3.0.110>

## What's Changed
* MA0204: exclude WPF XAML root types from unnecessary `partial`
diagnostics by @​meziantou with @​Copilot in
meziantou/Meziantou.Analyzer#1195


**Full Changelog**:
meziantou/Meziantou.Analyzer@3.0.109...3.0.110

## 3.0.109

NuGet package:
<https://www.nuget.org/packages/Meziantou.Analyzer/3.0.109>

## What's Changed
* Add MA0207/MA0208 for FixedAddressValueTypeAttribute validation by
@​meziantou in meziantou/Meziantou.Analyzer#1193


**Full Changelog**:
meziantou/Meziantou.Analyzer@3.0.108...3.0.109

Commits viewable in [compare
view](meziantou/Meziantou.Analyzer@3.0.108...3.0.113).
</details>

[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=Meziantou.Analyzer&package-manager=nuget&previous-version=3.0.108&new-version=3.0.113)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore <dependency name> major version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's major version (unless you unignore this specific
dependency's major version or upgrade to it yourself)
- `@dependabot ignore <dependency name> minor version` will close this
group update PR and stop Dependabot creating any more for the specific
dependency's minor version (unless you unignore this specific
dependency's minor version or upgrade to it yourself)
- `@dependabot ignore <dependency name>` will close this group update PR
and stop Dependabot creating any more for the specific dependency
(unless you unignore this specific dependency or upgrade to it yourself)
- `@dependabot unignore <dependency name>` will remove all of the ignore
conditions of the specified dependency
- `@dependabot unignore <dependency name> <ignore condition>` will
remove the ignore condition of the specified dependency and ignore
conditions


</details>
This was referenced Jun 26, 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