Skip to content

2.0 Migration Analyzer and codefixer with Agent update#3820

Draft
marcschier wants to merge 10 commits into
OPCFoundation:masterfrom
marcschier:opc-ua-codefixers
Draft

2.0 Migration Analyzer and codefixer with Agent update#3820
marcschier wants to merge 10 commits into
OPCFoundation:masterfrom
marcschier:opc-ua-codefixers

Conversation

@marcschier
Copy link
Copy Markdown
Collaborator

@marcschier marcschier commented May 28, 2026

Proposed changes

Introduces OPCFoundation.NetStandard.Opc.Ua.MigrationAnalyzer — a single NuGet package that ships three coordinated DLLs to make the 1.5.378 → 2.0 upgrade as close to "install package, run dotnet format, remove package" as possible:

DLL Where Purpose
Opc.Ua.MigrationAnalyzer.dll analyzers/dotnet/cs/ 19 Roslyn analyzers (UA0001UA0022) covering every breaking-change pattern in Docs/MigrationGuide.md. Built against the stable Roslyn 4.14 analyzer API — loads cleanly in csc.exe (no Microsoft.CodeAnalysis.Workspaces reference).
Opc.Ua.MigrationAnalyzer.CodeFixer.dll analyzers/dotnet/cs/ 14 companion CodeFixProviders; loaded only by Workspaces-aware hosts (Visual Studio, dotnet format analyzers).
Opc.Ua.MigrationAnalyzer.Core.dll lib/<tfm>/ for net472, net48, netstandard2.1, net8.0, net9.0, net10.0 1.5.378 → 2.0 compatibility shim. Re-exposes the obsolete extension surface so 1.5.378-style call sites keep compiling against 2.0 with warnings instead of errors.

Migration workflow the package enables

  1. Bump every OPCFoundation.NetStandard.Opc.Ua.* PackageReference to 2.0.*-*.
  2. Add the migration package as a build-only dependency:
    <PackageReference Include="OPCFoundation.NetStandard.Opc.Ua.MigrationAnalyzer"
                      Version="2.0.*-*"
                      PrivateAssets="all" />
  3. Run dotnet build. Most call sites that 1.6 made obsolete still compile because the Core (shim) DLL re-supplies them. What remains are [Obsolete] warnings + UA00xx analyzer diagnostics.
  4. Apply auto-fixes in the IDE or from the command line:
    dotnet format analyzers <solution>.sln \
        --diagnostics UA0002 UA0003 UA0004 UA0005 UA0006 UA0007 UA0008 \
                      UA0009 UA0010 UA0012 UA0014 UA0019 UA0020 UA0022 \
        --severity warn
  5. Walk the residual UA0001 / UA0011 / UA0015 / UA0018 / UA0021 diagnostics manually — they cover patterns that require human judgement (telemetry plumbing, async promotion, structural redesigns).
  6. Remove the migration package. You are now on clean 2.0 with no shim dependency.

19-rule reference

Rule Replaces Default Auto-fix
UA0001 Utils.Trace / Utils.LogX Info No (telemetry plumbing requires manual review)
UA0002 Removed <Type>Collection wrappers Warning Yes (→ List<T> / ArrayOf<T>)
UA0003 x == null on now-struct built-ins Warning Yes (→ x.IsNull)
UA0004 ?. on now-struct built-ins Warning Yes (drop the ?)
UA0005 byte[] where ByteString is expected Warning Yes (append .ToByteString())
UA0006 new Variant(object|DateTime|Guid|byte[]) Warning Yes (→ Variant.From(...))
UA0007 new NodeId(string) / new ExpandedNodeId(string) Warning Yes (→ Parse)
UA0008 Session.Call(..., params object[]) Warning Yes (wrap with Variant.From)
UA0009 [DataContract] / [DataMember] on config extensions Warning Yes (→ [DataType] / [DataTypeField])
UA0010 using / Dispose on CertificateIdentifier / UserIdentity / IUserIdentityTokenHandler Warning Yes (drop the disposable)
UA0011 Sync IUserIdentityTokenHandler.{Encrypt,Decrypt,Sign,Verify} Info No (async promotion required)
UA0012 CertificateFactory.* static helpers Warning Yes (→ instance methods)
UA0014 DataValue.IsGood(dv) static helper Warning Yes (→ dv.IsGood)
UA0015 Sync / APM members on GDS / LDS clients Info No (async promotion required)
UA0018 CertificateIdentifier.Certificate getter Info No (→ LoadCertificate2Async)
UA0019 new DataValue(StatusCode[, ts]) Warning Yes (→ object-initializer form)
UA0020 EncodeableFactory.GlobalFactory / Create() Warning Yes (→ ServiceMessageContext.Factory / Fork())
UA0021 CertificateValidator / CertificateValidationEventArgs (structural rename) Info No (see migration guide §Certificate validation)
UA0022 ApplicationConfiguration.CertificateValidator / ServerBase.CertificateValidator property rename Warning Yes (→ .CertificateManager)

