Mark ObjC declarations with _callable / _callable_class (#3228) - #3229
Mark ObjC declarations with _callable / _callable_class (#3228)#3229xiongjianxu wants to merge 1 commit into
Conversation
…s#3228) Every other extractor stamps its definitions through the generic engine: `_callable` for "a real callable, not a same-named data symbol" (Graphify-Labs#2438), narrowed to `_callable_class` for a type, which is callable only through a constructor (Graphify-Labs#2137). The ObjC extractor builds its nodes by hand and set neither, so an ObjC class was invisible to every pass that indexes declarations by those markers — the `same_type_as` pass from Graphify-Labs#3007, the qualified-name indexes, and anything new that needs to tell a declaration from a reference — and an ObjC method could not be told apart from a data symbol of the same name. `@interface`, `@implementation` and `@protocol` nodes now carry both markers (a protocol is a type declaration, the same way the engine marks a Java or C# interface), and method nodes carry `_callable`. The file node and the stubs minted for dangling references stay unmarked: a stub has no declaration behind it.
There was a problem hiding this comment.
Graphify reviewed this change.
Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).
Formal verification. No changes could be formally verified in this run.
Graphify review — findings
Stamps ObjC declarations with the _callable/_callable_class markers the generic engine puts on every other language, so ObjC classes, implementations, and protocols now carry _callable_class and methods carry _callable — making them visible to passes that index declarations by those markers and distinguishable from same-named data symbols. add_node takes an optional markers tuple and sets each as a node attribute; file nodes and dangling stubs like NSObject stay unmarked.
No blocking issues surfaced. 2 lower-confidence candidates did not survive cross-model review.
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 57 functions depend on the 32 functions this change touches.
Health — this change adds coupling hotspots:
- new:
extract_objc()— 27 callers, 9 callees - new:
walk()— 1 callers, 13 callees
Verification — 57 functions in the blast radius were not formally verified this run (proofs are advisory here).
Gate & verification
graphify gate
PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.
Advisory (not blocking):
- verification_scope: 57 function(s) in the blast radius were not formally verified this run
Formal verification
Could not verify: Could not verify extract\_objc.
The verifier did not have enough to check extract\_objc, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set
· 2 more finding(s) on lines outside this diff (see the check run).
Fixes #3228.
What was wrong
The ObjC extractor builds its nodes by hand and never set
_callableor_callable_class, so no ObjC declaration was visible to any pass that indexes declarations by those markers. Every generic-engine language gets both in one place (extractors/engine.py,callable_def_nids/callable_class_nids).The Java equivalent already reports
_callable=True, _callable_class=Truefor the type and_callable=Truefor the method, so this is extractor parity, not a new concept.What this changes
extractors/objc.pyonly:add_nodetakes an optional tuple of markers to set.@interfaceand@implementationclass nodes:_callable+_callable_class.@protocolnodes: the same, because a protocol is a type declaration and the engine marks a Java/C# interface the same way._callable.ensure_named_nodemints for dangling references stay unmarked — a stub has no declaration behind it, which is the property thesource_filehalf of every declaration gate relies on.No edges, ids, labels or traversal change, and the markers already survive the id-remap and the incremental rebuild path (
cli.py/watch.pycopy them across,extract.pydeliberately does not pop them, #2438).Why it matters
_callable_class+source_fileis the standard "real type declaration, not a stub" gate. Consumers today:cross_repo_types.py:42(thesame_type_aspass from #3007),extract.py:4211andextract.py:4320(qualified-name declaration indexes),extract.py:6904(class_nids, the #2137 callback-target exclusion). ObjC was excluded from all of them, andcli.py:3786/watch.py:1644carried the gap across incremental rebuilds rather than healing it.Tests
tests/test_objc_callable_markers.py— 7 tests: a class from a header, a class from an implementation with no header in the corpus, a header/impl pair after_merge_decl_def_classesfolds it (the markers have to be on whichever node survives), instance and class methods being callable but not types, a protocol, and the two kinds of node that must stay unmarked (the file node and a dangling-reference stub).Full suite before and after: identical failure sets — 408 failed / 4563 passed on
v8, 408 failed / 4570 passed here (+7, the new file). The 408 are pre-existing in my environment, mostly tree-sitter grammars I don't have installed (Python, Ruby, …);test_serve_http.pyis excluded from both runs for an x86_64/arm64rpdsmismatch.ruff checkclean.Not included
The second half of #3228 — ObjC method labels keeping the
+/-selector sigil, where every other extractor emits.greet()— is name normalization rather than extraction, so it is not in this diff. Happy to follow up once you say which side should own it.