@@ -630,6 +630,8 @@ func extractFunctionMethod(cpkg *cache.Package, pgf *parsego.File, start, end to
630630 allReturnsFinalErr = true // all ReturnStmts have final 'err' expression
631631 hasReturn = false // selection contains a ReturnStmt
632632 filter = []ast.Node {(* ast .ReturnStmt )(nil ), (* ast .FuncLit )(nil )}
633+
634+ origRetStmts []* ast.ReturnStmt // return stmts in source order, for type lookups
633635 )
634636 curEnclosing .Inspect (filter , func (cur inspector.Cursor ) (descend bool ) {
635637 if funcLit , ok := cur .Node ().(* ast.FuncLit ); ok {
@@ -643,6 +645,8 @@ func extractFunctionMethod(cpkg *cache.Package, pgf *parsego.File, start, end to
643645 }
644646 hasReturn = true
645647
648+ origRetStmts = append (origRetStmts , ret )
649+
646650 if cur .Parent () == curStart .Parent () {
647651 hasNonNestedReturn = true
648652 }
@@ -1117,19 +1121,9 @@ func extractFunctionMethod(cpkg *cache.Package, pgf *parsego.File, start, end to
11171121 // Expand multi-value function calls in return statements.
11181122 // If a return contains a single CallExpr that is being augmented with new
11191123 // return values, the call return values must be expanded to maintain valid syntax.
1120- ast .Inspect (extractedBlock , func (n ast.Node ) bool {
1121- switch n := n .(type ) {
1122- case * ast.BlockStmt :
1123- n .List = expandFunctionCallReturnValues (n .List , info , newFuncResults , file , start )
1124- case * ast.CaseClause :
1125- n .Body = expandFunctionCallReturnValues (n .Body , info , newFuncResults , file , start )
1126- case * ast.FuncLit :
1127- // Don't descend into nested functions.
1128- return false
1129- }
1130-
1131- return true
1132- })
1124+ if err := expandMultiValueCallReturns (extractedBlock , info , newFuncResults , file , start , origRetStmts ); err != nil {
1125+ return nil , nil , err
1126+ }
11331127
11341128 // Build the extracted function. We format the function declaration and body
11351129 // separately, so that comments are printed relative to the extracted
@@ -1231,8 +1225,59 @@ func extractFunctionMethod(cpkg *cache.Package, pgf *parsego.File, start, end to
12311225 }, nil
12321226}
12331227
1234- // expandFunctionCallReturnValues expands the return value of function calls when necessary.
1235- func expandFunctionCallReturnValues (stmts []ast.Stmt , info * types.Info , newFuncResults * ast.FieldList , file * ast.File , start token.Pos ) []ast.Stmt {
1228+ // expandMultiValueCallReturns expands multi-value function calls in return
1229+ // statements within the extracted block.
1230+ func expandMultiValueCallReturns (extractedBlock * ast.BlockStmt , info * types.Info , newFuncResults * ast.FieldList , file * ast.File , start token.Pos , origRetStmts []* ast.ReturnStmt ) error {
1231+ // The re-parsed AST has no type information, so we pair its return stmts
1232+ // with the original (type-checked) ones to look up types for naming.
1233+ //
1234+ // The pairing is done as a separate pass because the second pass doesn't
1235+ // exactly visit the ReturnStmt in the same way as how the origRetStmts
1236+ // is collected (via ast.Inspect).
1237+ origRetMap := map [* ast.ReturnStmt ]* ast.ReturnStmt {}
1238+ origIdx := 0
1239+ ast .Inspect (extractedBlock , func (n ast.Node ) bool {
1240+ switch n := n .(type ) {
1241+ case * ast.ReturnStmt :
1242+ if origIdx < len (origRetStmts ) {
1243+ origRetMap [n ] = origRetStmts [origIdx ]
1244+ origIdx ++
1245+ } else {
1246+ // The re-parsed AST may have injected returns appended
1247+ // at the end with no original counterpart but it is ok since
1248+ // we can guarantee it will not have CallExpr in it.
1249+ return false
1250+ }
1251+ case * ast.FuncLit :
1252+ return false // don't descend into closures.
1253+ }
1254+
1255+ return true
1256+ })
1257+
1258+ // Traverse the extracted block again and do the actual expansion.
1259+ var expandErr error
1260+ ast .Inspect (extractedBlock , func (n ast.Node ) bool {
1261+ if expandErr != nil {
1262+ return false
1263+ }
1264+ switch n := n .(type ) {
1265+ case * ast.BlockStmt :
1266+ n .List , expandErr = expandFunctionCallReturnValues (n .List , info , newFuncResults , file , start , origRetMap )
1267+ case * ast.CaseClause :
1268+ n .Body , expandErr = expandFunctionCallReturnValues (n .Body , info , newFuncResults , file , start , origRetMap )
1269+ case * ast.FuncLit :
1270+ return false // don't descend into closures.
1271+ }
1272+
1273+ return true
1274+ })
1275+ return expandErr
1276+ }
1277+
1278+ // expandFunctionCallReturnValues expands the return value of function calls
1279+ // in the given statement list when necessary.
1280+ func expandFunctionCallReturnValues (stmts []ast.Stmt , info * types.Info , newFuncResults * ast.FieldList , file * ast.File , start token.Pos , origRetMap map [* ast.ReturnStmt ]* ast.ReturnStmt ) ([]ast.Stmt , error ) {
12361281 result := make ([]ast.Stmt , 0 , len (stmts ))
12371282 for _ , stmt := range stmts {
12381283 result = append (result , stmt )
@@ -1261,13 +1306,32 @@ func expandFunctionCallReturnValues(stmts []ast.Stmt, info *types.Info, newFuncR
12611306 // type information here. This should be correct assuming the original code
12621307 // is valid to begin with.
12631308 expandedVars := make ([]ast.Expr , len (newFuncResults .List )- len (retStmt .Results )+ 1 ) // plus one to replace the CallExpr
1264- prevIdx := 1
1309+
1310+ // Use type information from the original return statement to
1311+ // generate type-aware names and detect scope collisions.
1312+ origRet := origRetMap [retStmt ]
1313+ if origRet == nil {
1314+ return nil , bug .Errorf ("no original return statement for re-parsed return" )
1315+ }
1316+
1317+ scopePos := origRet .Pos ()
1318+ origCallExpr := origRet .Results [0 ].(* ast.CallExpr )
1319+ sig := info .TypeOf (origCallExpr .Fun ).Underlying ().(* types.Signature )
1320+ tup := sig .Results ()
1321+
1322+ // Generate type-aware names for each expanded return values.
1323+ prevIdxByPrefix := map [string ]int {}
12651324 for i := range expandedVars {
1266- // ideally we want to generate a better name (e.g. `errX` for error values)
1267- // but we don't have type info at this stage.
1268- name , idx := freshName (info , file , start , "v" , prevIdx )
1325+ prefix := "v"
1326+ if name , ok := varNameForType (tup .At (i ).Type ()); ok {
1327+ prefix = name
1328+ }
1329+
1330+ prev := prevIdxByPrefix [prefix ]
1331+ name , next := freshName (info , file , scopePos , prefix , prev )
1332+ prevIdxByPrefix [prefix ] = next
1333+
12691334 expandedVars [i ] = ast .NewIdent (name )
1270- prevIdx = idx
12711335 }
12721336
12731337 result [len (result )- 1 ] = ast .Stmt (& ast.AssignStmt {
@@ -1282,7 +1346,7 @@ func expandFunctionCallReturnValues(stmts []ast.Stmt, info *types.Info, newFuncR
12821346 })
12831347 }
12841348
1285- return result
1349+ return result , nil
12861350}
12871351
12881352// isSelector reports if e is the selector expr <x>, <sel>. It works for pointer and non-pointer selector expressions.
0 commit comments