Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions .agents/skills/opcua-v20-migration/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ description: |
errors after upgrading to v20".
license: MIT
compatibility: |
Requires .NET SDK 10.0.300+, a C# project, and resolvable access to the
Requires .NET SDK 9.0.100+ (10.0.300+ for the `dotnet format analyzers`
auto-fix pass), a C# project, and resolvable access to the
OPCFoundation.NetStandard.Opc.Ua.MigrationAnalyzer NuGet package (v2.0.*-*).
IDE auto-fixes need a Workspaces-aware host (Visual Studio, Rider, or
`dotnet format`). Generator + analyzers load in csc.exe too.
Expand Down Expand Up @@ -166,9 +167,9 @@ The single `OPCFoundation.NetStandard.Opc.Ua.MigrationAnalyzer` NuGet contains

| Component | Where | Loaded by | Purpose |
|---|---|---|---|
| `Opc.Ua.MigrationAnalyzer.dll` | `analyzers/dotnet/cs/` | csc.exe and IDE | 19 `DiagnosticAnalyzer`s (UA0001–UA0022). Targets stable Roslyn 4.14 API, no `Workspaces` reference, csc-safe. |
| `Opc.Ua.MigrationAnalyzer.CodeFixer.dll` | `analyzers/dotnet/cs/` | Workspaces-aware hosts only (Visual Studio, Rider, `dotnet format analyzers`) | 14 `CodeFixProvider`s. |
| `Opc.Ua.MigrationAnalyzer.Generator.dll` | `analyzers/dotnet/cs/` | csc.exe and IDE | `IIncrementalGenerator` that emits `internal sealed [Obsolete] class <Name>Collection : List<TElement>` shims into the consumer compilation for every `<Type>Collection` reference that fails to bind. |
| `Opc.Ua.MigrationAnalyzer.dll` | `analyzers/dotnet/roslyn4.14/cs/` and `roslyn5.0/cs/` | csc.exe and IDE | 19 `DiagnosticAnalyzer`s (UA0001–UA0022). No `Workspaces` reference, csc-safe. |
| `Opc.Ua.MigrationAnalyzer.CodeFixer.dll` | `analyzers/dotnet/roslyn4.14/cs/` and `roslyn5.0/cs/` | Workspaces-aware hosts only (Visual Studio, Rider, `dotnet format analyzers`) | 14 `CodeFixProvider`s. |
| `Opc.Ua.MigrationAnalyzer.Generator.dll` | `analyzers/dotnet/roslyn4.14/cs/` and `roslyn5.0/cs/` | csc.exe and IDE | `IIncrementalGenerator` that emits `internal sealed [Obsolete] class <Name>Collection : List<TElement>` shims into the consumer compilation for every `<Type>Collection` reference that fails to bind. |
| `Opc.Ua.MigrationAnalyzer.Core.dll` | `lib/<tfm>/` × 6 TFMs (`net472`, `net48`, `netstandard2.1`, `net8.0`, `net9.0`, `net10.0`) | Runtime | Re-supplies the obsolete extension surface 2.0 moved or removed so 1.5.378 call sites continue to compile with `[Obsolete]` warnings. |

### The 19 analyzer rules at a glance
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ correctly.

