Source file src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/doc.go
1 // Copyright 2024 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 /* 6 Package modernize provides a suite of analyzers that suggest 7 simplifications to Go code, using modern language and library 8 features. 9 10 Each diagnostic provides a fix. Our intent is that these fixes may 11 be safely applied en masse without changing the behavior of your 12 program. In some cases the suggested fixes are imperfect and may 13 lead to (for example) unused imports or unused local variables, 14 causing build breakage. However, these problems are generally 15 trivial to fix. We regard any modernizer whose fix changes program 16 behavior to have a serious bug and will endeavor to fix it. 17 18 To apply all modernization fixes en masse, you can use the 19 following command: 20 21 $ go run golang.org/x/tools/go/analysis/passes/modernize/cmd/modernize@latest -fix ./... 22 23 (Do not use "go get -tool" to add gopls as a dependency of your 24 module; gopls commands must be built from their release branch.) 25 26 If the tool warns of conflicting fixes, you may need to run it more 27 than once until it has applied all fixes cleanly. This command is 28 not an officially supported interface and may change in the future. 29 30 Changes produced by this tool should be reviewed as usual before 31 being merged. In some cases, a loop may be replaced by a simple 32 function call, causing comments within the loop to be discarded. 33 Human judgment may be required to avoid losing comments of value. 34 35 The modernize suite contains many analyzers. Diagnostics from some, 36 such as "any" (which replaces "interface{}" with "any" where it 37 is safe to do so), are particularly numerous. It may ease the burden of 38 code review to apply fixes in two steps, the first consisting only of 39 fixes from the "any" analyzer, the second consisting of all 40 other analyzers. This can be achieved using flags, as in this example: 41 42 $ modernize -any=true -fix ./... 43 $ modernize -any=false -fix ./... 44 45 # Analyzer appendclipped 46 47 appendclipped: simplify append chains using slices.Concat 48 49 The appendclipped analyzer suggests replacing chains of append calls with a 50 single call to slices.Concat, which was added in Go 1.21. For example, 51 append(append(s, s1...), s2...) would be simplified to slices.Concat(s, s1, s2). 52 53 In the simple case of appending to a newly allocated slice, such as 54 append([]T(nil), s...), the analyzer suggests the more concise slices.Clone(s). 55 For byte slices, it will prefer bytes.Clone if the "bytes" package is 56 already imported. 57 58 This fix is only applied when the base of the append tower is a 59 "clipped" slice, meaning its length and capacity are equal (e.g. 60 x[:0:0] or []T{}). This is to avoid changing program behavior by 61 eliminating intended side effects on the base slice's underlying 62 array. 63 64 This analyzer is currently disabled by default as the 65 transformation does not preserve the nilness of the base slice in 66 all cases; see https://go.dev/issue/73557. 67 68 # Analyzer bloop 69 70 bloop: replace for-range over b.N with b.Loop 71 72 The bloop analyzer suggests replacing benchmark loops of the form 73 `for i := 0; i < b.N; i++` or `for range b.N` with the more modern 74 `for b.Loop()`, which was added in Go 1.24. 75 76 This change makes benchmark code more readable and also removes the need for 77 manual timer control, so any preceding calls to b.StartTimer, b.StopTimer, 78 or b.ResetTimer within the same function will also be removed. 79 80 Caveats: The b.Loop() method is designed to prevent the compiler from 81 optimizing away the benchmark loop, which can occasionally result in 82 slower execution due to increased allocations in some specific cases. 83 Since its fix may change the performance of nanosecond-scale benchmarks, 84 bloop is disabled by default in the `go fix` analyzer suite; see golang/go#74967. 85 86 # Analyzer any 87 88 any: replace interface{} with any 89 90 The any analyzer suggests replacing uses of the empty interface type, 91 `interface{}`, with the `any` alias, which was introduced in Go 1.18. 92 This is a purely stylistic change that makes code more readable. 93 94 # Analyzer errorsastype 95 96 errorsastype: replace errors.As with errors.AsType[T] 97 98 This analyzer suggests fixes to simplify uses of [errors.As] of 99 this form: 100 101 var myerr *MyErr 102 if errors.As(err, &myerr) { 103 handle(myerr) 104 } 105 106 by using the less error-prone generic [errors.AsType] function, 107 introduced in Go 1.26: 108 109 if myerr, ok := errors.AsType[*MyErr](err); ok { 110 handle(myerr) 111 } 112 113 The fix is only offered if the var declaration has the form shown and 114 there are no uses of myerr outside the if statement. 115 116 # Analyzer fmtappendf 117 118 fmtappendf: replace []byte(fmt.Sprintf) with fmt.Appendf 119 120 The fmtappendf analyzer suggests replacing `[]byte(fmt.Sprintf(...))` with 121 `fmt.Appendf(nil, ...)`. This avoids the intermediate allocation of a string 122 by Sprintf, making the code more efficient. The suggestion also applies to 123 fmt.Sprint and fmt.Sprintln. 124 125 # Analyzer forvar 126 127 forvar: remove redundant re-declaration of loop variables 128 129 The forvar analyzer removes unnecessary shadowing of loop variables. 130 Before Go 1.22, it was common to write `for _, x := range s { x := x ... }` 131 to create a fresh variable for each iteration. Go 1.22 changed the semantics 132 of `for` loops, making this pattern redundant. This analyzer removes the 133 unnecessary `x := x` statement. 134 135 This fix only applies to `range` loops. 136 137 # Analyzer mapsloop 138 139 mapsloop: replace explicit loops over maps with calls to maps package 140 141 The mapsloop analyzer replaces loops of the form 142 143 for k, v := range x { m[k] = v } 144 145 with a single call to a function from the `maps` package, added in Go 1.23. 146 Depending on the context, this could be `maps.Copy`, `maps.Insert`, 147 `maps.Clone`, or `maps.Collect`. 148 149 The transformation to `maps.Clone` is applied conservatively, as it 150 preserves the nilness of the source map, which may be a subtle change in 151 behavior if the original code did not handle a nil map in the same way. 152 153 # Analyzer minmax 154 155 minmax: replace if/else statements with calls to min or max 156 157 The minmax analyzer simplifies conditional assignments by suggesting the use 158 of the built-in `min` and `max` functions, introduced in Go 1.21. For example, 159 160 if a < b { x = a } else { x = b } 161 162 is replaced by 163 164 x = min(a, b). 165 166 This analyzer avoids making suggestions for floating-point types, 167 as the behavior of `min` and `max` with NaN values can differ from 168 the original if/else statement. 169 170 # Analyzer newexpr 171 172 newexpr: simplify code by using go1.26's new(expr) 173 174 This analyzer finds declarations of functions of this form: 175 176 func varOf(x int) *int { return &x } 177 178 and suggests a fix to turn them into inlinable wrappers around 179 go1.26's built-in new(expr) function: 180 181 //go:fix inline 182 func varOf(x int) *int { return new(x) } 183 184 (The directive comment causes the 'inline' analyzer to suggest 185 that calls to such functions are inlined.) 186 187 In addition, this analyzer suggests a fix for each call 188 to one of the functions before it is transformed, so that 189 190 use(varOf(123)) 191 192 is replaced by: 193 194 use(new(123)) 195 196 Wrapper functions such as varOf are common when working with Go 197 serialization packages such as for JSON or protobuf, where pointers 198 are often used to express optionality. 199 200 # Analyzer omitzero 201 202 omitzero: suggest replacing omitempty with omitzero for struct fields 203 204 The omitzero analyzer identifies uses of the `omitempty` JSON struct 205 tag on fields that are themselves structs. For struct-typed fields, 206 the `omitempty` tag has no effect on the behavior of json.Marshal and 207 json.Unmarshal. The analyzer offers two suggestions: either remove the 208 tag, or replace it with `omitzero` (added in Go 1.24), which correctly 209 omits the field if the struct value is zero. 210 211 However, some other serialization packages (notably kubebuilder, see 212 https://book.kubebuilder.io/reference/markers.html) may have their own 213 interpretation of the `json:",omitzero"` tag, so removing it may affect 214 program behavior. For this reason, the omitzero modernizer will not 215 make changes in any package that contains +kubebuilder annotations. 216 217 Replacing `omitempty` with `omitzero` is a change in behavior. The 218 original code would always encode the struct field, whereas the 219 modified code will omit it if it is a zero-value. 220 221 # Analyzer plusbuild 222 223 plusbuild: remove obsolete //+build comments 224 225 The plusbuild analyzer suggests a fix to remove obsolete build tags 226 of the form: 227 228 //+build linux,amd64 229 230 in files that also contain a Go 1.18-style tag such as: 231 232 //go:build linux && amd64 233 234 (It does not check that the old and new tags are consistent; 235 that is the job of the 'buildtag' analyzer in the vet suite.) 236 237 # Analyzer rangeint 238 239 rangeint: replace 3-clause for loops with for-range over integers 240 241 The rangeint analyzer suggests replacing traditional for loops such 242 as 243 244 for i := 0; i < n; i++ { ... } 245 246 with the more idiomatic Go 1.22 style: 247 248 for i := range n { ... } 249 250 This transformation is applied only if (a) the loop variable is not 251 modified within the loop body and (b) the loop's limit expression 252 is not modified within the loop, as `for range` evaluates its 253 operand only once. 254 255 # Analyzer reflecttypefor 256 257 reflecttypefor: replace reflect.TypeOf(x) with TypeFor[T]() 258 259 This analyzer suggests fixes to replace uses of reflect.TypeOf(x) with 260 reflect.TypeFor, introduced in go1.22, when the desired runtime type 261 is known at compile time, for example: 262 263 reflect.TypeOf(uint32(0)) -> reflect.TypeFor[uint32]() 264 reflect.TypeOf((*ast.File)(nil)) -> reflect.TypeFor[*ast.File]() 265 266 It also offers a fix to simplify the construction below, which uses 267 reflect.TypeOf to return the runtime type for an interface type, 268 269 reflect.TypeOf((*io.Reader)(nil)).Elem() 270 271 to: 272 273 reflect.TypeFor[io.Reader]() 274 275 No fix is offered in cases when the runtime type is dynamic, such as: 276 277 var r io.Reader = ... 278 reflect.TypeOf(r) 279 280 or when the operand has potential side effects. 281 282 # Analyzer slicescontains 283 284 slicescontains: replace loops with slices.Contains or slices.ContainsFunc 285 286 The slicescontains analyzer simplifies loops that check for the existence of 287 an element in a slice. It replaces them with calls to `slices.Contains` or 288 `slices.ContainsFunc`, which were added in Go 1.21. 289 290 If the expression for the target element has side effects, this 291 transformation will cause those effects to occur only once, not 292 once per tested slice element. 293 294 # Analyzer slicesdelete 295 296 slicesdelete: replace append-based slice deletion with slices.Delete 297 298 The slicesdelete analyzer suggests replacing the idiom 299 300 s = append(s[:i], s[j:]...) 301 302 with the more explicit 303 304 s = slices.Delete(s, i, j) 305 306 introduced in Go 1.21. 307 308 This analyzer is disabled by default. The `slices.Delete` function 309 zeros the elements between the new length and the old length of the 310 slice to prevent memory leaks, which is a subtle difference in 311 behavior compared to the append-based idiom; see https://go.dev/issue/73686. 312 313 # Analyzer slicessort 314 315 slicessort: replace sort.Slice with slices.Sort for basic types 316 317 The slicessort analyzer simplifies sorting slices of basic ordered 318 types. It replaces 319 320 sort.Slice(s, func(i, j int) bool { return s[i] < s[j] }) 321 322 with the simpler `slices.Sort(s)`, which was added in Go 1.21. 323 324 # Analyzer stditerators 325 326 stditerators: use iterators instead of Len/At-style APIs 327 328 This analyzer suggests a fix to replace each loop of the form: 329 330 for i := 0; i < x.Len(); i++ { 331 use(x.At(i)) 332 } 333 334 or its "for elem := range x.Len()" equivalent by a range loop over an 335 iterator offered by the same data type: 336 337 for elem := range x.All() { 338 use(x.At(i) 339 } 340 341 where x is one of various well-known types in the standard library. 342 343 # Analyzer stringscut 344 345 stringscut: replace strings.Index etc. with strings.Cut 346 347 This analyzer replaces certain patterns of use of [strings.Index] and string slicing by [strings.Cut], added in go1.18. 348 349 For example: 350 351 idx := strings.Index(s, substr) 352 if idx >= 0 { 353 return s[:idx] 354 } 355 356 is replaced by: 357 358 before, _, ok := strings.Cut(s, substr) 359 if ok { 360 return before 361 } 362 363 And: 364 365 idx := strings.Index(s, substr) 366 if idx >= 0 { 367 return 368 } 369 370 is replaced by: 371 372 found := strings.Contains(s, substr) 373 if found { 374 return 375 } 376 377 It also handles variants using [strings.IndexByte] instead of Index, or the bytes package instead of strings. 378 379 Fixes are offered only in cases in which there are no potential modifications of the idx, s, or substr expressions between their definition and use. 380 381 # Analyzer stringscutprefix 382 383 stringscutprefix: replace HasPrefix/TrimPrefix with CutPrefix 384 385 The stringscutprefix analyzer simplifies a common pattern where code first 386 checks for a prefix with `strings.HasPrefix` and then removes it with 387 `strings.TrimPrefix`. It replaces this two-step process with a single call 388 to `strings.CutPrefix`, introduced in Go 1.20. The analyzer also handles 389 the equivalent functions in the `bytes` package. 390 391 For example, this input: 392 393 if strings.HasPrefix(s, prefix) { 394 use(strings.TrimPrefix(s, prefix)) 395 } 396 397 is fixed to: 398 399 if after, ok := strings.CutPrefix(s, prefix); ok { 400 use(after) 401 } 402 403 The analyzer also offers fixes to use CutSuffix in a similar way. 404 This input: 405 406 if strings.HasSuffix(s, suffix) { 407 use(strings.TrimSuffix(s, suffix)) 408 } 409 410 is fixed to: 411 412 if before, ok := strings.CutSuffix(s, suffix); ok { 413 use(before) 414 } 415 416 # Analyzer stringsseq 417 418 stringsseq: replace ranging over Split/Fields with SplitSeq/FieldsSeq 419 420 The stringsseq analyzer improves the efficiency of iterating over substrings. 421 It replaces 422 423 for range strings.Split(...) 424 425 with the more efficient 426 427 for range strings.SplitSeq(...) 428 429 which was added in Go 1.24 and avoids allocating a slice for the 430 substrings. The analyzer also handles strings.Fields and the 431 equivalent functions in the bytes package. 432 433 # Analyzer stringsbuilder 434 435 stringsbuilder: replace += with strings.Builder 436 437 This analyzer replaces repeated string += string concatenation 438 operations with calls to Go 1.10's strings.Builder. 439 440 For example: 441 442 var s = "[" 443 for x := range seq { 444 s += x 445 s += "." 446 } 447 s += "]" 448 use(s) 449 450 is replaced by: 451 452 var s strings.Builder 453 s.WriteString("[") 454 for x := range seq { 455 s.WriteString(x) 456 s.WriteString(".") 457 } 458 s.WriteString("]") 459 use(s.String()) 460 461 This avoids quadratic memory allocation and improves performance. 462 463 The analyzer requires that all references to s except the final one 464 are += operations. To avoid warning about trivial cases, at least one 465 must appear within a loop. The variable s must be a local 466 variable, not a global or parameter. 467 468 The sole use of the finished string must be the last reference to the 469 variable s. (It may appear within an intervening loop or function literal, 470 since even s.String() is called repeatedly, it does not allocate memory.) 471 472 Often the addend is a call to fmt.Sprintf, as in this example: 473 474 var s string 475 for x := range seq { 476 s += fmt.Sprintf("%v", x) 477 } 478 479 which, once the suggested fix is applied, becomes: 480 481 var s strings.Builder 482 for x := range seq { 483 s.WriteString(fmt.Sprintf("%v", x)) 484 } 485 486 The WriteString call can be further simplified to the more efficient 487 fmt.Fprintf(&s, "%v", x), avoiding the allocation of an intermediary. 488 However, stringsbuilder does not perform this simplification; 489 it requires staticcheck analyzer QF1012. (See https://go.dev/issue/76918.) 490 491 # Analyzer testingcontext 492 493 testingcontext: replace context.WithCancel with t.Context in tests 494 495 The testingcontext analyzer simplifies context management in tests. It 496 replaces the manual creation of a cancellable context, 497 498 ctx, cancel := context.WithCancel(context.Background()) 499 defer cancel() 500 501 with a single call to t.Context(), which was added in Go 1.24. 502 503 This change is only suggested if the `cancel` function is not used 504 for any other purpose. 505 506 # Analyzer unsafefuncs 507 508 unsafefuncs: replace unsafe pointer arithmetic with function calls 509 510 The unsafefuncs analyzer simplifies pointer arithmetic expressions by 511 replacing them with calls to helper functions such as unsafe.Add, 512 added in Go 1.17. 513 514 Example: 515 516 unsafe.Pointer(uintptr(ptr) + uintptr(n)) 517 518 where ptr is an unsafe.Pointer, is replaced by: 519 520 unsafe.Add(ptr, n) 521 522 # Analyzer waitgroup 523 524 waitgroup: replace wg.Add(1)/go/wg.Done() with wg.Go 525 526 The waitgroup analyzer simplifies goroutine management with `sync.WaitGroup`. 527 It replaces the common pattern 528 529 wg.Add(1) 530 go func() { 531 defer wg.Done() 532 ... 533 }() 534 535 with a single call to 536 537 wg.Go(func(){ ... }) 538 539 which was added in Go 1.25. 540 */ 541 package modernize 542