package inline

import "cuelang.org/go/internal/golangorgx/tools/refactor/inline"

Package inline implements inlining of Go function calls.

The client provides information about the caller and callee, including the source text, syntax tree, and type information, and the inliner returns the modified source file for the caller, or an error if the inlining operation is invalid (for example because the function body refers to names that are inaccessible to the caller).

Although this interface demands more information from the client than might seem necessary, it enables smoother integration with existing batch and interactive tools that have their own ways of managing the processes of reading, parsing, and type-checking packages. In particular, this package does not assume that the caller and callee belong to the same token.FileSet or types.Importer realms.

There are many aspects to a function call. It is the only construct that can simultaneously bind multiple variables of different explicit types, with implicit assignment conversions. (Neither var nor := declarations can do that.) It defines the scope of control labels, of return statements, and of defer statements. Arguments and results of function calls may be tuples even though tuples are not first-class values in Go, and a tuple-valued call expression may be "spread" across the argument list of a call or the operands of a return statement. All these unique features mean that in the general case, not everything that can be expressed by a function call can be expressed without one.

So, in general, inlining consists of modifying a function or method call expression f(a1, ..., an) so that the name of the function f is replaced ("literalized") by a literal copy of the function declaration, with free identifiers suitably modified to use the locally appropriate identifiers or perhaps constant argument values.

Inlining must not change the semantics of the call. Semantics preservation is crucial for clients such as codebase maintenance tools that automatically inline all calls to designated functions on a large scale. Such tools must not introduce subtle behavior changes. (Fully inlining a call is dynamically observable using reflection over the call stack, but this exception to the rule is explicitly allowed.)

In many cases it is possible to entirely replace ("reduce") the call by a copy of the function's body in which parameters have been replaced by arguments. The inliner supports a number of reduction strategies, and we expect this set to grow. Nonetheless, sound reduction is surprisingly tricky.

The inliner is in some ways like an optimizing compiler. A compiler is considered correct if it doesn't change the meaning of the program in translation from source language to target language. An optimizing compiler exploits the particulars of the input to generate better code, where "better" usually means more efficient. When a case is found in which it emits suboptimal code, the compiler is improved to recognize more cases, or more rules, and more exceptions to rules; this process has no end. Inlining is similar except that "better" code means tidier code. The baseline translation (literalization) is correct, but there are endless rules--and exceptions to rules--by which the output can be improved.

The following section lists some of the challenges, and ways in which they can be addressed.

More complex callee functions are inlinable with more elaborate and invasive changes to the statements surrounding the call expression.

TODO(adonovan): future work:

Index

Functions

func Inline

func Inline(logf func(string, ...any), caller *Caller, callee *Callee) ([]byte, error)

Inline inlines the called function (callee) into the function call (caller) and returns the updated, formatted content of the caller source file.

Inline does not mutate any public fields of Caller or Callee.

The log records the decision-making process.

TODO(adonovan): provide an API for clients that want structured output: a list of import additions and deletions plus one or more localized diffs (or even AST transformations, though ownership and mutation are tricky) near the call site.

Types

type Callee

type Callee struct {
	// contains filtered or unexported fields
}

A Callee holds information about an inlinable function. Gob-serializable.

func AnalyzeCallee

func AnalyzeCallee(logf func(string, ...any), fset *token.FileSet, pkg *types.Package, info *types.Info, decl *ast.FuncDecl, content []byte) (*Callee, error)

AnalyzeCallee analyzes a function that is a candidate for inlining and returns a Callee that describes it. The Callee object, which is serializable, can be passed to one or more subsequent calls to Inline, each with a different Caller.

This design allows separate analysis of callers and callees in the golang.org/x/tools/go/analysis framework: the inlining information about a callee can be recorded as a "fact".

The content should be the actual input to the compiler, not the apparent source file according to any //line directives that may be present within it.

func (*Callee) GobDecode

func (callee *Callee) GobDecode(data []byte) error

func (*Callee) GobEncode

func (callee *Callee) GobEncode() ([]byte, error)

func (*Callee) String

func (callee *Callee) String() string

type Caller

type Caller struct {
	Fset    *token.FileSet
	Types   *types.Package
	Info    *types.Info
	File    *ast.File
	Call    *ast.CallExpr
	Content []byte // source of file containing
	// contains filtered or unexported fields
}

A Caller describes the function call and its enclosing context.

The client is responsible for populating this struct and passing it to Inline.

Source Files

callee.go calleefx.go doc.go escape.go falcon.go inline.go util.go

Version
v0.12.0 (latest)
Published
Jan 30, 2025
Platform
linux/amd64
Imports
19 packages
Last checked
8 hours ago

Tools for package owners.