WIP
diff --git a/MODULE.bazel b/MODULE.bazel
index c1af060..aa90b0b 100644
--- a/MODULE.bazel
+++ b/MODULE.bazel
@@ -24,6 +24,7 @@
# This name is ugly on purpose to avoid a conflict with a user-named SDK.
"io_bazel_rules_nogo",
)
+
register_toolchains("@go_toolchains//:all")
bazel_dep(name = "gazelle", version = "0.36.0")
@@ -35,6 +36,7 @@
"com_github_gogo_protobuf",
"com_github_golang_mock",
"com_github_golang_protobuf",
+ "com_github_pmezard_go_difflib",
"org_golang_google_genproto",
"org_golang_google_grpc",
"org_golang_google_grpc_cmd_protoc_gen_go_grpc",
diff --git a/go.mod b/go.mod
index 776305b..cbd908c 100644
--- a/go.mod
+++ b/go.mod
@@ -6,6 +6,7 @@
github.com/gogo/protobuf v1.3.2
github.com/golang/mock v1.7.0-rc.1
github.com/golang/protobuf v1.5.3
+ github.com/pmezard/go-difflib v1.0.0
golang.org/x/net v0.18.0
golang.org/x/tools v0.15.0
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013
diff --git a/go.sum b/go.sum
index 629573b..d55bd2f 100644
--- a/go.sum
+++ b/go.sum
@@ -48,6 +48,7 @@
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
diff --git a/go/private/rules/nogo.bzl b/go/private/rules/nogo.bzl
index 9d6b1f9..fe5b8c6 100644
--- a/go/private/rules/nogo.bzl
+++ b/go/private/rules/nogo.bzl
@@ -76,7 +76,7 @@
nogo_source = go.library_to_source(go, struct(
srcs = [struct(files = [nogo_main])],
embed = [ctx.attr._nogo_srcs],
- deps = analyzer_archives,
+ deps = analyzer_archives + [get_archive(ctx.attr._go_difflib)],
), nogo_library, False)
_, executable, runfiles = go.binary(
go,
@@ -106,6 +106,7 @@
),
"_cgo_context_data": attr.label(default = "//:cgo_context_data_proxy"),
"_go_config": attr.label(default = "//:go_config"),
+ "_go_difflib": attr.label(default = "@com_github_pmezard_go_difflib//difflib:go_default_library"),
"_stdlib": attr.label(default = "//:stdlib"),
"_allowlist_function_transition": attr.label(
default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
diff --git a/go/tools/builders/nogo_main.go b/go/tools/builders/nogo_main.go
index 9cf558b..b940e91 100644
--- a/go/tools/builders/nogo_main.go
+++ b/go/tools/builders/nogo_main.go
@@ -39,6 +39,7 @@
"strings"
"sync"
+ "github.com/pmezard/go-difflib/difflib"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/gcexportdata"
"golang.org/x/tools/internal/facts"
@@ -545,10 +546,70 @@
errMsg.WriteString(sep)
sep = "\n"
fmt.Fprintf(errMsg, "%s: %s (%s)", pkg.fset.Position(d.Pos), d.Message, d.Name)
+ for _, fix := range d.SuggestedFixes {
+ diff, err := calculateDiff(pkg.fset.File(d.Pos), fix.TextEdits)
+ if err != nil {
+ fmt.Fprintf(errMsg, "\nerror calculating diff: %v", err)
+ continue
+ }
+ println(diff)
+ }
}
return errMsg.String()
}
+func calculateDiff(f *token.File, edits []analysis.TextEdit) (string, error) {
+ sort.SliceStable(edits, func(i, j int) bool {
+ return edits[i].Pos < edits[j].Pos || edits[i].End < edits[j].End
+ // Some edits are purely insertion. They should be prioritised.
+ })
+
+ beforeBytes, err := os.ReadFile(f.Name())
+ if err != nil {
+ return "", err
+ }
+ before := string(beforeBytes)
+
+ after := applyEdits(f, edits, before)
+ // The epoch timestamp is assumed to represent file creation/deletion events
+ // by some tools, so use a dummy timestamp that is one ns past the epoch.
+ // See https://github.com/bazelbuild/bazel-gazelle/issues/1528.
+ date := "1970-01-01 00:00:00.000000001 +0000"
+ diff := difflib.UnifiedDiff{
+ Context: 3,
+ FromDate: date,
+ ToDate: date,
+ FromFile: f.Name(),
+ ToFile: f.Name(),
+ A: difflib.SplitLines(before),
+ B: difflib.SplitLines(after),
+ }
+
+ return difflib.GetUnifiedDiffString(diff)
+}
+
+func applyEdits(f *token.File, edits []analysis.TextEdit, before string) string {
+ var b strings.Builder
+ var currentOffset int
+ for _, edit := range edits {
+ off := f.Offset(edit.Pos)
+ b.WriteString(before[currentOffset:off])
+ if len(edit.NewText) > 0 {
+ b.Write(edit.NewText)
+ }
+
+ end := off
+ if edit.End.IsValid() {
+ // The end pos for text edits may be token.NoPos to represent pure
+ // insertion.
+ end = f.Offset(edit.End)
+ }
+ currentOffset = end
+ }
+ b.WriteString(before[currentOffset:])
+ return b.String()
+}
+
// config determines which source files an analyzer will emit diagnostics for.
// config values are generated in another file that is compiled with
// nogo_main.go by the nogo rule.
@@ -578,7 +639,7 @@
factMap map[string]string // map import path in source code to file containing serialized facts
}
-func newImporter(importMap, packageFile map[string]string, factMap map[string]string) *importer {
+func newImporter(importMap, packageFile, factMap map[string]string) *importer {
return &importer{
fset: token.NewFileSet(),
importMap: importMap,
diff --git a/tests/bcr/BUILD.bazel b/tests/bcr/BUILD.bazel
index 3426c21..493e3d0 100644
--- a/tests/bcr/BUILD.bazel
+++ b/tests/bcr/BUILD.bazel
@@ -12,6 +12,7 @@
name = "my_nogo",
visibility = ["//visibility:public"],
deps = TOOLS_NOGO,
+ debug = True,
)
go_library(
diff --git a/tests/bcr/go.mod b/tests/bcr/go.mod
new file mode 100644
index 0000000..cb298eb
--- /dev/null
+++ b/tests/bcr/go.mod
@@ -0,0 +1,17 @@
+module example.com
+
+go 1.22.0
+
+require github.com/shirou/gopsutil/v3 v3.24.5
+
+require (
+ github.com/go-ole/go-ole v1.2.6 // indirect
+ github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
+ github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
+ github.com/shoenig/go-m1cpu v0.1.6 // indirect
+ github.com/tklauser/go-sysconf v0.3.12 // indirect
+ github.com/tklauser/numcpus v0.6.1 // indirect
+ github.com/yusufpapurcu/wmi v1.2.4 // indirect
+ golang.org/x/sys v0.22.0 // indirect
+ golang.org/x/tools v0.23.0
+)
diff --git a/tests/bcr/go.sum b/tests/bcr/go.sum
new file mode 100644
index 0000000..6848a10
--- /dev/null
+++ b/tests/bcr/go.sum
@@ -0,0 +1,28 @@
+github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
+github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
+github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
+github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
+github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
+github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
+github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
+github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk=
+github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
+github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
+github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
+github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
+github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
+github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
+github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
+github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
+golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
+golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
+golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg=
+golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/tests/bcr/main.go b/tests/bcr/main.go
index 46c058e..73868bc 100644
--- a/tests/bcr/main.go
+++ b/tests/bcr/main.go
@@ -2,10 +2,25 @@
import (
"fmt"
+ "log"
"example.com/lib"
)
+type basicStruct struct {
+ foo int
+}
+
+var _ = basicStruct{}
+
func main() {
- fmt.Printf("Hello %s!", lib.Name())
+ if main != nil {
+ fmt.Printf("Hello %s!", lib.Name())
+ }
+ _ = !(true && false)
+ maps := make(map[string]string)
+ for k, _ := range maps {
+ log.Println(k)
+ }
+ maps = maps
}