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  

View as plain text