1// Copyright 2009 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package doc 6 7import ( 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) 20 21// ---------------------------------------------------------------------------- 22// function/method sets 23// 24// Internally, we treat functions like methods and collect them in method sets. 25 26// A methodSet describes a set of methods. Entries where Decl == nil are conflict 27// entries (more than one method with the same name at the same embedding level). 28type methodSet map[string]*Func 29 30// 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.Name 36 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.Builder 45 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} 58 59func recvParam(p ast.Expr) string { 60 if id, ok := p.(*ast.Ident); ok { 61 return id.Name 62 } 63 return "BADPARAM" 64} 65 66// 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 first 68// one with documentation; conflicts are ignored. The boolean 69// specifies whether to leave the AST untouched. 70func (mset methodSet) set(f *ast.FuncDecl, preserveAST bool) { 71 name := f.Name.Name 72 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 another 75 // implementation and ignore it. This does not happen if the 76 // caller is using go/build.ScanDir to determine the list of 77 // files implementing a package. 78 return 79 } 80 // function doesn't exist or has no documentation; use f 81 recv := "" 82 if f.Recv != nil { 83 var typ ast.Expr 84 // be careful in case of incorrect ASTs 85 if list := f.Recv.List; len(list) == 1 { 86 typ = list[0].Type 87 } 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 AST 99 } 100} 101 102// add adds method m to the method set; m is ignored if the method set 103// already contains a method with the same name at the same or a higher 104// 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] = m 109 return 110 } 111 if m.Level == old.Level { 112 // conflict - mark it using a method with nil Decl 113 mset[m.Name] = &Func{ 114 Name: m.Name, 115 Level: m.Level, 116 } 117 } 118} 119 120// ---------------------------------------------------------------------------- 121// Named types 122 123// 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, false 129 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 imported 137 return t.Sel.Name, true 138 } 139 case *ast.ParenExpr: 140 return baseTypeName(t.X) 141 case *ast.StarExpr: 142 return baseTypeName(t.X) 143 } 144 return "", false 145} 146 147// An embeddedSet describes a set of embedded types. 148type embeddedSet map[*namedType]bool 149 150// A namedType represents a named unqualified (package local, or possibly 151// predeclared) type. The namedType for a type name is always found via 152// reader.lookupType. 153type namedType struct { 154 doc string // doc comment for type 155 name string // type name 156 decl *ast.GenDecl // nil if declaration hasn't been seen yet 157 158 isEmbedded bool // true if this type is embedded 159 isStruct bool // true if this type is a struct 160 embedded embeddedSet // true if the embedded type is a pointer 161 162 // associated declarations 163 values []*Value // consts and vars 164 funcs methodSet 165 methods methodSet 166} 167 168// ---------------------------------------------------------------------------- 169// AST reader 170 171// 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 nil 174// in the respective AST nodes so that they are not printed 175// twice (once when printing the documentation and once when 176// printing the corresponding AST node). 177type reader struct { 178 mode Mode 179 180 // package properties 181 doc string // package documentation, if any 182 filenames []string 183 notes map[string][]*Note 184 185 // imports 186 imports map[string]int 187 hasDotImp bool // if set, package contains a dot import 188 importByName map[string]string 189 190 // declarations 191 values []*Value // consts and vars 192 order int // sort order of const and var declarations (when we can't use a name) 193 types map[string]*namedType 194 funcs methodSet 195 196 // support for package-local shadowing of predeclared types 197 shadowedPredecl map[string]bool 198 fixmap map[string][]*ast.InterfaceType 199} 200 201func (r *reader) isVisible(name string) bool { 202 return r.mode&AllDecls != 0 || token.IsExported(name) 203} 204 205// lookupType returns the base type with the given name. 206// If the base type has not been encountered yet, a new 207// type with the given name but no associated declaration 208// 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 types 212 } 213 if typ, found := r.types[name]; found { 214 return typ 215 } 216 // type not found - add one without declaration 217 typ := &namedType{ 218 name: name, 219 embedded: make(embeddedSet), 220 funcs: make(methodSet), 221 methods: make(methodSet), 222 } 223 r.types[name] = typ 224 return typ 225} 226 227// recordAnonymousField registers fieldType as the type of an 228// anonymous field in the parent type. If the field is imported 229// (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 return 235 } 236 if ftype := r.lookupType(fname); ftype != nil { 237 ftype.isEmbedded = true 238 _, ptr := fieldType.(*ast.StarExpr) 239 parent.embedded[ftype] = ptr 240 } 241 return 242} 243 244func (r *reader) readDoc(comment *ast.CommentGroup) { 245 // By convention there should be only one package comment 246 // but collect all of them if there are more than one. 247 text := comment.Text() 248 if r.doc == "" { 249 r.doc = text 250 return 251 } 252 r.doc += "\n" + text 253} 254 255func (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} 261 262func specNames(specs []ast.Spec) []string { 263 names := make([]string, 0, len(specs)) // reasonable estimate 264 for _, s := range specs { 265 // s guaranteed to be an *ast.ValueSpec by readValue 266 for _, ident := range s.(*ast.ValueSpec).Names { 267 names = append(names, ident.Name) 268 } 269 } 270 return names 271} 272 273// readValue processes a const or var declaration. 274func (r *reader) readValue(decl *ast.GenDecl) { 275 // determine if decl should be associated with a type 276 // Heuristic: For each typed entry, determine the type name, if any. 277 // If there is exactly one type name that is sufficiently 278 // frequent, associate the decl with the respective type. 279 domName := "" 280 domFreq := 0 281 prev := "" 282 n := 0 283 for _, spec := range decl.Specs { 284 s, ok := spec.(*ast.ValueSpec) 285 if !ok { 286 continue // should not happen, but be conservative 287 } 288 name := "" 289 switch { 290 case s.Type != nil: 291 // a type is present; determine its name 292 if n, imp := baseTypeName(s.Type); !imp { 293 name = n 294 } 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 = prev 299 } 300 if name != "" { 301 // entry has a named type 302 if domName != "" && domName != name { 303 // more than one type name - do not associate 304 // with any type 305 domName = "" 306 break 307 } 308 domName = name 309 domFreq++ 310 } 311 prev = name 312 n++ 313 } 314 315 // nothing to do w/o a legal declaration 316 if n == 0 { 317 return 318 } 319 320 // determine values list with which to associate the Value for this decl 321 values := &r.values 322 const threshold = 0.75 323 if domName != "" && r.isVisible(domName) && domFreq >= int(float64(len(decl.Specs))*threshold) { 324 // typed entries are sufficiently frequent 325 if typ := r.lookupType(domName); typ != nil { 326 values = &typ.values // associate with that type 327 } 328 } 329 330 *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 AST 338 } 339 // Note: It's important that the order used here is global because the cleanupTypes 340 // methods may move values associated with types back into the global list. If the 341 // order is list-specific, sorting is not deterministic because the same order value 342 // may appear multiple times (was bug, found when fixing #16153). 343 r.order++ 344} 345 346// 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.FieldList 349 switch t := typ.(type) { 350 case *ast.StructType: 351 fields = t.Fields 352 isStruct = true 353 case *ast.InterfaceType: 354 fields = t.Methods 355 } 356 if fields != nil { 357 list = fields.List 358 } 359 return 360} 361 362// 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 type 367 } 368 369 // A type should be added at most once, so typ.decl 370 // should be nil - if it is not, simply overwrite it. 371 typ.decl = decl 372 373 // compute documentation 374 doc := spec.Doc 375 if doc == nil { 376 // no doc associated with the spec, use the declaration doc, if any 377 doc = decl.Doc 378 } 379 if r.mode&PreserveAST == 0 { 380 spec.Doc = nil // doc consumed - remove from AST 381 decl.Doc = nil // doc consumed - remove from AST 382 } 383 typ.doc = doc.Text() 384 385 // record anonymous fields (they may contribute methods) 386 // (some fields may have been recorded already when filtering 387 // exports, but that's ok) 388 var list []*ast.Field 389 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} 396 397// isPredeclared reports whether n denotes a predeclared type. 398func (r *reader) isPredeclared(n string) bool { 399 return predeclaredTypes[n] && r.types[n] == nil 400} 401 402// 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 = nil 407 } 408 409 // associate methods with the receiver type, if any 410 if fun.Recv != nil { 411 // method 412 if len(fun.Recv.List) == 0 { 413 // should not happen (incorrect AST); (See issue 17788) 414 // don't show this method 415 return 416 } 417 recvTypeName, imp := baseTypeName(fun.Recv.List[0].Type) 418 if imp { 419 // should not happen (incorrect AST); 420 // don't show this method 421 return 422 } 423 if typ := r.lookupType(recvTypeName); typ != nil { 424 typ.methods.set(fun, r.mode&PreserveAST != 0) 425 } 426 // otherwise ignore the method 427 // TODO(gri): There may be exported methods of non-exported types 428 // that can be called because of exported values (consts, vars, or 429 // function results) of that type. Could determine if that is the 430 // case and then show those methods in an appropriate section. 431 return 432 } 433 434 // Associate factory functions with the first visible result type, as long as 435 // others are predeclared types. 436 if fun.Type.Results.NumFields() >= 1 { 437 var typ *namedType // type to associate the function with 438 numResultTypes := 0 439 for _, res := range fun.Type.Results.List { 440 factoryType := res.Type 441 if t, ok := factoryType.(*ast.ArrayType); ok { 442 // We consider functions that return slices or arrays of type 443 // T (or pointers to T) as factory functions of T. 444 factoryType = t.Elt 445 } 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 continue 451 } 452 if t := r.lookupType(n); t != nil { 453 typ = t 454 numResultTypes++ 455 if numResultTypes > 1 { 456 break 457 } 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 return 466 } 467 } 468 469 // just an ordinary function 470 r.funcs.set(fun, r.mode&PreserveAST != 0) 471} 472 473// lookupTypeParam searches for type parameters named name within the tparams 474// 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 nil 478 } 479 for _, field := range tparams.List { 480 for _, id := range field.Names { 481 if id.Name == name { 482 return id 483 } 484 } 485 } 486 return nil 487} 488 489var ( 490 noteMarker = `([A-Z][A-Z]+)\(([^)]+)\):?` // MARKER(uid), MARKER at least 2 chars, uid at least 1 char 491 noteMarkerRx = lazyregexp.New(`^[ \t]*` + noteMarker) // MARKER(uid) at text start 492 noteCommentRx = lazyregexp.New(`^/[/*][ \t]*` + noteMarker) // MARKER(uid) at comment start 493) 494 495// clean replaces each sequence of space, \r, or \t characters 496// with a single space and removes any trailing and leading spaces. 497func clean(s string) string { 498 var b []byte 499 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 = q 508 } 509 } 510 // remove trailing blank, if any 511 if n := len(b); n > 0 && p == ' ' { 512 b = b[0 : n-1] 513 } 514 return string(b) 515} 516 517// 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't 523 // get spurious line breaks/indentation when 524 // 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} 537 538// 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 of 542// 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 >= 0 546 list := group.List 547 for j, c := range list { 548 if noteCommentRx.MatchString(c.Text) { 549 if i >= 0 { 550 r.readNote(list[i:j]) 551 } 552 i = j 553 } 554 } 555 if i >= 0 { 556 r.readNote(list[i:]) 557 } 558 } 559} 560 561// readFile adds the AST for a source file to the reader. 562func (r *reader) readFile(src *ast.File) { 563 // add package documentation 564 if src.Doc != nil { 565 r.readDoc(src.Doc) 566 if r.mode&PreserveAST == 0 { 567 src.Doc = nil // doc consumed - remove from AST 568 } 569 } 570 571 // add all declarations but for functions which are processed in a separate pass 572 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 individually 578 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_] = 1 582 var name string 583 if s.Name != nil { 584 name = s.Name.Name 585 if name == "." { 586 r.hasDotImp = true 587 } 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] = "" // ambiguous 598 } 599 } 600 } 601 } 602 } 603 case token.CONST, token.VAR: 604 // constants and variables are always handled as a group 605 r.readValue(d) 606 case token.TYPE: 607 // types are handled individually 608 if len(d.Specs) == 1 && !d.Lparen.IsValid() { 609 // common case: single declaration w/o parentheses 610 // (if a single declaration is parenthesized, 611 // create a new fake declaration below, so that 612 // go/doc type declarations always appear w/o 613 // parentheses) 614 if s, ok := d.Specs[0].(*ast.TypeSpec); ok { 615 r.readType(d, s) 616 } 617 break 618 } 619 for _, spec := range d.Specs { 620 if s, ok := spec.(*ast.TypeSpec); ok { 621 // use an individual (possibly fake) declaration 622 // for each type; this also ensures that each type 623 // gets to (re-)use the declaration documentation 624 // if there's none associated with the spec itself 625 fake := &ast.GenDecl{ 626 Doc: d.Doc, 627 // don't use the existing TokPos because it 628 // will lead to the wrong selection range for 629 // the fake declaration if there are more 630 // than one type in the group (this affects 631 // 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 } 642 643 // collect MARKER(...): annotations 644 r.readNotes(src.Comments) 645 if r.mode&PreserveAST == 0 { 646 src.Comments = nil // consumed unassociated comments - remove from AST 647 } 648} 649 650func (r *reader) readPackage(pkg *ast.Package, mode Mode) { 651 // initialize reader 652 r.filenames = make([]string, len(pkg.Files)) 653 r.imports = make(map[string]int) 654 r.mode = mode 655 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) 659 660 // sort package files before reading them so that the 661 // result does not depend on map iteration order 662 i := 0 663 for filename := range pkg.Files { 664 r.filenames[i] = filename 665 i++ 666 } 667 slices.Sort(r.filenames) 668 669 // process files in sorted order 670 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 } 677 678 for name, path := range r.importByName { 679 if path == "" { 680 delete(r.importByName, name) 681 } 682 } 683 684 // process functions now that we have better type information 685 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} 693 694// ---------------------------------------------------------------------------- 695// Types 696 697func 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 safe 700 } 701 702 // copy existing receiver field and set new type 703 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 = newIdent 708 if !embeddedIsPtr && origRecvIsPtr { 709 newIdent.NamePos++ // '*' is one character 710 typ = &ast.StarExpr{Star: origPos, X: newIdent} 711 } 712 newField.Type = typ 713 714 // copy existing receiver field list and set new receiver field 715 newFieldList := *f.Decl.Recv 716 newFieldList.List = []*ast.Field{&newField} 717 718 // copy existing function declaration and set new receiver field list 719 newFuncDecl := *f.Decl 720 newFuncDecl.Recv = &newFieldList 721 722 // copy existing function documentation and set new declaration 723 newF := *f 724 newF.Decl = &newFuncDecl 725 newF.Recv = recvString(typ) 726 // the Orig field never changes 727 newF.Level = level 728 729 return &newF 730} 731 732// 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] = true 735 for embedded, isPtr := range typ.embedded { 736 // Once an embedded type is embedded as a pointer type 737 // all embedded types in those types are treated like 738 // pointer types for the purpose of the receiver type 739 // computation; i.e., embeddedIsPtr is sticky for this 740 // embedding hierarchy. 741 thisEmbeddedIsPtr := embeddedIsPtr || isPtr 742 for _, m := range embedded.methods { 743 // only top-level methods are embedded 744 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} 754 755// 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 t 759 if t.isStruct { 760 // struct 761 r.collectEmbeddedMethods(t.methods, t, t.name, false, 1, make(embeddedSet)) 762 } else { 763 // interface 764 // TODO(gri) fix this 765 } 766 } 767 768 // For any predeclared names that are declared locally, don't treat them as 769 // exported fields anymore. 770 for predecl := range r.shadowedPredecl { 771 for _, ityp := range r.fixmap[predecl] { 772 removeAnonymousField(predecl, ityp) 773 } 774 } 775} 776 777// cleanupTypes removes the association of functions and methods with 778// types that have no declaration. Instead, these functions and methods 779// are shown at the package level. It also removes types with missing 780// 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] 785 786 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 (because 789 // 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 so 791 // that they are not lost. 792 // 1) move values 793 r.values = append(r.values, t.values...) 794 // 2) move factory functions 795 for name, f := range t.funcs { 796 // in a correct AST, package-level function names 797 // are all different - no need to check for conflicts 798 r.funcs[name] = f 799 } 800 // 3) move methods 801 if !predeclared { 802 for name, m := range t.methods { 803 // don't overwrite functions with the same name - drop them 804 if _, found := r.funcs[name]; !found { 805 r.funcs[name] = m 806 } 807 } 808 } 809 } 810 // remove types w/o declaration or which are not visible 811 if t.decl == nil || !visible { 812 delete(r.types, t.name) 813 } 814 } 815} 816 817// ---------------------------------------------------------------------------- 818// Sorting 819 820func sortedKeys(m map[string]int) []string { 821 list := make([]string, len(m)) 822 i := 0 823 for key := range m { 824 list[i] = key 825 i++ 826 } 827 slices.Sort(list) 828 return list 829} 830 831// 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].Name 836 } 837 } 838 return "" 839} 840 841func sortedValues(m []*Value, tok token.Token) []*Value { 842 list := make([]*Value, len(m)) // big enough in any case 843 i := 0 844 for _, val := range m { 845 if val.Decl.Tok == tok { 846 list[i] = val 847 i++ 848 } 849 } 850 list = list[0:i] 851 852 slices.SortFunc(list, func(a, b *Value) int { 853 r := strings.Compare(sortingName(a.Decl), sortingName(b.Decl)) 854 if r != 0 { 855 return r 856 } 857 return cmp.Compare(a.order, b.order) 858 }) 859 860 return list 861} 862 863func sortedTypes(m map[string]*namedType, allMethods bool) []*Type { 864 list := make([]*Type, len(m)) 865 i := 0 866 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 } 878 879 slices.SortFunc(list, func(a, b *Type) int { 880 return strings.Compare(a.Name, b.Name) 881 }) 882 883 return list 884} 885 886func removeStar(s string) string { 887 if len(s) > 0 && s[0] == '*' { 888 return s[1:] 889 } 890 return s 891} 892 893func sortedFuncs(m methodSet, allMethods bool) []*Func { 894 list := make([]*Func, len(m)) 895 i := 0 896 for _, m := range m { 897 // determine which methods to include 898 switch { 899 case m.Decl == nil: 900 // exclude conflict entry 901 case allMethods, m.Level == 0, !token.IsExported(removeStar(m.Orig)): 902 // forced inclusion, method not embedded, or method 903 // embedded but original receiver type not exported 904 list[i] = m 905 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 list 913} 914 915// 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 []string 919 for _, n := range notes { 920 list = append(list, n.Body) 921 } 922 return list 923} 924 925// ---------------------------------------------------------------------------- 926// Predeclared identifiers 927 928// IsPredeclared reports whether s is a predeclared identifier. 929func IsPredeclared(s string) bool { 930 return predeclaredTypes[s] || predeclaredFuncs[s] || predeclaredConstants[s] 931} 932 933var 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} 957 958var 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} 978 979var predeclaredConstants = map[string]bool{ 980 "false": true, 981 "iota": true, 982 "nil": true, 983 "true": true, 984} 985 986// assumedPackageName returns the assumed package name 987// for a given import path. This is a copy of 988// 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 } 996 997 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 base 1011} 1012