@@ -40,20 +40,54 @@ type Caller struct {
40
40
enclosingFunc * ast.FuncDecl // top-level function/method enclosing the call, if any
41
41
}
42
42
43
- type unit struct {} // for representing sets as maps
43
+ // Options specifies parameters affecting the inliner algorithm.
44
+ // All fields are optional.
45
+ type Options struct {
46
+ Logf func (string , ... any ) // log output function, records decision-making process
47
+ IgnoreEffects bool // ignore potential side effects of arguments (unsound)
48
+ }
49
+
50
+ // Result holds the result of code transformation.
51
+ type Result struct {
52
+ Content []byte // formatted, transformed content of caller file
53
+ Literalized bool // chosen strategy replaced callee() with func(){...}()
54
+
55
+ // TODO(adonovan): provide an API for clients that want structured
56
+ // output: a list of import additions and deletions plus one or more
57
+ // localized diffs (or even AST transformations, though ownership and
58
+ // mutation are tricky) near the call site.
59
+ }
44
60
45
61
// Inline inlines the called function (callee) into the function call (caller)
46
62
// and returns the updated, formatted content of the caller source file.
47
63
//
48
64
// Inline does not mutate any public fields of Caller or Callee.
49
- //
50
- // The log records the decision-making process.
51
- //
52
- // TODO(adonovan): provide an API for clients that want structured
53
- // output: a list of import additions and deletions plus one or more
54
- // localized diffs (or even AST transformations, though ownership and
55
- // mutation are tricky) near the call site.
56
- func Inline (logf func (string , ... any ), caller * Caller , callee * Callee ) ([]byte , error ) {
65
+ func Inline (caller * Caller , callee * Callee , opts * Options ) (* Result , error ) {
66
+ copy := * opts // shallow copy
67
+ opts = & copy
68
+ // Set default options.
69
+ if opts .Logf == nil {
70
+ opts .Logf = func (string , ... any ) {}
71
+ }
72
+
73
+ st := & state {
74
+ caller : caller ,
75
+ callee : callee ,
76
+ opts : opts ,
77
+ }
78
+ return st .inline ()
79
+ }
80
+
81
+ // state holds the working state of the inliner.
82
+ type state struct {
83
+ caller * Caller
84
+ callee * Callee
85
+ opts * Options
86
+ }
87
+
88
+ func (st * state ) inline () (* Result , error ) {
89
+ logf , caller , callee := st .opts .Logf , st .caller , st .callee
90
+
57
91
logf ("inline %s @ %v" ,
58
92
debugFormatNode (caller .Fset , caller .Call ),
59
93
caller .Fset .PositionFor (caller .Call .Lparen , false ))
@@ -68,7 +102,7 @@ func Inline(logf func(string, ...any), caller *Caller, callee *Callee) ([]byte,
68
102
return nil , fmt .Errorf ("cannot inline calls from files that import \" C\" " )
69
103
}
70
104
71
- res , err := inline ( logf , caller , & callee . impl )
105
+ res , err := st . inlineCall ( )
72
106
if err != nil {
73
107
return nil , err
74
108
}
@@ -304,15 +338,25 @@ func Inline(logf func(string, ...any), caller *Caller, callee *Callee) ([]byte,
304
338
}
305
339
newSrc = formatted
306
340
}
307
- return newSrc , nil
341
+
342
+ literalized := false
343
+ if call , ok := res .new .(* ast.CallExpr ); ok && is [* ast.FuncLit ](call .Fun ) {
344
+ literalized = true
345
+ }
346
+
347
+ return & Result {
348
+ Content : newSrc ,
349
+ Literalized : literalized ,
350
+ }, nil
351
+
308
352
}
309
353
310
354
type newImport struct {
311
355
pkgName string
312
356
spec * ast.ImportSpec
313
357
}
314
358
315
- type result struct {
359
+ type inlineCallResult struct {
316
360
newImports []newImport
317
361
// If elideBraces is set, old is an ast.Stmt and new is an ast.BlockStmt to
318
362
// be spliced in. This allows the inlining analysis to assert that inlining
@@ -329,9 +373,7 @@ type result struct {
329
373
old , new ast.Node // e.g. replace call expr by callee function body expression
330
374
}
331
375
332
- type logger = func (string , ... any )
333
-
334
- // inline returns a pair of an old node (the call, or something
376
+ // inlineCall returns a pair of an old node (the call, or something
335
377
// enclosing it) and a new node (its replacement, which may be a
336
378
// combination of caller, callee, and new nodes), along with the set
337
379
// of new imports needed.
@@ -350,7 +392,9 @@ type logger = func(string, ...any)
350
392
// candidate for evaluating an alternative fully self-contained tree
351
393
// representation, such as any proposed solution to #20744, or even
352
394
// dst or some private fork of go/ast.)
353
- func inline (logf logger , caller * Caller , callee * gobCallee ) (* result , error ) {
395
+ func (st * state ) inlineCall () (* inlineCallResult , error ) {
396
+ logf , caller , callee := st .opts .Logf , st .caller , & st .callee .impl
397
+
354
398
checkInfoFields (caller .Info )
355
399
356
400
// Inlining of dynamic calls is not currently supported,
@@ -554,7 +598,7 @@ func inline(logf logger, caller *Caller, callee *gobCallee) (*result, error) {
554
598
objRenames [i ] = newName
555
599
}
556
600
557
- res := & result {
601
+ res := & inlineCallResult {
558
602
newImports : newImports ,
559
603
}
560
604
@@ -582,7 +626,7 @@ func inline(logf logger, caller *Caller, callee *gobCallee) (*result, error) {
582
626
583
627
// Gather the effective call arguments, including the receiver.
584
628
// Later, elements will be eliminated (=> nil) by parameter substitution.
585
- args , err := arguments (caller , calleeDecl , assign1 )
629
+ args , err := st . arguments (caller , calleeDecl , assign1 )
586
630
if err != nil {
587
631
return nil , err // e.g. implicit field selection cannot be made explicit
588
632
}
@@ -880,7 +924,7 @@ func inline(logf logger, caller *Caller, callee *gobCallee) (*result, error) {
880
924
(! needBindingDecl || (bindingDecl != nil && len (bindingDecl .names ) == 0 )) {
881
925
882
926
// Reduces to: { var (bindings); lhs... := rhs... }
883
- if newStmts , ok := assignStmts (logf , caller , stmt , callee , results ); ok {
927
+ if newStmts , ok := st . assignStmts (stmt , results ); ok {
884
928
logf ("strategy: reduce assign-context call to { return exprs }" )
885
929
clearPositions (calleeDecl .Body )
886
930
@@ -1151,7 +1195,7 @@ type argument struct {
1151
1195
//
1152
1196
// We compute type-based predicates like pure, duplicable,
1153
1197
// freevars, etc, now, before we start modifying syntax.
1154
- func arguments (caller * Caller , calleeDecl * ast.FuncDecl , assign1 func (* types.Var ) bool ) ([]* argument , error ) {
1198
+ func ( st * state ) arguments (caller * Caller , calleeDecl * ast.FuncDecl , assign1 func (* types.Var ) bool ) ([]* argument , error ) {
1155
1199
var args []* argument
1156
1200
1157
1201
callArgs := caller .Call .Args
@@ -1175,7 +1219,7 @@ func arguments(caller *Caller, calleeDecl *ast.FuncDecl, assign1 func(*types.Var
1175
1219
typ : caller .Info .TypeOf (recvArg ),
1176
1220
constant : caller .Info .Types [recvArg ].Value ,
1177
1221
pure : pure (caller .Info , assign1 , recvArg ),
1178
- effects : effects (caller .Info , recvArg ),
1222
+ effects : st . effects (caller .Info , recvArg ),
1179
1223
duplicable : duplicable (caller .Info , recvArg ),
1180
1224
freevars : freeVars (caller .Info , recvArg ),
1181
1225
}
@@ -1229,7 +1273,7 @@ func arguments(caller *Caller, calleeDecl *ast.FuncDecl, assign1 func(*types.Var
1229
1273
constant : tv .Value ,
1230
1274
spread : is [* types.Tuple ](tv .Type ), // => last
1231
1275
pure : pure (caller .Info , assign1 , expr ),
1232
- effects : effects (caller .Info , expr ),
1276
+ effects : st . effects (caller .Info , expr ),
1233
1277
duplicable : duplicable (caller .Info , expr ),
1234
1278
freevars : freeVars (caller .Info , expr ),
1235
1279
})
@@ -1911,7 +1955,7 @@ func freeishNames(free map[string]bool, t ast.Expr) {
1911
1955
// effects reports whether an expression might change the state of the
1912
1956
// program (through function calls and channel receives) and affect
1913
1957
// the evaluation of subsequent expressions.
1914
- func effects (info * types.Info , expr ast.Expr ) bool {
1958
+ func ( st * state ) effects (info * types.Info , expr ast.Expr ) bool {
1915
1959
effects := false
1916
1960
ast .Inspect (expr , func (n ast.Node ) bool {
1917
1961
switch n := n .(type ) {
@@ -1939,6 +1983,15 @@ func effects(info *types.Info, expr ast.Expr) bool {
1939
1983
}
1940
1984
return true
1941
1985
})
1986
+
1987
+ // Even if consideration of effects is not desired,
1988
+ // we continue to compute, log, and discard them.
1989
+ if st .opts .IgnoreEffects && effects {
1990
+ effects = false
1991
+ st .opts .Logf ("ignoring potential effects of argument %s" ,
1992
+ debugFormatNode (st .caller .Fset , expr ))
1993
+ }
1994
+
1942
1995
return effects
1943
1996
}
1944
1997
@@ -2766,7 +2819,9 @@ func declares(stmts []ast.Stmt) map[string]bool {
2766
2819
//
2767
2820
// Note: assignStmts may return (nil, true) if it determines that the rewritten
2768
2821
// assignment consists only of _ = nil assignments.
2769
- func assignStmts (logf logger , caller * Caller , callerStmt * ast.AssignStmt , callee * gobCallee , returnOperands []ast.Expr ) ([]ast.Stmt , bool ) {
2822
+ func (st * state ) assignStmts (callerStmt * ast.AssignStmt , returnOperands []ast.Expr ) ([]ast.Stmt , bool ) {
2823
+ logf , caller , callee := st .opts .Logf , st .caller , & st .callee .impl
2824
+
2770
2825
assert (len (callee .Returns ) == 1 , "unexpected multiple returns" )
2771
2826
resultInfo := callee .Returns [0 ]
2772
2827
@@ -3084,3 +3139,5 @@ func hasNonTrivialReturn(returnInfo [][]returnOperandFlags) bool {
3084
3139
}
3085
3140
return false
3086
3141
}
3142
+
3143
+ type unit struct {} // for representing sets as maps
0 commit comments