internal/lsp/analysis: analyzer for //go:embed directive

This CL adds a new analyzer for //go:embed directive, which
checks for the "embed" import.

Along with it, it improves doc for analysistest.Run for
comments of the the form  "//...// want..." or
 "//...// want..."

 Updates #50262

Change-Id: I60ef0ab740feadd4fff3a4d758123b27ceda0bc6
Reviewed-on: https://go-review.googlesource.com/c/tools/+/400854
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Peter Weinberger <pjw@google.com>
Reviewed-by: Robert Findley <rfindley@google.com>
Run-TryBot: Nooras Saba‎ <saba@golang.org>
gopls-CI: kokoro <noreply+kokoro@google.com>
This commit is contained in:
Nooras Saba 2022-04-18 18:17:15 -04:00 committed by Nooras Saba‎
parent 033cbfc76d
commit 28c754d415
9 changed files with 131 additions and 1 deletions

View File

@ -249,7 +249,8 @@ func RunWithSuggestedFixes(t Testing, dir string, a *analysis.Analyzer, patterns
// directory using golang.org/x/tools/go/packages, runs the analysis on
// them, and checks that each analysis emits the expected diagnostics
// and facts specified by the contents of '// want ...' comments in the
// package's source files.
// package's source files. It treats a comment of the form
// "//...// want..." or "/*...// want... */" as if it starts at 'want'
//
// An expectation of a Diagnostic is specified by a string literal
// containing a regular expression that must match the diagnostic

View File

@ -108,6 +108,15 @@ errors is discouraged.
**Enabled by default.**
## **embed**
check for //go:embed directive import
This analyzer checks that the embed package is imported when source code contains //go:embed comment directives.
The embed package must be imported for //go:embed directives to function.import _ "embed".
**Enabled by default.**
## **errorsas**
report passing non-pointer or non-error values to errors.As

View File

@ -0,0 +1,58 @@
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package embeddirective defines an Analyzer that validates import for //go:embed directive.
package embeddirective
import (
"go/ast"
"strings"
"golang.org/x/tools/go/analysis"
)
const Doc = `check for //go:embed directive import
This analyzer checks that the embed package is imported when source code contains //go:embed comment directives.
The embed package must be imported for //go:embed directives to function.import _ "embed".`
var Analyzer = &analysis.Analyzer{
Name: "embed",
Doc: Doc,
Requires: []*analysis.Analyzer{},
Run: run,
RunDespiteErrors: true,
}
func run(pass *analysis.Pass) (interface{}, error) {
for _, f := range pass.Files {
com := hasEmbedDirectiveComment(f)
if com != nil {
assertEmbedImport(pass, com, f)
}
}
return nil, nil
}
// Check if comment contains //go:embed directive.
func hasEmbedDirectiveComment(f *ast.File) *ast.Comment {
for _, cg := range f.Comments {
for _, c := range cg.List {
if strings.HasPrefix(c.Text, "//go:embed ") {
return c
}
}
}
return nil
}
// Verifies that "embed" import exists for //go:embed directive.
func assertEmbedImport(pass *analysis.Pass, com *ast.Comment, f *ast.File) {
for _, imp := range f.Imports {
if "\"embed\"" == imp.Path.Value {
return
}
}
pass.Report(analysis.Diagnostic{Pos: com.Pos(), End: com.Pos() + 10, Message: "The \"embed\" package must be imported when using go:embed directives."})
}

View File

@ -0,0 +1,22 @@
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package embeddirective
import (
"testing"
"golang.org/x/tools/go/analysis/analysistest"
"golang.org/x/tools/internal/typeparams"
)
func Test(t *testing.T) {
testdata := analysistest.TestData()
tests := []string{"a"}
if typeparams.Enabled {
tests = append(tests)
}
analysistest.RunWithSuggestedFixes(t, testdata, Analyzer, tests...)
}

View File

@ -0,0 +1,13 @@
package a
import (
"fmt"
)
//go:embed embedText // want "The \"embed\" package must be imported when using go:embed directives"
var s string
// This is main function
func main() {
fmt.Println(s)
}

View File

@ -0,0 +1,14 @@
package a
import (
_ "embed"
"fmt"
)
//go:embed embedText // ok
var s string
// This is main function
func main() {
fmt.Println(s)
}

View File

@ -0,0 +1 @@
Hello World

View File

@ -268,6 +268,11 @@ var GeneratedAPIJSON = &APIJSON{
Doc: "check for calls of reflect.DeepEqual on error values\n\nThe deepequalerrors checker looks for calls of the form:\n\n reflect.DeepEqual(err1, err2)\n\nwhere err1 and err2 are errors. Using reflect.DeepEqual to compare\nerrors is discouraged.",
Default: "true",
},
{
Name: "\"embed\"",
Doc: "check for //go:embed directive import\n\nThis analyzer checks that the embed package is imported when source code contains //go:embed comment directives.\nThe embed package must be imported for //go:embed directives to function.import _ \"embed\".",
Default: "true",
},
{
Name: "\"errorsas\"",
Doc: "report passing non-pointer or non-error values to errors.As\n\nThe errorsas analysis reports calls to errors.As where the type\nof the second argument is not a pointer to a type implementing error.",
@ -804,6 +809,11 @@ var GeneratedAPIJSON = &APIJSON{
Doc: "check for calls of reflect.DeepEqual on error values\n\nThe deepequalerrors checker looks for calls of the form:\n\n reflect.DeepEqual(err1, err2)\n\nwhere err1 and err2 are errors. Using reflect.DeepEqual to compare\nerrors is discouraged.",
Default: true,
},
{
Name: "embed",
Doc: "check for //go:embed directive import\n\nThis analyzer checks that the embed package is imported when source code contains //go:embed comment directives.\nThe embed package must be imported for //go:embed directives to function.import _ \"embed\".",
Default: true,
},
{
Name: "errorsas",
Doc: "report passing non-pointer or non-error values to errors.As\n\nThe errorsas analysis reports calls to errors.As where the type\nof the second argument is not a pointer to a type implementing error.",

View File

@ -49,6 +49,7 @@ import (
"golang.org/x/tools/go/analysis/passes/unusedresult"
"golang.org/x/tools/go/analysis/passes/unusedwrite"
"golang.org/x/tools/go/packages"
"golang.org/x/tools/internal/lsp/analysis/embeddirective"
"golang.org/x/tools/internal/lsp/analysis/fillreturns"
"golang.org/x/tools/internal/lsp/analysis/fillstruct"
"golang.org/x/tools/internal/lsp/analysis/infertypeargs"
@ -1308,6 +1309,7 @@ func defaultAnalyzers() map[string]*Analyzer {
unusedwrite.Analyzer.Name: {Analyzer: unusedwrite.Analyzer, Enabled: false},
useany.Analyzer.Name: {Analyzer: useany.Analyzer, Enabled: false},
infertypeargs.Analyzer.Name: {Analyzer: infertypeargs.Analyzer, Enabled: true},
embeddirective.Analyzer.Name: {Analyzer: embeddirective.Analyzer, Enabled: true},
// gofmt -s suite:
simplifycompositelit.Analyzer.Name: {