[patch] Read an input pin's default from its C# initializer - #442
Merged
Merged
Conversation
PinDefinition.DefaultValue was populated only from an explicit [InputPin(DefaultValue = ...)]. Every default in the shipped node library is written as a property initializer instead, so GetAllNodeDefinitions() reported null for all of them and any menu or inspector built from the definitions had to invent its own, which would not match what the node holds when it runs. ScanPins now falls back to reading the member off a prototype instance of the declaring type, which is the value the initializer wrote. The attribute still wins where it supplied one, matching what parameter and constructor-parameter pins already did. The prototype is built lazily, once per type, and a type that cannot be constructed without arguments or whose constructor throws simply reports no default, as before. Fixes #439 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P6bYWX7oFj1YTAAw9M516w
…cannot fire SonarCloud's quality gate failed the branch at 68.2% coverage on new code against an 80% floor. Collecting cobertura locally named the gaps exactly: the TargetInvocationException arm in ReadDeclaredDefault, the !constructible early return in CreatePrototype, and both of the extra catch arms. Two of those were dead rather than untested. CreatePrototype's guard turns away abstract and open-generic types and reference types with no public parameterless constructor before Activator.CreateInstance is reached, which is where MissingMethodException and MemberAccessException would have come from, and NotSupportedException only arises for types that cannot carry a [Node] attribute in the first place. Removing the two catches leaves the one case the guard lets through: a constructor that runs and throws. The reasoning is now in a remarks block rather than implied. The other two were real paths with no test. The cannot-construct test used ConstructedNode, whose pins are all outputs, so it never forced the prototype at all and its assertion was vacuous; it now uses a node with an input pin and a constructor argument, which reaches the early return. TouchyGetterNode covers the throwing getter. New-code coverage in the file is 26/26 lines measured, 0 uncovered. 103/103 tests pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P6bYWX7oFj1YTAAw9M516w
|
matt-edmondson
deleted the
claude/nodeeditor-439-initializer-defaults
branch
September 23, 2026 03:39
This was referenced Sep 23, 2026
This was referenced Oct 1, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Fixes #439
What was wrong
PinDefinition.DefaultValuewas populated only from an explicit[InputPin(DefaultValue = ...)], never from the property's C# initializer. The initializer form is whatNodeGraph/Libraryuses throughout —RandomNodedeclaresMin { get; set; } = 0.0,CounterNodedeclaresIncrementBy { get; set; } = 1— soGetAllNodeDefinitions()reportednullfor every default in the shipped library, and a menu or inspector built from it had to invent values that would not match what the node holds when it runs.Method parameters and constructor parameters already fell back to
parameter.DefaultValue. Properties and fields had no equivalent.The change
ScanPinsnow builds a prototype instance of the declaring type and reads the member off it, which is what the initializer wrote. It is used only where the attribute supplied nothing, so an explicitDefaultValuestill wins — the same precedenceAddParameterPinsalready applies.The prototype is a
Lazy<object?>, so it is constructed at most once per type and only when a pin actually needs it. A type that is abstract, open-generic, or has no parameterless constructor is not constructed at all, and a constructor that throws is caught: registration is metadata only, so those cases report no default exactly as before.One consequence worth naming: a value-type pin with neither an initializer nor an attribute now reports its zero rather than
null. That is what the node will actually hold once constructed, which is the point of the field.Tests
Four tests in
AttributeBasedNodeFactoryTests, against a newInitializedDefaultsNodefixture that mirrors how the library writes its defaults:ReadsAnInputDefaultFromItsPropertyInitializer— property and field initializers both come throughPrefersTheAttributeDefaultOverTheInitializer— precedenceReportsTheZeroForAnUninitializedValueTypePin— pins the consequence aboveSurvivesATypeItCannotConstruct— a throwing constructor and a type with only a parameterised constructor both still registerThe first and third fail on
main(expected: 128, actual: nullandexpected: 0, actual: null); verified by reverting the production change and re-running before finalising.tests/ImGui.NodeEditor.Tests102/102 andtests/NodeGraph.Tests106/106 pass on this branch, on .NET 10.0.401 / Linux.🤖 Generated with Claude Code
https://claude.ai/code/session_01P6bYWX7oFj1YTAAw9M516w
Generated by Claude Code