File: inline.go

package info (click to toggle)
golang-golang-x-tools 1%3A0.25.0%2Bds-1
  • links: PTS, VCS
  • area: main
  • in suites: experimental, forky, sid, trixie
  • size: 22,724 kB
  • sloc: javascript: 2,027; asm: 1,645; sh: 166; yacc: 155; makefile: 49; ansic: 8
file content (3289 lines) | stat: -rw-r--r-- 104,644 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
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
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
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
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package inline

import (
	"bytes"
	"fmt"
	"go/ast"
	"go/constant"
	"go/format"
	"go/parser"
	"go/token"
	"go/types"
	pathpkg "path"
	"reflect"
	"strconv"
	"strings"

	"golang.org/x/tools/go/ast/astutil"
	"golang.org/x/tools/go/types/typeutil"
	"golang.org/x/tools/imports"
	internalastutil "golang.org/x/tools/internal/astutil"
	"golang.org/x/tools/internal/typeparams"
)

// A Caller describes the function call and its enclosing context.
//
// The client is responsible for populating this struct and passing it to Inline.
type Caller struct {
	Fset    *token.FileSet
	Types   *types.Package
	Info    *types.Info
	File    *ast.File
	Call    *ast.CallExpr
	Content []byte // source of file containing

	path          []ast.Node    // path from call to root of file syntax tree
	enclosingFunc *ast.FuncDecl // top-level function/method enclosing the call, if any
}

// Options specifies parameters affecting the inliner algorithm.
// All fields are optional.
type Options struct {
	Logf          func(string, ...any) // log output function, records decision-making process
	IgnoreEffects bool                 // ignore potential side effects of arguments (unsound)
}

// Result holds the result of code transformation.
type Result struct {
	Content     []byte // formatted, transformed content of caller file
	Literalized bool   // chosen strategy replaced callee() with func(){...}()

	// 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.
}

// 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.
func Inline(caller *Caller, callee *Callee, opts *Options) (*Result, error) {
	copy := *opts // shallow copy
	opts = &copy
	// Set default options.
	if opts.Logf == nil {
		opts.Logf = func(string, ...any) {}
	}

	st := &state{
		caller: caller,
		callee: callee,
		opts:   opts,
	}
	return st.inline()
}

// state holds the working state of the inliner.
type state struct {
	caller *Caller
	callee *Callee
	opts   *Options
}

func (st *state) inline() (*Result, error) {
	logf, caller, callee := st.opts.Logf, st.caller, st.callee

	logf("inline %s @ %v",
		debugFormatNode(caller.Fset, caller.Call),
		caller.Fset.PositionFor(caller.Call.Lparen, false))

	if !consistentOffsets(caller) {
		return nil, fmt.Errorf("internal error: caller syntax positions are inconsistent with file content (did you forget to use FileSet.PositionFor when computing the file name?)")
	}

	// TODO(adonovan): use go1.21's ast.IsGenerated.
	// Break the string literal so we can use inlining in this file. :)
	if bytes.Contains(caller.Content, []byte("// Code generated by "+"cmd/cgo; DO NOT EDIT.")) {
		return nil, fmt.Errorf("cannot inline calls from files that import \"C\"")
	}

	res, err := st.inlineCall()
	if err != nil {
		return nil, err
	}

	// Replace the call (or some node that encloses it) by new syntax.
	assert(res.old != nil, "old is nil")
	assert(res.new != nil, "new is nil")

	// A single return operand inlined to a unary
	// expression context may need parens. Otherwise:
	//    func two() int { return 1+1 }
	//    print(-two())  =>  print(-1+1) // oops!
	//
	// Usually it is not necessary to insert ParenExprs
	// as the formatter is smart enough to insert them as
	// needed by the context. But the res.{old,new}
	// substitution is done by formatting res.new in isolation
	// and then splicing its text over res.old, so the
	// formatter doesn't see the parent node and cannot do
	// the right thing. (One solution would be to always
	// format the enclosing node of old, but that requires
	// non-lossy comment handling, #20744.)
	//
	// So, we must analyze the call's context
	// to see whether ambiguity is possible.
	// For example, if the context is x[y:z], then
	// the x subtree is subject to precedence ambiguity
	// (replacing x by p+q would give p+q[y:z] which is wrong)
	// but the y and z subtrees are safe.
	if needsParens(caller.path, res.old, res.new) {
		res.new = &ast.ParenExpr{X: res.new.(ast.Expr)}
	}

	// Some reduction strategies return a new block holding the
	// callee's statements. The block's braces may be elided when
	// there is no conflict between names declared in the block
	// with those declared by the parent block, and no risk of
	// a caller's goto jumping forward across a declaration.
	//
	// This elision is only safe when the ExprStmt is beneath a
	// BlockStmt, CaseClause.Body, or CommClause.Body;
	// (see "statement theory").
	//
	// The inlining analysis may have already determined that eliding braces is
	// safe. Otherwise, we analyze its safety here.
	elideBraces := res.elideBraces
	if !elideBraces {
		if newBlock, ok := res.new.(*ast.BlockStmt); ok {
			i := nodeIndex(caller.path, res.old)
			parent := caller.path[i+1]
			var body []ast.Stmt
			switch parent := parent.(type) {
			case *ast.BlockStmt:
				body = parent.List
			case *ast.CommClause:
				body = parent.Body
			case *ast.CaseClause:
				body = parent.Body
			}
			if body != nil {
				callerNames := declares(body)

				// If BlockStmt is a function body,
				// include its receiver, params, and results.
				addFieldNames := func(fields *ast.FieldList) {
					if fields != nil {
						for _, field := range fields.List {
							for _, id := range field.Names {
								callerNames[id.Name] = true
							}
						}
					}
				}
				switch f := caller.path[i+2].(type) {
				case *ast.FuncDecl:
					addFieldNames(f.Recv)
					addFieldNames(f.Type.Params)
					addFieldNames(f.Type.Results)
				case *ast.FuncLit:
					addFieldNames(f.Type.Params)
					addFieldNames(f.Type.Results)
				}

				if len(callerLabels(caller.path)) > 0 {
					// TODO(adonovan): be more precise and reject
					// only forward gotos across the inlined block.
					logf("keeping block braces: caller uses control labels")
				} else if intersects(declares(newBlock.List), callerNames) {
					logf("keeping block braces: avoids name conflict")
				} else {
					elideBraces = true
				}
			}
		}
	}

	// Don't call replaceNode(caller.File, res.old, res.new)
	// as it mutates the caller's syntax tree.
	// Instead, splice the file, replacing the extent of the "old"
	// node by a formatting of the "new" node, and re-parse.
	// We'll fix up the imports on this new tree, and format again.
	var f *ast.File
	{
		start := offsetOf(caller.Fset, res.old.Pos())
		end := offsetOf(caller.Fset, res.old.End())
		var out bytes.Buffer
		out.Write(caller.Content[:start])
		// TODO(adonovan): might it make more sense to use
		// callee.Fset when formatting res.new?
		// The new tree is a mix of (cloned) caller nodes for
		// the argument expressions and callee nodes for the
		// function body. In essence the question is: which
		// is more likely to have comments?
		// Usually the callee body will be larger and more
		// statement-heavy than the arguments, but a
		// strategy may widen the scope of the replacement
		// (res.old) from CallExpr to, say, its enclosing
		// block, so the caller nodes dominate.
		// Precise comment handling would make this a
		// non-issue. Formatting wouldn't really need a
		// FileSet at all.
		if elideBraces {
			for i, stmt := range res.new.(*ast.BlockStmt).List {
				if i > 0 {
					out.WriteByte('\n')
				}
				if err := format.Node(&out, caller.Fset, stmt); err != nil {
					return nil, err
				}
			}
		} else {
			if err := format.Node(&out, caller.Fset, res.new); err != nil {
				return nil, err
			}
		}
		out.Write(caller.Content[end:])
		const mode = parser.ParseComments | parser.SkipObjectResolution | parser.AllErrors
		f, err = parser.ParseFile(caller.Fset, "callee.go", &out, mode)
		if err != nil {
			// Something has gone very wrong.
			logf("failed to parse <<%s>>", &out) // debugging
			return nil, err
		}
	}

	// Add new imports.
	//
	// Insert new imports after last existing import,
	// to avoid migration of pre-import comments.
	// The imports will be organized below.
	if len(res.newImports) > 0 {
		var importDecl *ast.GenDecl
		if len(f.Imports) > 0 {
			// Append specs to existing import decl
			importDecl = f.Decls[0].(*ast.GenDecl)
		} else {
			// Insert new import decl.
			importDecl = &ast.GenDecl{Tok: token.IMPORT}
			f.Decls = prepend[ast.Decl](importDecl, f.Decls...)
		}
		for _, imp := range res.newImports {
			// Check that the new imports are accessible.
			path, _ := strconv.Unquote(imp.spec.Path.Value)
			if !canImport(caller.Types.Path(), path) {
				return nil, fmt.Errorf("can't inline function %v as its body refers to inaccessible package %q", callee, path)
			}
			importDecl.Specs = append(importDecl.Specs, imp.spec)
		}
	}

	// Delete imports referenced only by caller.Call.Fun.
	//
	// (We can't let imports.Process take care of it as it may
	// mistake obsolete imports for missing new imports when the
	// names are similar, as is common during a package migration.)
	for _, specToDelete := range res.oldImports {
		for _, decl := range f.Decls {
			if decl, ok := decl.(*ast.GenDecl); ok && decl.Tok == token.IMPORT {
				decl.Specs = slicesDeleteFunc(decl.Specs, func(spec ast.Spec) bool {
					imp := spec.(*ast.ImportSpec)
					// Since we re-parsed the file, we can't match by identity;
					// instead look for syntactic equivalence.
					return imp.Path.Value == specToDelete.Path.Value &&
						(imp.Name != nil) == (specToDelete.Name != nil) &&
						(imp.Name == nil || imp.Name.Name == specToDelete.Name.Name)
				})

				// Edge case: import "foo" => import ().
				if !decl.Lparen.IsValid() {
					decl.Lparen = decl.TokPos + token.Pos(len("import"))
					decl.Rparen = decl.Lparen + 1
				}
			}
		}
	}

	var out bytes.Buffer
	if err := format.Node(&out, caller.Fset, f); err != nil {
		return nil, err
	}
	newSrc := out.Bytes()

	// Remove imports that are no longer referenced.
	//
	// It ought to be possible to compute the set of PkgNames used
	// by the "old" code, compute the free identifiers of the
	// "new" code using a syntax-only (no go/types) algorithm, and
	// see if the reduction in the number of uses of any PkgName
	// equals the number of times it appears in caller.Info.Uses,
	// indicating that it is no longer referenced by res.new.
	//
	// However, the notorious ambiguity of resolving T{F: 0} makes this
	// unreliable: without types, we can't tell whether F refers to
	// a field of struct T, or a package-level const/var of a
	// dot-imported (!) package.
	//
	// So, for now, we run imports.Process, which is
	// unsatisfactory as it has to run the go command, and it
	// looks at the user's module cache state--unnecessarily,
	// since this step cannot add new imports.
	//
	// TODO(adonovan): replace with a simpler implementation since
	// all the necessary imports are present but merely untidy.
	// That will be faster, and also less prone to nondeterminism
	// if there are bugs in our logic for import maintenance.
	//
	// However, golang.org/x/tools/internal/imports.ApplyFixes is
	// too simple as it requires the caller to have figured out
	// all the logical edits. In our case, we know all the new
	// imports that are needed (see newImports), each of which can
	// be specified as:
	//
	//   &imports.ImportFix{
	//     StmtInfo: imports.ImportInfo{path, name,
	//     IdentName: name,
	//     FixType:   imports.AddImport,
	//   }
	//
	// but we don't know which imports are made redundant by the
	// inlining itself. For example, inlining a call to
	// fmt.Println may make the "fmt" import redundant.
	//
	// Also, both imports.Process and internal/imports.ApplyFixes
	// reformat the entire file, which is not ideal for clients
	// such as gopls. (That said, the point of a canonical format
	// is arguably that any tool can reformat as needed without
	// this being inconvenient.)
	//
	// We could invoke imports.Process and parse its result,
	// compare against the original AST, compute a list of import
	// fixes, and return that too.

	// Recompute imports only if there were existing ones.
	if len(f.Imports) > 0 {
		formatted, err := imports.Process("output", newSrc, nil)
		if err != nil {
			logf("cannot reformat: %v <<%s>>", err, &out)
			return nil, err // cannot reformat (a bug?)
		}
		newSrc = formatted
	}

	literalized := false
	if call, ok := res.new.(*ast.CallExpr); ok && is[*ast.FuncLit](call.Fun) {
		literalized = true
	}

	return &Result{
		Content:     newSrc,
		Literalized: literalized,
	}, nil

}

type newImport struct {
	pkgName string
	spec    *ast.ImportSpec
}

type inlineCallResult struct {
	newImports []newImport       // to add
	oldImports []*ast.ImportSpec // to remove

	// If elideBraces is set, old is an ast.Stmt and new is an ast.BlockStmt to
	// be spliced in. This allows the inlining analysis to assert that inlining
	// the block is OK; if elideBraces is unset and old is an ast.Stmt and new is
	// an ast.BlockStmt, braces may still be elided if the post-processing
	// analysis determines that it is safe to do so.
	//
	// Ideally, it would not be necessary for the inlining analysis to "reach
	// through" to the post-processing pass in this way. Instead, inlining could
	// just set old to be an ast.BlockStmt and rewrite the entire BlockStmt, but
	// unfortunately in order to preserve comments, it is important that inlining
	// replace as little syntax as possible.
	elideBraces bool
	old, new    ast.Node // e.g. replace call expr by callee function body expression
}

