internal/lsp: show "do not edit" message when user opens generated file

This is probably a better approach than showing an extra diagnostic,
since a user cannot dismiss a diagnostic.

Fixes golang/go#33397

Change-Id: I92b9a00f51a463673993793abfd4cfb99ce69a91
Reviewed-on: https://go-review.googlesource.com/c/tools/+/188766
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
This commit is contained in:
Rebecca Stambler 2019-08-02 19:45:56 -04:00
parent 88ddfcebc7
commit cae9aa5434
11 changed files with 79 additions and 12 deletions

View File

@ -85,7 +85,7 @@ func parseGo(ctx context.Context, c *cache, fh source.FileHandle, mode source.Pa
defer func() { <-parseLimit }()
parserMode := parser.AllErrors | parser.ParseComments
if mode == source.ParseHeader {
parserMode = parser.ImportsOnly
parserMode = parser.ImportsOnly | parser.ParseComments
}
ast, err := parser.ParseFile(c.fset, fh.Identity().URI.Filename(), buf, parserMode)
if ast != nil {

View File

@ -84,6 +84,13 @@ func (r *runner) Diagnostics(t *testing.T, data tests.Diagnostics) {
t.Fatal(err)
}
got := results[uri]
// A special case to test that there are no diagnostics for a file.
if len(want) == 1 && want[0].Source == "no_diagnostics" {
if len(got) != 0 {
t.Errorf("expected no diagnostics for %s, got %v", uri, got)
}
continue
}
if diff := diffDiagnostics(uri, want, got); diff != "" {
t.Error(diff)
}

View File

@ -108,9 +108,10 @@ type diagnosticSet struct {
listErrors, parseErrors, typeErrors []Diagnostic
}
func diagnostics(ctx context.Context, v View, pkg Package, reports map[span.URI][]Diagnostic) bool {
func diagnostics(ctx context.Context, view View, pkg Package, reports map[span.URI][]Diagnostic) bool {
ctx, done := trace.StartSpan(ctx, "source.diagnostics", telemetry.Package.Of(pkg.ID()))
defer done()
diagSets := make(map[span.URI]*diagnosticSet)
for _, err := range pkg.GetErrors() {
diag := Diagnostic{
@ -129,7 +130,7 @@ func diagnostics(ctx context.Context, v View, pkg Package, reports map[span.URI]
set.parseErrors = append(set.parseErrors, diag)
case packages.TypeError:
if diag.Span.IsPoint() {
diag.Span = pointToSpan(ctx, v, diag.Span)
diag.Span = pointToSpan(ctx, view, diag.Span)
}
set.typeErrors = append(set.typeErrors, diag)
default:

View File

@ -62,6 +62,13 @@ func (r *runner) Diagnostics(t *testing.T, data tests.Diagnostics) {
t.Fatal(err)
}
got := results[uri]
// A special case to test that there are no diagnostics for a file.
if len(want) == 1 && want[0].Source == "no_diagnostics" {
if len(got) != 0 {
t.Errorf("expected no diagnostics for %s, got %v", uri, got)
}
continue
}
if diff := diffDiagnostics(uri, want, got); diff != "" {
t.Error(diff)
}

View File

@ -5,14 +5,49 @@
package source
import (
"context"
"fmt"
"go/ast"
"go/token"
"go/types"
"path/filepath"
"regexp"
"strings"
"golang.org/x/tools/internal/span"
)
func IsGenerated(ctx context.Context, view View, uri span.URI) bool {
f, err := view.GetFile(ctx, uri)
if err != nil {
return false
}
ph := view.Session().Cache().ParseGoHandle(f.Handle(ctx), ParseHeader)
parsed, err := ph.Parse(ctx)
if parsed == nil {
return false
}
tok := view.Session().Cache().FileSet().File(parsed.Pos())
if tok == nil {
return false
}
for _, commentGroup := range parsed.Comments {
for _, comment := range commentGroup.List {
if matched := generatedRx.MatchString(comment.Text); matched {
// Check if comment is at the beginning of the line in source.
if pos := tok.Position(comment.Slash); pos.Column == 1 {
return true
}
}
}
}
return false
}
// Matches cgo generated comment as well as the proposed standard:
// https://golang.org/s/generatedcode
var generatedRx = regexp.MustCompile(`// .*DO NOT EDIT\.?`)
func DetectLanguage(langID, filename string) FileKind {
switch langID {
case "go":

View File

@ -0,0 +1,7 @@
package generated
// Code generated by generator.go. DO NOT EDIT.
func _() {
var y int //@diag("y", "LSP", "y declared but not used")
}

View File

@ -0,0 +1,5 @@
package generated
func _() {
var x int //@diag("x", "LSP", "x declared but not used")
}

View File

@ -1,4 +1,4 @@
package good //@diag("package", "", "")
package good //@diag("package", "no_diagnostics", "")
func stuff() { //@item(good_stuff, "stuff", "func()", "func")
x := 5

View File

@ -1,4 +1,4 @@
package good //@diag("package", "", "")
package good //@diag("package", "no_diagnostics", "")
import (
"golang.org/x/tools/internal/lsp/types" //@item(types_import, "types", "\"golang.org/x/tools/internal/lsp/types\"", "package")

View File

@ -28,7 +28,7 @@ import (
const (
ExpectedCompletionsCount = 144
ExpectedCompletionSnippetCount = 15
ExpectedDiagnosticsCount = 17
ExpectedDiagnosticsCount = 21
ExpectedFormatCount = 6
ExpectedImportCount = 2
ExpectedDefinitionsCount = 38
@ -420,11 +420,6 @@ func (data *Data) collectDiagnostics(spn span.Span, msgSource, msg string) {
if _, ok := data.Diagnostics[spn.URI()]; !ok {
data.Diagnostics[spn.URI()] = []source.Diagnostic{}
}
// If a file has an empty diagnostic message, return. This allows us to
// avoid testing diagnostics in files that may have a lot of them.
if msg == "" {
return
}
severity := source.SeverityError
if strings.Contains(string(spn.URI()), "analyzer") {
severity = source.SeverityWarning

View File

@ -7,6 +7,7 @@ package lsp
import (
"bytes"
"context"
"fmt"
"golang.org/x/tools/internal/jsonrpc2"
"golang.org/x/tools/internal/lsp/protocol"
@ -28,8 +29,17 @@ func (s *Server) didOpen(ctx context.Context, params *protocol.DidOpenTextDocume
// Open the file.
s.session.DidOpen(ctx, uri, fileKind, text)
// Run diagnostics on the newly-changed file.
view := s.session.ViewOf(uri)
// TODO: Ideally, we should be able to specify that a generated file should be opened as read-only.
if source.IsGenerated(ctx, view, uri) {
s.client.ShowMessage(ctx, &protocol.ShowMessageParams{
Message: fmt.Sprintf("Do not edit this file! %s is a generated file.", uri.Filename()),
Type: protocol.Warning,
})
}
// Run diagnostics on the newly-changed file.
go func() {
ctx := view.BackgroundContext()
ctx, done := trace.StartSpan(ctx, "lsp:background-worker")