fix: reject incomplete Objective-C enum macro parses

Validate both complete argument spans and reject unsupported body children instead of emitting partial enum graphs.

AI-Assistance: OpenAI Codex
This commit is contained in:
Alex Tumanov committed 2026-10-13 03:08:22 -05:00
1 parent d6a74da475
commit 7c09b4d360
2 files changed
+40

No files matched your search

+18
View File
@@ -322,6 +322,19 @@ def extract_objc(path: Path) -> dict:
"identifier", "type_identifier"
):
continue
# Valid headers also have grammar ERROR wrappers. Check complete
# argument spans rather than ignoring extra tokens inside them.
underlying_type = outer_types[0]
if any(token.is_missing for token in tokens) or any(
opening.end_byte <= token.start_byte < comma.start_byte
and not (underlying_type.start_byte <= token.start_byte
and token.end_byte <= underlying_type.end_byte)
or comma.end_byte <= token.start_byte < closing.start_byte
and not (names[0].start_byte <= token.start_byte
and token.end_byte <= names[0].end_byte)
for token in tokens
):
continue
following = [token for token in source_leaves
if token.start_byte >= macro.end_byte]
@@ -363,6 +376,11 @@ def extract_objc(path: Path) -> dict:
or body.has_error or body.start_byte != len(prefix)
or body.end_byte != len(prefix) + len(body_bytes)):
continue
# Conditional members need preprocessor handling. Do not emit a
# partial enum when a clean body contains unsupported child nodes.
if any(child.type not in {"{", "}", ",", "comment", "enumerator"}
for child in body.children):
continue
members: list[tuple[str, int]] = []
previous_member_end = None
+22
View File
@@ -210,3 +210,25 @@ def test_objc_macro_enums_preserve_siblings_and_unrelated_parser_errors(tmp_path
("Direction", "North"), ("Direction", "South"),
("Kind", "First"), ("Kind", "Second"), ("Mask", "Enabled"),
}
@pytest.mark.parametrize(
"declaration",
[
"typedef NS_ENUM(NSInteger = 1, Broken) { Bad };",
"typedef NS_ENUM(NSInteger, *Broken) { Bad };",
"typedef NS_ENUM(NSInteger, Broken[2]) { Bad };",
"typedef NS_OPTIONS(NSUInteger, Broken) {\n"
"#ifdef FEATURE\n Bad = 1 << 0,\n#endif\n Other = 1 << 1,\n};",
],
)
def test_objc_macro_enum_unsupported_parts_do_not_emit_partial_enums(tmp_path, declaration):
source = tmp_path / "Partial.h"
source.write_text(
"#import <Foundation/Foundation.h>\n" + declaration + "\n"
"typedef NS_ENUM(NSInteger, Good) { Kept };\n",
encoding="utf-8",
)
result = extract([source], cache_root=tmp_path)
assert {n["label"] for n in result["nodes"]}.isdisjoint({"Broken", "Bad", "Other"})
assert _edge_labels(result, "case_of") == {("Good", "Kept")}