internal/lsp: add an error result to findFile

This change allows us to return diagnostics in the case of a file that
doesn't exist.

Change-Id: I6275c0dc9103a3f44070919937afe27c64545828
Reviewed-on: https://go-review.googlesource.com/c/tools/+/170009
Reviewed-by: Ian Cottrell <iancottrell@google.com>
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
This commit is contained in:
Rebecca Stambler 2019-03-29 16:14:24 -04:00
parent 0ec5c269d4
commit 24738cbdc1
3 changed files with 36 additions and 20 deletions

View File

@ -150,7 +150,7 @@ func (v *View) link(pkgPath string, pkg *packages.Package, parent *metadata) *me
m.name = pkg.Name m.name = pkg.Name
m.files = pkg.CompiledGoFiles m.files = pkg.CompiledGoFiles
for _, filename := range m.files { for _, filename := range m.files {
if f := v.findFile(span.FileURI(filename)); f != nil { if f, _ := v.findFile(span.FileURI(filename)); f != nil {
f.meta = m f.meta = m
} }
} }
@ -341,7 +341,10 @@ func (v *View) parseFiles(filenames []string) ([]*ast.File, []error) {
} }
// First, check if we have already cached an AST for this file. // First, check if we have already cached an AST for this file.
f := v.findFile(span.FileURI(filename)) f, err := v.findFile(span.FileURI(filename))
if err != nil {
parsed[i], errors[i] = nil, err
}
var fAST *ast.File var fAST *ast.File
if f != nil { if f != nil {
fAST = f.ast fAST = f.ast

View File

@ -191,7 +191,7 @@ func (v *View) remove(pkgPath string) {
// All of the files in the package may also be holding a pointer to the // All of the files in the package may also be holding a pointer to the
// invalidated package. // invalidated package.
for _, filename := range m.files { for _, filename := range m.files {
if f := v.findFile(span.FileURI(filename)); f != nil { if f, _ := v.findFile(span.FileURI(filename)); f != nil {
f.pkg = nil f.pkg = nil
} }
} }
@ -213,7 +213,9 @@ func (v *View) GetFile(ctx context.Context, uri span.URI) (source.File, error) {
// getFile is the unlocked internal implementation of GetFile. // getFile is the unlocked internal implementation of GetFile.
func (v *View) getFile(uri span.URI) (*File, error) { func (v *View) getFile(uri span.URI) (*File, error) {
if f := v.findFile(uri); f != nil { if f, err := v.findFile(uri); err != nil {
return nil, err
} else if f != nil {
return f, nil return f, nil
} }
filename, err := uri.Filename() filename, err := uri.Filename()
@ -228,34 +230,41 @@ func (v *View) getFile(uri span.URI) (*File, error) {
return f, nil return f, nil
} }
func (v *View) findFile(uri span.URI) *File { // findFile checks the cache for any file matching the given uri.
//
// An error is only returned for an irreparable failure, for example, if the
// filename in question does not exist.
func (v *View) findFile(uri span.URI) (*File, error) {
if f := v.filesByURI[uri]; f != nil { if f := v.filesByURI[uri]; f != nil {
// a perfect match // a perfect match
return f return f, nil
} }
// no exact match stored, time to do some real work // no exact match stored, time to do some real work
// check for any files with the same basename // check for any files with the same basename
fname, err := uri.Filename() fname, err := uri.Filename()
if err != nil { if err != nil {
return nil return nil, err
} }
basename := basename(fname) basename := basename(fname)
if candidates := v.filesByBase[basename]; candidates != nil { if candidates := v.filesByBase[basename]; candidates != nil {
pathStat, err := os.Stat(fname) pathStat, err := os.Stat(fname)
if err != nil { if os.IsNotExist(err) {
return nil return nil, err
} else if err != nil {
return nil, nil // the file may exist, return without an error
} }
for _, c := range candidates { for _, c := range candidates {
if cStat, err := os.Stat(c.filename); err == nil { if cStat, err := os.Stat(c.filename); err == nil {
if os.SameFile(pathStat, cStat) { if os.SameFile(pathStat, cStat) {
// same file, map it // same file, map it
v.mapFile(uri, c) v.mapFile(uri, c)
return c return c, nil
} }
} }
} }
} }
return nil // no file with a matching name was found, it wasn't in our cache
return nil, nil
} }
func (v *View) mapFile(uri span.URI, f *File) { func (v *View) mapFile(uri span.URI, f *File) {

View File

@ -54,18 +54,11 @@ const (
func Diagnostics(ctx context.Context, v View, uri span.URI) (map[span.URI][]Diagnostic, error) { func Diagnostics(ctx context.Context, v View, uri span.URI) (map[span.URI][]Diagnostic, error) {
f, err := v.GetFile(ctx, uri) f, err := v.GetFile(ctx, uri)
if err != nil { if err != nil {
return nil, err return singleDiagnostic(uri, "no file found for %s", uri), nil
} }
pkg := f.GetPackage(ctx) pkg := f.GetPackage(ctx)
if pkg == nil { if pkg == nil {
return map[span.URI][]Diagnostic{ return singleDiagnostic(uri, "%s is not part of a package", uri), nil
uri: []Diagnostic{{
Source: "LSP",
Span: span.New(uri, span.Point{}, span.Point{}),
Message: fmt.Sprintf("not part of a package"),
Severity: SeverityError,
}},
}, nil
} }
// Prepare the reports we will send for this package. // Prepare the reports we will send for this package.
reports := make(map[span.URI][]Diagnostic) reports := make(map[span.URI][]Diagnostic)
@ -149,6 +142,17 @@ func Diagnostics(ctx context.Context, v View, uri span.URI) (map[span.URI][]Diag
return reports, nil return reports, nil
} }
func singleDiagnostic(uri span.URI, format string, a ...interface{}) map[span.URI][]Diagnostic {
return map[span.URI][]Diagnostic{
uri: []Diagnostic{{
Source: "LSP",
Span: span.New(uri, span.Point{}, span.Point{}),
Message: fmt.Sprintf(format, a...),
Severity: SeverityError,
}},
}
}
func runAnalyses(ctx context.Context, v View, pkg Package, report func(a *analysis.Analyzer, diag analysis.Diagnostic)) error { func runAnalyses(ctx context.Context, v View, pkg Package, report func(a *analysis.Analyzer, diag analysis.Diagnostic)) error {
// the traditional vet suite: // the traditional vet suite:
analyzers := []*analysis.Analyzer{ analyzers := []*analysis.Analyzer{