From 4a31163b3b40f7c8d7ee0de0fe52feda3106cd75 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Mon, 13 Jul 2026 06:38:48 +0200 Subject: [PATCH 1/2] docs: add AI plan 139 for Roslyn RS1038 fix Signed-off-by: Kenny Pflug --- ai-plans/0139-roslyn-RS1038-fix.md | 38 ++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 ai-plans/0139-roslyn-RS1038-fix.md diff --git a/ai-plans/0139-roslyn-RS1038-fix.md b/ai-plans/0139-roslyn-RS1038-fix.md new file mode 100644 index 0000000..26fddf2 --- /dev/null +++ b/ai-plans/0139-roslyn-RS1038-fix.md @@ -0,0 +1,38 @@ +# Resolve RS1038 by Splitting the Internal Roslyn Analyzers Assembly + +## Rationale + +Building the solution produces warning RS1038 because `Light.GuardClauses.InternalRoslynAnalyzers` contains a `DiagnosticAnalyzer` — an extension loaded by the compiler during every command-line build of the product — while also referencing `Microsoft.CodeAnalysis.CSharp.Workspaces`, which the compiler does not provide. The build only works today because the analyzer's executed code paths never touch a Workspaces type; any future change that makes one reachable would cause the analyzer to fail loading (CS8032), silently disabling it and breaking Release builds via `TreatWarningsAsErrors`. + +Split the assembly following the standard Roslyn pattern: the compiler-loaded analyzer must reference only compiler-layer Roslyn assemblies, while the IDE-only code fix providers move to a separate assembly that may reference Workspaces. + +## Acceptance Criteria + +- [ ] Building the complete solution in Release configuration produces no RS1038 warning and no new warnings. +- [ ] The `Light.GuardClauses.InternalRoslynAnalyzers` assembly references only compiler-layer Roslyn packages and contains no code fix providers; the two code fix providers reside in a new `Light.GuardClauses.InternalRoslynAnalyzers.CodeFixes` assembly. +- [ ] The product project receives both assemblies as analyzer references, so command-line builds still run the XML-comment analyzer and IDEs can still load the code fixes. +- [ ] Introducing a non-default `parameterName` or `message` XML comment on a standard exception assertion in the product project still produces the corresponding `LightGuardClauses_*` diagnostic during a command-line build. +- [ ] The existing analyzer and code-fix tests pass against the split assemblies, and the complete solution's tests all pass. +- [ ] `Directory.Packages.props` contains no project-name-conditional version override; the Roslyn version constraint for compiler-loaded assemblies is documented where the version is pinned. +- [ ] The new project appears in `Light.GuardClauses.sln` under the `tools/analyzers` solution folder hierarchy. + +## Technical Details + +Create `tools/analyzers/Light.GuardClauses.InternalRoslynAnalyzers.CodeFixes/` and move `MessageXmlCommentFix.cs` and `ParameterNameXmlCommentFix.cs` into it, keeping the existing `Light.GuardClauses.InternalRoslynAnalyzers` namespace to avoid churn. The shared types (`Descriptors`, `ParameterNameConstants`, `MessageConstants`) are already public and stay in the analyzer project; the CodeFixes project references it via a regular `ProjectReference`. Both projects target `netstandard2.0` and reference `Microsoft.CodeAnalysis.Analyzers` with `PrivateAssets="all"`; only the analyzer project keeps `EnforceExtendedAnalyzerRules`. + +The analyzer project replaces its `Microsoft.CodeAnalysis.CSharp.Workspaces` reference with `Microsoft.CodeAnalysis.CSharp`; the CodeFixes project references `Microsoft.CodeAnalysis.CSharp.Workspaces`. Both must stay at 4.11.0: a compiler-loaded extension must not be built against a newer Microsoft.CodeAnalysis than the Roslyn version shipped with the pinned .NET SDK 8.0.400 (Roslyn 4.11), and the code fixes must match the analyzer's Roslyn version. Express this via a central `Microsoft.CodeAnalysis.CSharp` entry at 4.11.0 (the analyzer is its only consumer) and a `VersionOverride="4.11.0"` on the Workspaces reference in the CodeFixes project, removing the existing `MSBuildProjectName`-conditional `PackageVersion Update` group. Record the SDK constraint as a comment next to the pinned versions. The central `Microsoft.CodeAnalysis.CSharp.Workspaces` version remains 4.13.0 for the source-export tooling. + +The product project adds a second analyzer reference alongside the existing one (illustrative — mirrors the existing reference): + +```xml + +``` + +The compiler finds no analyzer types in the CodeFixes assembly and ignores it; IDEs load the fix providers from it with Workspaces available at runtime. This is the same layout analyzer NuGet packages such as StyleCop.Analyzers use. + +`Light.GuardClauses.InternalRoslynAnalyzers.Tests` currently references only the analyzer project but tests both the analyzer and the fixes, and its helpers use Workspaces types that today arrive transitively through the analyzer. Repoint it to reference the CodeFixes project (the analyzer project arrives transitively); no test logic changes are expected. + +Behavior does not change; the existing test suite covering the analyzer and both code fixes constitutes the required coverage. From 6daa42c0973274857bc20dcbf996436b8c66be57 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Mon, 13 Jul 2026 07:25:06 +0200 Subject: [PATCH 2/2] chore: introduce new CodeFixes Roslyn project to avoid warning RS1038 Signed-off-by: Kenny Pflug --- Directory.Packages.props | 14 ++++++++++++-- Light.GuardClauses.sln | 7 +++++++ ai-plans/0139-roslyn-RS1038-fix.md | 14 +++++++------- src/Light.GuardClauses/Light.GuardClauses.csproj | 4 ++++ ...dClauses.InternalRoslynAnalyzers.Tests.csproj | 2 +- ...uses.InternalRoslynAnalyzers.CodeFixes.csproj | 16 ++++++++++++++++ .../MessageXmlCommentFix.cs | 0 .../ParameterNameXmlCommentFix.cs | 0 ...t.GuardClauses.InternalRoslynAnalyzers.csproj | 2 +- 9 files changed, 48 insertions(+), 11 deletions(-) create mode 100644 tools/analyzers/Light.GuardClauses.InternalRoslynAnalyzers.CodeFixes/Light.GuardClauses.InternalRoslynAnalyzers.CodeFixes.csproj rename tools/analyzers/{Light.GuardClauses.InternalRoslynAnalyzers => Light.GuardClauses.InternalRoslynAnalyzers.CodeFixes}/MessageXmlCommentFix.cs (100%) rename tools/analyzers/{Light.GuardClauses.InternalRoslynAnalyzers => Light.GuardClauses.InternalRoslynAnalyzers.CodeFixes}/ParameterNameXmlCommentFix.cs (100%) diff --git a/Directory.Packages.props b/Directory.Packages.props index 5a76156..67ed0fc 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -18,7 +18,17 @@ - - + + + diff --git a/Light.GuardClauses.sln b/Light.GuardClauses.sln index 6206f4d..e086c9d 100644 --- a/Light.GuardClauses.sln +++ b/Light.GuardClauses.sln @@ -10,6 +10,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Light.GuardClauses.Performa EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Light.GuardClauses.InternalRoslynAnalyzers", "tools\analyzers\Light.GuardClauses.InternalRoslynAnalyzers\Light.GuardClauses.InternalRoslynAnalyzers.csproj", "{DB4A3982-5312-4923-9C79-C39D18A5899C}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Light.GuardClauses.InternalRoslynAnalyzers.CodeFixes", "tools\analyzers\Light.GuardClauses.InternalRoslynAnalyzers.CodeFixes\Light.GuardClauses.InternalRoslynAnalyzers.CodeFixes.csproj", "{0D6B435A-E59C-48EB-A13D-69FDB5B6F4A1}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Light.GuardClauses.InternalRoslynAnalyzers.Tests", "tests\Light.GuardClauses.InternalRoslynAnalyzers.Tests\Light.GuardClauses.InternalRoslynAnalyzers.Tests.csproj", "{2FE2D52E-21B8-4C51-9983-9C1812BDEBA0}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Light.GuardClauses.SourceCodeTransformation", "tools\source-export\Light.GuardClauses.SourceCodeTransformation\Light.GuardClauses.SourceCodeTransformation.csproj", "{ECE42390-E3B5-49D4-9452-3B0CC04C633A}" @@ -66,6 +68,10 @@ Global {DB4A3982-5312-4923-9C79-C39D18A5899C}.Debug|Any CPU.Build.0 = Debug|Any CPU {DB4A3982-5312-4923-9C79-C39D18A5899C}.Release|Any CPU.ActiveCfg = Release|Any CPU {DB4A3982-5312-4923-9C79-C39D18A5899C}.Release|Any CPU.Build.0 = Release|Any CPU + {0D6B435A-E59C-48EB-A13D-69FDB5B6F4A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0D6B435A-E59C-48EB-A13D-69FDB5B6F4A1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0D6B435A-E59C-48EB-A13D-69FDB5B6F4A1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0D6B435A-E59C-48EB-A13D-69FDB5B6F4A1}.Release|Any CPU.Build.0 = Release|Any CPU {2FE2D52E-21B8-4C51-9983-9C1812BDEBA0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2FE2D52E-21B8-4C51-9983-9C1812BDEBA0}.Debug|Any CPU.Build.0 = Debug|Any CPU {2FE2D52E-21B8-4C51-9983-9C1812BDEBA0}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -93,6 +99,7 @@ Global {A2067796-F167-47BE-B367-191152DCE230} = {22B02CC1-06EC-4A20-8964-B7E941FAEB49} {F4EE1A0F-60E0-4077-BFC4-82387535352A} = {33B02CC1-06EC-4A20-8964-B7E941FAEB49} {DB4A3982-5312-4923-9C79-C39D18A5899C} = {55B02CC1-06EC-4A20-8964-B7E941FAEB49} + {0D6B435A-E59C-48EB-A13D-69FDB5B6F4A1} = {55B02CC1-06EC-4A20-8964-B7E941FAEB49} {ECE42390-E3B5-49D4-9452-3B0CC04C633A} = {66B02CC1-06EC-4A20-8964-B7E941FAEB49} {B546B7B7-9CA5-456C-96CD-15C21E64B7DF} = {66B02CC1-06EC-4A20-8964-B7E941FAEB49} {55B02CC1-06EC-4A20-8964-B7E941FAEB49} = {44B02CC1-06EC-4A20-8964-B7E941FAEB49} diff --git a/ai-plans/0139-roslyn-RS1038-fix.md b/ai-plans/0139-roslyn-RS1038-fix.md index 26fddf2..9dc12e3 100644 --- a/ai-plans/0139-roslyn-RS1038-fix.md +++ b/ai-plans/0139-roslyn-RS1038-fix.md @@ -8,13 +8,13 @@ Split the assembly following the standard Roslyn pattern: the compiler-loaded an ## Acceptance Criteria -- [ ] Building the complete solution in Release configuration produces no RS1038 warning and no new warnings. -- [ ] The `Light.GuardClauses.InternalRoslynAnalyzers` assembly references only compiler-layer Roslyn packages and contains no code fix providers; the two code fix providers reside in a new `Light.GuardClauses.InternalRoslynAnalyzers.CodeFixes` assembly. -- [ ] The product project receives both assemblies as analyzer references, so command-line builds still run the XML-comment analyzer and IDEs can still load the code fixes. -- [ ] Introducing a non-default `parameterName` or `message` XML comment on a standard exception assertion in the product project still produces the corresponding `LightGuardClauses_*` diagnostic during a command-line build. -- [ ] The existing analyzer and code-fix tests pass against the split assemblies, and the complete solution's tests all pass. -- [ ] `Directory.Packages.props` contains no project-name-conditional version override; the Roslyn version constraint for compiler-loaded assemblies is documented where the version is pinned. -- [ ] The new project appears in `Light.GuardClauses.sln` under the `tools/analyzers` solution folder hierarchy. +- [x] Building the complete solution in Release configuration produces no RS1038 warning and no new warnings. +- [x] The `Light.GuardClauses.InternalRoslynAnalyzers` assembly references only compiler-layer Roslyn packages and contains no code fix providers; the two code fix providers reside in a new `Light.GuardClauses.InternalRoslynAnalyzers.CodeFixes` assembly. +- [x] The product project receives both assemblies as analyzer references, so command-line builds still run the XML-comment analyzer and IDEs can still load the code fixes. +- [x] Introducing a non-default `parameterName` or `message` XML comment on a standard exception assertion in the product project still produces the corresponding `LightGuardClauses_*` diagnostic during a command-line build. +- [x] The existing analyzer and code-fix tests pass against the split assemblies, and the complete solution's tests all pass. +- [x] `Directory.Packages.props` contains no project-name-conditional version override; the Roslyn version constraint for compiler-loaded assemblies is documented where the version is pinned. +- [x] The new project appears in `Light.GuardClauses.sln` under the `tools/analyzers` solution folder hierarchy. ## Technical Details diff --git a/src/Light.GuardClauses/Light.GuardClauses.csproj b/src/Light.GuardClauses/Light.GuardClauses.csproj index 45a5af7..f31694b 100644 --- a/src/Light.GuardClauses/Light.GuardClauses.csproj +++ b/src/Light.GuardClauses/Light.GuardClauses.csproj @@ -17,6 +17,10 @@ Include="../../tools/analyzers/Light.GuardClauses.InternalRoslynAnalyzers/Light.GuardClauses.InternalRoslynAnalyzers.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" /> + diff --git a/tests/Light.GuardClauses.InternalRoslynAnalyzers.Tests/Light.GuardClauses.InternalRoslynAnalyzers.Tests.csproj b/tests/Light.GuardClauses.InternalRoslynAnalyzers.Tests/Light.GuardClauses.InternalRoslynAnalyzers.Tests.csproj index 8c764e0..c0c478d 100644 --- a/tests/Light.GuardClauses.InternalRoslynAnalyzers.Tests/Light.GuardClauses.InternalRoslynAnalyzers.Tests.csproj +++ b/tests/Light.GuardClauses.InternalRoslynAnalyzers.Tests/Light.GuardClauses.InternalRoslynAnalyzers.Tests.csproj @@ -5,7 +5,7 @@ - + diff --git a/tools/analyzers/Light.GuardClauses.InternalRoslynAnalyzers.CodeFixes/Light.GuardClauses.InternalRoslynAnalyzers.CodeFixes.csproj b/tools/analyzers/Light.GuardClauses.InternalRoslynAnalyzers.CodeFixes/Light.GuardClauses.InternalRoslynAnalyzers.CodeFixes.csproj new file mode 100644 index 0000000..aee134d --- /dev/null +++ b/tools/analyzers/Light.GuardClauses.InternalRoslynAnalyzers.CodeFixes/Light.GuardClauses.InternalRoslynAnalyzers.CodeFixes.csproj @@ -0,0 +1,16 @@ + + + + netstandard2.0 + + + + + + + + + + + + diff --git a/tools/analyzers/Light.GuardClauses.InternalRoslynAnalyzers/MessageXmlCommentFix.cs b/tools/analyzers/Light.GuardClauses.InternalRoslynAnalyzers.CodeFixes/MessageXmlCommentFix.cs similarity index 100% rename from tools/analyzers/Light.GuardClauses.InternalRoslynAnalyzers/MessageXmlCommentFix.cs rename to tools/analyzers/Light.GuardClauses.InternalRoslynAnalyzers.CodeFixes/MessageXmlCommentFix.cs diff --git a/tools/analyzers/Light.GuardClauses.InternalRoslynAnalyzers/ParameterNameXmlCommentFix.cs b/tools/analyzers/Light.GuardClauses.InternalRoslynAnalyzers.CodeFixes/ParameterNameXmlCommentFix.cs similarity index 100% rename from tools/analyzers/Light.GuardClauses.InternalRoslynAnalyzers/ParameterNameXmlCommentFix.cs rename to tools/analyzers/Light.GuardClauses.InternalRoslynAnalyzers.CodeFixes/ParameterNameXmlCommentFix.cs diff --git a/tools/analyzers/Light.GuardClauses.InternalRoslynAnalyzers/Light.GuardClauses.InternalRoslynAnalyzers.csproj b/tools/analyzers/Light.GuardClauses.InternalRoslynAnalyzers/Light.GuardClauses.InternalRoslynAnalyzers.csproj index c4ce195..2210dab 100644 --- a/tools/analyzers/Light.GuardClauses.InternalRoslynAnalyzers/Light.GuardClauses.InternalRoslynAnalyzers.csproj +++ b/tools/analyzers/Light.GuardClauses.InternalRoslynAnalyzers/Light.GuardClauses.InternalRoslynAnalyzers.csproj @@ -7,7 +7,7 @@ - +