Skip to content

[Bug]: C#: calls through a cast, an interface-typed property, ?., or a partial class's other file are dropped without being listed as unresolved #3797

Description

@ThatAblaze

What happened?

When the caller and callee live in different files, several common receiver shapes produce no
calls edge at all, and the call is also missing from the caller's unresolved_calls metadata,
so the gap is invisible. The same shapes do resolve when everything is in one file.

Steps to reproduce

Repro — four files:

`Store.cs`

namespace Demo
{
    public interface IStore { void Save(); }

    public class Store : IStore
    {
        public void Save() { }
    }
}


`Window.cs`

namespace Demo
{
    public class Window
    {
        public void Refresh() { }
        public void SetTarget(object o) { }
    }
}


`Editor.cs`

namespace Demo
{
    public partial class Editor
    {
        object _obj;
        Window _window;
        IStore Store => (IStore)_obj;

        public void Run()
        {
            ((IStore)_obj).Save();   // cast receiver
            Store.Save();            // interface-typed property receiver
            _window?.Refresh();      // conditional access on a typed field
            SetTarget(_obj);         // implicit this; defined in the other partial file
        }
    }
}


`EditorTarget.cs`

namespace Demo
{
    public partial class Editor
    {
        void SetTarget(object o) { }
    }
}

Error output or graph output

Actual: `editor_demo_editor_run` has **no** outgoing `calls` edges and no `unresolved_calls`
entries. (The `IStore.Save --dispatches_to--> Store.Save` edge from #3003 is present, but nothing
reaches `IStore.Save`, so a walk from `Run` never gets there.)

Expected: `Run --calls--> IStore.Save()` (twice collapsing to one), `Run --calls-->
Window.Refresh()`, `Run --calls--> Editor.SetTarget()`; or, at minimum, each dropped call listed
in `unresolved_calls` like other unresolved calls are.

Why it matters: in a codebase whose editor assembly reaches the runtime only through interfaces
(`((IFoo)x).M()` and `Ed.M()` through an `IFoo Ed => (IFoo)_x;` property are the idiom), the main
cross-assembly interface had zero incoming calls. A local post-pass that types these receivers
recovered about 600 call edges on ~120 files, and about 4,000 of the call sites it resolved
matched edges graphify already had.

Graphify version

No response

Environment

graphify 0.9.67 (pip graphifyy), Python 3.13.1, Windows 10 (10.0.19045)

Additional context

No response

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions