diff --git a/internal/lsp/cache/check.go b/internal/lsp/cache/check.go index 5d637e2d92..97b86da8bf 100644 --- a/internal/lsp/cache/check.go +++ b/internal/lsp/cache/check.go @@ -331,7 +331,6 @@ func typeCheck(ctx context.Context, fset *token.FileSet, m *metadata, mode sourc return nil, ctx.Err() } - // TODO(golang/go#35964): Propagate `go list` errors here when go/packages supports it. for _, e := range rawErrors { srcErr, err := sourceError(ctx, fset, pkg, e) if err != nil { diff --git a/internal/lsp/cache/errors.go b/internal/lsp/cache/errors.go index 2504c33162..0d93ccc33d 100644 --- a/internal/lsp/cache/errors.go +++ b/internal/lsp/cache/errors.go @@ -7,6 +7,8 @@ import ( "go/scanner" "go/token" "go/types" + "regexp" + "strconv" "strings" "golang.org/x/tools/go/analysis" @@ -30,14 +32,18 @@ func sourceError(ctx context.Context, fset *token.FileSet, pkg *pkg, e interface ) switch e := e.(type) { case packages.Error: + kind = toSourceErrorKind(e.Kind) + var ok bool + msg, spn, ok = parseGoListImportCycleError(ctx, fset, e, pkg) + if ok { + break + } + if e.Pos == "" { spn = parseGoListError(e.Msg) } else { spn = span.Parse(e.Pos) } - msg = e.Msg - kind = toSourceErrorKind(e.Kind) - // If the range can't be derived from the parseGoListError function, then we do not have a valid position. if _, err := spanToRange(ctx, pkg, spn); err != nil && e.Pos == "" { return &source.Error{ @@ -45,7 +51,6 @@ func sourceError(ctx context.Context, fset *token.FileSet, pkg *pkg, e interface Kind: kind, }, nil } - case *scanner.Error: msg = e.Msg kind = source.ParseError @@ -249,3 +254,37 @@ func parseGoListError(input string) span.Span { } return span.Parse(input[:msgIndex]) } + +func parseGoListImportCycleError(ctx context.Context, fset *token.FileSet, e packages.Error, pkg *pkg) (string, span.Span, bool) { + re := regexp.MustCompile(`(.*): import stack: \[(.+)\]`) + matches := re.FindStringSubmatch(strings.TrimSpace(e.Msg)) + if len(matches) < 3 { + return e.Msg, span.Span{}, false + } + msg := matches[1] + importList := strings.Split(matches[2], " ") + // Since the error is relative to the current package. The import that is causing + // the import cycle error is the second one in the list. + if len(importList) < 2 { + return msg, span.Span{}, false + } + // Imports have quotation marks around them. + circImp := strconv.Quote(importList[1]) + for _, ph := range pkg.compiledGoFiles { + fh, _, _, err := ph.Parse(ctx) + if err != nil { + continue + } + // Search file imports for the import that is causing the import cycle. + for _, imp := range fh.Imports { + if imp.Path.Value == circImp { + spn, err := span.NewRange(fset, imp.Pos(), imp.End()).Span() + if err != nil { + return msg, span.Span{}, false + } + return msg, spn, true + } + } + } + return msg, span.Span{}, false +} diff --git a/internal/lsp/testdata/circular/b/b.go b/internal/lsp/testdata/circular/b/b.go deleted file mode 100644 index 513db77641..0000000000 --- a/internal/lsp/testdata/circular/b/b.go +++ /dev/null @@ -1,9 +0,0 @@ -package b //@diag("", "go list", "import cycle not allowed: import stack: [golang.org/x/tools/internal/lsp/circular/b golang.org/x/tools/internal/lsp/circular/one golang.org/x/tools/internal/lsp/circular/b]") - -import ( - "golang.org/x/tools/internal/lsp/circular/one" -) - -func Test1() { - one.Test() -} diff --git a/internal/lsp/testdata/circular/double/b/b.go b/internal/lsp/testdata/circular/double/b/b.go new file mode 100644 index 0000000000..dea86f1eda --- /dev/null +++ b/internal/lsp/testdata/circular/double/b/b.go @@ -0,0 +1,5 @@ +package b + +import ( + _ "golang.org/x/tools/internal/lsp/circular/double/one" //@diag("_ \"golang.org/x/tools/internal/lsp/circular/double/one\"", "go list", "import cycle not allowed") +) diff --git a/internal/lsp/testdata/circular/double/one/one.go b/internal/lsp/testdata/circular/double/one/one.go new file mode 100644 index 0000000000..7a5f674393 --- /dev/null +++ b/internal/lsp/testdata/circular/double/one/one.go @@ -0,0 +1,5 @@ +package one + +import ( + _ "golang.org/x/tools/internal/lsp/circular/double/b" +) \ No newline at end of file diff --git a/internal/lsp/testdata/circular/one/one.go b/internal/lsp/testdata/circular/one/one.go deleted file mode 100644 index 8e74e8b0ed..0000000000 --- a/internal/lsp/testdata/circular/one/one.go +++ /dev/null @@ -1,9 +0,0 @@ -package one - -import ( - "golang.org/x/tools/internal/lsp/circular/b" -) - -func Test() { - b.Test1() -} diff --git a/internal/lsp/testdata/circular/self.go b/internal/lsp/testdata/circular/self.go index 05a70e3d46..84676593b0 100644 --- a/internal/lsp/testdata/circular/self.go +++ b/internal/lsp/testdata/circular/self.go @@ -1,12 +1,5 @@ -package circular //@diag("", "go list", "import cycle not allowed: import stack: [golang.org/x/tools/internal/lsp/circular golang.org/x/tools/internal/lsp/circular]") +package circular import ( - "golang.org/x/tools/internal/lsp/circular" + _ "golang.org/x/tools/internal/lsp/circular" //@diag("_ \"golang.org/x/tools/internal/lsp/circular\"", "go list", "import cycle not allowed") ) - -func print() { - Test() -} - -func Test() { -} diff --git a/internal/lsp/testdata/circular/triple/a/a.go b/internal/lsp/testdata/circular/triple/a/a.go new file mode 100644 index 0000000000..48fd0bb5b2 --- /dev/null +++ b/internal/lsp/testdata/circular/triple/a/a.go @@ -0,0 +1,5 @@ +package a + +import ( + _ "golang.org/x/tools/internal/lsp/circular/triple/b" //@diag("_ \"golang.org/x/tools/internal/lsp/circular/triple/b\"", "go list", "import cycle not allowed") +) diff --git a/internal/lsp/testdata/circular/triple/b/b.go b/internal/lsp/testdata/circular/triple/b/b.go new file mode 100644 index 0000000000..955a9c8091 --- /dev/null +++ b/internal/lsp/testdata/circular/triple/b/b.go @@ -0,0 +1,5 @@ +package b + +import ( + _ "golang.org/x/tools/internal/lsp/circular/triple/c" +) diff --git a/internal/lsp/testdata/circular/triple/c/c.go b/internal/lsp/testdata/circular/triple/c/c.go new file mode 100644 index 0000000000..fccd2e43e5 --- /dev/null +++ b/internal/lsp/testdata/circular/triple/c/c.go @@ -0,0 +1,5 @@ +package c + +import ( + _ "golang.org/x/tools/internal/lsp/circular/triple/a" +) diff --git a/internal/lsp/testdata/summary.txt.golden b/internal/lsp/testdata/summary.txt.golden index 14dc1bd1c2..fe545cc4bc 100644 --- a/internal/lsp/testdata/summary.txt.golden +++ b/internal/lsp/testdata/summary.txt.golden @@ -6,7 +6,7 @@ DeepCompletionsCount = 5 FuzzyCompletionsCount = 7 RankedCompletionsCount = 26 CaseSensitiveCompletionsCount = 4 -DiagnosticsCount = 34 +DiagnosticsCount = 35 FoldingRangesCount = 2 FormatCount = 6 ImportCount = 7