posBaseMap := make(map[*syntax.PosBase]*syntax.File)
for i, p := range noders {
files[i] = p.file
- posBaseMap[p.file.Pos().Base()] = p.file
+ // The file.Pos() is the position of the package clause.
+ // If there's a //line directive before that, file.Pos().Base()
+ // refers to that directive, not the file itself.
+ // Make sure to consistently map back to file base, here and
+ // when we look for a file in the conf.Error handler below,
+ // otherwise the file may not be found (was go.dev/issue/67141).
+ posBaseMap[fileBase(p.file.Pos())] = p.file
}
// typechecking
terr := err.(types2.Error)
msg := terr.Msg
if versionErrorRx.MatchString(msg) {
- posBase := terr.Pos.Base()
- for !posBase.IsFileBase() { // line directive base
- posBase = posBase.Pos().Base()
- }
+ posBase := fileBase(terr.Pos)
fileVersion := info.FileVersions[posBase]
file := posBaseMap[posBase]
- if file.GoVersion == fileVersion {
+ if file == nil {
+ // This should never happen, but be careful and don't crash.
+ } else if file.GoVersion == fileVersion {
// If we have a version error caused by //go:build, report it.
msg = fmt.Sprintf("%s (file declares //go:build %s)", msg, fileVersion)
} else {
return pkg, info
}
+// fileBase returns a file's position base given a position in the file.
+func fileBase(pos syntax.Pos) *syntax.PosBase {
+ base := pos.Base()
+ for !base.IsFileBase() { // line directive base
+ base = base.Pos().Base()
+ }
+ return base
+}
+
// A cycleFinder detects anonymous interface cycles (go.dev/issue/56103).
type cycleFinder struct {
cyclic map[*types2.Interface]bool
--- /dev/null
+// errorcheck -lang=go1.22
+
+//go:build go1.21
+
+// We need a line directive before the package clause,
+// but don't change file name or position so that the
+// error message appears at the right place.
+
+//line issue67141.go:10
+package p
+
+func _() {
+ for range 10 { // ERROR "cannot range over 10"
+ }
+}