| Component | Minimum | Recommended | Notes |
|---|---|---|---|
| .NET SDK | 10.0.300 | latest 10.x | Earlier SDKs ship older Roslyn that has known incremental-generator bugs |
| `dotnet format` | bundled with SDK 10.0.300+ | latest 10.x | The `analyzers` subcommand is what applies UA0002…UA0022 fixes |
| .NET SDK | 9.0.100 | latest 10.x | 9.0.100 ships Roslyn 4.14, which the package's `roslyn4.14` band targets. Earlier SDKs ship older Roslyn that has known incremental-generator bugs |
| `dotnet format` | bundled with SDK 10.0.300+ | latest 10.x | The `analyzers` subcommand is what applies UA0002…UA0022 fixes. Diagnostics alone need only SDK 9.0.100; the auto-fix pass needs 10.0.300+ |
| C# language version | 13 | 14 (default in SDK 10) | Required for `extension` keyword the runtime shim uses |
| Consumer project SDK | `Microsoft.NET.Sdk` (SDK-style) | same | Pre-SDK MSBuild XML projects (`xmlns="…/2003"`) cannot install the analyzer — see [`known-gaps.md` G1](known-gaps.md#g1--legacy-net-framework-winforms-projects-in-pre-sdk-msbuild-xml) |

Expand Down Expand Up @@ -37,19 +37,27 @@ and SDK-style csproj rewrites.

## Roslyn API targeting (internal)

The migration package's Roslyn components are built against the **stable
analyzer API surface**:
Every Roslyn component ships twice, once per band, under
`analyzers/dotnet/<band>/cs/`. The .NET SDK picks the highest band its compiler
supports and ignores the rest, so a single package serves both hosts:

| DLL | Roslyn API target | Why |
| Band | Built against | Loaded by |
|---|---|---|
| `Opc.Ua.MigrationAnalyzer.dll` | `Microsoft.CodeAnalysis.CSharp 4.14.0` | csc-safe (loads in `csc.exe`); Workspaces-free |
| `Opc.Ua.MigrationAnalyzer.Generator.dll` | `Microsoft.CodeAnalysis.CSharp 4.14.0` | csc-safe; needed for `IIncrementalGenerator` |
| `Opc.Ua.MigrationAnalyzer.CodeFixer.dll` | `Microsoft.CodeAnalysis.CSharp 4.14.0` + `Microsoft.CodeAnalysis.CSharp.Workspaces 4.14.0` | Loaded only in Workspaces-aware hosts (Visual Studio, `dotnet format`) |

> The repo's `Directory.Packages.props` pins all `Microsoft.CodeAnalysis.*`
> packages to `4.14.0`. This is the **stable analyzer API**, not the
> csc-internal version that the .NET SDK ships (which is `5.x` in SDK 10).
> Analyzers built against 5.x silently fail to load in csc.exe — see
| `roslyn4.14` | `Microsoft.CodeAnalysis.CSharp 4.14.0` | Visual Studio 2022 17.14+ / .NET 9 SDK |
| `roslyn5.0` | `Microsoft.CodeAnalysis.CSharp 5.0.0` | Visual Studio 2026 18.0+ / .NET 10 SDK |

Within a band, the three components differ only in what else they reference:

| DLL | Extra reference | Why |
|---|---|---|
| `Opc.Ua.MigrationAnalyzer.dll` | none | csc-safe (loads in `csc.exe`); Workspaces-free |
| `Opc.Ua.MigrationAnalyzer.Generator.dll` | none | csc-safe; needed for `IIncrementalGenerator` |
| `Opc.Ua.MigrationAnalyzer.CodeFixer.dll` | `Microsoft.CodeAnalysis.CSharp.Workspaces` | Loaded only in Workspaces-aware hosts (Visual Studio, `dotnet format`) |

> An analyzer built against a **newer** Roslyn than the host is skipped silently
> with warning `CS9057` — the consumer simply gets no diagnostics and no
> generated shims. That is why the package ships a band per supported host
> rather than a single `analyzers/dotnet/cs/` folder; see
> [`known-gaps.md` G9](known-gaps.md#g9--analyzer-silently-doesnt-load-under-cscexe-historical-fixed).

## Verifying analyzer + generator loaded under csc.exe
Expand Down
6 changes: 4 additions & 2 deletions .agents/skills/opcua-v20-migration/references/known-gaps.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,10 @@ code-fixers in one assembly, which transitively referenced
swallowed the Workspaces load failure → zero diagnostics across all samples
even though `/analyzer:` was on the csc command line.

**Status:** fixed (commit `861fa6ee1`). Analyzer split into two DLLs; analyzer
DLL is Workspaces-free + targets stable Roslyn 4.14 API.
**Status:** fixed (commit `861fa6ee1`). Analyzer split into two DLLs; the analyzer
DLL is Workspaces-free. Each DLL now ships once per Roslyn band (`roslyn4.14`,
`roslyn5.0`) so the host always loads a build it can run — see
[`compatibility-matrix.md`](compatibility-matrix.md#roslyn-api-targeting-internal).

**Verification:** if you ever suspect the analyzer isn't firing, run with
`/p:ReportAnalyzer=true` and confirm `Opc.Ua.MigrationAnalyzer` and
Expand Down
63 changes: 63 additions & 0 deletions .azurepipelines/expected-packages.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Every NuGet package the preview pipeline is expected to produce from
# `dotnet pack preview-pack.slnx`, one package id per line.
#
# `validate-source-generator-packages.ps1` fails the build when the packed output
# does not match this list exactly. The point is that adding, removing or renaming
# a shipped package has to be a conscious, reviewed act - a package silently
# disappearing (or a private build-time project accidentally becoming packable)
# is otherwise invisible until consumers notice.
#
# Lines starting with '#' and blank lines are ignored. The list covers the modern
# `dotnet pack` output only; the hand-authored `nuget/*.nuspec` packages are packed
# in a later pipeline step and are not part of this check.
#
# To update: run `dotnet pack preview-pack.slnx` and reconcile the produced
# package ids with this file in the same pull request that changes them.

OPCFoundation.NetStandard.Opc.Ua.Bindings.Https
OPCFoundation.NetStandard.Opc.Ua.Client
OPCFoundation.NetStandard.Opc.Ua.Client.ComplexTypes
OPCFoundation.NetStandard.Opc.Ua.Configuration
OPCFoundation.NetStandard.Opc.Ua.Core
OPCFoundation.NetStandard.Opc.Ua.Core.Diagnostics
OPCFoundation.NetStandard.Opc.Ua.Core.Schema
OPCFoundation.NetStandard.Opc.Ua.Core.Types
OPCFoundation.NetStandard.Opc.Ua.Di
OPCFoundation.NetStandard.Opc.Ua.Di.Client
OPCFoundation.NetStandard.Opc.Ua.Di.Server
OPCFoundation.NetStandard.Opc.Ua.Gds.Client.Common
OPCFoundation.NetStandard.Opc.Ua.Gds.Common
OPCFoundation.NetStandard.Opc.Ua.Gds.Server.Common
OPCFoundation.NetStandard.Opc.Ua.ISA95
OPCFoundation.NetStandard.Opc.Ua.ISA95.Client
OPCFoundation.NetStandard.Opc.Ua.ISA95.Server
OPCFoundation.NetStandard.Opc.Ua.Lds.Server
OPCFoundation.NetStandard.Opc.Ua.Mcp
OPCFoundation.NetStandard.Opc.Ua.MigrationAnalyzer
OPCFoundation.NetStandard.Opc.Ua.PubSub
OPCFoundation.NetStandard.Opc.Ua.PubSub.Adapter
OPCFoundation.NetStandard.Opc.Ua.PubSub.Diagnostics
OPCFoundation.NetStandard.Opc.Ua.PubSub.Eth
OPCFoundation.NetStandard.Opc.Ua.PubSub.Kafka
OPCFoundation.NetStandard.Opc.Ua.PubSub.Mqtt
OPCFoundation.NetStandard.Opc.Ua.PubSub.Schema
OPCFoundation.NetStandard.Opc.Ua.PubSub.Server
OPCFoundation.NetStandard.Opc.Ua.PubSub.Udp
OPCFoundation.NetStandard.Opc.Ua.Quickstarts.Servers
OPCFoundation.NetStandard.Opc.Ua.Redundancy
OPCFoundation.NetStandard.Opc.Ua.Redundancy.Client
OPCFoundation.NetStandard.Opc.Ua.Redundancy.Kubernetes
OPCFoundation.NetStandard.Opc.Ua.Redundancy.PubSub
OPCFoundation.NetStandard.Opc.Ua.Redundancy.Server
OPCFoundation.NetStandard.Opc.Ua.Security.Certificates
OPCFoundation.NetStandard.Opc.Ua.Server
OPCFoundation.NetStandard.Opc.Ua.SourceGeneration
OPCFoundation.NetStandard.Opc.Ua.SourceGeneration.Core
OPCFoundation.NetStandard.Opc.Ua.SourceGeneration.Stack
OPCFoundation.NetStandard.Opc.Ua.Types
OPCFoundation.NetStandard.Opc.Ua.WotCon
OPCFoundation.NetStandard.Opc.Ua.WotCon.Client
OPCFoundation.NetStandard.Opc.Ua.WotCon.Server
OPCFoundation.NetStandard.Opc.Ua.XRegistry
OPCFoundation.NetStandard.Opc.Ua.XRegistry.Client
OPCFoundation.NetStandard.Opc.Ua.XRegistry.Server
2 changes: 1 addition & 1 deletion .azurepipelines/preview.yml
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ jobs:
inputs:
command: 'pack'
# Scoped to nuget/ — the MigrationAnalyzer nuspec at tools/Opc.Ua.MigrationAnalyzer/
# uses MSBuild $(...) substitutions ($(shimBin), $(repoRoot), $(analyzerDll))
# uses MSBuild $(...) substitutions ($(shimBin), $(repoRoot), $(analyzerDllVS2026))
# that only `dotnet pack`'s _SetMigrationAnalyzerNuspecProperties target can
# resolve. Standalone `nuget.exe pack` leaves them literal and crashes with
# DirectoryNotFoundException on `D:\net472`. The MigrationAnalyzer package is
Expand Down
Loading
Loading