// inlineCall returns a pair of an old node (the call, or something
// enclosing it) and a new node (its replacement, which may be a
// combination of caller, callee, and new nodes), along with the set
// of new imports needed.
//
// TODO(adonovan): rethink the 'result' interface. The assumption of a
// one-to-one replacement seems fragile. One can easily imagine the
// transformation replacing the call and adding new variable
// declarations, for example, or replacing a call statement by zero or
// many statements.)
//
// TODO(adonovan): in earlier drafts, the transformation was expressed
// by splicing substrings of the two source files because syntax
// trees don't preserve comments faithfully (see #20744), but such
// transformations don't compose. The current implementation is
// tree-based but is very lossy wrt comments. It would make a good
// candidate for evaluating an alternative fully self-contained tree
// representation, such as any proposed solution to #20744, or even
// dst or some private fork of go/ast.)
func (st *state) inlineCall() (*inlineCallResult, error) {
	logf, caller, callee := st.opts.Logf, st.caller, &st.callee.impl

	checkInfoFields(caller.Info)

	// Inlining of dynamic calls is not currently supported,
	// even for local closure calls. (This would be a lot of work.)
	calleeSymbol := typeutil.StaticCallee(caller.Info, caller.Call)
	if calleeSymbol == nil {
		// e.g. interface method
		return nil, fmt.Errorf("cannot inline: not a static function call")
	}

	// Reject cross-package inlining if callee has
	// free references to unexported symbols.
	samePkg := caller.Types.Path() == callee.PkgPath
	if !samePkg && len(callee.Unexported) > 0 {
		return nil, fmt.Errorf("cannot inline call to %s because body refers to non-exported %s",
			callee.Name, callee.Unexported[0])
	}

	// -- analyze callee's free references in caller context --

	// Compute syntax path enclosing Call, innermost first (Path[0]=Call),
	// and outermost enclosing function, if any.
	caller.path, _ = astutil.PathEnclosingInterval(caller.File, caller.Call.Pos(), caller.Call.End())
	for _, n := range caller.path {
		if decl, ok := n.(*ast.FuncDecl); ok {
			caller.enclosingFunc = decl
			break
		}
	}

	// If call is within a function, analyze all its
	// local vars for the "single assignment" property.
	// (Taking the address &v counts as a potential assignment.)
	var assign1 func(v *types.Var) bool // reports whether v a single-assignment local var
	{
		updatedLocals := make(map[*types.Var]bool)
		if caller.enclosingFunc != nil {
			escape(caller.Info, caller.enclosingFunc, func(v *types.Var, _ bool) {
				updatedLocals[v] = true
			})
			logf("multiple-assignment vars: %v", updatedLocals)
		}
		assign1 = func(v *types.Var) bool { return !updatedLocals[v] }
	}

	// import map, initially populated with caller imports.
	//
	// For simplicity we ignore existing dot imports, so that a
	// qualified identifier (QI) in the callee is always
	// represented by a QI in the caller, allowing us to treat a
	// QI like a selection on a package name.
	importMap := make(map[string][]string) // maps package path to local name(s)
	for _, imp := range caller.File.Imports {
		if pkgname, ok := importedPkgName(caller.Info, imp); ok &&
			pkgname.Name() != "." &&
			pkgname.Name() != "_" {
			path := pkgname.Imported().Path()
			importMap[path] = append(importMap[path], pkgname.Name())
		}
	}

	var oldImports []*ast.ImportSpec // imports referenced only caller.Call.Fun

	// localImportName returns the local name for a given imported package path.
	var newImports []newImport
	localImportName := func(obj *object) string {
		// Does an import exist?
		for _, name := range importMap[obj.PkgPath] {
			// Check that either the import preexisted,
			// or that it was newly added (no PkgName) but is not shadowed,
			// either in the callee (shadows) or caller (caller.lookup).
			if !obj.Shadow[name] {
				found := caller.lookup(name)
				if is[*types.PkgName](found) || found == nil {
					return name
				}
			}
		}

		newlyAdded := func(name string) bool {
			for _, new := range newImports {
				if new.pkgName == name {
					return true
				}
			}
			return false
		}

		// shadowedInCaller reports whether a candidate package name
		// already refers to a declaration in the caller.
		shadowedInCaller := func(name string) bool {
			existing := caller.lookup(name)

			// If the candidate refers to a PkgName p whose sole use is
			// in caller.Call.Fun of the form p.F(...), where p.F is a
			// qualified identifier, the p import will be deleted,
			// so it's safe (and better) to recycle the name.
			//
			// Only the qualified identifier case matters, as other
			// references to imported package names in the Call.Fun
			// expression (e.g. x.after(3*time.Second).f()
			// or time.Second.String()) will remain after
			// inlining, as arguments.
			if pkgName, ok := existing.(*types.PkgName); ok {
				if sel, ok := astutil.Unparen(caller.Call.Fun).(*ast.SelectorExpr); ok {
					if sole := soleUse(caller.Info, pkgName); sole == sel.X {
						for _, spec := range caller.File.Imports {
							pkgName2, ok := importedPkgName(caller.Info, spec)
							if ok && pkgName2 == pkgName {
								oldImports = append(oldImports, spec)
								return false
							}
						}
					}
				}
			}

			return existing != nil
		}

		// import added by callee
		//
		// Choose local PkgName based on last segment of
		// package path plus, if needed, a numeric suffix to
		// ensure uniqueness.
		//
		// "init" is not a legal PkgName.
		//
		// TODO(rfindley): is it worth preserving local package names for callee
		// imports? Are they likely to be better or worse than the name we choose
		// here?
		base := obj.PkgName
		name := base
		for n := 0; obj.Shadow[name] || shadowedInCaller(name) || newlyAdded(name) || name == "init"; n++ {
			name = fmt.Sprintf("%s%d", base, n)
		}

		logf("adding import %s %q", name, obj.PkgPath)
		spec := &ast.ImportSpec{
			Path: &ast.BasicLit{
				Kind:  token.STRING,
				Value: strconv.Quote(obj.PkgPath),
			},
		}
		// Use explicit pkgname (out of necessity) when it differs from the declared name,
		// or (for good style) when it differs from base(pkgpath).
		if name != obj.PkgName || name != pathpkg.Base(obj.PkgPath) {
			spec.Name = makeIdent(name)
		}
		newImports = append(newImports, newImport{
			pkgName: name,
			spec:    spec,
		})
		importMap[obj.PkgPath] = append(importMap[obj.PkgPath], name)
		return name
	}

	// Compute the renaming of the callee's free identifiers.
	objRenames := make([]ast.Expr, len(callee.FreeObjs)) // nil => no change
	for i, obj := range callee.FreeObjs {
		// obj is a free object of the callee.
		//
		// Possible cases are:
		// - builtin function, type, or value (e.g. nil, zero)
		//   => check not shadowed in caller.
		// - package-level var/func/const/types
		//   => same package: check not shadowed in caller.
		//   => otherwise: import other package, form a qualified identifier.
		//      (Unexported cross-package references were rejected already.)
		// - type parameter
		//   => not yet supported
		// - pkgname
		//   => import other package and use its local name.
		//
		// There can be no free references to labels, fields, or methods.

		// Note that we must consider potential shadowing both
		// at the caller side (caller.lookup) and, when
		// choosing new PkgNames, within the callee (obj.shadow).

		var newName ast.Expr
		if obj.Kind == "pkgname" {
			// Use locally appropriate import, creating as needed.
			newName = makeIdent(localImportName(&obj)) // imported package
		} else if !obj.ValidPos {
			// Built-in function, type, or value (e.g. nil, zero):
			// check not shadowed at caller.
			found := caller.lookup(obj.Name) // always finds something
			if found.Pos().IsValid() {
				return nil, fmt.Errorf("cannot inline, because the callee refers to built-in %q, which in the caller is shadowed by a %s (declared at line %d)",
					obj.Name, objectKind(found),
					caller.Fset.PositionFor(found.Pos(), false).Line)
			}

		} else {
			// Must be reference to package-level var/func/const/type,
			// since type parameters are not yet supported.
			qualify := false
			if obj.PkgPath == callee.PkgPath {
				// reference within callee package
				if samePkg {
					// Caller and callee are in same package.
					// Check caller has not shadowed the decl.
					//
					// This may fail if the callee is "fake", such as for signature
					// refactoring where the callee is modified to be a trivial wrapper
					// around the refactored signature.
					found := caller.lookup(obj.Name)
					if found != nil && !isPkgLevel(found) {
						return nil, fmt.Errorf("cannot inline, because the callee refers to %s %q, which in the caller is shadowed by a %s (declared at line %d)",
							obj.Kind, obj.Name,
							objectKind(found),
							caller.Fset.PositionFor(found.Pos(), false).Line)
					}
				} else {
					// Cross-package reference.
					qualify = true
				}
			} else {
				// Reference to a package-level declaration
				// in another package, without a qualified identifier:
				// it must be a dot import.
				qualify = true
			}

			// Form a qualified identifier, pkg.Name.
			if qualify {
				pkgName := localImportName(&obj)
				newName = &ast.SelectorExpr{
					X:   makeIdent(pkgName),
					Sel: makeIdent(obj.Name),
				}
			}
		}
		objRenames[i] = newName
	}

	res := &inlineCallResult{
		newImports: newImports,
		oldImports: oldImports,
	}

	// Parse callee function declaration.
	calleeFset, calleeDecl, err := parseCompact(callee.Content)
	if err != nil {
		return nil, err // "can't happen"
	}

	// replaceCalleeID replaces an identifier in the callee.
	// The replacement tree must not belong to the caller; use cloneNode as needed.
	replaceCalleeID := func(offset int, repl ast.Expr) {
		id := findIdent(calleeDecl, calleeDecl.Pos()+token.Pos(offset))
		logf("- replace id %q @ #%d to %q", id.Name, offset, debugFormatNode(calleeFset, repl))
		replaceNode(calleeDecl, id, repl)
	}

	// Generate replacements for each free identifier.
	// (The same tree may be spliced in multiple times, resulting in a DAG.)
	for _, ref := range callee.FreeRefs {
		if repl := objRenames[ref.Object]; repl != nil {
			replaceCalleeID(ref.Offset, repl)
		}
	}

	// Gather the effective call arguments, including the receiver.
	// Later, elements will be eliminated (=> nil) by parameter substitution.
	args, err := st.arguments(caller, calleeDecl, assign1)
	if err != nil {
		return nil, err // e.g. implicit field selection cannot be made explicit
	}

	// Gather effective parameter tuple, including the receiver if any.
	// Simplify variadic parameters to slices (in all cases but one).
	var params []*parameter // including receiver; nil => parameter substituted
	{
		sig := calleeSymbol.Type().(*types.Signature)
		if sig.Recv() != nil {
			params = append(params, &parameter{
				obj:       sig.Recv(),
				fieldType: calleeDecl.Recv.List[0].Type,
				info:      callee.Params[0],
			})
		}

		// Flatten the list of syntactic types.
		var types []ast.Expr
		for _, field := range calleeDecl.Type.Params.List {
			if field.Names == nil {
				types = append(types, field.Type)
			} else {
				for range field.Names {
					types = append(types, field.Type)
				}
			}
		}

		for i := 0; i < sig.Params().Len(); i++ {
			params = append(params, &parameter{
				obj:       sig.Params().At(i),
				fieldType: types[i],
				info:      callee.Params[len(params)],
			})
		}

		// Variadic function?
		//
		// There are three possible types of call:
		// - ordinary f(a1, ..., aN)
		// - ellipsis f(a1, ..., slice...)
		// - spread   f(recv?, g()) where g() is a tuple.
		// The first two are desugared to non-variadic calls
		// with an ordinary slice parameter;
		// the third is tricky and cannot be reduced, and (if
		// a receiver is present) cannot even be literalized.
		// Fortunately it is vanishingly rare.
		//
		// TODO(adonovan): extract this to a function.
		if sig.Variadic() {
			lastParam := last(params)
			if len(args) > 0 && last(args).spread {
				// spread call to variadic: tricky
				lastParam.variadic = true
			} else {
				// ordinary/ellipsis call to variadic

				// simplify decl: func(T...) -> func([]T)
				lastParamField := last(calleeDecl.Type.Params.List)
				lastParamField.Type = &ast.ArrayType{
					Elt: lastParamField.Type.(*ast.Ellipsis).Elt,
				}

				if caller.Call.Ellipsis.IsValid() {
					// ellipsis call: f(slice...) -> f(slice)
					// nop
				} else {
					// ordinary call: f(a1, ... aN) -> f([]T{a1, ..., aN})
					n := len(params) - 1
					ordinary, extra := args[:n], args[n:]
					var elts []ast.Expr
					pure, effects := true, false
					for _, arg := range extra {
						elts = append(elts, arg.expr)
						pure = pure && arg.pure
						effects = effects || arg.effects
					}
					args = append(ordinary, &argument{
						expr: &ast.CompositeLit{
							Type: lastParamField.Type,
							Elts: elts,
						},
						typ:        lastParam.obj.Type(),
						constant:   nil,
						pure:       pure,
						effects:    effects,
						duplicable: false,
						freevars:   nil, // not needed
					})
				}
			}
		}
	}

	// Log effective arguments.
	for i, arg := range args {
		logf("arg #%d: %s pure=%t effects=%t duplicable=%t free=%v type=%v",
			i, debugFormatNode(caller.Fset, arg.expr),
			arg.pure, arg.effects, arg.duplicable, arg.freevars, arg.typ)
	}

	// Note: computation below should be expressed in terms of
	// the args and params slices, not the raw material.

	// Perform parameter substitution.
	// May eliminate some elements of params/args.
	substitute(logf, caller, params, args, callee.Effects, callee.Falcon, replaceCalleeID)

	// Update the callee's signature syntax.
	updateCalleeParams(calleeDecl, params)

	// Create a var (param = arg; ...) decl for use by some strategies.
	bindingDecl := createBindingDecl(logf, caller, args, calleeDecl, callee.Results)

	var remainingArgs []ast.Expr
	for _, arg := range args {
		if arg != nil {
			remainingArgs = append(remainingArgs, arg.expr)
		}
	}

	// -- let the inlining strategies begin --
	//
	// When we commit to a strategy, we log a message of the form:
	//
	//   "strategy: reduce expr-context call to { return expr }"
	//
	// This is a terse way of saying:
	//
	//    we plan to reduce a call
	//    that appears in expression context
	//    to a function whose body is of the form { return expr }

	// TODO(adonovan): split this huge function into a sequence of
	// function calls with an error sentinel that means "try the
	// next strategy", and make sure each strategy writes to the
	// log the reason it didn't match.

	// Special case: eliminate a call to a function whose body is empty.
	// (=> callee has no results and caller is a statement.)
	//
	//    func f(params) {}
	//    f(args)
	//    => _, _ = args
	//
	if len(calleeDecl.Body.List) == 0 {
		logf("strategy: reduce call to empty body")

		// Evaluate the arguments for effects and delete the call entirely.
		stmt := callStmt(caller.path, false) // cannot fail
		res.old = stmt
		if nargs := len(remainingArgs); nargs > 0 {
			// Emit "_, _ = args" to discard results.

			// TODO(adonovan): if args is the []T{a1, ..., an}
			// literal synthesized during variadic simplification,
			// consider unwrapping it to its (pure) elements.
			// Perhaps there's no harm doing this for any slice literal.

			// Make correction for spread calls
			// f(g()) or recv.f(g()) where g() is a tuple.
			if last := last(args); last != nil && last.spread {
				nspread := last.typ.(*types.Tuple).Len()
				if len(args) > 1 { // [recv, g()]
					// A single AssignStmt cannot discard both, so use a 2-spec var decl.
					res.new = &ast.GenDecl{
						Tok: token.VAR,
						Specs: []ast.Spec{
							&ast.ValueSpec{
								Names:  []*ast.Ident{makeIdent("_")},
								Values: []ast.Expr{args[0].expr},
							},
							&ast.ValueSpec{
								Names:  blanks[*ast.Ident](nspread),
								Values: []ast.Expr{args[1].expr},
							},
						},
					}
					return res, nil
				}

				// Sole argument is spread call.
				nargs = nspread
			}

			res.new = &ast.AssignStmt{
				Lhs: blanks[ast.Expr](nargs),
				Tok: token.ASSIGN,
				Rhs: remainingArgs,
			}

		} else {
			// No remaining arguments: delete call statement entirely
			res.new = &ast.EmptyStmt{}
		}
		return res, nil
	}

	// If all parameters have been substituted and no result
	// variable is referenced, we don't need a binding decl.
	// This may enable better reduction strategies.
	allResultsUnreferenced := forall(callee.Results, func(i int, r *paramInfo) bool { return len(r.Refs) == 0 })
	needBindingDecl := !allResultsUnreferenced ||
		exists(params, func(i int, p *parameter) bool { return p != nil })

	// The two strategies below overlap for a tail call of {return exprs}:
	// The expr-context reduction is nice because it keeps the
	// caller's return stmt and merely switches its operand,
	// without introducing a new block, but it doesn't work with
	// implicit return conversions.
	//
	// TODO(adonovan): unify these cases more cleanly, allowing return-
	// operand replacement and implicit conversions, by adding
	// conversions around each return operand (if not a spread return).

	// Special case: call to { return exprs }.
	//
	// Reduces to:
	//	    { var (bindings); _, _ = exprs }
	//     or   _, _ = exprs
	//     or   expr
	//
	// If:
	// - the body is just "return expr" with trivial implicit conversions,
	//   or the caller's return type matches the callee's,
	// - all parameters and result vars can be eliminated
	//   or replaced by a binding decl,
	// then the call expression can be replaced by the
	// callee's body expression, suitably substituted.
	if len(calleeDecl.Body.List) == 1 &&
		is[*ast.ReturnStmt](calleeDecl.Body.List[0]) &&
		len(calleeDecl.Body.List[0].(*ast.ReturnStmt).Results) > 0 { // not a bare return
		results := calleeDecl.Body.List[0].(*ast.ReturnStmt).Results

		parent, grandparent := callContext(caller.path)

		// statement context
		if stmt, ok := parent.(*ast.ExprStmt); ok &&
			(!needBindingDecl || bindingDecl != nil) {
			logf("strategy: reduce stmt-context call to { return exprs }")
			clearPositions(calleeDecl.Body)

			if callee.ValidForCallStmt {
				logf("callee body is valid as statement")
				// Inv: len(results) == 1
				if !needBindingDecl {
					// Reduces to: expr
					res.old = caller.Call
					res.new = results[0]
				} else {
					// Reduces to: { var (bindings); expr }
					res.old = stmt
					res.new = &ast.BlockStmt{
						List: []ast.Stmt{
							bindingDecl.stmt,
							&ast.ExprStmt{X: results[0]},
						},
					}
				}
			} else {
				logf("callee body is not valid as statement")
				// The call is a standalone statement, but the
				// callee body is not suitable as a standalone statement
				// (f() or <-ch), explicitly discard the results:
				// Reduces to: _, _ = exprs
				discard := &ast.AssignStmt{
					Lhs: blanks[ast.Expr](callee.NumResults),
					Tok: token.ASSIGN,
					Rhs: results,
				}
				res.old = stmt
				if !needBindingDecl {
					// Reduces to: _, _ = exprs
					res.new = discard
				} else {
					// Reduces to: { var (bindings); _, _ = exprs }
					res.new = &ast.BlockStmt{
						List: []ast.Stmt{
							bindingDecl.stmt,
							discard,
						},
					}
				}
			}
			return res, nil
		}

		// Assignment context.
		//
		// If there is no binding decl, or if the binding decl declares no names,
		// an assignment a, b := f() can be reduced to a, b := x, y.
		if stmt, ok := parent.(*ast.AssignStmt); ok &&
			is[*ast.BlockStmt](grandparent) &&
			(!needBindingDecl || (bindingDecl != nil && len(bindingDecl.names) == 0)) {

			// Reduces to: { var (bindings); lhs... := rhs... }
			if newStmts, ok := st.assignStmts(stmt, results); ok {
				logf("strategy: reduce assign-context call to { return exprs }")
				clearPositions(calleeDecl.Body)

				block := &ast.BlockStmt{
					List: newStmts,
				}
				if needBindingDecl {
					block.List = prepend(bindingDecl.stmt, block.List...)
				}

				// assignStmts does not introduce new bindings, and replacing an
				// assignment only works if the replacement occurs in the same scope.
				// Therefore, we must ensure that braces are elided.
				res.elideBraces = true
				res.old = stmt
				res.new = block
				return res, nil
			}
		}

		// expression context
		if !needBindingDecl {
			clearPositions(calleeDecl.Body)

			anyNonTrivialReturns := hasNonTrivialReturn(callee.Returns)

			if callee.NumResults == 1 {
				logf("strategy: reduce expr-context call to { return expr }")
				// (includes some simple tail-calls)

				// Make implicit return conversion explicit.
				if anyNonTrivialReturns {
					results[0] = convert(calleeDecl.Type.Results.List[0].Type, results[0])
				}

				res.old = caller.Call
				res.new = results[0]
				return res, nil

			} else if !anyNonTrivialReturns {
				logf("strategy: reduce spread-context call to { return expr }")
				// There is no general way to reify conversions in a spread
				// return, hence the requirement above.
				//
				// TODO(adonovan): allow this reduction when no
				// conversion is required by the context.

				// The call returns multiple results but is
				// not a standalone call statement. It must
				// be the RHS of a spread assignment:
				//   var x, y  = f()
				//       x, y := f()
				//       x, y  = f()
				// or the sole argument to a spread call:
				//        printf(f())
				// or spread return statement:
				//        return f()
				res.old = parent
				switch context := parent.(type) {
				case *ast.AssignStmt:
					// Inv: the call must be in Rhs[0], not Lhs.
					assign := shallowCopy(context)
					assign.Rhs = results
					res.new = assign
				case *ast.ValueSpec:
					// Inv: the call must be in Values[0], not Names.
					spec := shallowCopy(context)
					spec.Values = results
					res.new = spec
				case *ast.CallExpr:
					// Inv: the call must be in Args[0], not Fun.
					call := shallowCopy(context)
					call.Args = results
					res.new = call
				case *ast.ReturnStmt:
					// Inv: the call must be Results[0].
					ret := shallowCopy(context)
					ret.Results = results
					res.new = ret
				default:
					return nil, fmt.Errorf("internal error: unexpected context %T for spread call", context)
				}
				return res, nil
			}
		}
	}

	// Special case: tail-call.
	//
	// Inlining:
	//         return f(args)
	// where:
	//         func f(params) (results) { body }
	// reduces to:
	//         { var (bindings); body }
	//         { body }
	// so long as:
	// - all parameters can be eliminated or replaced by a binding decl,
	// - call is a tail-call;
	// - all returns in body have trivial result conversions,
	//   or the caller's return type matches the callee's,
	// - there is no label conflict;
	// - no result variable is referenced by name,
	//   or implicitly by a bare return.
	//
	// The body may use defer, arbitrary control flow, and
	// multiple returns.
	//
	// TODO(adonovan): add a strategy for a 'void tail
	// call', i.e. a call statement prior to an (explicit
	// or implicit) return.
	parent, _ := callContext(caller.path)
	if ret, ok := parent.(*ast.ReturnStmt); ok &&
		len(ret.Results) == 1 &&
		tailCallSafeReturn(caller, calleeSymbol, callee) &&
		!callee.HasBareReturn &&
		(!needBindingDecl || bindingDecl != nil) &&
		!hasLabelConflict(caller.path, callee.Labels) &&
		allResultsUnreferenced {
		logf("strategy: reduce tail-call")
		body := calleeDecl.Body
		clearPositions(body)
		if needBindingDecl {
			body.List = prepend(bindingDecl.stmt, body.List...)
		}
		res.old = ret
		res.new = body
		return res, nil
	}

	// Special case: call to void function
	//
	// Inlining:
	//         f(args)
	// where:
	//	   func f(params) { stmts }
	// reduces to:
	//         { var (bindings); stmts }
	//         { stmts }
	// so long as:
	// - callee is a void function (no returns)
	// - callee does not use defer
	// - there is no label conflict between caller and callee
	// - all parameters and result vars can be eliminated
	//   or replaced by a binding decl,
	// - caller ExprStmt is in unrestricted statement context.
	if stmt := callStmt(caller.path, true); stmt != nil &&
		(!needBindingDecl || bindingDecl != nil) &&
		!callee.HasDefer &&
		!hasLabelConflict(caller.path, callee.Labels) &&
		len(callee.Returns) == 0 {
		logf("strategy: reduce stmt-context call to { stmts }")
		body := calleeDecl.Body
		var repl ast.Stmt = body
		clearPositions(repl)
		if needBindingDecl {
			body.List = prepend(bindingDecl.stmt, body.List...)
		}
		res.old = stmt
		res.new = repl
		return res, nil
	}

	// TODO(adonovan): parameterless call to { stmts; return expr }
	// from one of these contexts:
	//    x, y     = f()
	//    x, y    := f()
	//    var x, y = f()
	// =>
	//    var (x T1, y T2); { stmts; x, y = expr }
	//
	// Because the params are no longer declared simultaneously
	// we need to check that (for example) x ∉ freevars(T2),
	// in addition to the usual checks for arg/result conversions,
	// complex control, etc.
	// Also test cases where expr is an n-ary call (spread returns).

	// Literalization isn't quite infallible.
	// Consider a spread call to a method in which
	// no parameters are eliminated, e.g.
	// 	new(T).f(g())
	// where
	//  	func (recv *T) f(x, y int) { body }
	//  	func g() (int, int)
	// This would be literalized to:
	// 	func (recv *T, x, y int) { body }(new(T), g()),
	// which is not a valid argument list because g() must appear alone.
	// Reject this case for now.
	if len(args) == 2 && args[0] != nil && args[1] != nil && is[*types.Tuple](args[1].typ) {
		return nil, fmt.Errorf("can't yet inline spread call to method")
	}

	// Infallible general case: literalization.
	//
	//    func(params) { body }(args)
	//
	logf("strategy: literalization")
	funcLit := &ast.FuncLit{
		Type: calleeDecl.Type,
		Body: calleeDecl.Body,
	}

	// Literalization can still make use of a binding
	// decl as it gives a more natural reading order:
	//
	//    func() { var params = args; body }()
	//
	// TODO(adonovan): relax the allResultsUnreferenced requirement
	// by adding a parameter-only (no named results) binding decl.
	if bindingDecl != nil && allResultsUnreferenced {
		funcLit.Type.Params.List = nil
		remainingArgs = nil
		funcLit.Body.List = prepend(bindingDecl.stmt, funcLit.Body.List...)
	}

	// Emit a new call to a function literal in place of
	// the callee name, with appropriate replacements.
	newCall := &ast.CallExpr{
		Fun:      funcLit,
		Ellipsis: token.NoPos, // f(slice...) is always simplified
		Args:     remainingArgs,
	}
	clearPositions(newCall.Fun)
	res.old = caller.Call
	res.new = newCall
	return res, nil
}