What the Core (shim) DLL covers

Opc.Ua.MigrationAnalyzer.Core.dll ships obsolete extension members (using the C# 14 extension keyword) so 1.5.378 call sites continue to compile:

  • Moved obsolete extensions the 1.6 libraries no longer carry inline: NodeId / Variant / DataValue null-check helpers, Session sync helpers, Subscription sync helpers, ApplicationInstance helpers, ServerBase.Start / Stop, TransportChannel APM (BeginX / EndX), ChannelBase static factory methods, etc.
  • New shims for genuinely-removed members: EncodeableFactory.GlobalFactory, CertificateIdentifier.Certificate (throws), sync wrappers for IUserIdentityTokenHandler.{Encrypt,Decrypt,Sign,Verify}, sync + APM wrappers for the GDS / LDS client APIs, GlobalDiscoverySampleServer 1.5.378-shape ctor (in-tree Libraries/Opc.Ua.Gds.Server.Common/).

⚠️ The sync shims wrap their *Async counterparts via Task.Run(…).GetAwaiter().GetResult(). Intended as a migration aid only — port to await before shipping.

What the shim cannot cover

Source-level changes the shim has no syntactic foothold for — use the listed analyzer fix:

  • == null / != null on now-struct types — UA0003
  • ?. on now-struct types — UA0004
  • using var x = new CertificateIdentifier(...)UA0010
  • [DataContract] / [DataMember] on configuration extensions — UA0009
  • Removed <Type>Collection wrappers — UA0002
  • CertificateValidator type rename — UA0021 (manual — structural rewrite)

Repo layout

Tools/Opc.Ua.MigrationAnalyzer/                # analyzer DLL
  Analyzers/UA00xxAnalyzer.cs                  # 19 DiagnosticAnalyzer impls
  Diagnostics/{DiagnosticIds,Descriptors,WellKnownProperties}.cs
  Helpers/{SymbolExtensions,UaSymbols}.cs
  Opc.Ua.MigrationAnalyzer.nuspec              # ships all 3 DLLs
  OPCFoundation.NetStandard.Opc.Ua.MigrationAnalyzer.props
  NugetREADME.md

Tools/Opc.Ua.MigrationAnalyzer.CodeFixer/      # code-fix DLL
  CodeFixes/UA00xxCodeFix.cs                   # 14 CodeFixProvider impls
  (shares Diagnostics + Helpers via <Compile Link=...> to avoid restore cycle)

Tools/Opc.Ua.MigrationAnalyzer.Core/           # shim DLL (multi-TFM)
  Client/, Configuration/, Core/, Gds.Client.Common/, Types/
  Marker/OpcUaShimAttribute.cs                 # tags each shim member with its UA rule id
  Properties/, readme.md

Tools/Roslyn.slnx                              # solution for SourceGen + MigrationAnalyzer projects

Tests/Opc.Ua.MigrationAnalyzer.Tests/          # 107 analyzer tests (NUnit + Microsoft.CodeAnalysis.Testing)
Tests/Opc.Ua.MigrationAnalyzer.Core.Tests/     # 13 shim tests (10 active + 3 environment-skipped)

Documentation updates

  • Docs/MigrationGuide.md — adds a "Automate the migration" callout pointing at the new package.
  • .github/agents/opcua-v20-migration.agent.md — rewritten so the first step is installing the migration package; the 14-section categorical reference is preserved as a fallback for patterns the analyzers can't cover. Adds a TreatWarningsAsErrors recipe and a "known compatibility gaps" section for legacy .NET Framework WinForms projects (which don't honour Directory.Build.targets PackageReference injection).

Central package pinning

Directory.Packages.props pins Microsoft.CodeAnalysis.* (Common, CSharp, CSharp.Workspaces, Workspaces.Common) to 4.14.0 — the stable analyzer API surface. The earlier 5.3 pin was the csc-internal version, which is not safe for analyzer hosts.

Dogfooded against OPCFoundation/UA-.NETStandard-Samples

