Skip to content

Commit 6d1dd12

Browse files
adonovangopherbot
authored andcommitted
go/analysis: simplify unusedresult
This change simplfies the unusedresult checker by using more modern library functions that also make it work in the presence of dot imports. It also adds a singlechecker command for it, cleans up various Doc constants, and avoids an allocation. Change-Id: I3af1844644356cbbeb2226b84063c8cdac487e00 Reviewed-on: https://go-review.googlesource.com/c/tools/+/492735 Run-TryBot: Alan Donovan <adonovan@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: Robert Findley <rfindley@google.com> Auto-Submit: Alan Donovan <adonovan@google.com>
1 parent 4a2dd0d commit 6d1dd12

File tree

12 files changed

+75
-86
lines changed

12 files changed

+75
-86
lines changed

go/analysis/passes/nilfunc/nilfunc.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,6 @@ import (
1919
"golang.org/x/tools/internal/typeparams"
2020
)
2121

22-
const Doc = `check for useless comparisons between functions and nil
23-
24-
A useless comparison is one like f == nil as opposed to f() == nil.`
25-
2622
//go:embed doc.go
2723
var doc string
2824

go/analysis/passes/timeformat/timeformat.go

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,6 @@ import (
2424
const badFormat = "2006-02-01"
2525
const goodFormat = "2006-01-02"
2626

27-
const Doc = `check for calls of (time.Time).Format or time.Parse with 2006-02-01
28-
29-
The timeformat checker looks for time formats with the 2006-02-01 (yyyy-dd-mm)
30-
format. Internationally, "yyyy-dd-mm" does not occur in common calendar date
31-
standards, and so it is more likely that 2006-01-02 (yyyy-mm-dd) was intended.
32-
`
33-
3427
//go:embed doc.go
3528
var doc string
3629

go/analysis/passes/unsafeptr/doc.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,5 @@
1313
// to convert integers to pointers. A conversion from uintptr to
1414
// unsafe.Pointer is invalid if it implies that there is a uintptr-typed
1515
// word in memory that holds a pointer value, because that word will be
16-
// invisible to stack copying and to the garbage collector.`
16+
// invisible to stack copying and to the garbage collector.
1717
package unsafeptr

go/analysis/passes/unsafeptr/unsafeptr.go

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,6 @@ import (
1818
"golang.org/x/tools/go/ast/inspector"
1919
)
2020

21-
const Doc = `check for invalid conversions of uintptr to unsafe.Pointer
22-
23-
The unsafeptr analyzer reports likely incorrect uses of unsafe.Pointer
24-
to convert integers to pointers. A conversion from uintptr to
25-
unsafe.Pointer is invalid if it implies that there is a uintptr-typed
26-
word in memory that holds a pointer value, because that word will be
27-
invisible to stack copying and to the garbage collector.`
28-
2921
//go:embed doc.go
3022
var doc string
3123

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
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+
// The unusedresult command applies the golang.org/x/tools/go/analysis/passes/unusedresult
6+
// analysis to the specified packages of Go source code.
7+
package main
8+
9+
import (
10+
"golang.org/x/tools/go/analysis/passes/unusedresult"
11+
"golang.org/x/tools/go/analysis/singlechecker"
12+
)
13+
14+
func main() { singlechecker.Main(unusedresult.Analyzer) }

go/analysis/passes/unusedresult/doc.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@
99
//
1010
// unusedresult: check for unused results of calls to some functions
1111
//
12-
// Some functions like fmt.Errorf return a result and have no side effects,
13-
// so it is always a mistake to discard the result. This analyzer reports
14-
// calls to certain functions in which the result of the call is ignored.
12+
// Some functions like fmt.Errorf return a result and have no side
13+
// effects, so it is always a mistake to discard the result. Other
14+
// functions may return an error that must not be ignored, or a cleanup
15+
// operation that must be called. This analyzer reports calls to
16+
// functions like these when the result of the call is ignored.
1517
//
1618
// The set of functions may be controlled using flags.
1719
package unusedresult

go/analysis/passes/unusedresult/testdata/src/a/a.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"bytes"
99
"errors"
1010
"fmt"
11+
. "fmt"
1112
)
1213