type argument struct {
	expr          ast.Expr
	typ           types.Type      // may be tuple for sole non-receiver arg in spread call
	constant      constant.Value  // value of argument if constant
	spread        bool            // final arg is call() assigned to multiple params
	pure          bool            // expr is pure (doesn't read variables)
	effects       bool            // expr has effects (updates variables)
	duplicable    bool            // expr may be duplicated
	freevars      map[string]bool // free names of expr
	substitutable bool            // is candidate for substitution
}

// arguments returns the effective arguments of the call.
//
// If the receiver argument and parameter have
// different pointerness, make the "&" or "*" explicit.
//
// Also, if x.f() is shorthand for promoted method x.y.f(),
// make the .y explicit in T.f(x.y, ...).
//
// Beware that:
//
//   - a method can only be called through a selection, but only
//     the first of these two forms needs special treatment:
//
//     expr.f(args)     -> ([&*]expr, args)	MethodVal
//     T.f(recv, args)  -> (    expr, args)	MethodExpr
//
//   - the presence of a value in receiver-position in the call
//     is a property of the caller, not the callee. A method
//     (calleeDecl.Recv != nil) may be called like an ordinary
//     function.
//
//   - the types.Signatures seen by the caller (from
//     StaticCallee) and by the callee (from decl type)
//     differ in this case.
//
// In a spread call f(g()), the sole ordinary argument g(),
// always last in args, has a tuple type.
//
// We compute type-based predicates like pure, duplicable,
// freevars, etc, now, before we start modifying syntax.
func (st *state) arguments(caller *Caller, calleeDecl *ast.FuncDecl, assign1 func(*types.Var) bool) ([]*argument, error) {
	var args []*argument

	callArgs := caller.Call.Args
	if calleeDecl.Recv != nil {
		sel := astutil.Unparen(caller.Call.Fun).(*ast.SelectorExpr)
		seln := caller.Info.Selections[sel]
		var recvArg ast.Expr
		switch seln.Kind() {
		case types.MethodVal: // recv.f(callArgs)
			recvArg = sel.X
		case types.MethodExpr: // T.f(recv, callArgs)
			recvArg = callArgs[0]
			callArgs = callArgs[1:]
		}
		if recvArg != nil {
			// Compute all the type-based predicates now,
			// before we start meddling with the syntax;
			// the meddling will update them.
			arg := &argument{
				expr:       recvArg,
				typ:        caller.Info.TypeOf(recvArg),
				constant:   caller.Info.Types[recvArg].Value,
				pure:       pure(caller.Info, assign1, recvArg),
				effects:    st.effects(caller.Info, recvArg),
				duplicable: duplicable(caller.Info, recvArg),
				freevars:   freeVars(caller.Info, recvArg),
			}
			recvArg = nil // prevent accidental use

			// Move receiver argument recv.f(args) to argument list f(&recv, args).
			args = append(args, arg)

			// Make field selections explicit (recv.f -> recv.y.f),
			// updating arg.{expr,typ}.
			indices := seln.Index()
			for _, index := range indices[:len(indices)-1] {
				fld := typeparams.CoreType(typeparams.Deref(arg.typ)).(*types.Struct).Field(index)
				if fld.Pkg() != caller.Types && !fld.Exported() {
					return nil, fmt.Errorf("in %s, implicit reference to unexported field .%s cannot be made explicit",
						debugFormatNode(caller.Fset, caller.Call.Fun),
						fld.Name())
				}
				if isPointer(arg.typ) {
					arg.pure = false // implicit *ptr operation => impure
				}
				arg.expr = &ast.SelectorExpr{
					X:   arg.expr,
					Sel: makeIdent(fld.Name()),
				}
				arg.typ = fld.Type()
				arg.duplicable = false
			}

			// Make * or & explicit.
			argIsPtr := isPointer(arg.typ)
			paramIsPtr := isPointer(seln.Obj().Type().Underlying().(*types.Signature).Recv().Type())
			if !argIsPtr && paramIsPtr {
				// &recv
				arg.expr = &ast.UnaryExpr{Op: token.AND, X: arg.expr}
				arg.typ = types.NewPointer(arg.typ)
			} else if argIsPtr && !paramIsPtr {
				// *recv
				arg.expr = &ast.StarExpr{X: arg.expr}
				arg.typ = typeparams.Deref(arg.typ)
				arg.duplicable = false
				arg.pure = false
			}
		}
	}
	for _, expr := range callArgs {
		tv := caller.Info.Types[expr]
		args = append(args, &argument{
			expr:       expr,
			typ:        tv.Type,
			constant:   tv.Value,
			spread:     is[*types.Tuple](tv.Type), // => last
			pure:       pure(caller.Info, assign1, expr),
			effects:    st.effects(caller.Info, expr),
			duplicable: duplicable(caller.Info, expr),
			freevars:   freeVars(caller.Info, expr),
		})
	}

	// Re-typecheck each constant argument expression in a neutral context.
	//
	// In a call such as func(int16){}(1), the type checker infers
	// the type "int16", not "untyped int", for the argument 1,
	// because it has incorporated information from the left-hand
	// side of the assignment implicit in parameter passing, but
	// of course in a different context, the expression 1 may have
	// a different type.
	//
	// So, we must use CheckExpr to recompute the type of the
	// argument in a neutral context to find its inherent type.
	// (This is arguably a bug in go/types, but I'm pretty certain
	// I requested it be this way long ago... -adonovan)
	//
	// This is only needed for constants. Other implicit
	// assignment conversions, such as unnamed-to-named struct or
	// chan to <-chan, do not result in the type-checker imposing
	// the LHS type on the RHS value.
	for _, arg := range args {
		if arg.constant == nil {
			continue
		}
		info := &types.Info{Types: make(map[ast.Expr]types.TypeAndValue)}
		if err := types.CheckExpr(caller.Fset, caller.Types, caller.Call.Pos(), arg.expr, info); err != nil {
			return nil, err
		}
		arg.typ = info.TypeOf(arg.expr)
	}

	return args, nil
}

