Skip to content

[CLI] Fix loading analyzers that reference System.Composition on .NET 10 - #1831

Merged
josefpihrt merged 4 commits into
dotnet:mainfrom
helgeu:fix/1649-system-composition-net10
Oct 4, 2026
Merged

josefpihrt merged 4 commits into
dotnet:mainfrom
helgeu:fix/1649-system-composition-net10

Conversation

@helgeu

@helgeu helgeu commented Sep 2, 2026

Copy link
Copy Markdown
Contributor
  • I've read the contributing guide
  • Bug fixes / features have an issue, or new analyzers / refactorings / fixes were discussed first
  • Unit tests added for bug fixes / features (see note below)
  • Changelog updated
  • Build passes locally for net8.0, net9.0 and net10.0

Summary

roslynator analyze and fix crash on the first project when run on the .NET 10 SDK:

System.IO.FileNotFoundException: Could not load file or assembly
'System.Composition.AttributedModel, Version=10.0.0.10, ...'
   at Roslynator.AnalyzerAssembly.Load(...)   # while a code fix provider's attributes are read

Root cause

The compiler only loads diagnostic analyzers, whose attributes live in Microsoft.CodeAnalysis, so the SDK does not deploy System.Composition next to its analyzer / code-fix assemblies. Roslynator also inspects code fix providers, so reading their [ExportCodeFixProvider] / [Shared] attributes forces System.Composition.AttributedModel to load. The SDK analyzers are compiled against the SDK's copy, whose assembly version tracks the SDK patch level (10.0.0.10 here). Roslynator restores System.Composition transitively through Roslyn (9.0.0), so the higher-versioned reference cannot bind and the load fails.

This is the same class of problem as #1729 / #1783 (MSBuild): an assembly whose version moves independently of Roslynator's releases, so matching the version is not viable. Unlike MSBuild, there is no locator that resolves System.Composition, and the SDK does not place it next to the analyzers, so it cannot be resolved from the SDK either.

Fix

Redirect System.Composition.* to the copy shipped with the tool, ignoring the requested version. The public surface used by the analyzer / fixer attributes (Export, Shared, ImportingConstructor, ...) is stable across versions, and Roslynator only reflects over these attributes and instantiates the types with Activator, so the deployed copy is sufficient. An AssemblyLoadContext.Default.Resolving handler is registered at startup (.NET build only; the net48 build is unaffected).

Verification

  • Repro: roslynator analyze on a new net10.0 class library — before: crash; after: analyzers and fixers load and run (e.g. reports CS0219).
  • A real 9-project net10.0 solution — before: crash on project 1/9; after: all 9 projects analyze (1671 diagnostics reported).
  • Builds clean for net8.0, net9.0 and net10.0.

Note on tests

There is no CLI test project, and the behaviour is AssemblyLoadContext resolution that has no natural unit-test home (the analogous #1783 shipped without one). I verified it end to end as above and am happy to add a test if you prefer.

Related issue

Fixes #1649


Investigated and implemented with AI assistance (opencode).

@helgeu helgeu mentioned this pull request Sep 2, 2026
The .NET 10 SDK ships analyzers and code fix providers that reference System.Composition but does not deploy System.Composition next to them. The version they were compiled against tracks the SDK patch level (for example 10.0.0.10) and is higher than the copy restored for the tool, so loading fixers failed with FileNotFoundException. Redirect System.Composition.* to the copy shipped with the tool, ignoring the requested version.
@helgeu
helgeu force-pushed the fix/1649-system-composition-net10 branch from 078a278 to e331730 Compare September 3, 2026 04:50
josefpihrt and others added 2 commits October 4, 2026 13:34
Use an anonymous function for the Resolving handler (RCS1207) and add
parentheses to the condition (RCS1051), as required by dotnet format in
pre_build. Drop the now-unused context parameter (RCS1163/IDE0060).

Co-authored-by: Cursor <cursoragent@cursor.com>
@josefpihrt
josefpihrt merged commit 2ce9822 into dotnet:main Oct 4, 2026
17 checks passed
@josefpihrt

Copy link
Copy Markdown
Collaborator

@helgeu Thank you for the contribution!

This was referenced Oct 4, 2026
This was referenced Oct 9, 2026
IhateTrains pushed a commit to ParadoxGameConverters/ImperatorToCK3 that referenced this pull request Oct 10, 2026
Updated [Roslynator.Analyzers](https://github.com/dotnet/roslynator)
from 5.0.0 to 5.0.1.

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

_Sourced from [Roslynator.Analyzers's
releases](https://github.com/dotnet/roslynator/releases)._

## 5.0.1

### Fixed

- Set Visual Studio extension `InstallationTarget` to API version
`[17.14,)` so Marketplace publish succeeds (VsixPub0029; API 18.0 is
experimental). Installable on Visual Studio 2022 17.14+ and Visual
Studio 2026 ([VS 2026 extension
compatibility](https://aka.ms/vs2026extensioncompat)).
- [CLI] Reference the compiled output of referenced projects that cannot
be loaded into the workspace (for example F# projects), so their types
are no longer reported as missing (`CS0103`/`CS0246`) during analysis
([PR](dotnet/roslynator#1833))
- [CLI] Fix loading of analyzers that reference `System.Composition` on
the .NET 10 SDK ([PR](dotnet/roslynator#1831))
- Fix code fix for
[RCS1233](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1233)
to add parentheses when the new `&&`/`||` expression is an operand of an
operator with higher precedence (e.g. `a && b | c` is now fixed to `a &&
(b || c)`) ([PR](dotnet/roslynator#1837))

Commits viewable in [compare
view](dotnet/roslynator@v5.0.0...v5.0.1).
</details>

[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=Roslynator.Analyzers&package-manager=nuget&previous-version=5.0.0&new-version=5.0.1)](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 this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
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.

CLI not working on Linux

2 participants