Skip to content

Commit 36684df

Browse files
findleyrgopherbot
authored andcommitted
go/analysis/passes/unusedwrite: silence if unsafe is imported
As reported in golang/go#67684, the unusedwrite analyzer may have false positives when used with unsafe, so silence it if unsafe is imported. This is a rather large hammer, but there is a high likelihood of such false positives, and they significantly detracting from the usefulness of the analysis. Specifically: this noise causes Go developers to disable the analyzer. Fixes golang/go#67684 Change-Id: I855a5da6124a0495c7eb11722b466824689780a3 Reviewed-on: https://go-review.googlesource.com/c/tools/+/621817 Auto-Submit: Robert Findley <[email protected]> Reviewed-by: Alan Donovan <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 17213ba commit 36684df

File tree

5 files changed

+49
-16
lines changed

5 files changed

+49
-16
lines changed

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

+9
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,15 @@ func BadWrites() {
3030
v.x = i // want "unused write to field x"
3131
_ = v.y
3232
}
33+
34+
// The analyzer can handle only simple control flow.
35+
type T struct{ x, y int }
36+
t := new(T)
37+
if true {
38+
t = new(T)
39+
} // causes t below to become phi(alloc, alloc), not a simple alloc
40+
t.x = 1 // false negative
41+
print(t.y)
3342
}
3443

3544
func (t T1) BadValueReceiverWrite(v T2) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package importsunsafe
2+
3+
import "unsafe"
4+
5+
type S struct {
6+
F, G int
7+
}
8+
9+
func _() {
10+
var s S
11+
s.F = 1
12+
// This write to G is used below, because &s.F allows access to all of s, but
13+
// the analyzer would naively report it as unused. For this reason, we
14+
// silence the analysis if unsafe is imported.
15+
s.G = 2
16+
17+
ptr := unsafe.Pointer(&s.F)
18+
t := (*S)(ptr)
19+
println(t.G)
20+
}

go/analysis/passes/unusedwrite/unusedwrite.go

+9-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,15 @@ var Analyzer = &analysis.Analyzer{
2828
Run: run,
2929
}
3030

31-
func run(pass *analysis.Pass) (interface{}, error) {
31+
func run(pass *analysis.Pass) (any, error) {
32+
for _, pkg := range pass.Pkg.Imports() {
33+
if pkg.Path() == "unsafe" {
34+
// See golang/go#67684, or testdata/src/importsunsafe: the unusedwrite
35+
// analyzer may have false positives when used with unsafe.
36+
return nil, nil
37+
}
38+
}
39+
3240
ssainput := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA)
3341
for _, fn := range ssainput.SrcFuncs {
3442
reports := checkStores(fn)

go/analysis/passes/unusedwrite/unusedwrite_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,5 @@ import (
1313

1414
func Test(t *testing.T) {
1515
testdata := analysistest.TestData()
16-
analysistest.Run(t, testdata, unusedwrite.Analyzer, "a")
16+
analysistest.Run(t, testdata, unusedwrite.Analyzer, "a", "importsunsafe")
1717
}

gopls/internal/test/marker/testdata/diagnostics/analyzers.txt

+10-14
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package analyzer
1919

2020
import (
2121
"fmt"
22+
"log/slog"
2223
"sync"
2324
"testing"
2425
"time"
@@ -61,9 +62,16 @@ func _(s struct{x int}) {
6162
s.x = 1 //@diag("x", re"unused write to field x")
6263
}
6364

64-
-- cgocall.go --
65-
package analyzer
65+
// slog
66+
func _() {
67+
slog.Info("msg", 1) //@diag("1", re`slog.Info arg "1" should be a string or a slog.Attr`)
68+
}
69+
70+
-- cgocall/cgocall.go --
71+
package cgocall
6672

73+
// Note: this test must be in a separate package, as the unsafe import
74+
// silences the unusedwrite analyzer.
6775
import "unsafe"
6876

6977
// void f(void *ptr) {}
@@ -73,15 +81,3 @@ import "C"
7381
func _(c chan bool) {
7482
C.f(unsafe.Pointer(&c)) //@ diag("unsafe", re"passing Go type with embedded pointer to C")
7583
}
76-
77-
-- bad_test_go121.go --
78-
//go:build go1.21
79-
80-
package analyzer
81-
82-
import "log/slog"
83-
84-
// slog
85-
func _() {
86-
slog.Info("msg", 1) //@diag("1", re`slog.Info arg "1" should be a string or a slog.Attr`)
87-
}

0 commit comments

Comments
 (0)