Fix false-positive MODELGEN010 for mixed NodeSet2 + ModelDesign [NodeManager] binding - #3968
Conversation
|
|
…Manager] (#3937) The model source generator resolves [NodeManager] attributes in two independent passes (NodeSet2 then ModelDesign). Each pass validated the full binding list against only its own models and reported MODELGEN010 for any binding it did not personally match, so a [NodeManager] bound to a NodeSet2 types model was false-flagged as unmatched by the ModelDesign pass (and vice-versa), blocking the build under TreatWarningsAsErrors. Both GenerateCode overloads now accept a caller-owned used-set and a global model count; matches are recorded in the shared set and the unmatched/ambiguity diagnostics are reported once, after both passes, via the new Generators.ReportUnmatchedNodeManagerBindings. This also corrects single-model fallback and ambiguity detection, which used the per-pass model count. Adds e2e and core regression tests, and fixes the MODELGEN010 entry in Docs/ModelDependencies.md plus a mixed-project [NodeManager] / Prefix-Name clarification in Docs/SourceGeneratedNodeManagers.md.
There was a problem hiding this comment.
Pull request overview
Note
Copilot couldn't run its full agentic review because no GitHub Actions runner was available. Make sure your repository has a runner available to run Copilot's review, or add a copilot-setup-steps.yml file specifying one with the runs-on attribute. See the docs for more details.
Adds regression coverage and updates generator binding logic so mixed NodeSet2 (types) + ModelDesign (instances) projects behave consistently regardless of input file order, and so [NodeManager] binding diagnostics are reported correctly across both generation passes.
Changes:
- Introduces a shared
[NodeManager]binding “used” set across NodeSet2 + ModelDesign passes and defers unmatched-binding reporting until both passes complete. - Adds regression tests for reversed
AdditionalFilesorder and for cross-pass[NodeManager]diagnostic behavior (including selector-less ambiguity). - Updates documentation to describe mixed-model
[NodeManager]binding behavior and adjusts diagnostic documentation.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| Tools/Opc.Ua.SourceGeneration/ModelCompilation.cs | Shares binding state across NodeSet2/ModelDesign passes and reports unmatched bindings once. |
| Tools/Opc.Ua.SourceGeneration.Core/Generators.cs | Adds shared binding tracking + centralized unmatched/ambiguity reporting helper. |
| Tests/Opc.Ua.SourceGeneration.Tests/ModelGeneratorTests.cs | Adds regression tests for reversed input ordering + cross-pass binding diagnostics. |
| Tests/Opc.Ua.SourceGeneration.Core.Tests/Generators/NodeManagerBindingReportingTests.cs | New unit tests for aggregated unmatched/ambiguity reporting behavior. |
| Docs/SourceGeneratedNodeManagers.md | Documents mixed-project [NodeManager] binding behavior. |
| Docs/ModelDependencies.md | Updates documented meaning/severity of MODELGEN010 and adds MODELGEN013. |
Comments suppressed due to low confidence (1)
Tools/Opc.Ua.SourceGeneration.Core/Generators.cs:1
- The new optional parameter
sharedUsedBindingsis typed asHashSet<NodeManagerAttributeBinding>, which forces callers to use that concrete collection type. Consider changing it to an interface type likeISet<NodeManagerAttributeBinding>(or at leastICollection<NodeManagerAttributeBinding>) to keep the public API flexible.
/* ========================================================================
- ModelCompilation: precompute a HashSet of NodeSet2 paths (Ordinal) and use Contains instead of Dictionary.ContainsValue, avoiding the O(n^2) scan over m_input; behavior is unchanged. - Generators: reword the unmatched-[NodeManager] diagnostic to "did not match any model" since binding resolution now spans NodeSet2 *and* ModelDesign models, not only ModelDesign. - Tests: resolve the Resources folder from TestContext.CurrentContext.TestDirectory instead of Directory.GetCurrentDirectory() for CI/parallel-run stability, and update the diagnostic-message assertions.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #3968 +/- ##
==========================================
+ Coverage 68.92% 76.28% +7.36%
==========================================
Files 1192 1229 +37
Lines 169560 173563 +4003
Branches 29329 30055 +726
==========================================
+ Hits 116861 132411 +15550
+ Misses 41749 29981 -11768
- Partials 10950 11171 +221
🚀 New features to boost your workflow:
|
Brings in 5 upstream commits: OPCFoundation#3957 (expand DI/Fluent API injectability + one-shot ergonomics across client/server/PubSub/discovery/bindings), OPCFoundation#3968 (fix false-positive MODELGEN010), OPCFoundation#3966 (fix source generator dropping BaseAnalogType EURange/EngineeringUnits), OPCFoundation#3949 (Apache Kafka PubSub transport, Part 14 Annex B.2), and OPCFoundation#3959 (MCP server stdio logs to stderr). Resolved one conflict in Directory.Packages.props (three regions), all from our UaLens-specific package entries interleaving with upstream's new Kafka dependency and a Microsoft.Extensions.* version bump: * Kept CommunityToolkit.Mvvm 8.4.2 and added upstream's new Confluent.Kafka 2.15.0. * Accepted upstream's Microsoft.Extensions.* bump (10.0.8 -> 10.0.9, 10.6.0 -> 10.7.0) while re-inserting our Microsoft.Extensions.Diagnostics.ResourceMonitoring 10.5.0 (a UaLens dependency absent from master) in alphabetical position. * Kept ScottPlot.Avalonia 5.1.58 and took upstream's SharpFuzz 2.2.0 -> 2.3.0 bump. UA.slnx auto-merged and retained our McpServer project path. UaLens build clean (0 warnings / 0 errors). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Description
Combining a NodeSet2 (object types) with a ModelDesign (object instances) in one
source-generated project is a supported layout, but the "types in NodeSet2 + instances in
ModelDesign" scenario from #3937 failed to build. This PR fixes it and locks the behavior in with
tests.
Two independent defects were involved:
TypeDefinitionresolution. An instance whoseTypeDefinitionreferenced aNodeSet2-defined type failed with
MODELGEN003because the NodeSet2 and ModelDesign inputs wereprocessed in separate passes without seeing each other as resolution dependencies (aligned with
the direction in Fix #3937: resolve ModelDesign references to NodeSet2-defined types #3940). Covered here with a reversed-input-order regression test.
MODELGEN010. The generator resolves[NodeManager]bindings in twoindependent passes (NodeSet2, then ModelDesign). Each pass validated the full binding list
against only its own models and reported
MODELGEN010 "did not match any model design"foranything it did not personally match. A
[NodeManager]bound to the NodeSet2 types model wastherefore false-flagged as unmatched by the ModelDesign pass (and vice-versa) which, under
TreatWarningsAsErrors, blocked the build. This matched the reported symptoms exactly: it workedwith only the types file, broke when the instances ModelDesign was added, and built again when the
[NodeManager]was removed.Changes
Fix —
Tools/Opc.Ua.SourceGeneration.Core/Generators.cs,Tools/Opc.Ua.SourceGeneration/ModelCompilation.csGenerateCodeoverloads now accept a caller-owned "used" set and a global model count.Matches are recorded in the shared set and the unmatched / ambiguity diagnostics are reported
once, after both passes, via the new
Generators.ReportUnmatchedNodeManagerBindings.ModelCompilationowns the shared used-set + global model count across both passes and reportsunmatched bindings once, instead of per pass.
model count; a selector-less
[NodeManager]in a multi-model project now yields a single, clear"specify NamespaceUri to disambiguate" diagnostic.
Tests
Tests/Opc.Ua.SourceGeneration.Tests/ModelGeneratorTests.cs— reversedAdditionalFilesorder formixed cross-model generation, plus e2e regressions: a
[NodeManager]bound to the NodeSet2 typesURI, one bound to the ModelDesign instances URI, and a selector-less binding across two models
reporting a single ambiguity.
Tests/Opc.Ua.SourceGeneration.Core.Tests/Generators/NodeManagerBindingReportingTests.cs(new) —unit tests for the shared-set / aggregate-reporting contract.
Docs
Docs/ModelDependencies.md—MODELGEN010was mislabeled ("model skipped" is actuallyMODELGEN013); corrected the entry and addedMODELGEN013.Docs/SourceGeneratedNodeManagers.md— how to bind a[NodeManager]in a mixed project, and aclarification that
ModelSourceGeneratorPrefix/Namerename the generated*State/type classes,not the
[NodeManager]class (whose name/namespace come from the annotated partial).Validation
Tools/Opc.Ua.SourceGeneration.CoreandTools/Opc.Ua.SourceGenerationbuild clean (0 warnings /0 errors).
Opc.Ua.SourceGeneration.Core.Tests: 3708 passed / 0 failed (net10); new tests pass on net48.Opc.Ua.SourceGeneration.Tests: 60 passed / 0 failed (net10); new tests pass on net48.MinimalBoilerServer(a real[NodeManager]+ source-generation consumer) builds clean.Related Issues
[NodeManager]build failure reported in Source-Generated NodeManagers combining model design and Nodeset2 XML #3937.Checklist