Example #1
0
func grindFunc(ctxt *grinder.Context, pkg *grinder.Package, edit *grinder.EditBuffer, fn *ast.FuncDecl) {
	if fn.Name.Name == "evconst" {
		old := debug
		debug = true
		defer func() { debug = old }()
	}

	if pkg.TypesError != nil {
		// Without scoping information, we can't be sure code moves are okay.
		fmt.Printf("%s: cannot inline gotos without type information\n", fn.Name)
		return
	}

	if fn.Body == nil {
		return
	}
	blocks := block.Build(pkg.FileSet, fn.Body)
	for labelname, gotos := range blocks.Goto {
		target, ok := findTargetBlock(pkg, edit, fn, blocks, labelname)
		if debug {
			println("TARGET", ok, labelname, len(gotos), target.dead, target.short)
		}
		if ok && (len(gotos) == 1 && target.dead || target.short) {
			numReplaced := 0
			for _, g := range gotos {
				code := edit.TextAt(target.comment, target.start) + target.code
				if !objsMatch(pkg, fn, g.Pos(), target.objs, target.start, target.end) {
					if debug {
						println("OBJS DO NOT MATCH")
					}
					// Cannot inline code here; needed identifiers have different meanings.
					continue
				}
				if target.needReturn {
					// NOTE: Should really check to see if function results are shadowed.
					// If we screw up, the code won't compile, so we can put it off.
					code += "; return"
				}
				if target.needGoto != "" {
					code += "; goto " + target.needGoto
				}
				edit.Replace(g.Pos(), g.End(), code)
				numReplaced++
			}
			if numReplaced == len(gotos) {
				if len(gotos) == 1 && target.dead {
					edit.Delete(target.comment, target.end)
				} else {
					edit.DeleteLine(target.start, target.endLabel)
				}
			}
			// The code we move might itself have gotos to inline,
			// and we can't make that change until we get new line
			// number position, so return after each label change.
			if numReplaced > 0 {
				return
			}
		}
	}
}
Example #2
0
func grindFunc(ctxt *grinder.Context, pkg *grinder.Package, edit *grinder.EditBuffer, fn *ast.FuncDecl) {
	vars := analyzeFunc(pkg, edit, fn.Body)
	// fmt.Printf("%s", vardecl.PrintVars(conf.Fset, vars))
	for _, v := range vars {
		spec := v.Decl.Decl.(*ast.GenDecl).Specs[0].(*ast.ValueSpec)
		if len(spec.Names) > 1 {
			// TODO: Handle decls with multiple variables
			continue
		}
		if pkg.FileSet.Position(v.Decl.Pos()).Line != pkg.FileSet.Position(v.Decl.End()).Line {
			// Declaration spans line. Maybe not great to move or duplicate?
			continue
		}
		keepDecl := false
		for _, d := range v.Defs {
			if d.Init == v.Decl {
				keepDecl = true
				continue
			}
			switch x := d.Init.(type) {
			default:
				panic("unexpected init")
			case *ast.EmptyStmt:
				edit.CopyLine(v.Decl.Pos(), v.Decl.End(), x.Semicolon)
			case *ast.AssignStmt:
				edit.Insert(x.TokPos, ":")
				if !hasType(pkg, fn, edit, x.Rhs[0], x.Lhs[0]) {
					typ := edit.TextAt(spec.Type.Pos(), spec.Type.End())
					if strings.Contains(typ, " ") || typ == "interface{}" || typ == "struct{}" || strings.HasPrefix(typ, "*") {
						typ = "(" + typ + ")"
					}
					edit.Insert(x.Rhs[0].Pos(), typ+"(")
					edit.Insert(x.Rhs[0].End(), ")")
				}
			}
		}
		if !keepDecl {
			edit.DeleteLine(v.Decl.Pos(), v.Decl.End())
		}
	}

	if edit.NumEdits() == 0 {
		initToDecl(ctxt, pkg, edit, fn)
	}
}