internal/lsp/cache: check for symlinks when checking "isSubdirectory"

This change copies the logic from the go command's inDir function
(https://cs.opensource.google/go/go/+/master:src/cmd/go/internal/search/search.go;drc=3931cc113f3f3e7d484842d6e4f53b7a78311e8e;l=570)
to replace gopls's "isSubdirectory" function. This function resolves
symlinks, which isSubdirectory did not previously do.

The only adjustments are to flip the arguments to match the previous
signature of isSubdirectory and to return a boolean instead of a string.

Fixes golang/go#38558

Change-Id: I9c64604222ac277eae81a4111eef432ead887e9f
Reviewed-on: https://go-review.googlesource.com/c/tools/+/266200
Trust: Rebecca Stambler <rstambler@golang.org>
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
This commit is contained in:
Rebecca Stambler 2020-10-28 22:47:46 -04:00
parent 8860a70d10
commit b53d4cbd60
3 changed files with 76 additions and 7 deletions

View File

@ -694,7 +694,7 @@ func (s *snapshot) CachedImportPaths(ctx context.Context) (map[string]source.Pac
func (s *snapshot) GoModForFile(ctx context.Context, uri span.URI) span.URI {
var match span.URI
for modURI := range s.workspace.activeModFiles() {
if !isSubdirectory(dirURI(modURI).Filename(), uri.Filename()) {
if !inDir(dirURI(modURI).Filename(), uri.Filename()) {
continue
}
if len(modURI) > len(match) {
@ -1377,7 +1377,7 @@ func (s *snapshot) shouldInvalidateMetadata(ctx context.Context, newSnapshot *sn
}
// If a go.mod in the workspace has been changed, invalidate metadata.
if kind := originalFH.Kind(); kind == source.Mod {
return isSubdirectory(filepath.Dir(s.view.rootURI.Filename()), filepath.Dir(originalFH.URI().Filename()))
return inDir(filepath.Dir(s.view.rootURI.Filename()), filepath.Dir(originalFH.URI().Filename()))
}
// Get the original and current parsed files in order to check package name
// and imports. Use the new snapshot to parse to avoid modifying the

View File

@ -698,16 +698,85 @@ func validBuildConfiguration(folder span.URI, ws *workspaceInformation, modFiles
// The user may have a multiple directories in their GOPATH.
// Check if the workspace is within any of them.
for _, gp := range filepath.SplitList(ws.gopath) {
if isSubdirectory(filepath.Join(gp, "src"), folder.Filename()) {
if inDir(filepath.Join(gp, "src"), folder.Filename()) {
return true
}
}
return false
}
func isSubdirectory(root, leaf string) bool {
rel, err := filepath.Rel(root, leaf)
return err == nil && !strings.HasPrefix(rel, "..")
// Copied and slightly adjusted from go/src/cmd/go/internal/search/search.go.
//
// inDir checks whether path is in the file tree rooted at dir.
// If so, InDir returns an equivalent path relative to dir.
// If not, InDir returns an empty string.
// InDir makes some effort to succeed even in the presence of symbolic links.
func inDir(dir, path string) bool {
if rel := inDirLex(path, dir); rel != "" {
return true
}
xpath, err := filepath.EvalSymlinks(path)
if err != nil || xpath == path {
xpath = ""
} else {
if rel := inDirLex(xpath, dir); rel != "" {
return true
}
}
xdir, err := filepath.EvalSymlinks(dir)
if err == nil && xdir != dir {
if rel := inDirLex(path, xdir); rel != "" {
return true
}
if xpath != "" {
if rel := inDirLex(xpath, xdir); rel != "" {
return true
}
}
}
return false
}
// Copied from go/src/cmd/go/internal/search/search.go.
//
// inDirLex is like inDir but only checks the lexical form of the file names.
// It does not consider symbolic links.
// TODO(rsc): This is a copy of str.HasFilePathPrefix, modified to
// return the suffix. Most uses of str.HasFilePathPrefix should probably
// be calling InDir instead.
func inDirLex(path, dir string) string {
pv := strings.ToUpper(filepath.VolumeName(path))
dv := strings.ToUpper(filepath.VolumeName(dir))
path = path[len(pv):]
dir = dir[len(dv):]
switch {
default:
return ""
case pv != dv:
return ""
case len(path) == len(dir):
if path == dir {
return "."
}
return ""
case dir == "":
return path
case len(path) > len(dir):
if dir[len(dir)-1] == filepath.Separator {
if path[:len(dir)] == dir {
return path[len(dir):]
}
return ""
}
if path[len(dir)] == filepath.Separator && path[:len(dir)] == dir {
if len(path) == len(dir)+1 {
return "."
}
return path[len(dir)+1:]
}
return ""
}
}
// getGoEnv gets the view's various GO* values.

View File

@ -258,7 +258,7 @@ func (wm *workspace) invalidate(ctx context.Context, changes map[span.URI]*fileC
// Legacy mode only considers a module a workspace root.
continue
}
if !isSubdirectory(wm.root.Filename(), uri.Filename()) {
if !inDir(wm.root.Filename(), uri.Filename()) {
// Otherwise, the module must be contained within the workspace root.
continue
}