Skip to content

Mark ObjC declarations with _callable / _callable_class (#3228) - #3229

Open
xiongjianxu wants to merge 1 commit into
Graphify-Labs:v8from
xiongjianxu:fix/objc-callable-markers
Open

Mark ObjC declarations with _callable / _callable_class (#3228)#3229
xiongjianxu wants to merge 1 commit into
Graphify-Labs:v8from
xiongjianxu:fix/objc-callable-markers

Conversation

@xiongjianxu

Copy link
Copy Markdown

Fixes #3228.

What was wrong

The ObjC extractor builds its nodes by hand and never set _callable or _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).

@interface Greeter : NSObject
- (void)greet;
@end
before                                          after
'Greeter'  _callable=None  _callable_class=None  ->  True / True
'-greet'   _callable=None  _callable_class=None  ->  True / -
'Greeter.h'        (file node, stays unmarked)   ->  unchanged
'NSObject'         (dangling stub, unmarked)     ->  unchanged

The Java equivalent already reports _callable=True, _callable_class=True for the type and _callable=True for the method, so this is extractor parity, not a new concept.

What this changes

extractors/objc.py only:

  • add_node takes an optional tuple of markers to set.
  • @interface and @implementation class nodes: _callable + _callable_class.
  • @protocol nodes: the same, because a protocol is a type declaration and the engine marks a Java/C# interface the same way.
  • Method nodes: _callable.
  • The file node and the stubs ensure_named_node mints for dangling references stay unmarked — a stub has no declaration behind it, which is the property the source_file half 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.py copy them across, extract.py deliberately does not pop them, #2438).

Why it matters

_callable_class + source_file is the standard "real type declaration, not a stub" gate. Consumers today: cross_repo_types.py:42 (the same_type_as pass from #3007), extract.py:4211 and extract.py:4320 (qualified-name declaration indexes), extract.py:6904 (class_nids, the #2137 callback-target exclusion). ObjC was excluded from all of them, and cli.py:3786 / watch.py:1644 carried 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_classes folds 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.py is excluded from both runs for an x86_64/arm64 rpds mismatch. ruff check clean.

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.

…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.

@graphify-labs graphify-labs Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

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.

ObjC declarations carry no _callable / _callable_class markers, so marker-gated passes skip ObjC entirely

1 participant