Пример #1
0
// Sets multiLine to true if the function body spans multiple lines.
func (p *printer) funcBody(b *ast.BlockStmt, headerSize int, isLit bool, multiLine *bool) {
	if b == nil {
		return
	}

	p.nesting++
	defer func() {
		p.nesting--
	}()

	if p.isOneLineFunc(b, headerSize) {
		sep := vtab
		if isLit {
			sep = blank
		}
		p.print(sep, b.Pos(), token.LBRACE)
		if len(b.List) > 0 {
			p.print(blank)
			for i, s := range b.List {
				if i > 0 {
					p.print(token.SEMICOLON, blank)
				}
				p.stmt(s, i == len(b.List)-1, ignoreMultiLine)
			}
			p.print(blank)
		}
		p.print(b.Rbrace, token.RBRACE)
		return
	}

	p.print(blank)
	p.block(b, 1)
	*multiLine = true
}
Пример #2
0
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
}
Пример #3
0
// labels checks correct label use in body.
func (check *Checker) labels(body *ast.BlockStmt) {
	// set of all labels in this body
	all := NewScope(nil, body.Pos(), body.End(), "label")

	fwdJumps := check.blockBranches(all, nil, nil, body.List)

	// If there are any forward jumps left, no label was found for
	// the corresponding goto statements. Either those labels were
	// never defined, or they are inside blocks and not reachable
	// for the respective gotos.
	for _, jmp := range fwdJumps {
		var msg string
		name := jmp.Label.Name
		if alt := all.Lookup(name); alt != nil {
			msg = "goto %s jumps into block"
			alt.(*Label).used = true // avoid another error
		} else {
			msg = "label %s not declared"
		}
		check.errorf(jmp.Label.Pos(), msg, name)
	}

	// spec: "It is illegal to define a label that is never used."
	for _, obj := range all.elems {
		if lbl := obj.(*Label); !lbl.used {
			check.softErrorf(lbl.pos, "label %s declared but not used", lbl.name)
		}
	}
}
Пример #4
0
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
}
Пример #5
0
func (c *compiler) createBlockMetadata(stmt *ast.BlockStmt) llvm.DebugDescriptor {
	uniqueId++
	file := c.fileset.File(stmt.Pos())
	fd := llvm.FileDescriptor(file.Name())
	return &llvm.BlockDescriptor{
		File:    &fd,
		Line:    uint32(file.Line(stmt.Pos())),
		Context: c.currentDebugContext(),
		Id:      uniqueId,
	}
}
Пример #6
0
// block prints an *ast.BlockStmt; it always spans at least two lines.
func (p *printer) block(s *ast.BlockStmt, indent int, moveComments bool) {
	if moveComments {
		p.print(p.beforeComment(s.Pos()))
	} else {
		p.print(s.Pos())
	}
	p.print(token.LBRACE)
	p.stmtList(s.List, indent)
	p.linebreak(s.Rbrace.Line, 1, maxStmtNewlines, ignore, true)
	p.print(s.Rbrace, token.RBRACE)
}
Пример #7
0
// lastComment returns the last comment inside the provided block.
func lastComment(b *ast.BlockStmt, c []*ast.CommentGroup) (i int, last *ast.CommentGroup) {
	pos, end := b.Pos(), b.End()
	for j, cg := range c {
		if cg.Pos() < pos {
			continue
		}
		if cg.End() > end {
			break
		}
		i, last = j, cg
	}
	return
}
Пример #8
0
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...)
	}
}
Пример #9
0
func (lp *linePrinter) trimBlock(stmt *ast.BlockStmt) *ast.BlockStmt {
	if !lp.trim(stmt) {
		return lp.emptyBlock(stmt)
	}
	stmt.Rbrace = stmt.Lbrace
	return stmt
}
Пример #10
0
// New returns a new control-flow graph for the specified function body,
// which must be non-nil.
//
// The CFG builder calls mayReturn to determine whether a given function
// call may return.  For example, calls to panic, os.Exit, and log.Fatal
// do not return, so the builder can remove infeasible graph edges
// following such calls.  The builder calls mayReturn only for a
// CallExpr beneath an ExprStmt.
func New(body *ast.BlockStmt, mayReturn func(*ast.CallExpr) bool) *CFG {
	b := builder{
		mayReturn: mayReturn,
		cfg:       new(CFG),
	}
	b.current = b.newBlock("entry")
	b.stmt(body)

	// Does control fall off the end of the function's body?
	// Make implicit return explicit.
	if b.current != nil && !b.current.unreachable {
		b.add(&ast.ReturnStmt{
			Return: body.End() - 1,
		})
	}

	return b.cfg
}
Пример #11
0
func (check *Checker) funcBody(decl *declInfo, name string, sig *Signature, body *ast.BlockStmt) {
	if trace {
		if name == "" {
			name = "<function literal>"
		}
		fmt.Printf("--- %s: %s {\n", name, sig)
		defer fmt.Println("--- <end>")
	}

	// set function scope extent
	sig.scope.pos = body.Pos()
	sig.scope.end = body.End()

	// save/restore current context and setup function context
	// (and use 0 indentation at function start)
	defer func(ctxt context, indent int) {
		check.context = ctxt
		check.indent = indent
	}(check.context, check.indent)
	check.context = context{
		decl:  decl,
		scope: sig.scope,
		sig:   sig,
	}
	check.indent = 0

	check.stmtList(0, body.List)

	if check.hasLabel {
		check.labels(body)
	}

	if sig.results.Len() > 0 && !check.isTerminating(body, "") {
		check.error(body.Rbrace, "missing return")
	}

	// spec: "Implementation restriction: A compiler may make it illegal to
	// declare a variable inside a function body if the variable is never used."
	// (One could check each scope after use, but that distributes this check
	// over several places because CloseScope is not always called explicitly.)
	check.usage(sig.scope)
}
Пример #12
0
// 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
}
Пример #13
0
// bodySize is like nodeSize but it is specialized for *ast.BlockStmt's.
func (p *printer) bodySize(b *ast.BlockStmt, maxSize int) int {
	pos1 := b.Pos()
	pos2 := b.Rbrace
	if pos1.IsValid() && pos2.IsValid() && p.lineFor(pos1) != p.lineFor(pos2) {
		// opening and closing brace are on different lines - don't make it a one-liner
		return maxSize + 1
	}
	if len(b.List) > 5 || p.commentBefore(p.posFor(pos2)) {
		// too many statements or there is a comment inside - don't make it a one-liner
		return maxSize + 1
	}
	// otherwise, estimate body size
	bodySize := 0
	for i, s := range b.List {
		if i > 0 {
			bodySize += 2 // space for a semicolon and blank
		}
		bodySize += p.nodeSize(s, maxSize)
	}
	return bodySize
}
Пример #14
0
/*
 * 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
}
Пример #15
0
func (p *printer) isOneLineFunc(b *ast.BlockStmt, headerSize int) bool {
	pos1 := b.Pos()
	pos2 := b.Rbrace
	if pos1.IsValid() && pos2.IsValid() && pos1.Line != pos2.Line {
		// opening and closing brace are on different lines - don't make it a one-liner
		return false
	}
	if len(b.List) > 5 || p.commentBefore(pos2) {
		// too many statements or there is a comment inside - don't make it a one-liner
		return false
	}
	// otherwise, estimate body size
	const maxSize = 100
	bodySize := 0
	for i, s := range b.List {
		if i > 0 {
			bodySize += 2 // space for a semicolon and blank
		}
		bodySize += p.nodeSize(s, maxSize)
	}
	return headerSize+bodySize <= maxSize
}
Пример #16
0
func (c *compiler) VisitBlockStmt(stmt *ast.BlockStmt, createNewBlock bool) {
	c.pushDebugContext(c.createBlockMetadata(stmt))
	defer c.popDebugContext()
	c.setDebugLine(stmt.Pos())

	// This is a little awkward, but it makes dealing with branching easier.
	// A free-standing block statement (i.e. one not attached to a control
	// statement) will splice in a new block.
	var doneBlock llvm.BasicBlock
	if createNewBlock {
		currBlock := c.builder.GetInsertBlock()
		doneBlock = llvm.InsertBasicBlock(currBlock, "")
		doneBlock.MoveAfter(currBlock)
		newBlock := llvm.InsertBasicBlock(doneBlock, "")
		c.builder.CreateBr(newBlock)
		c.builder.SetInsertPointAtEnd(newBlock)
	}

	// Visit each statement in the block. When we have a terminator,
	// ignore everything until we get to a labeled statement.
	for _, stmt := range stmt.List {
		currBlock := c.builder.GetInsertBlock()
		in := currBlock.LastInstruction()
		if in.IsNil() || in.IsATerminatorInst().IsNil() {
			c.VisitStmt(stmt)
		} else if _, ok := stmt.(*ast.LabeledStmt); ok {
			// FIXME we might end up with a labeled statement
			// with no predecessors, due to dead code elimination.
			c.VisitStmt(stmt)
		}
	}

	if createNewBlock {
		c.maybeImplicitBranch(doneBlock)
		c.builder.SetInsertPointAtEnd(doneBlock)
	}
}
Пример #17
0
// Sets multiLine to true if the function body spans multiple lines.
func (p *printer) funcBody(b *ast.BlockStmt, isLit bool, multiLine *bool) {
	if b == nil {
		return
	}

	if (oneLineFuncDecls || isLit) && p.isOneLiner(b) {
		sep := vtab
		if isLit {
			sep = blank
		}
		if len(b.List) > 0 {
			p.print(sep, b.Pos(), token.LBRACE, blank)
			p.stmt(b.List[0], ignoreMultiLine)
			p.print(blank, b.Rbrace, token.RBRACE)
		} else {
			p.print(sep, b.Pos(), token.LBRACE, b.Rbrace, token.RBRACE)
		}
		return
	}

	p.print(blank)
	p.block(b, 1)
	*multiLine = true
}
Пример #18
0
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)
	}
}
Пример #19
0
// playExample synthesizes a new *ast.File based on the provided
// file with the provided function body as the body of main.
func playExample(file *ast.File, body *ast.BlockStmt) *ast.File {
	if !strings.HasSuffix(file.Name.Name, "_test") {
		// We don't support examples that are part of the
		// greater package (yet).
		return nil
	}

	// Find top-level declarations in the file.
	topDecls := make(map[*ast.Object]bool)
	for _, decl := range file.Decls {
		switch d := decl.(type) {
		case *ast.FuncDecl:
			topDecls[d.Name.Obj] = true
		case *ast.GenDecl:
			for _, spec := range d.Specs {
				switch s := spec.(type) {
				case *ast.TypeSpec:
					topDecls[s.Name.Obj] = true
				case *ast.ValueSpec:
					for _, id := range s.Names {
						topDecls[id.Obj] = true
					}
				}
			}
		}
	}

	// Find unresolved identifiers and uses of top-level declarations.
	unresolved := make(map[string]bool)
	usesTopDecl := false
	var inspectFunc func(ast.Node) bool
	inspectFunc = func(n ast.Node) bool {
		// For selector expressions, only inspect the left hand side.
		// (For an expression like fmt.Println, only add "fmt" to the
		// set of unresolved names, not "Println".)
		if e, ok := n.(*ast.SelectorExpr); ok {
			ast.Inspect(e.X, inspectFunc)
			return false
		}
		// For key value expressions, only inspect the value
		// as the key should be resolved by the type of the
		// composite literal.
		if e, ok := n.(*ast.KeyValueExpr); ok {
			ast.Inspect(e.Value, inspectFunc)
			return false
		}
		if id, ok := n.(*ast.Ident); ok {
			if id.Obj == nil {
				unresolved[id.Name] = true
			} else if topDecls[id.Obj] {
				usesTopDecl = true
			}
		}
		return true
	}
	ast.Inspect(body, inspectFunc)
	if usesTopDecl {
		// We don't support examples that are not self-contained (yet).
		return nil
	}

	// Remove predeclared identifiers from unresolved list.
	for n := range unresolved {
		if predeclaredTypes[n] || predeclaredConstants[n] || predeclaredFuncs[n] {
			delete(unresolved, n)
		}
	}

	// Use unresolved identifiers to determine the imports used by this
	// example. The heuristic assumes package names match base import
	// paths for imports w/o renames (should be good enough most of the time).
	namedImports := make(map[string]string) // [name]path
	var blankImports []ast.Spec             // _ imports
	for _, s := range file.Imports {
		p, err := strconv.Unquote(s.Path.Value)
		if err != nil {
			continue
		}
		n := path.Base(p)
		if s.Name != nil {
			n = s.Name.Name
			switch n {
			case "_":
				blankImports = append(blankImports, s)
				continue
			case ".":
				// We can't resolve dot imports (yet).
				return nil
			}
		}
		if unresolved[n] {
			namedImports[n] = p
			delete(unresolved, n)
		}
	}

	// If there are other unresolved identifiers, give up because this
	// synthesized file is not going to build.
	if len(unresolved) > 0 {
		return nil
	}

	// Include documentation belonging to blank imports.
	var comments []*ast.CommentGroup
	for _, s := range blankImports {
		if c := s.(*ast.ImportSpec).Doc; c != nil {
			comments = append(comments, c)
		}
	}

	// Include comments that are inside the function body.
	for _, c := range file.Comments {
		if body.Pos() <= c.Pos() && c.End() <= body.End() {
			comments = append(comments, c)
		}
	}

	// Strip "Output:" commment and adjust body end position.
	body, comments = stripOutputComment(body, comments)

	// Synthesize import declaration.
	importDecl := &ast.GenDecl{
		Tok:    token.IMPORT,
		Lparen: 1, // Need non-zero Lparen and Rparen so that printer
		Rparen: 1, // treats this as a factored import.
	}
	for n, p := range namedImports {
		s := &ast.ImportSpec{Path: &ast.BasicLit{Value: strconv.Quote(p)}}
		if path.Base(p) != n {
			s.Name = ast.NewIdent(n)
		}
		importDecl.Specs = append(importDecl.Specs, s)
	}
	importDecl.Specs = append(importDecl.Specs, blankImports...)

	// Synthesize main function.
	funcDecl := &ast.FuncDecl{
		Name: ast.NewIdent("main"),
		Type: &ast.FuncType{},
		Body: body,
	}

	// Synthesize file.
	return &ast.File{
		Name:     ast.NewIdent("main"),
		Decls:    []ast.Decl{importDecl, funcDecl},
		Comments: comments,
	}
}
Пример #20
0
// buildFunction takes a function Value, a list of parameters, and a body,
// and generates code for the function.
func (c *compiler) buildFunction(f *LLVMValue, context, params, results *types.Tuple, body *ast.BlockStmt) {
	if currblock := c.builder.GetInsertBlock(); !currblock.IsNil() {
		defer c.builder.SetInsertPointAtEnd(currblock)
	}
	llvm_fn := llvm.ConstExtractValue(f.LLVMValue(), []uint32{0})
	entry := llvm.AddBasicBlock(llvm_fn, "entry")
	c.builder.SetInsertPointAtEnd(entry)

	// For closures, context is the captured context values.
	var paramoffset int
	if context != nil {
		paramoffset++

		// Store the existing values. We're going to temporarily
		// replace the values with offsets into the context param.
		oldvalues := make([]*LLVMValue, context.Len())
		for i := range oldvalues {
			v := context.At(i)
			oldvalues[i] = c.objectdata[v].Value
		}
		defer func() {
			for i := range oldvalues {
				v := context.At(i)
				c.objectdata[v].Value = oldvalues[i]
			}
		}()

		// The context parameter is a pointer to a struct
		// whose elements are pointers to captured values.
		arg0 := llvm_fn.Param(0)
		for i := range oldvalues {
			v := context.At(i)
			argptr := c.builder.CreateStructGEP(arg0, i, "")
			argptr = c.builder.CreateLoad(argptr, "")
			ptrtyp := oldvalues[i].pointer.Type()
			newvalue := c.NewValue(argptr, ptrtyp)
			c.objectdata[v].Value = newvalue.makePointee()
		}
	}

	// Bind receiver, arguments and return values to their
	// identifiers/objects. We'll store each parameter on the stack so
	// they're addressable.
	nparams := int(params.Len())
	for i := 0; i < nparams; i++ {
		v := params.At(i)
		name := v.Name()
		if !isBlank(name) {
			value := llvm_fn.Param(i + paramoffset)
			c.newArgStackVar(i+1, f, v, value, name)
		}
	}

	funcstate := &function{LLVMValue: f, results: results}
	c.functions.push(funcstate)
	hasdefer := hasDefer(funcstate, body)

	// Allocate space on the stack for named results.
	for i := 0; i < results.Len(); i++ {
		v := results.At(i)
		name := v.Name()
		allocstack := !isBlank(name)
		if !allocstack && hasdefer {
			c.objectdata[v] = &ObjectData{}
			allocstack = true
		}
		if allocstack {
			typ := v.Type()
			llvmtyp := c.types.ToLLVM(typ)
			c.newStackVar(f, v, llvm.ConstNull(llvmtyp), name)
		}
	}

	// Create the function body.
	if hasdefer {
		c.makeDeferBlock(funcstate, body)
	}
	c.VisitBlockStmt(body, false)
	c.functions.pop()

	c.setDebugLine(body.End())

	// If the last instruction in the function is not a terminator, then
	// we either have unreachable code or a missing optional return statement
	// (the latter case is allowable only for functions without results).
	//
	// Use GetInsertBlock rather than LastBasicBlock, since the
	// last basic block might actually be a "defer" block.
	last := c.builder.GetInsertBlock()
	if in := last.LastInstruction(); in.IsNil() || in.IsATerminatorInst().IsNil() {
		c.builder.SetInsertPointAtEnd(last)
		if results.Len() == 0 {
			if funcstate.deferblock.IsNil() {
				c.builder.CreateRetVoid()
			} else {
				c.builder.CreateBr(funcstate.deferblock)
			}
		} else {
			c.builder.CreateUnreachable()
		}
	}
}
Пример #21
0
func (f *File) validFuncBody(block *ast.BlockStmt) ([]*ast.DeclStmt, []ast.Stmt, *Error) {
	var declStmts []*ast.DeclStmt
	var initStmts []ast.Stmt
	var finishStmts []ast.Stmt
	declSection, initSection, forSection, finishSection, retSection := true, false, false, false, false
	// empty func bodies not allowed
	if block.List == nil {
		return nil, nil, &Error{errors.New("Empty func bodies not allowed"), block.Pos()}
	}
	for _, stmt := range block.List {
		if declSection {
			decl, ok := stmt.(*ast.DeclStmt)
			if ok {
				if err := f.validDeclStmt(decl); err != nil {
					return nil, nil, err
				}
				declStmts = append(declStmts, decl)
				continue
			} else {
				declSection = false
				initSection = true
			}
		}
		if initSection {
			if err := f.validStmt(stmt); err == nil {
				initStmts = append(initStmts, stmt)
				continue
			} else {
				initSection = false
				forSection = true
			}
		}
		if forSection {
			if stmt, ok := stmt.(*ast.ForStmt); ok {
				if !f.validForStmt(stmt) {
					return nil, nil, &Error{errors.New("Invalid for statement"), stmt.Pos()}
				}
			}
			forSection = false
			finishSection = true
		}
		if finishSection {
			if f.validFinishStmt(stmt) {
				finishStmts = append(finishStmts, stmt)
				continue
			} else {
				finishSection = false
				retSection = true
			}
		}
		if retSection {
			if ret, ok := stmt.(*ast.ReturnStmt); ok {
				if err := f.validRetStmt(ret); err != nil {
					return nil, nil, err
				}
			} else {
				return nil, nil, &Error{errors.New("Expected return statement in retSection"), stmt.Pos()}
			}
		}
		pos := f.fs.Position(stmt.Pos())
		end := f.fs.Position(stmt.End())
		panic(fmt.Sprintf("PANIC: NO SECTION, stmt: (begin, end) - (%v, %v)", pos, end))
	}

	return declStmts, initStmts, nil
}
Пример #22
0
// We are not at a branch, employ the following algorithm:
//    * Traverse tree, storing any loop as a parent
//    * Find next source line after the given line
//    * Check and see if we've passed the scope of any parent we've
//      stored. If so, pop them off the stack. The last parent that
//      is left get's appending to our list of lines since we could
//      end up at the top of the loop again.
func (s *Searcher) parseDefaultBlock(ifRoot ast.Node, parsedFile *ast.File, line int) []int {
	var (
		found                bool
		lines                []int
		parents              []*ast.BlockStmt
		parentBlockBeginLine int
		deferEndLine         int
	)
	ast.Inspect(parsedFile, func(n ast.Node) bool {
		if found || n == nil {
			return false
		}

		pos := s.fileset.Position(n.Pos())
		if line < pos.Line && deferEndLine != 0 {
			p := s.fileset.Position(n.Pos())
			if deferEndLine < p.Line {
				found = true
				lines = append(lines, p.Line)
				return false
			}
		}

		if stmt, ok := n.(*ast.ForStmt); ok {
			parents = append(parents, stmt.Body)
			pos := s.fileset.Position(stmt.Pos())
			parentBlockBeginLine = pos.Line
		}

		if _, ok := n.(*ast.GenDecl); ok {
			return true
		}

		if dn, ok := n.(*ast.DeferStmt); ok && line < pos.Line {
			endpos := s.fileset.Position(dn.End())
			deferEndLine = endpos.Line
			return false
		}

		if st, ok := n.(*ast.DeclStmt); ok {
			beginpos := s.fileset.Position(st.Pos())
			endpos := s.fileset.Position(st.End())
			if beginpos.Line < endpos.Line {
				return true
			}
		}

		// Check to see if we've found the "next" line.
		if line < pos.Line {
			if _, ok := n.(*ast.BlockStmt); ok {
				return true
			}
			var (
				parent        *ast.BlockStmt
				parentEndLine int
			)
			for len(parents) > 0 {
				parent = parents[len(parents)-1]

				// Grab the line number of the right brace of the parent block.
				parentEndLine = s.fileset.Position(parent.Rbrace).Line

				// Check to see if we're still within the parents block.
				// If we are, we're done and that is our parent.
				if parentEndLine > line {
					parentBlockBeginLine = s.fileset.Position(parent.Pos()).Line
					break
				}
				// If we weren't, and there is only 1 parent, we no longer have one.
				if len(parents) == 1 {
					parent = nil
					break
				}
				// Remove that parent from the stack.
				parents = parents[0 : len(parents)-1]
			}
			if parent != nil {
				var (
					endfound   bool
					beginFound bool
					beginLine  int
				)

				ast.Inspect(parsedFile, func(n ast.Node) bool {
					if n == nil || endfound {
						return false
					}
					if _, ok := n.(*ast.BlockStmt); ok {
						return true
					}
					pos := s.fileset.Position(n.Pos())
					if parentBlockBeginLine < pos.Line && !beginFound {
						beginFound = true
						beginLine = pos.Line
						return true
					}
					if parentEndLine < pos.Line {
						if _, ok := n.(*ast.FuncDecl); !ok {
							lines = append(lines, beginLine, pos.Line)
						}
						endfound = true
						return false
					}
					return true
				})
				lines = append(lines, parentBlockBeginLine)
			}
			switch n.(type) {
			case *ast.BranchStmt, *ast.FuncDecl:
			default:
				lines = append(lines, pos.Line)
			}
			found = true
			return false
		}
		return true
	})
	if len(lines) == 0 && 0 < len(parents) {
		parent := parents[len(parents)-1]
		lbrace := s.fileset.Position(parent.Lbrace).Line
		pos := s.fileset.Position(parent.List[0].Pos())
		lines = append(lines, lbrace, pos.Line)
	}
	return lines
}
Пример #23
0
// playExample synthesizes a new *ast.File based on the provided
// file with the provided function body as the body of main.
func playExample(file *ast.File, body *ast.BlockStmt) *ast.File {
	if !strings.HasSuffix(file.Name.Name, "_test") {
		// We don't support examples that are part of the
		// greater package (yet).
		return nil
	}

	// Find unresolved identifiers
	unresolved := make(map[string]bool)
	ast.Inspect(body, func(n ast.Node) bool {
		// For an expression like fmt.Println, only add "fmt" to the
		// set of unresolved names.
		if e, ok := n.(*ast.SelectorExpr); ok {
			if id, ok := e.X.(*ast.Ident); ok && id.Obj == nil {
				unresolved[id.Name] = true
			}
			return false
		}
		if id, ok := n.(*ast.Ident); ok && id.Obj == nil {
			unresolved[id.Name] = true
		}
		return true
	})

	// Remove predeclared identifiers from unresolved list.
	for n := range unresolved {
		if predeclaredTypes[n] || predeclaredConstants[n] || predeclaredFuncs[n] {
			delete(unresolved, n)
		}
	}

	// Use unresolved identifiers to determine the imports used by this
	// example. The heuristic assumes package names match base import
	// paths. (Should be good enough most of the time.)
	imports := make(map[string]string) // [name]path
	for _, s := range file.Imports {
		p, err := strconv.Unquote(s.Path.Value)
		if err != nil {
			continue
		}
		n := path.Base(p)
		if s.Name != nil {
			if s.Name.Name == "." {
				// We can't resolve dot imports (yet).
				return nil
			}
			n = s.Name.Name
		}
		if unresolved[n] {
			imports[n] = p
			delete(unresolved, n)
		}
	}

	// If there are other unresolved identifiers, give up because this
	// synthesized file is not going to build.
	if len(unresolved) > 0 {
		return nil
	}

	// Filter out comments that are outside the function body.
	var comments []*ast.CommentGroup
	for _, c := range file.Comments {
		if c.Pos() < body.Pos() || c.Pos() >= body.End() {
			continue
		}
		comments = append(comments, c)
	}

	// Strip "Output:" commment and adjust body end position.
	body, comments = stripOutputComment(body, comments)

	// Synthesize import declaration.
	importDecl := &ast.GenDecl{
		Tok:    token.IMPORT,
		Lparen: 1, // Need non-zero Lparen and Rparen so that printer
		Rparen: 1, // treats this as a factored import.
	}
	for n, p := range imports {
		s := &ast.ImportSpec{Path: &ast.BasicLit{Value: strconv.Quote(p)}}
		if path.Base(p) != n {
			s.Name = ast.NewIdent(n)
		}
		importDecl.Specs = append(importDecl.Specs, s)
	}

	// Synthesize main function.
	funcDecl := &ast.FuncDecl{
		Name: ast.NewIdent("main"),
		Type: &ast.FuncType{},
		Body: body,
	}

	// Synthesize file.
	return &ast.File{
		Name:     ast.NewIdent("main"),
		Decls:    []ast.Decl{importDecl, funcDecl},
		Comments: comments,
	}
}
Пример #24
0
// block prints an *ast.BlockStmt; it always spans at least two lines.
func (p *printer) block(s *ast.BlockStmt, indent int) {
	p.print(s.Pos(), token.LBRACE)
	p.stmtList(s.List, indent, true)
	p.linebreak(s.Rbrace.Line, 1, ignore, true)
	p.print(s.Rbrace, token.RBRACE)
}
Пример #25
0
// Returns all possible lines that could be executed after the given file:line,
// within the same source file.
func (s *Searcher) NextLines(fname string, line int) (lines []int, err error) {
	var found bool
	n, err := s.FirstNodeAt(fname, line)
	if err != nil {
		return lines, nil
	}
	defer func() {
		if e := recover(); e != nil {
			e = e.(Done)
			nl := make([]int, 0, len(lines))
			fnd := make(map[int]bool)
			for _, l := range lines {
				if _, ok := fnd[l]; !ok {
					fnd[l] = true
					nl = append(nl, l)
				}
			}
			lines = nl
		}
	}()

	switch x := n.(type) {
	// Check if we are at an 'if' statement.
	//
	// If we are at an 'if' statement, employ the following algorithm:
	//    * Follow all 'else if' statements, appending their line number
	//    * Follow any 'else' statement if it exists, appending the line
	//      number of the statement following the 'else'.
	//    * If there is no 'else' statement, append line of first statement
	//      following the entire 'if' block.
	case *ast.IfStmt:
		var rbrace int
		p := x.Body.List[0].Pos()
		pos := s.fileset.Position(p)
		lines = append(lines, pos.Line)

		if x.Else == nil {
			// Grab first line after entire 'if' block
			rbrace = s.fileset.Position(x.Body.Rbrace).Line
			f, err := s.parse(fname)
			if err != nil {
				return nil, err
			}
			ast.Inspect(f, func(n ast.Node) bool {
				if n == nil {
					return true
				}
				pos := s.fileset.Position(n.Pos())
				if rbrace < pos.Line {
					lines = append(lines, pos.Line)
					panic(Done("done"))
				}
				return true
			})
		} else {
			// Follow any 'else' statements
			for {
				if stmt, ok := x.Else.(*ast.IfStmt); ok {
					pos := s.fileset.Position(stmt.Pos())
					lines = append(lines, pos.Line)
					x = stmt
					continue
				}
				if x.Else != nil {
					pos := s.fileset.Position(x.Else.Pos())
					ast.Inspect(x, func(n ast.Node) bool {
						if found {
							panic(Done("done"))
						}
						if n == nil {
							return false
						}
						p := s.fileset.Position(n.Pos())
						if pos.Line < p.Line {
							lines = append(lines, p.Line)
							found = true
							return false
						}
						return true
					})
				}
			}
		}

	// Follow case statements.
	//
	// Append line for first statement following each 'case' condition.
	case *ast.SwitchStmt:
		ast.Inspect(x, func(n ast.Node) bool {
			if stmt, ok := n.(*ast.SwitchStmt); ok {
				ast.Inspect(stmt, func(n ast.Node) bool {
					if stmt, ok := n.(*ast.CaseClause); ok {
						p := stmt.Body[0].Pos()
						pos := s.fileset.Position(p)
						lines = append(lines, pos.Line)
						return false
					}
					return true
				})
				panic(Done("done"))
			}
			return true
		})
	// Default case - find next source line.
	//
	// We are not at a branch, employ the following algorithm:
	//    * Traverse tree, storing any loop as a parent
	//    * Find next source line after the given line
	//    * Check and see if we've passed the scope of any parent we've
	//      stored. If so, pop them off the stack. The last parent that
	//      is left get's appending to our list of lines since we could
	//      end up at the top of the loop again.
	default:
		var (
			parents              []*ast.BlockStmt
			parentBlockBeginLine int
			deferEndLine         int
		)
		f, err := s.parse(fname)
		if err != nil {
			return nil, err
		}
		ast.Inspect(f, func(n ast.Node) bool {
			if found {
				panic(Done("done"))
			}
			if n == nil {
				return true
			}

			pos := s.fileset.Position(n.Pos())
			if line < pos.Line && deferEndLine != 0 {
				p := s.fileset.Position(n.Pos())
				if deferEndLine < p.Line {
					found = true
					lines = append(lines, p.Line)
					return false
				}
			}

			if stmt, ok := n.(*ast.ForStmt); ok {
				parents = append(parents, stmt.Body)
				pos := s.fileset.Position(stmt.Pos())
				parentBlockBeginLine = pos.Line
			}

			if _, ok := n.(*ast.GenDecl); ok {
				return true
			}

			if dn, ok := n.(*ast.DeferStmt); ok && line < pos.Line {
				endpos := s.fileset.Position(dn.End())
				deferEndLine = endpos.Line
				return false
			}

			if st, ok := n.(*ast.DeclStmt); ok {
				beginpos := s.fileset.Position(st.Pos())
				endpos := s.fileset.Position(st.End())
				if beginpos.Line < endpos.Line {
					return true
				}
			}

			// Check to see if we've found the "next" line.
			if line < pos.Line {
				if _, ok := n.(*ast.BlockStmt); ok {
					return true
				}
				var (
					parent        *ast.BlockStmt
					parentEndLine int
				)
				for len(parents) > 0 {
					parent = parents[len(parents)-1]

					// Grab the line number of the right brace of the parent block.
					parentEndLine = s.fileset.Position(parent.Rbrace).Line

					// Check to see if we're still within the parents block.
					// If we are, we're done and that is our parent.
					if parentEndLine > line {
						parentBlockBeginLine = s.fileset.Position(parent.Pos()).Line
						break
					}
					// If we weren't, and there is only 1 parent, we no longer have one.
					if len(parents) == 1 {
						parent = nil
						break
					}
					// Remove that parent from the stack.
					parents = parents[0 : len(parents)-1]
				}
				if parent != nil {
					var (
						endfound   bool
						beginFound bool
						beginLine  int
					)

					ast.Inspect(f, func(n ast.Node) bool {
						if n == nil || endfound {
							return false
						}
						if _, ok := n.(*ast.BlockStmt); ok {
							return true
						}
						pos := s.fileset.Position(n.Pos())
						if parentBlockBeginLine < pos.Line && !beginFound {
							beginFound = true
							beginLine = pos.Line
							return true
						}
						if parentEndLine < pos.Line {
							if _, ok := n.(*ast.FuncDecl); !ok {
								lines = append(lines, beginLine, pos.Line)
							}
							endfound = true
							return false
						}
						return true
					})
					lines = append(lines, parentBlockBeginLine)
				}
				switch n.(type) {
				case *ast.BranchStmt, *ast.FuncDecl:
				default:
					lines = append(lines, pos.Line)
				}
				found = true
				return false
			}
			return true
		})
		if len(lines) == 0 && 0 < len(parents) {
			parent := parents[len(parents)-1]
			lbrace := s.fileset.Position(parent.Lbrace).Line
			pos := s.fileset.Position(parent.List[0].Pos())
			lines = append(lines, lbrace, pos.Line)
		}
	}
	return lines, nil
}