Skip to content

Commit a764a00

Browse files
authored
Merge branch 'master' into 12ya-patch
2 parents 834a098 + 7610d95 commit a764a00

File tree

36 files changed

+1430
-289
lines changed

36 files changed

+1430
-289
lines changed

go/analysis/passes/printf/printf.go

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"golang.org/x/tools/go/ast/inspector"
2323
"golang.org/x/tools/go/types/typeutil"
2424
"golang.org/x/tools/internal/analysisinternal"
25+
"golang.org/x/tools/internal/astutil"
2526
"golang.org/x/tools/internal/fmtstr"
2627
"golang.org/x/tools/internal/typeparams"
2728
"golang.org/x/tools/internal/versions"
@@ -540,7 +541,7 @@ func checkPrintf(pass *analysis.Pass, fileVersion string, kind Kind, call *ast.C
540541
firstArg := idx + 1 // Arguments are immediately after format string.
541542
if !strings.Contains(format, "%") {
542543
if len(call.Args) > firstArg {
543-
pass.Reportf(call.Lparen, "%s call has arguments but no formatting directives", name)
544+
pass.ReportRangef(call.Args[firstArg], "%s call has arguments but no formatting directives", name)
544545
}
545546
return
546547
}
@@ -552,28 +553,29 @@ func checkPrintf(pass *analysis.Pass, fileVersion string, kind Kind, call *ast.C
552553
if err != nil {
553554
// All error messages are in predicate form ("call has a problem")
554555
// so that they may be affixed into a subject ("log.Printf ").
555-
pass.ReportRangef(call.Args[idx], "%s %s", name, err)
556+
pass.ReportRangef(formatArg, "%s %s", name, err)
556557
return
557558
}
558559

559560
// index of the highest used index.
560561
maxArgIndex := firstArg - 1
561562
anyIndex := false
562563
// Check formats against args.
563-
for _, operation := range operations {
564-
if operation.Prec.Index != -1 ||
565-
operation.Width.Index != -1 ||
566-
operation.Verb.Index != -1 {
564+
for _, op := range operations {
565+
if op.Prec.Index != -1 ||
566+
op.Width.Index != -1 ||
567+
op.Verb.Index != -1 {
567568
anyIndex = true
568569
}
569-
if !okPrintfArg(pass, call, &maxArgIndex, firstArg, name, operation) {
570+
rng := opRange(formatArg, op)
571+
if !okPrintfArg(pass, call, rng, &maxArgIndex, firstArg, name, op) {
570572
// One error per format is enough.
571573
return
572574
}
573-
if operation.Verb.Verb == 'w' {
575+
if op.Verb.Verb == 'w' {
574576
switch kind {
575577
case KindNone, KindPrint, KindPrintf:
576-
pass.Reportf(call.Pos(), "%s does not support error-wrapping directive %%w", name)
578+
pass.ReportRangef(rng, "%s does not support error-wrapping directive %%w", name)
577579
return
578580
}
579581
}
@@ -594,6 +596,18 @@ func checkPrintf(pass *analysis.Pass, fileVersion string, kind Kind, call *ast.C
594596
}
595597
}
596598

599+
// opRange returns the source range for the specified printf operation,
600+
// such as the position of the %v substring of "...%v...".
601+
func opRange(formatArg ast.Expr, op *fmtstr.Operation) analysis.Range {
602+
if lit, ok := formatArg.(*ast.BasicLit); ok {
603+
start, end, err := astutil.RangeInStringLiteral(lit, op.Range.Start, op.Range.End)
604+
if err == nil {
605+
return analysisinternal.Range(start, end) // position of "%v"
606+
}
607+
}
608+
return formatArg // entire format string
609+
}
610+
597611
// printfArgType encodes the types of expressions a printf verb accepts. It is a bitmask.
598612
type printfArgType int
599613

@@ -657,7 +671,7 @@ var printVerbs = []printVerb{
657671
// okPrintfArg compares the operation to the arguments actually present,
658672
// reporting any discrepancies it can discern, maxArgIndex was the index of the highest used index.
659673
// If the final argument is ellipsissed, there's little it can do for that.
660-
func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, maxArgIndex *int, firstArg int, name string, operation *fmtstr.Operation) (ok bool) {
674+
func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, rng analysis.Range, maxArgIndex *int, firstArg int, name string, operation *fmtstr.Operation) (ok bool) {
661675
verb := operation.Verb.Verb
662676
var v printVerb
663677
found := false
@@ -680,7 +694,7 @@ func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, maxArgIndex *int, firs
680694

681695
if !formatter {
682696
if !found {
683-
pass.ReportRangef(call, "%s format %s has unknown verb %c", name, operation.Text, verb)
697+
pass.ReportRangef(rng, "%s format %s has unknown verb %c", name, operation.Text, verb)
684698
return false
685699
}
686700
for _, flag := range operation.Flags {
@@ -690,7 +704,7 @@ func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, maxArgIndex *int, firs
690704
continue
691705
}
692706
if !strings.ContainsRune(v.flags, rune(flag)) {
693-
pass.ReportRangef(call, "%s format %s has unrecognized flag %c", name, operation.Text, flag)
707+
pass.ReportRangef(rng, "%s format %s has unrecognized flag %c", name, operation.Text, flag)
694708
return false
695709
}
696710
}
@@ -707,7 +721,7 @@ func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, maxArgIndex *int, firs
707721
// If len(argIndexes)>0, we have something like %.*s and all
708722
// indexes in argIndexes must be an integer.
709723
for _, argIndex := range argIndexes {
710-
if !argCanBeChecked(pass, call, argIndex, firstArg, operation, name) {
724+
if !argCanBeChecked(pass, call, rng, argIndex, firstArg, operation, name) {
711725
return
712726
}
713727
arg := call.Args[argIndex]
@@ -716,7 +730,7 @@ func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, maxArgIndex *int, firs
716730
if reason != "" {
717731
details = " (" + reason + ")"
718732
}
719-
pass.ReportRangef(call, "%s format %s uses non-int %s%s as argument of *", name, operation.Text, analysisinternal.Format(pass.Fset, arg), details)
733+
pass.ReportRangef(rng, "%s format %s uses non-int %s%s as argument of *", name, operation.Text, analysisinternal.Format(pass.Fset, arg), details)
720734
return false
721735
}
722736
}
@@ -738,12 +752,12 @@ func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, maxArgIndex *int, firs
738752

739753
// Now check verb's type.
740754
verbArgIndex := operation.Verb.ArgIndex
741-
if !argCanBeChecked(pass, call, verbArgIndex, firstArg, operation, name) {
755+
if !argCanBeChecked(pass, call, rng, verbArgIndex, firstArg, operation, name) {
742756
return false
743757
}
744758
arg := call.Args[verbArgIndex]
745759
if isFunctionValue(pass, arg) && verb != 'p' && verb != 'T' {
746-
pass.ReportRangef(call, "%s format %s arg %s is a func value, not called", name, operation.Text, analysisinternal.Format(pass.Fset, arg))
760+
pass.ReportRangef(rng, "%s format %s arg %s is a func value, not called", name, operation.Text, analysisinternal.Format(pass.Fset, arg))
747761
return false
748762
}
749763
if reason, ok := matchArgType(pass, v.typ, arg); !ok {
@@ -755,14 +769,14 @@ func okPrintfArg(pass *analysis.Pass, call *ast.CallExpr, maxArgIndex *int, firs
755769
if reason != "" {
756770
details = " (" + reason + ")"
757771
}
758-
pass.ReportRangef(call, "%s format %s has arg %s of wrong type %s%s", name, operation.Text, analysisinternal.Format(pass.Fset, arg), typeString, details)
772+
pass.ReportRangef(rng, "%s format %s has arg %s of wrong type %s%s", name, operation.Text, analysisinternal.Format(pass.Fset, arg), typeString, details)
759773
return false
760774
}
761775
// Detect recursive formatting via value's String/Error methods.
762776
// The '#' flag suppresses the methods, except with %x, %X, and %q.
763777
if v.typ&argString != 0 && v.verb != 'T' && (!strings.Contains(operation.Flags, "#") || strings.ContainsRune("qxX", v.verb)) {
764778
if methodName, ok := recursiveStringer(pass, arg); ok {
765-
pass.ReportRangef(call, "%s format %s with arg %s causes recursive %s method call", name, operation.Text, analysisinternal.Format(pass.Fset, arg), methodName)
779+
pass.ReportRangef(rng, "%s format %s with arg %s causes recursive %s method call", name, operation.Text, analysisinternal.Format(pass.Fset, arg), methodName)
766780
return false
767781
}
768782
}
@@ -846,7 +860,7 @@ func isFunctionValue(pass *analysis.Pass, e ast.Expr) bool {
846860
// argCanBeChecked reports whether the specified argument is statically present;
847861
// it may be beyond the list of arguments or in a terminal slice... argument, which
848862
// means we can't see it.
849-
func argCanBeChecked(pass *analysis.Pass, call *ast.CallExpr, argIndex, firstArg int, operation *fmtstr.Operation, name string) bool {
863+
func argCanBeChecked(pass *analysis.Pass, call *ast.CallExpr, rng analysis.Range, argIndex, firstArg int, operation *fmtstr.Operation, name string) bool {
850864
if argIndex <= 0 {
851865
// Shouldn't happen, so catch it with prejudice.
852866
panic("negative argIndex")
@@ -863,7 +877,7 @@ func argCanBeChecked(pass *analysis.Pass, call *ast.CallExpr, argIndex, firstArg
863877
// There are bad indexes in the format or there are fewer arguments than the format needs.
864878
// This is the argument number relative to the format: Printf("%s", "hi") will give 1 for the "hi".
865879
arg := argIndex - firstArg + 1 // People think of arguments as 1-indexed.
866-
pass.ReportRangef(call, "%s format %s reads arg #%d, but call has %v", name, operation.Text, arg, count(len(call.Args)-firstArg, "arg"))
880+
pass.ReportRangef(rng, "%s format %s reads arg #%d, but call has %v", name, operation.Text, arg, count(len(call.Args)-firstArg, "arg"))
867881
return false
868882
}
869883

go/analysis/passes/tests/tests.go

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -447,18 +447,6 @@ func checkExampleName(pass *analysis.Pass, fn *ast.FuncDecl) {
447447
}
448448
}
449449

450-
type tokenRange struct {
451-
p, e token.Pos
452-
}
453-
454-
func (r tokenRange) Pos() token.Pos {
455-
return r.p
456-
}
457-
458-
func (r tokenRange) End() token.Pos {
459-
return r.e
460-
}
461-
462450
func checkTest(pass *analysis.Pass, fn *ast.FuncDecl, prefix string) {
463451
// Want functions with 0 results and 1 parameter.
464452
if fn.Type.Results != nil && len(fn.Type.Results.List) > 0 ||
@@ -476,8 +464,9 @@ func checkTest(pass *analysis.Pass, fn *ast.FuncDecl, prefix string) {
476464
if tparams := fn.Type.TypeParams; tparams != nil && len(tparams.List) > 0 {
477465
// Note: cmd/go/internal/load also errors about TestXXX and BenchmarkXXX functions with type parameters.
478466
// We have currently decided to also warn before compilation/package loading. This can help users in IDEs.
479-
at := tokenRange{tparams.Opening, tparams.Closing}
480-
pass.ReportRangef(at, "%s has type parameters: it will not be run by go test as a %sXXX function", fn.Name.Name, prefix)
467+
pass.ReportRangef(analysisinternal.Range(tparams.Opening, tparams.Closing),
468+
"%s has type parameters: it will not be run by go test as a %sXXX function",
469+
fn.Name.Name, prefix)
481470
}
482471

483472
if !isTestSuffix(fn.Name.Name[len(prefix):]) {

go/analysis/unitchecker/separate_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ func MyPrintf(format string, args ...any) {
222222
// Observe that the example produces a fact-based diagnostic
223223
// from separate analysis of "main", "lib", and "fmt":
224224

225-
const want = `/main/main.go:6:2: [printf] separate/lib.MyPrintf format %s has arg 123 of wrong type int`
225+
const want = `/main/main.go:6:16: [printf] separate/lib.MyPrintf format %s has arg 123 of wrong type int`
226226
sort.Strings(allDiagnostics)
227227
if got := strings.Join(allDiagnostics, "\n"); got != want {
228228
t.Errorf("Got: %s\nWant: %s", got, want)

gopls/doc/analyzers.md

Lines changed: 92 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1875,7 +1875,7 @@ In a type switch like the following
18751875
type T struct{}
18761876
func (T) Read(b []byte) (int, error) { return 0, nil }
18771877

1878-
var v interface{} = T{}
1878+
var v any = T{}
18791879

18801880
switch v.(type) {
18811881
case io.Reader:
@@ -1893,7 +1893,7 @@ Another example:
18931893
func (T) Read(b []byte) (int, error) { return 0, nil }
18941894
func (T) Close() error { return nil }
18951895

1896-
var v interface{} = T{}
1896+
var v any = T{}
18971897

18981898
switch v.(type) {
18991899
case io.Reader:
@@ -3488,6 +3488,96 @@ Package documentation: [framepointer](https://pkg.go.dev/golang.org/x/tools/go/a
34883488

34893489
The gofix analyzer inlines functions and constants that are marked for inlining.
34903490

3491+
## Functions
3492+
3493+
Given a function that is marked for inlining, like this one:
3494+
3495+
//go:fix inline
3496+
func Square(x int) int { return Pow(x, 2) }
3497+
3498+
this analyzer will recommend that calls to the function elsewhere, in the same
3499+
or other packages, should be inlined.
3500+
3501+
Inlining can be used to move off of a deprecated function:
3502+
3503+
// Deprecated: prefer Pow(x, 2).
3504+
//go:fix inline
3505+
func Square(x int) int { return Pow(x, 2) }
3506+
3507+
It can also be used to move off of an obsolete package,
3508+
as when the import path has changed or a higher major version is available:
3509+
3510+
package pkg
3511+
3512+
import pkg2 "pkg/v2"
3513+
3514+
//go:fix inline
3515+
func F() { pkg2.F(nil) }
3516+
3517+
Replacing a call pkg.F() by pkg2.F(nil) can have no effect on the program,
3518+
so this mechanism provides a low-risk way to update large numbers of calls.
3519+
We recommend, where possible, expressing the old API in terms of the new one
3520+
to enable automatic migration.
3521+
3522+
The inliner takes care to avoid behavior changes, even subtle ones,
3523+
such as changes to the order in which argument expressions are
3524+
evaluated. When it cannot safely eliminate all parameter variables,
3525+
it may introduce a "binding declaration" of the form
3526+
3527+
var params = args
3528+
3529+
to evaluate argument expressions in the correct order and bind them to
3530+
parameter variables. Since the resulting code transformation may be
3531+
stylistically suboptimal, such inlinings may be disabled by specifying
3532+
the -gofix.allow_binding_decl=false flag to the analyzer driver.
3533+
3534+
(In cases where it is not safe to "reduce" a call—that is, to replace
3535+
a call f(x) by the body of function f, suitably substituted—the
3536+
inliner machinery is capable of replacing f by a function literal,
3537+
func(){...}(). However, the gofix analyzer discards all such
3538+
"literalizations" unconditionally, again on grounds of style.)
3539+
3540+
## Constants
3541+
3542+
Given a constant that is marked for inlining, like this one:
3543+
3544+
//go:fix inline
3545+
const Ptr = Pointer
3546+
3547+
this analyzer will recommend that uses of Ptr should be replaced with Pointer.
3548+
3549+
As with functions, inlining can be used to replace deprecated constants and
3550+
constants in obsolete packages.
3551+
3552+
A constant definition can be marked for inlining only if it refers to another
3553+
named constant.
3554+
3555+
The "//go:fix inline" comment must appear before a single const declaration on its own,
3556+
as above; before a const declaration that is part of a group, as in this case:
3557+
3558+
const (
3559+
C = 1
3560+
//go:fix inline
3561+
Ptr = Pointer
3562+
)
3563+
3564+
or before a group, applying to every constant in the group:
3565+
3566+
//go:fix inline
3567+
const (
3568+
Ptr = Pointer
3569+
Val = Value
3570+
)
3571+
3572+
The proposal https://go.dev/issue/32816 introduces the "//go:fix" directives.
3573+
3574+
You can use this (officially unsupported) command to apply gofix fixes en masse:
3575+
3576+
$ go run golang.org/x/tools/gopls/internal/analysis/gofix/cmd/gofix@latest -test ./...
3577+
3578+
(Do not use "go get -tool" to add gopls as a dependency of your
3579+
module; gopls commands must be built from their release branch.)
3580+
34913581
Default: on.
34923582

34933583
Package documentation: [gofix](https://pkg.go.dev/golang.org/x/tools/internal/gofix)

gopls/go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ require (
1111
golang.org/x/sys v0.33.0
1212
golang.org/x/telemetry v0.0.0-20250417124945-06ef541f3fa3
1313
golang.org/x/text v0.25.0
14-
golang.org/x/tools v0.30.0
14+
golang.org/x/tools v0.33.1-0.20250521210010-423c5afcceff
1515
golang.org/x/vuln v1.1.4
1616
gopkg.in/yaml.v3 v3.0.1
17-
honnef.co/go/tools v0.6.1
17+
honnef.co/go/tools v0.7.0-0.dev.0.20250523013057-bbc2f4dd71ea
1818
mvdan.cc/gofumpt v0.7.0
1919
mvdan.cc/xurls/v2 v2.6.0
2020
)

gopls/go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,8 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogR
5959
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
6060
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
6161
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
62-
honnef.co/go/tools v0.6.1 h1:R094WgE8K4JirYjBaOpz/AvTyUu/3wbmAoskKN/pxTI=
63-
honnef.co/go/tools v0.6.1/go.mod h1:3puzxxljPCe8RGJX7BIy1plGbxEOZni5mR2aXe3/uk4=
62+
honnef.co/go/tools v0.7.0-0.dev.0.20250523013057-bbc2f4dd71ea h1:fj8r9irJSpolAGUdZBxJIRY3lLc4jH2Dt4lwnWyWwpw=
63+
honnef.co/go/tools v0.7.0-0.dev.0.20250523013057-bbc2f4dd71ea/go.mod h1:EPDDhEZqVHhWuPI5zPAsjU0U7v9xNIWjoOVyZ5ZcniQ=
6464
mvdan.cc/gofumpt v0.7.0 h1:bg91ttqXmi9y2xawvkuMXyvAA/1ZGJqYAEGjXuP0JXU=
6565
mvdan.cc/gofumpt v0.7.0/go.mod h1:txVFJy/Sc/mvaycET54pV8SW8gWxTlUuGHVEcncmNUo=
6666
mvdan.cc/xurls/v2 v2.6.0 h1:3NTZpeTxYVWNSokW3MKeyVkz/j7uYXYiMtXRUfmjbgI=

0 commit comments

Comments
 (0)