// Package source provides utilities for handling source-code.
package source // import "gotest.tools/v3/internal/source" import ( ) // FormattedCallExprArg returns the argument from an ast.CallExpr at the // index in the call stack. The argument is formatted using FormatNode. func ( int, int) (string, error) { , := CallExprArgs( + 1) if != nil { return "", } if >= len() { return "", errors.New("failed to find expression") } return FormatNode([]) } // CallExprArgs returns the ast.Expr slice for the args of an ast.CallExpr at // the index in the call stack. func ( int) ([]ast.Expr, error) { , , , := runtime.Caller( + 1) if ! { return nil, errors.New("failed to get call stack") } debug("call stack position: %s:%d", , ) := token.NewFileSet() , := parser.ParseFile(, , nil, parser.AllErrors) if != nil { return nil, fmt.Errorf("failed to parse source file %s: %w", , ) } , := getCallExprArgs(, , ) if != nil { return nil, fmt.Errorf("call from %s:%d: %w", , , ) } return , nil } func ( *token.FileSet, ast.Node, int) (ast.Node, error) { if := scanToLine(, , ); != nil { return , nil } if := scanToDeferLine(, , ); != nil { , := guessDefer() if != nil || != nil { return , } } return nil, nil } func ( *token.FileSet, ast.Node, int) ast.Node { var ast.Node ast.Inspect(, func( ast.Node) bool { switch { case == nil || != nil: return false case .Position(.Pos()).Line == : = return false } return true }) return } func ( *token.FileSet, ast.Node, int) ([]ast.Expr, error) { , := getNodeAtLine(, , ) switch { case != nil: return nil, case == nil: return nil, fmt.Errorf("failed to find an expression") } debug("found node: %s", debugFormatNode{}) := &callExprVisitor{} ast.Walk(, ) if .expr == nil { return nil, errors.New("failed to find call expression") } debug("callExpr: %s", debugFormatNode{.expr}) return .expr.Args, nil } type callExprVisitor struct { expr *ast.CallExpr } func ( *callExprVisitor) ( ast.Node) ast.Visitor { if .expr != nil || == nil { return nil } debug("visit: %s", debugFormatNode{}) switch typed := .(type) { case *ast.CallExpr: .expr = return nil case *ast.DeferStmt: ast.Walk(, .Call.Fun) return nil } return } // FormatNode using go/format.Node and return the result as a string func ( ast.Node) (string, error) { := new(bytes.Buffer) := format.Node(, token.NewFileSet(), ) return .String(), } var debugEnabled = os.Getenv("GOTESTTOOLS_DEBUG") != "" func ( string, ...interface{}) { if debugEnabled { fmt.Fprintf(os.Stderr, "DEBUG: "++"\n", ...) } } type debugFormatNode struct { ast.Node } func ( debugFormatNode) () string { if .Node == nil { return "none" } , := FormatNode(.Node) if != nil { return fmt.Sprintf("failed to format %s: %s", .Node, ) } return fmt.Sprintf("(%T) %s", .Node, ) }