Skip to content

Commit b53d4cb

Browse files
committed
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 <[email protected]> Run-TryBot: Rebecca Stambler <[email protected]> gopls-CI: kokoro <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Heschi Kreinick <[email protected]>
1 parent 8860a70 commit b53d4cb

File tree

3 files changed

+76
-7
lines changed

3 files changed

+76
-7
lines changed

internal/lsp/cache/snapshot.go

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

internal/lsp/cache/view.go

+73-4
Original file line numberDiff line numberDiff line change
@@ -698,16 +698,85 @@ func validBuildConfiguration(folder span.URI, ws *workspaceInformation, modFiles
698698
// The user may have a multiple directories in their GOPATH.
699699
// Check if the workspace is within any of them.
700700
for _, gp := range filepath.SplitList(ws.gopath) {
701-
if isSubdirectory(filepath.Join(gp, "src"), folder.Filename()) {
701+
if inDir(filepath.Join(gp, "src"), folder.Filename()) {
702702
return true
703703
}
704704
}
705705
return false
706706
}
707707

708-
func isSubdirectory(root, leaf string) bool {
709-
rel, err := filepath.Rel(root, leaf)
710-
return err == nil && !strings.HasPrefix(rel, "..")
708+
// Copied and slightly adjusted from go/src/cmd/go/internal/search/search.go.
709+
//
710+
// inDir checks whether path is in the file tree rooted at dir.
711+
// If so, InDir returns an equivalent path relative to dir.
712+
// If not, InDir returns an empty string.
713+
// InDir makes some effort to succeed even in the presence of symbolic links.
714+
func inDir(dir, path string) bool {
715+
if rel := inDirLex(path, dir); rel != "" {
716+
return true
717+
}
718+
xpath, err := filepath.EvalSymlinks(path)
719+
if err != nil || xpath == path {
720+
xpath = ""
721+
} else {
722+
if rel := inDirLex(xpath, dir); rel != "" {
723+
return true
724+
}
725+
}
726+
727+
xdir, err := filepath.EvalSymlinks(dir)
728+
if err == nil && xdir != dir {
729+
if rel := inDirLex(path, xdir); rel != "" {
730+
return true
731+
}
732+
if xpath != "" {
733+
if rel := inDirLex(xpath, xdir); rel != "" {
734+
return true
735+
}
736+
}
737+
}
738+
return false
739+
}
740+
741+
// Copied from go/src/cmd/go/internal/search/search.go.
742+
//
743+
// inDirLex is like inDir but only checks the lexical form of the file names.
744+
// It does not consider symbolic links.
745+
// TODO(rsc): This is a copy of str.HasFilePathPrefix, modified to
746+
// return the suffix. Most uses of str.HasFilePathPrefix should probably
747+
// be calling InDir instead.
748+
func inDirLex(path, dir string) string {
749+
pv := strings.ToUpper(filepath.VolumeName(path))
750+
dv := strings.ToUpper(filepath.VolumeName(dir))
751+
path = path[len(pv):]
752+
dir = dir[len(dv):]
753+
switch {
754+
default:
755+
return ""
756+
case pv != dv:
757+
return ""
758+
case len(path) == len(dir):
759+
if path == dir {
760+
return "."
761+
}
762+
return ""
763+
case dir == "":
764+
return path
765+
case len(path) > len(dir):
766+
if dir[len(dir)-1] == filepath.Separator {
767+
if path[:len(dir)] == dir {
768+
return path[len(dir):]
769+
}
770+
return ""
771+
}
772+
if path[len(dir)] == filepath.Separator && path[:len(dir)] == dir {
773+
if len(path) == len(dir)+1 {
774+
return "."
775+
}
776+
return path[len(dir)+1:]
777+
}
778+
return ""
779+
}
711780
}
712781

713782
// getGoEnv gets the view's various GO* values.

internal/lsp/cache/workspace.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ func (wm *workspace) invalidate(ctx context.Context, changes map[span.URI]*fileC
258258
// Legacy mode only considers a module a workspace root.
259259
continue
260260
}
261-
if !isSubdirectory(wm.root.Filename(), uri.Filename()) {
261+
if !inDir(wm.root.Filename(), uri.Filename()) {
262262
// Otherwise, the module must be contained within the workspace root.
263263
continue
264264
}

0 commit comments

Comments
 (0)