Skip to content

Source generator: compiled companion NodeSet support and base-qualified NodeState emission - #4063

Merged
marcschier merged 6 commits into
masterfrom
marcschier/source-generator-model-fixes
Jul 21, 2026
Merged

Source generator: compiled companion NodeSet support and base-qualified NodeState emission#4063
marcschier merged 6 commits into
masterfrom
marcschier/source-generator-model-fixes

Conversation

@marcschier

@marcschier marcschier commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator

Improves the OPC UA model source generator so it can source-generate compiled companion NodeSet2 models (for example an OPC companion spec such as Robotics/IA, or the OpenUSD binding model), and hardens the emitted NodeState/NodeManager code. The change is confined to the generator (tools/Opc.Ua.SourceGeneration*) and adds no runtime API surface.

Resolving standard types from referenced metadata. A new OpcUaStateTypeIndex collects the simple names of the standard Opc.Ua.*State classes (node-state and method-state) available in the compilation, including referenced assemblies such as Opc.Ua.Core. Wired through ModelCompilation and ModelSourceGenerator, it lets a companion model resolve standard (ns=0) class names from the referenced Core(.Types) metadata instead of inferring them.

Standard method-state fallback (interim, #4060). When a compiled NodeSet instantiates a standard type that carries methods (e.g. a FileDirectoryType Delete instance), the default naming can compute an Opc.Ua.*MethodState name that Core never emits. StandardMethodStateFallback is an ambient, execution-context-scoped policy that degrades such an unresolved global::Opc.Ua.*MethodState reference to the base global::Opc.Ua.MethodState - only when the typed class is neither declared by the current pass nor present in the compilation, keyed on the exact Opc.Ua namespace, and never applied in the Stack pass that builds Core.Types. This is the scoped interim workaround tracked by #4060; the proper upstream fix is a name-resolution step that makes it unnecessary.

Base-qualified attribute writes. The generator now emits base-qualified writes - ((global::Opc.Ua.NodeState)state).X = ... - for the base NodeState members NodeId, SymbolicName, BrowseName, AccessRestrictions, ReleaseStatus, Categories, Specification and Description. When a generated subclass shadows one of these with a strongly-typed new property, an unqualified write would target the shadow rather than the base setter; base-qualifying guarantees it reaches NodeState. For non-shadowing types it is a redundant-but-safe upcast, so existing generated consumers compile unchanged.

Supporting changes. The emitted NodeId/ExpandedNodeId static declarations are now global::-qualified on their namespace prefix to avoid namespace-collision ambiguities, with related adjustments in NodeStateGenerator, ModelDesignExtensions and FluentBuilderGenerator. Opc.Ua.Types additionally exposes internals to Opc.Ua.Core.Encoders.Tests.

Tests and validation. New StandardMethodStateFallbackTests cover the fallback policy, and the NodeState/NodeManager golden-string tests are updated to expect the base-qualified emission. The change set is exercised by the Opc.Ua.SourceGeneration.Core generator test suite on net10.0 and net48, and by regenerating and building Opc.Ua.Core.Types across the full target-framework set (net472 through net10.0); PR CI runs the complete build/test matrix.

marcschier and others added 2 commits July 20, 2026 17:57
Enhancements to the model source generator so that a *compiled* companion
NodeSet2 (e.g. an OPC companion spec such as Robotics/IA, or the OpenUSD
binding model) can be source-generated:

- Add OpcUaStateTypeIndex and wire it through ModelCompilation /
  ModelSourceGenerator so standard (ns=0) node-state/method-state class
  names are resolved from the referenced Core(.Types) metadata.
- Add StandardMethodStateFallback: when a compiled NodeSet instantiates a
  standard type that carries methods (e.g. a FileDirectoryType 'Delete'
  instance), the default naming can compute an 'Opc.Ua.*MethodState' name
  that Core never emits under that name. Degrade an unresolved standard
  'global::Opc.Ua.*MethodState' reference to the base 'global::Opc.Ua.
  MethodState' only when that typed class is neither declared by the
  current pass nor present in the compilation, keyed on the exact Opc.Ua
  namespace and never entered by the Stack pass that builds Core.Types.
  This is the scoped interim workaround tracked by
  #4060 (the proper upstream fix is a
  name-resolution step, which this makes unnecessary once landed).
