Skip to content

fix(go): a project package named like a standard-library package is not the standard library - #2368

Open
danusha2345 wants to merge 1 commit into
colbymchenry:mainfrom
danusha2345:fix/go-stdlib-named-project-package
Open

danusha2345 wants to merge 1 commit into
colbymchenry:mainfrom
danusha2345:fix/go-stdlib-named-project-package

Conversation

@danusha2345

@danusha2345 danusha2345 commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Problem

A Go reference written through a package qualifier is dropped as a standard-library reference when the qualifier matches a name in the built-in list (errors, log, types, user, url, token, parser, …). The check looks at the name alone, never at what the file imports under that name, so a project that has its own package with such a name loses nearly every call into it.

// lib/errors/errors.go
package errors

func New(msg string) *Error { … }
// svc/project.go
package svc

import "example.com/app/lib/errors"

func Create(name string) *errors.Error {
	return errors.New(name) // no `calls` edge: taken for the standard library's errors.New
}

The same happens to a composite literal (errors.Error{…}, types.Policy{…}), and to an import whose alias is the standard-library name (errors "example.com/app/lib/errors"). An alias with any other name (liberrors.New(…)) already resolved, as did the type reference in a signature (*errors.Error), which is indexed without its qualifier.

Found on goharbor/harbor, where src/lib/errors and src/lib/log are used by most of the code base: callers of lib/errors New returned 38 call sites, almost all of them inside the package itself, while 467 errors.New(…) calls are written through the project's package.

Fix

The file's import decides before the name does. A qualifier the file imports — by its last path segment, by the name #2410 assumes for an unaliased import (…/log/v2 → log), or by an alias — from a package of one of the project's own modules (the module lookup from #2361) is a project package, and the reference goes on to normal resolution, where the import already selects the package directory. Everything else is unchanged:

  • a file that imports the standard library's errors / log is skipped as before;
  • a qualifier the file does not import at all (a variable named errors, token, url) is skipped as before;
  • a project with no go.mod is unchanged, since no import maps to a project module.

One condition and a small helper in the resolver; no extraction change, so no re-parse is needed beyond the re-index the release already asks for.

Out of scope

  • A variable named like a standard-library package (func flush(ring *ringLog) { ring.Write(b) }) is still skipped. fix(go): type receivers from package-qualified parameters and call results; no guess for outside types #1954 handles that one in the adjacent condition. Rebased onto ed199e6, the two merge with a conflict in CHANGELOG.md only, their test suites pass together, and on harbor the merged tree gives exactly the sum of the two changes (25,538 → 28,537 calls edges).
  • A package-level value of such a package that holds a function (var G = GetLogger, var Is = errors.Is in harbor) has no function node to link to, so log.G(ctx) and errors.Is(…) still get no edge.
  • Since fix(go): an unaliased import is also known by the name goimports assumes for its package #2410 an unaliased import is also known by the name goimports assumes for it, so a project package imported as "example.com/app/lib/log/v2" is recognized as log here with no further change. A test covers it, next to an outside module's …/errors/v2 in the same file, which still gets no edge.

Tests

New __tests__/go-stdlib-named-package.test.ts (a real temp project with a root go.mod, real SQLite):

  • a call through a project errors package and a project log package resolves;
  • a composite literal of a type of such a package resolves;
  • an import alias resolves (liberrors "…/lib/errors"), and so does an alias that is the standard-library name itself, next to the standard library imported as stderrors in the same file;
  • an unaliased import whose path ends in a major version ("example.com/app/lib/log/v2", package log) resolves, and errors.New(…) through an outside module's github.com/acme/errors/v2 in the same file gets no edge;
  • controls: a file of the same package importing the standard library's errors and log gets no edge, and a parameter named errors in a file with no such import does not reach the package's function.

4 of the 7 fail on main at ed199e6 and all pass with the change, with the native kernel and with CODEGRAPH_KERNEL=0. The Go suites (go-*, goframe, rust-go-call-shape, gin-middleware-chain), resolution.test.ts and the neighbouring resolution suites pass in both modes.

Measured on goharbor/harbor

src/ at f25e9da (1,595 Go files, one go.mod), indexed from scratch with main at ed199e6 and with this branch. main indexes it identically run to run.

Go edges main this PR
calls 25,103 27,753
instantiates 6,185 6,352
all kinds (without contains) 45,758 48,575
nodes 38,243 38,243

2,876 edges added (2,676 calls, 200 instantiates), none removed and none retargeted. Every added edge was checked against the source: the line spells <qualifier>.<name>, and the file imports the target's directory under that qualifier.

call sites written through a project package sites with an edge on main with an edge here
errors.* (lib/errors) 1,402 0 1,334
log.* (lib/log) 1,325 1 1,241
types.* (pkg/permission/types, pkg/quota/types) 43 0 43
scanner.*, user.*, token.*, parser.*, url.*, trace.* 33 0 33

errors.New(…) alone: 467 calls through lib/errors, 0 linked before and 459 now; the 277 calls through the standard library's errors have no edge before or after. The 127 sites still without an edge are the package-level function values listed under "Out of scope" (log.G 84, errors.As 20, errors.Is 14), one log.WithField(…) for which lib/log has no package-level function in the index, and 8 package-level var ErrX = errors.New(…) initializers.

Index time is unchanged within noise (five interleaved runs each: 6.6–8.3 s on main, 6.8–7.6 s here). Two Go projects without such packages (100 and 52 Go files) produce byte-identical edge lists.

🤖 Generated with Claude Code

…ot the standard library

A call written through a package qualifier that matches a standard-library
package name (`errors.New`, `log.Infof`, `types.Policy{}`) was dropped as a
standard-library reference on the name alone, without looking at what the
file imports under that name. A project that has its own `lib/errors` or
`lib/log` lost nearly every call into them: on goharbor/harbor, 0 of 1,402
`errors.*` call sites and 1 of 1,325 `log.*` call sites written through the
project's packages had an edge.

The file's import now decides first: a qualifier imported, by its last path
segment or an alias, from a package of one of the project's own modules is a
project package. A file importing the real standard-library package, and a
name the file does not import at all, are handled as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@danusha2345
danusha2345 force-pushed the fix/go-stdlib-named-project-package branch from 14ccd56 to 07b426c Compare October 7, 2026 16:04

This branch has not been deployed

No deployments
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.

1 participant