Skip to content

Commit bacac14

Browse files
vikblomfindleyr
authored andcommitted
gopls/internal/lsp/source: Add SuggestedFix for embeddirective Analyzer
Give the embeddirective source.Analyzer a Fix that can add missing "embed" imports. Since not all reports from the analyzer is this diagnostic, give source.Analyzer an IsFixable method to filter diagnostics. Updates golang/go#50262 Change-Id: I24abdd2d1a88fc5cabd3197ef438c4da041a6a9c Reviewed-on: https://go-review.googlesource.com/c/tools/+/509056 Run-TryBot: Robert Findley <[email protected]> gopls-CI: kokoro <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Robert Findley <[email protected]> Reviewed-by: Hyang-Ah Hana Kim <[email protected]>
1 parent 38606b3 commit bacac14

File tree

12 files changed

+172
-16
lines changed

12 files changed

+172
-16
lines changed

gopls/internal/lsp/analysis/embeddirective/embeddirective.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
// Use of this source code is governed by a BSD-style
33
// license that can be found in the LICENSE file.
44

5-
// Package embeddirective defines an Analyzer that validates import for //go:embed directive.
5+
// Package embeddirective defines an Analyzer that validates //go:embed directives.
6+
// The analyzer defers fixes to it's parent source.Analyzer.
67
package embeddirective
78

