@@ -17,6 +17,7 @@ import (
17
17
18
18
"golang.org/x/tools/go/analysis"
19
19
"golang.org/x/tools/go/ast/astutil"
20
+ "golang.org/x/tools/gopls/internal/util/safetoken"
20
21
)
21
22
22
23
const Doc = `check for unused variables and suggest fixes`
@@ -37,7 +38,7 @@ var unusedVariableRegexp = []*regexp.Regexp{
37
38
regexp .MustCompile ("^declared and not used: (.*)$" ), // Go 1.23+
38
39
}
39
40
40
- func run (pass * analysis.Pass ) (interface {} , error ) {
41
+ func run (pass * analysis.Pass ) (any , error ) {
41
42
for _ , typeErr := range pass .TypeErrors {
42
43
for _ , re := range unusedVariableRegexp {
43
44
match := re .FindStringSubmatch (typeErr .Msg )
@@ -113,7 +114,7 @@ func runForError(pass *analysis.Pass, err types.Error, name string) error {
113
114
continue
114
115
}
115
116
116
- fixes := removeVariableFromAssignment (path , stmt , ident )
117
+ fixes := removeVariableFromAssignment (pass . Fset , path , stmt , ident )
117
118
// fixes may be nil
118
119
if len (fixes ) > 0 {
119
120
diag .SuggestedFixes = fixes
@@ -164,7 +165,7 @@ func removeVariableFromSpec(pass *analysis.Pass, path []ast.Node, stmt *ast.Valu
164
165
// Find parent DeclStmt and delete it
165
166
for _ , node := range path {
166
167
if declStmt , ok := node .(* ast.DeclStmt ); ok {
167
- edits := deleteStmtFromBlock (path , declStmt )
168
+ edits := deleteStmtFromBlock (pass . Fset , path , declStmt )
168
169
if len (edits ) == 0 {
169
170
return nil // can this happen?
170
171
}
@@ -198,7 +199,7 @@ func removeVariableFromSpec(pass *analysis.Pass, path []ast.Node, stmt *ast.Valu
198
199
}
199
200
}
200
201
201
- func removeVariableFromAssignment (path []ast.Node , stmt * ast.AssignStmt , ident * ast.Ident ) []analysis.SuggestedFix {
202
+ func removeVariableFromAssignment (fset * token. FileSet , path []ast.Node , stmt * ast.AssignStmt , ident * ast.Ident ) []analysis.SuggestedFix {
202
203
// The only variable in the assignment is unused
203
204
if len (stmt .Lhs ) == 1 {
204
205
// If LHS has only one expression to be valid it has to have 1 expression
@@ -221,7 +222,7 @@ func removeVariableFromAssignment(path []ast.Node, stmt *ast.AssignStmt, ident *
221
222
}
222
223
223
224
// RHS does not have any side effects, delete the whole statement
224
- edits := deleteStmtFromBlock (path , stmt )
225
+ edits := deleteStmtFromBlock (fset , path , stmt )
225
226
if len (edits ) == 0 {
226
227
return nil // can this happen?
227
228
}
@@ -252,7 +253,7 @@ func suggestedFixMessage(name string) string {
252
253
return fmt .Sprintf ("Remove variable %s" , name )
253
254
}
254
255
255
- func deleteStmtFromBlock (path []ast.Node , stmt ast.Stmt ) []analysis.TextEdit {
256
+ func deleteStmtFromBlock (fset * token. FileSet , path []ast.Node , stmt ast.Stmt ) []analysis.TextEdit {
256
257
// Find innermost enclosing BlockStmt.
257
258
var block * ast.BlockStmt
258
259
for i := range path {
@@ -282,6 +283,31 @@ func deleteStmtFromBlock(path []ast.Node, stmt ast.Stmt) []analysis.TextEdit {
282
283
end = block .List [nodeIndex + 1 ].Pos ()
283
284
}
284
285
286
+ // Account for comments within the block containing the statement
287
+ // TODO(adonovan): when golang/go#20744 is addressed, query the AST
288
+ // directly for comments between stmt.End() and end. For now we
289
+ // must scan the entire file's comments (though we could binary search).
290
+ astFile := path [len (path )- 1 ].(* ast.File )
291
+ currFile := fset .File (end )
292
+ stmtEndLine := safetoken .Line (currFile , stmt .End ())
293
+ outer:
294
+ for _ , cg := range astFile .Comments {
295
+ for _ , co := range cg .List {
296
+ if stmt .End () <= co .Pos () && co .Pos () <= end {
297
+ coLine := safetoken .Line (currFile , co .Pos ())
298
+ // If a comment exists within the current block, after the unused variable statement,
299
+ // and before the next statement, we shouldn't delete it.
300
+ if coLine > stmtEndLine {
301
+ end = co .Pos ()
302
+ break outer
303
+ }
304
+ if co .Pos () > end {
305
+ break outer
306
+ }
307
+ }
308
+ }
309
+ }
310
+
285
311
return []analysis.TextEdit {
286
312
{
287
313
Pos : stmt .Pos (),
0 commit comments