Source file src/cmd/vendor/golang.org/x/tools/go/analysis/passes/modernize/modernize.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  package modernize
     6  
     7  import (
     8  	_ "embed"
     9  	"go/ast"
    10  	"go/constant"
    11  	"go/format"
    12  	"go/token"
    13  	"go/types"
    14  	"iter"
    15  	"regexp"
    16  	"strings"
    17  
    18  	"golang.org/x/tools/go/analysis"
    19  	"golang.org/x/tools/go/analysis/passes/inspect"
    20  	"golang.org/x/tools/go/ast/edge"
    21  	"golang.org/x/tools/go/ast/inspector"
    22  	"golang.org/x/tools/internal/analysis/analyzerutil"
    23  	"golang.org/x/tools/internal/astutil"
    24  	"golang.org/x/tools/internal/moreiters"
    25  	"golang.org/x/tools/internal/packagepath"
    26  	"golang.org/x/tools/internal/stdlib"
    27  	"golang.org/x/tools/internal/typesinternal"
    28  )
    29  
    30  //go:embed doc.go
    31  var doc string
    32  
    33  // Suite lists all modernize analyzers.
    34  var Suite = []*analysis.Analyzer{
    35  	AnyAnalyzer,
    36  	// AppendClippedAnalyzer, // not nil-preserving!
    37  	// BLoopAnalyzer, // may skew benchmark results, see golang/go#74967
    38  	FmtAppendfAnalyzer,
    39  	ForVarAnalyzer,
    40  	MapsLoopAnalyzer,
    41  	MinMaxAnalyzer,
    42  	NewExprAnalyzer,
    43  	OmitZeroAnalyzer,
    44  	plusBuildAnalyzer,
    45  	RangeIntAnalyzer,
    46  	ReflectTypeForAnalyzer,
    47  	SlicesContainsAnalyzer,
    48  	// SlicesDeleteAnalyzer, // not nil-preserving!
    49  	SlicesSortAnalyzer,
    50  	stditeratorsAnalyzer,
    51  	stringscutAnalyzer,
    52  	StringsCutPrefixAnalyzer,
    53  	StringsSeqAnalyzer,
    54  	StringsBuilderAnalyzer,
    55  	TestingContextAnalyzer,
    56  	WaitGroupAnalyzer,
    57  }
    58  
    59  // -- helpers --
    60  
    61  // formatExprs formats a comma-separated list of expressions.
    62  func formatExprs(fset *token.FileSet, exprs []ast.Expr) string {
    63  	var buf strings.Builder
    64  	for i, e := range exprs {
    65  		if i > 0 {
    66  			buf.WriteString(",  ")
    67  		}
    68  		format.Node(&buf, fset, e) // ignore errors
    69  	}
    70  	return buf.String()
    71  }
    72  
    73  // isZeroIntConst reports whether e is an integer whose value is 0.
    74  func isZeroIntConst(info *types.Info, e ast.Expr) bool {
    75  	return isIntLiteral(info, e, 0)
    76  }
    77  
    78  // isIntLiteral reports whether e is an integer with given value.
    79  func isIntLiteral(info *types.Info, e ast.Expr, n int64) bool {
    80  	return info.Types[e].Value == constant.MakeInt64(n)
    81  }
    82  
    83  // filesUsingGoVersion returns a cursor for each *ast.File in the inspector
    84  // that uses at least the specified version of Go (e.g. "go1.24").
    85  //
    86  // The pass's analyzer must require [inspect.Analyzer].
    87  //
    88  // TODO(adonovan): opt: eliminate this function, instead following the
    89  // approach of [fmtappendf], which uses typeindex and
    90  // [analyzerutil.FileUsesGoVersion]; see "Tip" documented at the
    91  // latter function for motivation.
    92  func filesUsingGoVersion(pass *analysis.Pass, version string) iter.Seq[inspector.Cursor] {
    93  	inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
    94  
    95  	return func(yield func(inspector.Cursor) bool) {
    96  		for curFile := range inspect.Root().Children() {
    97  			file := curFile.Node().(*ast.File)
    98  			if analyzerutil.FileUsesGoVersion(pass, file, version) && !yield(curFile) {
    99  				break
   100  			}
   101  		}
   102  	}
   103  }
   104  
   105  // within reports whether the current pass is analyzing one of the
   106  // specified standard packages or their dependencies.
   107  func within(pass *analysis.Pass, pkgs ...string) bool {
   108  	path := pass.Pkg.Path()
   109  	return packagepath.IsStdPackage(path) &&
   110  		moreiters.Contains(stdlib.Dependencies(pkgs...), path)
   111  }
   112  
   113  // unparenEnclosing removes enclosing parens from cur in
   114  // preparation for a call to [Cursor.ParentEdge].
   115  func unparenEnclosing(cur inspector.Cursor) inspector.Cursor {
   116  	for astutil.IsChildOf(cur, edge.ParenExpr_X) {
   117  		cur = cur.Parent()
   118  	}
   119  	return cur
   120  }
   121  
   122  var (
   123  	builtinAny     = types.Universe.Lookup("any")
   124  	builtinAppend  = types.Universe.Lookup("append")
   125  	builtinBool    = types.Universe.Lookup("bool")
   126  	builtinInt     = types.Universe.Lookup("int")
   127  	builtinFalse   = types.Universe.Lookup("false")
   128  	builtinLen     = types.Universe.Lookup("len")
   129  	builtinMake    = types.Universe.Lookup("make")
   130  	builtinNew     = types.Universe.Lookup("new")
   131  	builtinNil     = types.Universe.Lookup("nil")
   132  	builtinString  = types.Universe.Lookup("string")
   133  	builtinTrue    = types.Universe.Lookup("true")
   134  	byteSliceType  = types.NewSlice(types.Typ[types.Byte])
   135  	omitemptyRegex = regexp.MustCompile(`(?:^json| json):"[^"]*(,omitempty)(?:"|,[^"]*")\s?`)
   136  )
   137  
   138  // lookup returns the symbol denoted by name at the position of the cursor.
   139  func lookup(info *types.Info, cur inspector.Cursor, name string) types.Object {
   140  	scope := typesinternal.EnclosingScope(info, cur)
   141  	_, obj := scope.LookupParent(name, cur.Node().Pos())
   142  	return obj
   143  }
   144  

View as plain text