Skip to content

Go: a call to a local function parameter is bound to a same-named method in a package the file does not import (false caller edge) #1857

Closed

Description

@PCeltide

Go: a call to a local function parameter is bound to a same-named method in a package the file does not import

Version: 1.6.0 (npm @colbymchenry/codegraph), Linux x64, node 24.18.0. Go repo, 239 files / 5,936 nodes / 20,845 edges.
Checked against main at 2026-09-14: the local-binding guards added for #1735/#1759/#1760 are JS/TS-only (isLocallyBoundJsName), and #1496 / #1545 are receiver-based collisions in TS/PHP, so this Go shape appears unfiled.

$ codegraph callers relogin
Callers of "relogin" (2):
  function RunReloginScheduler   internal/synapse/scheduler.go:36
  method   run                   internal/impulse/socketloop.go:297

The second edge is real. The first cannot exist. relogin is a method on *socketLoop in package impulse (internal/impulse/relogin.go:24); internal/synapse/scheduler.go imports only context, log/slog, time, calendar, clock, push — not impulse. Its relogin is a function parameter:

func RunReloginScheduler(ctx context.Context, clk clock.Clock, cal *calendar.Calendar,
    at calendar.TimeOfDay, owner *sharedpush.SessionOwner,
    relogin func(context.Context) error, log *slog.Logger) error {
    return calendar.RunDailySlot(ctx, clk, cal, at, func(ctx context.Context) error {
        owner.Down(reasonScheduled)
        return relogin(ctx)      // local parameter, not impulse's method
    }, log.With("slot", "re-login"))
}

A call to a local parameter was bound to a same-named method in a package not in scope. Two independent agents reported the edge as real before it was checked by hand — a false edge reads as confirmation, so it is more damaging than a missing one.

Positive controls (unique names, same repo): callers parseIndexEntry → 2/2 matching grep; callers releaseRenewInFlight → 1/1, correctly attributed to the enclosing method.

Suggested fix: for Go, do not resolve a callee name that is bound by a parameter or local declaration in the enclosing function, and require an import path or same-package scope before binding a call site across packages.

Activity

  1. maxmilian commented on Sep 17, 2026

    @maxmilian
    Contributor

    Thanks for the detailed report. I checked this against current main.

    The exact shape in this issue is already fixed on main, but not released yet. #1745 (2c251e2, merged 09-08) added Go's first-letter visibility rule to candidate matching. So an unexported method like impulse.(*socketLoop).relogin can no longer be matched by a call in package synapse. With a minimal repro (a func(context.Context) error parameter named like an unexported method in a package that isn't imported), v1.6.0 reports the false caller and a main build doesn't. It should ship in the next release after v1.6.0.

    Two similar shapes still reproduce on main, and both come down to the same root cause: a Go bare call name(...) can still be bound to a method.

    1. Same package: the parameter or local shares its name with a method declared in the same package, so the visibility rule doesn't filter it out.
    2. Exported name across packages: the parameter is named like an exported method (Relogin) in a package the file doesn't import.

    In both cases the callee has no receiver, so it can't be that method. This is the Go version of what #1714 handled for JS. The fix needs care, though: Go chains like pkg.Factory().Method() can reach the resolver as a bare Method ref (see #1640). So the check has to confirm from the source that there's no receiver, not just go by the ref's shape.

    Fix for both shapes: #1893.

  2. colbymchenry commented on Sep 27, 2026

    @colbymchenry
    Owner

    Thanks for the detailed report, @PCeltide, and thanks for using CodeGraph! This is fixed on main by #1999, which builds on @maxmilian's #1893 (thank you!): a receiver-less Go call such as relogin(ctx) (a function parameter or local func value) no longer links to a same-named method, in the same package or an unimported one; real method calls and pkg.Factory().Method() chains keep their edges. The fix will be in the next release.

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

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions