internal/lsp: add diagnostic on import causing import cycle

After the addition of golang/go#35964, the import cycle error now
has the import stack attached in the message. This CL parses that
stack and attached the import cycle diagnostic to the import versus
just adding it to the first character of the .go file.

Fixes golang/go#33085

Change-Id: I6f5f067c338879b898829951236f816aa63d9dfa
Reviewed-on: https://go-review.googlesource.com/c/tools/+/210942
Run-TryBot: Rohan Challa <rohan@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
This commit is contained in:
Rohan Challa 2019-12-11 14:30:48 -05:00
parent 74e303f875
commit 7ebc6af015
11 changed files with 71 additions and 33 deletions

View File

@ -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 {

View File

@ -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
}

View File

@ -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()
}

View File

@ -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")
)

View File

@ -0,0 +1,5 @@
package one
import (
_ "golang.org/x/tools/internal/lsp/circular/double/b"
)

View File

@ -1,9 +0,0 @@
package one
import (
"golang.org/x/tools/internal/lsp/circular/b"
)
func Test() {
b.Test1()
}

View File

@ -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() {
}

View File

@ -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")
)

View File

@ -0,0 +1,5 @@
package b
import (
_ "golang.org/x/tools/internal/lsp/circular/triple/c"
)

View File

@ -0,0 +1,5 @@
package c
import (
_ "golang.org/x/tools/internal/lsp/circular/triple/a"
)

View File

@ -6,7 +6,7 @@ DeepCompletionsCount = 5
FuzzyCompletionsCount = 7
RankedCompletionsCount = 26
CaseSensitiveCompletionsCount = 4
DiagnosticsCount = 34
DiagnosticsCount = 35
FoldingRangesCount = 2
FormatCount = 6
ImportCount = 7