The full 42-sample dogfood pass surfaced a critical analyzer-loader bug: the analyzer DLL initially co-shipped its code-fix providers in one assembly, which transitively referenced Microsoft.CodeAnalysis.Workspaces.dll. csc.exe's analyzer host ships only Microsoft.CodeAnalysis.dll + CSharp.dll in its bincore and silently swallows load failures — net result: zero diagnostics across all samples even though /analyzer: was on the csc command line. Fix landed in commit 861fa6ee1:

  1. Split the assembly: analyzer in Opc.Ua.MigrationAnalyzer.dll (Workspaces-free, csc-safe), code-fixes in Opc.Ua.MigrationAnalyzer.CodeFixer.dll (Workspaces-aware, loaded only in IDE / dotnet format).
  2. Downgrade to Roslyn 4.14 — the stable analyzer API. Microsoft.CodeAnalysis.CSharp 5.x is the csc compiler version and not appropriate for analyzers.
  3. /p:ReportAnalyzer=true confirms all 19 analyzers initialise and execute on real consumer code.

Additional dogfood findings documented in the migration agent's "known compatibility gaps" section:

  • Legacy WinForms .Net4 sample projects use pre-SDK MSBuild XML (xmlns="…/2003") and ignore Directory.Build.targets <PackageReference> injection — consumers in this format must add the migration package to their csproj inline. The dotnet build resx-tooling failure (MSB3822 / MSB3823) on these projects is unrelated to this PR; users must build them with full MSBuild.exe.
  • Reference Server.csproj depends on the unpublished OPCFoundation.NetStandard.Opc.Ua.Quickstarts.Servers meta-package; consumers must switch to a ProjectReference against Applications/Quickstarts.Servers.

