diff --git a/go/analysis/analysistest/analysistest.go b/go/analysis/analysistest/analysistest.go index dac75d8a6c..40b2da3190 100644 --- a/go/analysis/analysistest/analysistest.go +++ b/go/analysis/analysistest/analysistest.go @@ -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 diff --git a/gopls/doc/analyzers.md b/gopls/doc/analyzers.md index 07f846db84..f5c83d5771 100644 --- a/gopls/doc/analyzers.md +++ b/gopls/doc/analyzers.md @@ -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 diff --git a/internal/lsp/analysis/embeddirective/embeddirective.go b/internal/lsp/analysis/embeddirective/embeddirective.go new file mode 100644 index 0000000000..c925d8ee32 --- /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 0000000000..1165c0bf6e --- /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 0000000000..4203f6ce24 --- /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 0000000000..c8c701e663 --- /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 0000000000..5e1c309dae --- /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 14140bb630..c2c1f82426 100755 --- a/internal/lsp/source/api_json.go +++ b/internal/lsp/source/api_json.go @@ -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.", diff --git a/internal/lsp/source/options.go b/internal/lsp/source/options.go index 123bbe120c..de82cb2fe7 100644 --- a/internal/lsp/source/options.go +++ b/internal/lsp/source/options.go @@ -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: {