89
import (
@@ -26,6 +27,10 @@ var Analyzer = &analysis.Analyzer{
2627
RunDespiteErrors: true,
2728
}
2829

30+
// source.fixedByImportingEmbed relies on this message to filter
31+
// out fixable diagnostics from this Analyzer.
32+
const MissingImportMessage = `must import "embed" when using go:embed directives`
33+
2934
func run(pass *analysis.Pass) (interface{}, error) {
3035
for _, f := range pass.Files {
3136
comments := embedDirectiveComments(f)
@@ -51,7 +56,7 @@ func run(pass *analysis.Pass) (interface{}, error) {
5156
}
5257

5358
if !hasEmbedImport {
54-
report(`must import "embed" when using go:embed directives`)
59+
report(MissingImportMessage)
5560
}
5661

5762
spec := nextVarSpec(c, f)

gopls/internal/lsp/cache/errors.go

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -339,17 +339,20 @@ func toSourceDiagnostic(srcAnalyzer *source.Analyzer, gobDiag *gobDiagnostic) *s
339339
}
340340

341341
diag := &source.Diagnostic{
342-
URI: gobDiag.Location.URI.SpanURI(),
343-
Range: gobDiag.Location.Range,
344-
Severity: severity,
345-
Code: gobDiag.Code,
346-
CodeHref: gobDiag.CodeHref,
347-
Source: source.AnalyzerErrorKind(gobDiag.Source),
348-
Message: gobDiag.Message,
349-
Related: related,
350-
SuggestedFixes: fixes,
351-
Tags: srcAnalyzer.Tag,
342+
URI: gobDiag.Location.URI.SpanURI(),
343+
Range: gobDiag.Location.Range,
344+
Severity: severity,
345+
Code: gobDiag.Code,
346+
CodeHref: gobDiag.CodeHref,
347+
Source: source.AnalyzerErrorKind(gobDiag.Source),
348+
Message: gobDiag.Message,
349+
Related: related,
350+
Tags: srcAnalyzer.Tag,
351+
}
352+
if srcAnalyzer.FixesDiagnostic(diag) {
353+
diag.SuggestedFixes = fixes
352354
}
355+
353356
// If the fixes only delete code, assume that the diagnostic is reporting dead code.
354357
if onlyDeletions(fixes) {
355358
diag.Tags = append(diag.Tags, protocol.Unnecessary)

gopls/internal/lsp/source/fix.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@ import (
1313

1414
"golang.org/x/tools/go/analysis"
1515
"golang.org/x/tools/gopls/internal/bug"
16+
"golang.org/x/tools/gopls/internal/lsp/analysis/embeddirective"
1617
"golang.org/x/tools/gopls/internal/lsp/analysis/fillstruct"
1718
"golang.org/x/tools/gopls/internal/lsp/analysis/undeclaredname"
1819
"golang.org/x/tools/gopls/internal/lsp/protocol"
1920
"golang.org/x/tools/gopls/internal/span"
21+
"golang.org/x/tools/internal/imports"
2022
)
2123

2224
type (
@@ -41,6 +43,7 @@ const (
4143
ExtractFunction = "extract_function"
4244
ExtractMethod = "extract_method"
4345
InvertIfCondition = "invert_if_condition"
46+
AddEmbedImport = "add_embed_import"
4447
)
4548

4649
// suggestedFixes maps a suggested fix command id to its handler.
@@ -52,6 +55,7 @@ var suggestedFixes = map[string]SuggestedFixFunc{
5255
ExtractMethod: singleFile(extractMethod),
5356
InvertIfCondition: singleFile(invertIfCondition),
5457
StubMethods: stubSuggestedFixFunc,
58+
AddEmbedImport: addEmbedImport,
5559
}
5660

5761
// singleFile calls analyzers that expect inputs for a single file
@@ -138,3 +142,50 @@ func ApplyFix(ctx context.Context, fix string, snapshot Snapshot, fh FileHandle,
138142
}
139143
return edits, nil
140144
}
145+
146+
// fixedByImportingEmbed returns true if diag can be fixed by addEmbedImport.
147+
func fixedByImportingEmbed(diag *Diagnostic) bool {
148+
if diag == nil {
149+
return false
150+
}
151+
return diag.Message == embeddirective.MissingImportMessage
152+
}
153+
154+
// addEmbedImport adds a missing embed "embed" import with blank name.
155+
func addEmbedImport(ctx context.Context, snapshot Snapshot, fh FileHandle, rng protocol.Range) (*token.FileSet, *analysis.SuggestedFix, error) {
156+
pkg, pgf, err := NarrowestPackageForFile(ctx, snapshot, fh.URI())
157+
if err != nil {
158+
return nil, nil, fmt.Errorf("narrow pkg: %w", err)
159+
}
160+
161+
// Like source.AddImport, but with _ as Name and using our pgf.
162+
protoEdits, err := ComputeOneImportFixEdits(snapshot, pgf, &imports.ImportFix{
163+
StmtInfo: imports.ImportInfo{
164+
ImportPath: "embed",
165+
Name: "_",
166+
},
167+
FixType: imports.AddImport,
168+
})
169+
if err != nil {
170+
return nil, nil, fmt.Errorf("compute edits: %w", err)
171+
}
172+
173+
var edits []analysis.TextEdit
174+
for _, e := range protoEdits {
175+
start, end, err := pgf.RangePos(e.Range)
176+
if err != nil {
177+
return nil, nil, fmt.Errorf("map range: %w", err)
178+
}
179+
edits = append(edits, analysis.TextEdit{
180+
Pos: start,
181+
End: end,
182+
NewText: []byte(e.NewText),
183+
})
184+
}
185+
186+
fix := &analysis.SuggestedFix{
187+
Message: "Add embed import",
188+
TextEdits: edits,
189+
}
190+
return pkg.FileSet(), fix, nil
191+
}

gopls/internal/lsp/source/options.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1574,8 +1574,13 @@ func defaultAnalyzers() map[string]*Analyzer {
15741574
unusedparams.Analyzer.Name: {Analyzer: unusedparams.Analyzer, Enabled: false},
15751575
unusedwrite.Analyzer.Name: {Analyzer: unusedwrite.Analyzer, Enabled: false},
15761576
useany.Analyzer.Name: {Analyzer: useany.Analyzer, Enabled: false},
1577-
embeddirective.Analyzer.Name: {Analyzer: embeddirective.Analyzer, Enabled: true},
15781577
timeformat.Analyzer.Name: {Analyzer: timeformat.Analyzer, Enabled: true},
1578+
embeddirective.Analyzer.Name: {
1579+
Analyzer: embeddirective.Analyzer,
1580+
Enabled: true,
1581+
Fix: AddEmbedImport,
1582+
fixesDiagnostic: fixedByImportingEmbed,
1583+
},
15791584

15801585
// gofmt -s suite:
15811586
simplifycompositelit.Analyzer.Name: {

gopls/internal/lsp/source/view.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -879,6 +879,10 @@ type Analyzer struct {
879879
// the analyzer's suggested fixes through a Command, not a TextEdit.
880880
Fix string
881881

882+
// fixesDiagnostic reports if a diagnostic from the analyzer can be fixed by Fix.
883+
// If nil then all diagnostics from the analyzer are assumed to be fixable.
884+
fixesDiagnostic func(*Diagnostic) bool
885+
882886
// ActionKind is the kind of code action this analyzer produces. If
883887
// unspecified the type defaults to quickfix.
884888
ActionKind []protocol.CodeActionKind
@@ -908,6 +912,14 @@ func (a Analyzer) IsEnabled(options *Options) bool {
908912
return a.Enabled
909913
}
910914

915+
// FixesDiagnostic returns true if Analyzer.Fix can fix the Diagnostic.
916+
func (a Analyzer) FixesDiagnostic(d *Diagnostic) bool {
917+
if a.fixesDiagnostic == nil {
918+
return true
919+
}
920+
return a.fixesDiagnostic(d)
921+
}
922+
911923
// Declare explicit types for package paths, names, and IDs to ensure that we
912924
// never use an ID where a path belongs, and vice versa. If we confused these,
913925
// it would result in confusing errors because package IDs often look like
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
text
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright 2023 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 embeddirective
6+
7+
import (
8+
"io"
9+
"os"
10+
)
11+
12+
//go:embed embed.txt //@suggestedfix("//go:embed", "quickfix", "")
13+
var t string
14+
15+
func unused() {
16+
_ = os.Stdin
17+
_ = io.EOF
18+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
-- suggestedfix_fix_import_12_1 --
2+
// Copyright 2023 The Go Authors. All rights reserved.
3+
// Use of this source code is governed by a BSD-style
4+
// license that can be found in the LICENSE file.
5+
6+
package embeddirective
7+
8+
import (
9+
_ "embed"
10+
"io"
11+
"os"
12+
)
13+
14+
//go:embed embed.txt //@suggestedfix("//go:embed", "quickfix", "")
15+
var t string
16+
17+
func unused() {
18+
_ = os.Stdin
19+
_ = io.EOF
20+
}
21+

gopls/internal/lsp/testdata/summary.txt.golden

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ CaseSensitiveCompletionsCount = 4
1111
DiagnosticsCount = 23
1212
FoldingRangesCount = 2
1313
SemanticTokenCount = 3
14-
SuggestedFixCount = 73
14+
SuggestedFixCount = 74
1515
MethodExtractionCount = 8
1616
DefinitionsCount = 46
1717
TypeDefinitionsCount = 18

gopls/internal/lsp/testdata/summary_go1.18.txt.golden

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ CaseSensitiveCompletionsCount = 4
1111
DiagnosticsCount = 23
1212
FoldingRangesCount = 2
1313
SemanticTokenCount = 3
14-
SuggestedFixCount = 79
14+
SuggestedFixCount = 80
1515
MethodExtractionCount = 8
1616
DefinitionsCount = 46
1717
TypeDefinitionsCount = 18

0 commit comments

Comments
 (0)