// Sets multiLine to true if the declaration spans multiple lines. func (p *printer) genDecl(d *ast.GenDecl, multiLine *bool) { p.setComment(d.Doc) p.print(d.Pos(), d.Tok, blank) if d.Lparen.IsValid() { // group of parenthesized declarations p.print(d.Lparen, token.LPAREN) if len(d.Specs) > 0 { p.print(indent, formfeed) var ml bool for i, s := range d.Specs { if i > 0 { p.linebreak(s.Pos().Line, 1, ignore, ml) } ml = false p.spec(s, len(d.Specs), false, &ml) } p.print(unindent, formfeed) *multiLine = true } p.print(d.Rparen, token.RPAREN) } else { // single declaration p.spec(d.Specs[0], 1, true, multiLine) } }
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) }
// importsToDecl turns Lisp import into Go AST func importsToDecl(specs []ast.Spec) ast.Decl { s := ast.GenDecl{ Doc: genComment(), Tok: token.IMPORT, Specs: specs, } // https://godoc.org/go/ast#GenDecl // A valid Lparen position (Lparen.Line > 0) indicates a parenthesized declaration. s.Lparen = 1 return &s }
func genSnippet(fset *token.FileSet, d *ast.GenDecl, id *ast.Ident) *Snippet { s := findSpec(d.Specs, id) if s == nil { return nil // declaration doesn't contain id - exit gracefully } // only use the spec containing the id for the snippet dd := &ast.GenDecl{d.Doc, d.Pos(), d.Tok, d.Lparen, []ast.Spec{s}, d.Rparen} return newSnippet(fset, dd, id) }
func isEmpty(f *ast.File, g *ast.GenDecl) bool { if g.Doc != nil || g.Specs != nil { return false } for _, c := range f.Comments { // if there is a comment in the declaration, it is not considered empty if g.Pos() <= c.Pos() && c.End() <= g.End() { return false } } return true }
func (p *printer) genDecl(d *ast.GenDecl) { p.setComment(d.Doc) p.print(d.Pos(), d.Tok, blank) if d.Lparen.IsValid() { // group of parenthesized declarations p.print(d.Lparen, token.LPAREN) if n := len(d.Specs); n > 0 { p.print(indent, formfeed) if n > 1 && (d.Tok == token.CONST || d.Tok == token.VAR) { // two or more grouped const/var declarations: // determine if the type column must be kept keepType := keepTypeColumn(d.Specs) var line int for i, s := range d.Specs { if i > 0 { p.linebreak(p.lineFor(s.Pos()), 1, ignore, p.linesFrom(line) > 0) } p.recordLine(&line) switch t := s.(type) { case *ast.AliasSpec: p.aliasSpec(t, keepType[i]) case *ast.ValueSpec: p.valueSpec(t, keepType[i]) default: p.internalError("unknown ast.Spec type: %T", t) } } } else { var line int for i, s := range d.Specs { if i > 0 { p.linebreak(p.lineFor(s.Pos()), 1, ignore, p.linesFrom(line) > 0) } p.recordLine(&line) p.spec(s, n, false) } } p.print(unindent, formfeed) } p.print(d.Rparen, token.RPAREN) } else { // single declaration p.spec(d.Specs[0], 1, true) } }
// assocInitvals associates "inherited" initialization expressions // with the corresponding *ast.ValueSpec in the check.initexprs map // for constant declarations without explicit initialization expressions. // func (check *checker) assocInitvals(decl *ast.GenDecl) { var values []ast.Expr for _, s := range decl.Specs { if s, ok := s.(*ast.ValueSpec); ok { if len(s.Values) > 0 { values = s.Values } else { check.initexprs[s] = values } } } if len(values) == 0 { check.invalidAST(decl.Pos(), "no initialization values provided") } }
// assocInitvals associates "inherited" initialization expressions // with the corresponding *ast.ValueSpec in the check.initspecs map // for constant declarations without explicit initialization expressions. // func (check *checker) assocInitvals(decl *ast.GenDecl) { var last *ast.ValueSpec for _, s := range decl.Specs { if s, ok := s.(*ast.ValueSpec); ok { if len(s.Values) > 0 { last = s } else { check.initspecs[s] = last } } } if last == nil { check.invalidAST(decl.Pos(), "no initialization values provided") } }
// readType processes a type declaration. // func (r *reader) readType(decl *ast.GenDecl, spec *ast.TypeSpec) { typ := r.lookupType(spec.Name.Name) if typ == nil { return // no name or blank name - ignore the type } // A type should be added at most once, so typ.decl // should be nil - if it is not, simply overwrite it. typ.decl = decl // compute documentation doc := spec.Doc spec.Doc = nil // doc consumed - remove from AST if doc == nil { // no doc associated with the spec, use the declaration doc, if any doc = decl.Doc } decl.Doc = nil // doc consumed - remove from AST typ.doc = doc.Text() // record anonymous fields (they may contribute methods) // (some fields may have been recorded already when filtering // exports, but that's ok) var list []*ast.Field list, typ.isStruct = fields(spec.Type) for _, field := range list { if len(field.Names) == 0 { r.recordAnonymousField(typ, field.Type) } } }
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 }
func (p *Presentation) genSnippet(fset *token.FileSet, d *ast.GenDecl, id *ast.Ident) *Snippet { s := findSpec(d.Specs, id) if s == nil { return nil // declaration doesn't contain id - exit gracefully } // only use the spec containing the id for the snippet dd := &ast.GenDecl{ Doc: d.Doc, TokPos: d.Pos(), Tok: d.Tok, Lparen: d.Lparen, Specs: []ast.Spec{s}, Rparen: d.Rparen, } return p.newSnippet(fset, dd, id) }
// Sets multiLine to true if the declaration spans multiple lines. func (p *printer) genDecl(d *ast.GenDecl, multiLine *bool) { p.setComment(d.Doc) p.print(d.Pos(), d.Tok, blank) if d.Lparen.IsValid() { // group of parenthesized declarations p.print(d.Lparen, token.LPAREN) if n := len(d.Specs); n > 0 { p.print(indent, formfeed) if n > 1 && (d.Tok == token.CONST || d.Tok == token.VAR) { // two or more grouped const/var declarations: // determine if the type column must be kept keepType := keepTypeColumn(d.Specs) var ml bool for i, s := range d.Specs { if i > 0 { p.linebreak(p.fset.Position(s.Pos()).Line, 1, ignore, ml) } ml = false p.valueSpec(s.(*ast.ValueSpec), keepType[i], false, &ml) } } else { var ml bool for i, s := range d.Specs { if i > 0 { p.linebreak(p.fset.Position(s.Pos()).Line, 1, ignore, ml) } ml = false p.spec(s, n, false, &ml) } } p.print(unindent, formfeed) *multiLine = true } p.print(d.Rparen, token.RPAREN) } else { // single declaration p.spec(d.Specs[0], 1, true, multiLine) } }
func (p *printer) genDecl(d *ast.GenDecl) { p.setComment(d.Doc) p.print(d.Pos(), d.Tok, blank) if d.Lparen.IsValid() { // group of parenthesized declarations p.print(d.Lparen, token.LPAREN) if n := len(d.Specs); n > 0 { p.print(indent, formfeed) if n > 1 && (d.Tok == token.CONST || d.Tok == token.VAR) { // two or more grouped const/var declarations: // determine if the type column must be kept keepType := keepTypeColumn(d.Specs) newSection := false for i, s := range d.Specs { if i > 0 { p.linebreak(p.lineFor(s.Pos()), 1, ignore, newSection) } p.valueSpec(s.(*ast.ValueSpec), keepType[i], false) newSection = p.isMultiLine(s) } } else { newSection := false for i, s := range d.Specs { if i > 0 { p.linebreak(p.lineFor(s.Pos()), 1, ignore, newSection) } p.spec(s, n, false) newSection = p.isMultiLine(s) } } p.print(unindent, formfeed) } p.print(d.Rparen, token.RPAREN) } else { // single declaration p.spec(d.Specs[0], 1, true) } }
func newValue(decl *ast.GenDecl) *Value { v := new(Value) v.Doc = doc.CommentText(decl.Doc) decl.Doc = nil // count names and figure out type n := 0 for _, spec := range decl.Specs { vspec := spec.(*ast.ValueSpec) for _, name := range vspec.Names { if ast.IsExported(name.Name) { n++ } } if v.Type == "" { t := typeAsString(vspec.Type) if t != "" && ast.IsExported(t) { v.Type = t } } } if n == 0 { return nil } // collect names v.Names = make([]string, n) i := 0 for _, spec := range decl.Specs { vspec := spec.(*ast.ValueSpec) for _, name := range vspec.Names { if !ast.IsExported(name.Name) { continue } v.Names[i] = name.Name i++ } } v.Decl = decl return v }
// readType processes a type declaration. // func (r *reader) readType(decl *ast.GenDecl, spec *ast.TypeSpec) { typ := r.lookupType(spec.Name.Name) if typ == nil { return // no name or blank name - ignore the type } // A type should be added at most once, so info.decl // should be nil - if it is not, simply overwrite it. typ.decl = decl // compute documentation doc := spec.Doc spec.Doc = nil // doc consumed - remove from AST if doc == nil { // no doc associated with the spec, use the declaration doc, if any doc = decl.Doc } decl.Doc = nil // doc consumed - remove from AST typ.doc = doc.Text() // look for anonymous fields that might contribute methods var list []*ast.Field list, typ.isStruct = fields(spec.Type) for _, field := range list { if len(field.Names) == 0 { // anonymous field - add corresponding field type to typ n, imp := baseTypeName(field.Type) if imp { // imported type - we don't handle this case // at the moment return } if embedded := r.lookupType(n); embedded != nil { _, ptr := field.Type.(*ast.StarExpr) typ.addEmbeddedType(embedded, ptr) } } } }
// oneLineValueGenDecl prints a var or const declaration as a single line. func (pkg *Package) oneLineValueGenDecl(decl *ast.GenDecl) { decl.Doc = nil dotDotDot := "" if len(decl.Specs) > 1 { dotDotDot = " ..." } // Find the first relevant spec. for i, spec := range decl.Specs { valueSpec := spec.(*ast.ValueSpec) // Must succeed; we can't mix types in one genDecl. if !isExported(valueSpec.Names[0].Name) { continue } typ := "" if valueSpec.Type != nil { typ = fmt.Sprintf(" %s", pkg.formatNode(valueSpec.Type)) } val := "" if i < len(valueSpec.Values) && valueSpec.Values[i] != nil { val = fmt.Sprintf(" = %s", pkg.formatNode(valueSpec.Values[i])) } pkg.Printf("%s %s%s%s%s\n", decl.Tok, valueSpec.Names[0], typ, val, dotDotDot) break } }
// 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 }
// 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)) }
// 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 }
// readValue processes a const or var declaration. // func (r *reader) readValue(decl *ast.GenDecl) { // determine if decl should be associated with a type // Heuristic: For each typed entry, determine the type name, if any. // If there is exactly one type name that is sufficiently // frequent, associate the decl with the respective type. domName := "" domFreq := 0 prev := "" n := 0 for _, spec := range decl.Specs { s, ok := spec.(*ast.ValueSpec) if !ok { continue // should not happen, but be conservative } name := "" switch { case s.Type != nil: // a type is present; determine its name if n, imp := baseTypeName(s.Type); !imp { name = n } case decl.Tok == token.CONST: // no type is present but we have a constant declaration; // use the previous type name (w/o more type information // we cannot handle the case of unnamed variables with // initializer expressions except for some trivial cases) name = prev } if name != "" { // entry has a named type if domName != "" && domName != name { // more than one type name - do not associate // with any type domName = "" break } domName = name domFreq++ } prev = name n++ } // nothing to do w/o a legal declaration if n == 0 { return } // determine values list with which to associate the Value for this decl values := &r.values const threshold = 0.75 if domName != "" && r.isVisible(domName) && domFreq >= int(float64(len(decl.Specs))*threshold) { // typed entries are sufficiently frequent if typ := r.lookupType(domName); typ != nil { values = &typ.values // associate with that type } } *values = append(*values, &Value{ Doc: decl.Doc.Text(), Names: specNames(decl.Specs), Decl: decl, order: len(*values), }) decl.Doc = nil // doc consumed - remove from AST }
// 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 }