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.
20 "cmd/compile/internal/base"
21 "cmd/compile/internal/importer"
22 "cmd/compile/internal/ir"
23 "cmd/compile/internal/syntax"
24 "cmd/compile/internal/typecheck"
25 "cmd/compile/internal/types"
26 "cmd/compile/internal/types2"
27 "cmd/internal/archive"
34 // Temporary import helper to get type2-based type-checking going.
35 type gcimports struct {
36 packages map[string]*types2.Package
39 func (m *gcimports) Import(path string) (*types2.Package, error) {
40 return m.ImportFrom(path, "" /* no vendoring */, 0)
43 func (m *gcimports) ImportFrom(path, srcDir string, mode types2.ImportMode) (*types2.Package, error) {
45 panic("mode must be 0")
48 path, err := resolveImportPath(path)
53 lookup := func(path string) (io.ReadCloser, error) { return openPackage(path) }
54 return importer.Import(m.packages, path, srcDir, lookup)
57 func isDriveLetter(b byte) bool {
58 return 'a' <= b && b <= 'z' || 'A' <= b && b <= 'Z'
61 // is this path a local name? begins with ./ or ../ or /
62 func islocalname(name string) bool {
63 return strings.HasPrefix(name, "/") ||
64 runtime.GOOS == "windows" && len(name) >= 3 && isDriveLetter(name[0]) && name[1] == ':' && name[2] == '/' ||
65 strings.HasPrefix(name, "./") || name == "." ||
66 strings.HasPrefix(name, "../") || name == ".."
69 func openPackage(path string) (*os.File, error) {
70 if islocalname(path) {
71 if base.Flag.NoLocalImports {
72 return nil, errors.New("local imports disallowed")
75 if base.Flag.Cfg.PackageFile != nil {
76 return os.Open(base.Flag.Cfg.PackageFile[path])
79 // try .a before .o. important for building libraries:
80 // if there is an array.o in the array.a library,
81 // want to find all of array.a, not just array.o.
82 if file, err := os.Open(fmt.Sprintf("%s.a", path)); err == nil {
85 if file, err := os.Open(fmt.Sprintf("%s.o", path)); err == nil {
88 return nil, errors.New("file not found")
91 // local imports should be canonicalized already.
92 // don't want to see "encoding/../encoding/base64"
93 // as different from "encoding/base64".
94 if q := pathpkg.Clean(path); q != path {
95 return nil, fmt.Errorf("non-canonical import path %q (should be %q)", path, q)
98 if base.Flag.Cfg.PackageFile != nil {
99 return os.Open(base.Flag.Cfg.PackageFile[path])
102 for _, dir := range base.Flag.Cfg.ImportDirs {
103 if file, err := os.Open(fmt.Sprintf("%s/%s.a", dir, path)); err == nil {
106 if file, err := os.Open(fmt.Sprintf("%s/%s.o", dir, path)); err == nil {
111 if objabi.GOROOT != "" {
113 if base.Flag.InstallSuffix != "" {
114 suffix = "_" + base.Flag.InstallSuffix
115 } else if base.Flag.Race {
117 } else if base.Flag.MSan {
121 if file, err := os.Open(fmt.Sprintf("%s/pkg/%s_%s%s/%s.a", objabi.GOROOT, objabi.GOOS, objabi.GOARCH, suffix, path)); err == nil {
124 if file, err := os.Open(fmt.Sprintf("%s/pkg/%s_%s%s/%s.o", objabi.GOROOT, objabi.GOOS, objabi.GOARCH, suffix, path)); err == nil {
128 return nil, errors.New("file not found")
131 // myheight tracks the local package's height based on packages
135 // resolveImportPath resolves an import path as it appears in a Go
136 // source file to the package's full path.
137 func resolveImportPath(path string) (string, error) {
138 // The package name main is no longer reserved,
139 // but we reserve the import path "main" to identify
140 // the main package, just as we reserve the import
141 // path "math" to identify the standard math package.
143 return "", errors.New("cannot import \"main\"")
146 if base.Ctxt.Pkgpath != "" && path == base.Ctxt.Pkgpath {
147 return "", fmt.Errorf("import %q while compiling that package (import cycle)", path)
150 if mapped, ok := base.Flag.Cfg.ImportMap[path]; ok {
154 if islocalname(path) {
156 return "", errors.New("import path cannot be absolute path")
159 prefix := base.Flag.D
161 // Questionable, but when -D isn't specified, historically we
162 // resolve local import paths relative to the directory the
163 // compiler's current directory, not the respective source
165 prefix = base.Ctxt.Pathname
167 path = pathpkg.Join(prefix, path)
169 if err := checkImportPath(path, true); err != nil {
177 // TODO(mdempsky): Return an error instead.
178 func importfile(decl *syntax.ImportDecl) *types.Pkg {
179 if decl.Path.Kind != syntax.StringLit {
180 base.Errorf("import path must be a string")
184 path, err := strconv.Unquote(decl.Path.Value)
186 base.Errorf("import path must be a string")
190 if err := checkImportPath(path, false); err != nil {
191 base.Errorf("%s", err.Error())
195 path, err = resolveImportPath(path)
197 base.Errorf("%s", err)
201 importpkg := types.NewPkg(path, "")
202 if importpkg.Direct {
203 return importpkg // already fully loaded
205 importpkg.Direct = true
206 typecheck.Target.Imports = append(typecheck.Target.Imports, importpkg)
208 if path == "unsafe" {
209 return importpkg // initialized with universe
212 f, err := openPackage(path)
214 base.Errorf("could not import %q: %v", path, err)
217 imp := bio.NewReader(f)
221 // check object header
222 p, err := imp.ReadString('\n')
224 base.Errorf("import %s: reading input: %v", file, err)
228 if p == "!<arch>\n" { // package archive
229 // package export block should be first
230 sz := archive.ReadHeader(imp.Reader, "__.PKGDEF")
232 base.Errorf("import %s: not a package file", file)
235 p, err = imp.ReadString('\n')
237 base.Errorf("import %s: reading input: %v", file, err)
242 if !strings.HasPrefix(p, "go object ") {
243 base.Errorf("import %s: not a go object file: %s", file, p)
246 q := objabi.HeaderString()
248 base.Errorf("import %s: object is [%s] expected [%s]", file, p, q)
252 // process header lines
254 p, err = imp.ReadString('\n')
256 base.Errorf("import %s: reading input: %v", file, err)
260 break // header ends with blank line
264 // Expect $$B\n to signal binary import format.
269 c, err = imp.ReadByte()
274 c, err = imp.ReadByte()
275 if c == '$' || err != nil {
281 // get character after $$
283 c, _ = imp.ReadByte()
286 var fingerprint goobj.FingerprintType
289 base.Errorf("cannot import %s: old export format no longer supported (recompile library)", path)
293 if base.Debug.Export != 0 {
294 fmt.Printf("importing %s (%s)\n", path, file)
296 imp.ReadByte() // skip \n after $$B
298 c, err = imp.ReadByte()
300 base.Errorf("import %s: reading input: %v", file, err)
304 // Indexed format is distinguished by an 'i' byte,
305 // whereas previous export formats started with 'c', 'd', or 'v'.
307 base.Errorf("import %s: unexpected package format byte: %v", file, c)
310 fingerprint = typecheck.ReadImports(importpkg, imp)
313 base.Errorf("no import in %q", path)
317 // assume files move (get installed) so don't record the full path
318 if base.Flag.Cfg.PackageFile != nil {
319 // If using a packageFile map, assume path_ can be recorded directly.
320 base.Ctxt.AddImport(path, fingerprint)
322 // For file "/Users/foo/go/pkg/darwin_amd64/math.a" record "math.a".
323 base.Ctxt.AddImport(file[len(file)-len(path)-len(".a"):], fingerprint)
326 if importpkg.Height >= myheight {
327 myheight = importpkg.Height + 1
333 // The linker uses the magic symbol prefixes "go." and "type."
334 // Avoid potential confusion between import paths and symbols
335 // by rejecting these reserved imports for now. Also, people
336 // "can do weird things in GOPATH and we'd prefer they didn't
337 // do _that_ weird thing" (per rsc). See also #4257.
338 var reservedimports = []string{
343 func checkImportPath(path string, allowSpace bool) error {
345 return errors.New("import path is empty")
348 if strings.Contains(path, "\x00") {
349 return errors.New("import path contains NUL")
352 for _, ri := range reservedimports {
354 return fmt.Errorf("import path %q is reserved and cannot be used", path)
358 for _, r := range path {
360 case r == utf8.RuneError:
361 return fmt.Errorf("import path contains invalid UTF-8 sequence: %q", path)
362 case r < 0x20 || r == 0x7f:
363 return fmt.Errorf("import path contains control character: %q", path)
365 return fmt.Errorf("import path contains backslash; use slash: %q", path)
366 case !allowSpace && unicode.IsSpace(r):
367 return fmt.Errorf("import path contains space character: %q", path)
368 case strings.ContainsRune("!\"#$%&'()*,:;<=>?[]^`{|}", r):
369 return fmt.Errorf("import path contains invalid character '%c': %q", r, path)
376 func pkgnotused(lineno src.XPos, path string, name string) {
377 // If the package was imported with a name other than the final
378 // import path element, show it explicitly in the error message.
379 // Note that this handles both renamed imports and imports of
380 // packages containing unconventional package declarations.
381 // Note that this uses / always, even on Windows, because Go import
382 // paths always use forward slashes.
384 if i := strings.LastIndex(elem, "/"); i >= 0 {
387 if name == "" || elem == name {
388 base.ErrorfAt(lineno, "imported and not used: %q", path)
390 base.ErrorfAt(lineno, "imported and not used: %q as %s", path, name)
394 func mkpackage(pkgname string) {
395 if types.LocalPkg.Name == "" {
397 base.Errorf("invalid package name _")
399 types.LocalPkg.Name = pkgname
401 if pkgname != types.LocalPkg.Name {
402 base.Errorf("package %s; expected %s", pkgname, types.LocalPkg.Name)
407 func clearImports() {
408 type importedPkg struct {
413 var unused []importedPkg
415 for _, s := range types.LocalPkg.Syms {
416 n := ir.AsNode(s.Def)
420 if n.Op() == ir.OPACK {
421 // throw away top-level package name left over
422 // from previous file.
423 // leave s->block set to cause redeclaration
424 // errors if a conflicting top-level name is
425 // introduced by a different file.
427 if !p.Used && base.SyntaxErrors() == 0 {
428 unused = append(unused, importedPkg{p.Pos(), p.Pkg.Path, s.Name})
433 if types.IsDotAlias(s) {
434 // throw away top-level name left over
435 // from previous import . "x"
436 // We'll report errors after type checking in CheckDotImports.
442 sort.Slice(unused, func(i, j int) bool { return unused[i].pos.Before(unused[j].pos) })
443 for _, pkg := range unused {
444 pkgnotused(pkg.pos, pkg.path, pkg.name)
448 // CheckDotImports reports errors for any unused dot imports.
449 func CheckDotImports() {
450 for _, pack := range dotImports {
452 base.ErrorfAt(pack.Pos(), "imported and not used: %q", pack.Pkg.Path)
456 // No longer needed; release memory.
458 typecheck.DotImportRefs = nil
461 // dotImports tracks all PkgNames that have been dot-imported.
462 var dotImports []*ir.PkgName
464 // find all the exported symbols in package referenced by PkgName,
465 // and make them available in the current package
466 func importDot(pack *ir.PkgName) {
467 if typecheck.DotImportRefs == nil {
468 typecheck.DotImportRefs = make(map[*ir.Ident]*ir.PkgName)
472 for _, s := range opkg.Syms {
474 if _, ok := typecheck.DeclImporter[s]; !ok {
478 if !types.IsExported(s.Name) || strings.ContainsRune(s.Name, 0xb7) { // 0xb7 = center dot
481 s1 := typecheck.Lookup(s.Name)
483 pkgerror := fmt.Sprintf("during import %q", opkg.Path)
484 typecheck.Redeclared(base.Pos, s1, pkgerror)
488 id := ir.NewIdent(src.NoXPos, s)
489 typecheck.DotImportRefs[id] = pack
494 dotImports = append(dotImports, pack)
497 // importName is like oldname,
498 // but it reports an error if sym is from another package and not exported.
499 func importName(sym *types.Sym) ir.Node {
501 if !types.IsExported(sym.Name) && sym.Pkg != types.LocalPkg {
503 base.Errorf("cannot refer to unexported name %s.%s", sym.Pkg.Name, sym.Name)