type parameter struct {
	obj       *types.Var // parameter var from caller's signature
	fieldType ast.Expr   // syntax of type, from calleeDecl.Type.{Recv,Params}
	info      *paramInfo // information from AnalyzeCallee
	variadic  bool       // (final) parameter is unsimplified ...T
}

// substitute implements parameter elimination by substitution.
//
// It considers each parameter and its corresponding argument in turn
// and evaluate these conditions:
//
//   - the parameter is neither address-taken nor assigned;
//   - the argument is pure;
//   - if the parameter refcount is zero, the argument must
//     not contain the last use of a local var;
//   - if the parameter refcount is > 1, the argument must be duplicable;
//   - the argument (or types.Default(argument) if it's untyped) has
//     the same type as the parameter.
//
// If all conditions are met then the parameter can be substituted and
// each reference to it replaced by the argument. In that case, the
// replaceCalleeID function is called for each reference to the
// parameter, and is provided with its relative offset and replacement
// expression (argument), and the corresponding elements of params and
// args are replaced by nil.
func substitute(logf func(string, ...any), caller *Caller, params []*parameter, args []*argument, effects []int, falcon falconResult, replaceCalleeID func(offset int, repl ast.Expr)) {
	// Inv:
	//  in        calls to     variadic, len(args) >= len(params)-1
	//  in spread calls to non-variadic, len(args) <  len(params)
	//  in spread calls to     variadic, len(args) <= len(params)
	// (In spread calls len(args) = 1, or 2 if call has receiver.)
	// Non-spread variadics have been simplified away already,
	// so the args[i] lookup is safe if we stop after the spread arg.
next:
	for i, param := range params {
		arg := args[i]
		// Check argument against parameter.
		//
		// Beware: don't use types.Info on arg since
		// the syntax may be synthetic (not created by parser)
		// and thus lacking positions and types;
		// do it earlier (see pure/duplicable/freevars).

		if arg.spread {
			// spread => last argument, but not always last parameter
			logf("keeping param %q and following ones: argument %s is spread",
				param.info.Name, debugFormatNode(caller.Fset, arg.expr))
			return // give up
		}
		assert(!param.variadic, "unsimplified variadic parameter")
		if param.info.Escapes {
			logf("keeping param %q: escapes from callee", param.info.Name)
			continue
		}
		if param.info.Assigned {
			logf("keeping param %q: assigned by callee", param.info.Name)
			continue // callee needs the parameter variable
		}
		if len(param.info.Refs) > 1 && !arg.duplicable {
			logf("keeping param %q: argument is not duplicable", param.info.Name)
			continue // incorrect or poor style to duplicate an expression
		}
		if len(param.info.Refs) == 0 {
			if arg.effects {
				logf("keeping param %q: though unreferenced, it has effects", param.info.Name)
				continue
			}

			// If the caller is within a function body,
			// eliminating an unreferenced parameter might
			// remove the last reference to a caller local var.
			if caller.enclosingFunc != nil {
				for free := range arg.freevars {
					// TODO(rfindley): we can get this 100% right by looking for
					// references among other arguments which have non-zero references
					// within the callee.
					if v, ok := caller.lookup(free).(*types.Var); ok && within(v.Pos(), caller.enclosingFunc.Body) && !isUsedOutsideCall(caller, v) {
						logf("keeping param %q: arg contains perhaps the last reference to caller local %v @ %v",
							param.info.Name, v, caller.Fset.PositionFor(v.Pos(), false))
						continue next
					}
				}
			}
		}

		// Check for shadowing.
		//
		// Consider inlining a call f(z, 1) to
		// func f(x, y int) int { z := y; return x + y + z }:
		// we can't replace x in the body by z (or any
		// expression that has z as a free identifier)
		// because there's an intervening declaration of z
		// that would shadow the caller's one.
		for free := range arg.freevars {
			if param.info.Shadow[free] {
				logf("keeping param %q: cannot replace with argument as it has free ref to %s that is shadowed", param.info.Name, free)
				continue next // shadowing conflict
			}
		}

		arg.substitutable = true // may be substituted, if effects permit
	}

	// Reject constant arguments as substitution candidates
	// if they cause violation of falcon constraints.
	checkFalconConstraints(logf, params, args, falcon)

	// As a final step, introduce bindings to resolve any
	// evaluation order hazards. This must be done last, as
	// additional subsequent bindings could introduce new hazards.
	resolveEffects(logf, args, effects)

	// The remaining candidates are safe to substitute.
	for i, param := range params {
		if arg := args[i]; arg.substitutable {

			// Wrap the argument in an explicit conversion if
			// substitution might materially change its type.
			// (We already did the necessary shadowing check
			// on the parameter type syntax.)
			//
			// This is only needed for substituted arguments. All
			// other arguments are given explicit types in either
			// a binding decl or when using the literalization
			// strategy.

			// If the types are identical, we can eliminate
			// redundant type conversions such as this:
			//
			// Callee:
			//    func f(i int32) { print(i) }
			// Caller:
			//    func g() { f(int32(1)) }
			// Inlined as:
			//    func g() { print(int32(int32(1)))
			//
			// Recall that non-trivial does not imply non-identical
			// for constant conversions; however, at this point state.arguments
			// has already re-typechecked the constant and set arg.type to
			// its (possibly "untyped") inherent type, so
			// the conversion from untyped 1 to int32 is non-trivial even
			// though both arg and param have identical types (int32).
			if len(param.info.Refs) > 0 &&
				!types.Identical(arg.typ, param.obj.Type()) &&
				!trivialConversion(arg.constant, arg.typ, param.obj.Type()) {
				arg.expr = convert(param.fieldType, arg.expr)
				logf("param %q: adding explicit %s -> %s conversion around argument",
					param.info.Name, arg.typ, param.obj.Type())
			}

			// It is safe to substitute param and replace it with arg.
			// The formatter introduces parens as needed for precedence.
			//
			// Because arg.expr belongs to the caller,
			// we clone it before splicing it into the callee tree.
			logf("replacing parameter %q by argument %q",
				param.info.Name, debugFormatNode(caller.Fset, arg.expr))
			for _, ref := range param.info.Refs {
				replaceCalleeID(ref, internalastutil.CloneNode(arg.expr).(ast.Expr))
			}
			params[i] = nil // substituted
			args[i] = nil   // substituted
		}
	}
}

