Skip to content

fix(apex): resolve a qualified heritage clause to its tail type (#3277) - #3278

Open
Soumyadip2003-AI wants to merge 3 commits into
Graphify-Labs:v8from
Soumyadip2003-AI:fix/apex-qualified-heritage-clause
Open

fix(apex): resolve a qualified heritage clause to its tail type (#3277)#3278
Soumyadip2003-AI wants to merge 3 commits into
Graphify-Labs:v8from
Soumyadip2003-AI:fix/apex-qualified-heritage-clause

Conversation

@Soumyadip2003-AI

Copy link
Copy Markdown

Fixes #3277.

Stacked on #3276. Both fixes live in graphify/extractors/apex.py and share the _TYPE pattern that #3276 introduces, so this branch is built on top of it and its commit appears here too. Merging #3276 first reduces this PR to its own single commit; happy to rebase, split, or squash the two into one — whatever suits your queue.

The bug

The class and interface regexes match the heritage clause with patterns that admit no dot:

rf"(?:\s+extends\s+(\w+))?(?:\s+implements\s+([\w,\s]+))?\s*\{{?"

Both stop at the first ., with two silent consequences:

  1. A node is fabricated for the namespace (Database, Outer) and the heritage edge points at it. A namespace is not a type, and the real base type gets no node at all.
  2. Because the capture stops at the dot, it never reaches the comma, so every remaining interface in the list is dropped.

implements Database.Batchable<sObject>, Schedulable is the canonical shape of a scheduled batch job in Salesforce, so this is not an edge case in a real org — and the fabricated Database node collects an implements edge from every batch class in the repo, which is mild hub pollution on top of the lost data.

Repro

public with sharing class Batch implements Database.Batchable<sObject>, Schedulable { }
public class Child extends Outer.BaseThing { }
before:
  Batch.cls  nodes ['Batch', 'Database', ...]   implements -> 'Database'    # Schedulable lost
  Child.cls  nodes ['Child', 'Outer', ...]      extends    -> 'Outer'       # BaseThing never appears

after:
  Batch.cls  nodes ['Batch', 'Batchable', 'Schedulable', ...]
             implements -> 'Batchable', implements -> 'Schedulable'
  Child.cls  extends -> 'BaseThing'

The fix

Both clauses reuse the _TYPE expression, and each is routed through one heritage_names() helper that:

  • splits on top-level commas only, tracking </> depth — so an interface list containing a generic argument (implements Map<String, Object>, Schedulable) is not shredded into Map<String / Object> fragments;
  • drops the generic arguments and takes the tail segment of each entry.

The tail-name choice follows the treatment Kotlin (#1793) and Scala (#1794) received, and the unqualified-tail convention the C++ qualified-base handling already uses, so the edge can bind to a locally declared type of that name. Interface extends goes through the same helper rather than keeping its own comma split.

Verification

  • Extraction of the existing fixtures (tests/fixtures/sample.cls, sample.trigger) is unchanged — same nodes, same 18 / 2 edges.
  • Two tests added in tests/test_languages.py:
    • test_apex_qualified_heritage_uses_tail_type — a qualified extends plus a three-entry implements list containing a comma-bearing generic, asserting the three real bases are linked and that Database, Outer, sObject and Object are not fabricated as nodes.
    • test_apex_interface_qualified_extends_uses_tail_type — the same for an interface's extends list.
  • Full suite: 5301 passed, 12 skipped. The one failure, tests/test_labeling.py::test_label_communities_batches_when_over_batch_size, reproduces identically on a clean checkout of v8 and is unrelated to this change.

🤖 Generated with Claude Code

https://claude.ai/code/session_01UEfivKVYzq1e5E6hJHiuKi

…Labs#3217)

`method_re` matched the return type with `[\w<>\[\]]+`, so any signature whose
return type carried a dot, a comma, or a space never matched and the method was
dropped with no node, no edges, and no warning. That covers the two most common
shapes in real Apex: `Database.QueryLocator` (every `Database.Batchable.start()`)
and `Map<String, Object>` (the `@AuraEnabled` controller convention), plus
nested generics like `List<Map<String, Id>>`.

Widen the return type to a single `_TYPE` pattern that admits a
namespace-qualified name and generic arguments. Whitespace is admitted only
adjacent to a comma, so a statement such as `insert new Account(...)` or
`Integer a = 1, b = compute();` still cannot be read as a declaration -
extraction of the existing fixtures is byte-identical.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UEfivKVYzq1e5E6hJHiuKi

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

Worth a look — the grounded gate found no coupling regressions or blocking issues, but 1 advisory finding(s) below merit a look before merge.

Formal verification. No changes could be formally verified in this run.


Graphify review — findings

Adds namespace-qualified and generic type support to the Apex extractor's heritage and method matching. extract_apex now resolves an extends/implements type to its tail segment (Database.Batchable<sObject>Batchable) via heritage_names, which splits the clause on top-level commas only so generic argument lists like Map<String, Object> stay intact rather than being shredded into fake nodes, and applies the same _TYPE shape to method return types so dotted/generic returns are recognized while statements like insert new Account(...) are no longer misread as method declarations.

Worth a look

  • method regex with permissive _TYPE misreads statements like insert new Account(...) as methodsgraphify/extractors/apex.py:116 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 564 functions depend on the 564 functions this change touches.

Health — this change adds coupling hotspots:

  • new: extract_apex() — 19 callers, 5 callees

Verification — 564 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: 564 function(s) in the blast radius were not formally verified this run

Formal verification

Could not verify: Could not verify extract\_apex.

The verifier did not have enough to check extract\_apex, 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

· 1 more finding(s) on lines outside this diff (see the check run).

Soumyadip2003-AI and others added 2 commits September 2, 2026 12:21
… type

Review follow-up. The first pass admitted whitespace only next to a comma, so a
return type spaced around the angle brackets themselves - all legal Apex - was
still dropped:

    public Map <String, Object> spaceBeforeAngle()   # dropped
    public List< Account > spacesInside()            # dropped
    public List < Map< String, Id > > roomy()        # dropped

Read the type as segments joined by the type punctuators `<`, `>` and `,`, with
whitespace allowed only adjacent to one of them and never between two bare
words. That separator rule is what still bars an ordinary statement from being
read as a declaration, so a bare `<`/`>` used as a comparison
(`if (a > b) { doIt(x); }`, `while (i < list.size()) { next(); }`) fabricates
nothing; the negative test grew those cases plus a generic cast, and extraction
of the existing fixtures is unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UEfivKVYzq1e5E6hJHiuKi
…hify-Labs#3277)

`extends (\w+)` and `implements ([\w,\s]+)` both stop at the first `.`, so a
namespace-qualified base was truncated to its namespace. Two silent
consequences: a node was fabricated for the namespace (`Database`, `Outer`) and
the heritage edge pointed at it, and because the capture never reached the
comma, every remaining interface in the list was dropped -
`implements Database.Batchable<sObject>, Schedulable` lost `Schedulable`
entirely. That shape is the canonical scheduled batch job in Salesforce, and the
fabricated `Database` node collected an edge from every batch class in the repo.

Reuse the `_TYPE` expression for both clauses and route each through a
`heritage_names()` helper that splits on top-level commas only - so a generic
argument list (`Map<String, Object>`) is not shredded into fragments - then
drops generic arguments and takes the tail segment. This is the tail-name
treatment Kotlin (Graphify-Labs#1793) and Scala (Graphify-Labs#1794) already use. Interface `extends`
goes through the same helper. Extraction of the existing fixtures is unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UEfivKVYzq1e5E6hJHiuKi
@Soumyadip2003-AI
Soumyadip2003-AI force-pushed the fix/apex-qualified-heritage-clause branch from a5c8ecb to 07f910d Compare September 2, 2026 06:54
@Soumyadip2003-AI

Copy link
Copy Markdown
Author

Rebased on the updated #3276, which now carries a whitespace-tolerant _TYPE (review follow-up there). Re-verified after the rebase: heritage resolution is unchanged — extends Outer.BaseThing implements Database.Batchable<sObject>, Map<String, Object>, Schedulable still yields BaseThing / Batchable / Map / Schedulable and fabricates no Outer, Database, sObject or Object node. Full suite 5301 passed, 12 skipped, with the same unrelated test_labeling failure that reproduces on a clean v8.

On the advisory finding — insert new Account(...) is not misread as a method declaration. The finding is flagged "NOT verified (no proof, no reproducing execution)", and running it does not reproduce:

public class Claim {
    public void real() {
        insert new Account(Name = 'x');
        insert new List<Account>{ a, b };
        upsert new Contact(LastName = 'y');
        update new Account(Id = i);
    }
}
methods:   ['.real()']
all nodes: ['.real()', 'Claim', 'Claim.cls', 'insert', 'update', 'upsert']

Only .real() is extracted; the insert / update / upsert nodes are the extractor's intended DML nodes, which dml_re has always emitted. That case is also asserted in the suite, in test_apex_statements_are_not_read_as_methods, which pins the whole method set to exactly {".real()"} — so a regression here fails CI rather than needing to be noticed by eye.

The mechanism, since "permissive" is the reasonable prior for a widened regex: _TYPE can only cross whitespace by consuming a <, > or ,, never between two bare words. insert new Account( therefore cannot be spanned — _TYPE stops at insert, the required \s+ then puts the method-name group on new, and \s*\( fails because Account follows instead of (. Backtracking finds no shorter split, since the group must end immediately before the paren. The negative test covers the comparison-operator forms this widening does newly touch (if (a > b) { doIt(x); }, while (i < list.size()) { next(); }, and a generic cast), all of which produce zero method nodes.

Same note as on #3276: the trailing "1 more finding(s) on lines outside this diff" is not present in the check-run output or the review-comment payload, so I cannot read it — happy to address it if it can be surfaced.

🤖 Generated with Claude Code

@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

Fixes Apex heritage and return-type parsing so namespace-qualified and generic types resolve correctly. Heritage clauses now split on top-level commas only and reduce each entry to its tail type name, so implements Database.Batchable<sObject>, Schedulable yields Batchable and Schedulable instead of a spurious Database node and a dropped Schedulable; the shared _TYPE pattern likewise lets method matching accept qualified, generic, array, and whitespace-padded return types (Database.QueryLocator, Map <String, Object>) while still refusing to read statements like insert new Account(...) as declarations.

No blocking issues surfaced. 6 lower-confidence candidates did not survive cross-model review.

Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 564 functions depend on the 564 functions this change touches.

Health — this change adds coupling hotspots:

  • new: extract_apex() — 19 callers, 5 callees

Verification — 564 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: 564 function(s) in the blast radius were not formally verified this run

Formal verification

Could not verify: Could not verify extract\_apex.

The verifier did not have enough to check extract\_apex, 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

· 1 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.

Apex: class heritage clause stops at a dot — implements Database.Batchable<sObject>, Schedulable fabricates a Database node and drops Schedulable

1 participant