- NodeStateGenerator / ModelDesignExtensions / FluentBuilderGenerator:
  supporting changes for the above.
- Opc.Ua.Types: expose internals to Opc.Ua.Core.Encoders.Tests.

Validated: generator builds (netstandard2.0), Opc.Ua.Core.Types generates
and builds, and the full Opc.Ua.SourceGeneration.Core test suite passes
(3715 passed / 8 skipped / 0 failed).
… generator

The model source generator now emits base-qualified writes
(((global::Opc.Ua.NodeState)state).X = ...) for the base NodeState members
NodeId, SymbolicName, BrowseName, AccessRestrictions, ReleaseStatus, Categories,
Specification and Description in the generated NodeState/NodeManager factories.
When a generated subclass shadows one of these members with a strongly-typed
'new' property, an unqualified write would target the shadow rather than the
base setter; base-qualifying guarantees it reaches NodeState. For non-shadowing
types it is a redundant-but-safe upcast, so all existing generated consumers
continue to compile unchanged.

Also global::-qualifies the namespace prefix in the emitted NodeId/ExpandedNodeId
static declarations (NodeIdTemplates) to avoid namespace-collision ambiguities.

Updates the affected source-generator golden-string tests to expect the
base-qualified emission.
Copilot AI review requested due to automatic review settings July 20, 2026 17:46
@CLAassistant

CLAassistant commented Jul 20, 2026

Copy link
Copy Markdown

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.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

This PR updates the OPC UA model source generator to emit base-qualified writes to NodeState members (avoiding issues when generated subclasses shadow those members), and to global::-qualify emitted NodeId/ExpandedNodeId declarations to prevent namespace collisions.

Changes:

  • Base-qualify generated assignments to NodeState properties in NodeState/NodeManager factories.
  • global::-qualify namespace prefixes in emitted NodeId/ExpandedNodeId declarations.
  • Update golden-string tests to assert the new emitted code forms.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tools/Opc.Ua.SourceGeneration.Core/Generators/NodeStateTemplates.cs Emits base-qualified writes in templates (factory output).
tools/Opc.Ua.SourceGeneration.Core/Generators/NodeStateGenerator.cs Base-qualifies additional emitted property assignments (ReleaseStatus/Categories/etc.).
tools/Opc.Ua.SourceGeneration.Core/Generators/NodeIdTemplates.cs global::-qualifies namespace prefix usage in emitted declarations.
tests/Opc.Ua.SourceGeneration.Core.Tests/Generators/NodeStateGeneratorTests.cs Updates assertions for base-qualified NodeId/BrowseName writes.
tests/Opc.Ua.SourceGeneration.Core.Tests/Generators/NodeManagerGeneratorTests.cs Updates assertions for base-qualified AccessRestrictions writes.

Comment thread tools/Opc.Ua.SourceGeneration.Core/Generators/NodeStateTemplates.cs Outdated
Comment thread tools/Opc.Ua.SourceGeneration.Core/Generators/NodeStateTemplates.cs Outdated
Comment thread tools/Opc.Ua.SourceGeneration.Core/Generators/NodeStateTemplates.cs Outdated
Comment thread tools/Opc.Ua.SourceGeneration.Core/Generators/NodeStateTemplates.cs Outdated
@marcschier marcschier changed the title Base-qualify generated NodeState attribute writes (model source generator) Source generator: support compiled companion models and safe NodeState writes Jul 20, 2026

@koepalex koepalex left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@codecov

codecov Bot commented Jul 21, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.62416% with 19 lines in your changes missing coverage. Please review.
✅ Project coverage is 73.59%. Comparing base (12f770b) to head (26275ca).
⚠️ Report is 20 commits behind head on master.

