cue/load: cleanup and made code more defensive

It is important that code does not search past
the module root. Added some safety checks
to make the code more robust, factored out
and simplified some code.

Change-Id: I076d82490d6f141846619e7648add3ade7313df5
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/4240
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cue/load/config.go b/cue/load/config.go
index 09cbb60..dc752ce 100644
--- a/cue/load/config.go
+++ b/cue/load/config.go
@@ -369,12 +369,10 @@
 	// pkgname.cue.mod
 	// Look to see if there is a cue.mod.
 	if c.ModuleRoot == "" {
-		abs, err := c.findRoot(c.Dir)
-		if err != nil {
-			// Not using modules: only consider the current directory.
-			c.ModuleRoot = c.Dir
-		} else {
-			c.ModuleRoot = abs
+		// Only consider the current directory by default
+		c.ModuleRoot = c.Dir
+		if root := c.findRoot(c.Dir); root != "" {
+			c.ModuleRoot = root
 		}
 	}
 
@@ -422,22 +420,32 @@
 	return &c, nil
 }
 
-func (c Config) findRoot(dir string) (string, error) {
+func (c Config) isRoot(dir string) bool {
+	fs := &c.fileSystem
+	// Note: cue.mod used to be a file. We still allow both to match.
+	_, err := fs.stat(filepath.Join(dir, modDir))
+	return err == nil
+}
+
+// findRoot returns the module root or "" if none was found.
+func (c Config) findRoot(dir string) string {
 	fs := &c.fileSystem
 
 	absDir, err := filepath.Abs(dir)
 	if err != nil {
-		return "", err
+		return ""
 	}
 	abs := absDir
 	for {
-		_, err := fs.stat(filepath.Join(abs, modDir))
-		if err == nil {
-			// Note: cue.mod used to be a file. We still allow
-			// both to match.
-			return abs, nil
+		if c.isRoot(abs) {
+			return abs
 		}
 		d := filepath.Dir(abs)
+		if filepath.Base(filepath.Dir(abs)) == modDir {
+			// The package was located within a "cue.mod" dir and there was
+			// not cue.mod found until now. So there is no root.
+			return ""
+		}
 		if len(d) >= len(abs) {
 			break // reached top of file system, no cue.mod
 		}
@@ -449,11 +457,11 @@
 	for {
 		info, err := fs.stat(filepath.Join(abs, pkgDir))
 		if err == nil && info.IsDir() {
-			return abs, nil
+			return abs
 		}
 		d := filepath.Dir(abs)
 		if len(d) >= len(abs) {
-			return "", err // reached top of file system, no pkg dir.
+			return "" // reached top of file system, no pkg dir.
 		}
 		abs = d
 	}
diff --git a/cue/load/import.go b/cue/load/import.go
index 4adbade..afb5255 100644
--- a/cue/load/import.go
+++ b/cue/load/import.go
@@ -129,8 +129,18 @@
 		return p
 	}
 
+	// This algorithm assumes that multiple directories within cue.mod/*/
+	// have the same module scope and that there are no invalid modules.
+	inModule := false
 	for _, d := range dirs {
-		for dir := d[1]; ctxt.isDir(dir); {
+		if l.cfg.findRoot(d[1]) != "" {
+			inModule = true
+			break
+		}
+	}
+
+	for _, d := range dirs {
+		for dir := filepath.Clean(d[1]); ctxt.isDir(dir); {
 			files, err := ctxt.readDir(dir)
 			if err != nil && !os.IsNotExist(err) {
 				p.ReportError(errors.Wrapf(err, pos, "import failed reading dir %v", dirs[0][1]))
@@ -143,7 +153,7 @@
 				fp.add(pos, dir, f.Name(), importComment)
 			}
 
-			if filepath.Clean(dir) == d[0] || fp.pkg.PkgName == "" {
+			if fp.pkg.PkgName == "" || !inModule || l.cfg.isRoot(dir) || dir == d[0] {
 				break
 			}
 
@@ -151,8 +161,10 @@
 			// package.
 			fp.ignoreOther = true
 
-			parent, _ := filepath.Split(filepath.Clean(dir))
-			if parent == dir {
+			parent, _ := filepath.Split(dir)
+			parent = filepath.Clean(parent)
+
+			if parent == dir || len(parent) < len(d[0]) {
 				break
 			}
 			dir = parent