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  	unsafeFuncsAnalyzer,
    57  	WaitGroupAnalyzer,
    58  }
    59  
    60  // -- helpers --
    61  
    62  // formatExprs formats a comma-separated list of expressions.
    63  func formatExprs(fset *token.FileSet, exprs []ast.Expr) string {
    64  	var buf strings.Builder
    65  	for i, e := range exprs {
    66  		if i > 0 {
    67  			buf.WriteString(",  ")
    68  		}
    69  		format.Node(&buf, fset, e) // ignore errors
    70  	}
    71  	return buf.String()
    72  }
    73  
    74  // isZeroIntConst reports whether e is an integer whose value is 0.
    75  func isZeroIntConst(info *types.Info, e ast.Expr) bool {
    76  	return isIntLiteral(info, e, 0)
    77  }
    78  
    79  // isIntLiteral reports whether e is an integer with given value.
    80  func isIntLiteral(info *types.Info, e ast.Expr, n int64) bool {
    81  	return info.Types[e].Value == constant.MakeInt64(n)
    82  }
    83  
    84  // filesUsingGoVersion returns a cursor for each *ast.File in the inspector
    85  // that uses at least the specified version of Go (e.g. "go1.24").
    86  //
    87  // The pass's analyzer must require [inspect.Analyzer].
    88  //
    89  // TODO(adonovan): opt: eliminate this function, instead following the
    90  // approach of [fmtappendf], which uses typeindex and
    91  // [analyzerutil.FileUsesGoVersion]; see "Tip" documented at the
    92  // latter function for motivation.
    93  func filesUsingGoVersion(pass *analysis.Pass, version string) iter.Seq[inspector.Cursor] {
    94  	inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
    95  
    96  	return func(yield func(inspector.Cursor) bool) {
    97  		for curFile := range inspect.Root().Children() {
    98  			file := curFile.Node().(*ast.File)
    99  			if analyzerutil.FileUsesGoVersion(pass, file, version) && !yield(curFile) {
   100  				break
   101  			}
   102  		}
   103  	}
   104  }
   105  
   106  // within reports whether the current pass is analyzing one of the
   107  // specified standard packages or their dependencies.
   108  func within(pass *analysis.Pass, pkgs ...string) bool {
   109  	path := pass.Pkg.Path()
   110  	return packagepath.IsStdPackage(path) &&
   111  		moreiters.Contains(stdlib.Dependencies(pkgs...), path)
   112  }
   113  
   114  // unparenEnclosing removes enclosing parens from cur in
   115  // preparation for a call to [Cursor.ParentEdge].
   116  func unparenEnclosing(cur inspector.Cursor) inspector.Cursor {
   117  	for astutil.IsChildOf(cur, edge.ParenExpr_X) {
   118  		cur = cur.Parent()
   119  	}
   120  	return cur
   121  }
   122  
   123  var (
   124  	builtinAny     = types.Universe.Lookup("any")
   125  	builtinAppend  = types.Universe.Lookup("append")
   126  	builtinBool    = types.Universe.Lookup("bool")
   127  	builtinInt     = types.Universe.Lookup("int")
   128  	builtinFalse   = types.Universe.Lookup("false")
   129  	builtinLen     = types.Universe.Lookup("len")
   130  	builtinMake    = types.Universe.Lookup("make")
   131  	builtinNew     = types.Universe.Lookup("new")
   132  	builtinNil     = types.Universe.Lookup("nil")
   133  	builtinString  = types.Universe.Lookup("string")
   134  	builtinTrue    = types.Universe.Lookup("true")
   135  	byteSliceType  = types.NewSlice(types.Typ[types.Byte])
   136  	omitemptyRegex = regexp.MustCompile(`(?:^json| json):"[^"]*(,omitempty)(?:"|,[^"]*")\s?`)
   137  )
   138  
   139  // lookup returns the symbol denoted by name at the position of the cursor.
   140  func lookup(info *types.Info, cur inspector.Cursor, name string) types.Object {
   141  	scope := typesinternal.EnclosingScope(info, cur)
   142  	_, obj := scope.LookupParent(name, cur.Node().Pos())
   143  	return obj
   144  }
   145  
   146  func first[T any](x T, _ any) T { return x }
   147  

View as plain text