rogpeppe / godef

Print where symbols are defined in Go source code
BSD 3-Clause "New" or "Revised" License
666 stars 142 forks source link

Godef fails in dependency module directories where no go.mod is present #124

Open teeters opened 3 years ago

teeters commented 3 years ago

Reproduction steps:

  1. Install go version 1.16+ or export GO111MODULE=on
  2. Go to the module directory of a module that does not include a go.mod file--for example, confluentinc's kafka client library: https://github.com/confluentinc/confluent-kafka-go
  3. Run godef on a function in the library. Observe that it fails to locate the source, even though the source is in the current directory:
    .../kafka$ godef -f consumer_test.go Consumer.CommitMessage
    godef: There must be at least one package that contains the file

Analysis:

godef uses the go env GOMOD command to determine whether modules are enabled. Unfortunately, this command for some reason returns the operating system's null device when no go.mod is present, as documented here. This means that when go.mod is not present, godef will attempt to use the module system but will be unable to find any modules.

Proposed solution:

Obviously there are various ways to work around this, such as setting GO111MODULE=auto. However users may not wish to do this, and it seems strictly better for godef to fall back to non-module mode than to fail. I therefore think this method in adapt.go should be modified to check whether GOMOD is equal to the system's null device:

func detectModuleMode(cfg *packages.Config) bool {
    // first see if the config forces module mode
    for _, e := range cfg.Env {
        switch e {
        case "GO111MODULE=off":
            return false
        case "GO111MODULE=on":
            return true
        }
    }
    // do a fast test for go.mod in the working directory
    if _, err := os.Stat(filepath.Join(cfg.Dir, "go.mod")); !os.IsNotExist(err) {
        return true
    }
    // fall back to invoking the go tool to see if it will pick module mode
    cmd := exec.Command("go", "env", "GOMOD")
    cmd.Env = cfg.Env
    cmd.Dir = cfg.Dir
    out, err := cmd.Output()
    if err == nil {
        return len(strings.TrimSpace(string(out))) > 0
    }
    // default to non module mode
    return false
}
neo532 commented 3 years ago

I meet this,also.