Skip to content

mavgen_cpp11: fix IndexError when an enum entry is a prefix of the enum name - #1277

Open
azrabano23 wants to merge 1 commit into
ArduPilot:masterfrom
azrabano23:fix-887-cpp11-enum-prefix
Open

mavgen_cpp11: fix IndexError when an enum entry is a prefix of the enum name#1277
azrabano23 wants to merge 1 commit into
ArduPilot:masterfrom
azrabano23:fix-887-cpp11-enum-prefix

Conversation

@azrabano23

Copy link
Copy Markdown

What was wrong

mavgen.py --lang=C++11 crashes when an enum contains an entry whose name is a prefix of the enum name (issue #887):

<enum name="SOME_ENUM_NAME">
  <entry value="0" name="SOME_ENUM"/>
</enum>
$ mavgen.py --lang=C++11 --wire-protocol=2.0 -o out test.xml
...
  File ".../pymavlink/generator/mavgen_cpp11.py", line 304, in enum_remove_prefix
    if pl[i] == sl[0]:
IndexError: list index out of range

The C generator handles the same file fine; only the C++11 output is affected.

Root cause

enum_remove_prefix() builds the scoped enum class entry names by popping one leading component of the entry name for each matching component of the enum name. It never checks that at least one component is left, so for SOME_ENUM in SOME_ENUM_NAME the list runs empty after two pops and the third comparison indexes into an empty list. (Even if the loop ended there, the following sl[0][0].isdigit() would fail the same way.)

The fix

Stop stripping once a single component remains, so the entry keeps its last component: SOME_ENUM becomes SOME_ENUM_NAME::ENUM. This is the guard @shancock884 suggested on the issue.

It only changes behaviour for inputs that previously crashed: an entry that used to strip down to exactly one component takes the same path as before. To confirm, I regenerated C++11 headers for message_definitions/v1.0/all.xml before and after the change; all 389 .hpp files are byte-identical (diff -r).

Verification

New tests/test_mavgen_cpp11.py:

  • test_enum_remove_prefix: existing cases (MAV_CMD_NAV_WAYPOINT -> NAV_WAYPOINT, the digit case MAV_SYS_STATUS_SENSOR_3D_GYRO -> SENSOR_3D_GYRO) plus the prefix cases from the issue.
  • test_cpp11_generator_enum_entry_is_prefix_of_enum_name: runs mavgen.mavgen(..., language="C++11") on a temp XML with the enum above and checks the generated test.hpp contains enum class SOME_ENUM_NAME with ENUM=0 and VALUE=1.

Both fail on master with the IndexError above and pass with this change.

python3 -m pytest tests/test_mavgen_cpp11.py tests/test_mavgen_typescript.py -q   # 3 passed
flake8 generator/mavgen_cpp11.py tests/test_mavgen_cpp11.py --count --select=E9,F63,F7,F82   # 0

Tested with Python 3.12 on macOS.

Fixes #887

🤖 Generated with Claude Code

…um name

The C++11 generator strips the enum name from each entry to build the
scoped `enum class` names. enum_remove_prefix() pops one leading
component of the entry for every matching component of the enum name,
but never checks that something is left. For an entry whose name is a
prefix of the enum name, e.g.

    <enum name="SOME_ENUM_NAME">
      <entry value="0" name="SOME_ENUM"/>
    </enum>

the list of components runs empty and the next comparison raises:

    File "generator/mavgen_cpp11.py", line 304, in enum_remove_prefix
      if pl[i] == sl[0]:
    IndexError: list index out of range

Stop stripping once a single component is left, so the entry keeps its
last component (SOME_ENUM -> ENUM). This only changes behaviour for
inputs that previously crashed: any entry that used to strip down to
exactly one component is unaffected, and regenerating C++11 headers
for message_definitions/v1.0/all.xml produces byte-identical output.

Add tests for enum_remove_prefix() and for end-to-end C++11 generation
from an XML file containing such an enum.

Fixes ArduPilot#887

Signed-off-by: Azra Bano <azrabano.work@gmail.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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.

C++11 generation fails if an enum value is a substring of the enum name

1 participant