Skip to content

Cross-file class relationships are mostly undetected by AST extraction #1356

Description

@JabberYQ

Problem

The AST extractor (tree-sitter) processes files in isolation and does not perform cross-file type inference. This causes a large class of inter-file relationships — arguably the most architecturally important ones — to be absent from the graph.

Root Cause

Tree-sitter is a per-file parser. When analyzing FileA.swift, it can resolve types and method calls only for symbols defined within the same file. Any reference to a type defined in FileB.swift requires cross-file type inference, which tree-sitter does not do.

Concrete Examples (from a real iOS project)

Example 1: View → ViewModel (MVVM core relationship)

Code:

// SessionViewController.swift
class SessionViewController: UIViewController {
    let viewModel = SessionViewModel()   // ← constructor call to another file

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        viewModel.update()               // ← method call to another file
    }
}

// SessionViewModel.swift  
class SessionViewModel: NSObject {
    func update() { ... }
}

Graph result: Zero edges between SessionViewController and SessionViewModel. They sit in different communities (284 vs 34) with no connection.

Why:

  • viewModel.update()var.method() syntax requires inferring viewModel's type (SessionViewModel), which is defined in another file. Tree-sitter cannot resolve this.
  • SessionViewModel() — constructor syntax TypeName() should theoretically be detectable, but property initializers do not generate calls edges the way method-body constructors do.

Example 2: Static method on cross-file class

Code:

// SettingViewController.swift (LaikeMineModule)
let alert = NLKAlertStandardViewController.alertView(
    withTitle: "...",
    isButtonHorizontalLayout: true,
    highlightButtonTitle: "...",
    ...
)

// NLKAlertStandardViewController+Helper.m (LaikeUIKit)
@implementation NLKAlertStandardViewController (Helper)
- (id)alertViewWithTitle:... { ... }

Graph result: No calls edge from SettingViewController to NLKAlertStandardViewController.

Why: TypeName.staticMethod(...) requires resolving TypeName first, then looking up staticMethod on it. NLKAlertStandardViewController is defined in another pod (LaikeUIKit), tree-sitter cannot resolve.

Example 3: Singleton method chain

Code:

// LoginManager.swift
public func logout() {
    NENetworkCenter.shared().cancelAllRequests()       // not captured
    AccountCenter.shared.clearUserStatus()              // not captured
    clearUserCookies()                                  // captured (same file)
}

Graph result: Only clearUserCookies() is captured as a calls edge.

Why: clearUserCookies() uses prefix-less syntax → same-file method → detected. AccountCenter.shared.clearUserStatus() uses Type.method.method() chain → AccountCenter is defined in another file → not resolved.

What IS captured (for contrast)

Call form Example Captured? Why
func() clearUserCookies() Yes Same-file, no type resolution needed
TypeName(args) ASAuthorizationController(...) Yes Constructor syntax is unambiguous
var.method() same-file fortuneCard.configure(...) Yes Both caller and callee in same file
var.method() cross-file viewModel.update() No var type in another file
Type.method() cross-file NLKAlert.alertView(...) No Type defined in another file
Singleton.shared.method() AccountCenter.shared.clearUserStatus() No Singleton type in another file

Impact

This is a significant gap. The most architecturally meaningful edges — MVVM bindings, service dependencies, manager relationships — are predominantly cross-file. In the analyzed project, only 906 calls edges exist vs 3121 references edges, suggesting most cross-file method invocations are downgraded to type references or lost entirely.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions