]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.typeparams] cmd/compile: refactor import logic
authorMatthew Dempsky <mdempsky@google.com>
Sat, 9 Jan 2021 07:44:31 +0000 (23:44 -0800)
committerMatthew Dempsky <mdempsky@google.com>
Tue, 12 Jan 2021 02:16:50 +0000 (02:16 +0000)
This CL refactors noder's package import logic so it's easier to reuse
with types2 and gcimports. In particular, this allows the types2
integration to now support vendored packages.

Change-Id: I1fd98ad612b4683d2e1ac640839e64de1fa7324b
Reviewed-on: https://go-review.googlesource.com/c/go/+/282919
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
Trust: Robert Griesemer <gri@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>

src/cmd/compile/internal/noder/import.go
src/cmd/compile/internal/noder/noder.go
src/cmd/compile/internal/types/pkg.go
test/fixedbugs/issue11362.go
test/run.go

index 08f19a4028466fb5c5117b38eff4cd976d35a6c2..ac7bc8bbf03b8a83c25392874af00a6acc39df8c 100644 (file)
@@ -5,20 +5,25 @@
 package noder
 
 import (
+       "errors"
        "fmt"
-       "go/constant"
+       "io"
        "os"
-       "path"
+       pathpkg "path"
        "runtime"
        "sort"
+       "strconv"
        "strings"
        "unicode"
        "unicode/utf8"
 
        "cmd/compile/internal/base"
+       "cmd/compile/internal/importer"
        "cmd/compile/internal/ir"
+       "cmd/compile/internal/syntax"
        "cmd/compile/internal/typecheck"
        "cmd/compile/internal/types"
+       "cmd/compile/internal/types2"
        "cmd/internal/archive"
        "cmd/internal/bio"
        "cmd/internal/goobj"
@@ -26,6 +31,29 @@ import (
        "cmd/internal/src"
 )
 
+// Temporary import helper to get type2-based type-checking going.
+type gcimports struct {
+       packages map[string]*types2.Package
+}
+
+func (m *gcimports) Import(path string) (*types2.Package, error) {
+       return m.ImportFrom(path, "" /* no vendoring */, 0)
+}
+
+func (m *gcimports) ImportFrom(path, srcDir string, mode types2.ImportMode) (*types2.Package, error) {
+       if mode != 0 {
+               panic("mode must be 0")
+       }
+
+       path, err := resolveImportPath(path)
+       if err != nil {
+               return nil, err
+       }
+
+       lookup := func(path string) (io.ReadCloser, error) { return openPackage(path) }
+       return importer.Import(m.packages, path, srcDir, lookup)
+}
+
 func isDriveLetter(b byte) bool {
        return 'a' <= b && b <= 'z' || 'A' <= b && b <= 'Z'
 }
@@ -38,160 +66,152 @@ func islocalname(name string) bool {
                strings.HasPrefix(name, "../") || name == ".."
 }
 
-func findpkg(name string) (file string, ok bool) {
-       if islocalname(name) {
+func openPackage(path string) (*os.File, error) {
+       if islocalname(path) {
                if base.Flag.NoLocalImports {
-                       return "", false
+                       return nil, errors.New("local imports disallowed")
                }
 
                if base.Flag.Cfg.PackageFile != nil {
-                       file, ok = base.Flag.Cfg.PackageFile[name]
-                       return file, ok
+                       return os.Open(base.Flag.Cfg.PackageFile[path])
                }
 
-               // try .a before .6.  important for building libraries:
-               // if there is an array.6 in the array.a library,
-               // want to find all of array.a, not just array.6.
-               file = fmt.Sprintf("%s.a", name)
-               if _, err := os.Stat(file); err == nil {
-                       return file, true
+               // try .a before .o.  important for building libraries:
+               // if there is an array.o in the array.a library,
+               // want to find all of array.a, not just array.o.
+               if file, err := os.Open(fmt.Sprintf("%s.a", path)); err == nil {
+                       return file, nil
                }
-               file = fmt.Sprintf("%s.o", name)
-               if _, err := os.Stat(file); err == nil {
-                       return file, true
+               if file, err := os.Open(fmt.Sprintf("%s.o", path)); err == nil {
+                       return file, nil
                }
-               return "", false
+               return nil, errors.New("file not found")
        }
 
        // local imports should be canonicalized already.
        // don't want to see "encoding/../encoding/base64"
        // as different from "encoding/base64".
-       if q := path.Clean(name); q != name {
-               base.Errorf("non-canonical import path %q (should be %q)", name, q)
-               return "", false
+       if q := pathpkg.Clean(path); q != path {
+               return nil, fmt.Errorf("non-canonical import path %q (should be %q)", path, q)
        }
 
        if base.Flag.Cfg.PackageFile != nil {
-               file, ok = base.Flag.Cfg.PackageFile[name]
-               return file, ok
+               return os.Open(base.Flag.Cfg.PackageFile[path])
        }
 
        for _, dir := range base.Flag.Cfg.ImportDirs {
-               file = fmt.Sprintf("%s/%s.a", dir, name)
-               if _, err := os.Stat(file); err == nil {
-                       return file, true
+               if file, err := os.Open(fmt.Sprintf("%s/%s.a", dir, path)); err == nil {
+                       return file, nil
                }
-               file = fmt.Sprintf("%s/%s.o", dir, name)
-               if _, err := os.Stat(file); err == nil {
-                       return file, true
+               if file, err := os.Open(fmt.Sprintf("%s/%s.o", dir, path)); err == nil {
+                       return file, nil
                }
        }
 
        if objabi.GOROOT != "" {
                suffix := ""
-               suffixsep := ""
                if base.Flag.InstallSuffix != "" {
-                       suffixsep = "_"
-                       suffix = base.Flag.InstallSuffix
+                       suffix = "_" + base.Flag.InstallSuffix
                } else if base.Flag.Race {
-                       suffixsep = "_"
-                       suffix = "race"
+                       suffix = "_race"
                } else if base.Flag.MSan {
-                       suffixsep = "_"
-                       suffix = "msan"
+                       suffix = "_msan"
                }
 
-               file = fmt.Sprintf("%s/pkg/%s_%s%s%s/%s.a", objabi.GOROOT, objabi.GOOS, objabi.GOARCH, suffixsep, suffix, name)
-               if _, err := os.Stat(file); err == nil {
-                       return file, true
+               if file, err := os.Open(fmt.Sprintf("%s/pkg/%s_%s%s/%s.a", objabi.GOROOT, objabi.GOOS, objabi.GOARCH, suffix, path)); err == nil {
+                       return file, nil
                }
-               file = fmt.Sprintf("%s/pkg/%s_%s%s%s/%s.o", objabi.GOROOT, objabi.GOOS, objabi.GOARCH, suffixsep, suffix, name)
-               if _, err := os.Stat(file); err == nil {
-                       return file, true
+               if file, err := os.Open(fmt.Sprintf("%s/pkg/%s_%s%s/%s.o", objabi.GOROOT, objabi.GOOS, objabi.GOARCH, suffix, path)); err == nil {
+                       return file, nil
                }
        }
-
-       return "", false
+       return nil, errors.New("file not found")
 }
 
 // myheight tracks the local package's height based on packages
 // imported so far.
 var myheight int
 
-func importfile(f constant.Value) *types.Pkg {
-       if f.Kind() != constant.String {
-               base.Errorf("import path must be a string")
-               return nil
-       }
-
-       path_ := constant.StringVal(f)
-       if len(path_) == 0 {
-               base.Errorf("import path is empty")
-               return nil
-       }
-
-       if isbadimport(path_, false) {
-               return nil
-       }
-
+// resolveImportPath resolves an import path as it appears in a Go
+// source file to the package's full path.
+func resolveImportPath(path string) (string, error) {
        // The package name main is no longer reserved,
        // but we reserve the import path "main" to identify
        // the main package, just as we reserve the import
        // path "math" to identify the standard math package.
-       if path_ == "main" {
-               base.Errorf("cannot import \"main\"")
-               base.ErrorExit()
-       }
-
-       if base.Ctxt.Pkgpath != "" && path_ == base.Ctxt.Pkgpath {
-               base.Errorf("import %q while compiling that package (import cycle)", path_)
-               base.ErrorExit()
+       if path == "main" {
+               return "", errors.New("cannot import \"main\"")
        }
 
-       if mapped, ok := base.Flag.Cfg.ImportMap[path_]; ok {
-               path_ = mapped
+       if base.Ctxt.Pkgpath != "" && path == base.Ctxt.Pkgpath {
+               return "", fmt.Errorf("import %q while compiling that package (import cycle)", path)
        }
 
-       if path_ == "unsafe" {
-               return ir.Pkgs.Unsafe
+       if mapped, ok := base.Flag.Cfg.ImportMap[path]; ok {
+               path = mapped
        }
 
-       if islocalname(path_) {
-               if path_[0] == '/' {
-                       base.Errorf("import path cannot be absolute path")
-                       return nil
+       if islocalname(path) {
+               if path[0] == '/' {
+                       return "", errors.New("import path cannot be absolute path")
                }
 
-               prefix := base.Ctxt.Pathname
-               if base.Flag.D != "" {
-                       prefix = base.Flag.D
+               prefix := base.Flag.D
+               if prefix == "" {
+                       // Questionable, but when -D isn't specified, historically we
+                       // resolve local import paths relative to the directory the
+                       // compiler's current directory, not the respective source
+                       // file's directory.
+                       prefix = base.Ctxt.Pathname
                }
-               path_ = path.Join(prefix, path_)
+               path = pathpkg.Join(prefix, path)
 
-               if isbadimport(path_, true) {
-                       return nil
+               if err := checkImportPath(path, true); err != nil {
+                       return "", err
                }
        }
 
-       file, found := findpkg(path_)
-       if !found {
-               base.Errorf("can't find import: %q", path_)
-               base.ErrorExit()
+       return path, nil
+}
+
+// TODO(mdempsky): Return an error instead.
+func importfile(decl *syntax.ImportDecl) *types.Pkg {
+       path, err := strconv.Unquote(decl.Path.Value)
+       if err != nil {
+               base.Errorf("import path must be a string")
+               return nil
        }
 
-       importpkg := types.NewPkg(path_, "")
-       if importpkg.Imported {
-               return importpkg
+       if err := checkImportPath(path, false); err != nil {
+               base.Errorf("%s", err.Error())
+               return nil
+       }
+
+       path, err = resolveImportPath(path)
+       if err != nil {
+               base.Errorf("%s", err)
+               return nil
        }
 
-       importpkg.Imported = true
+       importpkg := types.NewPkg(path, "")
+       if importpkg.Direct {
+               return importpkg // already fully loaded
+       }
+       importpkg.Direct = true
+       typecheck.Target.Imports = append(typecheck.Target.Imports, importpkg)
 
-       imp, err := bio.Open(file)
+       if path == "unsafe" {
+               return importpkg // initialized with universe
+       }
+
+       f, err := openPackage(path)
        if err != nil {
-               base.Errorf("can't open import: %q: %v", path_, err)
+               base.Errorf("could not import %q: %v", path, err)
                base.ErrorExit()
        }
+       imp := bio.NewReader(f)
        defer imp.Close()
+       file := f.Name()
 
        // check object header
        p, err := imp.ReadString('\n')
@@ -261,12 +281,12 @@ func importfile(f constant.Value) *types.Pkg {
        var fingerprint goobj.FingerprintType
        switch c {
        case '\n':
-               base.Errorf("cannot import %s: old export format no longer supported (recompile library)", path_)
+               base.Errorf("cannot import %s: old export format no longer supported (recompile library)", path)
                return nil
 
        case 'B':
                if base.Debug.Export != 0 {
-                       fmt.Printf("importing %s (%s)\n", path_, file)
+                       fmt.Printf("importing %s (%s)\n", path, file)
                }
                imp.ReadByte() // skip \n after $$B
 
@@ -285,17 +305,17 @@ func importfile(f constant.Value) *types.Pkg {
                fingerprint = typecheck.ReadImports(importpkg, imp)
 
        default:
-               base.Errorf("no import in %q", path_)
+               base.Errorf("no import in %q", path)
                base.ErrorExit()
        }
 
        // assume files move (get installed) so don't record the full path
        if base.Flag.Cfg.PackageFile != nil {
                // If using a packageFile map, assume path_ can be recorded directly.
-               base.Ctxt.AddImport(path_, fingerprint)
+               base.Ctxt.AddImport(path, fingerprint)
        } else {
                // For file "/Users/foo/go/pkg/darwin_amd64/math.a" record "math.a".
-               base.Ctxt.AddImport(file[len(file)-len(path_)-len(".a"):], fingerprint)
+               base.Ctxt.AddImport(file[len(file)-len(path)-len(".a"):], fingerprint)
        }
 
        if importpkg.Height >= myheight {
@@ -315,47 +335,37 @@ var reservedimports = []string{
        "type",
 }
 
-func isbadimport(path string, allowSpace bool) bool {
+func checkImportPath(path string, allowSpace bool) error {
+       if path == "" {
+               return errors.New("import path is empty")
+       }
+
        if strings.Contains(path, "\x00") {
-               base.Errorf("import path contains NUL")
-               return true
+               return errors.New("import path contains NUL")
        }
 
        for _, ri := range reservedimports {
                if path == ri {
-                       base.Errorf("import path %q is reserved and cannot be used", path)
-                       return true
+                       return fmt.Errorf("import path %q is reserved and cannot be used", path)
                }
        }
 
        for _, r := range path {
-               if r == utf8.RuneError {
-                       base.Errorf("import path contains invalid UTF-8 sequence: %q", path)
-                       return true
-               }
-
-               if r < 0x20 || r == 0x7f {
-                       base.Errorf("import path contains control character: %q", path)
-                       return true
-               }
-
-               if r == '\\' {
-                       base.Errorf("import path contains backslash; use slash: %q", path)
-                       return true
-               }
-
-               if !allowSpace && unicode.IsSpace(r) {
-                       base.Errorf("import path contains space character: %q", path)
-                       return true
-               }
-
-               if strings.ContainsRune("!\"#$%&'()*,:;<=>?[]^`{|}", r) {
-                       base.Errorf("import path contains invalid character '%c': %q", r, path)
-                       return true
+               switch {
+               case r == utf8.RuneError:
+                       return fmt.Errorf("import path contains invalid UTF-8 sequence: %q", path)
+               case r < 0x20 || r == 0x7f:
+                       return fmt.Errorf("import path contains control character: %q", path)
+               case r == '\\':
+                       return fmt.Errorf("import path contains backslash; use slash: %q", path)
+               case !allowSpace && unicode.IsSpace(r):
+                       return fmt.Errorf("import path contains space character: %q", path)
+               case strings.ContainsRune("!\"#$%&'()*,:;<=>?[]^`{|}", r):
+                       return fmt.Errorf("import path contains invalid character '%c': %q", r, path)
                }
        }
 
-       return false
+       return nil
 }
 
 func pkgnotused(lineno src.XPos, path string, name string) {
index 71a5df082bc9cb0400baa7c1321755128940083f..5a9e37af7d3f052f9f925d7d599eb19d2a7fc653 100644 (file)
@@ -9,7 +9,6 @@ import (
        "fmt"
        "go/constant"
        "go/token"
-       "io"
        "os"
        "path/filepath"
        "runtime"
@@ -20,7 +19,6 @@ import (
 
        "cmd/compile/internal/base"
        "cmd/compile/internal/dwarfgen"
-       "cmd/compile/internal/importer"
        "cmd/compile/internal/ir"
        "cmd/compile/internal/syntax"
        "cmd/compile/internal/typecheck"
@@ -126,13 +124,6 @@ func ParseFiles(filenames []string) (lines uint) {
                        },
                        Importer: &gcimports{
                                packages: make(map[string]*types2.Package),
-                               lookup: func(path string) (io.ReadCloser, error) {
-                                       file, ok := findpkg(path)
-                                       if !ok {
-                                               return nil, fmt.Errorf("can't find import: %q", path)
-                                       }
-                                       return os.Open(file)
-                               },
                        },
                        Sizes: &gcSizes{},
                }
@@ -255,23 +246,6 @@ func Package() {
 
 }
 
-// Temporary import helper to get type2-based type-checking going.
-type gcimports struct {
-       packages map[string]*types2.Package
-       lookup   func(path string) (io.ReadCloser, error)
-}
-
-func (m *gcimports) Import(path string) (*types2.Package, error) {
-       return m.ImportFrom(path, "" /* no vendoring */, 0)
-}
-
-func (m *gcimports) ImportFrom(path, srcDir string, mode types2.ImportMode) (*types2.Package, error) {
-       if mode != 0 {
-               panic("mode must be 0")
-       }
-       return importer.Import(m.packages, path, srcDir, m.lookup)
-}
-
 func (p *noder) errorAt(pos syntax.Pos, format string, args ...interface{}) {
        base.ErrorfAt(p.makeXPos(pos), format, args...)
 }
@@ -483,7 +457,7 @@ func (p *noder) importDecl(imp *syntax.ImportDecl) {
                p.checkUnused(pragma)
        }
 
-       ipkg := importfile(p.basicLit(imp.Path))
+       ipkg := importfile(imp)
        if ipkg == nil {
                if base.Errors() == 0 {
                        base.Fatalf("phase error in import")
@@ -498,11 +472,6 @@ func (p *noder) importDecl(imp *syntax.ImportDecl) {
                p.importedEmbed = true
        }
 
-       if !ipkg.Direct {
-               typecheck.Target.Imports = append(typecheck.Target.Imports, ipkg)
-       }
-       ipkg.Direct = true
-
        var my *types.Sym
        if imp.LocalPkgName != nil {
                my = p.name(imp.LocalPkgName)
index de45d32bfa30e973c0302a319abcc38ef9a0ceb8..a6d2e2007b0424d4927ef5cda15fb67d94d27186 100644 (file)
@@ -31,8 +31,7 @@ type Pkg struct {
        // height of their imported packages.
        Height int
 
-       Imported bool // export data of this package was parsed
-       Direct   bool // imported directly
+       Direct bool // imported directly
 }
 
 // NewPkg returns a new Pkg for the given package path and name.
index 964e5fdf6b7ce843c4e12f99f91b25f41c0c8bb3..f4b65b0f727c705a5e6a17e5602997b2c136e417 100644 (file)
@@ -8,8 +8,7 @@
 
 package main
 
-import _ "unicode//utf8" // GC_ERROR "non-canonical import path .unicode//utf8. \(should be .unicode/utf8.\)" "can't find import: .unicode//utf8."
+import _ "unicode//utf8" // GC_ERROR "non-canonical import path .unicode//utf8. \(should be .unicode/utf8.\)"
 
 func main() {
 }
-
index fcf8a4fcc96f697682f1df5b388166db4bc154c7..5315f9867d6a6596452c19b47df788d53eccfbfd 100644 (file)
@@ -1954,7 +1954,6 @@ var excluded = map[string]bool{
        "fixedbugs/bug388.go":    true, // types2 not run due to syntax errors
        "fixedbugs/bug412.go":    true, // types2 produces a follow-on error
 
-       "fixedbugs/issue11362.go":  true, // types2 import path handling
        "fixedbugs/issue11590.go":  true, // types2 doesn't report a follow-on error (pref: types2)
        "fixedbugs/issue11610.go":  true, // types2 not run after syntax errors
        "fixedbugs/issue11614.go":  true, // types2 reports an extra error