1314
func _() {
@@ -20,8 +21,11 @@ func _() {
2021
err.Error() // want `result of \(error\).Error call not used`
2122

2223
var buf bytes.Buffer
23-
buf.String() // want `result of \(bytes.Buffer\).String call not used`
24+
buf.String() // want `result of \(\*bytes.Buffer\).String call not used`
2425

2526
fmt.Sprint("") // want "result of fmt.Sprint call not used"
2627
fmt.Sprintf("") // want "result of fmt.Sprintf call not used"
28+
29+
Sprint("") // want "result of fmt.Sprint call not used"
30+
Sprintf("") // want "result of fmt.Sprintf call not used"
2731
}

go/analysis/passes/unusedresult/testdata/src/typeparams/typeparams.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ func _[T any]() {
2323
err.Error() // want `result of \(error\).Error call not used`
2424

2525
var buf bytes.Buffer
26-
buf.String() // want `result of \(bytes.Buffer\).String call not used`
26+
buf.String() // want `result of \(\*bytes.Buffer\).String call not used`
2727

2828
fmt.Sprint("") // want "result of fmt.Sprint call not used"
2929
fmt.Sprintf("") // want "result of fmt.Sprintf call not used"
@@ -32,10 +32,10 @@ func _[T any]() {
3232
_ = userdefs.MustUse[int](2)
3333

3434
s := userdefs.SingleTypeParam[int]{X: 1}
35-
s.String() // want `result of \(typeparams/userdefs.SingleTypeParam\[int\]\).String call not used`
35+
s.String() // want `result of \(\*typeparams/userdefs.SingleTypeParam\[int\]\).String call not used`
3636
_ = s.String()
3737

3838
m := userdefs.MultiTypeParam[int, string]{X: 1, Y: "one"}
39-
m.String() // want `result of \(typeparams/userdefs.MultiTypeParam\[int, string\]\).String call not used`
39+
m.String() // want `result of \(\*typeparams/userdefs.MultiTypeParam\[int, string\]\).String call not used`
4040
_ = m.String()
41-
}
41+
}

go/analysis/passes/unusedresult/unusedresult.go

Lines changed: 31 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,16 @@
33
// license that can be found in the LICENSE file.
44

55
// Package unusedresult defines an analyzer that checks for unused
6-
// results of calls to certain pure functions.
6+
// results of calls to certain functions.
77
package unusedresult
88

9+
// It is tempting to make this analysis inductive: for each function
10+
// that tail-calls one of the functions that we check, check those
11+
// functions too. However, just because you must use the result of
12+
// fmt.Sprintf doesn't mean you need to use the result of every
13+
// function that returns a formatted string: it may have other results
14+
// and effects.
15+
916
import (
1017
_ "embed"
1118
"go/ast"
@@ -18,17 +25,9 @@ import (
1825
"golang.org/x/tools/go/analysis/passes/inspect"
1926
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
2027
"golang.org/x/tools/go/ast/inspector"
21-
"golang.org/x/tools/internal/typeparams"
28+
"golang.org/x/tools/go/types/typeutil"
2229
)
2330

24-
const Doc = `check for unused results of calls to some functions
25-
26-
Some functions like fmt.Errorf return a result and have no side effects,
27-
so it is always a mistake to discard the result. This analyzer reports
28-
calls to certain functions in which the result of the call is ignored.
29-
30-
The set of functions may be controlled using flags.`
31-
3231
//go:embed doc.go
3332
var doc string
3433

@@ -56,15 +55,9 @@ func init() {
5655
// ignoringTheErrorWouldBeVeryBad() // oops
5756
//
5857

59-
// Also, it is tempting to make this analysis modular: one
60-
// could export a "mustUseResult" fact for each function that
61-
// tail-calls one of the functions that we check, and check
62-
// those functions too.
63-
//
64-
// However, just because you must use the result of
65-
// fmt.Sprintf doesn't mean you need to use the result of
66-
// every function that returns a formatted string:
67-
// it may have other results and effects.
58+
// List standard library functions here.
59+
// The context.With{Cancel,Deadline,Timeout} entries are
60+
// effectively redundant wrt the lostcancel analyzer.
6861
funcs.Set("errors.New,fmt.Errorf,fmt.Sprintf,fmt.Sprint,sort.Reverse,context.WithValue,context.WithCancel,context.WithDeadline,context.WithTimeout")
6962
Analyzer.Flags.Var(&funcs, "funcs",
7063
"comma-separated list of functions whose results must be used")
@@ -77,6 +70,14 @@ func init() {
7770
func run(pass *analysis.Pass) (interface{}, error) {
7871
inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
7972

73+
// Split functions into (pkg, name) pairs to save allocation later.
74+
pkgFuncs := make(map[[2]string]bool, len(funcs))
75+
for s := range funcs {
76+
if i := strings.LastIndexByte(s, '.'); i > 0 {
77+
pkgFuncs[[2]string{s[:i], s[i+1:]}] = true
78+
}
79+
}
80+
8081
nodeFilter := []ast.Node{
8182
(*ast.ExprStmt)(nil),
8283
}
@@ -85,41 +86,26 @@ func run(pass *analysis.Pass) (interface{}, error) {
8586
if !ok {
8687
return // not a call statement
8788
}
88-
fun := analysisutil.Unparen(call.Fun)
89-
90-
if pass.TypesInfo.Types[fun].IsType() {
91-
return // a conversion, not a call
92-
}
9389

94-
x, _, _, _ := typeparams.UnpackIndexExpr(fun)
95-
if x != nil {
96-
fun = x // If this is generic function or method call, skip the instantiation arguments
97-
}
98-
99-
selector, ok := fun.(*ast.SelectorExpr)
90+
// Call to function or method?
91+
fn, ok := typeutil.Callee(pass.TypesInfo, call).(*types.Func)
10092
if !ok {
101-
return // neither a method call nor a qualified ident
93+
return // e.g. var or builtin
10294
}
10395

104-
sel, ok := pass.TypesInfo.Selections[selector]
105-
if ok && sel.Kind() == types.MethodVal {
96+
if sig := fn.Type().(*types.Signature); sig.Recv() != nil {
10697
// method (e.g. foo.String())
107-
obj := sel.Obj().(*types.Func)
108-
sig := sel.Type().(*types.Signature)
10998
if types.Identical(sig, sigNoArgsStringResult) {
110-
if stringMethods[obj.Name()] {
99+
if stringMethods[fn.Name()] {
111100
pass.Reportf(call.Lparen, "result of (%s).%s call not used",
112-
sig.Recv().Type(), obj.Name())
101+
sig.Recv().Type(), fn.Name())
113102
}
114103
}
115-
} else if !ok {
116-
// package-qualified function (e.g. fmt.Errorf)
117-
obj := pass.TypesInfo.Uses[selector.Sel]
118-
if obj, ok := obj.(*types.Func); ok {
119-
qname := obj.Pkg().Path() + "." + obj.Name()
120-
if funcs[qname] {
121-
pass.Reportf(call.Lparen, "result of %v call not used", qname)
122-
}
104+
} else {
105+
// package-level function (e.g. fmt.Errorf)
106+
if pkgFuncs[[2]string{fn.Pkg().Path(), fn.Name()}] {
107+
pass.Reportf(call.Lparen, "result of %s.%s call not used",
108+
fn.Pkg().Path(), fn.Name())
123109
}
124110
}
125111
})

gopls/doc/analyzers.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -584,7 +584,7 @@ The unsafeptr analyzer reports likely incorrect uses of unsafe.Pointer
584584
to convert integers to pointers. A conversion from uintptr to
585585
unsafe.Pointer is invalid if it implies that there is a uintptr-typed
586586
word in memory that holds a pointer value, because that word will be
587-
invisible to stack copying and to the garbage collector.`
587+
invisible to stack copying and to the garbage collector.
588588

589589
**Enabled by default.**
590590

@@ -607,9 +607,11 @@ To reduce false positives it ignores:
607607

608608
check for unused results of calls to some functions
609609

610-
Some functions like fmt.Errorf return a result and have no side effects,
611-
so it is always a mistake to discard the result. This analyzer reports
612-
calls to certain functions in which the result of the call is ignored.
610+
Some functions like fmt.Errorf return a result and have no side
611+
effects, so it is always a mistake to discard the result. Other
612+
functions may return an error that must not be ignored, or a cleanup
613+
operation that must be called. This analyzer reports calls to
614+
functions like these when the result of the call is ignored.
613615

614616
The set of functions may be controlled using flags.
615617

gopls/internal/lsp/server_gen.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -236,16 +236,16 @@ func (s *Server) SelectionRange(ctx context.Context, params *protocol.SelectionR
236236
return s.selectionRange(ctx, params)
237237
}
238238

239-
func (s *Server) SemanticTokensFull(ctx context.Context, p *protocol.SemanticTokensParams) (*protocol.SemanticTokens, error) {
240-
return s.semanticTokensFull(ctx, p)
239+
func (s *Server) SemanticTokensFull(ctx context.Context, params *protocol.SemanticTokensParams) (*protocol.SemanticTokens, error) {
240+
return s.semanticTokensFull(ctx, params)
241241
}
242242

243-
func (s *Server) SemanticTokensFullDelta(ctx context.Context, p *protocol.SemanticTokensDeltaParams) (interface{}, error) {
243+
func (s *Server) SemanticTokensFullDelta(context.Context, *protocol.SemanticTokensDeltaParams) (interface{}, error) {
244244
return nil, notImplemented("SemanticTokensFullDelta")
245245
}
246246

247-
func (s *Server) SemanticTokensRange(ctx context.Context, p *protocol.SemanticTokensRangeParams) (*protocol.SemanticTokens, error) {
248-
return s.semanticTokensRange(ctx, p)
247+
func (s *Server) SemanticTokensRange(ctx context.Context, params *protocol.SemanticTokensRangeParams) (*protocol.SemanticTokens, error) {
248+
return s.semanticTokensRange(ctx, params)
249249
}
250250

251251
func (s *Server) SetTrace(context.Context, *protocol.SetTraceParams) error {

gopls/internal/lsp/source/api_json.go

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)