func rewriteFnWithRecovers(body *ast.BlockStmt, fnType *ast.FuncType) (wrapped *ast.FuncLit) { // The formatting of the channel declaration is ugly, but it's presented this way here to show how it will look in the actual output. // As far as I know, I would need to set the token.Pos values for the left and right braces of the struct and interface type literals // in order to get them on one line, but I don't think I can do that without computing all of the other token.Pos values for everything // else I generate. // TODO: These identifiers will probably conflict if there is a nested function that also has unnamed outputs. Should probably make a better gensym. outputDecls, outputs := inputsOrOutputs(fnType.Results, idents.result) if len(outputs) > 0 { body.List = astPrintf(`%s = func() (%s) {%s}()`, outputs, fnType.Results, body.List) } body.List = astPrintf(` {{%s}} _r := make(chan chan interface { }) recovers, panicChan := godebug.EnterFuncWithRecovers(_r, func(ctx *godebug.Context) { %s }) for recoverChan := range recovers { recoverChan <- recover() } if panicVal, ok := <-panicChan; ok { panic(panicVal) } {{return %s}}`, outputDecls, body.List, outputs) body.Rbrace = token.NoPos // without this I was getting extra whitespace at the end of the function return wrapped }
func (v *visitor) finalizeLoop(pos token.Pos, body *ast.BlockStmt) { if body == nil { return } line := pos2line(pos) if len(v.newIdents) == 0 { call := newCall(idents.godebug, "Line", ast.NewIdent(idents.ctx), ast.NewIdent(v.scopeVar), newInt(line)) body.List = append(body.List, &ast.ExprStmt{X: call}) } else { body.List = append([]ast.Stmt{ astPrintf(`godebug.Line(ctx, scope, %s)`, strconv.Itoa(line))[0], newDeclareCall(idents.scope, v.newIdents), }, body.List...) } }
func (p *parser) parseBlock(n *parse.Node, scope *ast.Scope) *ast.BlockStmt { block := ast.BlockStmt{ Lbrace: token.Pos(n.Child(0).Pos()), Rbrace: token.Pos(n.LastChild().Pos()), } eachListItem(stmt, n.Child(1), func(item *parse.Node) { block.List = append(block.List, p.parseStmt(item)) }) return &block }
// filterBlocks keeps the statements that are need to calculate MappedCells. func filterBlock(blk *ast.BlockStmt, filterIDs map[string]bool, imports map[string]string) (*ast.BlockStmt, []dictKey) { dicts := dictionaries(blk, imports) usedDks := make(map[dictKey]bool) filtered := make([]ast.Stmt, 0, len(blk.List)) for i := len(blk.List) - 1; i >= 0; i-- { switch s := blk.List[i].(type) { // TODO(soheil): Support switches. case *ast.SwitchStmt: // TODO(soheil): Add support for ifs. case *ast.IfStmt: default: // TODO(soheil): It's actually more complicated that. What about // functional calls, what about multiple return values, ...? dks, yes := accessesDict(s, dicts) if yes { for _, dk := range dks { filterIDs[dk.k] = true usedDks[dk] = true } continue } dirty := false rIDs, wIDs := ids(s) for _, id := range wIDs { if filterIDs[id] { dirty = true break } } if !dirty { continue } for _, id := range rIDs { filterIDs[id] = true } filtered = append([]ast.Stmt{s}, filtered...) } } blk.List = filtered keys := make([]dictKey, 0, len(usedDks)) for dk, _ := range usedDks { keys = append(keys, dk) } return blk, keys }
/* * Given a test func named TestDoesSomethingNeat, rewrites it as * It("does something neat", func() { __test_body_here__ }) and adds it * to the Describe's list of statements */ func rewriteTestFuncAsItStatement(testFunc *ast.FuncDecl, rootNode *ast.File, describe *ast.CallExpr) { var funcIndex int = -1 for index, child := range rootNode.Decls { if child == testFunc { funcIndex = index break } } if funcIndex < 0 { panic(fmt.Sprintf("Assert failed: Error finding index for test node %s\n", testFunc.Name.Name)) } var block *ast.BlockStmt = blockStatementFromDescribe(describe) block.List = append(block.List, createItStatementForTestFunc(testFunc)) replaceTestingTsWithGinkgoT(block, namedTestingTArg(testFunc)) // remove the old test func from the root node's declarations rootNode.Decls = append(rootNode.Decls[:funcIndex], rootNode.Decls[funcIndex+1:]...) return }
func rewriteStructLiteralAsIdentifierAtTopOfBlock(newFile *ast.File, literalToReplace *ast.CompositeLit, name string) { var ( foundStmtNode ast.Stmt blocksSeen []*ast.BlockStmt stmtsSeen []ast.Stmt deleteOriginalStatement bool newAssignStmtToken = token.DEFINE ) ast.Inspect(newFile, func(node ast.Node) bool { switch node := node.(type) { case *ast.BlockStmt: if foundStmtNode == nil { blocksSeen = append(blocksSeen, node) } case ast.Stmt: stmtsSeen = append(stmtsSeen, node) } return true }) var block *ast.BlockStmt var insertionIndex int for i := len(blocksSeen) - 1; i >= 0; i-- { b := blocksSeen[i] for j, stmt := range b.List { ast.Inspect(stmt, func(parentNode ast.Node) bool { switch parentNode := parentNode.(type) { case *ast.CompositeLit: if parentNode == literalToReplace { block = b insertionIndex = j return false } } return true }) } } for i := len(stmtsSeen) - 1; i >= 0; i-- { s := stmtsSeen[i] ast.Inspect(s, func(node ast.Node) bool { switch node := node.(type) { case *ast.CompositeLit: if node == literalToReplace { foundStmtNode = s return false } } return true }) } switch foundStmtNode := foundStmtNode.(type) { case *ast.AssignStmt: expr := foundStmtNode.Rhs[0] if expr == literalToReplace { deleteOriginalStatement = true switch ident := foundStmtNode.Lhs[0].(type) { case *ast.Ident: name = ident.Name } if foundStmtNode.Tok == token.ASSIGN { newAssignStmtToken = token.ASSIGN } } } lhsExpr := []ast.Expr{ast.NewIdent(name)} rhsExpr := []ast.Expr{&ast.CompositeLit{Type: literalToReplace.Type}} block.List = insert(block.List, insertionIndex, ast.AssignStmt{ Lhs: lhsExpr, Rhs: rhsExpr, Tok: newAssignStmtToken, }) insertionIndex++ for _, elt := range literalToReplace.Elts { keyVal := elt.(*ast.KeyValueExpr) fieldName := keyVal.Key.(*ast.Ident) selector := &ast.SelectorExpr{ X: ast.NewIdent(name), Sel: ast.NewIdent(fieldName.Name), } innerLhs := []ast.Expr{selector} innerRhs := []ast.Expr{keyVal.Value} block.List = insert(block.List, insertionIndex, ast.AssignStmt{ Lhs: innerLhs, Rhs: innerRhs, Tok: token.ASSIGN, }) insertionIndex++ } if deleteOriginalStatement { copy(block.List[insertionIndex:], block.List[insertionIndex+1:]) block.List[len(block.List)-1] = &ast.EmptyStmt{} block.List = block.List[:len(block.List)-1] } else { replaceStructLiteralWithIdentifier(foundStmtNode, literalToReplace, name) } }