// isUsedOutsideCall reports whether v is used outside of caller.Call, within
// the body of caller.enclosingFunc.
func isUsedOutsideCall(caller *Caller, v *types.Var) bool {
	used := false
	ast.Inspect(caller.enclosingFunc.Body, func(n ast.Node) bool {
		if n == caller.Call {
			return false
		}
		switch n := n.(type) {
		case *ast.Ident:
			if use := caller.Info.Uses[n]; use == v {
				used = true
			}
		case *ast.FuncType:
			// All params are used.
			for _, fld := range n.Params.List {
				for _, n := range fld.Names {
					if def := caller.Info.Defs[n]; def == v {
						used = true
					}
				}
			}
		}
		return !used // keep going until we find a use
	})
	return used
}

// checkFalconConstraints checks whether constant arguments
// are safe to substitute (e.g. s[i] -> ""[0] is not safe.)
//
// Any failed constraint causes us to reject all constant arguments as
// substitution candidates (by clearing args[i].substitution=false).
//
// TODO(adonovan): we could obtain a finer result rejecting only the
// freevars of each failed constraint, and processing constraints in
// order of increasing arity, but failures are quite rare.
func checkFalconConstraints(logf func(string, ...any), params []*parameter, args []*argument, falcon falconResult) {
	// Create a dummy package, as this is the only
	// way to create an environment for CheckExpr.
	pkg := types.NewPackage("falcon", "falcon")

	// Declare types used by constraints.
	for _, typ := range falcon.Types {
		logf("falcon env: type %s %s", typ.Name, types.Typ[typ.Kind])
		pkg.Scope().Insert(types.NewTypeName(token.NoPos, pkg, typ.Name, types.Typ[typ.Kind]))
	}

	// Declared constants and variables for for parameters.
	nconst := 0
	for i, param := range params {
		name := param.info.Name
		if name == "" {
			continue // unreferenced
		}
		arg := args[i]
		if arg.constant != nil && arg.substitutable && param.info.FalconType != "" {
			t := pkg.Scope().Lookup(param.info.FalconType).Type()
			pkg.Scope().Insert(types.NewConst(token.NoPos, pkg, name, t, arg.constant))
			logf("falcon env: const %s %s = %v", name, param.info.FalconType, arg.constant)
			nconst++
		} else {
			pkg.Scope().Insert(types.NewVar(token.NoPos, pkg, name, arg.typ))
			logf("falcon env: var %s %s", name, arg.typ)
		}
	}
	if nconst == 0 {
		return // nothing to do
	}

	// Parse and evaluate the constraints in the environment.
	fset := token.NewFileSet()
	for _, falcon := range falcon.Constraints {
		expr, err := parser.ParseExprFrom(fset, "falcon", falcon, 0)
		if err != nil {
			panic(fmt.Sprintf("failed to parse falcon constraint %s: %v", falcon, err))
		}
		if err := types.CheckExpr(fset, pkg, token.NoPos, expr, nil); err != nil {
			logf("falcon: constraint %s violated: %v", falcon, err)
			for j, arg := range args {
				if arg.constant != nil && arg.substitutable {
					logf("keeping param %q due falcon violation", params[j].info.Name)
					arg.substitutable = false
				}
			}
			break
		}
		logf("falcon: constraint %s satisfied", falcon)
	}
}

// resolveEffects marks arguments as non-substitutable to resolve
// hazards resulting from the callee evaluation order described by the
// effects list.
//
// To do this, each argument is categorized as a read (R), write (W),
// or pure. A hazard occurs when the order of evaluation of a W
// changes with respect to any R or W. Pure arguments can be
// effectively ignored, as they can be safely evaluated in any order.
//
// The callee effects list contains the index of each parameter in the
// order it is first evaluated during execution of the callee. In
// addition, the two special values R∞ and W∞ indicate the relative
// position of the callee's first non-parameter read and its first
// effects (or other unknown behavior).
// For example, the list [0 2 1 R∞ 3 W∞] for func(a, b, c, d)
// indicates that the callee referenced parameters a, c, and b,
// followed by an arbitrary read, then parameter d, and finally
// unknown behavior.
//
// When an argument is marked as not substitutable, we say that it is
// 'bound', in the sense that its evaluation occurs in a binding decl
// or literalized call. Such bindings always occur in the original
// callee parameter order.
//
// In this context, "resolving hazards" means binding arguments so
// that they are evaluated in a valid, hazard-free order. A trivial
// solution to this problem would be to bind all arguments, but of
// course that's not useful. The goal is to bind as few arguments as
// possible.
//
// The algorithm proceeds by inspecting arguments in reverse parameter
// order (right to left), preserving the invariant that every
// higher-ordered argument is either already substituted or does not
// need to be substituted. At each iteration, if there is an
// evaluation hazard in the callee effects relative to the current
// argument, the argument must be bound. Subsequently, if the argument
// is bound for any reason, each lower-ordered argument must also be
// bound if either the argument or lower-order argument is a
// W---otherwise the binding itself would introduce a hazard.
//
// Thus, after each iteration, there are no hazards relative to the
// current argument. Subsequent iterations cannot introduce hazards
// with that argument because they can result only in additional
// binding of lower-ordered arguments.
func resolveEffects(logf func(string, ...any), args []*argument, effects []int) {
	effectStr := func(effects bool, idx int) string {
		i := fmt.Sprint(idx)
		if idx == len(args) {
			i = "∞"
		}
		return string("RW"[btoi(effects)]) + i
	}
	for i := len(args) - 1; i >= 0; i-- {
		argi := args[i]
		if argi.substitutable && !argi.pure {
			// i is not bound: check whether it must be bound due to hazards.
			idx := index(effects, i)
			if idx >= 0 {
				for _, j := range effects[:idx] {
					var (
						ji int  // effective param index
						jw bool // j is a write
					)
					if j == winf || j == rinf {
						jw = j == winf
						ji = len(args)
					} else {
						jw = args[j].effects
						ji = j
					}
					if ji > i && (jw || argi.effects) { // out of order evaluation
						logf("binding argument %s: preceded by %s",
							effectStr(argi.effects, i), effectStr(jw, ji))
						argi.substitutable = false
						break
					}
				}
			}
		}
		if !argi.substitutable {
			for j := 0; j < i; j++ {
				argj := args[j]
				if argj.pure {
					continue
				}
				if (argi.effects || argj.effects) && argj.substitutable {
					logf("binding argument %s: %s is bound",
						effectStr(argj.effects, j), effectStr(argi.effects, i))
					argj.substitutable = false
				}
			}
		}
	}
}

// updateCalleeParams updates the calleeDecl syntax to remove
// substituted parameters and move the receiver (if any) to the head
// of the ordinary parameters.
func updateCalleeParams(calleeDecl *ast.FuncDecl, params []*parameter) {
	// The logic is fiddly because of the three forms of ast.Field:
	//
	//	func(int), func(x int), func(x, y int)
	//
	// Also, ensure that all remaining parameters are named
	// to avoid a mix of named/unnamed when joining (recv, params...).
	// func (T) f(int, bool) -> (_ T, _ int, _ bool)
	// (Strictly, we need do this only for methods and only when
	// the namednesses of Recv and Params differ; that might be tidier.)

	paramIdx := 0 // index in original parameter list (incl. receiver)
	var newParams []*ast.Field
	filterParams := func(field *ast.Field) {
		var names []*ast.Ident
		if field.Names == nil {
			// Unnamed parameter field (e.g. func f(int)
			if params[paramIdx] != nil {
				// Give it an explicit name "_" since we will
				// make the receiver (if any) a regular parameter
				// and one cannot mix named and unnamed parameters.
				names = append(names, makeIdent("_"))
			}
			paramIdx++
		} else {
			// Named parameter field e.g. func f(x, y int)
			// Remove substituted parameters in place.
			// If all were substituted, delete field.
			for _, id := range field.Names {
				if pinfo := params[paramIdx]; pinfo != nil {
					// Rename unreferenced parameters with "_".
					// This is crucial for binding decls, since
					// unlike parameters, they are subject to
					// "unreferenced var" checks.
					if len(pinfo.info.Refs) == 0 {
						id = makeIdent("_")
					}
					names = append(names, id)
				}
				paramIdx++
			}
		}
		if names != nil {
			newParams = append(newParams, &ast.Field{
				Names: names,
				Type:  field.Type,
			})
		}
	}
	if calleeDecl.Recv != nil {
		filterParams(calleeDecl.Recv.List[0])
		calleeDecl.Recv = nil
	}
	for _, field := range calleeDecl.Type.Params.List {
		filterParams(field)
	}
	calleeDecl.Type.Params.List = newParams
}

// bindingDeclInfo records information about the binding decl produced by
// createBindingDecl.
type bindingDeclInfo struct {
	names map[string]bool // names bound by the binding decl; possibly empty
	stmt  ast.Stmt        // the binding decl itself
}

// createBindingDecl constructs a "binding decl" that implements
// parameter assignment and declares any named result variables
// referenced by the callee. It returns nil if there were no
// unsubstituted parameters.
//
// It may not always be possible to create the decl (e.g. due to
// shadowing), in which case it also returns nil; but if it succeeds,
// the declaration may be used by reduction strategies to relax the
// requirement that all parameters have been substituted.
//
// For example, a call:
//
//	f(a0, a1, a2)
//
// where:
//
//	func f(p0, p1 T0, p2 T1) { body }
//
// reduces to:
//
//	{
//	  var (
//	    p0, p1 T0 = a0, a1
//	    p2     T1 = a2
//	  )
//	  body
//	}
//
// so long as p0, p1 ∉ freevars(T1) or freevars(a2), and so on,
// because each spec is statically resolved in sequence and
// dynamically assigned in sequence. By contrast, all
// parameters are resolved simultaneously and assigned
// simultaneously.
//
// The pX names should already be blank ("_") if the parameter
// is unreferenced; this avoids "unreferenced local var" checks.
//
// Strategies may impose additional checks on return
// conversions, labels, defer, etc.
func createBindingDecl(logf func(string, ...any), caller *Caller, args []*argument, calleeDecl *ast.FuncDecl, results []*paramInfo) *bindingDeclInfo {
	// Spread calls are tricky as they may not align with the
	// parameters' field groupings nor types.
	// For example, given
	//   func g() (int, string)
	// the call
	//   f(g())
	// is legal with these decls of f:
	//   func f(int, string)
	//   func f(x, y any)
	//   func f(x, y ...any)
	// TODO(adonovan): support binding decls for spread calls by
	// splitting parameter groupings as needed.
	if lastArg := last(args); lastArg != nil && lastArg.spread {
		logf("binding decls not yet supported for spread calls")
		return nil
	}

	var (
		specs []ast.Spec
		names = make(map[string]bool) // names defined by previous specs
	)
	// shadow reports whether any name referenced by spec is
	// shadowed by a name declared by a previous spec (since,
	// unlike parameters, each spec of a var decl is within the
	// scope of the previous specs).
	shadow := func(spec *ast.ValueSpec) bool {
		// Compute union of free names of type and values
		// and detect shadowing. Values is the arguments
		// (caller syntax), so we can use type info.
		// But Type is the untyped callee syntax,
		// so we have to use a syntax-only algorithm.
		free := make(map[string]bool)
		for _, value := range spec.Values {
			for name := range freeVars(caller.Info, value) {
				free[name] = true
			}
		}
		freeishNames(free, spec.Type)
		for name := range free {
			if names[name] {
				logf("binding decl would shadow free name %q", name)
				return true
			}
		}
		for _, id := range spec.Names {
			if id.Name != "_" {
				names[id.Name] = true
			}
		}
		return false
	}

	// parameters
	//
	// Bind parameters that were not eliminated through
	// substitution. (Non-nil arguments correspond to the
	// remaining parameters in calleeDecl.)
	var values []ast.Expr
	for _, arg := range args {
		if arg != nil {
			values = append(values, arg.expr)
		}
	}
	for _, field := range calleeDecl.Type.Params.List {
		// Each field (param group) becomes a ValueSpec.
		spec := &ast.ValueSpec{
			Names:  field.Names,
			Type:   field.Type,
			Values: values[:len(field.Names)],
		}
		values = values[len(field.Names):]
		if shadow(spec) {
			return nil
		}
		specs = append(specs, spec)
	}
	assert(len(values) == 0, "args/params mismatch")

	// results
	//
	// Add specs to declare any named result
	// variables that are referenced by the body.
	if calleeDecl.Type.Results != nil {
		resultIdx := 0
		for _, field := range calleeDecl.Type.Results.List {
			if field.Names == nil {
				resultIdx++
				continue // unnamed field
			}
			var names []*ast.Ident
			for _, id := range field.Names {
				if len(results[resultIdx].Refs) > 0 {
					names = append(names, id)
				}
				resultIdx++
			}
			if len(names) > 0 {
				spec := &ast.ValueSpec{
					Names: names,
					Type:  field.Type,
				}
				if shadow(spec) {
					return nil
				}
				specs = append(specs, spec)
			}
		}
	}

	if len(specs) == 0 {
		logf("binding decl not needed: all parameters substituted")
		return nil
	}

	stmt := &ast.DeclStmt{
		Decl: &ast.GenDecl{
			Tok:   token.VAR,
			Specs: specs,
		},
	}
	logf("binding decl: %s", debugFormatNode(caller.Fset, stmt))
	return &bindingDeclInfo{names: names, stmt: stmt}
}

// lookup does a symbol lookup in the lexical environment of the caller.
func (caller *Caller) lookup(name string) types.Object {
	pos := caller.Call.Pos()
	for _, n := range caller.path {
		if scope := scopeFor(caller.Info, n); scope != nil {
			if _, obj := scope.LookupParent(name, pos); obj != nil {
				return obj
			}
		}
	}
	return nil
}

