// Copyright 2011 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // This file is a modified copy of $GOROOT/src/go/internal/gcimporter/gcimporter.go, // but it also contains the original source-based importer code for Go1.6. // Once we stop supporting 1.6, we can remove that code. // Package gcimporter provides various functions for reading // gc-generated object files that can be used to implement the // Importer interface defined by the Go 1.5 standard library package. package gcimporter // import "golang.org/x/tools/go/internal/gcimporter" import ( "bufio" "errors" "fmt" "go/build" "go/constant" "go/token" "go/types" "io" "io/ioutil" "os" "path/filepath" "sort" "strconv" "strings" "text/scanner" ) // debugging/development support const debug = false var pkgExts = [...]string{".a", ".o"} // FindPkg returns the filename and unique package id for an import // path based on package information provided by build.Import (using // the build.Default build.Context). A relative srcDir is interpreted // relative to the current working directory. // If no file was found, an empty filename is returned. // func FindPkg(path, srcDir string) (filename, id string) { if path == "" { return } var noext string switch { default: // "x" -> "$GOPATH/pkg/$GOOS_$GOARCH/x.ext", "x" // Don't require the source files to be present. if abs, err := filepath.Abs(srcDir); err == nil { // see issue 14282 srcDir = abs } bp, _ := build.Import(path, srcDir, build.FindOnly|build.AllowBinary) if bp.PkgObj == "" { id = path // make sure we have an id to print in error message return } noext = strings.TrimSuffix(bp.PkgObj, ".a") id = bp.ImportPath case build.IsLocalImport(path): // "./x" -> "/this/directory/x.ext", "/this/directory/x" noext = filepath.Join(srcDir, path) id = noext case filepath.IsAbs(path): // for completeness only - go/build.Import // does not support absolute imports // "/x" -> "/x.ext", "/x" noext = path id = path } if false { // for debugging if path != id { fmt.Printf("%s -> %s\n", path, id) } } // try extensions for _, ext := range pkgExts { filename = noext + ext if f, err := os.Stat(filename); err == nil && !f.IsDir() { return } } filename = "" // not found return } // ImportData imports a package by reading the gc-generated export data, // adds the corresponding package object to the packages map indexed by id, // and returns the object. // // The packages map must contains all packages already imported. The data // reader position must be the beginning of the export data section. The // filename is only used in error messages. // // If packages[id] contains the completely imported package, that package // can be used directly, and there is no need to call this function (but // there is also no harm but for extra time used). // func ImportData(packages map[string]*types.Package, filename, id string, data io.Reader) (pkg *types.Package, err error) { // support for parser error handling defer func() { switch r := recover().(type) { case nil: // nothing to do case importError: err = r default: panic(r) // internal error } }() var p parser p.init(filename, id, data, packages) pkg = p.parseExport() return } // Import imports a gc-generated package given its import path and srcDir, adds // the corresponding package object to the packages map, and returns the object. // The packages map must contain all packages already imported. // func Import(packages map[string]*types.Package, path, srcDir string, lookup func(path string) (io.ReadCloser, error)) (pkg *types.Package, err error) { var rc io.ReadCloser var filename, id string if lookup != nil { // With custom lookup specified, assume that caller has // converted path to a canonical import path for use in the map. if path == "unsafe" { return types.Unsafe, nil } id = path // No need to re-import if the package was imported completely before. if pkg = packages[id]; pkg != nil && pkg.Complete() { return } f, err := lookup(path) if err != nil { return nil, err } rc = f } else { filename, id = FindPkg(path, srcDir) if filename == "" { if path == "unsafe" { return types.Unsafe, nil } return nil, fmt.Errorf("can't find import: %q", id) } // no need to re-import if the package was imported completely before if pkg = packages[id]; pkg != nil && pkg.Complete() { return } // open file f, err := os.Open(filename) if err != nil { return nil, err } defer func() { if err != nil { // add file name to error err = fmt.Errorf("%s: %v", filename, err) } }() rc = f } defer rc.Close() var hdr string buf := bufio.NewReader(rc) if hdr, err = FindExportData(buf); err != nil { return } switch hdr { case "$$\n": // Work-around if we don't have a filename; happens only if lookup != nil. // Either way, the filename is only needed for importer error messages, so // this is fine. if filename == "" { filename = path } return ImportData(packages, filename, id, buf) case "$$B\n": var data []byte data, err = ioutil.ReadAll(buf) if err != nil { break } // TODO(gri): allow clients of go/importer to provide a FileSet. // Or, define a new standard go/types/gcexportdata package. fset := token.NewFileSet() // The indexed export format starts with an 'i'; the older // binary export format starts with a 'c', 'd', or 'v' // (from "version"). Select appropriate importer. if len(data) > 0 && data[0] == 'i' { _, pkg, err = IImportData(fset, packages, data[1:], id) } else { _, pkg, err = BImportData(fset, packages, data, id) } default: err = fmt.Errorf("unknown export data header: %q", hdr) } return } // ---------------------------------------------------------------------------- // Parser // TODO(gri) Imported objects don't have position information. // Ideally use the debug table line info; alternatively // create some fake position (or the position of the // import). That way error messages referring to imported // objects can print meaningful information. // parser parses the exports inside a gc compiler-produced // object/archive file and populates its scope with the results. type parser struct { scanner scanner.Scanner tok rune // current token lit string // literal string; only valid for Ident, Int, String tokens id string // package id of imported package sharedPkgs map[string]*types.Package // package id -> package object (across importer) localPkgs map[string]*types.Package // package id -> package object (just this package) } func (p *parser) init(filename, id string, src io.Reader, packages map[string]*types.Package) { p.scanner.Init(src) p.scanner.Error = func(_ *scanner.Scanner, msg string) { p.error(msg) } p.scanner.Mode = scanner.ScanIdents | scanner.ScanInts | scanner.ScanChars | scanner.ScanStrings | scanner.ScanComments | scanner.SkipComments p.scanner.Whitespace = 1<<'\t' | 1<<' ' p.scanner.Filename = filename // for good error messages p.next() p.id = id p.sharedPkgs = packages if debug { // check consistency of packages map for _, pkg := range packages { if pkg.Name() == "" { fmt.Printf("no package name for %s\n", pkg.Path()) } } } } func (p *parser) next() { p.tok = p.scanner.Scan() switch p.tok { case scanner.Ident, scanner.Int, scanner.Char, scanner.String, '·': p.lit = p.scanner.TokenText() default: p.lit = "" } if debug { fmt.Printf("%s: %q -> %q\n", scanner.TokenString(p.tok), p.scanner.TokenText(), p.lit) } } func declTypeName(pkg *types.Package, name string) *types.TypeName { scope := pkg.Scope() if obj := scope.Lookup(name); obj != nil { return obj.(*types.TypeName) } obj := types.NewTypeName(token.NoPos, pkg, name, nil) // a named type may be referred to before the underlying type // is known - set it up types.NewNamed(obj, nil, nil) scope.Insert(obj) return obj } // ---------------------------------------------------------------------------- // Error handling // Internal errors are boxed as importErrors. type importError struct { pos scanner.Position err error } func (e importError) Error() string { return fmt.Sprintf("import error %s (byte offset = %d): %s", e.pos, e.pos.Offset, e.err) } func (p *parser) error(err interface{}) { if s, ok := err.(string); ok { err = errors.New(s) } // panic with a runtime.Error if err is not an error panic(importError{p.scanner.Pos(), err.(error)}) } func (p *parser) errorf(format string, args ...interface{}) { p.error(fmt.Sprintf(format, args...)) } func (p *parser) expect(tok rune) string { lit := p.lit if p.tok != tok { p.errorf("expected %s, got %s (%s)", scanner.TokenString(tok), scanner.TokenString(p.tok), lit) } p.next() return lit } func (p *parser) expectSpecial(tok string) { sep := 'x' // not white space i := 0 for i < len(tok) && p.tok == rune(tok[i]) && sep > ' ' { sep = p.scanner.Peek() // if sep <= ' ', there is white space before the next token p.next() i++ } if i < len(tok) { p.errorf("expected %q, got %q", tok, tok[0:i]) } } func (p *parser) expectKeyword(keyword string) { lit := p.expect(scanner.Ident) if lit != keyword { p.errorf("expected keyword %s, got %q", keyword, lit) } } // ---------------------------------------------------------------------------- // Qualified and unqualified names // PackageId = string_lit . // func (p *parser) parsePackageID() string { id, err := strconv.Unquote(p.expect(scanner.String)) if err != nil { p.error(err) } // id == "" stands for the imported package id // (only known at time of package installation) if id == "" { id = p.id } return id } // PackageName = ident . // func (p *parser) parsePackageName() string { return p.expect(scanner.Ident) } // dotIdentifier = ( ident | '·' ) { ident | int | '·' } . func (p *parser) parseDotIdent() string { ident := "" if p.tok != scanner.Int { sep := 'x' // not white space for (p.tok == scanner.Ident || p.tok == scanner.Int || p.tok == '·') && sep > ' ' { ident += p.lit sep = p.scanner.Peek() // if sep <= ' ', there is white space before the next token p.next() } } if ident == "" { p.expect(scanner.Ident) // use expect() for error handling } return ident } // QualifiedName = "@" PackageId "." ( "?" | dotIdentifier ) . // func (p *parser) parseQualifiedName() (id, name string) { p.expect('@') id = p.parsePackageID() p.expect('.') // Per rev f280b8a485fd (10/2/2013), qualified names may be used for anonymous fields. if p.tok == '?' { p.next() } else { name = p.parseDotIdent() } return } // getPkg returns the package for a given id. If the package is // not found, create the package and add it to the p.localPkgs // and p.sharedPkgs maps. name is the (expected) name of the // package. If name == "", the package name is expected to be // set later via an import clause in the export data. // // id identifies a package, usually by a canonical package path like // "encoding/json" but possibly by a non-canonical import path like // "./json". // func (p *parser) getPkg(id, name string) *types.Package { // package unsafe is not in the packages maps - handle explicitly if id == "unsafe" { return types.Unsafe } pkg := p.localPkgs[id] if pkg == nil { // first import of id from this package pkg = p.sharedPkgs[id] if pkg == nil { // first import of id by this importer; // add (possibly unnamed) pkg to shared packages pkg = types.NewPackage(id, name) p.sharedPkgs[id] = pkg } // add (possibly unnamed) pkg to local packages if p.localPkgs == nil { p.localPkgs = make(map[string]*types.Package) } p.localPkgs[id] = pkg } else if name != "" { // package exists already and we have an expected package name; // make sure names match or set package name if necessary if pname := pkg.Name(); pname == "" { pkg.SetName(name) } else if pname != name { p.errorf("%s package name mismatch: %s (given) vs %s (expected)", id, pname, name) } } return pkg } // parseExportedName is like parseQualifiedName, but // the package id is resolved to an imported *types.Package. // func (p *parser) parseExportedName() (pkg *types.Package, name string) { id, name := p.parseQualifiedName() pkg = p.getPkg(id, "") return } // ---------------------------------------------------------------------------- // Types // BasicType = identifier . // func (p *parser) parseBasicType() types.Type { id := p.expect(scanner.Ident) obj := types.Universe.Lookup(id) if obj, ok := obj.(*types.TypeName); ok { return obj.Type() } p.errorf("not a basic type: %s", id) return nil } // ArrayType = "[" int_lit "]" Type . // func (p *parser) parseArrayType(parent *types.Package) types.Type { // "[" already consumed and lookahead known not to be "]" lit := p.expect(scanner.Int) p.expect(']') elem := p.parseType(parent) n, err := strconv.ParseInt(lit, 10, 64) if err != nil { p.error(err) } return types.NewArray(elem, n) } // MapType = "map" "[" Type "]" Type . // func (p *parser) parseMapType(parent *types.Package) types.Type { p.expectKeyword("map") p.expect('[') key := p.parseType(parent) p.expect(']') elem := p.parseType(parent) return types.NewMap(key, elem) } // Name = identifier | "?" | QualifiedName . // // For unqualified and anonymous names, the returned package is the parent // package unless parent == nil, in which case the returned package is the // package being imported. (The parent package is not nil if the name // is an unqualified struct field or interface method name belonging to a // type declared in another package.) // // For qualified names, the returned package is nil (and not created if // it doesn't exist yet) unless materializePkg is set (which creates an // unnamed package with valid package path). In the latter case, a // subsequent import clause is expected to provide a name for the package. // func (p *parser) parseName(parent *types.Package, materializePkg bool) (pkg *types.Package, name string) { pkg = parent if pkg == nil { pkg = p.sharedPkgs[p.id] } switch p.tok { case scanner.Ident: name = p.lit p.next() case '?': // anonymous p.next() case '@': // exported name prefixed with package path pkg = nil var id string id, name = p.parseQualifiedName() if materializePkg { pkg = p.getPkg(id, "") } default: p.error("name expected") } return } func deref(typ types.Type) types.Type { if p, _ := typ.(*types.Pointer); p != nil { return p.Elem() } return typ } // Field = Name Type [ string_lit ] . // func (p *parser) parseField(parent *types.Package) (*types.Var, string) { pkg, name := p.parseName(parent, true) if name == "_" { // Blank fields should be package-qualified because they // are unexported identifiers, but gc does not qualify them. // Assuming that the ident belongs to the current package // causes types to change during re-exporting, leading // to spurious "can't assign A to B" errors from go/types. // As a workaround, pretend all blank fields belong // to the same unique dummy package. const blankpkg = "<_>" pkg = p.getPkg(blankpkg, blankpkg) } typ := p.parseType(parent) anonymous := false if name == "" { // anonymous field - typ must be T or *T and T must be a type name switch typ := deref(typ).(type) { case *types.Basic: // basic types are named types pkg = nil // objects defined in Universe scope have no package name = typ.Name() case *types.Named: name = typ.Obj().Name() default: p.errorf("anonymous field expected") } anonymous = true } tag := "" if p.tok == scanner.String { s := p.expect(scanner.String) var err error tag, err = strconv.Unquote(s) if err != nil { p.errorf("invalid struct tag %s: %s", s, err) } } return types.NewField(token.NoPos, pkg, name, typ, anonymous), tag } // StructType = "struct" "{" [ FieldList ] "}" . // FieldList = Field { ";" Field } . // func (p *parser) parseStructType(parent *types.Package) types.Type { var fields []*types.Var var tags []string p.expectKeyword("struct") p.expect('{') for i := 0; p.tok != '}' && p.tok != scanner.EOF; i++ { if i > 0 { p.expect(';') } fld, tag := p.parseField(parent) if tag != "" && tags == nil { tags = make([]string, i) } if tags != nil { tags = append(tags, tag) } fields = append(fields, fld) } p.expect('}') return types.NewStruct(fields, tags) } // Parameter = ( identifier | "?" ) [ "..." ] Type [ string_lit ] . // func (p *parser) parseParameter() (par *types.Var, isVariadic bool) { _, name := p.parseName(nil, false) // remove gc-specific parameter numbering if i := strings.Index(name, "·"); i >= 0 { name = name[:i] } if p.tok == '.' { p.expectSpecial("...") isVariadic = true } typ := p.parseType(nil) if isVariadic { typ = types.NewSlice(typ) } // ignore argument tag (e.g. "noescape") if p.tok == scanner.String { p.next() } // TODO(gri) should we provide a package? par = types.NewVar(token.NoPos, nil, name, typ) return } // Parameters = "(" [ ParameterList ] ")" . // ParameterList = { Parameter "," } Parameter . // func (p *parser) parseParameters() (list []*types.Var, isVariadic bool) { p.expect('(') for p.tok != ')' && p.tok != scanner.EOF { if len(list) > 0 { p.expect(',') } par, variadic := p.parseParameter() list = append(list, par) if variadic { if isVariadic { p.error("... not on final argument") } isVariadic = true } } p.expect(')') return } // Signature = Parameters [ Result ] . // Result = Type | Parameters . // func (p *parser) parseSignature(recv *types.Var) *types.Signature { params, isVariadic := p.parseParameters() // optional result type var results []*types.Var if p.tok == '(' { var variadic bool results, variadic = p.parseParameters() if variadic { p.error("... not permitted on result type") } } return types.NewSignature(recv, types.NewTuple(params...), types.NewTuple(results...), isVariadic) } // InterfaceType = "interface" "{" [ MethodList ] "}" . // MethodList = Method { ";" Method } . // Method = Name Signature . // // The methods of embedded interfaces are always "inlined" // by the compiler and thus embedded interfaces are never // visible in the export data. // func (p *parser) parseInterfaceType(parent *types.Package) types.Type { var methods []*types.Func p.expectKeyword("interface") p.expect('{') for i := 0; p.tok != '}' && p.tok != scanner.EOF; i++ { if i > 0 { p.expect(';') } pkg, name := p.parseName(parent, true) sig := p.parseSignature(nil) methods = append(methods, types.NewFunc(token.NoPos, pkg, name, sig)) } p.expect('}') // Complete requires the type's embedded interfaces to be fully defined, // but we do not define any return newInterface(methods, nil).Complete() } // ChanType = ( "chan" [ "<-" ] | "<-" "chan" ) Type . // func (p *parser) parseChanType(parent *types.Package) types.Type { dir := types.SendRecv if p.tok == scanner.Ident { p.expectKeyword("chan") if p.tok == '<' { p.expectSpecial("<-") dir = types.SendOnly } } else { p.expectSpecial("<-") p.expectKeyword("chan") dir = types.RecvOnly } elem := p.parseType(parent) return types.NewChan(dir, elem) } // Type = // BasicType | TypeName | ArrayType | SliceType | StructType | // PointerType | FuncType | InterfaceType | MapType | ChanType | // "(" Type ")" . // // BasicType = ident . // TypeName = ExportedName . // SliceType = "[" "]" Type . // PointerType = "*" Type . // FuncType = "func" Signature . // func (p *parser) parseType(parent *types.Package) types.Type { switch p.tok { case scanner.Ident: switch p.lit { default: return p.parseBasicType() case "struct": return p.parseStructType(parent) case "func": // FuncType p.next() return p.parseSignature(nil) case "interface": return p.parseInterfaceType(parent) case "map": return p.parseMapType(parent) case "chan": return p.parseChanType(parent) } case '@': // TypeName pkg, name := p.parseExportedName() return declTypeName(pkg, name).Type() case '[': p.next() // look ahead if p.tok == ']' { // SliceType p.next() return types.NewSlice(p.parseType(parent)) } return p.parseArrayType(parent) case '*': // PointerType p.next() return types.NewPointer(p.parseType(parent)) case '<': return p.parseChanType(parent) case '(': // "(" Type ")" p.next() typ := p.parseType(parent) p.expect(')') return typ } p.errorf("expected type, got %s (%q)", scanner.TokenString(p.tok), p.lit) return nil } // ---------------------------------------------------------------------------- // Declarations // ImportDecl = "import" PackageName PackageId . // func (p *parser) parseImportDecl() { p.expectKeyword("import") name := p.parsePackageName() p.getPkg(p.parsePackageID(), name) } // int_lit = [ "+" | "-" ] { "0" ... "9" } . // func (p *parser) parseInt() string { s := "" switch p.tok { case '-': s = "-" p.next() case '+': p.next() } return s + p.expect(scanner.Int) } // number = int_lit [ "p" int_lit ] . // func (p *parser) parseNumber() (typ *types.Basic, val constant.Value) { // mantissa mant := constant.MakeFromLiteral(p.parseInt(), token.INT, 0) if mant == nil { panic("invalid mantissa") } if p.lit == "p" { // exponent (base 2) p.next() exp, err := strconv.ParseInt(p.parseInt(), 10, 0) if err != nil { p.error(err) } if exp < 0 { denom := constant.MakeInt64(1) denom = constant.Shift(denom, token.SHL, uint(-exp)) typ = types.Typ[types.UntypedFloat] val = constant.BinaryOp(mant, token.QUO, denom) return } if exp > 0 { mant = constant.Shift(mant, token.SHL, uint(exp)) } typ = types.Typ[types.UntypedFloat] val = mant return } typ = types.Typ[types.UntypedInt] val = mant return } // ConstDecl = "const" ExportedName [ Type ] "=" Literal . // Literal = bool_lit | int_lit | float_lit | complex_lit | rune_lit | string_lit . // bool_lit = "true" | "false" . // complex_lit = "(" float_lit "+" float_lit "i" ")" . // rune_lit = "(" int_lit "+" int_lit ")" . // string_lit = `"` { unicode_char } `"` . // func (p *parser) parseConstDecl() { p.expectKeyword("const") pkg, name := p.parseExportedName() var typ0 types.Type if p.tok != '=' { // constant types are never structured - no need for parent type typ0 = p.parseType(nil) } p.expect('=') var typ types.Type var val constant.Value switch p.tok { case scanner.Ident: // bool_lit if p.lit != "true" && p.lit != "false" { p.error("expected true or false") } typ = types.Typ[types.UntypedBool] val = constant.MakeBool(p.lit == "true") p.next() case '-', scanner.Int: // int_lit typ, val = p.parseNumber() case '(': // complex_lit or rune_lit p.next() if p.tok == scanner.Char { p.next() p.expect('+') typ = types.Typ[types.UntypedRune] _, val = p.parseNumber() p.expect(')') break } _, re := p.parseNumber() p.expect('+') _, im := p.parseNumber() p.expectKeyword("i") p.expect(')') typ = types.Typ[types.UntypedComplex] val = constant.BinaryOp(re, token.ADD, constant.MakeImag(im)) case scanner.Char: // rune_lit typ = types.Typ[types.UntypedRune] val = constant.MakeFromLiteral(p.lit, token.CHAR, 0) p.next() case scanner.String: // string_lit typ = types.Typ[types.UntypedString] val = constant.MakeFromLiteral(p.lit, token.STRING, 0) p.next() default: p.errorf("expected literal got %s", scanner.TokenString(p.tok)) } if typ0 == nil { typ0 = typ } pkg.Scope().Insert(types.NewConst(token.NoPos, pkg, name, typ0, val)) } // TypeDecl = "type" ExportedName Type . // func (p *parser) parseTypeDecl() { p.expectKeyword("type") pkg, name := p.parseExportedName() obj := declTypeName(pkg, name) // The type object may have been imported before and thus already // have a type associated with it. We still need to parse the type // structure, but throw it away if the object already has a type. // This ensures that all imports refer to the same type object for // a given type declaration. typ := p.parseType(pkg) if name := obj.Type().(*types.Named); name.Underlying() == nil { name.SetUnderlying(typ) } } // VarDecl = "var" ExportedName Type . // func (p *parser) parseVarDecl() { p.expectKeyword("var") pkg, name := p.parseExportedName() typ := p.parseType(pkg) pkg.Scope().Insert(types.NewVar(token.NoPos, pkg, name, typ)) } // Func = Signature [ Body ] . // Body = "{" ... "}" . // func (p *parser) parseFunc(recv *types.Var) *types.Signature { sig := p.parseSignature(recv) if p.tok == '{' { p.next() for i := 1; i > 0; p.next() { switch p.tok { case '{': i++ case '}': i-- } } } return sig } // MethodDecl = "func" Receiver Name Func . // Receiver = "(" ( identifier | "?" ) [ "*" ] ExportedName ")" . // func (p *parser) parseMethodDecl() { // "func" already consumed p.expect('(') recv, _ := p.parseParameter() // receiver p.expect(')') // determine receiver base type object base := deref(recv.Type()).(*types.Named) // parse method name, signature, and possibly inlined body _, name := p.parseName(nil, false) sig := p.parseFunc(recv) // methods always belong to the same package as the base type object pkg := base.Obj().Pkg() // add method to type unless type was imported before // and method exists already // TODO(gri) This leads to a quadratic algorithm - ok for now because method counts are small. base.AddMethod(types.NewFunc(token.NoPos, pkg, name, sig)) } // FuncDecl = "func" ExportedName Func . // func (p *parser) parseFuncDecl() { // "func" already consumed pkg, name := p.parseExportedName() typ := p.parseFunc(nil) pkg.Scope().Insert(types.NewFunc(token.NoPos, pkg, name, typ)) } // Decl = [ ImportDecl | ConstDecl | TypeDecl | VarDecl | FuncDecl | MethodDecl ] "\n" . // func (p *parser) parseDecl() { if p.tok == scanner.Ident { switch p.lit { case "import": p.parseImportDecl() case "const": p.parseConstDecl() case "type": p.parseTypeDecl() case "var": p.parseVarDecl() case "func": p.next() // look ahead if p.tok == '(' { p.parseMethodDecl() } else { p.parseFuncDecl() } } } p.expect('\n') } // ---------------------------------------------------------------------------- // Export // Export = "PackageClause { Decl } "$$" . // PackageClause = "package" PackageName [ "safe" ] "\n" . // func (p *parser) parseExport() *types.Package { p.expectKeyword("package") name := p.parsePackageName() if p.tok == scanner.Ident && p.lit == "safe" { // package was compiled with -u option - ignore p.next() } p.expect('\n') pkg := p.getPkg(p.id, name) for p.tok != '$' && p.tok != scanner.EOF { p.parseDecl() } if ch := p.scanner.Peek(); p.tok != '$' || ch != '$' { // don't call next()/expect() since reading past the // export data may cause scanner errors (e.g. NUL chars) p.errorf("expected '$$', got %s %c", scanner.TokenString(p.tok), ch) } if n := p.scanner.ErrorCount; n != 0 { p.errorf("expected no scanner errors, got %d", n) } // Record all locally referenced packages as imports. var imports []*types.Package for id, pkg2 := range p.localPkgs { if pkg2.Name() == "" { p.errorf("%s package has no name", id) } if id == p.id { continue // avoid self-edge } imports = append(imports, pkg2) } sort.Sort(byPath(imports)) pkg.SetImports(imports) // package was imported completely and without errors pkg.MarkComplete() return pkg } type byPath []*types.Package func (a byPath) Len() int { return len(a) } func (a byPath) Swap(i, j int) { a[i], a[j] = a[j], a[i] } func (a byPath) Less(i, j int) bool { return a[i].Path() < a[j].Path() }