golang.org/x/tools/gopls@v0.15.3/internal/work/diagnostics.go (about) 1 // Copyright 2022 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package work 6 7 import ( 8 "context" 9 "fmt" 10 "os" 11 "path/filepath" 12 13 "golang.org/x/mod/modfile" 14 "golang.org/x/tools/gopls/internal/cache" 15 "golang.org/x/tools/gopls/internal/file" 16 "golang.org/x/tools/gopls/internal/protocol" 17 "golang.org/x/tools/internal/event" 18 ) 19 20 func Diagnostics(ctx context.Context, snapshot *cache.Snapshot) (map[protocol.DocumentURI][]*cache.Diagnostic, error) { 21 ctx, done := event.Start(ctx, "work.Diagnostics", snapshot.Labels()...) 22 defer done() 23 24 reports := map[protocol.DocumentURI][]*cache.Diagnostic{} 25 uri := snapshot.View().GoWork() 26 if uri == "" { 27 return nil, nil 28 } 29 fh, err := snapshot.ReadFile(ctx, uri) 30 if err != nil { 31 return nil, err 32 } 33 reports[fh.URI()] = []*cache.Diagnostic{} 34 diagnostics, err := diagnoseOne(ctx, snapshot, fh) 35 if err != nil { 36 return nil, err 37 } 38 for _, d := range diagnostics { 39 fh, err := snapshot.ReadFile(ctx, d.URI) 40 if err != nil { 41 return nil, err 42 } 43 reports[fh.URI()] = append(reports[fh.URI()], d) 44 } 45 46 return reports, nil 47 } 48 49 func diagnoseOne(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle) ([]*cache.Diagnostic, error) { 50 pw, err := snapshot.ParseWork(ctx, fh) 51 if err != nil { 52 if pw == nil || len(pw.ParseErrors) == 0 { 53 return nil, err 54 } 55 return pw.ParseErrors, nil 56 } 57 58 // Add diagnostic if a directory does not contain a module. 59 var diagnostics []*cache.Diagnostic 60 for _, use := range pw.File.Use { 61 rng, err := pw.Mapper.OffsetRange(use.Syntax.Start.Byte, use.Syntax.End.Byte) 62 if err != nil { 63 return nil, err 64 } 65 66 modfh, err := snapshot.ReadFile(ctx, modFileURI(pw, use)) 67 if err != nil { 68 return nil, err 69 } 70 if _, err := modfh.Content(); err != nil && os.IsNotExist(err) { 71 diagnostics = append(diagnostics, &cache.Diagnostic{ 72 URI: fh.URI(), 73 Range: rng, 74 Severity: protocol.SeverityError, 75 Source: cache.WorkFileError, 76 Message: fmt.Sprintf("directory %v does not contain a module", use.Path), 77 }) 78 } 79 } 80 return diagnostics, nil 81 } 82 83 func modFileURI(pw *cache.ParsedWorkFile, use *modfile.Use) protocol.DocumentURI { 84 workdir := filepath.Dir(pw.URI.Path()) 85 86 modroot := filepath.FromSlash(use.Path) 87 if !filepath.IsAbs(modroot) { 88 modroot = filepath.Join(workdir, modroot) 89 } 90 91 return protocol.URIFromPath(filepath.Join(modroot, "go.mod")) 92 }