1
2
3
4
5 package inline
6
7
8
9 import (
10 "bytes"
11 "encoding/gob"
12 "fmt"
13 "go/ast"
14 "go/parser"
15 "go/token"
16 "go/types"
17 "slices"
18 "strings"
19
20 "golang.org/x/tools/go/types/typeutil"
21 "golang.org/x/tools/internal/astutil"
22 "golang.org/x/tools/internal/typeparams"
23 "golang.org/x/tools/internal/typesinternal"
24 )
25
26
27 type Callee struct {
28 impl gobCallee
29 }
30
31 func (callee *Callee) String() string { return callee.impl.Name }
32
33 type gobCallee struct {
34 Content []byte
35
36
37 PkgPath string
38 Name string
39 GoVersion string
40 Unexported []string
41 FreeRefs []freeRef
42 FreeObjs []object
43 ValidForCallStmt bool
44 NumResults int
45 Params []*paramInfo
46 TypeParams []*paramInfo
47 Results []*paramInfo
48 Effects []int
49 HasDefer bool
50 HasBareReturn bool
51 Returns [][]returnOperandFlags
52 Labels []string
53 Falcon falconResult
54 }
55
56
57
58 type returnOperandFlags int
59
60 const (
61 nonTrivialResult returnOperandFlags = 1 << iota
62 untypedNilResult
63 )
64
65
66
67 type freeRef struct {
68 Offset int
69 Object int
70 }
71
72
73 type object struct {
74 Name string
75 Kind string
76 PkgPath string
77 PkgName string
78
79
80 ValidPos bool
81 Shadow shadowMap
82 }
83
84
85
86
87
88
89
90
91
92
93
94
95
96 func AnalyzeCallee(logf func(string, ...any), fset *token.FileSet, pkg *types.Package, info *types.Info, decl *ast.FuncDecl, content []byte) (*Callee, error) {
97 checkInfoFields(info)
98
99
100
101 fn := info.Defs[decl.Name].(*types.Func)
102 sig := fn.Type().(*types.Signature)
103
104 logf("analyzeCallee %v @ %v", fn, fset.PositionFor(decl.Pos(), false))
105
106
107 var name string
108 if sig.Recv() == nil {
109 name = fmt.Sprintf("%s.%s", fn.Pkg().Name(), fn.Name())
110 } else {
111 name = fmt.Sprintf("(%s).%s", types.TypeString(sig.Recv().Type(), (*types.Package).Name), fn.Name())
112 }
113
114 if decl.Body == nil {
115 return nil, fmt.Errorf("cannot inline function %s as it has no body", name)
116 }
117
118
119
120
121
122
123
124
125
126
127
128 var goVersion string
129 for file, v := range info.FileVersions {
130 if file.Pos() < decl.Pos() && decl.Pos() < file.End() {
131 goVersion = v
132 break
133 }
134 }
135
136
137
138 var (
139 fieldObjs = fieldObjs(sig)
140 freeObjIndex = make(map[types.Object]int)
141 freeObjs []object
142 freeRefs []freeRef
143 unexported []string
144 )
145 var f func(n ast.Node, stack []ast.Node) bool
146 var stack []ast.Node
147 stack = append(stack, decl.Type)
148 visit := func(n ast.Node, stack []ast.Node) { astutil.PreorderStack(n, stack, f) }
149 f = func(n ast.Node, stack []ast.Node) bool {
150 switch n := n.(type) {
151 case *ast.SelectorExpr:
152
153 if sel, ok := info.Selections[n]; ok &&
154 !within(sel.Obj().Pos(), decl) &&
155 !n.Sel.IsExported() {
156 sym := fmt.Sprintf("(%s).%s", info.TypeOf(n.X), n.Sel.Name)
157 unexported = append(unexported, sym)
158 }
159
160
161 visit(n.X, stack)
162 return false
163
164 case *ast.CompositeLit:
165
166
167 litType := typeparams.Deref(info.TypeOf(n))
168 if s, ok := typeparams.CoreType(litType).(*types.Struct); ok {
169 if n.Type != nil {
170 visit(n.Type, stack)
171 }
172 for i, elt := range n.Elts {
173 var field *types.Var
174 var value ast.Expr
175 if kv, ok := elt.(*ast.KeyValueExpr); ok {
176 field = info.Uses[kv.Key.(*ast.Ident)].(*types.Var)
177 value = kv.Value
178 } else {
179 field = s.Field(i)
180 value = elt
181 }
182 if !within(field.Pos(), decl) && !field.Exported() {
183 sym := fmt.Sprintf("(%s).%s", litType, field.Name())
184 unexported = append(unexported, sym)
185 }
186
187
188 visit(value, stack)
189 }
190 return false
191 }
192
193 case *ast.Ident:
194 if obj, ok := info.Uses[n]; ok {
195
196 if isField(obj) || isMethod(obj) {
197 panic(obj)
198 }
199
200
201
202
203 if !n.IsExported() &&
204 obj.Pkg() != nil && obj.Parent() == obj.Pkg().Scope() {
205 unexported = append(unexported, n.Name)
206 }
207
208
209 if obj == fn || !within(obj.Pos(), decl) {
210 objidx, ok := freeObjIndex[obj]
211 if !ok {
212 objidx = len(freeObjIndex)
213 var pkgPath, pkgName string
214 if pn, ok := obj.(*types.PkgName); ok {
215 pkgPath = pn.Imported().Path()
216 pkgName = pn.Imported().Name()
217 } else if obj.Pkg() != nil {
218 pkgPath = obj.Pkg().Path()
219 pkgName = obj.Pkg().Name()
220 }
221 freeObjs = append(freeObjs, object{
222 Name: obj.Name(),
223 Kind: objectKind(obj),
224 PkgName: pkgName,
225 PkgPath: pkgPath,
226 ValidPos: obj.Pos().IsValid(),
227 })
228 freeObjIndex[obj] = objidx
229 }
230
231 freeObjs[objidx].Shadow = freeObjs[objidx].Shadow.add(info, fieldObjs, obj.Name(), stack)
232
233 freeRefs = append(freeRefs, freeRef{
234 Offset: int(n.Pos() - decl.Pos()),
235 Object: objidx,
236 })
237 }
238 }
239 }
240 return true
241 }
242 visit(decl, stack)
243
244
245
246
247 validForCallStmt := false
248 if len(decl.Body.List) != 1 {
249
250 } else if ret, ok := decl.Body.List[0].(*ast.ReturnStmt); ok && len(ret.Results) == 1 {
251 validForCallStmt = func() bool {
252 switch expr := ast.Unparen(ret.Results[0]).(type) {
253 case *ast.CallExpr:
254 callee := typeutil.Callee(info, expr)
255 if callee == nil {
256 return false
257 }
258
259
260
261
262
263 if builtin, ok := callee.(*types.Builtin); ok {
264 return builtin.Name() == "copy" ||
265 builtin.Name() == "recover"
266 }
267
268 return true
269
270 case *ast.UnaryExpr:
271 return expr.Op == token.ARROW
272 }
273
274
275 return false
276 }()
277 }
278
279
280
281 var (
282 hasDefer = false
283 hasBareReturn = false
284 returnInfo [][]returnOperandFlags
285 labels []string
286 )
287 ast.Inspect(decl.Body, func(n ast.Node) bool {
288 switch n := n.(type) {
289 case *ast.FuncLit:
290 return false
291 case *ast.DeferStmt:
292 hasDefer = true
293 case *ast.LabeledStmt:
294 labels = append(labels, n.Label.Name)
295 case *ast.ReturnStmt:
296
297
298
299 var resultInfo []returnOperandFlags
300 if len(n.Results) > 0 {
301 argInfo := func(i int) (ast.Expr, types.Type) {
302 expr := n.Results[i]
303 return expr, info.TypeOf(expr)
304 }
305 if len(n.Results) == 1 && sig.Results().Len() > 1 {
306
307 tuple := info.TypeOf(n.Results[0]).(*types.Tuple)
308 argInfo = func(i int) (ast.Expr, types.Type) {
309 return nil, tuple.At(i).Type()
310 }
311 }
312 for i := range sig.Results().Len() {
313 expr, typ := argInfo(i)
314 var flags returnOperandFlags
315 if typ == types.Typ[types.UntypedNil] {
316 flags |= untypedNilResult
317 }
318 if !trivialConversion(info.Types[expr].Value, typ, sig.Results().At(i).Type()) {
319 flags |= nonTrivialResult
320 }
321 resultInfo = append(resultInfo, flags)
322 }
323 } else if sig.Results().Len() > 0 {
324 hasBareReturn = true
325 }
326 returnInfo = append(returnInfo, resultInfo)
327 }
328 return true
329 })
330
331
332 for _, obj := range freeObjs {
333
334
335 if strings.HasPrefix(obj.Name, "_Cfunc_") ||
336 strings.HasPrefix(obj.Name, "_Ctype_") ||
337 strings.HasPrefix(obj.Name, "_Cvar_") {
338 return nil, fmt.Errorf("cannot inline cgo-generated functions")
339 }
340 }
341
342
343
344
345
346
347
348
349
350
351 content = append([]byte("package _\n"),
352 content[offsetOf(fset, decl.Pos()):offsetOf(fset, decl.End())]...)
353
354 if _, _, err := parseCompact(content); err != nil {
355 return nil, err
356 }
357
358 params, results, effects, falcon := analyzeParams(logf, fset, info, decl)
359 tparams := analyzeTypeParams(logf, fset, info, decl)
360 return &Callee{gobCallee{
361 Content: content,
362 PkgPath: pkg.Path(),
363 Name: name,
364 GoVersion: goVersion,
365 Unexported: unexported,
366 FreeObjs: freeObjs,
367 FreeRefs: freeRefs,
368 ValidForCallStmt: validForCallStmt,
369 NumResults: sig.Results().Len(),
370 Params: params,
371 TypeParams: tparams,
372 Results: results,
373 Effects: effects,
374 HasDefer: hasDefer,
375 HasBareReturn: hasBareReturn,
376 Returns: returnInfo,
377 Labels: labels,
378 Falcon: falcon,
379 }}, nil
380 }
381
382
383
384 func parseCompact(content []byte) (*token.FileSet, *ast.FuncDecl, error) {
385 fset := token.NewFileSet()
386 const mode = parser.ParseComments | parser.SkipObjectResolution | parser.AllErrors
387 f, err := parser.ParseFile(fset, "callee.go", content, mode)
388 if err != nil {
389 return nil, nil, fmt.Errorf("internal error: cannot compact file: %v", err)
390 }
391 return fset, f.Decls[0].(*ast.FuncDecl), nil
392 }
393
394
395 type paramInfo struct {
396 Name string
397 Index int
398 IsResult bool
399 IsInterface bool
400 Assigned bool
401 Escapes bool
402 Refs []refInfo
403 Shadow shadowMap
404 FalconType string
405 }
406
407 type refInfo struct {
408 Offset int
409 Assignable bool
410 IfaceAssignment bool
411 AffectsInference bool
412
413
414
415
416 IsSelectionOperand bool
417 }
418
419
420
421
422
423
424
425
426 func analyzeParams(logf func(string, ...any), fset *token.FileSet, info *types.Info, decl *ast.FuncDecl) (params, results []*paramInfo, effects []int, _ falconResult) {
427 sig := signature(fset, info, decl)
428
429 paramInfos := make(map[*types.Var]*paramInfo)
430 {
431 newParamInfo := func(param *types.Var, isResult bool) *paramInfo {
432 info := ¶mInfo{
433 Name: param.Name(),
434 IsResult: isResult,
435 Index: len(paramInfos),
436 IsInterface: isNonTypeParamInterface(param.Type()),
437 }
438 paramInfos[param] = info
439 return info
440 }
441 if sig.Recv() != nil {
442 params = append(params, newParamInfo(sig.Recv(), false))
443 }
444 for v := range sig.Params().Variables() {
445 params = append(params, newParamInfo(v, false))
446 }
447 for v := range sig.Results().Variables() {
448 results = append(results, newParamInfo(v, true))
449 }
450 }
451
452
453
454 escape(info, decl, func(v *types.Var, escapes bool) {
455 if info := paramInfos[v]; info != nil {
456 if escapes {
457 info.Escapes = true
458 } else {
459 info.Assigned = true
460 }
461 }
462 })
463
464
465
466
467
468
469 fieldObjs := fieldObjs(sig)
470 var stack []ast.Node
471 stack = append(stack, decl.Type)
472 astutil.PreorderStack(decl.Body, stack, func(n ast.Node, stack []ast.Node) bool {
473 if id, ok := n.(*ast.Ident); ok {
474 if v, ok := info.Uses[id].(*types.Var); ok {
475 if pinfo, ok := paramInfos[v]; ok {
476
477
478
479
480
481
482
483
484
485
486
487 stack = append(stack, n)
488 assignable, ifaceAssign, affectsInference := analyzeAssignment(info, stack)
489 ref := refInfo{
490 Offset: int(n.Pos() - decl.Pos()),
491 Assignable: assignable,
492 IfaceAssignment: ifaceAssign,
493 AffectsInference: affectsInference,
494 IsSelectionOperand: isSelectionOperand(stack),
495 }
496 pinfo.Refs = append(pinfo.Refs, ref)
497 pinfo.Shadow = pinfo.Shadow.add(info, fieldObjs, pinfo.Name, stack)
498 }
499 }
500 }
501 return true
502 })
503
504
505
506 effects = calleefx(info, decl.Body, paramInfos)
507 logf("effects list = %v", effects)
508
509 falcon := falcon(logf, fset, paramInfos, info, decl)
510
511 return params, results, effects, falcon
512 }
513
514
515 func analyzeTypeParams(_ logger, fset *token.FileSet, info *types.Info, decl *ast.FuncDecl) []*paramInfo {
516 sig := signature(fset, info, decl)
517 paramInfos := make(map[*types.TypeName]*paramInfo)
518 var params []*paramInfo
519 collect := func(tpl *types.TypeParamList) {
520 for tparam := range tpl.TypeParams() {
521 typeName := tparam.Obj()
522 info := ¶mInfo{Name: typeName.Name()}
523 params = append(params, info)
524 paramInfos[typeName] = info
525 }
526 }
527 collect(sig.RecvTypeParams())
528 collect(sig.TypeParams())
529
530
531
532
533
534 var stack []ast.Node
535 stack = append(stack, decl.Type)
536 astutil.PreorderStack(decl.Body, stack, func(n ast.Node, stack []ast.Node) bool {
537 if id, ok := n.(*ast.Ident); ok {
538 if v, ok := info.Uses[id].(*types.TypeName); ok {
539 if pinfo, ok := paramInfos[v]; ok {
540 ref := refInfo{Offset: int(n.Pos() - decl.Pos())}
541 pinfo.Refs = append(pinfo.Refs, ref)
542 pinfo.Shadow = pinfo.Shadow.add(info, nil, pinfo.Name, stack)
543 }
544 }
545 }
546 return true
547 })
548 return params
549 }
550
551 func signature(fset *token.FileSet, info *types.Info, decl *ast.FuncDecl) *types.Signature {
552 fnobj, ok := info.Defs[decl.Name]
553 if !ok {
554 panic(fmt.Sprintf("%s: no func object for %q",
555 fset.PositionFor(decl.Name.Pos(), false), decl.Name))
556 }
557 return fnobj.Type().(*types.Signature)
558 }
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584 func analyzeAssignment(info *types.Info, stack []ast.Node) (assignable, ifaceAssign, affectsInference bool) {
585 remaining, parent, expr := exprContext(stack)
586 if parent == nil {
587 return false, false, false
588 }
589
590
591
592
593 if assign, ok := parent.(*ast.AssignStmt); ok {
594 for i, v := range assign.Rhs {
595 if v == expr {
596 if i >= len(assign.Lhs) {
597 return false, false, false
598 }
599
600 if i < len(assign.Lhs) {
601
602
603 if id, _ := assign.Lhs[i].(*ast.Ident); id != nil && info.Defs[id] != nil {
604
605
606 return false, false, false
607 }
608
609 typ := info.TypeOf(assign.Lhs[i])
610 return true, typ == nil || types.IsInterface(typ), false
611 }
612
613 return assign.Tok == token.ASSIGN, true, false
614 }
615 }
616 }
617
618
619 if spec, ok := parent.(*ast.ValueSpec); ok && spec.Type != nil {
620 if slices.Contains(spec.Values, expr) {
621 typ := info.TypeOf(spec.Type)
622 return true, typ == nil || types.IsInterface(typ), false
623 }
624 }
625
626
627 if ix, ok := parent.(*ast.IndexExpr); ok {
628 if ix.Index == expr {
629 typ := info.TypeOf(ix.X)
630 if typ == nil {
631 return true, true, false
632 }
633 m, _ := typeparams.CoreType(typ).(*types.Map)
634 return true, m == nil || types.IsInterface(m.Key()), false
635 }
636 }
637
638
639
640 if kv, ok := parent.(*ast.KeyValueExpr); ok {
641 var under types.Type
642 if len(remaining) > 0 {
643 if complit, ok := remaining[len(remaining)-1].(*ast.CompositeLit); ok {
644 if typ := info.TypeOf(complit); typ != nil {
645
646
647
648 under = typesinternal.Unpointer(typeparams.CoreType(typ))
649 }
650 }
651 }
652 if kv.Key == expr {
653 m, _ := under.(*types.Map)
654 return true, m == nil || types.IsInterface(m.Key()), false
655 }
656 if kv.Value == expr {
657 switch under := under.(type) {
658 case interface{ Elem() types.Type }:
659 return true, types.IsInterface(under.Elem()), false
660 case *types.Struct:
661 if id, _ := kv.Key.(*ast.Ident); id != nil {
662 for field := range under.Fields() {
663 if info.Uses[id] == field {
664 return true, types.IsInterface(field.Type()), false
665 }
666 }
667 }
668 default:
669 return true, true, false
670 }
671 }
672 }
673 if lit, ok := parent.(*ast.CompositeLit); ok {
674 for i, v := range lit.Elts {
675 if v == expr {
676 typ := info.TypeOf(lit)
677 if typ == nil {
678 return true, true, false
679 }
680
681
682 under := typesinternal.Unpointer(typeparams.CoreType(typ))
683 switch under := under.(type) {
684 case interface{ Elem() types.Type }:
685 return true, types.IsInterface(under.Elem()), false
686 case *types.Struct:
687 if i < under.NumFields() {
688 return true, types.IsInterface(under.Field(i).Type()), false
689 }
690 }
691 return true, true, false
692 }
693 }
694 }
695
696
697 if send, ok := parent.(*ast.SendStmt); ok {
698 if send.Value == expr {
699 typ := info.TypeOf(send.Chan)
700 if typ == nil {
701 return true, true, false
702 }
703 ch, _ := typeparams.CoreType(typ).(*types.Chan)
704 return true, ch == nil || types.IsInterface(ch.Elem()), false
705 }
706 }
707
708
709
710
711 if call, ok := parent.(*ast.CallExpr); ok {
712 if _, ok := isConversion(info, call); ok {
713 return false, false, false
714 }
715
716 for i, arg := range call.Args {
717 if arg == expr {
718 typ := info.TypeOf(call.Fun)
719 if typ == nil {
720 return true, true, false
721 }
722 sig, _ := typeparams.CoreType(typ).(*types.Signature)
723 if sig != nil {
724
725 paramType := paramTypeAtIndex(sig, call, i)
726 ifaceAssign := paramType == nil || types.IsInterface(paramType)
727 affectsInference := false
728 switch callee := typeutil.Callee(info, call).(type) {
729 case *types.Builtin:
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761 switch callee.Name() {
762 case "new", "complex", "real", "imag", "min", "max":
763 affectsInference = true
764 }
765
766 case *types.Func:
767
768
769 if sig2 := callee.Signature(); sig2.Recv() == nil {
770 originParamType := paramTypeAtIndex(sig2, call, i)
771 affectsInference = originParamType == nil || new(typeparams.Free).Has(originParamType)
772 }
773 }
774 return true, ifaceAssign, affectsInference
775 }
776 }
777 }
778 }
779
780 return false, false, false
781 }
782
783
784
785 func paramTypeAtIndex(sig *types.Signature, call *ast.CallExpr, index int) types.Type {
786 if plen := sig.Params().Len(); sig.Variadic() && index >= plen-1 && !call.Ellipsis.IsValid() {
787 if s, ok := sig.Params().At(plen - 1).Type().(*types.Slice); ok {
788 return s.Elem()
789 }
790 } else if index < plen {
791 return sig.Params().At(index).Type()
792 }
793 return nil
794 }
795
796
797
798
799
800
801 func exprContext(stack []ast.Node) (remaining []ast.Node, parent ast.Node, expr ast.Expr) {
802 expr, _ = stack[len(stack)-1].(ast.Expr)
803 if expr == nil {
804 return nil, nil, nil
805 }
806 i := len(stack) - 2
807 for ; i >= 0; i-- {
808 if pexpr, ok := stack[i].(*ast.ParenExpr); ok {
809 expr = pexpr
810 } else {
811 parent = stack[i]
812 break
813 }
814 }
815 if parent == nil {
816 return nil, nil, nil
817 }
818
819 return stack[:i], parent, expr
820 }
821
822
823
824 func isSelectionOperand(stack []ast.Node) bool {
825 _, parent, expr := exprContext(stack)
826 if parent == nil {
827 return false
828 }
829 sel, ok := parent.(*ast.SelectorExpr)
830 return ok && sel.X == expr
831 }
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852 type shadowMap map[string]int
853
854
855
856
857
858
859
860
861 func (s shadowMap) add(info *types.Info, paramIndexes map[types.Object]int, exclude string, stack []ast.Node) shadowMap {
862 for _, n := range stack {
863 if scope := scopeFor(info, n); scope != nil {
864 for _, name := range scope.Names() {
865 if name != exclude {
866 if s == nil {
867 s = make(shadowMap)
868 }
869 obj := scope.Lookup(name)
870 if idx, ok := paramIndexes[obj]; ok {
871 s[name] = idx + 1
872 } else {
873 s[name] = -1
874 }
875 }
876 }
877 }
878 }
879 return s
880 }
881
882
883
884
885 func fieldObjs(sig *types.Signature) map[types.Object]int {
886 m := make(map[types.Object]int)
887 for i := range sig.Params().Len() {
888 if p := sig.Params().At(i); p.Name() != "" && p.Name() != "_" {
889 m[p] = i
890 }
891 }
892 return m
893 }
894
895 func isField(obj types.Object) bool {
896 if v, ok := obj.(*types.Var); ok && v.IsField() {
897 return true
898 }
899 return false
900 }
901
902 func isMethod(obj types.Object) bool {
903 if f, ok := obj.(*types.Func); ok && f.Type().(*types.Signature).Recv() != nil {
904 return true
905 }
906 return false
907 }
908
909
910
911 var (
912 _ gob.GobEncoder = (*Callee)(nil)
913 _ gob.GobDecoder = (*Callee)(nil)
914 )
915
916 func (callee *Callee) GobEncode() ([]byte, error) {
917 var out bytes.Buffer
918 if err := gob.NewEncoder(&out).Encode(callee.impl); err != nil {
919 return nil, err
920 }
921 return out.Bytes(), nil
922 }
923
924 func (callee *Callee) GobDecode(data []byte) error {
925 return gob.NewDecoder(bytes.NewReader(data)).Decode(&callee.impl)
926 }
927
View as plain text