func inlineMethod(programTree *program.Program, filename string, lineStart int, colStart int, lineEnd int, colEnd int) (bool, *errors.GoRefactorError) { if ok, err := CheckInlineMethodParameters(filename, lineStart, colStart, lineEnd, colEnd); !ok { return false, err } pack, file := programTree.FindPackageAndFileByFilename(filename) if pack == nil { return false, errors.ArgumentError("filename", "Program packages don't contain file '"+filename+"'") } fset := pack.FileSet callNode, nodeFrom := getCall(fset, file, filename, lineStart, colStart, lineEnd, colEnd) if callNode == nil { return false, &errors.GoRefactorError{ErrorType: "inline method error", Message: "couldn't find call expression"} } _, CallAsExpression := callNode.(*ast.CallExpr) callExpr, err := getCallExpr(callNode) if err != nil { return false, err } funSym, err := getMethodSymbol(programTree, callExpr) if err != nil { return false, err } if funSym.PackageFrom() != pack { return false, &errors.GoRefactorError{ErrorType: "inline method error", Message: "can't inline method from other package"} } decl, sourceFile, err := getDeclarationInFile(programTree, pack, funSym) if err != nil { return false, err } if len(decl.Body.List) == 0 { ok, err := printerUtil.DeleteNode(fset, filename, file, fset.Position(callExpr.Pos()), fset.Position(callExpr.End())) if !ok { return false, err } programTree.SaveFileExplicit(filename, fset, file) return true, nil } destScope := getDestScope(programTree, pack, nodeFrom) newNames := getNewNames(callExpr, funSym, destScope) for sym, expr := range newNames { fmt.Printf("%s -> ", sym.Name()) printer.Fprint(os.Stdout, token.NewFileSet(), expr) print("; ") } tokFile := printerUtil.GetFileFromFileSet(fset, filename) sourceTokFile := printerUtil.GetFileFromFileSet(fset, sourceFile) lines := printerUtil.GetLines(tokFile) for i, offset := range lines { fmt.Printf("%d -> %s(%d)\n", i+1, fset.Position(tokFile.Pos(offset)), offset) } oldListLines, fline := getRangeLinesAtLeastOne(sourceTokFile, decl.Body.List[0].Pos(), decl.Body.List[len(decl.Body.List)-1].End(), sourceTokFile.Size()) fmt.Printf("fline = %d\n", fline) fmt.Printf("CONVERTER before: %v\n", oldListLines) fmt.Printf("CONVERTER pos,end: %d,%d\n", decl.Body.List[0].Pos(), decl.Body.List[len(decl.Body.List)-1].End()) resList, newListLines, importsToAdd := getResultStmtList(programTree.IdentMap, pack, funSym, newNames, sourceFile, filename, decl.Body.List, pack.FileSet, callExpr.Pos()) sourceLines := printerUtil.GetLines(sourceTokFile) zeroSourceLine := newListLines[0] - sourceLines[fline-2] fmt.Printf("zeroSourceLine = %d\n", zeroSourceLine) fmt.Printf("CONVERTER after: %v\n", newListLines) fmt.Printf("CONVERTER pos,end: %d,%d\n", resList[0].Pos(), resList[len(resList)-1].End()) callExprLen := int(callExpr.End() - callExpr.Pos()) listLen := newListLines[len(newListLines)-1] - sourceLines[fline-2] importsLen := 0 for ps, _ := range importsToAdd { fmt.Printf("import \"%s\"\n", ps.ShortPath) importsLen += len("import \"\"\n") + len(ps.ShortPath) } mod := listLen + importsLen - callExprLen fmt.Printf("mod = %d\n", mod) if mod > 0 { println("REPARSING *****************************************") oldSourceTokFileSize := sourceTokFile.Size() fset, file = printerUtil.ReparseFile(file, filename, mod, programTree.IdentMap) tokFile = printerUtil.GetFileFromFileSet(fset, filename) sourceTokFile = printerUtil.GetFileFromFileSet(pack.FileSet, sourceFile) lines = printerUtil.GetLines(tokFile) tokFile.SetLines(lines[:len(lines)-(mod)]) callNode, nodeFrom = getCall(fset, file, filename, lineStart, colStart, lineEnd, colEnd) callExpr, _ = getCallExpr(callNode) funSym, _ = getMethodSymbol(programTree, callExpr) decl, sourceFile, _ = getDeclarationInFile(programTree, pack, funSym) destScope = getDestScope(programTree, pack, nodeFrom) newNames = getNewNames(callExpr, funSym, destScope) lines = printerUtil.GetLines(tokFile) for i, offset := range lines { fmt.Printf("%d -> %s(%d)\n", i+1, fset.Position(tokFile.Pos(offset)), offset) } oldListLines, fline = getRangeLinesAtLeastOne(sourceTokFile, decl.Body.List[0].Pos(), decl.Body.List[len(decl.Body.List)-1].End(), oldSourceTokFileSize) fmt.Printf("fline = %d\n", fline) fmt.Printf("CONVERTER before: %v\n", oldListLines) fmt.Printf("CONVERTER pos,end: %d,%d\n", decl.Body.List[0].Pos(), decl.Body.List[len(decl.Body.List)-1].End()) resList, newListLines, importsToAdd = getResultStmtList(programTree.IdentMap, pack, funSym, newNames, sourceFile, filename, decl.Body.List, pack.FileSet, callExpr.Pos()) sourceLines := printerUtil.GetLines(sourceTokFile) zeroSourceLine = newListLines[0] - sourceLines[fline-2] fmt.Printf("lines[fline - 1] = %d, zeroSourceLine = %d\n", sourceLines[fline-2], zeroSourceLine) fmt.Printf("CONVERTER after: %v\n", newListLines) fmt.Printf("CONVERTER pos,end: %d,%d\n", resList[0].Pos(), resList[len(resList)-1].End()) } impPos := file.Decls[0].Pos() nextLineInd := 0 for i, offs := range lines { if offs > tokFile.Offset(impPos) { nextLineInd = i break } } for ps, _ := range importsToAdd { printDecls(tokFile, file) file.Decls = append([]ast.Decl{makeImportDecl(impPos, ps.ShortPath)}, file.Decls...) mod := len("import \"\"\n") + len(ps.ShortPath) //positions printerUtil.FixPositionsExcept(impPos-token.Pos(1), mod, file, true, map[ast.Node]bool{file.Decls[0]: true}) //lines lines = printerUtil.GetLines(tokFile) fmt.Printf("before import %s (%d): %v\n", ps.ShortPath, impPos, lines) newLines := make([]int, 0, len(lines)+1) newLines = append(newLines, lines[:nextLineInd]...) newLines = append(newLines, tokFile.Offset(impPos)) newLines = append(newLines, lines[nextLineInd:]...) for i := nextLineInd; i < len(newLines); i++ { newLines[i] += mod } fmt.Printf("after import %s (nextLine = %d): %v\n", ps.ShortPath, nextLineInd, newLines) if !tokFile.SetLines(newLines) { panic("couldn't set lines for file " + tokFile.Name()) } printDecls(tokFile, file) } resMod := int(callExpr.Pos() - resList[0].Pos()) for _, stmt := range resList { printerUtil.FixPositions(0, resMod, stmt, true) } lines = printerUtil.GetLines(tokFile) resLinesMod := lines[tokFile.Line(callExpr.Pos())-1] - newListLines[0] + zeroSourceLine fmt.Printf("resLinesMod: %d\n", resLinesMod) for i, _ := range newListLines { newListLines[i] += resLinesMod } fmt.Printf("call pos: %d,%d; list pos: %d,%d\n", callExpr.Pos(), callExpr.End(), resList[0].Pos(), resList[len(resList)-1].End()) fmt.Printf("newLines %v\n", newListLines) lines = printerUtil.GetLines(tokFile) for i, offset := range lines { fmt.Printf("%d -> %s(%d)\n", i+1, fset.Position(tokFile.Pos(offset)), offset) } if CallAsExpression { rs, ok := resList[0].(*ast.ReturnStmt) if !ok { return false, &errors.GoRefactorError{ErrorType: "inline method error", Message: "method, inlined as expression, must have only one statement - return statement"} } switch len(rs.Results) { case 0: panic("methods, inlined as expression, doesn't return anything") default: elist := rs.Results for _, e := range elist { printerUtil.FixPositions(0, -len("return "), e, true) } mod := int(elist[len(elist)-1].End()-elist[0].Pos()) - callExprLen lines = printerUtil.GetLines(tokFile) fmt.Printf("before last (mod = %d) : %v\n", mod, lines) for i, offset := range lines { if offset > tokFile.Offset(callExpr.Pos()) { for j := i; j < len(lines); j++ { lines[j] += mod } break } } fmt.Printf("after last (mod = %d) : %v\n", mod, lines) fmt.Printf("posits: %s,%s\n", fset.Position(callExpr.Pos()), fset.Position(callExpr.End())) if !tokFile.SetLines(lines) { panic("couldn't set lines for file " + tokFile.Name()) } printerUtil.FixPositionsExcept(callExpr.Pos(), mod, file, true, map[ast.Node]bool{callExpr: true}) errs := replaceExprList(fset.Position(callExpr.Pos()), fset.Position(callExpr.End()), elist, fset, file) if err, ok := errs[INLINE_METHOD]; ok { return false, err } programTree.SaveFileExplicit(filename, fset, file) } } else { list := getStmtList(nodeFrom) i, ok := getIndexOfStmt(callNode.(ast.Stmt), list) if !ok { panic("couldn't find call statement during inline") } callExprLine := tokFile.Line(callExpr.Pos()) - 1 mod := int(resList[len(resList)-1].End()-resList[0].Pos()) - callExprLen printerUtil.FixPositions(callExpr.Pos(), mod, file, true) lines = printerUtil.GetLines(tokFile) fmt.Printf("before last (mod = %d) : %v\n", mod, lines) newLines := make([]int, 0, len(lines)+len(newListLines)-1) newLines = append(newLines, lines[:callExprLine+1]...) newLines = append(newLines, newListLines...) newLines = append(newLines, lines[callExprLine+2:]...) fmt.Printf("after last (lines[callExprLine] = %d): %v\n", lines[callExprLine], newLines) for i := callExprLine + 1 + len(newListLines); i < len(newLines); i++ { newLines[i] += mod } fmt.Printf("after last: %v\n", newLines) if !tokFile.SetLines(newLines) { panic("couldn't set lines for file " + tokFile.Name()) } if len(resList) == 1 { list[i] = resList[0] programTree.SaveFileExplicit(filename, fset, file) return true, nil } fmt.Printf("len = %d\n", len(list)-1+len(resList)) fmt.Printf("%v\n", list) newList := make([]ast.Stmt, len(list)-1+len(resList)) for j := 0; j < i; j++ { newList[j] = list[j] } fmt.Printf("%v\n", newList) for j := 0; j < len(resList); j++ { newList[j+i] = resList[j] } fmt.Printf("%v\n", newList) for j := i + 1; j < len(list); j++ { newList[j+len(resList)-1] = list[j] } fmt.Printf("%v\n", newList) setStmtList(nodeFrom, newList) programTree.SaveFileExplicit(filename, fset, file) } return true, nil }
func extractInterface(programTree *program.Program, filename string, line int, column int, interfaceName string) (bool, *errors.GoRefactorError) { if ok, err := CheckExtractInterfaceParameters(filename, line, column, interfaceName); !ok { return false, err } pack, file := programTree.FindPackageAndFileByFilename(filename) if pack == nil { return false, errors.ArgumentError("filename", "Program packages don't contain file '"+filename+"'") } fset := pack.FileSet mod := 20000 fset, file = printerUtil.ReparseFile(file, filename, mod, programTree.IdentMap) tokFile := printerUtil.GetFileFromFileSet(fset, filename) lines := printerUtil.GetLines(tokFile) tokFile.SetLines(lines[:len(lines)-(mod)]) if _, ok := pack.Symbols.LookUp(interfaceName, filename); ok { return false, errors.IdentifierAlreadyExistsError(interfaceName) } field, nameNum, fdecl, ok := getSelectedArgument(file, token.Position{filename, 0, line, column}, fset) if !ok { return false, &errors.GoRefactorError{ErrorType: "extract interface error", Message: "couldn't find function argument to extract interface"} } if field.Names == nil { return false, &errors.GoRefactorError{ErrorType: "extract interface error", Message: "argument has no name, so empty interface"} } varS, ok := programTree.IdentMap.GetSymbol(field.Names[nameNum]).(*st.VariableSymbol) if !ok { panic("symbol supposed to be a variable, but it's not") } meths, errs := getUsedMethods(programTree, fset, fdecl, varS) if len(errs) > 0 { println("some errors don't allow to extract interface:") for e, _ := range errs { println(e.String()) } return false, &errors.GoRefactorError{ErrorType: "extract interface error", Message: "some errors occured, stopped extracting"} } methList, i := make([]*ast.Field, len(meths)), 0 for m, _ := range meths { methList[i] = &ast.Field{nil, []*ast.Ident{ast.NewIdent(m.Name())}, m.FunctionType.ToAstExpr(pack, filename), nil, nil} i++ } interfaceMethods := &ast.FieldList{token.NoPos, methList, token.NoPos} interfaceType := &ast.InterfaceType{token.NoPos, interfaceMethods, false} interfaceDecl := &ast.TypeSpec{nil, ast.NewIdent(interfaceName), interfaceType, nil} genDecl := &ast.GenDecl{nil, token.NoPos, token.TYPE, token.NoPos, []ast.Spec{interfaceDecl}, token.NoPos} fieldNum := 0 for i := 0; i < len(fdecl.Type.Params.List); i++ { if field == fdecl.Type.Params.List[i] { fieldNum = i break } } oldFTypeLen := int(fdecl.Type.End() - fdecl.Type.Pos()) var lField, mField, rField *ast.Field mField = &ast.Field{nil, []*ast.Ident{field.Names[nameNum]}, ast.NewIdent(interfaceName), nil, nil} if nameNum > 0 { lField = &ast.Field{nil, field.Names[:nameNum], field.Type, nil, nil} } if nameNum < len(field.Names)-1 { rField = &ast.Field{nil, field.Names[(nameNum + 1):], field.Type, nil, nil} } newList := make([]*ast.Field, fieldNum) copy(newList, fdecl.Type.Params.List[:fieldNum]) if lField != nil { newList = append(newList, lField) } newList = append(newList, mField) if rField != nil { newList = append(newList, rField) } newList = append(newList, fdecl.Type.Params.List[(fieldNum+1):]...) fdecl.Type.Params.List = newList newFTypeLen, _ := utils.GetNodeLength(fdecl.Type) newFTypeLen += len(fdecl.Name.Name) + 1 fmt.Printf("old %d new %d\n", oldFTypeLen, newFTypeLen) allMod := newFTypeLen - oldFTypeLen if allMod != 0 { printerUtil.FixPositions(fdecl.Type.End(), allMod, file, true) lines = printerUtil.GetLines(tokFile) fmt.Printf("before last (mod = %d) : %v\n", allMod, lines) for i, offset := range lines { if offset > tokFile.Offset(fdecl.Type.Pos()) { for j := i; j < len(lines); j++ { lines[j] += allMod } break } } fmt.Printf("after last (mod = %d) : %v\n", allMod, lines) if !tokFile.SetLines(lines) { panic("couldn't set lines for file " + tokFile.Name()) } } file.Decls = append(file.Decls, genDecl) programTree.SaveFileExplicit(filename, fset, file) return true, nil }
func extractMethod(programTree *program.Program, filename string, lineStart int, colStart int, lineEnd int, colEnd int, methodName string, recieverVarLine int, recieverVarCol int) (bool, *errors.GoRefactorError) { if ok, err := CheckExtractMethodParameters(filename, lineStart, colStart, lineEnd, colEnd, methodName, recieverVarLine, recieverVarCol); !ok { return false, err } pack, file := programTree.FindPackageAndFileByFilename(filename) if pack == nil { return false, errors.ArgumentError("filename", "Program packages don't contain file '"+filename+"'") } fset := pack.FileSet recvSym, err := getRecieverSymbol(programTree, pack, filename, recieverVarLine, recieverVarCol) if err != nil { return false, err } if recvSym != nil { if recvSym.VariableType.Methods() != nil { if _, ok := recvSym.VariableType.Methods().LookUp(methodName, ""); ok { return false, errors.ArgumentError("methodName", "reciever already contains a method with name "+methodName) } } switch t := recvSym.VariableType.(type) { case *st.StructTypeSymbol: if _, ok := t.Fields.LookUp(methodName, ""); ok { return false, errors.ArgumentError("methodName", "reciever already contains a field with name "+methodName) } case *st.PointerTypeSymbol: if _, ok := t.Fields.LookUp(methodName, ""); ok { return false, errors.ArgumentError("methodName", "reciever already contains a field with name "+methodName) } } } else { if _, ok := pack.Symbols.LookUp(methodName, ""); ok { return false, errors.ArgumentError("methodName", "package already contains a symbol with name "+methodName) } } stmtList, nodeFrom, err := getExtractedStatementList(pack, file, filename, lineStart, colStart, lineEnd, colEnd) if err != nil { return false, err } fmt.Printf("list pos,end = %d,%d\n", stmtList[0].Pos(), stmtList[len(stmtList)-1].End()) params, declared := getParametersAndDeclaredIn(pack, stmtList, programTree) fmt.Printf("list pos,end = %d,%d\n", stmtList[0].Pos(), stmtList[len(stmtList)-1].End()) if recvSym != nil { if _, found := params.LookUp(recvSym.Name(), ""); !found { return false, &errors.GoRefactorError{ErrorType: "extract method error", Message: "symbol, desired to be reciever, is not a parameter to extracted code"} } params.RemoveSymbol(recvSym.Name()) } resultList := getResultList(programTree, pack, filename, stmtList) results := st.NewSymbolTable(pack) for _, r := range resultList { results.AddSymbol(st.MakeVariable(st.NO_NAME, results, r)) } fmt.Printf("list pos,end = %d,%d\n", stmtList[0].Pos(), stmtList[len(stmtList)-1].End()) pointerSymbols := getPointerPassedSymbols(stmtList, params, programTree.IdentMap) fmt.Printf("list pos,end = %d,%d\n", stmtList[0].Pos(), stmtList[len(stmtList)-1].End()) for s, depth := range pointerSymbols { println(s.Name(), depth) } applyPointerTransform(fset, file, stmtList, pointerSymbols, programTree.IdentMap) fmt.Printf("list pos,end = %d,%d\n", stmtList[0].Pos(), stmtList[len(stmtList)-1].End()) fdecl := makeFuncDecl(methodName, stmtList, params, pointerSymbols, results, recvSym, pack, filename) if nodeFrom != nil { callExpr, callExprLen := makeCallExpr(methodName, params, pointerSymbols, stmtList[0].Pos(), recvSym, pack, filename) if ok, errs := checkScoping(nodeFrom, stmtList, declared, programTree.IdentMap); !ok { s := "" errs.ForEach(func(sym st.Symbol) { s += sym.Name() + " " }) return false, &errors.GoRefactorError{ErrorType: "extract method error", Message: "extracted code declares symbols that are used in not-extracted code: " + s} } app := callExprLen - int(stmtList[len(stmtList)-1].End()-stmtList[0].Pos()) if app > 0 { S, E := fset.Position(nodeFrom.Pos()), fset.Position(nodeFrom.End()) poses, ends := make([]token.Position, len(stmtList)), make([]token.Position, len(stmtList)) for i, stmt := range stmtList { poses[i], ends[i] = fset.Position(stmt.Pos()), fset.Position(stmt.End()) } tfile := printerUtil.GetFileFromFileSet(fset, filename) baseMod := tfile.Base() fmt.Printf("app = %d,baseMod = %d\n", app, baseMod) fset, file = printerUtil.ReparseFile(file, filename, app, programTree.IdentMap) tfile = printerUtil.GetFileFromFileSet(fset, filename) lines := printerUtil.GetLines(tfile) tfile.SetLines(lines[:len(lines)-(app)]) nodeFrom = printerUtil.FindNode(fset, file, S, E) if baseMod != 1 { for _, stmt := range stmtList { printerUtil.FixPositions(0, 1-baseMod, stmt, true) } printerUtil.FixPositions(0, 1-baseMod, callExpr, true) printerUtil.FixPositions(0, 1-baseMod, fdecl, true) } stmtList = make([]ast.Stmt, len(stmtList)) for i, _ := range stmtList { stmtList[i] = printerUtil.FindNode(fset, file, poses[i], ends[i]).(ast.Stmt) } } list := getStmtList(nodeFrom) ind, found := getIndexOfStmt(stmtList[0], list) if !found { panic("didn't find replace origin") } fmt.Printf("stmtList length = %d\n", len(stmtList)) if ok, err := printerUtil.DeleteNodeList(fset, filename, file, stmtList); !ok { return false, err } list = getStmtList(nodeFrom) newList := make([]ast.Stmt, len(list)+1) copy(newList, list[0:ind]) newList[ind] = &ast.ExprStmt{callExpr} for i := ind; i < len(list); i++ { newList[i+1] = list[i] } printerUtil.AddLineForRange(fset, filename, callExpr.Pos(), callExpr.End()) setStmtList(nodeFrom, newList) printerUtil.FixPositionsExcept(callExpr.Pos(), callExprLen, file, true, map[ast.Node]bool{callExpr: true}) } else { //stmtList[0] = utils.CopyAstNode(stmtList[0]).(ast.Stmt) rs := stmtList[0].(*ast.ReturnStmt) callExpr, callExprLen := makeCallExpr(methodName, params, pointerSymbols, rs.Results[0].Pos(), recvSym, pack, filename) mod, baseMod := callExprLen-int(rs.Results[len(rs.Results)-1].End()-rs.Results[0].Pos()), 0 fset, file, baseMod = printerUtil.ModifyLine(pack.FileSet, file, filename, programTree.IdentMap, callExpr.Pos(), mod) if baseMod != 1 { fmt.Printf("baseMod = %d\n", baseMod) printerUtil.FixPositions(0, 1-baseMod, callExpr, true) printerUtil.FixPositions(0, 1-baseMod, rs, true) printerUtil.FixPositions(0, 1-baseMod, fdecl, true) } fmt.Printf("results st,end = %d,%d, callExpr pos,end = %d,%d\n", rs.Results[0].Pos(), rs.Results[len(rs.Results)-1].End(), callExpr.Pos(), callExpr.End()) errs := replaceExprList(fset.Position(rs.Results[0].Pos()), fset.Position(rs.Results[len(rs.Results)-1].End()), []ast.Expr{callExpr}, fset, file) if err, ok := errs[EXTRACT_METHOD]; ok { return false, err } fmt.Printf("mod = %d\n", mod) printerUtil.FixPositionsExcept(callExpr.Pos(), mod, file, true, map[ast.Node]bool{callExpr: true}) } programTree.SaveFileExplicit(filename, fset, file) print("AAAAAA") if ok, fset, newF, err := printerUtil.AddDeclExplicit(fset, filename, file, fset, filename, file, fdecl, programTree.IdentMap); !ok { return false, err } else { print("BBBBBB") programTree.SaveFileExplicit(filename, fset, newF) } return true, nil }