From 781a7f92b3c314817cc395cb653bc3855a792e84 Mon Sep 17 00:00:00 2001 From: "wangzekun.zekin" Date: Fri, 8 May 2026 17:07:25 +0800 Subject: [PATCH 1/2] fix(golang): skip out-of-repo local replace targets to avoid nil panic Local `replace => /abs` or `replace => ../outside` targets are not walked by collectGoMods, so they are absent from p.repo.Modules. ParseRepo's old `@`-based skip didn't filter them, causing a nil deref on mod.Dir. Switch ParseRepo's skip to the canonical signal (mod == nil) and have getDeps only record local replace deps whose target lives inside the repo root. Co-Authored-By: Claude Opus 4.7 (1M context) --- lang/golang/parser/parser.go | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/lang/golang/parser/parser.go b/lang/golang/parser/parser.go index 162db2e5..b3f82802 100644 --- a/lang/golang/parser/parser.go +++ b/lang/golang/parser/parser.go @@ -148,7 +148,7 @@ func (p *GoParser) collectGoMods(startDir string) error { p.repo.Modules[name] = newModule(name, rel) p.modules = append(p.modules, newModuleInfo(name, rel, name)) - deps, cgoPkgs, err = getDeps(filepath.Dir(path), p.workDirs) + deps, cgoPkgs, err = getDeps(filepath.Dir(path), p.homePageDir, p.workDirs) if err != nil { return err } @@ -192,7 +192,7 @@ type dep struct { CgoFiles []string `json:"CgoFiles"` } -func getDeps(dir string, workDirs map[string]bool) (a map[string]string, cgoPkgs map[string]bool, err error) { +func getDeps(dir string, homePageDir string, workDirs map[string]bool) (a map[string]string, cgoPkgs map[string]bool, err error) { cgoPkgs = make(map[string]bool) absDir, err := filepath.Abs(dir) if err != nil { @@ -268,9 +268,20 @@ func getDeps(dir string, workDirs map[string]bool) (a map[string]string, cgoPkgs if strings.HasPrefix(module.Replace.Path, "./") || strings.HasPrefix(module.Replace.Path, "../") || strings.HasPrefix(module.Replace.Path, "/") { - // local replace - deps[module.Path] = module.Path + // local replace: only treat as a parseable module when the + // target lives inside the repo root. Out-of-repo replace + // targets cannot be walked by collectGoMods, so skip them + // here to avoid downstream nil-module lookups. + replaceAbs := module.Replace.Path + if !filepath.IsAbs(replaceAbs) { + replaceAbs = filepath.Join(dir, replaceAbs) + } + replaceAbs = filepath.Clean(replaceAbs) + rel, relErr := filepath.Rel(homePageDir, replaceAbs) + if relErr == nil && !strings.HasPrefix(rel, "..") { + deps[module.Path] = module.Path + } } else { deps[module.Path] = module.Replace.Path + "@" + module.Replace.Version } @@ -294,10 +305,12 @@ func getDeps(dir string, workDirs map[string]bool) (a map[string]string, cgoPkgs // ParseRepo parse the entiry repo from homePageDir recursively until end func (p *GoParser) ParseRepo() (Repository, error) { for _, lib := range p.modules { - if strings.Contains(lib.path, "@") { + mod := p.repo.Modules[lib.name] + if mod == nil { + // Not a module of this repo (external dep, or out-of-repo + // local replace target). Skip parsing. continue } - mod := p.repo.Modules[lib.name] if err := p.ParseModule(mod, filepath.Join(p.homePageDir, mod.Dir)); err != nil { return p.getRepo(), err } From fddb93e24812d6119f8f9e90ace683627fb4e13d Mon Sep 17 00:00:00 2001 From: "wangzekun.zekin" Date: Fri, 8 May 2026 20:07:32 +0800 Subject: [PATCH 2/2] fix(golang): keep '@' skip in ParseRepo to avoid re-parsing self-version entries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switching the skip purely to mod==nil broke deduplication: getDeps writes deps[self] = self+'@'+commit for the current module (via getCommitHash), and that self-entry has mod != nil, so it triggered an extra ParseModule pass that re-walked the dir and reset Files entries (clearing Imports / Package on already-parsed files in submodules). Restore the existing strings.Contains(lib.path, "@") skip and add the mod == nil guard after it — that catches out-of-repo local-replace entries (which carry no '@' since they have no version) without breaking the self-version dedup. Co-Authored-By: Claude Opus 4.7 (1M context) --- lang/golang/parser/parser.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lang/golang/parser/parser.go b/lang/golang/parser/parser.go index b3f82802..3f3c0769 100644 --- a/lang/golang/parser/parser.go +++ b/lang/golang/parser/parser.go @@ -305,10 +305,13 @@ func getDeps(dir string, homePageDir string, workDirs map[string]bool) (a map[st // ParseRepo parse the entiry repo from homePageDir recursively until end func (p *GoParser) ParseRepo() (Repository, error) { for _, lib := range p.modules { + if strings.Contains(lib.path, "@") { + continue + } mod := p.repo.Modules[lib.name] if mod == nil { - // Not a module of this repo (external dep, or out-of-repo - // local replace target). Skip parsing. + // Out-of-repo local replace target — collectGoMods didn't + // register it; skip to avoid nil deref. continue } if err := p.ParseModule(mod, filepath.Join(p.homePageDir, mod.Dir)); err != nil {