func constDecl(kind token.Token, args ...string) *ast.GenDecl { decl := ast.GenDecl{Tok: token.CONST} if len(args)%3 != 0 { panic("Number of values passed to ConstString must be a multiple of 3") } for i := 0; i < len(args); i += 3 { name, typ, val := args[i], args[i+1], args[i+2] lit := &ast.BasicLit{Kind: kind} if kind == token.STRING { lit.Value = strconv.Quote(val) } else { lit.Value = val } a := &ast.ValueSpec{ Names: []*ast.Ident{ast.NewIdent(name)}, Values: []ast.Expr{lit}, } if typ != "" { a.Type = ast.NewIdent(typ) } decl.Specs = append(decl.Specs, a) } if len(decl.Specs) > 1 { decl.Lparen = 1 } return &decl }
// addImport adds the import path to the file f, if absent. func addImport(f *ast.File, path string) { if imports(f, path) { return } newImport := &ast.ImportSpec{ Path: &ast.BasicLit{ Kind: token.STRING, Value: strconv.Quote(path), }, } var impdecl *ast.GenDecl // Find an import decl to add to. for _, decl := range f.Decls { gen, ok := decl.(*ast.GenDecl) if ok && gen.Tok == token.IMPORT { impdecl = gen break } } // No import decl found. Add one. if impdecl == nil { impdecl = &ast.GenDecl{ Tok: token.IMPORT, } f.Decls = append(f.Decls, nil) copy(f.Decls[1:], f.Decls) f.Decls[0] = impdecl } // Ensure the import decl has parentheses, if needed. if len(impdecl.Specs) > 0 && !impdecl.Lparen.IsValid() { impdecl.Lparen = impdecl.Pos() } // Assume the import paths are alphabetically ordered. // If they are not, the result is ugly, but legal. insertAt := len(impdecl.Specs) // default to end of specs for i, spec := range impdecl.Specs { impspec := spec.(*ast.ImportSpec) if importPath(impspec) > path { insertAt = i break } } impdecl.Specs = append(impdecl.Specs, nil) copy(impdecl.Specs[insertAt+1:], impdecl.Specs[insertAt:]) impdecl.Specs[insertAt] = newImport f.Imports = append(f.Imports, newImport) }
func ast_decl_split(d ast.Decl) []ast.Decl { var decls []ast.Decl if t, ok := d.(*ast.GenDecl); ok { decls = make([]ast.Decl, len(t.Specs)) for i, s := range t.Specs { decl := new(ast.GenDecl) *decl = *t decl.Specs = make([]ast.Spec, 1) decl.Specs[0] = s decls[i] = decl } } else { decls = make([]ast.Decl, 1) decls[0] = d } return decls }
// AddNamedImport adds the import path to the file f, if absent. // If name is not empty, it is used to rename the import. // // For example, calling // AddNamedImport(fset, f, "pathpkg", "path") // adds // import pathpkg "path" func AddNamedImport(fset *token.FileSet, f *ast.File, name, ipath string) (added bool) { if imports(f, ipath) { return false } newImport := &ast.ImportSpec{ Path: &ast.BasicLit{ Kind: token.STRING, Value: strconv.Quote(ipath), }, } if name != "" { newImport.Name = &ast.Ident{Name: name} } // Find an import decl to add to. // The goal is to find an existing import // whose import path has the longest shared // prefix with ipath. var ( bestMatch = -1 // length of longest shared prefix lastImport = -1 // index in f.Decls of the file's final import decl impDecl *ast.GenDecl // import decl containing the best match impIndex = -1 // spec index in impDecl containing the best match ) for i, decl := range f.Decls { gen, ok := decl.(*ast.GenDecl) if ok && gen.Tok == token.IMPORT { lastImport = i // Do not add to import "C", to avoid disrupting the // association with its doc comment, breaking cgo. if declImports(gen, "C") { continue } // Match an empty import decl if that's all that is available. if len(gen.Specs) == 0 && bestMatch == -1 { impDecl = gen } // Compute longest shared prefix with imports in this group. for j, spec := range gen.Specs { impspec := spec.(*ast.ImportSpec) n := matchLen(importPath(impspec), ipath) if n > bestMatch { bestMatch = n impDecl = gen impIndex = j } } } } // If no import decl found, add one after the last import. if impDecl == nil { impDecl = &ast.GenDecl{ Tok: token.IMPORT, } if lastImport >= 0 { impDecl.TokPos = f.Decls[lastImport].End() } else { // There are no existing imports. // Our new import goes after the package declaration and after // the comment, if any, that starts on the same line as the // package declaration. impDecl.TokPos = f.Package file := fset.File(f.Package) pkgLine := file.Line(f.Package) for _, c := range f.Comments { if file.Line(c.Pos()) > pkgLine { break } impDecl.TokPos = c.End() } } f.Decls = append(f.Decls, nil) copy(f.Decls[lastImport+2:], f.Decls[lastImport+1:]) f.Decls[lastImport+1] = impDecl } // Insert new import at insertAt. insertAt := 0 if impIndex >= 0 { // insert after the found import insertAt = impIndex + 1 } impDecl.Specs = append(impDecl.Specs, nil) copy(impDecl.Specs[insertAt+1:], impDecl.Specs[insertAt:]) impDecl.Specs[insertAt] = newImport pos := impDecl.Pos() if insertAt > 0 { // If there is a comment after an existing import, preserve the comment // position by adding the new import after the comment. if spec, ok := impDecl.Specs[insertAt-1].(*ast.ImportSpec); ok && spec.Comment != nil { pos = spec.Comment.End() } else { // Assign same position as the previous import, // so that the sorter sees it as being in the same block. pos = impDecl.Specs[insertAt-1].Pos() } } if newImport.Name != nil { newImport.Name.NamePos = pos } newImport.Path.ValuePos = pos newImport.EndPos = pos // Clean up parens. impDecl contains at least one spec. if len(impDecl.Specs) == 1 { // Remove unneeded parens. impDecl.Lparen = token.NoPos } else if !impDecl.Lparen.IsValid() { // impDecl needs parens added. impDecl.Lparen = impDecl.Specs[0].Pos() } f.Imports = append(f.Imports, newImport) if len(f.Decls) <= 1 { return true } // Merge all the import declarations into the first one. var first *ast.GenDecl for i := 0; i < len(f.Decls); i++ { decl := f.Decls[i] gen, ok := decl.(*ast.GenDecl) if !ok || gen.Tok != token.IMPORT || declImports(gen, "C") { continue } if first == nil { first = gen continue // Don't touch the first one. } // We now know there is more than one package in this import // declaration. Ensure that it ends up parenthesized. first.Lparen = first.Pos() // Move the imports of the other import declaration to the first one. for _, spec := range gen.Specs { spec.(*ast.ImportSpec).Path.ValuePos = first.Pos() first.Specs = append(first.Specs, spec) } f.Decls = append(f.Decls[:i], f.Decls[i+1:]...) i-- } return true }
func imp(fset *token.FileSet, af *ast.File, toggle []ImportDeclArg) *ast.File { add := map[ImportDecl]bool{} del := map[ImportDecl]bool{} for _, sda := range toggle { sd := ImportDecl{ Path: sda.Path, Name: sda.Name, } if sda.Add { add[sd] = true } else { del[sd] = true } } var firstDecl *ast.GenDecl imports := map[ImportDecl]bool{} for _, decl := range af.Decls { if gdecl, ok := decl.(*ast.GenDecl); ok && len(gdecl.Specs) > 0 { hasC := false sj := 0 for _, spec := range gdecl.Specs { if ispec, ok := spec.(*ast.ImportSpec); ok { sd := ImportDecl{ Path: unquote(ispec.Path.Value), } if ispec.Name != nil { sd.Name = ispec.Name.String() } if sd.Path == "C" { hasC = true } else if del[sd] { if sj > 0 { if lspec, ok := gdecl.Specs[sj-1].(*ast.ImportSpec); ok { lspec.EndPos = ispec.Pos() } } continue } else { imports[sd] = true } } gdecl.Specs[sj] = spec sj += 1 } gdecl.Specs = gdecl.Specs[:sj] if !hasC && firstDecl == nil { firstDecl = gdecl } } } if len(add) > 0 { if firstDecl == nil { firstDecl = &ast.GenDecl{ Tok: token.IMPORT, Lparen: 1, } af.Decls = append(af.Decls, firstDecl) } else if firstDecl.Lparen == token.NoPos { firstDecl.Lparen = 1 } for sd, _ := range add { if !imports[sd] { ispec := &ast.ImportSpec{ Path: &ast.BasicLit{ Value: quote(sd.Path), Kind: token.STRING, }, } if sd.Name != "" { ispec.Name = &ast.Ident{ Name: sd.Name, } } firstDecl.Specs = append(firstDecl.Specs, ispec) imports[sd] = true } } } dj := 0 for _, decl := range af.Decls { if gdecl, ok := decl.(*ast.GenDecl); ok { if len(gdecl.Specs) == 0 { continue } } af.Decls[dj] = decl dj += 1 } af.Decls = af.Decls[:dj] ast.SortImports(fset, af) return af }
// AddNamedImport adds the import path to the file f, if absent. // If name is not empty, it is used to rename the import. // // For example, calling // AddNamedImport(fset, f, "pathpkg", "path") // adds // import pathpkg "path" func AddNamedImport(fset *token.FileSet, f *ast.File, name, ipath string) (added bool) { if imports(f, ipath) { return false } newImport := &ast.ImportSpec{ Path: &ast.BasicLit{ Kind: token.STRING, Value: strconv.Quote(ipath), }, } if name != "" { newImport.Name = &ast.Ident{Name: name} } // Find an import decl to add to. var ( bestMatch = -1 lastImport = -1 impDecl *ast.GenDecl impIndex = -1 hasImports = false ) for i, decl := range f.Decls { gen, ok := decl.(*ast.GenDecl) if ok && gen.Tok == token.IMPORT { hasImports = true lastImport = i // Do not add to import "C", to avoid disrupting the // association with its doc comment, breaking cgo. if declImports(gen, "C") { continue } // Compute longest shared prefix with imports in this block. for j, spec := range gen.Specs { impspec := spec.(*ast.ImportSpec) n := matchLen(importPath(impspec), ipath) if n > bestMatch { bestMatch = n impDecl = gen impIndex = j } } } } // If no import decl found, add one after the last import. if impDecl == nil { // TODO(bradfitz): remove this hack. See comment below on // addImportViaSourceModification. if !hasImports { f2, err := addImportViaSourceModification(fset, f, name, ipath) if err == nil { *f = *f2 return true } log.Printf("addImportViaSourceModification error: %v", err) } // TODO(bradfitz): fix above and resume using this old code: impDecl = &ast.GenDecl{ Tok: token.IMPORT, } f.Decls = append(f.Decls, nil) copy(f.Decls[lastImport+2:], f.Decls[lastImport+1:]) f.Decls[lastImport+1] = impDecl } // Ensure the import decl has parentheses, if needed. if len(impDecl.Specs) > 0 && !impDecl.Lparen.IsValid() { impDecl.Lparen = impDecl.Pos() } insertAt := impIndex + 1 if insertAt == 0 { insertAt = len(impDecl.Specs) } impDecl.Specs = append(impDecl.Specs, nil) copy(impDecl.Specs[insertAt+1:], impDecl.Specs[insertAt:]) impDecl.Specs[insertAt] = newImport if insertAt > 0 { // Assign same position as the previous import, // so that the sorter sees it as being in the same block. prev := impDecl.Specs[insertAt-1] newImport.Path.ValuePos = prev.Pos() newImport.EndPos = prev.Pos() } if len(impDecl.Specs) > 1 && impDecl.Lparen == 0 { // set Lparen to something not zero, so the printer prints // the full block rather just the first ImportSpec. impDecl.Lparen = 1 } f.Imports = append(f.Imports, newImport) return true }
// addImport adds the import path to the file f, if absent. func addImport(f *ast.File, ipath string) (added bool) { if imports(f, ipath) { return false } // Determine name of import. // Assume added imports follow convention of using last element. _, name := path.Split(ipath) // Rename any conflicting top-level references from name to name_. renameTop(f, name, name+"_") newImport := &ast.ImportSpec{ Path: &ast.BasicLit{ Kind: token.STRING, Value: strconv.Quote(ipath), }, } // Find an import decl to add to. var ( bestMatch = -1 lastImport = -1 impDecl *ast.GenDecl impIndex = -1 ) for i, decl := range f.Decls { gen, ok := decl.(*ast.GenDecl) if ok && gen.Tok == token.IMPORT { lastImport = i // Do not add to import "C", to avoid disrupting the // association with its doc comment, breaking cgo. if declImports(gen, "C") { continue } // Compute longest shared prefix with imports in this block. for j, spec := range gen.Specs { impspec := spec.(*ast.ImportSpec) n := matchLen(importPath(impspec), ipath) if n > bestMatch { bestMatch = n impDecl = gen impIndex = j } } } } // If no import decl found, add one after the last import. if impDecl == nil { impDecl = &ast.GenDecl{ Tok: token.IMPORT, } f.Decls = append(f.Decls, nil) copy(f.Decls[lastImport+2:], f.Decls[lastImport+1:]) f.Decls[lastImport+1] = impDecl } // Ensure the import decl has parentheses, if needed. if len(impDecl.Specs) > 0 && !impDecl.Lparen.IsValid() { impDecl.Lparen = impDecl.Pos() } insertAt := impIndex + 1 if insertAt == 0 { insertAt = len(impDecl.Specs) } impDecl.Specs = append(impDecl.Specs, nil) copy(impDecl.Specs[insertAt+1:], impDecl.Specs[insertAt:]) impDecl.Specs[insertAt] = newImport if insertAt > 0 { // Assign same position as the previous import, // so that the sorter sees it as being in the same block. prev := impDecl.Specs[insertAt-1] newImport.Path.ValuePos = prev.Pos() newImport.EndPos = prev.Pos() } f.Imports = append(f.Imports, newImport) return true }
// TODO(bradfitz): delete this function (and whole file) once // http://golang.org/issue/4380 is fixed. func clone(i interface{}) (cloned interface{}) { if debugClone { defer func() { if !reflect.DeepEqual(i, cloned) { log.Printf("cloned %T doesn't match: in=%#v out=%#v", i, i, cloned) } }() } switch v := i.(type) { case nil: return nil case *ast.File: o := &ast.File{ Doc: v.Doc, // shallow Package: v.Package, Comments: v.Comments, // shallow Name: v.Name, Scope: v.Scope, } for _, x := range v.Decls { o.Decls = append(o.Decls, clone(x).(ast.Decl)) } for _, x := range v.Imports { o.Imports = append(o.Imports, clone(x).(*ast.ImportSpec)) } for _, x := range v.Unresolved { o.Unresolved = append(o.Unresolved, x) } return o case *ast.GenDecl: o := new(ast.GenDecl) *o = *v o.Specs = nil for _, x := range v.Specs { o.Specs = append(o.Specs, clone(x).(ast.Spec)) } return o case *ast.TypeSpec: o := new(ast.TypeSpec) *o = *v o.Type = cloneExpr(v.Type) return o case *ast.InterfaceType: o := new(ast.InterfaceType) *o = *v o.Methods = clone(v.Methods).(*ast.FieldList) return o case *ast.FieldList: if v == nil { return v } o := new(ast.FieldList) *o = *v o.List = nil for _, x := range v.List { o.List = append(o.List, clone(x).(*ast.Field)) } return o case *ast.Field: o := &ast.Field{ Doc: v.Doc, // shallow Type: cloneExpr(v.Type), Tag: clone(v.Tag).(*ast.BasicLit), Comment: v.Comment, // shallow } for _, x := range v.Names { o.Names = append(o.Names, clone(x).(*ast.Ident)) } return o case *ast.FuncType: if v == nil { return v } return &ast.FuncType{ Func: v.Func, Params: clone(v.Params).(*ast.FieldList), Results: clone(v.Results).(*ast.FieldList), } case *ast.FuncDecl: if v == nil { return v } return &ast.FuncDecl{ Recv: clone(v.Recv).(*ast.FieldList), Name: v.Name, Type: clone(v.Type).(*ast.FuncType), Body: v.Body, // shallow } case *ast.ValueSpec: if v == nil { return v } o := &ast.ValueSpec{ Type: cloneExpr(v.Type), } for _, x := range v.Names { o.Names = append(o.Names, x) } for _, x := range v.Values { o.Values = append(o.Values, cloneExpr(x)) } return o case *ast.CallExpr: if v == nil { return v } o := &ast.CallExpr{} *o = *v o.Args = cloneExprs(v.Args) o.Fun = cloneExpr(v.Fun) return o case *ast.SelectorExpr: if v == nil { return nil } return &ast.SelectorExpr{ X: cloneExpr(v.X), Sel: v.Sel, } case *ast.ArrayType: return &ast.ArrayType{ Lbrack: v.Lbrack, Len: cloneExpr(v.Len), Elt: cloneExpr(v.Elt), } case *ast.StructType: return &ast.StructType{ Struct: v.Struct, Fields: clone(v.Fields).(*ast.FieldList), Incomplete: v.Incomplete, } case *ast.StarExpr: return &ast.StarExpr{ Star: v.Star, X: cloneExpr(v.X), } case *ast.CompositeLit: return &ast.CompositeLit{ Type: cloneExpr(v.Type), Lbrace: v.Lbrace, Elts: cloneExprs(v.Elts), Rbrace: v.Rbrace, } case *ast.UnaryExpr: return &ast.UnaryExpr{ OpPos: v.OpPos, Op: v.Op, X: cloneExpr(v.X), } case *ast.BinaryExpr: return &ast.BinaryExpr{ OpPos: v.OpPos, Op: v.Op, X: cloneExpr(v.X), Y: cloneExpr(v.Y), } case *ast.Ellipsis: return &ast.Ellipsis{ Ellipsis: v.Ellipsis, Elt: cloneExpr(v.Elt), } case *ast.KeyValueExpr: return &ast.KeyValueExpr{ Key: cloneExpr(v.Key), Colon: v.Colon, Value: cloneExpr(v.Value), } case *ast.FuncLit: return &ast.FuncLit{ Type: clone(v.Type).(*ast.FuncType), Body: v.Body, // shallow } case *ast.MapType: return &ast.MapType{ Map: v.Map, Key: cloneExpr(v.Key), Value: cloneExpr(v.Value), } case *ast.ParenExpr: return &ast.ParenExpr{ Lparen: v.Lparen, X: cloneExpr(v.X), Rparen: v.Rparen, } case *ast.Ident, *ast.BasicLit: return v case *ast.ImportSpec: return &ast.ImportSpec{ Doc: v.Doc, // shallow Name: v.Name, Path: clone(v.Path).(*ast.BasicLit), Comment: v.Comment, // shallow EndPos: v.EndPos, } case *ast.ChanType: return &ast.ChanType{ Begin: v.Begin, Arrow: v.Arrow, Dir: v.Dir, Value: cloneExpr(v.Value), } case *ast.TypeAssertExpr: return &ast.TypeAssertExpr{ X: cloneExpr(v.X), Type: cloneExpr(v.Type), } case *ast.IndexExpr: return &ast.IndexExpr{ X: cloneExpr(v.X), Index: cloneExpr(v.Index), Lbrack: v.Lbrack, Rbrack: v.Rbrack, } } panic(fmt.Sprintf("Uncloneable type %T", i)) }