Skip to content

Commit

Permalink
Fix real casing of UNC paths (including mounted paths) (#6028)
Browse files Browse the repository at this point in the history
  • Loading branch information
debonte authored Sep 26, 2023
1 parent a3cdd52 commit 2d23364
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 11 deletions.
22 changes: 14 additions & 8 deletions packages/pyright-internal/src/common/pathUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,24 +92,26 @@ export function getDirectoryPath(pathString: string): string {
return pathString.substr(0, Math.max(getRootLength(pathString), pathString.lastIndexOf(path.sep)));
}

/**
* Returns length of the root part of a path or URL (i.e. length of "/", "x:/", "//server/").
*/
export function getRootLength(pathString: string): number {
if (pathString.charAt(0) === path.sep) {
if (pathString.charAt(1) !== path.sep) {
return 1;
return 1; // POSIX: "/" (or non-normalized "\")
}
const p1 = pathString.indexOf(path.sep, 2);
if (p1 < 0) {
return 2;
}
const p2 = pathString.indexOf(path.sep, p1 + 1);
if (p2 < 0) {
return p1 + 1;
return pathString.length; // UNC: "//server" or "\\server"
}
return p2 + 1;
return p1 + 1; // UNC: "//server/" or "\\server\"
}
if (pathString.charAt(1) === ':') {
if (pathString.charAt(2) === path.sep) {
return 3;
return 3; // DOS: "c:/" or "c:\"
}
if (pathString.length === 2) {
return 2; // DOS: "c:" (but not "c:d")
}
}
return 0;
Expand Down Expand Up @@ -646,6 +648,10 @@ export function getWildcardRegexPattern(rootPath: string, fileSpec: string): str
// Strip the directory separator from the root component.
if (pathComponents.length > 0) {
pathComponents[0] = stripTrailingDirectorySeparator(pathComponents[0]);

if (pathComponents[0].startsWith('\\\\')) {
pathComponents[0] = '\\\\' + pathComponents[0];
}
}

let regExPattern = '';
Expand Down
4 changes: 2 additions & 2 deletions packages/pyright-internal/src/common/realFileSystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
FileWatcherProvider,
nullFileWatcherProvider,
} from './fileWatcher';
import { combinePaths, getDirectoryPath, getFileName, getRootLength } from './pathUtils';
import { combinePaths, getDirectoryPath, getFileName, isDiskPathRoot, isRootedDiskPath } from './pathUtils';

// Automatically remove files created by tmp at process exit.
tmp.setGracefulCleanup();
Expand Down Expand Up @@ -371,7 +371,7 @@ class RealFileSystem implements FileSystem {
try {
// If it doesn't exist in the real FS, try going up a level and combining it.
if (!this.existsSync(path)) {
if (getRootLength(path) <= 0) {
if (!isRootedDiskPath(path) || isDiskPathRoot(path)) {
return path;
}
return combinePaths(this.realCasePath(getDirectoryPath(path)), getFileName(path));
Expand Down
51 changes: 50 additions & 1 deletion packages/pyright-internal/src/tests/pathUtils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
getPathComponents,
getRelativePath,
getRelativePathFromDirectory,
getRootLength,
getWildcardRegexPattern,
getWildcardRoot,
hasTrailingDirectorySeparator,
Expand Down Expand Up @@ -79,6 +80,15 @@ test('getPathComponents5', () => {
assert.equal(components[1], 'hello.py');
});

test('getPathComponents6', () => {
const components = getPathComponents(fixSeparators('//server/share/dir/file.py'));
assert.equal(components.length, 4);
assert.equal(components[0], fixSeparators('//server/'));
assert.equal(components[1], 'share');
assert.equal(components[2], 'dir');
assert.equal(components[3], 'file.py');
});

test('combinePaths1', () => {
const p = combinePaths('/user', '1', '2', '3');
assert.equal(p, normalizeSlashes('/user/1/2/3'));
Expand Down Expand Up @@ -162,6 +172,13 @@ test('getWildcardRegexPattern3', () => {
assert.ok(!regex.test(fixSeparators('/users/me/.blah/foo.py')));
});

test('getWildcardRegexPattern4', () => {
const pattern = getWildcardRegexPattern('//server/share/dir', '.');
const regex = new RegExp(pattern);
assert.ok(regex.test(fixSeparators('//server/share/dir/foo.py')));
assert.ok(!regex.test(fixSeparators('//server/share/dix/foo.py')));
});

test('isDirectoryWildcardPatternPresent1', () => {
const isPresent = isDirectoryWildcardPatternPresent('./**/*.py');
assert.equal(isPresent, true);
Expand Down Expand Up @@ -316,6 +333,34 @@ test('getRelativePathFromDirectory2', () => {
assert.equal(getRelativePathFromDirectory('/a', '/b/c/d', true), normalizeSlashes('../b/c/d'));
});

test('getRootLength1', () => {
assert.equal(getRootLength('a'), 0);
});

test('getRootLength2', () => {
assert.equal(getRootLength(fixSeparators('/')), 1);
});

test('getRootLength3', () => {
assert.equal(getRootLength('c:'), 2);
});

test('getRootLength4', () => {
assert.equal(getRootLength('c:d'), 0);
});

test('getRootLength5', () => {
assert.equal(getRootLength(fixSeparators('c:/')), 3);
});

test('getRootLength6', () => {
assert.equal(getRootLength(fixSeparators('//server')), 8);
});

test('getRootLength7', () => {
assert.equal(getRootLength(fixSeparators('//server/share')), 9);
});

test('isRootedDiskPath1', () => {
assert(isRootedDiskPath(normalizeSlashes('C:/a/b')));
});
Expand All @@ -337,7 +382,11 @@ test('isDiskPathRoot2', () => {
});

test('isDiskPathRoot3', () => {
assert(!isRootedDiskPath(normalizeSlashes('c:')));
assert(isRootedDiskPath(normalizeSlashes('c:')));
});

test('isDiskPathRoot4', () => {
assert(!isRootedDiskPath(normalizeSlashes('c:d')));
});

test('getRelativePath', () => {
Expand Down

0 comments on commit 2d23364

Please sign in to comment.