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