// FormatCode runs "goimports -w" on the source file. func (f *SourceFile) FormatCode() error { if NoFormat { return nil } // Parse file into AST fset := token.NewFileSet() file, err := parser.ParseFile(fset, f.Abs(), nil, parser.ParseComments) if err != nil { return err } // Clean unused imports imports := astutil.Imports(fset, file) for _, group := range imports { for _, imp := range group { path := strings.Trim(imp.Path.Value, `"`) if !astutil.UsesImport(file, path) { astutil.DeleteImport(fset, file, path) } } } ast.SortImports(fset, file) // Open file to be written w, err := os.OpenFile(f.Abs(), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.ModePerm) if err != nil { return err } defer w.Close() // Write formatted code without unused imports return format.Node(w, fset, file) }
// FormatCode runs "goimports -w" on the source file. func (f *SourceFile) FormatCode() error { // Parse file into AST fset := token.NewFileSet() file, err := parser.ParseFile(fset, f.Abs(), nil, parser.ParseComments) if err != nil { content, _ := ioutil.ReadFile(f.Abs()) var buf bytes.Buffer scanner.PrintError(&buf, err) return fmt.Errorf("%s\n========\nContent:\n%s", buf.String(), content) } // Clean unused imports imports := astutil.Imports(fset, file) for _, group := range imports { for _, imp := range group { path := strings.Trim(imp.Path.Value, `"`) if !astutil.UsesImport(file, path) { if imp.Name != nil { astutil.DeleteNamedImport(fset, file, imp.Name.Name, path) } else { astutil.DeleteImport(fset, file, path) } } } } ast.SortImports(fset, file) // Open file to be written w, err := os.OpenFile(f.Abs(), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.ModePerm) if err != nil { return err } defer w.Close() // Write formatted code without unused imports return format.Node(w, fset, file) }
// Clean writes the clean source to io.Writer. The source can be a io.Reader, // string or []bytes func Clean(w io.Writer, src interface{}) error { fset := token.NewFileSet() file, err := parser.ParseFile(fset, "clean.go", src, parser.ParseComments) if err != nil { return err } // Clean unused imports imports := astutil.Imports(fset, file) for _, group := range imports { for _, imp := range group { path := strings.Trim(imp.Path.Value, `"`) if !astutil.UsesImport(file, path) { astutil.DeleteImport(fset, file, path) } } } ast.SortImports(fset, file) // Write formatted code without unused imports return format.Node(w, fset, file) }
func Generate(filename string, typenames ...string) ([]byte, error) { fset := token.NewFileSet() f, err := parser.ParseFile(fset, filename, nil, parser.ParseComments) if err != nil { return nil, err } f = replace(func(node ast.Node) ast.Node { se, ok := node.(*ast.SelectorExpr) if !ok { return node } x, ok := se.X.(*ast.Ident) if !ok || x.Name != genericPkg { return node } for i, t := range genericTypes { if se.Sel.Name == t { return &ast.Ident{NamePos: 0, Name: typenames[i]} } } return node }, f).(*ast.File) if !astutil.UsesImport(f, pkgPath) { astutil.DeleteImport(fset, f, pkgPath) } var buf bytes.Buffer if err = format.Node(&buf, fset, f); err != nil { return nil, err } return format.Source(buf.Bytes()) }
// RemoveImport will remove an import from source code func RemoveImport(source, path string) string { header, body := header(source) if header == "" { panic("parse failure") } src := []byte(header) fset := token.NewFileSet() f, err := parser.ParseFile(fset, "", src, 0) if err != nil { panic(err) } astutil.DeleteImport(fset, f, path) ast.SortImports(fset, f) var buf bytes.Buffer err = printer.Fprint(&buf, fset, f) if err != nil { panic(err) } return buf.String() + "\n" + body }
func fixImports(fset *token.FileSet, f *ast.File) (added []string, err error) { // refs are a set of possible package references currently unsatisfied by imports. // first key: either base package (e.g. "fmt") or renamed package // second key: referenced package symbol (e.g. "Println") refs := make(map[string]map[string]bool) // decls are the current package imports. key is base package or renamed package. decls := make(map[string]*ast.ImportSpec) // collect potential uses of packages. var visitor visitFn visitor = visitFn(func(node ast.Node) ast.Visitor { if node == nil { return visitor } switch v := node.(type) { case *ast.ImportSpec: if v.Name != nil { decls[v.Name.Name] = v } else { local := importPathToName(strings.Trim(v.Path.Value, `\"`)) decls[local] = v } case *ast.SelectorExpr: xident, ok := v.X.(*ast.Ident) if !ok { break } if xident.Obj != nil { // if the parser can resolve it, it's not a package ref break } pkgName := xident.Name if refs[pkgName] == nil { refs[pkgName] = make(map[string]bool) } if decls[pkgName] == nil { refs[pkgName][v.Sel.Name] = true } } return visitor }) ast.Walk(visitor, f) // Search for imports matching potential package references. searches := 0 type result struct { ipath string name string err error } results := make(chan result) for pkgName, symbols := range refs { if len(symbols) == 0 { continue // skip over packages already imported } go func(pkgName string, symbols map[string]bool) { ipath, rename, err := findImport(pkgName, symbols) r := result{ipath: ipath, err: err} if rename { r.name = pkgName } results <- r }(pkgName, symbols) searches++ } for i := 0; i < searches; i++ { result := <-results if result.err != nil { return nil, result.err } if result.ipath != "" { if result.name != "" { astutil.AddNamedImport(fset, f, result.name, result.ipath) } else { astutil.AddImport(fset, f, result.ipath) } added = append(added, result.ipath) } } // Nil out any unused ImportSpecs, to be removed in following passes unusedImport := map[string]bool{} for pkg, is := range decls { if refs[pkg] == nil && pkg != "_" && pkg != "." { unusedImport[strings.Trim(is.Path.Value, `"`)] = true } } for ipath := range unusedImport { if ipath == "C" { // Don't remove cgo stuff. continue } astutil.DeleteImport(fset, f, ipath) } return added, nil }
// ProcessFileAST processes the files using golang's AST parser func ProcessFileAST(filePath string, from string, to string) { //Colors to be used on the console red := ansi.ColorCode("red+bh") white := ansi.ColorCode("white+bh") yellow := ansi.ColorCode("yellow+bh") blackOnWhite := ansi.ColorCode("black+b:white+h") //Reset the color reset := ansi.ColorCode("reset") fmt.Println(blackOnWhite+"Processing file", filePath, "in SAFE MODE", reset) // New FileSet to parse the go file to fSet := token.NewFileSet() // Parse the file file, err := parser.ParseFile(fSet, filePath, nil, 0) if err != nil { fmt.Println(err) } // Get the list of imports from the ast imports := astutil.Imports(fSet, file) // Keep track of number of changes numChanges := 0 // Iterate through the imports array for _, mPackage := range imports { for _, mImport := range mPackage { // Since astutil returns the path string with quotes, remove those importString := strings.TrimSuffix(strings.TrimPrefix(mImport.Path.Value, "\""), "\"") // If the path matches the oldpath, replace it with the new one if strings.Contains(importString, from) { //If it needs to be replaced, increase numChanges so we can write the file later numChanges++ // Join the path of the import package with the remainder from the old one after removing the old import package replacePackage := strings.Replace(importString, from, to, -1) fmt.Println(red + "Updating import " + reset + white + importString + reset + red + " to " + reset + white + replacePackage + reset) // Remove the old import and replace it with the replacement astutil.DeleteImport(fSet, file, importString) astutil.AddImport(fSet, file, replacePackage) } } } // If the number of changes are more than 0, write file if numChanges > 0 { // Print the new AST tree to a new output buffer var outputBuffer bytes.Buffer printer.Fprint(&outputBuffer, fSet, file) ioutil.WriteFile(filePath, outputBuffer.Bytes(), os.ModePerm) fmt.Println(yellow+ "File", filePath, "saved after", numChanges, "changes", reset, "\n\n") } else { fmt.Println(yellow+ "No changes to write on this file.", reset, "\n\n") } }
func (d *DirectiveList) Save() error { fset := token.NewFileSet() f, err := parser.ParseFile(fset, d.file, nil, 0) if err != nil { return ErrInvalidDirectiveFile } imps := astutil.Imports(fset, f) for _, imp := range imps[0] { name := "" if imp.Name != nil && imp.Name.String() != "." { name = imp.Name.String() } path, _ := strconv.Unquote(imp.Path.Value) if name != "" { astutil.DeleteNamedImport(fset, f, name, path) } else { astutil.DeleteImport(fset, f, path) } } astutil.AddImport(fset, f, "github.com/mholt/caddy/caddy/https") astutil.AddImport(fset, f, "github.com/mholt/caddy/caddy/parse") astutil.AddImport(fset, f, "github.com/mholt/caddy/caddy/setup") astutil.AddImport(fset, f, "github.com/mholt/caddy/middleware") importName := "" for _, dir := range d.list { if dir.Removed { continue } if dir.Core == false && len(dir.ImportPath) != 0 { importName = strings.Replace(dir.Name, "-", "_", -1) astutil.AddImport(fset, f, fmt.Sprintf("{{import-%s}}", importName)) } } var buf bytes.Buffer err = printer.Fprint(&buf, fset, f) if err != nil { return err } out := buf.String() f, err = parser.ParseFile(token.NewFileSet(), "", out, 0) node, ok := f.Scope.Lookup("directiveOrder").Decl.(ast.Node) if !ok { return ErrInvalidDirectiveFile } begin := out[0 : node.Pos()-1] end := out[node.End():len(out)] dirOrder := "" for _, dir := range d.list { if dir.Removed { continue } comment := "" if dir.Active == false { comment = "//@caddyext " } importName = strings.Replace(dir.Name, "-", "_", -1) begin = strings.Replace(begin, fmt.Sprintf(`"{{import-%s}}"`, importName), fmt.Sprintf(`%s%s "%s"`, comment, importName, dir.ImportPath), -1) if dir.Core == true { dirOrder = dirOrder + fmt.Sprintf(` %s{"%s", %s},`+"\n", comment, dir.Name, dir.Setup) } else { dirOrder = dirOrder + fmt.Sprintf(` %s{"%s", %s.Setup},`+"\n", comment, dir.Name, importName) } } out = begin + dirOrderStart + dirOrder + dirOrderEnd + end return ioutil.WriteFile(d.file, []byte(out), os.FileMode(0660)) }
func mainret(file *ast.File) bool { fixed := false // Add "os" import. addImport(file, "os") // Locate the "main" function. mainFunc, ok := findMainFunc(file) if !ok { return false } // Apply the following transitions for the "main" function: // // 1) // // from: // return 42 // // // to: // os.Exit(42) // // 2) // // from: // return 0 // // // to: // return // // 3) // // from: // func main() { // return // } // // // to: // func main() { // } walk(mainFunc, func(n interface{}) { stmt, ok := n.(*ast.Stmt) if !ok { return } retStmt, ok := (*stmt).(*ast.ReturnStmt) if !ok { return } switch len(retStmt.Results) { case 0: // Leave blank returns as is. return case 1: result := retStmt.Results[0] if isZero(result) { // Replace "return 0" with "return". retStmt.Results = nil } else { // Replace "return 42" with "os.Exit(42)". exit := createExit(result) *stmt = exit } fixed = true default: log.Fatalf("invalid number of arguments to return; expected 1, got %d", len(retStmt.Results)) } }) // Remove "os" import if not required. if !astutil.UsesImport(file, "os") { astutil.DeleteImport(token.NewFileSet(), file, "os") } // Remove trailing blank return statement. list := mainFunc.Body.List n := len(list) if n > 0 { if isEmptyReturn(list[n-1]) { mainFunc.Body.List = list[:n-1] fixed = true } } return fixed }