Source file src/cmd/vendor/golang.org/x/tools/go/analysis/passes/inline/inline.go

     1  // Copyright 2023 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 inline
     6  
     7  import (
     8  	"fmt"
     9  	"go/ast"
    10  	"go/types"
    11  	"slices"
    12  	"strings"
    13  
    14  	_ "embed"
    15  
    16  	"golang.org/x/tools/go/analysis"
    17  	"golang.org/x/tools/go/analysis/passes/inspect"
    18  	"golang.org/x/tools/go/analysis/passes/internal/gofixdirective"
    19  	"golang.org/x/tools/go/ast/edge"
    20  	"golang.org/x/tools/go/ast/inspector"
    21  	"golang.org/x/tools/go/types/typeutil"
    22  	"golang.org/x/tools/internal/analysis/analyzerutil"
    23  	typeindexanalyzer "golang.org/x/tools/internal/analysis/typeindex"
    24  	"golang.org/x/tools/internal/astutil"
    25  	"golang.org/x/tools/internal/moreiters"
    26  	"golang.org/x/tools/internal/packagepath"
    27  	"golang.org/x/tools/internal/refactor"
    28  	"golang.org/x/tools/internal/refactor/inline"
    29  	"golang.org/x/tools/internal/typesinternal"
    30  	"golang.org/x/tools/internal/typesinternal/typeindex"
    31  )
    32  
    33  //go:embed doc.go
    34  var doc string
    35  
    36  var Analyzer = &analysis.Analyzer{
    37  	Name: "inline",
    38  	Doc:  analyzerutil.MustExtractDoc(doc, "inline"),
    39  	URL:  "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/inline",
    40  	Run:  run,
    41  	FactTypes: []analysis.Fact{
    42  		(*goFixInlineFuncFact)(nil),
    43  		(*goFixInlineConstFact)(nil),
    44  		(*goFixInlineAliasFact)(nil),
    45  	},
    46  	Requires: []*analysis.Analyzer{
    47  		inspect.Analyzer,
    48  		typeindexanalyzer.Analyzer,
    49  	},
    50  }
    51  
    52  var (
    53  	allowBindingDecl bool
    54  	lazyEdits        bool
    55  )
    56  
    57  func init() {
    58  	Analyzer.Flags.BoolVar(&allowBindingDecl, "allow_binding_decl", false,
    59  		"permit inlinings that require a 'var params = args' declaration")
    60  	Analyzer.Flags.BoolVar(&lazyEdits, "lazy_edits", false,
    61  		"compute edits lazily (only meaningful to gopls driver)")
    62  }
    63  
    64  // analyzer holds the state for this analysis.
    65  type analyzer struct {
    66  	pass  *analysis.Pass
    67  	root  inspector.Cursor
    68  	index *typeindex.Index
    69  	// memoization of repeated calls for same file.
    70  	fileContent map[string][]byte
    71  	// memoization of fact imports (nil => no fact)
    72  	inlinableFuncs   map[*types.Func]*inline.Callee
    73  	inlinableConsts  map[*types.Const]*goFixInlineConstFact
    74  	inlinableAliases map[*types.TypeName]*goFixInlineAliasFact
    75  }
    76  
    77  func run(pass *analysis.Pass) (any, error) {
    78  	a := &analyzer{
    79  		pass:             pass,
    80  		root:             pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Root(),
    81  		index:            pass.ResultOf[typeindexanalyzer.Analyzer].(*typeindex.Index),
    82  		fileContent:      make(map[string][]byte),
    83  		inlinableFuncs:   make(map[*types.Func]*inline.Callee),
    84  		inlinableConsts:  make(map[*types.Const]*goFixInlineConstFact),
    85  		inlinableAliases: make(map[*types.TypeName]*goFixInlineAliasFact),
    86  	}
    87  	gofixdirective.Find(pass, a.root, a)
    88  	a.inline()
    89  	return nil, nil
    90  }
    91  
    92  // HandleFunc exports a fact for functions marked with go:fix.
    93  func (a *analyzer) HandleFunc(decl *ast.FuncDecl) {
    94  	content, err := a.readFile(decl)
    95  	if err != nil {
    96  		a.pass.Reportf(decl.Doc.Pos(), "invalid inlining candidate: cannot read source file: %v", err)
    97  		return
    98  	}
    99  	callee, err := inline.AnalyzeCallee(discard, a.pass.Fset, a.pass.Pkg, a.pass.TypesInfo, decl, content)
   100  	if err != nil {
   101  		a.pass.Reportf(decl.Doc.Pos(), "invalid inlining candidate: %v", err)
   102  		return
   103  	}
   104  	fn := a.pass.TypesInfo.Defs[decl.Name].(*types.Func)
   105  	a.pass.ExportObjectFact(fn, &goFixInlineFuncFact{callee})
   106  	a.inlinableFuncs[fn] = callee
   107  }
   108  
   109  // HandleAlias exports a fact for aliases marked with go:fix.
   110  func (a *analyzer) HandleAlias(spec *ast.TypeSpec) {
   111  	// Remember that this is an inlinable alias.
   112  	typ := &goFixInlineAliasFact{}
   113  	lhs := a.pass.TypesInfo.Defs[spec.Name].(*types.TypeName)
   114  	a.inlinableAliases[lhs] = typ
   115  	// Create a fact only if the LHS is exported and defined at top level.
   116  	// We create a fact even if the RHS is non-exported,
   117  	// so we can warn about uses in other packages.
   118  	if lhs.Exported() && typesinternal.IsPackageLevel(lhs) {
   119  		a.pass.ExportObjectFact(lhs, typ)
   120  	}
   121  }
   122  
   123  // HandleConst exports a fact for constants marked with go:fix.
   124  func (a *analyzer) HandleConst(nameIdent, rhsIdent *ast.Ident) {
   125  	lhs := a.pass.TypesInfo.Defs[nameIdent].(*types.Const)
   126  	rhs := a.pass.TypesInfo.Uses[rhsIdent].(*types.Const) // must be so in a well-typed program
   127  	con := &goFixInlineConstFact{
   128  		RHSName:    rhs.Name(),
   129  		RHSPkgName: rhs.Pkg().Name(),
   130  		RHSPkgPath: rhs.Pkg().Path(),
   131  	}
   132  	if rhs.Pkg() == a.pass.Pkg {
   133  		con.rhsObj = rhs
   134  	}
   135  	a.inlinableConsts[lhs] = con
   136  	// Create a fact only if the LHS is exported and defined at top level.
   137  	// We create a fact even if the RHS is non-exported,
   138  	// so we can warn about uses in other packages.
   139  	if lhs.Exported() && typesinternal.IsPackageLevel(lhs) {
   140  		a.pass.ExportObjectFact(lhs, con)
   141  	}
   142  }
   143  
   144  // inline inlines each static call to an inlinable function
   145  // and each reference to an inlinable constant or type alias.
   146  func (a *analyzer) inline() {
   147  	for cur := range a.root.Preorder((*ast.CallExpr)(nil), (*ast.Ident)(nil)) {
   148  		switch n := cur.Node().(type) {
   149  		case *ast.CallExpr:
   150  			a.inlineCall(n, cur)
   151  
   152  		case *ast.Ident:
   153  			switch t := a.pass.TypesInfo.Uses[n].(type) {
   154  			case *types.TypeName:
   155  				a.inlineAlias(t, cur)
   156  			case *types.Const:
   157  				a.inlineConst(t, cur)
   158  			}
   159  		}
   160  	}
   161  }
   162  
   163  // If call is a call to an inlinable func, suggest inlining its use at cur.
   164  func (a *analyzer) inlineCall(call *ast.CallExpr, cur inspector.Cursor) {
   165  	if fn := typeutil.StaticCallee(a.pass.TypesInfo, call); fn != nil {
   166  		// Inlinable?
   167  		callee, ok := a.inlinableFuncs[fn]
   168  		if !ok {
   169  			var fact goFixInlineFuncFact
   170  			if a.pass.ImportObjectFact(fn, &fact) {
   171  				callee = fact.Callee
   172  				a.inlinableFuncs[fn] = callee
   173  			}
   174  		}
   175  		if callee == nil {
   176  			return // nope
   177  		}
   178  
   179  		if a.withinTestOf(cur, fn) {
   180  			return // don't inline a function from within its own test
   181  		}
   182  
   183  		// Compute the edits.
   184  		//
   185  		// Ordinarily the analyzer reports a fix containing
   186  		// edits. However, the algorithm is somewhat expensive
   187  		// (unnecessarily so: see go.dev/issue/75773) so
   188  		// to reduce costs in gopls, we omit the edits,
   189  		// meaning that gopls must compute them on demand
   190  		// (based on the Diagnostic.Category) when they are
   191  		// requested via a code action.
   192  		//
   193  		// This does mean that the following categories of
   194  		// caller-dependent obstacles to inlining will be
   195  		// reported when the gopls user requests the fix,
   196  		// rather than by quietly suppressing the diagnostic:
   197  		// - shadowing problems
   198  		// - callee imports inaccessible "internal" packages
   199  		// - callee refers to nonexported symbols
   200  		// - callee uses too-new Go features
   201  		// - inlining call from a cgo file
   202  		var edits []analysis.TextEdit
   203  		if !lazyEdits {
   204  			// Inline the call.
   205  			caller := &inline.Caller{
   206  				Fset:  a.pass.Fset,
   207  				Types: a.pass.Pkg,
   208  				Info:  a.pass.TypesInfo,
   209  				File:  astutil.EnclosingFile(cur),
   210  				Call:  call,
   211  				CountUses: func(pkgname *types.PkgName) int {
   212  					return moreiters.Len(a.index.Uses(pkgname))
   213  				},
   214  			}
   215  			res, err := inline.Inline(caller, callee, &inline.Options{Logf: discard})
   216  			if err != nil {
   217  				a.pass.Reportf(call.Lparen, "%v", err)
   218  				return
   219  			}
   220  
   221  			if res.Literalized {
   222  				// Users are not fond of inlinings that literalize
   223  				// f(x) to func() { ... }(), so avoid them.
   224  				//
   225  				// (Unfortunately the inliner is very timid,
   226  				// and often literalizes when it cannot prove that
   227  				// reducing the call is safe; the user of this tool
   228  				// has no indication of what the problem is.)
   229  				return
   230  			}
   231  			if res.BindingDecl && !allowBindingDecl {
   232  				// When applying fix en masse, users are similarly
   233  				// unenthusiastic about inlinings that cannot
   234  				// entirely eliminate the parameters and
   235  				// insert a 'var params = args' declaration.
   236  				// The flag allows them to decline such fixes.
   237  				return
   238  			}
   239  			edits = res.Edits
   240  		}
   241  
   242  		a.pass.Report(analysis.Diagnostic{
   243  			Pos:      call.Pos(),
   244  			End:      call.End(),
   245  			Message:  fmt.Sprintf("Call of %v should be inlined", callee),
   246  			Category: "inline_call", // keep consistent with gopls/internal/golang.fixInlineCall
   247  			SuggestedFixes: []analysis.SuggestedFix{{
   248  				Message:   fmt.Sprintf("Inline call of %v", callee),
   249  				TextEdits: edits, // within gopls, this is nil => compute fix's edits lazily
   250  			}},
   251  		})
   252  	}
   253  }
   254  
   255  // withinTestOf reports whether cur is within a dedicated test
   256  // function for the inlinable target function.
   257  // A call within its dedicated test should not be inlined.
   258  func (a *analyzer) withinTestOf(cur inspector.Cursor, target *types.Func) bool {
   259  	curFuncDecl, ok := moreiters.First(cur.Enclosing((*ast.FuncDecl)(nil)))
   260  	if !ok {
   261  		return false // not in a function
   262  	}
   263  	funcDecl := curFuncDecl.Node().(*ast.FuncDecl)
   264  	if funcDecl.Recv != nil {
   265  		return false // not a test func
   266  	}
   267  	if strings.TrimSuffix(a.pass.Pkg.Path(), "_test") != target.Pkg().Path() {
   268  		return false // different package
   269  	}
   270  	if !strings.HasSuffix(a.pass.Fset.File(funcDecl.Pos()).Name(), "_test.go") {
   271  		return false // not a test file
   272  	}
   273  
   274  	// Computed expected SYMBOL portion of "TestSYMBOL_comment"
   275  	// for the target symbol.
   276  	symbol := target.Name()
   277  	if recv := target.Signature().Recv(); recv != nil {
   278  		_, named := typesinternal.ReceiverNamed(recv)
   279  		symbol = named.Obj().Name() + "_" + symbol
   280  	}
   281  
   282  	// TODO(adonovan): use a proper Test function parser.
   283  	fname := funcDecl.Name.Name
   284  	for _, pre := range []string{"Test", "Example", "Bench"} {
   285  		if fname == pre+symbol || strings.HasPrefix(fname, pre+symbol+"_") {
   286  			return true
   287  		}
   288  	}
   289  
   290  	return false
   291  }
   292  
   293  // If tn is the TypeName of an inlinable alias, suggest inlining its use at cur.
   294  func (a *analyzer) inlineAlias(tn *types.TypeName, curId inspector.Cursor) {
   295  	inalias, ok := a.inlinableAliases[tn]
   296  	if !ok {
   297  		var fact goFixInlineAliasFact
   298  		if a.pass.ImportObjectFact(tn, &fact) {
   299  			inalias = &fact
   300  			a.inlinableAliases[tn] = inalias
   301  		}
   302  	}
   303  	if inalias == nil {
   304  		return // nope
   305  	}
   306  
   307  	alias := tn.Type().(*types.Alias)
   308  	// Remember the names of the alias's type params. When we check for shadowing
   309  	// later, we'll ignore these because they won't appear in the replacement text.
   310  	typeParamNames := map[*types.TypeName]bool{}
   311  	for tp := range alias.TypeParams().TypeParams() {
   312  		typeParamNames[tp.Obj()] = true
   313  	}
   314  	rhs := alias.Rhs()
   315  	curPath := a.pass.Pkg.Path()
   316  	curFile := astutil.EnclosingFile(curId)
   317  	id := curId.Node().(*ast.Ident)
   318  
   319  	// Find the complete identifier, which may take any of these forms:
   320  	//       Id
   321  	//       Id[T]
   322  	//       Id[K, V]
   323  	//   pkg.Id
   324  	//   pkg.Id[T]
   325  	//   pkg.Id[K, V]
   326  	var expr ast.Expr = id
   327  	if astutil.IsChildOf(curId, edge.SelectorExpr_Sel) {
   328  		curId = curId.Parent()
   329  		expr = curId.Node().(ast.Expr)
   330  	}
   331  	// If expr is part of an IndexExpr or IndexListExpr, we'll need that node.
   332  	// Given C[int], TypeOf(C) is generic but TypeOf(C[int]) is instantiated.
   333  	switch ek, _ := curId.ParentEdge(); ek {
   334  	case edge.IndexExpr_X:
   335  		expr = curId.Parent().Node().(*ast.IndexExpr)
   336  	case edge.IndexListExpr_X:
   337  		expr = curId.Parent().Node().(*ast.IndexListExpr)
   338  	}
   339  	t := a.pass.TypesInfo.TypeOf(expr).(*types.Alias) // type of entire identifier
   340  	if targs := t.TypeArgs(); targs.Len() > 0 {
   341  		// Instantiate the alias with the type args from this use.
   342  		// For example, given type A = M[K, V], compute the type of the use
   343  		// A[int, Foo] as M[int, Foo].
   344  		// Don't validate instantiation: it can't panic unless we have a bug,
   345  		// in which case seeing the stack trace via telemetry would be helpful.
   346  		instAlias, _ := types.Instantiate(nil, alias, slices.Collect(targs.Types()), false)
   347  		rhs = instAlias.(*types.Alias).Rhs()
   348  	}
   349  
   350  	// We have an identifier A here (n), possibly qualified by a package
   351  	// identifier (sel.n), and an inlinable "type A = rhs" elsewhere.
   352  	//
   353  	// We can replace A with rhs if no name in rhs is shadowed at n's position,
   354  	// and every package in rhs is importable by the current package.
   355  	var (
   356  		importPrefixes = map[string]string{curPath: ""} // from pkg path to prefix
   357  		edits          []analysis.TextEdit
   358  	)
   359  	for _, tn := range typenames(rhs) {
   360  		// Ignore the type parameters of the alias: they won't appear in the result.
   361  		if typeParamNames[tn] {
   362  			continue
   363  		}
   364  		var pkgPath, pkgName string
   365  		if pkg := tn.Pkg(); pkg != nil {
   366  			pkgPath = pkg.Path()
   367  			pkgName = pkg.Name()
   368  		}
   369  		if pkgPath == "" || pkgPath == curPath {
   370  			// The name is in the current package or the universe scope, so no import
   371  			// is required. Check that it is not shadowed (that is, that the type
   372  			// it refers to in rhs is the same one it refers to at n).
   373  			scope := a.pass.TypesInfo.Scopes[curFile].Innermost(id.Pos()) // n's scope
   374  			_, obj := scope.LookupParent(tn.Name(), id.Pos())             // what qn.name means in n's scope
   375  			if obj != tn {
   376  				return
   377  			}
   378  		} else if !packagepath.CanImport(a.pass.Pkg.Path(), pkgPath) {
   379  			// If this package can't see the package of this part of rhs, we can't inline.
   380  			return
   381  		} else if _, ok := importPrefixes[pkgPath]; !ok {
   382  			// Use AddImport to add pkgPath if it's not there already. Associate the prefix it assigns
   383  			// with the prefix it assigns
   384  			// with the package path for use by the TypeString qualifier below.
   385  			prefix, eds := refactor.AddImport(
   386  				a.pass.TypesInfo, curFile, pkgName, pkgPath, tn.Name(), id.Pos())
   387  			importPrefixes[pkgPath] = strings.TrimSuffix(prefix, ".")
   388  			edits = append(edits, eds...)
   389  		}
   390  	}
   391  
   392  	// To get the replacement text, render the alias RHS using the package prefixes
   393  	// we assigned above.
   394  	newText := types.TypeString(rhs, func(p *types.Package) string {
   395  		if p == a.pass.Pkg {
   396  			return ""
   397  		}
   398  		if prefix, ok := importPrefixes[p.Path()]; ok {
   399  			return prefix
   400  		}
   401  		panic(fmt.Sprintf("in %q, package path %q has no import prefix", rhs, p.Path()))
   402  	})
   403  	a.reportInline("type alias", "Type alias", expr, edits, newText)
   404  }
   405  
   406  // typenames returns the TypeNames for types within t (including t itself) that have
   407  // them: basic types, named types and alias types.
   408  // The same name may appear more than once.
   409  func typenames(t types.Type) []*types.TypeName {
   410  	var tns []*types.TypeName
   411  
   412  	var visit func(types.Type)
   413  	visit = func(t types.Type) {
   414  		if hasName, ok := t.(interface{ Obj() *types.TypeName }); ok {
   415  			tns = append(tns, hasName.Obj())
   416  		}
   417  		switch t := t.(type) {
   418  		case *types.Basic:
   419  			tns = append(tns, types.Universe.Lookup(t.Name()).(*types.TypeName))
   420  		case *types.Named:
   421  			for t := range t.TypeArgs().Types() {
   422  				visit(t)
   423  			}
   424  		case *types.Alias:
   425  			for t := range t.TypeArgs().Types() {
   426  				visit(t)
   427  			}
   428  		case *types.TypeParam:
   429  			tns = append(tns, t.Obj())
   430  		case *types.Pointer:
   431  			visit(t.Elem())
   432  		case *types.Slice:
   433  			visit(t.Elem())
   434  		case *types.Array:
   435  			visit(t.Elem())
   436  		case *types.Chan:
   437  			visit(t.Elem())
   438  		case *types.Map:
   439  			visit(t.Key())
   440  			visit(t.Elem())
   441  		case *types.Struct:
   442  			for field := range t.Fields() {
   443  				visit(field.Type())
   444  			}
   445  		case *types.Signature:
   446  			// Ignore the receiver: although it may be present, it has no meaning
   447  			// in a type expression.
   448  			// Ditto for receiver type params.
   449  			// Also, function type params cannot appear in a type expression.
   450  			if t.TypeParams() != nil {
   451  				panic("Signature.TypeParams in type expression")
   452  			}
   453  			visit(t.Params())
   454  			visit(t.Results())
   455  		case *types.Interface:
   456  			for etyp := range t.EmbeddedTypes() {
   457  				visit(etyp)
   458  			}
   459  			for method := range t.ExplicitMethods() {
   460  				visit(method.Type())
   461  			}
   462  		case *types.Tuple:
   463  			for v := range t.Variables() {
   464  				visit(v.Type())
   465  			}
   466  		case *types.Union:
   467  			panic("Union in type expression")
   468  		default:
   469  			panic(fmt.Sprintf("unknown type %T", t))
   470  		}
   471  	}
   472  
   473  	visit(t)
   474  
   475  	return tns
   476  }
   477  
   478  // If con is an inlinable constant, suggest inlining its use at cur.
   479  func (a *analyzer) inlineConst(con *types.Const, cur inspector.Cursor) {
   480  	incon, ok := a.inlinableConsts[con]
   481  	if !ok {
   482  		var fact goFixInlineConstFact
   483  		if a.pass.ImportObjectFact(con, &fact) {
   484  			incon = &fact
   485  			a.inlinableConsts[con] = incon
   486  		}
   487  	}
   488  	if incon == nil {
   489  		return // nope
   490  	}
   491  
   492  	// If n is qualified by a package identifier, we'll need the full selector expression.
   493  	curFile := astutil.EnclosingFile(cur)
   494  	n := cur.Node().(*ast.Ident)
   495  
   496  	// We have an identifier A here (n), possibly qualified by a package identifier (sel.X,
   497  	// where sel is the parent of n), // and an inlinable "const A = B" elsewhere (incon).
   498  	// Consider replacing A with B.
   499  
   500  	// Check that the expression we are inlining (B) means the same thing
   501  	// (refers to the same object) in n's scope as it does in A's scope.
   502  	// If the RHS is not in the current package, AddImport will handle
   503  	// shadowing, so we only need to worry about when both expressions
   504  	// are in the current package.
   505  	if a.pass.Pkg.Path() == incon.RHSPkgPath {
   506  		// incon.rhsObj is the object referred to by B in the definition of A.
   507  		scope := a.pass.TypesInfo.Scopes[curFile].Innermost(n.Pos()) // n's scope
   508  		_, obj := scope.LookupParent(incon.RHSName, n.Pos())         // what "B" means in n's scope
   509  		if obj == nil {
   510  			// Should be impossible: if code at n can refer to the LHS,
   511  			// it can refer to the RHS.
   512  			panic(fmt.Sprintf("no object for inlinable const %s RHS %s", n.Name, incon.RHSName))
   513  		}
   514  		if obj != incon.rhsObj {
   515  			// "B" means something different here than at the inlinable const's scope.
   516  			return
   517  		}
   518  	} else if !packagepath.CanImport(a.pass.Pkg.Path(), incon.RHSPkgPath) {
   519  		// If this package can't see the RHS's package, we can't inline.
   520  		return
   521  	}
   522  	var (
   523  		importPrefix string
   524  		edits        []analysis.TextEdit
   525  	)
   526  	if incon.RHSPkgPath != a.pass.Pkg.Path() {
   527  		importPrefix, edits = refactor.AddImport(
   528  			a.pass.TypesInfo, curFile, incon.RHSPkgName, incon.RHSPkgPath, incon.RHSName, n.Pos())
   529  	}
   530  	// If n is qualified by a package identifier, we'll need the full selector expression.
   531  	var expr ast.Expr = n
   532  	if astutil.IsChildOf(cur, edge.SelectorExpr_Sel) {
   533  		expr = cur.Parent().Node().(ast.Expr)
   534  	}
   535  	a.reportInline("constant", "Constant", expr, edits, importPrefix+incon.RHSName)
   536  }
   537  
   538  // reportInline reports a diagnostic for fixing an inlinable name.
   539  func (a *analyzer) reportInline(kind, capKind string, ident ast.Expr, edits []analysis.TextEdit, newText string) {
   540  	edits = append(edits, analysis.TextEdit{
   541  		Pos:     ident.Pos(),
   542  		End:     ident.End(),
   543  		NewText: []byte(newText),
   544  	})
   545  	name := astutil.Format(a.pass.Fset, ident)
   546  	a.pass.Report(analysis.Diagnostic{
   547  		Pos:     ident.Pos(),
   548  		End:     ident.End(),
   549  		Message: fmt.Sprintf("%s %s should be inlined", capKind, name),
   550  		SuggestedFixes: []analysis.SuggestedFix{{
   551  			Message:   fmt.Sprintf("Inline %s %s", kind, name),
   552  			TextEdits: edits,
   553  		}},
   554  	})
   555  }
   556  
   557  func (a *analyzer) readFile(node ast.Node) ([]byte, error) {
   558  	filename := a.pass.Fset.File(node.Pos()).Name()
   559  	content, ok := a.fileContent[filename]
   560  	if !ok {
   561  		var err error
   562  		content, err = a.pass.ReadFile(filename)
   563  		if err != nil {
   564  			return nil, err
   565  		}
   566  		a.fileContent[filename] = content
   567  	}
   568  	return content, nil
   569  }
   570  
   571  // A goFixInlineFuncFact is exported for each function marked "//go:fix inline".
   572  // It holds information about the callee to support inlining.
   573  type goFixInlineFuncFact struct{ Callee *inline.Callee }
   574  
   575  func (f *goFixInlineFuncFact) String() string { return "goFixInline " + f.Callee.String() }
   576  func (*goFixInlineFuncFact) AFact()           {}
   577  
   578  // A goFixInlineConstFact is exported for each constant marked "//go:fix inline".
   579  // It holds information about an inlinable constant. Gob-serializable.
   580  type goFixInlineConstFact struct {
   581  	// Information about "const LHSName = RHSName".
   582  	RHSName    string
   583  	RHSPkgPath string
   584  	RHSPkgName string
   585  	rhsObj     types.Object // for current package
   586  }
   587  
   588  func (c *goFixInlineConstFact) String() string {
   589  	return fmt.Sprintf("goFixInline const %q.%s", c.RHSPkgPath, c.RHSName)
   590  }
   591  
   592  func (*goFixInlineConstFact) AFact() {}
   593  
   594  // A goFixInlineAliasFact is exported for each type alias marked "//go:fix inline".
   595  // It holds no information; its mere existence demonstrates that an alias is inlinable.
   596  type goFixInlineAliasFact struct{}
   597  
   598  func (c *goFixInlineAliasFact) String() string { return "goFixInline alias" }
   599  func (*goFixInlineAliasFact) AFact()           {}
   600  
   601  func discard(string, ...any) {}
   602  

View as plain text