golang.org/x/tools@v0.21.0/internal/refactor/inline/inline_test.go (about) 1 // Copyright 2023 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package inline_test 6 7 import ( 8 "bytes" 9 "crypto/sha256" 10 "encoding/binary" 11 "encoding/gob" 12 "fmt" 13 "go/ast" 14 "go/parser" 15 "go/token" 16 "go/types" 17 "os" 18 "path/filepath" 19 "reflect" 20 "regexp" 21 "strings" 22 "testing" 23 "unsafe" 24 25 "golang.org/x/tools/go/ast/astutil" 26 "golang.org/x/tools/go/expect" 27 "golang.org/x/tools/go/packages" 28 "golang.org/x/tools/go/types/typeutil" 29 "golang.org/x/tools/internal/diff" 30 "golang.org/x/tools/internal/refactor/inline" 31 "golang.org/x/tools/internal/testenv" 32 "golang.org/x/tools/txtar" 33 ) 34 35 // TestData executes test scenarios specified by files in testdata/*.txtar. 36 func TestData(t *testing.T) { 37 testenv.NeedsGoPackages(t) 38 39 files, err := filepath.Glob("testdata/*.txtar") 40 if err != nil { 41 t.Fatal(err) 42 } 43 for _, file := range files { 44 file := file 45 t.Run(filepath.Base(file), func(t *testing.T) { 46 t.Parallel() 47 48 // The few tests that use cgo should be in 49 // files whose name includes "cgo". 50 if strings.Contains(t.Name(), "cgo") { 51 testenv.NeedsTool(t, "cgo") 52 } 53 54 // Extract archive to temporary tree. 55 ar, err := txtar.ParseFile(file) 56 if err != nil { 57 t.Fatal(err) 58 } 59 dir := t.TempDir() 60 if err := extractTxtar(ar, dir); err != nil { 61 t.Fatal(err) 62 } 63 64 // Load packages. 65 cfg := &packages.Config{ 66 Dir: dir, 67 Mode: packages.LoadAllSyntax, 68 Env: append(os.Environ(), 69 "GO111MODULES=on", 70 "GOPATH=", 71 "GOWORK=off", 72 "GOPROXY=off"), 73 } 74 pkgs, err := packages.Load(cfg, "./...") 75 if err != nil { 76 t.Errorf("Load: %v", err) 77 } 78 // Report parse/type errors; they may be benign. 79 packages.Visit(pkgs, nil, func(pkg *packages.Package) { 80 for _, err := range pkg.Errors { 81 t.Log(err) 82 } 83 }) 84 85 // Process @inline notes in comments in initial packages. 86 for _, pkg := range pkgs { 87 for _, file := range pkg.Syntax { 88 // Read file content (for @inline regexp, and inliner). 89 content, err := os.ReadFile(pkg.Fset.File(file.Pos()).Name()) 90 if err != nil { 91 t.Error(err) 92 continue 93 } 94 95 // Read and process @inline notes. 96 notes, err := expect.ExtractGo(pkg.Fset, file) 97 if err != nil { 98 t.Errorf("parsing notes in %q: %v", pkg.Fset.File(file.Pos()).Name(), err) 99 continue 100 } 101 for _, note := range notes { 102 posn := pkg.Fset.PositionFor(note.Pos, false) 103 if note.Name != "inline" { 104 t.Errorf("%s: invalid marker @%s", posn, note.Name) 105 continue 106 } 107 if nargs := len(note.Args); nargs != 2 { 108 t.Errorf("@inline: want 2 args, got %d", nargs) 109 continue 110 } 111 pattern, ok := note.Args[0].(*regexp.Regexp) 112 if !ok { 113 t.Errorf("%s: @inline(rx, want): want regular expression rx", posn) 114 continue 115 } 116 117 // want is a []byte (success) or *Regexp (failure) 118 var want any 119 switch x := note.Args[1].(type) { 120 case string, expect.Identifier: 121 for _, file := range ar.Files { 122 if file.Name == fmt.Sprint(x) { 123 want = file.Data 124 break 125 } 126 } 127 if want == nil { 128 t.Errorf("%s: @inline(rx, want): archive entry %q not found", posn, x) 129 continue 130 } 131 case *regexp.Regexp: 132 want = x 133 default: 134 t.Errorf("%s: @inline(rx, want): want file name (to assert success) or error message regexp (to assert failure)", posn) 135 continue 136 } 137 if err := doInlineNote(t.Logf, pkg, file, content, pattern, posn, want); err != nil { 138 t.Errorf("%s: @inline(%v, %v): %v", posn, note.Args[0], note.Args[1], err) 139 continue 140 } 141 } 142 } 143 } 144 }) 145 } 146 } 147 148 // doInlineNote executes an assertion specified by a single 149 // @inline(re"pattern", want) note in a comment. It finds the first 150 // match of regular expression 'pattern' on the same line, finds the 151 // innermost enclosing CallExpr, and inlines it. 152 // 153 // Finally it checks that, on success, the transformed file is equal 154 // to want (a []byte), or on failure that the error message matches 155 // want (a *Regexp). 156 func doInlineNote(logf func(string, ...any), pkg *packages.Package, file *ast.File, content []byte, pattern *regexp.Regexp, posn token.Position, want any) error { 157 // Find extent of pattern match within commented line. 158 var startPos, endPos token.Pos 159 { 160 tokFile := pkg.Fset.File(file.Pos()) 161 lineStartOffset := int(tokFile.LineStart(posn.Line)) - tokFile.Base() 162 line := content[lineStartOffset:] 163 if i := bytes.IndexByte(line, '\n'); i >= 0 { 164 line = line[:i] 165 } 166 matches := pattern.FindSubmatchIndex(line) 167 var start, end int // offsets 168 switch len(matches) { 169 case 2: 170 // no subgroups: return the range of the regexp expression 171 start, end = matches[0], matches[1] 172 case 4: 173 // one subgroup: return its range 174 start, end = matches[2], matches[3] 175 default: 176 return fmt.Errorf("invalid location regexp %q: expect either 0 or 1 subgroups, got %d", 177 pattern, len(matches)/2-1) 178 } 179 startPos = tokFile.Pos(lineStartOffset + start) 180 endPos = tokFile.Pos(lineStartOffset + end) 181 } 182 183 // Find innermost call enclosing the pattern match. 184 var caller *inline.Caller 185 { 186 path, _ := astutil.PathEnclosingInterval(file, startPos, endPos) 187 for _, n := range path { 188 if call, ok := n.(*ast.CallExpr); ok { 189 caller = &inline.Caller{ 190 Fset: pkg.Fset, 191 Types: pkg.Types, 192 Info: pkg.TypesInfo, 193 File: file, 194 Call: call, 195 Content: content, 196 } 197 break 198 } 199 } 200 if caller == nil { 201 return fmt.Errorf("no enclosing call") 202 } 203 } 204 205 // Is it a static function call? 206 fn := typeutil.StaticCallee(caller.Info, caller.Call) 207 if fn == nil { 208 return fmt.Errorf("cannot inline: not a static call") 209 } 210 211 // Find callee function. 212 var calleePkg *packages.Package 213 { 214 // Is the call within the package? 215 if fn.Pkg() == caller.Types { 216 calleePkg = pkg // same as caller 217 } else { 218 // Different package. Load it now. 219 // (The primary load loaded all dependencies, 220 // but we choose to load it again, with 221 // a distinct token.FileSet and types.Importer, 222 // to keep the implementation honest.) 223 cfg := &packages.Config{ 224 // TODO(adonovan): get the original module root more cleanly 225 Dir: filepath.Dir(filepath.Dir(pkg.GoFiles[0])), 226 Fset: token.NewFileSet(), 227 Mode: packages.LoadSyntax, 228 } 229 roots, err := packages.Load(cfg, fn.Pkg().Path()) 230 if err != nil { 231 return fmt.Errorf("loading callee package: %v", err) 232 } 233 if packages.PrintErrors(roots) > 0 { 234 return fmt.Errorf("callee package had errors") // (see log) 235 } 236 calleePkg = roots[0] 237 } 238 } 239 240 calleeDecl, err := findFuncByPosition(calleePkg, caller.Fset.PositionFor(fn.Pos(), false)) 241 if err != nil { 242 return err 243 } 244 245 // Do the inlining. For the purposes of the test, 246 // AnalyzeCallee and Inline are a single operation. 247 res, err := func() (*inline.Result, error) { 248 filename := calleePkg.Fset.File(calleeDecl.Pos()).Name() 249 content, err := os.ReadFile(filename) 250 if err != nil { 251 return nil, err 252 } 253 callee, err := inline.AnalyzeCallee( 254 logf, 255 calleePkg.Fset, 256 calleePkg.Types, 257 calleePkg.TypesInfo, 258 calleeDecl, 259 content) 260 if err != nil { 261 return nil, err 262 } 263 264 if err := checkTranscode(callee); err != nil { 265 return nil, err 266 } 267 268 check := checkNoMutation(caller.File) 269 defer check() 270 return inline.Inline(caller, callee, &inline.Options{Logf: logf}) 271 }() 272 if err != nil { 273 if wantRE, ok := want.(*regexp.Regexp); ok { 274 if !wantRE.MatchString(err.Error()) { 275 return fmt.Errorf("Inline failed with wrong error: %v (want error matching %q)", err, want) 276 } 277 return nil // expected error 278 } 279 return fmt.Errorf("Inline failed: %v", err) // success was expected 280 } 281 282 // Inline succeeded. 283 got := res.Content 284 if want, ok := want.([]byte); ok { 285 got = append(bytes.TrimSpace(got), '\n') 286 want = append(bytes.TrimSpace(want), '\n') 287 if diff := diff.Unified("want", "got", string(want), string(got)); diff != "" { 288 return fmt.Errorf("Inline returned wrong output:\n%s\nWant:\n%s\nDiff:\n%s", 289 got, want, diff) 290 } 291 return nil 292 } 293 return fmt.Errorf("Inline succeeded unexpectedly: want error matching %q, got <<%s>>", want, got) 294 295 } 296 297 // findFuncByPosition returns the FuncDecl at the specified (package-agnostic) position. 298 func findFuncByPosition(pkg *packages.Package, posn token.Position) (*ast.FuncDecl, error) { 299 same := func(decl *ast.FuncDecl) bool { 300 // We can't rely on columns in export data: 301 // some variants replace it with 1. 302 // We can't expect file names to have the same prefix. 303 // export data for go1.20 std packages have $GOROOT written in 304 // them, so how are we supposed to find the source? Yuck! 305 // Ugh. need to samefile? Nope $GOROOT just won't work 306 // This is highly client specific anyway. 307 posn2 := pkg.Fset.PositionFor(decl.Name.Pos(), false) 308 return posn.Filename == posn2.Filename && 309 posn.Line == posn2.Line 310 } 311 for _, file := range pkg.Syntax { 312 for _, decl := range file.Decls { 313 if decl, ok := decl.(*ast.FuncDecl); ok && same(decl) { 314 return decl, nil 315 } 316 } 317 } 318 return nil, fmt.Errorf("can't find FuncDecl at %v in package %q", posn, pkg.PkgPath) 319 } 320 321 // Each callee must declare a function or method named f, 322 // and each caller must call it. 323 const funcName = "f" 324 325 // A testcase is an item in a table-driven test. 326 // 327 // The table-driven tests are less flexible, but enable more compact 328 // expression of single-package test cases than is possible with the 329 // txtar notation. 330 // 331 // TODO(adonovan): improve coverage of the cross product of each 332 // strategy with the checklist of concerns enumerated in the package 333 // doc comment. 334 type testcase struct { 335 descr string // description; substrings enable options (e.g. "IgnoreEffects") 336 callee, caller string // Go source files (sans package decl) of caller, callee 337 want string // expected new portion of caller file, or "error: regexp" 338 } 339 340 func TestErrors(t *testing.T) { 341 runTests(t, []testcase{ 342 { 343 "Generic functions are not yet supported.", 344 `func f[T any](x T) T { return x }`, 345 `var _ = f(0)`, 346 `error: type parameters are not yet supported`, 347 }, 348 { 349 "Methods on generic types are not yet supported.", 350 `type G[T any] struct{}; func (G[T]) f(x T) T { return x }`, 351 `var _ = G[int]{}.f(0)`, 352 `error: type parameters are not yet supported`, 353 }, 354 }) 355 } 356 357 func TestBasics(t *testing.T) { 358 runTests(t, []testcase{ 359 { 360 "Basic", 361 `func f(x int) int { return x }`, 362 `var _ = f(0)`, 363 `var _ = 0`, 364 }, 365 { 366 "Empty body, no arg effects.", 367 `func f(x, y int) {}`, 368 `func _() { f(1, 2) }`, 369 `func _() {}`, 370 }, 371 { 372 "Empty body, some arg effects.", 373 `func f(x, y, z int) {}`, 374 `func _() { f(1, recover().(int), 3) }`, 375 `func _() { _ = recover().(int) }`, 376 }, 377 { 378 "Non-duplicable arguments are not substituted even if pure.", 379 `func f(s string, i int) { print(s, s, i, i) }`, 380 `func _() { f("hi", 0) }`, 381 `func _() { 382 var s string = "hi" 383 print(s, s, 0, 0) 384 }`, 385 }, 386 { 387 "Workaround for T(x) misformatting (#63362).", 388 `func f(ch <-chan int) { <-ch }`, 389 `func _(ch chan int) { f(ch) }`, 390 `func _(ch chan int) { <-(<-chan int)(ch) }`, 391 }, 392 { 393 // (a regression test for unnecessary braces) 394 "In block elision, blank decls don't count when computing name conflicts.", 395 `func f(x int) { var _ = x; var _ = 3 }`, 396 `func _() { var _ = 1; f(2) }`, 397 `func _() { 398 var _ = 1 399 var _ = 2 400 var _ = 3 401 }`, 402 }, 403 { 404 // (a regression test for a missing conversion) 405 "Implicit return conversions are inserted in expr-context reduction.", 406 `func f(x int) error { return nil }`, 407 `func _() { if err := f(0); err != nil {} }`, 408 `func _() { 409 if err := error(nil); err != nil { 410 } 411 }`, 412 }, 413 }) 414 } 415 416 func TestDuplicable(t *testing.T) { 417 runTests(t, []testcase{ 418 { 419 "Empty strings are duplicable.", 420 `func f(s string) { print(s, s) }`, 421 `func _() { f("") }`, 422 `func _() { print("", "") }`, 423 }, 424 { 425 "Non-empty string literals are not duplicable.", 426 `func f(s string) { print(s, s) }`, 427 `func _() { f("hi") }`, 428 `func _() { 429 var s string = "hi" 430 print(s, s) 431 }`, 432 }, 433 { 434 "Empty array literals are duplicable.", 435 `func f(a [2]int) { print(a, a) }`, 436 `func _() { f([2]int{}) }`, 437 `func _() { print([2]int{}, [2]int{}) }`, 438 }, 439 { 440 "Non-empty array literals are not duplicable.", 441 `func f(a [2]int) { print(a, a) }`, 442 `func _() { f([2]int{1, 2}) }`, 443 `func _() { 444 var a [2]int = [2]int{1, 2} 445 print(a, a) 446 }`, 447 }, 448 { 449 "Empty struct literals are duplicable.", 450 `func f(s S) { print(s, s) }; type S struct { x int }`, 451 `func _() { f(S{}) }`, 452 `func _() { print(S{}, S{}) }`, 453 }, 454 { 455 "Non-empty struct literals are not duplicable.", 456 `func f(s S) { print(s, s) }; type S struct { x int }`, 457 `func _() { f(S{x: 1}) }`, 458 `func _() { 459 var s S = S{x: 1} 460 print(s, s) 461 }`, 462 }, 463 }) 464 } 465 466 func TestExprStmtReduction(t *testing.T) { 467 runTests(t, []testcase{ 468 { 469 "A call in an unrestricted ExprStmt may be replaced by the body stmts.", 470 `func f() { var _ = len("") }`, 471 `func _() { f() }`, 472 `func _() { var _ = len("") }`, 473 }, 474 { 475 "ExprStmts in the body of a switch case are unrestricted.", 476 `func f() { x := 1; print(x) }`, 477 `func _() { switch { case true: f() } }`, 478 `func _() { 479 switch { 480 case true: 481 x := 1 482 print(x) 483 } 484 }`, 485 }, 486 { 487 "ExprStmts in the body of a select case are unrestricted.", 488 `func f() { x := 1; print(x) }`, 489 `func _() { select { default: f() } }`, 490 `func _() { 491 select { 492 default: 493 x := 1 494 print(x) 495 } 496 }`, 497 }, 498 { 499 "Some ExprStmt contexts are restricted to simple statements.", 500 `func f() { var _ = len("") }`, 501 `func _(cond bool) { if f(); cond {} }`, 502 `func _(cond bool) { 503 if func() { var _ = len("") }(); cond { 504 } 505 }`, 506 }, 507 { 508 "Braces must be preserved to avoid a name conflict (decl before).", 509 `func f() { x := 1; print(x) }`, 510 `func _() { x := 2; print(x); f() }`, 511 `func _() { 512 x := 2 513 print(x) 514 { 515 x := 1 516 print(x) 517 } 518 }`, 519 }, 520 { 521 "Braces must be preserved to avoid a name conflict (decl after).", 522 `func f() { x := 1; print(x) }`, 523 `func _() { f(); x := 2; print(x) }`, 524 `func _() { 525 { 526 x := 1 527 print(x) 528 } 529 x := 2 530 print(x) 531 }`, 532 }, 533 { 534 "Braces must be preserved to avoid a forward jump across a decl.", 535 `func f() { x := 1; print(x) }`, 536 `func _() { goto label; f(); label: }`, 537 `func _() { 538 goto label 539 { 540 x := 1 541 print(x) 542 } 543 label: 544 }`, 545 }, 546 }) 547 } 548 549 func TestPrecedenceParens(t *testing.T) { 550 // Ensure that parens are inserted when (and only when) necessary 551 // around the replacement for the call expression. (This is a special 552 // case in the way the inliner uses a combination of AST formatting 553 // for the call and text splicing for the rest of the file.) 554 runTests(t, []testcase{ 555 { 556 "Multiplication in addition context (no parens).", 557 `func f(x, y int) int { return x * y }`, 558 `func _() { _ = 1 + f(2, 3) }`, 559 `func _() { _ = 1 + 2*3 }`, 560 }, 561 { 562 "Addition in multiplication context (parens).", 563 `func f(x, y int) int { return x + y }`, 564 `func _() { _ = 1 * f(2, 3) }`, 565 `func _() { _ = 1 * (2 + 3) }`, 566 }, 567 { 568 "Addition in negation context (parens).", 569 `func f(x, y int) int { return x + y }`, 570 `func _() { _ = -f(1, 2) }`, 571 `func _() { _ = -(1 + 2) }`, 572 }, 573 { 574 "Addition in call context (no parens).", 575 `func f(x, y int) int { return x + y }`, 576 `func _() { println(f(1, 2)) }`, 577 `func _() { println(1 + 2) }`, 578 }, 579 { 580 "Addition in slice operand context (parens).", 581 `func f(x, y string) string { return x + y }`, 582 `func _() { _ = f("x", "y")[1:2] }`, 583 `func _() { _ = ("x" + "y")[1:2] }`, 584 }, 585 { 586 "String literal in slice operand context (no parens).", 587 `func f(x string) string { return x }`, 588 `func _() { _ = f("xy")[1:2] }`, 589 `func _() { _ = "xy"[1:2] }`, 590 }, 591 }) 592 } 593 594 func TestSubstitution(t *testing.T) { 595 runTests(t, []testcase{ 596 { 597 "Arg to unref'd param can be eliminated if has no effects.", 598 `func f(x, y int) {}; var global int`, 599 `func _() { f(0, global) }`, 600 `func _() {}`, 601 }, 602 { 603 "But not if it may contain last reference to a caller local var.", 604 `func f(int) {}`, 605 `func _() { var local int; f(local) }`, 606 `func _() { var local int; _ = local }`, 607 }, 608 { 609 "Arguments that are used are detected", 610 `func f(int) {}`, 611 `func _() { var local int; _ = local; f(local) }`, 612 `func _() { var local int; _ = local }`, 613 }, 614 { 615 "Arguments that are used are detected", 616 `func f(x, y int) { print(x) }`, 617 `func _() { var z int; f(z, z) }`, 618 `func _() { 619 var z int 620 var _ int = z 621 print(z) 622 }`, 623 }, 624 { 625 "Function parameters are always used", 626 `func f(int) {}`, 627 `func _() { 628 func(local int) { 629 f(local) 630 }(1) 631 }`, 632 `func _() { 633 func(local int) { 634 635 }(1) 636 }`, 637 }, 638 { 639 "Regression test for detection of shadowing in nested functions.", 640 `func f(x int) { _ = func() { y := 1; print(y); print(x) } }`, 641 `func _(y int) { f(y) } `, 642 `func _(y int) { 643 var x int = y 644 _ = func() { y := 1; print(y); print(x) } 645 }`, 646 }, 647 }) 648 } 649 650 func TestTailCallStrategy(t *testing.T) { 651 runTests(t, []testcase{ 652 { 653 "simple", 654 `func f() int { return 1 }`, 655 `func _() int { return f() }`, 656 `func _() int { return 1 }`, 657 }, 658 { 659 "void", 660 `func f() { println() }`, 661 `func _() { f() }`, 662 `func _() { println() }`, 663 }, 664 { 665 "void with defer", // => literalized 666 `func f() { defer f(); println() }`, 667 `func _() { f() }`, 668 `func _() { func() { defer f(); println() }() }`, 669 }, 670 // Tests for issue #63336: 671 { 672 "non-trivial return conversion (caller.sig = callee.sig)", 673 `func f() error { if true { return nil } else { return e } }; var e struct{error}`, 674 `func _() error { return f() }`, 675 `func _() error { 676 if true { 677 return nil 678 } else { 679 return e 680 } 681 }`, 682 }, 683 { 684 "non-trivial return conversion (caller.sig != callee.sig)", 685 `func f() error { return E{} }; type E struct{error}`, 686 `func _() any { return f() }`, 687 `func _() any { return error(E{}) }`, 688 }, 689 }) 690 } 691 692 func TestSpreadCalls(t *testing.T) { 693 runTests(t, []testcase{ 694 { 695 "Edge case: cannot literalize spread method call.", 696 `type I int 697 func g() (I, I) 698 func (r I) f(x, y I) I { 699 defer g() // force literalization 700 return x + y + r 701 }`, 702 `func _() I { return recover().(I).f(g()) }`, 703 `error: can't yet inline spread call to method`, 704 }, 705 { 706 "Spread argument evaluated for effect.", 707 `func f(int, int) {}; func g() (int, int)`, 708 `func _() { f(g()) }`, 709 `func _() { _, _ = g() }`, 710 }, 711 { 712 "Edge case: receiver and spread argument, both evaluated for effect.", 713 `type T int; func (T) f(int, int) {}; func g() (int, int)`, 714 `func _() { T(0).f(g()) }`, 715 `func _() { 716 var ( 717 _ = T(0) 718 _, _ = g() 719 ) 720 }`, 721 }, 722 { 723 "Spread call in return (#63398).", 724 `func f() (int, error) { return 0, nil }`, 725 `func _() (int, error) { return f() }`, 726 `func _() (int, error) { return 0, nil }`, 727 }, 728 }) 729 } 730 731 func TestAssignmentCallStrategy(t *testing.T) { 732 runTests(t, []testcase{ 733 { 734 "splice: basic", 735 `func f(x int) (int, int) { return x, 2 }`, 736 `func _() { x, y := f(1); _, _ = x, y }`, 737 `func _() { x, y := 1, 2; _, _ = x, y }`, 738 }, 739 { 740 "spread: basic", 741 `func f(x int) (any, any) { return g() }; func g() (error, error) { return nil, nil }`, 742 `func _() { 743 var x any 744 x, y := f(0) 745 _, _ = x, y 746 }`, 747 `func _() { 748 var x any 749 var y any 750 x, y = g() 751 _, _ = x, y 752 }`, 753 }, 754 { 755 "spread: free var conflict", 756 `func f(x int) (any, any) { return g(x) }; func g(x int) (int, int) { return x, x }`, 757 `func _() { 758 y := 2 759 { 760 var x any 761 x, y := f(y) 762 _, _ = x, y 763 } 764 }`, 765 `func _() { 766 y := 2 767 { 768 var x any 769 x, y := func() (any, any) { return g(y) }() 770 _, _ = x, y 771 } 772 }`, 773 }, 774 { 775 "convert: basic", 776 `func f(x int) (int32, int8) { return 1, 2 }`, 777 `func _() { 778 var x int32 779 x, y := f(0) 780 _, _ = x, y 781 }`, 782 `func _() { 783 var x int32 784 x, y := 1, int8(2) 785 _, _ = x, y 786 }`, 787 }, 788 { 789 "convert: rune and byte", 790 `func f(x int) (rune, byte) { return 0, 0 }`, 791 `func _() { 792 x, y := f(0) 793 _, _ = x, y 794 }`, 795 `func _() { 796 x, y := rune(0), byte(0) 797 _, _ = x, y 798 }`, 799 }, 800 { 801 "convert: interface conversions", 802 `func f(x int) (_, _ error) { return nil, nil }`, 803 `func _() { 804 x, y := f(0) 805 _, _ = x, y 806 }`, 807 `func _() { 808 x, y := error(nil), error(nil) 809 _, _ = x, y 810 }`, 811 }, 812 { 813 "convert: implicit nil conversions", 814 `func f(x int) (_, _ error) { return nil, nil }`, 815 `func _() { x, y := f(0); _, _ = x, y }`, 816 `func _() { x, y := error(nil), error(nil); _, _ = x, y }`, 817 }, 818 { 819 "convert: pruning nil assignments left", 820 `func f(x int) (_, _ error) { return nil, nil }`, 821 `func _() { _, y := f(0); _ = y }`, 822 `func _() { y := error(nil); _ = y }`, 823 }, 824 { 825 "convert: pruning nil assignments right", 826 `func f(x int) (_, _ error) { return nil, nil }`, 827 `func _() { x, _ := f(0); _ = x }`, 828 `func _() { x := error(nil); _ = x }`, 829 }, 830 { 831 "convert: partial assign", 832 `func f(x int) (_, _ error) { return nil, nil }`, 833 `func _() { 834 var x error 835 x, y := f(0) 836 _, _ = x, y 837 }`, 838 `func _() { 839 var x error 840 x, y := nil, error(nil) 841 _, _ = x, y 842 }`, 843 }, 844 { 845 "convert: single assignment left", 846 `func f() int { return 0 }`, 847 `func _() { 848 x, y := f(), "hello" 849 _, _ = x, y 850 }`, 851 `func _() { 852 x, y := 0, "hello" 853 _, _ = x, y 854 }`, 855 }, 856 { 857 "convert: single assignment left with conversion", 858 `func f() int32 { return 0 }`, 859 `func _() { 860 x, y := f(), "hello" 861 _, _ = x, y 862 }`, 863 `func _() { 864 x, y := int32(0), "hello" 865 _, _ = x, y 866 }`, 867 }, 868 { 869 "convert: single assignment right", 870 `func f() int32 { return 0 }`, 871 `func _() { 872 x, y := "hello", f() 873 _, _ = x, y 874 }`, 875 `func _() { 876 x, y := "hello", int32(0) 877 _, _ = x, y 878 }`, 879 }, 880 { 881 "convert: single assignment middle", 882 `func f() int32 { return 0 }`, 883 `func _() { 884 x, y, z := "hello", f(), 1.56 885 _, _, _ = x, y, z 886 }`, 887 `func _() { 888 x, y, z := "hello", int32(0), 1.56 889 _, _, _ = x, y, z 890 }`, 891 }, 892 }) 893 } 894 895 func TestVariadic(t *testing.T) { 896 runTests(t, []testcase{ 897 { 898 "Variadic cancellation (basic).", 899 `func f(args ...any) { defer f(&args); println(args) }`, 900 `func _(slice []any) { f(slice...) }`, 901 `func _(slice []any) { func() { var args []any = slice; defer f(&args); println(args) }() }`, 902 }, 903 { 904 "Variadic cancellation (literalization with parameter elimination).", 905 `func f(args ...any) { defer f(); println(args) }`, 906 `func _(slice []any) { f(slice...) }`, 907 `func _(slice []any) { func() { defer f(); println(slice) }() }`, 908 }, 909 { 910 "Variadic cancellation (reduction).", 911 `func f(args ...any) { println(args) }`, 912 `func _(slice []any) { f(slice...) }`, 913 `func _(slice []any) { println(slice) }`, 914 }, 915 { 916 "Variadic elimination (literalization).", 917 `func f(x any, rest ...any) { defer println(x, rest) }`, // defer => literalization 918 `func _() { f(1, 2, 3) }`, 919 `func _() { func() { defer println(any(1), []any{2, 3}) }() }`, 920 }, 921 { 922 "Variadic elimination (reduction).", 923 `func f(x int, rest ...int) { println(x, rest) }`, 924 `func _() { f(1, 2, 3) }`, 925 `func _() { println(1, []int{2, 3}) }`, 926 }, 927 { 928 "Spread call to variadic (1 arg, 1 param).", 929 `func f(rest ...int) { println(rest) }; func g() (a, b int)`, 930 `func _() { f(g()) }`, 931 `func _() { func(rest ...int) { println(rest) }(g()) }`, 932 }, 933 { 934 "Spread call to variadic (1 arg, 2 params).", 935 `func f(x int, rest ...int) { println(x, rest) }; func g() (a, b int)`, 936 `func _() { f(g()) }`, 937 `func _() { func(x int, rest ...int) { println(x, rest) }(g()) }`, 938 }, 939 { 940 "Spread call to variadic (1 arg, 3 params).", 941 `func f(x, y int, rest ...int) { println(x, y, rest) }; func g() (a, b, c int)`, 942 `func _() { f(g()) }`, 943 `func _() { func(x, y int, rest ...int) { println(x, y, rest) }(g()) }`, 944 }, 945 }) 946 } 947 948 func TestParameterBindingDecl(t *testing.T) { 949 runTests(t, []testcase{ 950 { 951 "IncDec counts as assignment.", 952 `func f(x int) { x++ }`, 953 `func _() { f(1) }`, 954 `func _() { 955 var x int = 1 956 x++ 957 }`, 958 }, 959 { 960 "Binding declaration (x, y, z eliminated).", 961 `func f(w, x, y any, z int) { println(w, y, z) }; func g(int) int`, 962 `func _() { f(g(0), g(1), g(2), g(3)) }`, 963 `func _() { 964 var w, _ any = g(0), g(1) 965 println(w, any(g(2)), g(3)) 966 }`, 967 }, 968 { 969 "Reduction of stmt-context call to { return exprs }, with substitution", 970 `func f(ch chan int) int { return <-ch }; func g() chan int`, 971 `func _() { f(g()) }`, 972 `func _() { <-g() }`, 973 }, 974 { 975 // Same again, with callee effects: 976 "Binding decl in reduction of stmt-context call to { return exprs }", 977 `func f(x int) int { return <-h(g(2), x) }; func g(int) int; func h(int, int) chan int`, 978 `func _() { f(g(1)) }`, 979 `func _() { 980 var x int = g(1) 981 <-h(g(2), x) 982 }`, 983 }, 984 { 985 "No binding decl due to shadowing of int", 986 `func f(int, y any, z int) { defer g(0); println(int, y, z) }; func g(int) int`, 987 `func _() { f(g(1), g(2), g(3)) }`, 988 `func _() { func(int, y any, z int) { defer g(0); println(int, y, z) }(g(1), g(2), g(3)) }`, 989 }, 990 { 991 "An indirect method selection (*x).g acts as a read.", 992 `func f(x *T, y any) any { return x.g(y) }; type T struct{}; func (T) g(x any) any { return x }`, 993 `func _(x *T) { f(x, recover()) }`, 994 `func _(x *T) { 995 var y any = recover() 996 x.g(y) 997 }`, 998 }, 999 { 1000 "A direct method selection x.g is pure.", 1001 `func f(x *T, y any) any { return x.g(y) }; type T struct{}; func (*T) g(x any) any { return x }`, 1002 `func _(x *T) { f(x, recover()) }`, 1003 `func _(x *T) { x.g(recover()) }`, 1004 }, 1005 { 1006 "Literalization can make use of a binding decl (all params).", 1007 `func f(x, y int) int { defer println(); return y + x }; func g(int) int`, 1008 `func _() { println(f(g(1), g(2))) }`, 1009 `func _() { println(func() int { var x, y int = g(1), g(2); defer println(); return y + x }()) }`, 1010 }, 1011 { 1012 "Literalization can make use of a binding decl (some params).", 1013 `func f(x, y int) int { z := y + x; defer println(); return z }; func g(int) int`, 1014 `func _() { println(f(g(1), g(2))) }`, 1015 `func _() { println(func() int { var x int = g(1); z := g(2) + x; defer println(); return z }()) }`, 1016 }, 1017 { 1018 "Literalization can't yet use of a binding decl if named results.", 1019 `func f(x, y int) (z int) { z = y + x; defer println(); return }; func g(int) int`, 1020 `func _() { println(f(g(1), g(2))) }`, 1021 `func _() { println(func(x int) (z int) { z = g(2) + x; defer println(); return }(g(1))) }`, 1022 }, 1023 }) 1024 } 1025 1026 func TestEmbeddedFields(t *testing.T) { 1027 runTests(t, []testcase{ 1028 { 1029 "Embedded fields in x.f method selection (direct).", 1030 `type T int; func (t T) f() { print(t) }; type U struct{ T }`, 1031 `func _(u U) { u.f() }`, 1032 `func _(u U) { print(u.T) }`, 1033 }, 1034 { 1035 "Embedded fields in x.f method selection (implicit *).", 1036 `type ( T int; U struct{*T}; V struct {U} ); func (t T) f() { print(t) }`, 1037 `func _(v V) { v.f() }`, 1038 `func _(v V) { print(*v.U.T) }`, 1039 }, 1040 { 1041 "Embedded fields in x.f method selection (implicit &).", 1042 `type ( T int; U struct{T}; V struct {U} ); func (t *T) f() { print(t) }`, 1043 `func _(v V) { v.f() }`, 1044 `func _(v V) { print(&v.U.T) }`, 1045 }, 1046 // Now the same tests again with T.f(recv). 1047 { 1048 "Embedded fields in T.f method selection.", 1049 `type T int; func (t T) f() { print(t) }; type U struct{ T }`, 1050 `func _(u U) { U.f(u) }`, 1051 `func _(u U) { print(u.T) }`, 1052 }, 1053 { 1054 "Embedded fields in T.f method selection (implicit *).", 1055 `type ( T int; U struct{*T}; V struct {U} ); func (t T) f() { print(t) }`, 1056 `func _(v V) { V.f(v) }`, 1057 `func _(v V) { print(*v.U.T) }`, 1058 }, 1059 { 1060 "Embedded fields in (*T).f method selection.", 1061 `type ( T int; U struct{T}; V struct {U} ); func (t *T) f() { print(t) }`, 1062 `func _(v V) { (*V).f(&v) }`, 1063 `func _(v V) { print(&(&v).U.T) }`, 1064 }, 1065 { 1066 // x is a single-assign var, and x.f does not load through a pointer 1067 // (despite types.Selection.Indirect=true), so x is pure. 1068 "No binding decl is required for recv in method-to-method calls.", 1069 `type T struct{}; func (x *T) f() { g(); print(*x) }; func g()`, 1070 `func (x *T) _() { x.f() }`, 1071 `func (x *T) _() { 1072 g() 1073 print(*x) 1074 }`, 1075 }, 1076 { 1077 "Same, with implicit &recv.", 1078 `type T struct{}; func (x *T) f() { g(); print(*x) }; func g()`, 1079 `func (x T) _() { x.f() }`, 1080 `func (x T) _() { 1081 { 1082 var x *T = &x 1083 g() 1084 print(*x) 1085 } 1086 }`, 1087 }, 1088 }) 1089 } 1090 1091 func TestSubstitutionPreservesArgumentEffectOrder(t *testing.T) { 1092 runTests(t, []testcase{ 1093 { 1094 "Arguments have effects, but parameters are evaluated in order.", 1095 `func f(a, b, c int) { print(a, b, c) }; func g(int) int`, 1096 `func _() { f(g(1), g(2), g(3)) }`, 1097 `func _() { print(g(1), g(2), g(3)) }`, 1098 }, 1099 { 1100 "Arguments have effects, and parameters are evaluated out of order.", 1101 `func f(a, b, c int) { print(a, c, b) }; func g(int) int`, 1102 `func _() { f(g(1), g(2), g(3)) }`, 1103 `func _() { 1104 var a, b int = g(1), g(2) 1105 print(a, g(3), b) 1106 }`, 1107 }, 1108 { 1109 "Pure arguments may commute with argument that have effects.", 1110 `func f(a, b, c int) { print(a, c, b) }; func g(int) int`, 1111 `func _() { f(g(1), 2, g(3)) }`, 1112 `func _() { print(g(1), g(3), 2) }`, 1113 }, 1114 { 1115 "Impure arguments may commute with each other.", 1116 `func f(a, b, c, d int) { print(a, c, b, d) }; func g(int) int; var x, y int`, 1117 `func _() { f(g(1), x, y, g(2)) }`, 1118 `func _() { print(g(1), y, x, g(2)) }`, 1119 }, 1120 { 1121 "Impure arguments do not commute with arguments that have effects (1)", 1122 `func f(a, b, c, d int) { print(a, c, b, d) }; func g(int) int; var x, y int`, 1123 `func _() { f(g(1), g(2), y, g(3)) }`, 1124 `func _() { 1125 var a, b int = g(1), g(2) 1126 print(a, y, b, g(3)) 1127 }`, 1128 }, 1129 { 1130 "Impure arguments do not commute with those that have effects (2).", 1131 `func f(a, b, c, d int) { print(a, c, b, d) }; func g(int) int; var x, y int`, 1132 `func _() { f(g(1), y, g(2), g(3)) }`, 1133 `func _() { 1134 var a, b int = g(1), y 1135 print(a, g(2), b, g(3)) 1136 }`, 1137 }, 1138 { 1139 "Callee effects commute with pure arguments.", 1140 `func f(a, b, c int) { print(a, c, recover().(int), b) }; func g(int) int`, 1141 `func _() { f(g(1), 2, g(3)) }`, 1142 `func _() { print(g(1), g(3), recover().(int), 2) }`, 1143 }, 1144 { 1145 "Callee reads may commute with impure arguments.", 1146 `func f(a, b int) { print(a, x, b) }; func g(int) int; var x, y int`, 1147 `func _() { f(g(1), y) }`, 1148 `func _() { print(g(1), x, y) }`, 1149 }, 1150 { 1151 "All impure parameters preceding a read hazard must be kept.", 1152 `func f(a, b, c int) { print(a, b, recover().(int), c) }; var x, y, z int`, 1153 `func _() { f(x, y, z) }`, 1154 `func _() { 1155 var c int = z 1156 print(x, y, recover().(int), c) 1157 }`, 1158 }, 1159 { 1160 "All parameters preceding a write hazard must be kept.", 1161 `func f(a, b, c int) { print(a, b, recover().(int), c) }; func g(int) int; var x, y, z int`, 1162 `func _() { f(x, y, g(0)) }`, 1163 `func _() { 1164 var a, b, c int = x, y, g(0) 1165 print(a, b, recover().(int), c) 1166 }`, 1167 }, 1168 { 1169 "[W1 R0 W2 W4 R3] -- test case for second iteration of effect loop", 1170 `func f(a, b, c, d, e int) { print(b, a, c, e, d) }; func g(int) int; var x, y int`, 1171 `func _() { f(x, g(1), g(2), y, g(3)) }`, 1172 `func _() { 1173 var a, b, c, d int = x, g(1), g(2), y 1174 print(b, a, c, g(3), d) 1175 }`, 1176 }, 1177 { 1178 // In this example, the set() call is rejected as a substitution 1179 // candidate due to a shadowing conflict (x). This must entail that the 1180 // selection x.y (R) is also rejected, because it is lower numbered. 1181 // 1182 // Incidentally this program (which panics when executed) illustrates 1183 // that although effects occur left-to-right, read operations such 1184 // as x.y are not ordered wrt writes, depending on the compiler. 1185 // Changing x.y to identity(x).y forces the ordering and avoids the panic. 1186 "Hazards with args already rejected (e.g. due to shadowing) are detected too.", 1187 `func f(x, y int) int { return x + y }; func set[T any](ptr *T, old, new T) int { println(old); *ptr = new; return 0; }`, 1188 `func _() { x := new(struct{ y int }); f(x.y, set(&x, x, nil)) }`, 1189 `func _() { 1190 x := new(struct{ y int }) 1191 { 1192 var x, y int = x.y, set(&x, x, nil) 1193 _ = x + y 1194 } 1195 }`, 1196 }, 1197 { 1198 // Rejection of a later parameter for reasons other than callee 1199 // effects (e.g. escape) may create hazards with lower-numbered 1200 // parameters that require them to be rejected too. 1201 "Hazards with already eliminated parameters (variant)", 1202 `func f(x, y int) { _ = &y }; func g(int) int`, 1203 `func _() { f(g(1), g(2)) }`, 1204 `func _() { 1205 var _, y int = g(1), g(2) 1206 _ = &y 1207 }`, 1208 }, 1209 { 1210 // In this case g(2) is rejected for substitution because it is 1211 // unreferenced but has effects, so parameter x must also be rejected 1212 // so that its argument v can be evaluated earlier in the binding decl. 1213 "Hazards with already eliminated parameters (unreferenced fx variant)", 1214 `func f(x, y int) { _ = x }; func g(int) int; var v int`, 1215 `func _() { f(v, g(2)) }`, 1216 `func _() { 1217 var x, _ int = v, g(2) 1218 _ = x 1219 }`, 1220 }, 1221 { 1222 "Defer f() evaluates f() before unknown effects", 1223 `func f(int, y any, z int) { defer println(int, y, z) }; func g(int) int`, 1224 `func _() { f(g(1), g(2), g(3)) }`, 1225 `func _() { func() { defer println(any(g(1)), any(g(2)), g(3)) }() }`, 1226 }, 1227 { 1228 "Effects are ignored when IgnoreEffects", 1229 `func f(x, y int) { println(y, x) }; func g(int) int`, 1230 `func _() { f(g(1), g(2)) }`, 1231 `func _() { println(g(2), g(1)) }`, 1232 }, 1233 }) 1234 } 1235 1236 func TestNamedResultVars(t *testing.T) { 1237 runTests(t, []testcase{ 1238 { 1239 "Stmt-context call to {return g()} that mentions named result.", 1240 `func f() (x int) { return g(x) }; func g(int) int`, 1241 `func _() { f() }`, 1242 `func _() { 1243 var x int 1244 g(x) 1245 }`, 1246 }, 1247 { 1248 "Ditto, with binding decl again.", 1249 `func f(y string) (x int) { return x+x+len(y+y) }`, 1250 `func _() { f(".") }`, 1251 `func _() { 1252 var ( 1253 y string = "." 1254 x int 1255 ) 1256 _ = x + x + len(y+y) 1257 }`, 1258 }, 1259 1260 { 1261 "Ditto, with binding decl (due to repeated y refs).", 1262 `func f(y string) (x string) { return x+y+y }`, 1263 `func _() { f(".") }`, 1264 `func _() { 1265 var ( 1266 y string = "." 1267 x string 1268 ) 1269 _ = x + y + y 1270 }`, 1271 }, 1272 { 1273 "Stmt-context call to {return binary} that mentions named result.", 1274 `func f() (x int) { return x+x }`, 1275 `func _() { f() }`, 1276 `func _() { 1277 var x int 1278 _ = x + x 1279 }`, 1280 }, 1281 { 1282 "Tail call to {return expr} that mentions named result.", 1283 `func f() (x int) { return x }`, 1284 `func _() int { return f() }`, 1285 `func _() int { return func() (x int) { return x }() }`, 1286 }, 1287 { 1288 "Tail call to {return} that implicitly reads named result.", 1289 `func f() (x int) { return }`, 1290 `func _() int { return f() }`, 1291 `func _() int { return func() (x int) { return }() }`, 1292 }, 1293 { 1294 "Spread-context call to {return expr} that mentions named result.", 1295 `func f() (x, y int) { return x, y }`, 1296 `func _() { var _, _ = f() }`, 1297 `func _() { var _, _ = func() (x, y int) { return x, y }() }`, 1298 }, 1299 { 1300 "Shadowing in binding decl for named results => literalization.", 1301 `func f(y string) (x y) { return x+x+len(y+y) }; type y = int`, 1302 `func _() { f(".") }`, 1303 `func _() { func(y string) (x y) { return x + x + len(y+y) }(".") }`, 1304 }, 1305 }) 1306 } 1307 1308 func TestSubstitutionPreservesParameterType(t *testing.T) { 1309 runTests(t, []testcase{ 1310 { 1311 "Substitution preserves argument type (#63193).", 1312 `func f(x int16) { y := x; _ = (*int16)(&y) }`, 1313 `func _() { f(1) }`, 1314 `func _() { 1315 y := int16(1) 1316 _ = (*int16)(&y) 1317 }`, 1318 }, 1319 { 1320 "Same, with non-constant (unnamed to named struct) conversion.", 1321 `func f(x T) { y := x; _ = (*T)(&y) }; type T struct{}`, 1322 `func _() { f(struct{}{}) }`, 1323 `func _() { 1324 y := T(struct{}{}) 1325 _ = (*T)(&y) 1326 }`, 1327 }, 1328 { 1329 "Same, with non-constant (chan to <-chan) conversion.", 1330 `func f(x T) { y := x; _ = (*T)(&y) }; type T = <-chan int; var ch chan int`, 1331 `func _() { f(ch) }`, 1332 `func _() { 1333 y := T(ch) 1334 _ = (*T)(&y) 1335 }`, 1336 }, 1337 { 1338 "Same, with untyped nil to typed nil conversion.", 1339 `func f(x *int) { y := x; _ = (**int)(&y) }`, 1340 `func _() { f(nil) }`, 1341 `func _() { 1342 y := (*int)(nil) 1343 _ = (**int)(&y) 1344 }`, 1345 }, 1346 { 1347 "Conversion of untyped int to named type is made explicit.", 1348 `type T int; func (x T) f() { x.g() }; func (T) g() {}`, 1349 `func _() { T.f(1) }`, 1350 `func _() { T(1).g() }`, 1351 }, 1352 { 1353 "Check for shadowing error on type used in the conversion.", 1354 `func f(x T) { _ = &x == (*T)(nil) }; type T int16`, 1355 `func _() { type T bool; f(1) }`, 1356 `error: T.*shadowed.*by.*type`, 1357 }, 1358 }) 1359 } 1360 1361 func runTests(t *testing.T, tests []testcase) { 1362 for _, test := range tests { 1363 test := test 1364 t.Run(test.descr, func(t *testing.T) { 1365 fset := token.NewFileSet() 1366 mustParse := func(filename string, content any) *ast.File { 1367 f, err := parser.ParseFile(fset, filename, content, parser.ParseComments|parser.SkipObjectResolution) 1368 if err != nil { 1369 t.Fatalf("ParseFile: %v", err) 1370 } 1371 return f 1372 } 1373 1374 // Parse callee file and find first func decl named f. 1375 calleeContent := "package p\n" + test.callee 1376 calleeFile := mustParse("callee.go", calleeContent) 1377 var decl *ast.FuncDecl 1378 for _, d := range calleeFile.Decls { 1379 if d, ok := d.(*ast.FuncDecl); ok && d.Name.Name == funcName { 1380 decl = d 1381 break 1382 } 1383 } 1384 if decl == nil { 1385 t.Fatalf("declaration of func %s not found: %s", funcName, test.callee) 1386 } 1387 1388 // Parse caller file and find first call to f(). 1389 callerContent := "package p\n" + test.caller 1390 callerFile := mustParse("caller.go", callerContent) 1391 var call *ast.CallExpr 1392 ast.Inspect(callerFile, func(n ast.Node) bool { 1393 if n, ok := n.(*ast.CallExpr); ok { 1394 switch fun := n.Fun.(type) { 1395 case *ast.SelectorExpr: 1396 if fun.Sel.Name == funcName { 1397 call = n 1398 } 1399 case *ast.Ident: 1400 if fun.Name == funcName { 1401 call = n 1402 } 1403 } 1404 } 1405 return call == nil 1406 }) 1407 if call == nil { 1408 t.Fatalf("call to %s not found: %s", funcName, test.caller) 1409 } 1410 1411 // Type check both files as one package. 1412 info := &types.Info{ 1413 Defs: make(map[*ast.Ident]types.Object), 1414 Uses: make(map[*ast.Ident]types.Object), 1415 Types: make(map[ast.Expr]types.TypeAndValue), 1416 Implicits: make(map[ast.Node]types.Object), 1417 Selections: make(map[*ast.SelectorExpr]*types.Selection), 1418 Scopes: make(map[ast.Node]*types.Scope), 1419 } 1420 conf := &types.Config{Error: func(err error) { t.Error(err) }} 1421 pkg, err := conf.Check("p", fset, []*ast.File{callerFile, calleeFile}, info) 1422 if err != nil { 1423 t.Fatal("transformation introduced type errors") 1424 } 1425 1426 // Analyze callee and inline call. 1427 doIt := func() (*inline.Result, error) { 1428 callee, err := inline.AnalyzeCallee(t.Logf, fset, pkg, info, decl, []byte(calleeContent)) 1429 if err != nil { 1430 return nil, err 1431 } 1432 if err := checkTranscode(callee); err != nil { 1433 t.Fatal(err) 1434 } 1435 1436 caller := &inline.Caller{ 1437 Fset: fset, 1438 Types: pkg, 1439 Info: info, 1440 File: callerFile, 1441 Call: call, 1442 Content: []byte(callerContent), 1443 } 1444 check := checkNoMutation(caller.File) 1445 defer check() 1446 return inline.Inline(caller, callee, &inline.Options{ 1447 Logf: t.Logf, 1448 IgnoreEffects: strings.Contains(test.descr, "IgnoreEffects"), 1449 }) 1450 } 1451 res, err := doIt() 1452 1453 // Want error? 1454 if rest := strings.TrimPrefix(test.want, "error: "); rest != test.want { 1455 if err == nil { 1456 t.Fatalf("unexpected success: want error matching %q", rest) 1457 } 1458 msg := err.Error() 1459 if ok, err := regexp.MatchString(rest, msg); err != nil { 1460 t.Fatalf("invalid regexp: %v", err) 1461 } else if !ok { 1462 t.Fatalf("wrong error: %s (want match for %q)", msg, rest) 1463 } 1464 return 1465 } 1466 1467 // Want success. 1468 if err != nil { 1469 t.Fatal(err) 1470 } 1471 1472 gotContent := res.Content 1473 1474 // Compute a single-hunk line-based diff. 1475 srcLines := strings.Split(callerContent, "\n") 1476 gotLines := strings.Split(string(gotContent), "\n") 1477 for len(srcLines) > 0 && len(gotLines) > 0 && 1478 srcLines[0] == gotLines[0] { 1479 srcLines = srcLines[1:] 1480 gotLines = gotLines[1:] 1481 } 1482 for len(srcLines) > 0 && len(gotLines) > 0 && 1483 srcLines[len(srcLines)-1] == gotLines[len(gotLines)-1] { 1484 srcLines = srcLines[:len(srcLines)-1] 1485 gotLines = gotLines[:len(gotLines)-1] 1486 } 1487 got := strings.Join(gotLines, "\n") 1488 1489 if strings.TrimSpace(got) != strings.TrimSpace(test.want) { 1490 t.Fatalf("\nInlining this call:\t%s\nof this callee: \t%s\nproduced:\n%s\nWant:\n\n%s", 1491 test.caller, 1492 test.callee, 1493 got, 1494 test.want) 1495 } 1496 1497 // Check that resulting code type-checks. 1498 newCallerFile := mustParse("newcaller.go", gotContent) 1499 if _, err := conf.Check("p", fset, []*ast.File{newCallerFile, calleeFile}, nil); err != nil { 1500 t.Fatalf("modified source failed to typecheck: <<%s>>", gotContent) 1501 } 1502 }) 1503 } 1504 } 1505 1506 // -- helpers -- 1507 1508 // checkNoMutation returns a function that, when called, 1509 // asserts that file was not modified since the checkNoMutation call. 1510 func checkNoMutation(file *ast.File) func() { 1511 pre := deepHash(file) 1512 return func() { 1513 post := deepHash(file) 1514 if pre != post { 1515 panic("Inline mutated caller.File") 1516 } 1517 } 1518 } 1519 1520 // checkTranscode replaces *callee by the results of gob-encoding and 1521 // then decoding it, to test that these operations are lossless. 1522 func checkTranscode(callee *inline.Callee) error { 1523 // Perform Gob transcoding so that it is exercised by the test. 1524 var enc bytes.Buffer 1525 if err := gob.NewEncoder(&enc).Encode(callee); err != nil { 1526 return fmt.Errorf("internal error: gob encoding failed: %v", err) 1527 } 1528 *callee = inline.Callee{} 1529 if err := gob.NewDecoder(&enc).Decode(callee); err != nil { 1530 return fmt.Errorf("internal error: gob decoding failed: %v", err) 1531 } 1532 return nil 1533 } 1534 1535 // TODO(adonovan): publish this a helper (#61386). 1536 func extractTxtar(ar *txtar.Archive, dir string) error { 1537 for _, file := range ar.Files { 1538 name := filepath.Join(dir, file.Name) 1539 if err := os.MkdirAll(filepath.Dir(name), 0777); err != nil { 1540 return err 1541 } 1542 if err := os.WriteFile(name, file.Data, 0666); err != nil { 1543 return err 1544 } 1545 } 1546 return nil 1547 } 1548 1549 // deepHash computes a cryptographic hash of an ast.Node so that 1550 // if the data structure is mutated, the hash changes. 1551 // It assumes Go variables do not change address. 1552 // 1553 // TODO(adonovan): consider publishing this in the astutil package. 1554 // 1555 // TODO(adonovan): consider a variant that reports where in the tree 1556 // the mutation occurred (obviously at a cost in space). 1557 func deepHash(n ast.Node) any { 1558 seen := make(map[unsafe.Pointer]bool) // to break cycles 1559 1560 hasher := sha256.New() 1561 le := binary.LittleEndian 1562 writeUint64 := func(v uint64) { 1563 var bs [8]byte 1564 le.PutUint64(bs[:], v) 1565 hasher.Write(bs[:]) 1566 } 1567 1568 var visit func(reflect.Value) 1569 visit = func(v reflect.Value) { 1570 switch v.Kind() { 1571 case reflect.Ptr: 1572 ptr := v.UnsafePointer() 1573 writeUint64(uint64(uintptr(ptr))) 1574 if !v.IsNil() { 1575 if !seen[ptr] { 1576 seen[ptr] = true 1577 // Skip types we don't handle yet, but don't care about. 1578 switch v.Interface().(type) { 1579 case *ast.Scope: 1580 return // involves a map 1581 } 1582 1583 visit(v.Elem()) 1584 } 1585 } 1586 1587 case reflect.Struct: 1588 for i := 0; i < v.Type().NumField(); i++ { 1589 visit(v.Field(i)) 1590 } 1591 1592 case reflect.Slice: 1593 ptr := v.UnsafePointer() 1594 // We may encounter different slices at the same address, 1595 // so don't mark ptr as "seen". 1596 writeUint64(uint64(uintptr(ptr))) 1597 writeUint64(uint64(v.Len())) 1598 writeUint64(uint64(v.Cap())) 1599 for i := 0; i < v.Len(); i++ { 1600 visit(v.Index(i)) 1601 } 1602 1603 case reflect.Interface: 1604 if v.IsNil() { 1605 writeUint64(0) 1606 } else { 1607 rtype := reflect.ValueOf(v.Type()).UnsafePointer() 1608 writeUint64(uint64(uintptr(rtype))) 1609 visit(v.Elem()) 1610 } 1611 1612 case reflect.Array, reflect.Chan, reflect.Func, reflect.Map, reflect.UnsafePointer: 1613 panic(v) // unreachable in AST 1614 1615 default: // bool, string, number 1616 if v.Kind() == reflect.String { // proper framing 1617 writeUint64(uint64(v.Len())) 1618 } 1619 binary.Write(hasher, le, v.Interface()) 1620 } 1621 } 1622 visit(reflect.ValueOf(n)) 1623 1624 var hash [sha256.Size]byte 1625 hasher.Sum(hash[:0]) 1626 return hash 1627 }