XKNX / XKNX/xknxtoolkit

[Detail Bug] Parser v2: Empty module-def IDs leak into allocator/argument indexes

Open Beginner friendly
#86 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
4
Forks
0
Avg merge
15h 38m
Merged PRs (30d)
37

Description

Detail Bug Report

https://app.detail.dev/org_62aa40f5-2c23-4914-a665-3bb2068af20e/bugs/bug_214c29be-8766-4126-b6be-31f37050e347

Introduced in 12d59c28aed1f86872a3abd2ffc9285eac62c8e5 by @kewde on Jun 28, 2026

Summary

  • Context: ApplicationIndexer builds lookup tables (module_defs, allocators, arg_alloc, parameter_refs, …) from an ApplicationProgram so downstream code in dynamic.py, encode.py, and EvalContext (parser_v2) can do id-keyed lookups by module-def id.
  • Bug: In _index_module_def, the if md.id: guard that skips empty/None-id module defs is applied to module_defs only. The two sibling registrations — allocators and arg_alloc — use md.id as the dict key with no such guard, so an empty-id ModuleDef that carries an allocators or arguments block is registered under the "" key in those dicts even though it was deliberately dropped from module_defs.
  • Actual vs. expected: Expected: empty-id module defs are not registered anywhere (as documented by test_module_def_with_empty_id_is_not_registered). Actual: module_defs omits them, but allocators[""] / arg_alloc[""] are created when the module def has allocators/arguments.
  • Impact: Inconsistent index state and a test gap. The leaked "" entries are currently not reachable by production read paths (allocation is gated by module_defs presence), but this will surface as soon as any code iterates or consumes allocators/arg_alloc by module-def id without re-checking module_defs.

Code with Bug

def _index_module_def(self, md: ModuleDef) -> None:
    if md.id:                                  # guard applied here (line 124)
        self.module_defs[md.id] = md
    ...
    if md.static.allocators is not None:
        self.allocators[md.id] = {             # <-- BUG 🔴 no `md.id` guard; writes `""` key
            a.id: Allocator(id=a.id, start=a.start, max_inclusive=a.max_inclusive)
            for a in md.static.allocators.allocator
        }
    if md.arguments is not None:
        self.arg_alloc[md.id] = {              # <-- BUG 🔴 no `md.id` guard; writes `""` key
            a.id: (a.allocates if a.allocates is not None else 1, a.alignment.value)
            for a in md.arguments.argument
        }
    if md.sub_module_defs is not None:
        for sub in md.sub_module_defs.module_def:
            self._index_module_def(sub)        # same gap reached recursively for empty-id sub-module defs

Explanation

  • Empty module-def IDs are schema-legal, and ApplicationIndexer intentionally skips them via if md.id: when populating module_defs.
  • The same contract is not applied to allocators and arg_alloc, so an empty-id ModuleDef with allocators/arguments populates those dicts under the "" key.
  • This is demonstrable by extending the existing empty-id test to include allocators/arguments: idx.module_defs == {} passes, but idx.allocators and idx.arg_alloc contain "".

Codebase Inconsistency

  • dynamic.py only creates ModuleNode when ref_id is present in idx.module_defs, and encode.py errors on unknown module-def IDs. This gating makes the "" entries dead state today, but it also highlights the intended invariant: allocation data should be keyed only by registered module-def IDs.

Recommended Fix

  • Apply the same if md.id: guard to allocator/argument indexing (or fold all per-md registrations into a single if md.id: block), and extend test_module_def_with_empty_id_is_not_registered to also assert idx.allocators == {} and idx.arg_alloc == {}.

History

This bug was introduced in commit 12d59c28. The commit "feat: add allocator support" extended _index_module_def with two new md.id-keyed registrations (allocators and arg_alloc) but added them outside the pre-existing if md.id: guard. A later commit (1b84e21) reformatted via ruff format without logic changes.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at ApplicationIndexer._index_module_def and the existing test_module_def_with_empty_id_is_not_registered test. Cover empty-id module definitions with allocators and arguments, then run the relevant parser_v2 tests; done means module_defs, allocators, and arg_alloc all omit the empty ID, including recursively indexed sub-module definitions.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
backend
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
90/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.