func scopeFor(info *types.Info, n ast.Node) *types.Scope {
	// The function body scope (containing not just params)
	// is associated with the function's type, not body.
	switch fn := n.(type) {
	case *ast.FuncDecl:
		n = fn.Type
	case *ast.FuncLit:
		n = fn.Type
	}
	return info.Scopes[n]
}

// -- predicates over expressions --

// freeVars returns the names of all free identifiers of e:
// those lexically referenced by it but not defined within it.
// (Fields and methods are not included.)
func freeVars(info *types.Info, e ast.Expr) map[string]bool {
	free := make(map[string]bool)
	ast.Inspect(e, func(n ast.Node) bool {
		if id, ok := n.(*ast.Ident); ok {
			// The isField check is so that we don't treat T{f: 0} as a ref to f.
			if obj, ok := info.Uses[id]; ok && !within(obj.Pos(), e) && !isField(obj) {
				free[obj.Name()] = true
			}
		}
		return true
	})
	return free
}

// freeishNames computes an over-approximation to the free names
// of the type syntax t, inserting values into the map.
//
// Because we don't have go/types annotations, we can't give an exact
// result in all cases. In particular, an array type [n]T might have a
// size such as unsafe.Sizeof(func() int{stmts...}()) and now the
// precise answer depends upon all the statement syntax too. But that
// never happens in practice.
func freeishNames(free map[string]bool, t ast.Expr) {
	var visit func(n ast.Node) bool
	visit = func(n ast.Node) bool {
		switch n := n.(type) {
		case *ast.Ident:
			free[n.Name] = true

		case *ast.SelectorExpr:
			ast.Inspect(n.X, visit)
			return false // don't visit .Sel

		case *ast.Field:
			ast.Inspect(n.Type, visit)
			// Don't visit .Names:
			// FuncType parameters, interface methods, struct fields
			return false
		}
		return true
	}
	ast.Inspect(t, visit)
}

// effects reports whether an expression might change the state of the
// program (through function calls and channel receives) and affect
// the evaluation of subsequent expressions.
func (st *state) effects(info *types.Info, expr ast.Expr) bool {
	effects := false
	ast.Inspect(expr, func(n ast.Node) bool {
		switch n := n.(type) {
		case *ast.FuncLit:
			return false // prune descent

		case *ast.CallExpr:
			if info.Types[n.Fun].IsType() {
				// A conversion T(x) has only the effect of its operand.
			} else if !callsPureBuiltin(info, n) {
				// A handful of built-ins have no effect
				// beyond those of their arguments.
				// All other calls (including append, copy, recover)
				// have unknown effects.
				//
				// As with 'pure', there is room for
				// improvement by inspecting the callee.
				effects = true
			}

		case *ast.UnaryExpr:
			if n.Op == token.ARROW { // <-ch
				effects = true
			}
		}
		return true
	})

	// Even if consideration of effects is not desired,
	// we continue to compute, log, and discard them.
	if st.opts.IgnoreEffects && effects {
		effects = false
		st.opts.Logf("ignoring potential effects of argument %s",
			debugFormatNode(st.caller.Fset, expr))
	}

	return effects
}

// pure reports whether an expression has the same result no matter
// when it is executed relative to other expressions, so it can be
// commuted with any other expression or statement without changing
// its meaning.
//
// An expression is considered impure if it reads the contents of any
// variable, with the exception of "single assignment" local variables
// (as classified by the provided callback), which are never updated
// after their initialization.
//
// Pure does not imply duplicable: for example, new(T) and T{} are
// pure expressions but both return a different value each time they
// are evaluated, so they are not safe to duplicate.
//
// Purity does not imply freedom from run-time panics. We assume that
// target programs do not encounter run-time panics nor depend on them
// for correct operation.
//
// TODO(adonovan): add unit tests of this function.
func pure(info *types.Info, assign1 func(*types.Var) bool, e ast.Expr) bool {
	var pure func(e ast.Expr) bool
	pure = func(e ast.Expr) bool {
		switch e := e.(type) {
		case *ast.ParenExpr:
			return pure(e.X)

		case *ast.Ident:
			if v, ok := info.Uses[e].(*types.Var); ok {
				// In general variables are impure
				// as they may be updated, but
				// single-assignment local variables
				// never change value.
				//
				// We assume all package-level variables
				// may be updated, but for non-exported
				// ones we could do better by analyzing
				// the complete package.
				return !isPkgLevel(v) && assign1(v)
			}

			// All other kinds of reference are pure.
			return true

		case *ast.FuncLit:
			// A function literal may allocate a closure that
			// references mutable variables, but mutation
			// cannot be observed without calling the function,
			// and calls are considered impure.
			return true

		case *ast.BasicLit:
			return true

		case *ast.UnaryExpr: // + - ! ^ & but not <-
			return e.Op != token.ARROW && pure(e.X)

		case *ast.BinaryExpr: // arithmetic, shifts, comparisons, &&/||
			return pure(e.X) && pure(e.Y)

		case *ast.CallExpr:
			// A conversion is as pure as its operand.
			if info.Types[e.Fun].IsType() {
				return pure(e.Args[0])
			}

			// Calls to some built-ins are as pure as their arguments.
			if callsPureBuiltin(info, e) {
				for _, arg := range e.Args {
					if !pure(arg) {
						return false
					}
				}
				return true
			}

			// All other calls are impure, so we can
			// reject them without even looking at e.Fun.
			//
			// More sophisticated analysis could infer purity in
			// commonly used functions such as strings.Contains;
			// perhaps we could offer the client a hook so that
			// go/analysis-based implementation could exploit the
			// results of a purity analysis. But that would make
			// the inliner's choices harder to explain.
			return false

		case *ast.CompositeLit:
			// T{...} is as pure as its elements.
			for _, elt := range e.Elts {
				if kv, ok := elt.(*ast.KeyValueExpr); ok {
					if !pure(kv.Value) {
						return false
					}
					if id, ok := kv.Key.(*ast.Ident); ok {
						if v, ok := info.Uses[id].(*types.Var); ok && v.IsField() {
							continue // struct {field: value}
						}
					}
					// map/slice/array {key: value}
					if !pure(kv.Key) {
						return false
					}

				} else if !pure(elt) {
					return false
				}
			}
			return true

		case *ast.SelectorExpr:
			if seln, ok := info.Selections[e]; ok {

				// See types.SelectionKind for background.
				switch seln.Kind() {
				case types.MethodExpr:
					// A method expression T.f acts like a
					// reference to a func decl, so it is pure.
					return true

				case types.MethodVal, types.FieldVal:
					// A field or method selection x.f is pure
					// if x is pure and the selection does
					// not indirect a pointer.
					return !indirectSelection(seln) && pure(e.X)

				default:
					panic(seln)
				}
			} else {
				// A qualified identifier is
				// treated like an unqualified one.
				return pure(e.Sel)
			}

		case *ast.StarExpr:
			return false // *ptr depends on the state of the heap

		default:
			return false
		}
	}
	return pure(e)
}

// callsPureBuiltin reports whether call is a call of a built-in
// function that is a pure computation over its operands (analogous to
// a + operator). Because it does not depend on program state, it may
// be evaluated at any point--though not necessarily at multiple
// points (consider new, make).
func callsPureBuiltin(info *types.Info, call *ast.CallExpr) bool {
	if id, ok := astutil.Unparen(call.Fun).(*ast.Ident); ok {
		if b, ok := info.ObjectOf(id).(*types.Builtin); ok {
			switch b.Name() {
			case "len", "cap", "complex", "imag", "real", "make", "new", "max", "min":
				return true
			}
			// Not: append clear close copy delete panic print println recover
		}
	}
	return false
}

// duplicable reports whether it is appropriate for the expression to
// be freely duplicated.
//
// Given the declaration
//
//	func f(x T) T { return x + g() + x }
//
// an argument y is considered duplicable if we would wish to see a
// call f(y) simplified to y+g()+y. This is true for identifiers,
// integer literals, unary negation, and selectors x.f where x is not
// a pointer. But we would not wish to duplicate expressions that:
// - have side effects (e.g. nearly all calls),
// - are not referentially transparent (e.g. &T{}, ptr.field, *ptr), or
// - are long (e.g. "huge string literal").
func duplicable(info *types.Info, e ast.Expr) bool {
	switch e := e.(type) {
	case *ast.ParenExpr:
		return duplicable(info, e.X)

	case *ast.Ident:
		return true

	case *ast.BasicLit:
		v := info.Types[e].Value
		switch e.Kind {
		case token.INT:
			return true // any int
		case token.STRING:
			return consteq(v, kZeroString) // only ""
		case token.FLOAT:
			return consteq(v, kZeroFloat) || consteq(v, kOneFloat) // only 0.0 or 1.0
		}

	case *ast.UnaryExpr: // e.g. +1, -1
		return (e.Op == token.ADD || e.Op == token.SUB) && duplicable(info, e.X)

	case *ast.CompositeLit:
		// Empty struct or array literals T{} are duplicable.
		// (Non-empty literals are too verbose, and slice/map
		// literals allocate indirect variables.)
		if len(e.Elts) == 0 {
			switch info.TypeOf(e).Underlying().(type) {
			case *types.Struct, *types.Array:
				return true
			}
		}
		return false

	case *ast.CallExpr:
		// Treat type conversions as duplicable if they do not observably allocate.
		// The only cases of observable allocations are
		// the `[]byte(string)` and `[]rune(string)` conversions.
		//
		// Duplicating string([]byte) conversions increases
		// allocation but doesn't change behavior, but the
		// reverse, []byte(string), allocates a distinct array,
		// which is observable.

		if !info.Types[e.Fun].IsType() { // check whether e.Fun is a type conversion
			return false
		}

		fun := info.TypeOf(e.Fun)
		arg := info.TypeOf(e.Args[0])

		switch fun := fun.Underlying().(type) {
		case *types.Slice:
			// Do not mark []byte(string) and []rune(string) as duplicable.
			elem, ok := fun.Elem().Underlying().(*types.Basic)
			if ok && (elem.Kind() == types.Rune || elem.Kind() == types.Byte) {
				from, ok := arg.Underlying().(*types.Basic)
				isString := ok && from.Info()&types.IsString != 0
				return !isString
			}
		case *types.TypeParam:
			return false // be conservative
		}
		return true

	case *ast.SelectorExpr:
		if seln, ok := info.Selections[e]; ok {
			// A field or method selection x.f is referentially
			// transparent if it does not indirect a pointer.
			return !indirectSelection(seln)
		}
		// A qualified identifier pkg.Name is referentially transparent.
		return true
	}
	return false
}

func consteq(x, y constant.Value) bool {
	return constant.Compare(x, token.EQL, y)
}

var (
	kZeroInt    = constant.MakeInt64(0)
	kZeroString = constant.MakeString("")
	kZeroFloat  = constant.MakeFloat64(0.0)
	kOneFloat   = constant.MakeFloat64(1.0)
)

// -- inline helpers --

func assert(cond bool, msg string) {
	if !cond {
		panic(msg)
	}
}

// blanks returns a slice of n > 0 blank identifiers.
func blanks[E ast.Expr](n int) []E {
	if n == 0 {
		panic("blanks(0)")
	}
	res := make([]E, n)
	for i := range res {
		res[i] = ast.Expr(makeIdent("_")).(E) // ugh
	}
	return res
}

func makeIdent(name string) *ast.Ident {
	return &ast.Ident{Name: name}
}

// importedPkgName returns the PkgName object declared by an ImportSpec.
// TODO(adonovan): make this a method of types.Info (#62037).
func importedPkgName(info *types.Info, imp *ast.ImportSpec) (*types.PkgName, bool) {
	var obj types.Object
	if imp.Name != nil {
		obj = info.Defs[imp.Name]
	} else {
		obj = info.Implicits[imp]
	}
	pkgname, ok := obj.(*types.PkgName)
	return pkgname, ok
}

func isPkgLevel(obj types.Object) bool {
	// TODO(adonovan): consider using the simpler obj.Parent() ==
	// obj.Pkg().Scope() instead. But be sure to test carefully
	// with instantiations of generics.
	return obj.Pkg().Scope().Lookup(obj.Name()) == obj
}

// callContext returns the two nodes immediately enclosing the call
// (specified as a PathEnclosingInterval), ignoring parens.
func callContext(callPath []ast.Node) (parent, grandparent ast.Node) {
	_ = callPath[0].(*ast.CallExpr) // sanity check
	for _, n := range callPath[1:] {
		if !is[*ast.ParenExpr](n) {
			if parent == nil {
				parent = n
			} else {
				return parent, n
			}
		}
	}
	return parent, nil
}

// hasLabelConflict reports whether the set of labels of the function
// enclosing the call (specified as a PathEnclosingInterval)
// intersects with the set of callee labels.
func hasLabelConflict(callPath []ast.Node, calleeLabels []string) bool {
	labels := callerLabels(callPath)
	for _, label := range calleeLabels {
		if labels[label] {
			return true // conflict
		}
	}
	return false
}

