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  	// We have an identifier A here (n), possibly qualified by a package
   319  	// identifier (sel.n), and an inlinable "type A = rhs" elsewhere.
   320  	//
   321  	// We can replace A with rhs if no name in rhs is shadowed at n's position,
   322  	// and every package in rhs is importable by the current package.
   323  
   324  	var (
   325  		importPrefixes = map[string]string{curPath: ""} // from pkg path to prefix
   326  		edits          []analysis.TextEdit
   327  	)
   328  	for _, tn := range typenames(rhs) {
   329  		// Ignore the type parameters of the alias: they won't appear in the result.
   330  		if typeParamNames[tn] {
   331  			continue
   332  		}
   333  		var pkgPath, pkgName string
   334  		if pkg := tn.Pkg(); pkg != nil {
   335  			pkgPath = pkg.Path()
   336  			pkgName = pkg.Name()
   337  		}
   338  		if pkgPath == "" || pkgPath == curPath {
   339  			// The name is in the current package or the universe scope, so no import
   340  			// is required. Check that it is not shadowed (that is, that the type
   341  			// it refers to in rhs is the same one it refers to at n).
   342  			scope := a.pass.TypesInfo.Scopes[curFile].Innermost(id.Pos()) // n's scope
   343  			_, obj := scope.LookupParent(tn.Name(), id.Pos())             // what qn.name means in n's scope
   344  			if obj != tn {
   345  				return
   346  			}
   347  		} else if !packagepath.CanImport(a.pass.Pkg.Path(), pkgPath) {
   348  			// If this package can't see the package of this part of rhs, we can't inline.
   349  			return
   350  		} else if _, ok := importPrefixes[pkgPath]; !ok {
   351  			// Use AddImport to add pkgPath if it's not there already. Associate the prefix it assigns
   352  			// with the package path for use by the TypeString qualifier below.
   353  			prefix, eds := refactor.AddImport(
   354  				a.pass.TypesInfo, curFile, pkgName, pkgPath, tn.Name(), id.Pos())
   355  			importPrefixes[pkgPath] = strings.TrimSuffix(prefix, ".")
   356  			edits = append(edits, eds...)
   357  		}
   358  	}
   359  	// Find the complete identifier, which may take any of these forms:
   360  	//       Id
   361  	//       Id[T]
   362  	//       Id[K, V]
   363  	//   pkg.Id
   364  	//   pkg.Id[T]
   365  	//   pkg.Id[K, V]
   366  	var expr ast.Expr = id
   367  	if astutil.IsChildOf(curId, edge.SelectorExpr_Sel) {
   368  		curId = curId.Parent()
   369  		expr = curId.Node().(ast.Expr)
   370  	}
   371  	// If expr is part of an IndexExpr or IndexListExpr, we'll need that node.
   372  	// Given C[int], TypeOf(C) is generic but TypeOf(C[int]) is instantiated.
   373  	switch ek, _ := curId.ParentEdge(); ek {
   374  	case edge.IndexExpr_X:
   375  		expr = curId.Parent().Node().(*ast.IndexExpr)
   376  	case edge.IndexListExpr_X:
   377  		expr = curId.Parent().Node().(*ast.IndexListExpr)
   378  	}
   379  	t := a.pass.TypesInfo.TypeOf(expr).(*types.Alias) // type of entire identifier
   380  	if targs := t.TypeArgs(); targs.Len() > 0 {
   381  		// Instantiate the alias with the type args from this use.
   382  		// For example, given type A = M[K, V], compute the type of the use
   383  		// A[int, Foo] as M[int, Foo].
   384  		// Don't validate instantiation: it can't panic unless we have a bug,
   385  		// in which case seeing the stack trace via telemetry would be helpful.
   386  		instAlias, _ := types.Instantiate(nil, alias, slices.Collect(targs.Types()), false)
   387  		rhs = instAlias.(*types.Alias).Rhs()
   388  	}
   389  	// To get the replacement text, render the alias RHS using the package prefixes
   390  	// we assigned above.
   391  	newText := types.TypeString(rhs, func(p *types.Package) string {
   392  		if p == a.pass.Pkg {
   393  			return ""
   394  		}
   395  		if prefix, ok := importPrefixes[p.Path()]; ok {
   396  			return prefix
   397  		}
   398  		panic(fmt.Sprintf("in %q, package path %q has no import prefix", rhs, p.Path()))
   399  	})
   400  	a.reportInline("type alias", "Type alias", expr, edits, newText)
   401  }
   402  
   403  // typenames returns the TypeNames for types within t (including t itself) that have
   404  // them: basic types, named types and alias types.
   405  // The same name may appear more than once.
   406  func typenames(t types.Type) []*types.TypeName {
   407  	var tns []*types.TypeName
   408  
   409  	var visit func(types.Type)
   410  	visit = func(t types.Type) {
   411  		if hasName, ok := t.(interface{ Obj() *types.TypeName }); ok {
   412  			tns = append(tns, hasName.Obj())
   413  		}
   414  		switch t := t.(type) {
   415  		case *types.Basic:
   416  			tns = append(tns, types.Universe.Lookup(t.Name()).(*types.TypeName))
   417  		case *types.Named:
   418  			for t := range t.TypeArgs().Types() {
   419  				visit(t)
   420  			}
   421  		case *types.Alias:
   422  			for t := range t.TypeArgs().Types() {
   423  				visit(t)
   424  			}
   425  		case *types.TypeParam:
   426  			tns = append(tns, t.Obj())
   427  		case *types.Pointer:
   428  			visit(t.Elem())
   429  		case *types.Slice:
   430  			visit(t.Elem())
   431  		case *types.Array:
   432  			visit(t.Elem())
   433  		case *types.Chan:
   434  			visit(t.Elem())
   435  		case *types.Map:
   436  			visit(t.Key())
   437  			visit(t.Elem())
   438  		case *types.Struct:
   439  			for field := range t.Fields() {
   440  				visit(field.Type())
   441  			}
   442  		case *types.Signature:
   443  			// Ignore the receiver: although it may be present, it has no meaning
   444  			// in a type expression.
   445  			// Ditto for receiver type params.
   446  			// Also, function type params cannot appear in a type expression.
   447  			if t.TypeParams() != nil {
   448  				panic("Signature.TypeParams in type expression")
   449  			}
   450  			visit(t.Params())
   451  			visit(t.Results())
   452  		case *types.Interface:
   453  			for etyp := range t.EmbeddedTypes() {
   454  				visit(etyp)
   455  			}
   456  			for method := range t.ExplicitMethods() {
   457  				visit(method.Type())
   458  			}
   459  		case *types.Tuple:
   460  			for v := range t.Variables() {
   461  				visit(v.Type())
   462  			}
   463  		case *types.Union:
   464  			panic("Union in type expression")
   465  		default:
   466  			panic(fmt.Sprintf("unknown type %T", t))
   467  		}
   468  	}
   469  
   470  	visit(t)
   471  
   472  	return tns
   473  }
   474  
   475  // If con is an inlinable constant, suggest inlining its use at cur.
   476  func (a *analyzer) inlineConst(con *types.Const, cur inspector.Cursor) {
   477  	incon, ok := a.inlinableConsts[con]
   478  	if !ok {
   479  		var fact goFixInlineConstFact
   480  		if a.pass.ImportObjectFact(con, &fact) {
   481  			incon = &fact
   482  			a.inlinableConsts[con] = incon
   483  		}
   484  	}
   485  	if incon == nil {
   486  		return // nope
   487  	}
   488  
   489  	// If n is qualified by a package identifier, we'll need the full selector expression.
   490  	curFile := astutil.EnclosingFile(cur)
   491  	n := cur.Node().(*ast.Ident)
   492  
   493  	// We have an identifier A here (n), possibly qualified by a package identifier (sel.X,
   494  	// where sel is the parent of n), // and an inlinable "const A = B" elsewhere (incon).
   495  	// Consider replacing A with B.
   496  
   497  	// Check that the expression we are inlining (B) means the same thing
   498  	// (refers to the same object) in n's scope as it does in A's scope.
   499  	// If the RHS is not in the current package, AddImport will handle
   500  	// shadowing, so we only need to worry about when both expressions
   501  	// are in the current package.
   502  	if a.pass.Pkg.Path() == incon.RHSPkgPath {
   503  		// incon.rhsObj is the object referred to by B in the definition of A.
   504  		scope := a.pass.TypesInfo.Scopes[curFile].Innermost(n.Pos()) // n's scope
   505  		_, obj := scope.LookupParent(incon.RHSName, n.Pos())         // what "B" means in n's scope
   506  		if obj == nil {
   507  			// Should be impossible: if code at n can refer to the LHS,
   508  			// it can refer to the RHS.
   509  			panic(fmt.Sprintf("no object for inlinable const %s RHS %s", n.Name, incon.RHSName))
   510  		}
   511  		if obj != incon.rhsObj {
   512  			// "B" means something different here than at the inlinable const's scope.
   513  			return
   514  		}
   515  	} else if !packagepath.CanImport(a.pass.Pkg.Path(), incon.RHSPkgPath) {
   516  		// If this package can't see the RHS's package, we can't inline.
   517  		return
   518  	}
   519  	var (
   520  		importPrefix string
   521  		edits        []analysis.TextEdit
   522  	)
   523  	if incon.RHSPkgPath != a.pass.Pkg.Path() {
   524  		importPrefix, edits = refactor.AddImport(
   525  			a.pass.TypesInfo, curFile, incon.RHSPkgName, incon.RHSPkgPath, incon.RHSName, n.Pos())
   526  	}
   527  	// If n is qualified by a package identifier, we'll need the full selector expression.
   528  	var expr ast.Expr = n
   529  	if astutil.IsChildOf(cur, edge.SelectorExpr_Sel) {
   530  		expr = cur.Parent().Node().(ast.Expr)
   531  	}
   532  	a.reportInline("constant", "Constant", expr, edits, importPrefix+incon.RHSName)
   533  }
   534  
   535  // reportInline reports a diagnostic for fixing an inlinable name.
   536  func (a *analyzer) reportInline(kind, capKind string, ident ast.Expr, edits []analysis.TextEdit, newText string) {
   537  	edits = append(edits, analysis.TextEdit{
   538  		Pos:     ident.Pos(),
   539  		End:     ident.End(),
   540  		NewText: []byte(newText),
   541  	})
   542  	name := astutil.Format(a.pass.Fset, ident)
   543  	a.pass.Report(analysis.Diagnostic{
   544  		Pos:     ident.Pos(),
   545  		End:     ident.End(),
   546  		Message: fmt.Sprintf("%s %s should be inlined", capKind, name),
   547  		SuggestedFixes: []analysis.SuggestedFix{{
   548  			Message:   fmt.Sprintf("Inline %s %s", kind, name),
   549  			TextEdits: edits,
   550  		}},
   551  	})
   552  }
   553  
   554  func (a *analyzer) readFile(node ast.Node) ([]byte, error) {
   555  	filename := a.pass.Fset.File(node.Pos()).Name()
   556  	content, ok := a.fileContent[filename]
   557  	if !ok {
   558  		var err error
   559  		content, err = a.pass.ReadFile(filename)
   560  		if err != nil {
   561  			return nil, err
   562  		}
   563  		a.fileContent[filename] = content
   564  	}
   565  	return content, nil
   566  }
   567  
   568  // A goFixInlineFuncFact is exported for each function marked "//go:fix inline".
   569  // It holds information about the callee to support inlining.
   570  type goFixInlineFuncFact struct{ Callee *inline.Callee }
   571  
   572  func (f *goFixInlineFuncFact) String() string { return "goFixInline " + f.Callee.String() }
   573  func (*goFixInlineFuncFact) AFact()           {}
   574  
   575  // A goFixInlineConstFact is exported for each constant marked "//go:fix inline".
   576  // It holds information about an inlinable constant. Gob-serializable.
   577  type goFixInlineConstFact struct {
   578  	// Information about "const LHSName = RHSName".
   579  	RHSName    string
   580  	RHSPkgPath string
   581  	RHSPkgName string
   582  	rhsObj     types.Object // for current package
   583  }
   584  
   585  func (c *goFixInlineConstFact) String() string {
   586  	return fmt.Sprintf("goFixInline const %q.%s", c.RHSPkgPath, c.RHSName)
   587  }
   588  
   589  func (*goFixInlineConstFact) AFact() {}
   590  
   591  // A goFixInlineAliasFact is exported for each type alias marked "//go:fix inline".
   592  // It holds no information; its mere existence demonstrates that an alias is inlinable.
   593  type goFixInlineAliasFact struct{}
   594  
   595  func (c *goFixInlineAliasFact) String() string { return "goFixInline alias" }
   596  func (*goFixInlineAliasFact) AFact()           {}
   597  
   598  func discard(string, ...any) {}
   599  

View as plain text