Skip to content

Report a declared port whose name cannot be bound - #1216

Open
dv-picknik wants to merge 1 commit into
BehaviorTree:masterfrom
PickNikRobotics:fix/declared-port-name-cannot-be-bound
Open

dv-picknik wants to merge 1 commit into
BehaviorTree:masterfrom
PickNikRobotics:fix/declared-port-name-cannot-be-bound

Conversation

@dv-picknik

@dv-picknik dv-picknik commented Sep 29, 2026 •

Copy link
Copy Markdown

Fixes #1215.

A SubTree model can declare a port that nothing is able to bind, and nothing says so.

validatePortName rejects only a leading digit, so <input_port name="_myPort"/> is accepted. IsAllowedPortName requires an alphabetic first character, and createNodeFromXML uses it to classify every instance attribute. So _myPort="{outer}" lands in other_attributes, the remapping is dropped, and the SubTree reads its declared default:

Parent sets outer='FROM_PARENT'. Declared default is DEFAULT_NOT_WIRED.
  myPort  inside SubTree -> FROM_PARENT
  _myPort inside SubTree -> DEFAULT_NOT_WIRED

InputPort("_myPort") already throws "Underscore is reserved", so the C++ API and the XML declaration path disagree about the same name.

This throws when an instance attribute names a port the node declares but cannot bind. Declared ports live in two places, so the check reads manifest->ports for a registered node and subtree_models for a SubTree.

Two ways to fix it

  1. Reject a leading underscore in validatePortName, matching IsAllowedPortName. One line, and it removes the inconsistency at its source. It also stops XML that loads today, since a declaration nobody remaps is inert and generated trees can carry one.
  2. Report only where an instance attribute names a declared port that cannot be bound. Leaves inert declarations alone, but treats the symptom rather than the disagreement between the two rules.

This PR does 2, because it breaks nothing that works today. Say the word and I will switch it to 1.

Behavioral change

One case that loaded before now throws, and it is the case that was losing data. No API or ABI change.

Case Before After
_myPort declared and remapped on the instance loads, remapping lost throws
_my_editor_state on an instance that does not declare it loads loads
_myPort declared and never remapped loads loads

Testing

pixi run build && pixi run test, 533/533. SubTree.DeclaredPortNameThatCannotBeBound fails on master and passes here. The other two pass either way, so a later tightening cannot break them unnoticed.

pre-commit run clean, clang-format included.

For clang-tidy I could not use run_clang_tidy.sh directly, since it wants compile_commands.json under the source tree, and clangd-21 is not available on the machine I built on. I ran its clangd-21 invocation, flags and skip list unchanged, inside an Ubuntu 24.04 container built from the install steps in CONTRIBUTORS_GUIDE.md. Both changed files report All checks completed, 0 errors, and 114 of the 116 swept files under src/ and include/ are clean. The two that are not, bt_parser.h and leaf_node.h, have no compile_commands.json entry, so clangd analyzes them with no flags and cannot resolve their includes. That is unrelated to this change and reproduces on master.

Co-authored by Claude Opus 5

A SubTree model can declare a port that nothing is able to bind, and nothing
says so.

`validatePortName` rejects only a leading digit, so `<input_port
name="_myPort"/>` is accepted. `IsAllowedPortName` requires an alphabetic first
character, and `createNodeFromXML` uses it to classify every instance
attribute, so `_myPort="{outer}"` is diverted into `other_attributes`, the
remapping is dropped, and the SubTree reads its declared default:

  Parent sets outer='FROM_PARENT'. Declared default is DEFAULT_NOT_WIRED.
    myPort  inside SubTree -> FROM_PARENT
    _myPort inside SubTree -> DEFAULT_NOT_WIRED

`InputPort("_myPort")` already throws "Underscore is reserved", so the C++ API
and the XML declaration path disagree about the same name.

Throw when an instance attribute names a port the node declares but cannot
bind. Declared ports live in two places, so the check reads `manifest->ports`
for a registered node and `subtree_models` for a SubTree.

Rejecting the declaration in `validatePortName` would be the smaller change. It
would also break XML that loads today: a port declared and never remapped is
inert, and generated Objectives can carry such a declaration. This fires only
where a remapping is actually being lost, so an inert declaration keeps loading.

This branch has not been deployed

No deployments
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.

A SubTree port declaration whose name cannot be bound loses its remapping silently

1 participant