// callerLabels returns the set of control labels in the function (if
// any) enclosing the call (specified as a PathEnclosingInterval).
func callerLabels(callPath []ast.Node) map[string]bool {
	var callerBody *ast.BlockStmt
	switch f := callerFunc(callPath).(type) {
	case *ast.FuncDecl:
		callerBody = f.Body
	case *ast.FuncLit:
		callerBody = f.Body
	}
	var labels map[string]bool
	if callerBody != nil {
		ast.Inspect(callerBody, func(n ast.Node) bool {
			switch n := n.(type) {
			case *ast.FuncLit:
				return false // prune traversal
			case *ast.LabeledStmt:
				if labels == nil {
					labels = make(map[string]bool)
				}
				labels[n.Label.Name] = true
			}
			return true
		})
	}
	return labels
}

// callerFunc returns the innermost Func{Decl,Lit} node enclosing the
// call (specified as a PathEnclosingInterval).
func callerFunc(callPath []ast.Node) ast.Node {
	_ = callPath[0].(*ast.CallExpr) // sanity check
	for _, n := range callPath[1:] {
		if is[*ast.FuncDecl](n) || is[*ast.FuncLit](n) {
			return n
		}
	}
	return nil
}

// callStmt reports whether the function call (specified
// as a PathEnclosingInterval) appears within an ExprStmt,
// and returns it if so.
//
// If unrestricted, callStmt returns nil if the ExprStmt f() appears
// in a restricted context (such as "if f(); cond {") where it cannot
// be replaced by an arbitrary statement. (See "statement theory".)
func callStmt(callPath []ast.Node, unrestricted bool) *ast.ExprStmt {
	parent, _ := callContext(callPath)
	stmt, ok := parent.(*ast.ExprStmt)
	if ok && unrestricted {
		switch callPath[nodeIndex(callPath, stmt)+1].(type) {
		case *ast.LabeledStmt,
			*ast.BlockStmt,
			*ast.CaseClause,
			*ast.CommClause:
			// unrestricted
		default:
			// TODO(adonovan): handle restricted
			// XYZStmt.Init contexts (but not ForStmt.Post)
			// by creating a block around the if/for/switch:
			// "if f(); cond {"  ->  "{ stmts; if cond {"

			return nil // restricted
		}
	}
	return stmt
}

// Statement theory
//
// These are all the places a statement may appear in the AST:
//
// LabeledStmt.Stmt       Stmt      -- any
// BlockStmt.List       []Stmt      -- any (but see switch/select)
// IfStmt.Init            Stmt?     -- simple
// IfStmt.Body            BlockStmt
// IfStmt.Else            Stmt?     -- IfStmt or BlockStmt
// CaseClause.Body      []Stmt      -- any
// SwitchStmt.Init        Stmt?     -- simple
// SwitchStmt.Body        BlockStmt -- CaseClauses only
// TypeSwitchStmt.Init    Stmt?     -- simple
// TypeSwitchStmt.Assign  Stmt      -- AssignStmt(TypeAssertExpr) or ExprStmt(TypeAssertExpr)
// TypeSwitchStmt.Body    BlockStmt -- CaseClauses only
// CommClause.Comm        Stmt?     -- SendStmt or ExprStmt(UnaryExpr) or AssignStmt(UnaryExpr)
// CommClause.Body      []Stmt      -- any
// SelectStmt.Body        BlockStmt -- CommClauses only
// ForStmt.Init           Stmt?     -- simple
// ForStmt.Post           Stmt?     -- simple
// ForStmt.Body           BlockStmt
// RangeStmt.Body         BlockStmt
//
// simple = AssignStmt | SendStmt | IncDecStmt | ExprStmt.
//
// A BlockStmt cannot replace an ExprStmt in
// {If,Switch,TypeSwitch}Stmt.Init or ForStmt.Post.
// That is allowed only within:
//   LabeledStmt.Stmt       Stmt
//   BlockStmt.List       []Stmt
//   CaseClause.Body      []Stmt
//   CommClause.Body      []Stmt

// replaceNode performs a destructive update of the tree rooted at
// root, replacing each occurrence of "from" with "to". If to is nil and
// the element is within a slice, the slice element is removed.
//
// The root itself cannot be replaced; an attempt will panic.
//
// This function must not be called on the caller's syntax tree.
//
// TODO(adonovan): polish this up and move it to astutil package.
// TODO(adonovan): needs a unit test.
func replaceNode(root ast.Node, from, to ast.Node) {
	if from == nil {
		panic("from == nil")
	}
	if reflect.ValueOf(from).IsNil() {
		panic(fmt.Sprintf("from == (%T)(nil)", from))
	}
	if from == root {
		panic("from == root")
	}
	found := false
	var parent reflect.Value // parent variable of interface type, containing a pointer
	var visit func(reflect.Value)
	visit = func(v reflect.Value) {
		switch v.Kind() {
		case reflect.Ptr:
			if v.Interface() == from {
				found = true

				// If v is a struct field or array element
				// (e.g. Field.Comment or Field.Names[i])
				// then it is addressable (a pointer variable).
				//
				// But if it was the value an interface
				// (e.g. *ast.Ident within ast.Node)
				// then it is non-addressable, and we need
				// to set the enclosing interface (parent).
				if !v.CanAddr() {
					v = parent
				}

				// to=nil => use zero value
				var toV reflect.Value
				if to != nil {
					toV = reflect.ValueOf(to)
				} else {
					toV = reflect.Zero(v.Type()) // e.g. ast.Expr(nil)
				}
				v.Set(toV)

			} else if !v.IsNil() {
				switch v.Interface().(type) {
				case *ast.Object, *ast.Scope:
					// Skip fields of types potentially involved in cycles.
				default:
					visit(v.Elem())
				}
			}

		case reflect.Struct:
			for i := 0; i < v.Type().NumField(); i++ {
				visit(v.Field(i))
			}

		case reflect.Slice:
			compact := false
			for i := 0; i < v.Len(); i++ {
				visit(v.Index(i))
				if v.Index(i).IsNil() {
					compact = true
				}
			}
			if compact {
				// Elements were deleted. Eliminate nils.
				// (Do this is a second pass to avoid
				// unnecessary writes in the common case.)
				j := 0
				for i := 0; i < v.Len(); i++ {
					if !v.Index(i).IsNil() {
						v.Index(j).Set(v.Index(i))
						j++
					}
				}
				v.SetLen(j)
			}
		case reflect.Interface:
			parent = v
			visit(v.Elem())

		case reflect.Array, reflect.Chan, reflect.Func, reflect.Map, reflect.UnsafePointer:
			panic(v) // unreachable in AST
		default:
			// bool, string, number: nop
		}
		parent = reflect.Value{}
	}
	visit(reflect.ValueOf(root))
	if !found {
		panic(fmt.Sprintf("%T not found", from))
	}
}

// clearPositions destroys token.Pos information within the tree rooted at root,
// as positions in callee trees may cause caller comments to be emitted prematurely.
//
// In general it isn't safe to clear a valid Pos because some of them
// (e.g. CallExpr.Ellipsis, TypeSpec.Assign) are significant to
// go/printer, so this function sets each non-zero Pos to 1, which
// suffices to avoid advancing the printer's comment cursor.
//
// This function mutates its argument; do not invoke on caller syntax.
//
// TODO(adonovan): remove this horrendous workaround when #20744 is finally fixed.
func clearPositions(root ast.Node) {
	posType := reflect.TypeOf(token.NoPos)
	ast.Inspect(root, func(n ast.Node) bool {
		if n != nil {
			v := reflect.ValueOf(n).Elem() // deref the pointer to struct
			fields := v.Type().NumField()
			for i := 0; i < fields; i++ {
				f := v.Field(i)
				// Clearing Pos arbitrarily is destructive,
				// as its presence may be semantically significant
				// (e.g. CallExpr.Ellipsis, TypeSpec.Assign)
				// or affect formatting preferences (e.g. GenDecl.Lparen).
				//
				// Note: for proper formatting, it may be necessary to be selective
				// about which positions we set to 1 vs which we set to token.NoPos.
				// (e.g. we can set most to token.NoPos, save the few that are
				// significant).
				if f.Type() == posType {
					if f.Interface() != token.NoPos {
						f.Set(reflect.ValueOf(token.Pos(1)))
					}
				}
			}
		}
		return true
	})
}

// findIdent returns the Ident beneath root that has the given pos.
func findIdent(root ast.Node, pos token.Pos) *ast.Ident {
	// TODO(adonovan): opt: skip subtrees that don't contain pos.
	var found *ast.Ident
	ast.Inspect(root, func(n ast.Node) bool {
		if found != nil {
			return false
		}
		if id, ok := n.(*ast.Ident); ok {
			if id.Pos() == pos {
				found = id
			}
		}
		return true
	})
	if found == nil {
		panic(fmt.Sprintf("findIdent %d not found in %s",
			pos, debugFormatNode(token.NewFileSet(), root)))
	}
	return found
}

func prepend[T any](elem T, slice ...T) []T {
	return append([]T{elem}, slice...)
}

// debugFormatNode formats a node or returns a formatting error.
// Its sloppy treatment of errors is appropriate only for logging.
func debugFormatNode(fset *token.FileSet, n ast.Node) string {
	var out strings.Builder
	if err := format.Node(&out, fset, n); err != nil {
		out.WriteString(err.Error())
	}
	return out.String()
}

func shallowCopy[T any](ptr *T) *T {
	copy := *ptr
	return &copy
}

// ∀
func forall[T any](list []T, f func(i int, x T) bool) bool {
	for i, x := range list {
		if !f(i, x) {
			return false
		}
	}
	return true
}

// ∃
func exists[T any](list []T, f func(i int, x T) bool) bool {
	for i, x := range list {
		if f(i, x) {
			return true
		}
	}
	return false
}

// last returns the last element of a slice, or zero if empty.
func last[T any](slice []T) T {
	n := len(slice)
	if n > 0 {
		return slice[n-1]
	}
	return *new(T)
}

// canImport reports whether one package is allowed to import another.
//
// TODO(adonovan): allow customization of the accessibility relation
// (e.g. for Bazel).
func canImport(from, to string) bool {
	// TODO(adonovan): better segment hygiene.
	if strings.HasPrefix(to, "internal/") {
		// Special case: only std packages may import internal/...
		// We can't reliably know whether we're in std, so we
		// use a heuristic on the first segment.
		first, _, _ := strings.Cut(from, "/")
		if strings.Contains(first, ".") {
			return false // example.com/foo ∉ std
		}
		if first == "testdata" {
			return false // testdata/foo ∉ std
		}
	}
	if i := strings.LastIndex(to, "/internal/"); i >= 0 {
		return strings.HasPrefix(from, to[:i])
	}
	return true
}

// consistentOffsets reports whether the portion of caller.Content
// that corresponds to caller.Call can be parsed as a call expression.
// If not, the client has provided inconsistent information, possibly
// because they forgot to ignore line directives when computing the
// filename enclosing the call.
// This is just a heuristic.
func consistentOffsets(caller *Caller) bool {
	start := offsetOf(caller.Fset, caller.Call.Pos())
	end := offsetOf(caller.Fset, caller.Call.End())
	if !(0 < start && start < end && end <= len(caller.Content)) {
		return false
	}
	expr, err := parser.ParseExpr(string(caller.Content[start:end]))
	if err != nil {
		return false
	}
	return is[*ast.CallExpr](expr)
}

// needsParens reports whether parens are required to avoid ambiguity
// around the new node replacing the specified old node (which is some
// ancestor of the CallExpr identified by its PathEnclosingInterval).
func needsParens(callPath []ast.Node, old, new ast.Node) bool {
	// Find enclosing old node and its parent.
	i := nodeIndex(callPath, old)
	if i == -1 {
		panic("not found")
	}

	// There is no precedence ambiguity when replacing
	// (e.g.) a statement enclosing the call.
	if !is[ast.Expr](old) {
		return false
	}

	// An expression beneath a non-expression
	// has no precedence ambiguity.
	parent, ok := callPath[i+1].(ast.Expr)
	if !ok {
		return false
	}

	precedence := func(n ast.Node) int {
		switch n := n.(type) {
		case *ast.UnaryExpr, *ast.StarExpr:
			return token.UnaryPrec
		case *ast.BinaryExpr:
			return n.Op.Precedence()
		}
		return -1
	}

	// Parens are not required if the new node
	// is not unary or binary.
	newprec := precedence(new)
	if newprec < 0 {
		return false
	}

	// Parens are required if parent and child are both
	// unary or binary and the parent has higher precedence.
	if precedence(parent) > newprec {
		return true
	}

	// Was the old node the operand of a postfix operator?
	//  f().sel
	//  f()[i:j]
	//  f()[i]
	//  f().(T)
	//  f()(x)
	switch parent := parent.(type) {
	case *ast.SelectorExpr:
		return parent.X == old
	case *ast.IndexExpr:
		return parent.X == old
	case *ast.SliceExpr:
		return parent.X == old
	case *ast.TypeAssertExpr:
		return parent.X == old
	case *ast.CallExpr:
		return parent.Fun == old
	}
	return false
}

func nodeIndex(nodes []ast.Node, n ast.Node) int {
	// TODO(adonovan): Use index[ast.Node]() in go1.20.
	for i, node := range nodes {
		if node == n {
			return i
		}
	}
	return -1
}

