Declared map variable without initialization; writing to a nil map causes a panic. Use make() to initialize
var predeclaredTypes = map[string]bool{
1// Copyright 2009 The Go Authors. All rights reserved.2// Use of this source code is governed by a BSD-style3// license that can be found in the LICENSE file.45package doc67import (8 "cmp"9 "fmt"10 "go/ast"11 "go/token"12 "internal/lazyregexp"13 "path"14 "slices"15 "strconv"16 "strings"17 "unicode"18 "unicode/utf8"19)2021// ----------------------------------------------------------------------------22// function/method sets23//24// Internally, we treat functions like methods and collect them in method sets.2526// A methodSet describes a set of methods. Entries where Decl == nil are conflict27// entries (more than one method with the same name at the same embedding level).28type methodSet map[string]*Func2930// recvString returns a string representation of recv of the form "T", "*T",31// "T[A, ...]", "*T[A, ...]" or "BADRECV" (if not a proper receiver type).32func recvString(recv ast.Expr) string {33 switch t := recv.(type) {34 case *ast.Ident:35 return t.Name36 case *ast.StarExpr:37 return "*" + recvString(t.X)38 case *ast.IndexExpr:39 // Generic type with one parameter.40 return fmt.Sprintf("%s[%s]", recvString(t.X), recvParam(t.Index))41 case *ast.IndexListExpr:42 // Generic type with multiple parameters.43 if len(t.Indices) > 0 {44 var b strings.Builder45 b.WriteString(recvString(t.X))46 b.WriteByte('[')47 b.WriteString(recvParam(t.Indices[0]))48 for _, e := range t.Indices[1:] {49 b.WriteString(", ")50 b.WriteString(recvParam(e))51 }52 b.WriteByte(']')53 return b.String()54 }55 }56 return "BADRECV"57}5859func recvParam(p ast.Expr) string {60 if id, ok := p.(*ast.Ident); ok {61 return id.Name62 }63 return "BADPARAM"64}6566// set creates the corresponding Func for f and adds it to mset.67// If there are multiple f's with the same name, set keeps the first68// one with documentation; conflicts are ignored. The boolean69// specifies whether to leave the AST untouched.70func (mset methodSet) set(f *ast.FuncDecl, preserveAST bool) {71 name := f.Name.Name72 if g := mset[name]; g != nil && g.Doc != "" {73 // A function with the same name has already been registered;74 // since it has documentation, assume f is simply another75 // implementation and ignore it. This does not happen if the76 // caller is using go/build.ScanDir to determine the list of77 // files implementing a package.78 return79 }80 // function doesn't exist or has no documentation; use f81 recv := ""82 if f.Recv != nil {83 var typ ast.Expr84 // be careful in case of incorrect ASTs85 if list := f.Recv.List; len(list) == 1 {86 typ = list[0].Type87 }88 recv = recvString(typ)89 }90 mset[name] = &Func{91 Doc: f.Doc.Text(),92 Name: name,93 Decl: f,94 Recv: recv,95 Orig: recv,96 }97 if !preserveAST {98 f.Doc = nil // doc consumed - remove from AST99 }100}101102// add adds method m to the method set; m is ignored if the method set103// already contains a method with the same name at the same or a higher104// level than m.105func (mset methodSet) add(m *Func) {106 old := mset[m.Name]107 if old == nil || m.Level < old.Level {108 mset[m.Name] = m109 return110 }111 if m.Level == old.Level {112 // conflict - mark it using a method with nil Decl113 mset[m.Name] = &Func{114 Name: m.Name,115 Level: m.Level,116 }117 }118}119120// ----------------------------------------------------------------------------121// Named types122123// baseTypeName returns the name of the base type of x (or "")124// and whether the type is imported or not.125func baseTypeName(x ast.Expr) (name string, imported bool) {126 switch t := x.(type) {127 case *ast.Ident:128 return t.Name, false129 case *ast.IndexExpr:130 return baseTypeName(t.X)131 case *ast.IndexListExpr:132 return baseTypeName(t.X)133 case *ast.SelectorExpr:134 if _, ok := t.X.(*ast.Ident); ok {135 // only possible for qualified type names;136 // assume type is imported137 return t.Sel.Name, true138 }139 case *ast.ParenExpr:140 return baseTypeName(t.X)141 case *ast.StarExpr:142 return baseTypeName(t.X)143 }144 return "", false145}146147// An embeddedSet describes a set of embedded types.148type embeddedSet map[*namedType]bool149150// A namedType represents a named unqualified (package local, or possibly151// predeclared) type. The namedType for a type name is always found via152// reader.lookupType.153type namedType struct {154 doc string // doc comment for type155 name string // type name156 decl *ast.GenDecl // nil if declaration hasn't been seen yet157158 isEmbedded bool // true if this type is embedded159 isStruct bool // true if this type is a struct160 embedded embeddedSet // true if the embedded type is a pointer161162 // associated declarations163 values []*Value // consts and vars164 funcs methodSet165 methods methodSet166}167168// ----------------------------------------------------------------------------169// AST reader170171// reader accumulates documentation for a single package.172// It modifies the AST: Comments (declaration documentation)173// that have been collected by the reader are set to nil174// in the respective AST nodes so that they are not printed175// twice (once when printing the documentation and once when176// printing the corresponding AST node).177type reader struct {178 mode Mode179180 // package properties181 doc string // package documentation, if any182 filenames []string183 notes map[string][]*Note184185 // imports186 imports map[string]int187 hasDotImp bool // if set, package contains a dot import188 importByName map[string]string189190 // declarations191 values []*Value // consts and vars192 order int // sort order of const and var declarations (when we can't use a name)193 types map[string]*namedType194 funcs methodSet195196 // support for package-local shadowing of predeclared types197 shadowedPredecl map[string]bool198 fixmap map[string][]*ast.InterfaceType199}200201func (r *reader) isVisible(name string) bool {202 return r.mode&AllDecls != 0 || token.IsExported(name)203}204205// lookupType returns the base type with the given name.206// If the base type has not been encountered yet, a new207// type with the given name but no associated declaration208// is added to the type map.209func (r *reader) lookupType(name string) *namedType {210 if name == "" || name == "_" {211 return nil // no type docs for anonymous types212 }213 if typ, found := r.types[name]; found {214 return typ215 }216 // type not found - add one without declaration217 typ := &namedType{218 name: name,219 embedded: make(embeddedSet),220 funcs: make(methodSet),221 methods: make(methodSet),222 }223 r.types[name] = typ224 return typ225}226227// recordAnonymousField registers fieldType as the type of an228// anonymous field in the parent type. If the field is imported229// (qualified name) or the parent is nil, the field is ignored.230// The function returns the field name.231func (r *reader) recordAnonymousField(parent *namedType, fieldType ast.Expr) (fname string) {232 fname, imp := baseTypeName(fieldType)233 if parent == nil || imp {234 return235 }236 if ftype := r.lookupType(fname); ftype != nil {237 ftype.isEmbedded = true238 _, ptr := fieldType.(*ast.StarExpr)239 parent.embedded[ftype] = ptr240 }241 return242}243244func (r *reader) readDoc(comment *ast.CommentGroup) {245 // By convention there should be only one package comment246 // but collect all of them if there are more than one.247 text := comment.Text()248 if r.doc == "" {249 r.doc = text250 return251 }252 r.doc += "\n" + text253}254255func (r *reader) remember(predecl string, typ *ast.InterfaceType) {256 if r.fixmap == nil {257 r.fixmap = make(map[string][]*ast.InterfaceType)258 }259 r.fixmap[predecl] = append(r.fixmap[predecl], typ)260}261262func specNames(specs []ast.Spec) []string {263 names := make([]string, 0, len(specs)) // reasonable estimate264 for _, s := range specs {265 // s guaranteed to be an *ast.ValueSpec by readValue266 for _, ident := range s.(*ast.ValueSpec).Names {267 names = append(names, ident.Name)268 }269 }270 return names271}272273// readValue processes a const or var declaration.274func (r *reader) readValue(decl *ast.GenDecl) {275 // determine if decl should be associated with a type276 // Heuristic: For each typed entry, determine the type name, if any.277 // If there is exactly one type name that is sufficiently278 // frequent, associate the decl with the respective type.279 domName := ""280 domFreq := 0281 prev := ""282 n := 0283 for _, spec := range decl.Specs {284 s, ok := spec.(*ast.ValueSpec)285 if !ok {286 continue // should not happen, but be conservative287 }288 name := ""289 switch {290 case s.Type != nil:291 // a type is present; determine its name292 if n, imp := baseTypeName(s.Type); !imp {293 name = n294 }295 case decl.Tok == token.CONST && len(s.Values) == 0:296 // no type or value is present but we have a constant declaration;297 // use the previous type name (possibly the empty string)298 name = prev299 }300 if name != "" {301 // entry has a named type302 if domName != "" && domName != name {303 // more than one type name - do not associate304 // with any type305 domName = ""306 break307 }308 domName = name309 domFreq++310 }311 prev = name312 n++313 }314315 // nothing to do w/o a legal declaration316 if n == 0 {317 return318 }319320 // determine values list with which to associate the Value for this decl321 values := &r.values322 const threshold = 0.75323 if domName != "" && r.isVisible(domName) && domFreq >= int(float64(len(decl.Specs))*threshold) {324 // typed entries are sufficiently frequent325 if typ := r.lookupType(domName); typ != nil {326 values = &typ.values // associate with that type327 }328 }329330 *values = append(*values, &Value{331 Doc: decl.Doc.Text(),332 Names: specNames(decl.Specs),333 Decl: decl,334 order: r.order,335 })336 if r.mode&PreserveAST == 0 {337 decl.Doc = nil // doc consumed - remove from AST338 }339 // Note: It's important that the order used here is global because the cleanupTypes340 // methods may move values associated with types back into the global list. If the341 // order is list-specific, sorting is not deterministic because the same order value342 // may appear multiple times (was bug, found when fixing #16153).343 r.order++344}345346// fields returns a struct's fields or an interface's methods.347func fields(typ ast.Expr) (list []*ast.Field, isStruct bool) {348 var fields *ast.FieldList349 switch t := typ.(type) {350 case *ast.StructType:351 fields = t.Fields352 isStruct = true353 case *ast.InterfaceType:354 fields = t.Methods355 }356 if fields != nil {357 list = fields.List358 }359 return360}361362// readType processes a type declaration.363func (r *reader) readType(decl *ast.GenDecl, spec *ast.TypeSpec) {364 typ := r.lookupType(spec.Name.Name)365 if typ == nil {366 return // no name or blank name - ignore the type367 }368369 // A type should be added at most once, so typ.decl370 // should be nil - if it is not, simply overwrite it.371 typ.decl = decl372373 // compute documentation374 doc := spec.Doc375 if doc == nil {376 // no doc associated with the spec, use the declaration doc, if any377 doc = decl.Doc378 }379 if r.mode&PreserveAST == 0 {380 spec.Doc = nil // doc consumed - remove from AST381 decl.Doc = nil // doc consumed - remove from AST382 }383 typ.doc = doc.Text()384385 // record anonymous fields (they may contribute methods)386 // (some fields may have been recorded already when filtering387 // exports, but that's ok)388 var list []*ast.Field389 list, typ.isStruct = fields(spec.Type)390 for _, field := range list {391 if len(field.Names) == 0 {392 r.recordAnonymousField(typ, field.Type)393 }394 }395}396397// isPredeclared reports whether n denotes a predeclared type.398func (r *reader) isPredeclared(n string) bool {399 return predeclaredTypes[n] && r.types[n] == nil400}401402// readFunc processes a func or method declaration.403func (r *reader) readFunc(fun *ast.FuncDecl) {404 // strip function body if requested.405 if r.mode&PreserveAST == 0 {406 fun.Body = nil407 }408409 // associate methods with the receiver type, if any410 if fun.Recv != nil {411 // method412 if len(fun.Recv.List) == 0 {413 // should not happen (incorrect AST); (See issue 17788)414 // don't show this method415 return416 }417 recvTypeName, imp := baseTypeName(fun.Recv.List[0].Type)418 if imp {419 // should not happen (incorrect AST);420 // don't show this method421 return422 }423 if typ := r.lookupType(recvTypeName); typ != nil {424 typ.methods.set(fun, r.mode&PreserveAST != 0)425 }426 // otherwise ignore the method427 // TODO(gri): There may be exported methods of non-exported types428 // that can be called because of exported values (consts, vars, or429 // function results) of that type. Could determine if that is the430 // case and then show those methods in an appropriate section.431 return432 }433434 // Associate factory functions with the first visible result type, as long as435 // others are predeclared types.436 if fun.Type.Results.NumFields() >= 1 {437 var typ *namedType // type to associate the function with438 numResultTypes := 0439 for _, res := range fun.Type.Results.List {440 factoryType := res.Type441 if t, ok := factoryType.(*ast.ArrayType); ok {442 // We consider functions that return slices or arrays of type443 // T (or pointers to T) as factory functions of T.444 factoryType = t.Elt445 }446 if n, imp := baseTypeName(factoryType); !imp && r.isVisible(n) && !r.isPredeclared(n) {447 if lookupTypeParam(n, fun.Type.TypeParams) != nil {448 // Issue #49477: don't associate fun with its type parameter result.449 // A type parameter is not a defined type.450 continue451 }452 if t := r.lookupType(n); t != nil {453 typ = t454 numResultTypes++455 if numResultTypes > 1 {456 break457 }458 }459 }460 }461 // If there is exactly one result type,462 // associate the function with that type.463 if numResultTypes == 1 {464 typ.funcs.set(fun, r.mode&PreserveAST != 0)465 return466 }467 }468469 // just an ordinary function470 r.funcs.set(fun, r.mode&PreserveAST != 0)471}472473// lookupTypeParam searches for type parameters named name within the tparams474// field list, returning the relevant identifier if found, or nil if not.475func lookupTypeParam(name string, tparams *ast.FieldList) *ast.Ident {476 if tparams == nil {477 return nil478 }479 for _, field := range tparams.List {480 for _, id := range field.Names {481 if id.Name == name {482 return id483 }484 }485 }486 return nil487}488489var (490 noteMarker = `([A-Z][A-Z]+)\(([^)]+)\):?` // MARKER(uid), MARKER at least 2 chars, uid at least 1 char491 noteMarkerRx = lazyregexp.New(`^[ \t]*` + noteMarker) // MARKER(uid) at text start492 noteCommentRx = lazyregexp.New(`^/[/*][ \t]*` + noteMarker) // MARKER(uid) at comment start493)494495// clean replaces each sequence of space, \r, or \t characters496// with a single space and removes any trailing and leading spaces.497func clean(s string) string {498 var b []byte499 p := byte(' ')500 for i := 0; i < len(s); i++ {501 q := s[i]502 if q == '\r' || q == '\t' {503 q = ' '504 }505 if q != ' ' || p != ' ' {506 b = append(b, q)507 p = q508 }509 }510 // remove trailing blank, if any511 if n := len(b); n > 0 && p == ' ' {512 b = b[0 : n-1]513 }514 return string(b)515}516517// readNote collects a single note from a sequence of comments.518func (r *reader) readNote(list []*ast.Comment) {519 text := (&ast.CommentGroup{List: list}).Text()520 if m := noteMarkerRx.FindStringSubmatchIndex(text); m != nil {521 // The note body starts after the marker.522 // We remove any formatting so that we don't523 // get spurious line breaks/indentation when524 // showing the TODO body.525 body := clean(text[m[1]:])526 if body != "" {527 marker := text[m[2]:m[3]]528 r.notes[marker] = append(r.notes[marker], &Note{529 Pos: list[0].Pos(),530 End: list[len(list)-1].End(),531 UID: text[m[4]:m[5]],532 Body: body,533 })534 }535 }536}537538// readNotes extracts notes from comments.539// A note must start at the beginning of a comment with "MARKER(uid):"540// and is followed by the note body (e.g., "// BUG(gri): fix this").541// The note ends at the end of the comment group or at the start of542// another note in the same comment group, whichever comes first.543func (r *reader) readNotes(comments []*ast.CommentGroup) {544 for _, group := range comments {545 i := -1 // comment index of most recent note start, valid if >= 0546 list := group.List547 for j, c := range list {548 if noteCommentRx.MatchString(c.Text) {549 if i >= 0 {550 r.readNote(list[i:j])551 }552 i = j553 }554 }555 if i >= 0 {556 r.readNote(list[i:])557 }558 }559}560561// readFile adds the AST for a source file to the reader.562func (r *reader) readFile(src *ast.File) {563 // add package documentation564 if src.Doc != nil {565 r.readDoc(src.Doc)566 if r.mode&PreserveAST == 0 {567 src.Doc = nil // doc consumed - remove from AST568 }569 }570571 // add all declarations but for functions which are processed in a separate pass572 for _, decl := range src.Decls {573 switch d := decl.(type) {574 case *ast.GenDecl:575 switch d.Tok {576 case token.IMPORT:577 // imports are handled individually578 for _, spec := range d.Specs {579 if s, ok := spec.(*ast.ImportSpec); ok {580 if import_, err := strconv.Unquote(s.Path.Value); err == nil {581 r.imports[import_] = 1582 var name string583 if s.Name != nil {584 name = s.Name.Name585 if name == "." {586 r.hasDotImp = true587 }588 }589 if name != "." {590 if name == "" {591 name = assumedPackageName(import_)592 }593 old, ok := r.importByName[name]594 if !ok {595 r.importByName[name] = import_596 } else if old != import_ && old != "" {597 r.importByName[name] = "" // ambiguous598 }599 }600 }601 }602 }603 case token.CONST, token.VAR:604 // constants and variables are always handled as a group605 r.readValue(d)606 case token.TYPE:607 // types are handled individually608 if len(d.Specs) == 1 && !d.Lparen.IsValid() {609 // common case: single declaration w/o parentheses610 // (if a single declaration is parenthesized,611 // create a new fake declaration below, so that612 // go/doc type declarations always appear w/o613 // parentheses)614 if s, ok := d.Specs[0].(*ast.TypeSpec); ok {615 r.readType(d, s)616 }617 break618 }619 for _, spec := range d.Specs {620 if s, ok := spec.(*ast.TypeSpec); ok {621 // use an individual (possibly fake) declaration622 // for each type; this also ensures that each type623 // gets to (re-)use the declaration documentation624 // if there's none associated with the spec itself625 fake := &ast.GenDecl{626 Doc: d.Doc,627 // don't use the existing TokPos because it628 // will lead to the wrong selection range for629 // the fake declaration if there are more630 // than one type in the group (this affects631 // src/cmd/godoc/godoc.go's posLink_urlFunc)632 TokPos: s.Pos(),633 Tok: token.TYPE,634 Specs: []ast.Spec{s},635 }636 r.readType(fake, s)637 }638 }639 }640 }641 }642643 // collect MARKER(...): annotations644 r.readNotes(src.Comments)645 if r.mode&PreserveAST == 0 {646 src.Comments = nil // consumed unassociated comments - remove from AST647 }648}649650func (r *reader) readPackage(pkg *ast.Package, mode Mode) {651 // initialize reader652 r.filenames = make([]string, len(pkg.Files))653 r.imports = make(map[string]int)654 r.mode = mode655 r.types = make(map[string]*namedType)656 r.funcs = make(methodSet)657 r.notes = make(map[string][]*Note)658 r.importByName = make(map[string]string)659660 // sort package files before reading them so that the661 // result does not depend on map iteration order662 i := 0663 for filename := range pkg.Files {664 r.filenames[i] = filename665 i++666 }667 slices.Sort(r.filenames)668669 // process files in sorted order670 for _, filename := range r.filenames {671 f := pkg.Files[filename]672 if mode&AllDecls == 0 {673 r.fileExports(f)674 }675 r.readFile(f)676 }677678 for name, path := range r.importByName {679 if path == "" {680 delete(r.importByName, name)681 }682 }683684 // process functions now that we have better type information685 for _, f := range pkg.Files {686 for _, decl := range f.Decls {687 if d, ok := decl.(*ast.FuncDecl); ok {688 r.readFunc(d)689 }690 }691 }692}693694// ----------------------------------------------------------------------------695// Types696697func customizeRecv(f *Func, recvTypeName string, embeddedIsPtr bool, level int) *Func {698 if f == nil || f.Decl == nil || f.Decl.Recv == nil || len(f.Decl.Recv.List) != 1 {699 return f // shouldn't happen, but be safe700 }701702 // copy existing receiver field and set new type703 newField := *f.Decl.Recv.List[0]704 origPos := newField.Type.Pos()705 _, origRecvIsPtr := newField.Type.(*ast.StarExpr)706 newIdent := &ast.Ident{NamePos: origPos, Name: recvTypeName}707 var typ ast.Expr = newIdent708 if !embeddedIsPtr && origRecvIsPtr {709 newIdent.NamePos++ // '*' is one character710 typ = &ast.StarExpr{Star: origPos, X: newIdent}711 }712 newField.Type = typ713714 // copy existing receiver field list and set new receiver field715 newFieldList := *f.Decl.Recv716 newFieldList.List = []*ast.Field{&newField}717718 // copy existing function declaration and set new receiver field list719 newFuncDecl := *f.Decl720 newFuncDecl.Recv = &newFieldList721722 // copy existing function documentation and set new declaration723 newF := *f724 newF.Decl = &newFuncDecl725 newF.Recv = recvString(typ)726 // the Orig field never changes727 newF.Level = level728729 return &newF730}731732// collectEmbeddedMethods collects the embedded methods of typ in mset.733func (r *reader) collectEmbeddedMethods(mset methodSet, typ *namedType, recvTypeName string, embeddedIsPtr bool, level int, visited embeddedSet) {734 visited[typ] = true735 for embedded, isPtr := range typ.embedded {736 // Once an embedded type is embedded as a pointer type737 // all embedded types in those types are treated like738 // pointer types for the purpose of the receiver type739 // computation; i.e., embeddedIsPtr is sticky for this740 // embedding hierarchy.741 thisEmbeddedIsPtr := embeddedIsPtr || isPtr742 for _, m := range embedded.methods {743 // only top-level methods are embedded744 if m.Level == 0 {745 mset.add(customizeRecv(m, recvTypeName, thisEmbeddedIsPtr, level))746 }747 }748 if !visited[embedded] {749 r.collectEmbeddedMethods(mset, embedded, recvTypeName, thisEmbeddedIsPtr, level+1, visited)750 }751 }752 delete(visited, typ)753}754755// computeMethodSets determines the actual method sets for each type encountered.756func (r *reader) computeMethodSets() {757 for _, t := range r.types {758 // collect embedded methods for t759 if t.isStruct {760 // struct761 r.collectEmbeddedMethods(t.methods, t, t.name, false, 1, make(embeddedSet))762 } else {763 // interface764 // TODO(gri) fix this765 }766 }767768 // For any predeclared names that are declared locally, don't treat them as769 // exported fields anymore.770 for predecl := range r.shadowedPredecl {771 for _, ityp := range r.fixmap[predecl] {772 removeAnonymousField(predecl, ityp)773 }774 }775}776777// cleanupTypes removes the association of functions and methods with778// types that have no declaration. Instead, these functions and methods779// are shown at the package level. It also removes types with missing780// declarations or which are not visible.781func (r *reader) cleanupTypes() {782 for _, t := range r.types {783 visible := r.isVisible(t.name)784 predeclared := predeclaredTypes[t.name]785786 if t.decl == nil && (predeclared || visible && (t.isEmbedded || r.hasDotImp)) {787 // t.name is a predeclared type (and was not redeclared in this package),788 // or it was embedded somewhere but its declaration is missing (because789 // the AST is incomplete), or we have a dot-import (and all bets are off):790 // move any associated values, funcs, and methods back to the top-level so791 // that they are not lost.792 // 1) move values793 r.values = append(r.values, t.values...)794 // 2) move factory functions795 for name, f := range t.funcs {796 // in a correct AST, package-level function names797 // are all different - no need to check for conflicts798 r.funcs[name] = f799 }800 // 3) move methods801 if !predeclared {802 for name, m := range t.methods {803 // don't overwrite functions with the same name - drop them804 if _, found := r.funcs[name]; !found {805 r.funcs[name] = m806 }807 }808 }809 }810 // remove types w/o declaration or which are not visible811 if t.decl == nil || !visible {812 delete(r.types, t.name)813 }814 }815}816817// ----------------------------------------------------------------------------818// Sorting819820func sortedKeys(m map[string]int) []string {821 list := make([]string, len(m))822 i := 0823 for key := range m {824 list[i] = key825 i++826 }827 slices.Sort(list)828 return list829}830831// sortingName returns the name to use when sorting d into place.832func sortingName(d *ast.GenDecl) string {833 if len(d.Specs) == 1 {834 if s, ok := d.Specs[0].(*ast.ValueSpec); ok {835 return s.Names[0].Name836 }837 }838 return ""839}840841func sortedValues(m []*Value, tok token.Token) []*Value {842 list := make([]*Value, len(m)) // big enough in any case843 i := 0844 for _, val := range m {845 if val.Decl.Tok == tok {846 list[i] = val847 i++848 }849 }850 list = list[0:i]851852 slices.SortFunc(list, func(a, b *Value) int {853 r := strings.Compare(sortingName(a.Decl), sortingName(b.Decl))854 if r != 0 {855 return r856 }857 return cmp.Compare(a.order, b.order)858 })859860 return list861}862863func sortedTypes(m map[string]*namedType, allMethods bool) []*Type {864 list := make([]*Type, len(m))865 i := 0866 for _, t := range m {867 list[i] = &Type{868 Doc: t.doc,869 Name: t.name,870 Decl: t.decl,871 Consts: sortedValues(t.values, token.CONST),872 Vars: sortedValues(t.values, token.VAR),873 Funcs: sortedFuncs(t.funcs, true),874 Methods: sortedFuncs(t.methods, allMethods),875 }876 i++877 }878879 slices.SortFunc(list, func(a, b *Type) int {880 return strings.Compare(a.Name, b.Name)881 })882883 return list884}885886func removeStar(s string) string {887 if len(s) > 0 && s[0] == '*' {888 return s[1:]889 }890 return s891}892893func sortedFuncs(m methodSet, allMethods bool) []*Func {894 list := make([]*Func, len(m))895 i := 0896 for _, m := range m {897 // determine which methods to include898 switch {899 case m.Decl == nil:900 // exclude conflict entry901 case allMethods, m.Level == 0, !token.IsExported(removeStar(m.Orig)):902 // forced inclusion, method not embedded, or method903 // embedded but original receiver type not exported904 list[i] = m905 i++906 }907 }908 list = list[0:i]909 slices.SortFunc(list, func(a, b *Func) int {910 return strings.Compare(a.Name, b.Name)911 })912 return list913}914915// noteBodies returns a list of note body strings given a list of notes.916// This is only used to populate the deprecated Package.Bugs field.917func noteBodies(notes []*Note) []string {918 var list []string919 for _, n := range notes {920 list = append(list, n.Body)921 }922 return list923}924925// ----------------------------------------------------------------------------926// Predeclared identifiers927928// IsPredeclared reports whether s is a predeclared identifier.929func IsPredeclared(s string) bool {930 return predeclaredTypes[s] || predeclaredFuncs[s] || predeclaredConstants[s]931}932933var predeclaredTypes = map[string]bool{934 "any": true,935 "bool": true,936 "byte": true,937 "comparable": true,938 "complex64": true,939 "complex128": true,940 "error": true,941 "float32": true,942 "float64": true,943 "int": true,944 "int8": true,945 "int16": true,946 "int32": true,947 "int64": true,948 "rune": true,949 "string": true,950 "uint": true,951 "uint8": true,952 "uint16": true,953 "uint32": true,954 "uint64": true,955 "uintptr": true,956}957958var predeclaredFuncs = map[string]bool{959 "append": true,960 "cap": true,961 "clear": true,962 "close": true,963 "complex": true,964 "copy": true,965 "delete": true,966 "imag": true,967 "len": true,968 "make": true,969 "max": true,970 "min": true,971 "new": true,972 "panic": true,973 "print": true,974 "println": true,975 "real": true,976 "recover": true,977}978979var predeclaredConstants = map[string]bool{980 "false": true,981 "iota": true,982 "nil": true,983 "true": true,984}985986// assumedPackageName returns the assumed package name987// for a given import path. This is a copy of988// golang.org/x/tools/internal/imports.ImportPathToAssumedName.989func assumedPackageName(importPath string) string {990 notIdentifier := func(ch rune) bool {991 return !('a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' ||992 '0' <= ch && ch <= '9' ||993 ch == '_' ||994 ch >= utf8.RuneSelf && (unicode.IsLetter(ch) || unicode.IsDigit(ch)))995 }996997 base := path.Base(importPath)998 if strings.HasPrefix(base, "v") {999 if _, err := strconv.Atoi(base[1:]); err == nil {1000 dir := path.Dir(importPath)1001 if dir != "." {1002 base = path.Base(dir)1003 }1004 }1005 }1006 base = strings.TrimPrefix(base, "go-")1007 if i := strings.IndexFunc(base, notIdentifier); i >= 0 {1008 base = base[:i]1009 }1010 return base1011}
Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.