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.
17 // ----------------------------------------------------------------------------
18 // function/method sets
20 // Internally, we treat functions like methods and collect them in method sets.
22 // A methodSet describes a set of methods. Entries where Decl == nil are conflict
23 // entries (more than one method with the same name at the same embedding level).
25 type methodSet
map[string]*Func
27 // recvString returns a string representation of recv of the form "T", "*T",
28 // "T[A, ...]", "*T[A, ...]" or "BADRECV" (if not a proper receiver type).
30 func recvString(recv ast
.Expr
) string {
31 switch t
:= recv
.(type) {
35 return "*" + recvString(t
.X
)
37 // Generic type with one parameter.
38 return fmt
.Sprintf("%s[%s]", recvString(t
.X
), recvParam(t
.Index
))
39 case *ast
.IndexListExpr
:
40 // Generic type with multiple parameters.
41 if len(t
.Indices
) > 0 {
43 b
.WriteString(recvString(t
.X
))
45 b
.WriteString(recvParam(t
.Indices
[0]))
46 for _
, e
:= range t
.Indices
[1:] {
48 b
.WriteString(recvParam(e
))
57 func recvParam(p ast
.Expr
) string {
58 if id
, ok
:= p
.(*ast
.Ident
); ok
{
64 // set creates the corresponding Func for f and adds it to mset.
65 // If there are multiple f's with the same name, set keeps the first
66 // one with documentation; conflicts are ignored. The boolean
67 // specifies whether to leave the AST untouched.
69 func (mset methodSet
) set(f
*ast
.FuncDecl
, preserveAST
bool) {
71 if g
:= mset
[name
]; g
!= nil && g
.Doc
!= "" {
72 // A function with the same name has already been registered;
73 // since it has documentation, assume f is simply another
74 // implementation and ignore it. This does not happen if the
75 // caller is using go/build.ScanDir to determine the list of
76 // files implementing a package.
79 // function doesn't exist or has no documentation; use f
83 // be careful in case of incorrect ASTs
84 if list
:= f
.Recv
.List
; len(list
) == 1 {
87 recv
= recvString(typ
)
97 f
.Doc
= nil // doc consumed - remove from AST
101 // add adds method m to the method set; m is ignored if the method set
102 // already contains a method with the same name at the same or a higher
105 func (mset methodSet
) add(m
*Func
) {
107 if old
== nil || m
.Level
< old
.Level
{
111 if m
.Level
== old
.Level
{
112 // conflict - mark it using a method with nil Decl
113 mset
[m
.Name
] = &Func
{
120 // ----------------------------------------------------------------------------
123 // baseTypeName returns the name of the base type of x (or "")
124 // and whether the type is imported or not.
126 func baseTypeName(x ast
.Expr
) (name
string, imported
bool) {
127 switch t
:= x
.(type) {
131 return baseTypeName(t
.X
)
132 case *ast
.IndexListExpr
:
133 return baseTypeName(t
.X
)
134 case *ast
.SelectorExpr
:
135 if _
, ok
:= t
.X
.(*ast
.Ident
); ok
{
136 // only possible for qualified type names;
137 // assume type is imported
138 return t
.Sel
.Name
, true
141 return baseTypeName(t
.X
)
143 return baseTypeName(t
.X
)
148 // An embeddedSet describes a set of embedded types.
149 type embeddedSet
map[*namedType
]bool
151 // A namedType represents a named unqualified (package local, or possibly
152 // predeclared) type. The namedType for a type name is always found via
153 // reader.lookupType.
155 type namedType
struct {
156 doc
string // doc comment for type
157 name
string // type name
158 decl
*ast
.GenDecl
// nil if declaration hasn't been seen yet
160 isEmbedded
bool // true if this type is embedded
161 isStruct
bool // true if this type is a struct
162 embedded embeddedSet
// true if the embedded type is a pointer
164 // associated declarations
165 values
[]*Value
// consts and vars
170 // ----------------------------------------------------------------------------
173 // reader accumulates documentation for a single package.
174 // It modifies the AST: Comments (declaration documentation)
175 // that have been collected by the reader are set to nil
176 // in the respective AST nodes so that they are not printed
177 // twice (once when printing the documentation and once when
178 // printing the corresponding AST node).
183 // package properties
184 doc
string // package documentation, if any
186 notes
map[string][]*Note
189 imports
map[string]int
190 hasDotImp
bool // if set, package contains a dot import
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
196 // support for package-local shadowing of predeclared types
197 shadowedPredecl
map[string]bool
198 fixmap
map[string][]*ast
.InterfaceType
201 func (r
*reader
) isVisible(name
string) bool {
202 return r
.mode
&AllDecls
!= 0 || token
.IsExported(name
)
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.
210 func (r
*reader
) lookupType(name
string) *namedType
{
211 if name
== "" || name
== "_" {
212 return nil // no type docs for anonymous types
214 if typ
, found
:= r
.types
[name
]; found
{
217 // type not found - add one without declaration
220 embedded
: make(embeddedSet
),
221 funcs
: make(methodSet
),
222 methods
: make(methodSet
),
228 // recordAnonymousField registers fieldType as the type of an
229 // anonymous field in the parent type. If the field is imported
230 // (qualified name) or the parent is nil, the field is ignored.
231 // The function returns the field name.
233 func (r
*reader
) recordAnonymousField(parent
*namedType
, fieldType ast
.Expr
) (fname
string) {
234 fname
, imp
:= baseTypeName(fieldType
)
235 if parent
== nil || imp
{
238 if ftype
:= r
.lookupType(fname
); ftype
!= nil {
239 ftype
.isEmbedded
= true
240 _
, ptr
:= fieldType
.(*ast
.StarExpr
)
241 parent
.embedded
[ftype
] = ptr
246 func (r
*reader
) readDoc(comment
*ast
.CommentGroup
) {
247 // By convention there should be only one package comment
248 // but collect all of them if there are more than one.
249 text
:= comment
.Text()
257 func (r
*reader
) remember(predecl
string, typ
*ast
.InterfaceType
) {
259 r
.fixmap
= make(map[string][]*ast
.InterfaceType
)
261 r
.fixmap
[predecl
] = append(r
.fixmap
[predecl
], typ
)
264 func specNames(specs
[]ast
.Spec
) []string {
265 names
:= make([]string, 0, len(specs
)) // reasonable estimate
266 for _
, s
:= range specs
{
267 // s guaranteed to be an *ast.ValueSpec by readValue
268 for _
, ident
:= range s
.(*ast
.ValueSpec
).Names
{
269 names
= append(names
, ident
.Name
)
275 // readValue processes a const or var declaration.
277 func (r
*reader
) readValue(decl
*ast
.GenDecl
) {
278 // determine if decl should be associated with a type
279 // Heuristic: For each typed entry, determine the type name, if any.
280 // If there is exactly one type name that is sufficiently
281 // frequent, associate the decl with the respective type.
286 for _
, spec
:= range decl
.Specs
{
287 s
, ok
:= spec
.(*ast
.ValueSpec
)
289 continue // should not happen, but be conservative
294 // a type is present; determine its name
295 if n
, imp
:= baseTypeName(s
.Type
); !imp
{
298 case decl
.Tok
== token
.CONST
&& len(s
.Values
) == 0:
299 // no type or value is present but we have a constant declaration;
300 // use the previous type name (possibly the empty string)
304 // entry has a named type
305 if domName
!= "" && domName
!= name
{
306 // more than one type name - do not associate
318 // nothing to do w/o a legal declaration
323 // determine values list with which to associate the Value for this decl
325 const threshold
= 0.75
326 if domName
!= "" && r
.isVisible(domName
) && domFreq
>= int(float64(len(decl
.Specs
))*threshold
) {
327 // typed entries are sufficiently frequent
328 if typ
:= r
.lookupType(domName
); typ
!= nil {
329 values
= &typ
.values
// associate with that type
333 *values
= append(*values
, &Value
{
334 Doc
: decl
.Doc
.Text(),
335 Names
: specNames(decl
.Specs
),
339 if r
.mode
&PreserveAST
== 0 {
340 decl
.Doc
= nil // doc consumed - remove from AST
342 // Note: It's important that the order used here is global because the cleanupTypes
343 // methods may move values associated with types back into the global list. If the
344 // order is list-specific, sorting is not deterministic because the same order value
345 // may appear multiple times (was bug, found when fixing #16153).
349 // fields returns a struct's fields or an interface's methods.
351 func fields(typ ast
.Expr
) (list
[]*ast
.Field
, isStruct
bool) {
352 var fields
*ast
.FieldList
353 switch t
:= typ
.(type) {
354 case *ast
.StructType
:
357 case *ast
.InterfaceType
:
366 // readType processes a type declaration.
368 func (r
*reader
) readType(decl
*ast
.GenDecl
, spec
*ast
.TypeSpec
) {
369 typ
:= r
.lookupType(spec
.Name
.Name
)
371 return // no name or blank name - ignore the type
374 // A type should be added at most once, so typ.decl
375 // should be nil - if it is not, simply overwrite it.
378 // compute documentation
381 // no doc associated with the spec, use the declaration doc, if any
384 if r
.mode
&PreserveAST
== 0 {
385 spec
.Doc
= nil // doc consumed - remove from AST
386 decl
.Doc
= nil // doc consumed - remove from AST
390 // record anonymous fields (they may contribute methods)
391 // (some fields may have been recorded already when filtering
392 // exports, but that's ok)
393 var list
[]*ast
.Field
394 list
, typ
.isStruct
= fields(spec
.Type
)
395 for _
, field
:= range list
{
396 if len(field
.Names
) == 0 {
397 r
.recordAnonymousField(typ
, field
.Type
)
402 // isPredeclared reports whether n denotes a predeclared type.
404 func (r
*reader
) isPredeclared(n
string) bool {
405 return predeclaredTypes
[n
] && r
.types
[n
] == nil
408 // readFunc processes a func or method declaration.
410 func (r
*reader
) readFunc(fun
*ast
.FuncDecl
) {
411 // strip function body if requested.
412 if r
.mode
&PreserveAST
== 0 {
416 // associate methods with the receiver type, if any
419 if len(fun
.Recv
.List
) == 0 {
420 // should not happen (incorrect AST); (See issue 17788)
421 // don't show this method
424 recvTypeName
, imp
:= baseTypeName(fun
.Recv
.List
[0].Type
)
426 // should not happen (incorrect AST);
427 // don't show this method
430 if typ
:= r
.lookupType(recvTypeName
); typ
!= nil {
431 typ
.methods
.set(fun
, r
.mode
&PreserveAST
!= 0)
433 // otherwise ignore the method
434 // TODO(gri): There may be exported methods of non-exported types
435 // that can be called because of exported values (consts, vars, or
436 // function results) of that type. Could determine if that is the
437 // case and then show those methods in an appropriate section.
441 // Associate factory functions with the first visible result type, as long as
442 // others are predeclared types.
443 if fun
.Type
.Results
.NumFields() >= 1 {
444 var typ
*namedType
// type to associate the function with
446 for _
, res
:= range fun
.Type
.Results
.List
{
447 factoryType
:= res
.Type
448 if t
, ok
:= factoryType
.(*ast
.ArrayType
); ok
{
449 // We consider functions that return slices or arrays of type
450 // T (or pointers to T) as factory functions of T.
453 if n
, imp
:= baseTypeName(factoryType
); !imp
&& r
.isVisible(n
) && !r
.isPredeclared(n
) {
454 if lookupTypeParam(n
, fun
.Type
.TypeParams
) != nil {
455 // Issue #49477: don't associate fun with its type parameter result.
456 // A type parameter is not a defined type.
459 if t
:= r
.lookupType(n
); t
!= nil {
462 if numResultTypes
> 1 {
468 // If there is exactly one result type,
469 // associate the function with that type.
470 if numResultTypes
== 1 {
471 typ
.funcs
.set(fun
, r
.mode
&PreserveAST
!= 0)
476 // just an ordinary function
477 r
.funcs
.set(fun
, r
.mode
&PreserveAST
!= 0)
480 // lookupTypeParam searches for type parameters named name within the tparams
481 // field list, returning the relevant identifier if found, or nil if not.
482 func lookupTypeParam(name
string, tparams
*ast
.FieldList
) *ast
.Ident
{
486 for _
, field
:= range tparams
.List
{
487 for _
, id
:= range field
.Names
{
497 noteMarker
= `([A-Z][A-Z]+)\(([^)]+)\):?` // MARKER(uid), MARKER at least 2 chars, uid at least 1 char
498 noteMarkerRx
= lazyregexp
.New(`^[ \t]*` + noteMarker
) // MARKER(uid) at text start
499 noteCommentRx
= lazyregexp
.New(`^/[/*][ \t]*` + noteMarker
) // MARKER(uid) at comment start
502 // readNote collects a single note from a sequence of comments.
504 func (r
*reader
) readNote(list
[]*ast
.Comment
) {
505 text
:= (&ast
.CommentGroup
{List
: list
}).Text()
506 if m
:= noteMarkerRx
.FindStringSubmatchIndex(text
); m
!= nil {
507 // The note body starts after the marker.
508 // We remove any formatting so that we don't
509 // get spurious line breaks/indentation when
510 // showing the TODO body.
511 body
:= clean(text
[m
[1]:], keepNL
)
513 marker
:= text
[m
[2]:m
[3]]
514 r
.notes
[marker
] = append(r
.notes
[marker
], &Note
{
516 End
: list
[len(list
)-1].End(),
517 UID
: text
[m
[4]:m
[5]],
524 // readNotes extracts notes from comments.
525 // A note must start at the beginning of a comment with "MARKER(uid):"
526 // and is followed by the note body (e.g., "// BUG(gri): fix this").
527 // The note ends at the end of the comment group or at the start of
528 // another note in the same comment group, whichever comes first.
530 func (r
*reader
) readNotes(comments
[]*ast
.CommentGroup
) {
531 for _
, group
:= range comments
{
532 i
:= -1 // comment index of most recent note start, valid if >= 0
534 for j
, c
:= range list
{
535 if noteCommentRx
.MatchString(c
.Text
) {
537 r
.readNote(list
[i
:j
])
548 // readFile adds the AST for a source file to the reader.
550 func (r
*reader
) readFile(src
*ast
.File
) {
551 // add package documentation
554 if r
.mode
&PreserveAST
== 0 {
555 src
.Doc
= nil // doc consumed - remove from AST
559 // add all declarations but for functions which are processed in a separate pass
560 for _
, decl
:= range src
.Decls
{
561 switch d
:= decl
.(type) {
565 // imports are handled individually
566 for _
, spec
:= range d
.Specs
{
567 if s
, ok
:= spec
.(*ast
.ImportSpec
); ok
{
568 if import_
, err
:= strconv
.Unquote(s
.Path
.Value
); err
== nil {
569 r
.imports
[import_
] = 1
570 if s
.Name
!= nil && s
.Name
.Name
== "." {
576 case token
.CONST
, token
.VAR
:
577 // constants and variables are always handled as a group
580 // types are handled individually
581 if len(d
.Specs
) == 1 && !d
.Lparen
.IsValid() {
582 // common case: single declaration w/o parentheses
583 // (if a single declaration is parenthesized,
584 // create a new fake declaration below, so that
585 // go/doc type declarations always appear w/o
587 if s
, ok
:= d
.Specs
[0].(*ast
.TypeSpec
); ok
{
592 for _
, spec
:= range d
.Specs
{
593 if s
, ok
:= spec
.(*ast
.TypeSpec
); ok
{
594 // use an individual (possibly fake) declaration
595 // for each type; this also ensures that each type
596 // gets to (re-)use the declaration documentation
597 // if there's none associated with the spec itself
598 fake
:= &ast
.GenDecl
{
600 // don't use the existing TokPos because it
601 // will lead to the wrong selection range for
602 // the fake declaration if there are more
603 // than one type in the group (this affects
604 // src/cmd/godoc/godoc.go's posLink_urlFunc)
607 Specs
: []ast
.Spec
{s
},
616 // collect MARKER(...): annotations
617 r
.readNotes(src
.Comments
)
618 if r
.mode
&PreserveAST
== 0 {
619 src
.Comments
= nil // consumed unassociated comments - remove from AST
623 func (r
*reader
) readPackage(pkg
*ast
.Package
, mode Mode
) {
625 r
.filenames
= make([]string, len(pkg
.Files
))
626 r
.imports
= make(map[string]int)
628 r
.types
= make(map[string]*namedType
)
629 r
.funcs
= make(methodSet
)
630 r
.notes
= make(map[string][]*Note
)
632 // sort package files before reading them so that the
633 // result does not depend on map iteration order
635 for filename
:= range pkg
.Files
{
636 r
.filenames
[i
] = filename
639 sort
.Strings(r
.filenames
)
641 // process files in sorted order
642 for _
, filename
:= range r
.filenames
{
643 f
:= pkg
.Files
[filename
]
644 if mode
&AllDecls
== 0 {
650 // process functions now that we have better type information
651 for _
, f
:= range pkg
.Files
{
652 for _
, decl
:= range f
.Decls
{
653 if d
, ok
:= decl
.(*ast
.FuncDecl
); ok
{
660 // ----------------------------------------------------------------------------
663 func customizeRecv(f
*Func
, recvTypeName
string, embeddedIsPtr
bool, level
int) *Func
{
664 if f
== nil || f
.Decl
== nil || f
.Decl
.Recv
== nil ||
len(f
.Decl
.Recv
.List
) != 1 {
665 return f
// shouldn't happen, but be safe
668 // copy existing receiver field and set new type
669 newField
:= *f
.Decl
.Recv
.List
[0]
670 origPos
:= newField
.Type
.Pos()
671 _
, origRecvIsPtr
:= newField
.Type
.(*ast
.StarExpr
)
672 newIdent
:= &ast
.Ident
{NamePos
: origPos
, Name
: recvTypeName
}
673 var typ ast
.Expr
= newIdent
674 if !embeddedIsPtr
&& origRecvIsPtr
{
675 newIdent
.NamePos
++ // '*' is one character
676 typ
= &ast
.StarExpr
{Star
: origPos
, X
: newIdent
}
680 // copy existing receiver field list and set new receiver field
681 newFieldList
:= *f
.Decl
.Recv
682 newFieldList
.List
= []*ast
.Field
{&newField
}
684 // copy existing function declaration and set new receiver field list
685 newFuncDecl
:= *f
.Decl
686 newFuncDecl
.Recv
= &newFieldList
688 // copy existing function documentation and set new declaration
690 newF
.Decl
= &newFuncDecl
691 newF
.Recv
= recvString(typ
)
692 // the Orig field never changes
698 // collectEmbeddedMethods collects the embedded methods of typ in mset.
700 func (r
*reader
) collectEmbeddedMethods(mset methodSet
, typ
*namedType
, recvTypeName
string, embeddedIsPtr
bool, level
int, visited embeddedSet
) {
702 for embedded
, isPtr
:= range typ
.embedded
{
703 // Once an embedded type is embedded as a pointer type
704 // all embedded types in those types are treated like
705 // pointer types for the purpose of the receiver type
706 // computation; i.e., embeddedIsPtr is sticky for this
707 // embedding hierarchy.
708 thisEmbeddedIsPtr
:= embeddedIsPtr || isPtr
709 for _
, m
:= range embedded
.methods
{
710 // only top-level methods are embedded
712 mset
.add(customizeRecv(m
, recvTypeName
, thisEmbeddedIsPtr
, level
))
715 if !visited
[embedded
] {
716 r
.collectEmbeddedMethods(mset
, embedded
, recvTypeName
, thisEmbeddedIsPtr
, level
+1, visited
)
722 // computeMethodSets determines the actual method sets for each type encountered.
724 func (r
*reader
) computeMethodSets() {
725 for _
, t
:= range r
.types
{
726 // collect embedded methods for t
729 r
.collectEmbeddedMethods(t
.methods
, t
, t
.name
, false, 1, make(embeddedSet
))
732 // TODO(gri) fix this
736 // For any predeclared names that are declared locally, don't treat them as
737 // exported fields anymore.
738 for predecl
:= range r
.shadowedPredecl
{
739 for _
, ityp
:= range r
.fixmap
[predecl
] {
740 removeAnonymousField(predecl
, ityp
)
745 // cleanupTypes removes the association of functions and methods with
746 // types that have no declaration. Instead, these functions and methods
747 // are shown at the package level. It also removes types with missing
748 // declarations or which are not visible.
750 func (r
*reader
) cleanupTypes() {
751 for _
, t
:= range r
.types
{
752 visible
:= r
.isVisible(t
.name
)
753 predeclared
:= predeclaredTypes
[t
.name
]
755 if t
.decl
== nil && (predeclared || visible
&& (t
.isEmbedded || r
.hasDotImp
)) {
756 // t.name is a predeclared type (and was not redeclared in this package),
757 // or it was embedded somewhere but its declaration is missing (because
758 // the AST is incomplete), or we have a dot-import (and all bets are off):
759 // move any associated values, funcs, and methods back to the top-level so
760 // that they are not lost.
762 r
.values
= append(r
.values
, t
.values
...)
763 // 2) move factory functions
764 for name
, f
:= range t
.funcs
{
765 // in a correct AST, package-level function names
766 // are all different - no need to check for conflicts
771 for name
, m
:= range t
.methods
{
772 // don't overwrite functions with the same name - drop them
773 if _
, found
:= r
.funcs
[name
]; !found
{
779 // remove types w/o declaration or which are not visible
780 if t
.decl
== nil ||
!visible
{
781 delete(r
.types
, t
.name
)
786 // ----------------------------------------------------------------------------
792 less
func(i
, j
int) bool
795 func (d
*data
) Len() int { return d
.n
}
796 func (d
*data
) Swap(i
, j
int) { d
.swap(i
, j
) }
797 func (d
*data
) Less(i
, j
int) bool { return d
.less(i
, j
) }
799 // sortBy is a helper function for sorting
800 func sortBy(less
func(i
, j
int) bool, swap
func(i
, j
int), n
int) {
801 sort
.Sort(&data
{n
, swap
, less
})
804 func sortedKeys(m
map[string]int) []string {
805 list
:= make([]string, len(m
))
815 // sortingName returns the name to use when sorting d into place.
817 func sortingName(d
*ast
.GenDecl
) string {
818 if len(d
.Specs
) == 1 {
819 if s
, ok
:= d
.Specs
[0].(*ast
.ValueSpec
); ok
{
820 return s
.Names
[0].Name
826 func sortedValues(m
[]*Value
, tok token
.Token
) []*Value
{
827 list
:= make([]*Value
, len(m
)) // big enough in any case
829 for _
, val
:= range m
{
830 if val
.Decl
.Tok
== tok
{
838 func(i
, j
int) bool {
839 if ni
, nj
:= sortingName(list
[i
].Decl
), sortingName(list
[j
].Decl
); ni
!= nj
{
842 return list
[i
].order
< list
[j
].order
844 func(i
, j
int) { list
[i
], list
[j
] = list
[j
], list
[i
] },
851 func sortedTypes(m
map[string]*namedType
, allMethods
bool) []*Type
{
852 list
:= make([]*Type
, len(m
))
854 for _
, t
:= range m
{
859 Consts
: sortedValues(t
.values
, token
.CONST
),
860 Vars
: sortedValues(t
.values
, token
.VAR
),
861 Funcs
: sortedFuncs(t
.funcs
, true),
862 Methods
: sortedFuncs(t
.methods
, allMethods
),
868 func(i
, j
int) bool { return list
[i
].Name
< list
[j
].Name
},
869 func(i
, j
int) { list
[i
], list
[j
] = list
[j
], list
[i
] },
876 func removeStar(s
string) string {
877 if len(s
) > 0 && s
[0] == '*' {
883 func sortedFuncs(m methodSet
, allMethods
bool) []*Func
{
884 list
:= make([]*Func
, len(m
))
886 for _
, m
:= range m
{
887 // determine which methods to include
890 // exclude conflict entry
891 case allMethods
, m
.Level
== 0, !token
.IsExported(removeStar(m
.Orig
)):
892 // forced inclusion, method not embedded, or method
893 // embedded but original receiver type not exported
900 func(i
, j
int) bool { return list
[i
].Name
< list
[j
].Name
},
901 func(i
, j
int) { list
[i
], list
[j
] = list
[j
], list
[i
] },
907 // noteBodies returns a list of note body strings given a list of notes.
908 // This is only used to populate the deprecated Package.Bugs field.
910 func noteBodies(notes
[]*Note
) []string {
912 for _
, n
:= range notes
{
913 list
= append(list
, n
.Body
)
918 // ----------------------------------------------------------------------------
919 // Predeclared identifiers
921 // IsPredeclared reports whether s is a predeclared identifier.
922 func IsPredeclared(s
string) bool {
923 return predeclaredTypes
[s
] || predeclaredFuncs
[s
] || predeclaredConstants
[s
]
926 var predeclaredTypes
= map[string]bool{
951 var predeclaredFuncs
= map[string]bool{
969 var predeclaredConstants
= map[string]bool{