Packaging design notes

  • Hand-authored nuspec (because the SDK-pack flow can't ship two analyzer DLLs + a six-TFM shim from a single csproj). All 6 OPC UA runtime package dependencies are declared per-TFM-group.
  • build/OPCFoundation.NetStandard.Opc.Ua.MigrationAnalyzer.props is auto-imported by NuGet (file name must match package id exactly) and injects both analyzer DLLs as <Analyzer> items — this works around the hand-authored nuspec not auto-registering analyzers/dotnet/cs/ assets.
  • DiagnosticIds.cs / DiagnosticDescriptors.cs / WellKnownProperties.cs / Helpers/*.cs are shared via <Compile Link=…> between the analyzer and code-fixer projects to break what would otherwise be a NuGet restore cycle (CodeFixer ProjectReferences the analyzer for shared constants; analyzer's pack pipeline MSBuild-invokes the CodeFixer build).
  • Opc.Ua.MigrationAnalyzer.Core is multi-TFM (net472, net48, netstandard2.1, net8.0, net9.0, net10.0) and non-packable on its own; the parent migration package's nuspec consumes each TFM's DLL by path.

Verification

  • dotnet build UA.slnx -c Debug0 errors (572 pre-existing CA2007 / NU1702 warnings).
  • dotnet test Tests/Opc.Ua.MigrationAnalyzer.Tests107 / 107 analyzer + code-fix tests pass on net10.0.
  • dotnet test Tests/Opc.Ua.MigrationAnalyzer.Core.Tests10 / 10 active shim tests + 3 skipped (environment-dependent).
  • dotnet pack Tools/Opc.Ua.MigrationAnalyzer/Opc.Ua.MigrationAnalyzer.csproj — produces a single .nupkg with analyzers/dotnet/cs/{Opc.Ua.MigrationAnalyzer.dll, Opc.Ua.MigrationAnalyzer.CodeFixer.dll} + 6-TFM lib/<tfm>/Opc.Ua.MigrationAnalyzer.Core.dll.
  • Analyzer DLL ref check confirms it references only Microsoft.CodeAnalysis 4.14.0 + Microsoft.CodeAnalysis.CSharp 4.14.0 — Workspaces-free, csc-safe.

Types of changes

  • New feature (non-breaking change which adds functionality)
  • Documentation update
  • Bug fix
  • Breaking change

Introduces Tools/Opc.Ua.CodeFixers, a new Roslyn analyzer + code-fixer package that helps consumers migrate from OPC UA .NET Standard 1.5.378 to 2.0 by mechanizing 17 of the breaking-change patterns documented in Docs/MigrationGuide.md.

Rules (UA0001-UA0020): UA0001 Utils.Trace/LogX to ILogger (diagnostic only); UA0002 removed Type Collections to List<T>/ArrayOf<T>; UA0003 == null on now-struct built-ins to .IsNull; UA0004 ?. on now-struct built-ins; UA0005 byte[] to ByteString at API boundaries; UA0006 obsolete Variant(object|DateTime|Guid|byte[]) ctors to Variant.From; UA0007 new NodeId(string) to NodeId.Parse; UA0008 Session.Call params object[] to Variant.From wrapping; UA0009 [DataContract]/[DataMember] to [DataType]/[DataTypeField]; UA0010 remove using/Dispose on CertificateIdentifier/UserIdentity/IUserIdentityTokenHandler (diagnostic only); UA0011 IUserIdentityTokenHandler sync to Async (diagnostic only); UA0012 CertificateFactory static to DefaultCertificateFactory.Instance; UA0014 DataValue.IsGood static helper to instance property; UA0015 GDS/LDS client sync/APM to Async (diagnostic only); UA0018 CertificateIdentifier.Certificate getter to ResolveAsync (diagnostic only); UA0019 obsolete new DataValue(StatusCode) to DataValue.FromStatusCode; UA0020 EncodeableFactory.GlobalFactory/Create to ServiceMessageContext.Factory/Fork.

Includes Tools/Opc.Ua.CodeFixers (analyzer project, netstandard2.0, EnforceExtendedAnalyzerRules, centralized DiagnosticDescriptors and DiagnosticIds, UaSymbols + SymbolExtensions helpers, AnalyzerReleases tracking) and Tests/Opc.Ua.CodeFixers.Tests (87 NUnit tests with a custom AnalyzerHarness and OpcUaStubs).

Adds Microsoft.CodeAnalysis.CSharp.Workspaces 5.3.0 and Microsoft.CodeAnalysis.Workspaces.Common 5.3.0 to Directory.Packages.props (used PrivateAssets=all by the analyzer; required by the test harness).

Registers the projects in UA.slnx and Tools/SourceGeneration.slnx under a new 'CodeFixers' folder. Tests pass on net10.0 (87/87).
@CLAassistant
Copy link
Copy Markdown

CLAassistant commented May 28, 2026

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
1 out of 2 committers have signed the CLA.

✅ marcschier
❌ Copilot
You have signed the CLA already but the status is still pending? Let us recheck it.

@marcschier marcschier changed the title [Tools] Add Opc.Ua.CodeFixers Roslyn analyzer package for 1.5.378 → 1.6 migration DRAFT ONLY Add Opc.Ua.CodeFixers Roslyn analyzer package for 2.0 solution migration May 28, 2026
@codecov
Copy link
Copy Markdown

codecov Bot commented May 28, 2026

Codecov Report

❌ Patch coverage is 70.49180% with 18 lines in your changes missing coverage. Please review.
✅ Project coverage is 71.12%. Comparing base (01d7162) to head (0a019c8).

Files with missing lines Patch % Lines
Libraries/Opc.Ua.Server/Server/StandardServer.cs 0.00% 7 Missing ⚠️
...ore/Security/Certificates/CertificateIdentifier.cs 0.00% 4 Missing ⚠️
...raries/Opc.Ua.Configuration/ApplicationInstance.cs 57.14% 1 Missing and 2 partials ⚠️
...igrationAnalyzer.Core/Marker/OpcUaShimAttribute.cs 0.00% 3 Missing ⚠️
...yzer.Core/Core/Types/Encoders/EncodeableFactory.cs 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #3820      +/-   ##
==========================================
- Coverage   71.13%   71.12%   -0.01%     
==========================================
  Files         778      781       +3     
  Lines      143274   143329      +55     
  Branches    24234    24241       +7     
==========================================
+ Hits       101918   101946      +28     
- Misses      33005    33026      +21     
- Partials     8351     8357       +6     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copilot AI and others added 9 commits May 29, 2026 10:25
…package

Introduces Tools/Opc.Ua.CodeFixers.Shim/Opc.Ua.CodeFixers.Shim.csproj which ships alongside the analyzer DLL in the OPCFoundation.NetStandard.Opc.Ua.CodeFixers NuGet. Consumer projects can drop the package in: 1.5.378-style code keeps compiling against 1.6 via the shim extension surface, and the matching UA00xx analyzer fires Info-level diagnostics so consumers can migrate off the shim incrementally.

Shim contents organized under Shims/<libname>/ mirroring the source project layout: Shims/Types/, Shims/Core.Types/, Shims/Core/, Shims/Client/, Shims/Configuration/, Shims/Gds.Client.Common/. Marker [OpcUaShim(RuleId)] in Marker/OpcUaShimAttribute.cs lets the analyzer correlate shim calls back to the UA00xx migration rule.

Phase 6.C moved obsolete extension surface from 8 *Obsolete*.cs files in Stack/Opc.Ua.Types, Stack/Opc.Ua.Core, Stack/Opc.Ua.Core.Types, Libraries/Opc.Ua.Client, Libraries/Opc.Ua.Configuration into the shim project. Phase 6.D added 6 NEW shims for genuinely-removed members (EncodeableFactory.GlobalFactory; CertificateIdentifier.Certificate (throwing); IUserIdentityTokenHandler.Encrypt/Decrypt/Sign/Verify sync wrappers; GlobalDiscoveryServerClient.RegisterApplication/UnregisterApplication; ServerPushConfigurationClient.ApplyChanges; LocalDiscoveryServerClient.BeginFindServers/EndFindServers APM).

Phase 6.E extended the existing UA0008/UA0011/UA0015/UA0018/UA0020 analyzers to detect calls binding to shim extensions via [OpcUaShim] marker. Phase 6.F added Tests/Opc.Ua.CodeFixers.Shim.Tests with 11 runtime + meta tests (verifying every [OpcUaShim] member also carries [Obsolete] and a well-formed UA00xx id). Phase 6.G rewrote NugetREADME.md to cover both the analyzer rules and the shim.

Three *Obsolete*.cs files left in-place (BLOCKED): Stack/Opc.Ua.Core/Types/Utils/UtilsObsolete.cs, Stack/Opc.Ua.Core.Types/Constants/Helpers.Obsolete.cs, Libraries/Opc.Ua.Client/CoreClientUtilsObsolete.cs. Reason: C# 14 extension(StaticClass) does NOT add real static members to a static class (per csharplang/proposals/extensions.md 131-138), so consumer call syntax like Utils.Trace(...) cannot be preserved by relocating the implementation to a separate assembly.

Cross-project internal callers in Opc.Ua.Configuration/Opc.Ua.Server/ConsoleReferenceClient that called the moved IServerBase.Stop() and TraceConfiguration.ApplySettings() extensions were rewritten to inline the modern non-obsolete equivalents (Utils.SetTraceLog/SetTraceMask/SetTraceOutput, IServerBase.StopAsync). InternalsVisibleTo added on Opc.Ua.Core and Opc.Ua.Configuration so the shim can reach the internal helpers its moved factory members already depended on.

Build: UA.slnx compiles 0 errors. Tests: Opc.Ua.CodeFixers.Tests 94/94 passing; Opc.Ua.CodeFixers.Shim.Tests 8 pass + 3 ignored placeholders (sealed GDS client types cannot be Moq-ed; tests deferred to integration). Tools/Opc.Ua.SourceGeneration.Core/Generators/ClientApiTemplates.cs adapted to emit modern async-with-GetAwaiter().GetResult() bodies instead of the now-moved obsolete sync/APM helpers.
…packaging

Phase 7: UA0021 covers the 1.5.378 to 1.6 rename of CertificateValidator and CertificateValidationEventArgs (discovered during Phase 6.F samples dogfood). Rule is Info-level / diagnostic-only because the 1.6 replacement is structurally different (event-based -> async result + callback pattern); no auto-fix is feasible and no shim is provided. Adds analyzer at Tools/Opc.Ua.CodeFixers/Analyzers/UA0021CertificateValidatorRenameAnalyzer.cs with dual-mode detection (semantic when the legacy Obsolete symbol still resolves, syntactic fallback gated by 'using Opc.Ua' when the type is fully removed). Tests increase from 94 to 99 (5 new UA0021 tests).

Phase 8: enable bundled NuGet packaging for OPCFoundation.NetStandard.Opc.Ua.CodeFixers. Adds a hand-authored Tools/Opc.Ua.CodeFixers/Opc.Ua.CodeFixers.nuspec that combines the analyzer DLL (analyzers/dotnet/cs/Opc.Ua.CodeFixers.dll) and the shim DLL (lib/<tfm>/Opc.Ua.CodeFixers.Shim.dll) for all six TFMs (net472, net48, netstandard2.1, net8.0, net9.0, net10.0). Declares the six OPCFoundation.NetStandard.Opc.Ua.* runtime dependencies per TFM group. Opc.Ua.CodeFixers.csproj switched from IsPackable=false to true with GeneratePackageOnBuild, a _BuildShimAllTfmsBeforePack target that triggers shim builds for every required TFM, and NuspecProperties set inside the GenerateNuspec target so NBGV's computed PackageVersion is captured. Smoke test on a throwaway net10.0 console project resolves the analyzer + shim cleanly and produces only the expected UA00xx warnings, zero errors.

Other: Tools/Opc.Ua.CodeFixers.Shim/Opc.Ua.CodeFixers.Shim.csproj suppresses CS0419 (pre-existing baseline failure on net472/net48/netstandard2.1 from the OpcUaShimAttribute cref to ObsoleteAttribute - ambiguous on TFMs where System.Runtime forwards the type). NugetREADME.md gains the UA0021 row and a one-line RS1038 packaging note (analyzer + code-fix shipped in one assembly is deliberate, suppression documented).

Verification: dotnet build 0 errors; Opc.Ua.CodeFixers.Tests 99/99 passing; Opc.Ua.CodeFixers.Shim.Tests 8 pass + 3 ignored.
Fixes three bugs surfaced by the Phase 10 samples dogfood and adds a new
analyzer rule for the CertificateValidator property rename.

F1 - analyzer auto-load via NuGet props injection.
The hand-authored .nuspec packaged the analyzer at analyzers/dotnet/cs/
but NuGet's restore-time analyzer detection did not register it in the
consumer's project.assets.json. Two coordinated fixes here:
  * Renamed the build props file from OPCFoundation.Opc.Ua.CodeFixers.props
    to OPCFoundation.NetStandard.Opc.Ua.CodeFixers.props so it matches the
    package id exactly. NuGet auto-imports build/<package-id>.props into
    consumer csprojs; the previous mismatched name prevented auto-import.
  * Added an <Analyzer Include="..\analyzers\dotnet\cs\..." /> item to the
    props file as a belt-and-suspenders measure. Once both routes are in
    place the analyzer loads transparently, so consumers no longer need
    the explicit <Analyzer> workaround.

F2 - DataValueObsolete extension(ExtensionObject) typo (Shims/Types/BuiltIn/BuiltInType.cs).
Changed extension(ExtensionObject) to extension(DataValue) so the six
obsolete static helpers (IsGood / IsBad / IsUncertain / IsNotGood /
IsNotBad / IsNotUncertain) bind as DataValue.IsGood(...) again. The bug
was inherited from upstream master at Phase 6.C move time and surfaced
as a CS1929 compile error on the dogfood sample. Adds
DataValueObsoleteShimTests with two regression tests.

F3 - UA0021 namespace heuristic relaxation
(UA0021CertificateValidatorRenameAnalyzer.cs).
Replaced the strict "using Opc.Ua;" check with a HasOpcUaContext helper
that accepts any using directive whose name is Opc.Ua or starts with
Opc.Ua. plus namespace declarations under the same prefix. Real-world
consumer code rarely imports the bare Opc.Ua namespace - sub-namespace
imports like Opc.Ua.Configuration and Opc.Ua.Server are the norm. Three
new tests cover the relaxation.

F5 / UA0022 - ApplicationConfiguration.CertificateValidator and
ServerBase.CertificateValidator removed in 2.0. New analyzer
UA0022CertificateValidatorPropertyRenameAnalyzer with a dual-mode design
that mirrors UA0021: semantic path when the legacy property is still
present as [Obsolete], syntactic fallback when the property has been
fully removed. Code-fix mechanically renames the property identifier to
CertificateManager. Five tests cover both receivers, the negative case,
and the fix. NugetREADME row added and the rule range bumped to
UA0001-UA0022.

Test counts: analyzer suite 99 -> 107; shim suite 8 active -> 10 active.
Combined build 0 errors. Dogfood re-run confirmed the F1 props
injection now auto-loads the analyzer (UA0007 fires on a probe without
manual <Analyzer> wiring).

Out of scope / deferred:
- F6 (bool -> ITelemetryContext parameter swap across multiple ctors) -
  generalizes beyond a single rule; deferred to a future PR.
- Upstream issue for F2 - file separately, not blocking.
- A second-sample dogfood (e.g. ConsoleAggregationServer) - follow-up.
…Server

The 2.0 ctor inserted a required ITelemetryContext parameter BEFORE the
trailing 'bool autoApprove = true' parameter, which silently breaks
1.5.378 callers that wrote:

    new GlobalDiscoverySampleServer(database, request, group, userDb, true)

because the trailing 'true' now binds to ITelemetryContext (compile
error). This was the lone F6-shape error surfaced by the Phase 11
CodeFixers dogfood run against UA-.NETStandard-Samples.

Add an [Obsolete] back-compat overload matching the 1.5.378 signature
exactly, forwarding to the modern ctor with telemetry: null!. Existing
in-tree callers (test fixtures, Aot tests) already use the new
ITelemetryContext signature, so this is purely additive and gives
sample-style code a soft landing.
Previously the single Opc.Ua.CodeFixers.dll bundled both DiagnosticAnalyzer
and CodeFixProvider types and referenced Microsoft.CodeAnalysis 5.3
(.NET SDK 10's csc-internal version). This caused csc.exe to silently
*fail* to load the analyzer: csc.exe ships only Microsoft.CodeAnalysis.dll
+ CSharp.dll in its bincore, not Workspaces. When the analyzer DLL was
JIT-loaded, lazy resolution of Workspaces types from CodeFix code paths
crashed the analyzer host, which swallowed the failure silently. Dogfood
result: 0 UA diagnostics across all 42 sample projects despite the
analyzer being on csc's /analyzer: line.

Fix:

* Split into Opc.Ua.CodeFixers.dll (analyzers only, references Microsoft.
  CodeAnalysis.CSharp only) and Opc.Ua.CodeFixers.CodeFixes.dll (code-fix
  providers, references Microsoft.CodeAnalysis.CSharp.Workspaces). Both
  ship under analyzers/dotnet/cs/; csc.exe loads the first, Workspaces-
  aware hosts (VS, dotnet format) load both.
* Pin Microsoft.CodeAnalysis.CSharp to 4.14.0 via VersionOverride in both
  analyzer projects. The repo-wide 5.3 version is the csc compiler version,
  not a stable analyzer API; analyzers must target the stable 4.x API.
* Share DiagnosticIds/DiagnosticDescriptors + Helpers via <Compile Link=>
  to avoid a NuGet restore cycle (CodeFixes ProjectReferences would loop).
* Hoist UA0008.MethodNameProperty and UA0020.{FormProperty,FormGlobalFactory,
  FormCreate} into a shared internal WellKnownProperties.cs so the analyzer
  + code-fix copies can pull from the same string constants.
* Nuspec ships both DLLs in analyzers/dotnet/cs/.
* Auto-import props (build/OPCFoundation.NetStandard.Opc.Ua.CodeFixers.props)
  references both DLLs as <Analyzer> items.

Verified:

* Pack/restore produces a package with both DLLs.
* /reportAnalyzer in a real consumer build confirms all 19 analyzers
  initialize and execute (UA0001-UA0022).
* All 107 unit tests pass on net10.0.

Also updates .github/agents/opcua-v20-migration.agent.md to put the
CodeFixers NuGet install at the top of the migration workflow, so any
user upgrading from 1.5.378 to 2.0 starts by adding one PackageReference
and lets the analyzer+shim do as much of the work as possible before
falling back to the categorical manual rules.
User-facing rename to better convey purpose: the package was about
"migrating" 1.5.378 callers to 2.0, not about generic Roslyn code fixers.

Directory / project / assembly / package / namespace renames:

  Tools/Opc.Ua.CodeFixers            -> Tools/Opc.Ua.MigrationAnalyzer
  Tools/Opc.Ua.CodeFixers.CodeFixes  -> Tools/Opc.Ua.MigrationAnalyzer.CodeFixes
  Tools/Opc.Ua.CodeFixers.Shim       -> Tools/Opc.Ua.MigrationHelpers
  Tests/Opc.Ua.CodeFixers.Tests      -> Tests/Opc.Ua.MigrationAnalyzer.Tests
  Tests/Opc.Ua.CodeFixers.Shim.Tests -> Tests/Opc.Ua.MigrationHelpers.Tests

  OPCFoundation.NetStandard.Opc.Ua.CodeFixers
                                     -> OPCFoundation.NetStandard.Opc.Ua.MigrationAnalyzer

  Assembly: Opc.Ua.CodeFixers        -> Opc.Ua.MigrationAnalyzer
            Opc.Ua.CodeFixers.CodeFixes -> Opc.Ua.MigrationAnalyzer.CodeFixes
            Opc.Ua.CodeFixers.Shim   -> Opc.Ua.MigrationHelpers

  Namespaces: Opc.Ua.CodeFixers.*    -> Opc.Ua.MigrationAnalyzer.*
              Opc.Ua.CodeFixers.Shim -> Opc.Ua.MigrationHelpers

Solution layout:

  Tools/SourceGeneration.slnx        -> Tools/Roslyn.slnx
  (now hosts both source-generation and migration-analyzer projects, plus
  the CodeFixes project that was missing from the prior slnx.)

Central package pinning:

  Microsoft.CodeAnalysis.CSharp 5.3.0 -> 4.14.0
  Microsoft.CodeAnalysis.CSharp.Workspaces 5.3.0 -> 4.14.0
  Microsoft.CodeAnalysis.Workspaces.Common 5.3.0 -> 4.14.0
  (the analyzer assembly must target the stable 4.x analyzer API; the 5.x
  range was the csc-internal version and unsafe for analyzers. Per-project
  VersionOverride removed since the central pin now covers it.)

Documentation:

  Docs/MigrationGuide.md - one ref updated to new package id.
  .github/agents/opcua-v20-migration.agent.md - all DLL/package/namespace
  references in the agent text updated to the new names.

Test fixes shaken out by the full-slnx build:

* Removed redundant async/await in AnalyzerHarness (RCS1174) - was hidden
  under Roslynator's stricter analyzer that fired post-rename rebuild.
* Polyfilled [GeneratedRegex] partial method in
  OpcUaShimAttributeInventoryTests for net472/net48 - the attribute is
  .NET 7+ only; on legacy TFMs we fall back to a static compiled Regex.

Verified:

* dotnet build UA.slnx -c Debug succeeds (0 errors, 2 expected NU1702
  warnings from the netstandard2.0 analyzer csproj cross-referencing
  the multi-TFM helpers project).
* dotnet test ... MigrationAnalyzer.Tests passes 107/107 on net10.0.
* dotnet test ... MigrationHelpers.Tests passes 10/10 + 3 skipped.
* dotnet pack ... MigrationAnalyzer.csproj produces a package with two
  analyzer DLLs (analyzers/dotnet/cs/) + six TFMs of helpers under lib/.
* Analyzer DLL references only Microsoft.CodeAnalysis 4.14.0 +
  Microsoft.CodeAnalysis.CSharp 4.14.0 (Workspaces-free, csc-safe).
…nalyzer.Core, flatten Shims

Three follow-up renames:

  Tools/Opc.Ua.MigrationAnalyzer.CodeFixes -> Tools/Opc.Ua.MigrationAnalyzer.CodeFixer
  Tools/Opc.Ua.MigrationHelpers            -> Tools/Opc.Ua.MigrationAnalyzer.Core
  Tests/Opc.Ua.MigrationHelpers.Tests      -> Tests/Opc.Ua.MigrationAnalyzer.Core.Tests

Also collapses the nested Shims/ folder in the Core project so the shim
type folders (Client/, Configuration/, Core/, Gds.Client.Common/, Types/)
sit side-by-side with the .csproj at the project root. The old
Shims/Foo/Bar.cs path now lives directly at Foo/Bar.cs. Marker/,
Properties/, readme.md are unchanged.

Assembly / namespace / package id rename in lock-step:

  Opc.Ua.MigrationAnalyzer.CodeFixes (assembly + namespace)
                                   -> Opc.Ua.MigrationAnalyzer.CodeFixer
  Opc.Ua.MigrationHelpers (assembly)
                                   -> Opc.Ua.MigrationAnalyzer.Core
  Namespace inside the Core (shim) project stays Opc.Ua.* / sub-namespaces.
  Package id OPCFoundation.NetStandard.Opc.Ua.MigrationAnalyzer unchanged.

UA.slnx / Tools/Roslyn.slnx project paths updated; analyzer csproj's
nuspec property paths, MSBuild self-invoked targets, and ProjectReferences
all point at the renamed directories / csproj filenames.

Verified:

* dotnet build UA.slnx -c Debug succeeds (0 errors, 572 pre-existing CA2007
  / NU1702 warnings).
* dotnet test ... MigrationAnalyzer.Tests passes 107/107 on net10.0.
* dotnet test ... MigrationAnalyzer.Core.Tests passes 10/10 (+3 skipped).
* dotnet pack ... MigrationAnalyzer.csproj produces the package with
  analyzers/dotnet/cs/{Opc.Ua.MigrationAnalyzer.dll,
  Opc.Ua.MigrationAnalyzer.CodeFixer.dll} + six TFMs of
  Opc.Ua.MigrationAnalyzer.Core.dll under lib/.
@marcschier marcschier changed the title DRAFT ONLY Add Opc.Ua.CodeFixers Roslyn analyzer package for 2.0 solution migration 2.0 Migration Analyzer and codefixer with Agent update Jun 1, 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.

3 participants