// declares returns the set of lexical names declared by a
// sequence of statements from the same block, excluding sub-blocks.
// (Lexical names do not include control labels.)
func declares(stmts []ast.Stmt) map[string]bool {
	names := make(map[string]bool)
	for _, stmt := range stmts {
		switch stmt := stmt.(type) {
		case *ast.DeclStmt:
			for _, spec := range stmt.Decl.(*ast.GenDecl).Specs {
				switch spec := spec.(type) {
				case *ast.ValueSpec:
					for _, id := range spec.Names {
						names[id.Name] = true
					}
				case *ast.TypeSpec:
					names[spec.Name.Name] = true
				}
			}

		case *ast.AssignStmt:
			if stmt.Tok == token.DEFINE {
				for _, lhs := range stmt.Lhs {
					names[lhs.(*ast.Ident).Name] = true
				}
			}
		}
	}
	delete(names, "_")
	return names
}

// assignStmts rewrites a statement assigning the results of a call into zero
// or more statements that assign its return operands, or (nil, false) if no
// such rewrite is possible. The set of bindings created by the result of
// assignStmts is the same as the set of bindings created by the callerStmt.
//
// The callee must contain exactly one return statement.
//
// This is (once again) a surprisingly complex task. For example, depending on
// types and existing bindings, the assignment
//
//	a, b := f()
//
// could be rewritten as:
//
//	a, b := 1, 2
//
// but may need to be written as:
//
//	a, b := int8(1), int32(2)
//
// In the case where the return statement within f is a spread call to another
// function g(), we cannot explicitly convert the return values inline, and so
// it may be necessary to split the declaration and assignment of variables
// into separate statements:
//
//	a, b := g()
//
// or
//
//	var a int32
//	a, b = g()
//
// or
//
//	var (
//		a int8
//		b int32
//	)
//	a, b = g()
//
// Note: assignStmts may return (nil, true) if it determines that the rewritten
// assignment consists only of _ = nil assignments.
func (st *state) assignStmts(callerStmt *ast.AssignStmt, returnOperands []ast.Expr) ([]ast.Stmt, bool) {
	logf, caller, callee := st.opts.Logf, st.caller, &st.callee.impl

	assert(len(callee.Returns) == 1, "unexpected multiple returns")
	resultInfo := callee.Returns[0]

	// When constructing assign statements, we need to make sure that we don't
	// modify types on the left-hand side, such as would happen if the type of a
	// RHS expression does not match the corresponding LHS type at the caller
	// (due to untyped conversion or interface widening).
	//
	// This turns out to be remarkably tricky to handle correctly.
	//
	// Substrategies below are labeled as `Substrategy <name>:`.

	// Collect LHS information.
	var (
		lhs    []ast.Expr                                // shallow copy of the LHS slice, for mutation
		defs   = make([]*ast.Ident, len(callerStmt.Lhs)) // indexes in lhs of defining identifiers
		blanks = make([]bool, len(callerStmt.Lhs))       // indexes in lhs of blank identifiers
		byType typeutil.Map                              // map of distinct types -> indexes, for writing specs later
	)
	for i, expr := range callerStmt.Lhs {
		lhs = append(lhs, expr)
		if name, ok := expr.(*ast.Ident); ok {
			if name.Name == "_" {
				blanks[i] = true
				continue // no type
			}

			if obj, isDef := caller.Info.Defs[name]; isDef {
				defs[i] = name
				typ := obj.Type()
				idxs, _ := byType.At(typ).([]int)
				idxs = append(idxs, i)
				byType.Set(typ, idxs)
			}
		}
	}

	// Collect RHS information
	//
	// The RHS is either a parallel assignment or spread assignment, but by
	// looping over both callerStmt.Rhs and returnOperands we handle both.
	var (
		rhs             []ast.Expr              // new RHS of assignment, owned by the inliner
		callIdx         = -1                    // index of the call among the original RHS
		nilBlankAssigns = make(map[int]unit)    // indexes in rhs of _ = nil assignments, which can be deleted
		freeNames       = make(map[string]bool) // free(ish) names among rhs expressions
		nonTrivial      = make(map[int]bool)    // indexes in rhs of nontrivial result conversions
	)
	for i, expr := range callerStmt.Rhs {
		if expr == caller.Call {
			assert(callIdx == -1, "malformed (duplicative) AST")
			callIdx = i
			for j, returnOperand := range returnOperands {
				freeishNames(freeNames, returnOperand)
				rhs = append(rhs, returnOperand)
				if resultInfo[j]&nonTrivialResult != 0 {
					nonTrivial[i+j] = true
				}
				if blanks[i+j] && resultInfo[j]&untypedNilResult != 0 {
					nilBlankAssigns[i+j] = unit{}
				}
			}
		} else {
			// We must clone before clearing positions, since e came from the caller.
			expr = internalastutil.CloneNode(expr)
			clearPositions(expr)
			freeishNames(freeNames, expr)
			rhs = append(rhs, expr)
		}
	}
	assert(callIdx >= 0, "failed to find call in RHS")

	// Substrategy "splice": Check to see if we can simply splice in the result
	// expressions from the callee, such as simplifying
	//
	//  x, y := f()
	//
	// to
	//
	//  x, y := e1, e2
	//
	// where the types of x and y match the types of e1 and e2.
	//
	// This works as long as we don't need to write any additional type
	// information.
	if callerStmt.Tok == token.ASSIGN && // LHS types already determined before call
		len(nonTrivial) == 0 { // no non-trivial conversions to worry about

		logf("substrategy: slice assignment")
		return []ast.Stmt{&ast.AssignStmt{
			Lhs:    lhs,
			Tok:    callerStmt.Tok,
			TokPos: callerStmt.TokPos,
			Rhs:    rhs,
		}}, true
	}

	// Inlining techniques below will need to write type information in order to
	// preserve the correct types of LHS identifiers.
	//
	// writeType is a simple helper to write out type expressions.
	// TODO(rfindley):
	//   1. handle qualified type names (potentially adding new imports)
	//   2. expand this to handle more type expressions.
	//   3. refactor to share logic with callee rewriting.
	universeAny := types.Universe.Lookup("any")
	typeExpr := func(typ types.Type, shadows ...map[string]bool) ast.Expr {
		var typeName string
		switch typ := typ.(type) {
		case *types.Basic:
			typeName = typ.Name()
		case interface{ Obj() *types.TypeName }: // Named, Alias, TypeParam
			typeName = typ.Obj().Name()
		}

		// Special case: check for universe "any".
		// TODO(golang/go#66921): this may become unnecessary if any becomes a proper alias.
		if typ == universeAny.Type() {
			typeName = "any"
		}

		if typeName == "" {
			return nil
		}

		for _, shadow := range shadows {
			if shadow[typeName] {
				logf("cannot write shadowed type name %q", typeName)
				return nil
			}
		}
		obj, _ := caller.lookup(typeName).(*types.TypeName)
		if obj != nil && types.Identical(obj.Type(), typ) {
			return ast.NewIdent(typeName)
		}
		return nil
	}

	// Substrategy "spread": in the case of a spread call (func f() (T1, T2) return
	// g()), since we didn't hit the 'splice' substrategy, there must be some
	// non-declaring expression on the LHS. Simplify this by pre-declaring
	// variables, rewriting
	//
	//   x, y := f()
	//
	// to
	//
	//  var x int
	//  x, y = g()
	//
	// Which works as long as the predeclared variables do not overlap with free
	// names on the RHS.
	if len(rhs) != len(lhs) {
		assert(len(rhs) == 1 && len(returnOperands) == 1, "expected spread call")

		for _, id := range defs {
			if id != nil && freeNames[id.Name] {
				// By predeclaring variables, we're changing them to be in scope of the
				// RHS. We can't do this if their names are free on the RHS.
				return nil, false
			}
		}

		// Write out the specs, being careful to avoid shadowing free names in
		// their type expressions.
		var (
			specs    []ast.Spec
			specIdxs []int
			shadow   = make(map[string]bool)
		)
		failed := false
		byType.Iterate(func(typ types.Type, v any) {
			if failed {
				return
			}
			idxs := v.([]int)
			specIdxs = append(specIdxs, idxs[0])
			texpr := typeExpr(typ, shadow)
			if texpr == nil {
				failed = true
				return
			}
			spec := &ast.ValueSpec{
				Type: texpr,
			}
			for _, idx := range idxs {
				spec.Names = append(spec.Names, ast.NewIdent(defs[idx].Name))
			}
			specs = append(specs, spec)
		})
		if failed {
			return nil, false
		}
		logf("substrategy: spread assignment")
		return []ast.Stmt{
			&ast.DeclStmt{
				Decl: &ast.GenDecl{
					Tok:   token.VAR,
					Specs: specs,
				},
			},
			&ast.AssignStmt{
				Lhs: callerStmt.Lhs,
				Tok: token.ASSIGN,
				Rhs: returnOperands,
			},
		}, true
	}

	assert(len(lhs) == len(rhs), "mismatching LHS and RHS")

	// Substrategy "convert": write out RHS expressions with explicit type conversions
	// as necessary, rewriting
	//
	//  x, y := f()
	//
	// to
	//
	//  x, y := 1, int32(2)
	//
	// As required to preserve types.
	//
	// In the special case of _ = nil, which is disallowed by the type checker
	// (since nil has no default type), we delete the assignment.
	var origIdxs []int // maps back to original indexes after lhs and rhs are pruned
	i := 0
	for j := range lhs {
		if _, ok := nilBlankAssigns[j]; !ok {
			lhs[i] = lhs[j]
			rhs[i] = rhs[j]
			origIdxs = append(origIdxs, j)
			i++
		}
	}
	lhs = lhs[:i]
	rhs = rhs[:i]

	if len(lhs) == 0 {
		logf("trivial assignment after pruning nil blanks assigns")
		// After pruning, we have no remaining assignments.
		// Signal this by returning a non-nil slice of statements.
		return nil, true
	}

	// Write out explicit conversions as necessary.
	//
	// A conversion is necessary if the LHS is being defined, and the RHS return
	// involved a nontrivial implicit conversion.
	for i, expr := range rhs {
		idx := origIdxs[i]
		if nonTrivial[idx] && defs[idx] != nil {
			typ := caller.Info.TypeOf(lhs[i])
			texpr := typeExpr(typ)
			if texpr == nil {
				return nil, false
			}
			if _, ok := texpr.(*ast.StarExpr); ok {
				// TODO(rfindley): is this necessary? Doesn't the formatter add these parens?
				texpr = &ast.ParenExpr{X: texpr} // *T -> (*T)   so that (*T)(x) is valid
			}
			rhs[i] = &ast.CallExpr{
				Fun:  texpr,
				Args: []ast.Expr{expr},
			}
		}
	}
	logf("substrategy: convert assignment")
	return []ast.Stmt{&ast.AssignStmt{
		Lhs: lhs,
		Tok: callerStmt.Tok,
		Rhs: rhs,
	}}, true
}

// tailCallSafeReturn reports whether the callee's return statements may be safely
// used to return from the function enclosing the caller (which must exist).
func tailCallSafeReturn(caller *Caller, calleeSymbol *types.Func, callee *gobCallee) bool {
	// It is safe if all callee returns involve only trivial conversions.
	if !hasNonTrivialReturn(callee.Returns) {
		return true
	}

	var callerType types.Type
	// Find type of innermost function enclosing call.
	// (Beware: Caller.enclosingFunc is the outermost.)
loop:
	for _, n := range caller.path {
		switch f := n.(type) {
		case *ast.FuncDecl:
			callerType = caller.Info.ObjectOf(f.Name).Type()
			break loop
		case *ast.FuncLit:
			callerType = caller.Info.TypeOf(f)
			break loop
		}
	}

	// Non-trivial return conversions in the callee are permitted
	// if the same non-trivial conversion would occur after inlining,
	// i.e. if the caller and callee results tuples are identical.
	callerResults := callerType.(*types.Signature).Results()
	calleeResults := calleeSymbol.Type().(*types.Signature).Results()
	return types.Identical(callerResults, calleeResults)
}

// hasNonTrivialReturn reports whether any of the returns involve a nontrivial
// implicit conversion of a result expression.
func hasNonTrivialReturn(returnInfo [][]returnOperandFlags) bool {
	for _, resultInfo := range returnInfo {
		for _, r := range resultInfo {
			if r&nonTrivialResult != 0 {
				return true
			}
		}
	}
	return false
}

// soleUse returns the ident that refers to obj, if there is exactly one.
func soleUse(info *types.Info, obj types.Object) (sole *ast.Ident) {
	// This is not efficient, but it is called infrequently.
	for id, obj2 := range info.Uses {
		if obj2 == obj {
			if sole != nil {
				return nil // not unique
			}
			sole = id
		}
	}
	return sole
}

type unit struct{} // for representing sets as maps

// slicesDeleteFunc removes any elements from s for which del returns true,
// returning the modified slice.
// slicesDeleteFunc zeroes the elements between the new length and the original length.
// TODO(adonovan): use go1.21 slices.DeleteFunc
func slicesDeleteFunc[S ~[]E, E any](s S, del func(E) bool) S {
	i := slicesIndexFunc(s, del)
	if i == -1 {
		return s
	}
	// Don't start copying elements until we find one to delete.
	for j := i + 1; j < len(s); j++ {
		if v := s[j]; !del(v) {
			s[i] = v
			i++
		}
	}
	//	clear(s[i:]) // zero/nil out the obsolete elements, for GC
	return s[:i]
}

// slicesIndexFunc returns the first index i satisfying f(s[i]),
// or -1 if none do.
func slicesIndexFunc[S ~[]E, E any](s S, f func(E) bool) int {
	for i := range s {
		if f(s[i]) {
			return i
		}
	}
	return -1
}