Files with missing lines Patch % Lines
...eration.Core/Schema/StandardMethodStateFallback.cs 77.50% 4 Missing and 5 partials ⚠️
...ols/Opc.Ua.SourceGeneration/OpcUaStateTypeIndex.cs 71.42% 2 Missing and 4 partials ⚠️
...neration.Core/Generators/FluentBuilderGenerator.cs 76.92% 2 Missing and 1 partial ⚠️
...rceGeneration.Core/Schema/ModelDesignExtensions.cs 85.71% 0 Missing and 1 partial ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #4063      +/-   ##
==========================================
- Coverage   73.86%   73.59%   -0.28%     
==========================================
  Files        1345     1347       +2     
  Lines      180038   180218     +180     
  Branches    31678    31704      +26     
==========================================
- Hits       132993   132632     -361     
- Misses      36290    36863     +573     
+ Partials    10755    10723      -32     
Files with missing lines Coverage Δ
...ourceGeneration.Core/Generators/NodeIdTemplates.cs 100.00% <100.00%> (ø)
...ceGeneration.Core/Generators/NodeStateGenerator.cs 86.96% <100.00%> (+0.03%) ⬆️
...ceGeneration.Core/Generators/NodeStateTemplates.cs 100.00% <100.00%> (ø)
tools/Opc.Ua.SourceGeneration/ModelCompilation.cs 69.68% <100.00%> (+0.49%) ⬆️
...ls/Opc.Ua.SourceGeneration/ModelSourceGenerator.cs 100.00% <100.00%> (ø)
...rceGeneration.Core/Schema/ModelDesignExtensions.cs 86.34% <85.71%> (-0.02%) ⬇️
...neration.Core/Generators/FluentBuilderGenerator.cs 85.92% <76.92%> (-0.13%) ⬇️
...ols/Opc.Ua.SourceGeneration/OpcUaStateTypeIndex.cs 71.42% <71.42%> (ø)
...eration.Core/Schema/StandardMethodStateFallback.cs 77.50% <77.50%> (ø)

... and 48 files with indirect coverage changes

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

@marcschier
marcschier merged commit 66be673 into master Jul 21, 2026
163 of 164 checks passed
@marcschier
marcschier deleted the marcschier/source-generator-model-fixes branch July 21, 2026 18:48
marcschier added a commit that referenced this pull request Jul 22, 2026
Resolved conflicts:
- Source generator (StandardMethodStateFallback/NodeStateGenerator/ModelSourceGenerator/
  FluentBuilderGenerator/ModelDesignExtensions/ModelCompilation/OpcUaStateTypeIndex): took
  master's #4063 (canonical merged 'compiled companion models' implementation), which
  correctly generates the OpenUSD/Robotics companion models.
- PumpDeviceIntegrationServer: kept the OpenUSD demo wiring (UpdateAlarmActive/representation),
  dropped the device-health feature that master removed; captured the PumpState returned by
  master's MaterialisePumpInstanceAsync instead of the removed m_pump1 field.
- Opc.Ua.Di.Tests.csproj: unioned net472 Compile-Remove entries.
Validated: Di.Tests + both samples + OpenUsd/Robotics libs build clean on net10.0; unit tests green.
@marcschier marcschier changed the title Source generator: support compiled companion models and safe NodeState writes Source generator: compiled companion NodeSet support and base-qualified NodeState emission Jul 22, 2026
marcschier pushed a commit that referenced this pull request Jul 23, 2026
…ntal-dataencodings

Resolves conflicts in the two model source-generator files
(NodeStateGenerator.cs, NodeStateTemplates.cs) in favour of origin/master.
Master's version is the reviewed evolution of this branch's own generator
work (branch commits 842c876 + 0f94bc1, extracted as PR #4063 and
squash-merged as 66be673): it replaces the inline
((global::Opc.Ua.NodeState)state).X = ... cast with a NodeState-typed
'nodeState' local and adds compiled-companion-model support
(OpcUaStateTypeIndex / StandardMethodStateFallback). The branch has no
independent changes to these two files, so taking master's version adopts
the reviewed approach without losing branch-specific work.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready Ready to merge once CI Passes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Model source generator: foreign compiled NodeSet instantiating a standard type's method emits a mismatched Opc.Ua.*MethodState reference (CS0234/CS0246)

6 participants