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>
diff --git a/go/analysis/analysistest/analysistest.go b/go/analysis/analysistest/analysistest.go
index dac75d8..40b2da3 100644
--- a/go/analysis/analysistest/analysistest.go
+++ b/go/analysis/analysistest/analysistest.go
@@ -249,7 +249,8 @@
// 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
diff --git a/gopls/doc/analyzers.md b/gopls/doc/analyzers.md
index 07f846d..f5c83d5 100644
--- a/gopls/doc/analyzers.md
+++ b/gopls/doc/analyzers.md
@@ -108,6 +108,15 @@
**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
diff --git a/internal/lsp/analysis/embeddirective/embeddirective.go b/internal/lsp/analysis/embeddirective/embeddirective.go
new file mode 100644
index 0000000..c925d8e
--- /dev/null
+++ b/internal/lsp/analysis/embeddirective/embeddirective.go
@@ -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."})
+}
diff --git a/internal/lsp/analysis/embeddirective/embeddirective_test.go b/internal/lsp/analysis/embeddirective/embeddirective_test.go
new file mode 100644
index 0000000..1165c0b
--- /dev/null
+++ b/internal/lsp/analysis/embeddirective/embeddirective_test.go
@@ -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...)
+}
diff --git a/internal/lsp/analysis/embeddirective/testdata/src/a/a.go b/internal/lsp/analysis/embeddirective/testdata/src/a/a.go
new file mode 100644
index 0000000..4203f6c
--- /dev/null
+++ b/internal/lsp/analysis/embeddirective/testdata/src/a/a.go
@@ -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)
+}
diff --git a/internal/lsp/analysis/embeddirective/testdata/src/a/b.go b/internal/lsp/analysis/embeddirective/testdata/src/a/b.go
new file mode 100644
index 0000000..c8c701e
--- /dev/null
+++ b/internal/lsp/analysis/embeddirective/testdata/src/a/b.go
@@ -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)
+}
diff --git a/internal/lsp/analysis/embeddirective/testdata/src/a/embedText b/internal/lsp/analysis/embeddirective/testdata/src/a/embedText
new file mode 100644
index 0000000..5e1c309
--- /dev/null
+++ b/internal/lsp/analysis/embeddirective/testdata/src/a/embedText
@@ -0,0 +1 @@
+Hello World
\ No newline at end of file
diff --git a/internal/lsp/source/api_json.go b/internal/lsp/source/api_json.go
index 14140bb..c2c1f82 100755
--- a/internal/lsp/source/api_json.go
+++ b/internal/lsp/source/api_json.go
@@ -269,6 +269,11 @@
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.",
Default: "true",
@@ -805,6 +810,11 @@
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.",
Default: true,
diff --git a/internal/lsp/source/options.go b/internal/lsp/source/options.go
index 123bbe1..de82cb2 100644
--- a/internal/lsp/source/options.go
+++ b/internal/lsp/source/options.go
@@ -49,6 +49,7 @@
"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 @@
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: {