blob: 06cb41e6306ac750a4eb3d507ec13ca610e02437 [file] [edit]
// Copyright 2026 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"
"go/ast"
"go/parser"
"go/token"
"go/types"
"strings"
"testing"
"golang.org/x/tools/go/ast/inspector"
)
// TestIssue73187 tests that inlining does not panic on variadic calls when
// the AST param field type is not an *ast.Ellipsis (golang/go#73187).
//
// This cannot be tested via standard TestData or TestBasics
// because normal type checking always pairs a variadic signature with an
// *ast.Ellipsis. The error only arises during synthetic transformations
// (such as ChangeSignature) or ill-typed AST edits.
func TestIssue73187(t *testing.T) {
const src = `package p
func f(args ...string) {
println(args)
}
func main() {
f("a", "b")
}
`
fset := token.NewFileSet()
file, err := parser.ParseFile(fset, "p.go", src, 0)
if err != nil {
t.Fatal(err)
}
info := &types.Info{
Defs: make(map[*ast.Ident]types.Object),
Uses: make(map[*ast.Ident]types.Object),
Types: make(map[ast.Expr]types.TypeAndValue),
Selections: make(map[*ast.SelectorExpr]*types.Selection),
Scopes: make(map[ast.Node]*types.Scope),
Implicits: make(map[ast.Node]types.Object),
Instances: make(map[*ast.Ident]types.Instance),
FileVersions: make(map[*ast.File]string),
}
conf := types.Config{}
pkg, err := conf.Check("p", fset, []*ast.File{file}, info)
if err != nil {
t.Fatal(err)
}
var fDecl *ast.FuncDecl
var call *ast.CallExpr
for _, decl := range file.Decls {
if fd, ok := decl.(*ast.FuncDecl); ok {
if fd.Name.Name == "f" {
fDecl = fd
} else if fd.Name.Name == "main" {
call = fd.Body.List[0].(*ast.ExprStmt).X.(*ast.CallExpr)
}
}
}
curCall, _ := inspector.New([]*ast.File{file}).Root().FindNode(call)
caller := &Caller{
Fset: fset,
Types: pkg,
Info: info,
Call: curCall,
}
callee, err := AnalyzeCallee(t.Logf, fset, pkg, info, fDecl, []byte(src))
if err != nil {
t.Fatal(err)
}
// Mutate Content in callee.impl with equal-length ArrayType instead of Ellipsis
callee.impl.Content = bytes.Replace(callee.impl.Content, []byte("...string"), []byte(" []string"), 1)
// Inlining must not panic.
if _, err := Inline(caller, callee, &Options{Logf: t.Logf}); err != nil {
t.Fatalf("Inline failed: %v", err)
}
}
func TestRecover(t *testing.T) {
// Test that Options.Recover catches inliner panics and returns them as errors.
var caller Caller
var callee Callee
_, err := Inline(&caller, &callee, &Options{Recover: true})
if err == nil {
t.Fatal("Inline unexpectedly succeeded on nil caller")
}
if !strings.Contains(err.Error(), "inlining failed") {
t.Fatalf("expected inlining failed error, got: %v", err)
}
}