Skip to content

Commit 02f1738

Browse files
committed
internal/span: always uppercase the drive letter for Windows
Drive letters are always case-insensitive, so we should standardize them by always keeping them uppercase. Updates golang/go#36904 Change-Id: I8de25b175790b01627f947600c1511edf38c316c Reviewed-on: https://go-review.googlesource.com/c/tools/+/217080 Run-TryBot: Rebecca Stambler <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Heschi Kreinick <[email protected]> (cherry picked from commit 0725381) Reviewed-on: https://go-review.googlesource.com/c/tools/+/217088 Reviewed-by: Michael Matloob <[email protected]>
1 parent 32d82c0 commit 02f1738

File tree

3 files changed

+144
-36
lines changed

3 files changed

+144
-36
lines changed

internal/span/uri.go

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,12 @@ func filename(uri URI) (string, error) {
4141
if u.Scheme != fileScheme {
4242
return "", fmt.Errorf("only file URIs are supported, got %q from %q", u.Scheme, uri)
4343
}
44-
if isWindowsDriveURI(u.Path) {
45-
u.Path = u.Path[1:]
44+
// If the URI is a Windows URI, we trim the leading "/" and lowercase
45+
// the drive letter, which will never be case sensitive.
46+
if isWindowsDriveURIPath(u.Path) {
47+
u.Path = strings.ToUpper(string(u.Path[1])) + u.Path[2:]
4648
}
49+
4750
return u.Path, nil
4851
}
4952

@@ -53,7 +56,16 @@ func NewURI(s string) URI {
5356
if u, err := url.PathUnescape(s); err == nil {
5457
s = u
5558
}
59+
// If a path has a scheme, it is already a URI.
60+
// We only handle the file:// scheme.
5661
if strings.HasPrefix(s, fileScheme+"://") {
62+
// File URIs from Windows may have lowercase drive letters.
63+
// Since drive letters are guaranteed to be case insensitive,
64+
// we change them to uppercase to remain consistent.
65+
// For example, file:///c:/x/y/z becomes file:///C:/x/y/z.
66+
if i := len(fileScheme + "://"); isWindowsDriveURIPath(s[i:]) {
67+
s = s[:i+1] + strings.ToUpper(string(s[i+1])) + s[i+2:]
68+
}
5769
return URI(s)
5870
}
5971
return FileURI(s)
@@ -117,7 +129,7 @@ func FileURI(path string) URI {
117129
}
118130
// Check the file path again, in case it became absolute.
119131
if isWindowsDrivePath(path) {
120-
path = "/" + path
132+
path = "/" + strings.ToUpper(string(path[0])) + path[1:]
121133
}
122134
path = filepath.ToSlash(path)
123135
u := url.URL{
@@ -133,18 +145,18 @@ func FileURI(path string) URI {
133145

134146
// isWindowsDrivePath returns true if the file path is of the form used by
135147
// Windows. We check if the path begins with a drive letter, followed by a ":".
148+
// For example: C:/x/y/z.
136149
func isWindowsDrivePath(path string) bool {
137-
if len(path) < 4 {
150+
if len(path) < 3 {
138151
return false
139152
}
140153
return unicode.IsLetter(rune(path[0])) && path[1] == ':'
141154
}
142155

143156
// isWindowsDriveURI returns true if the file URI is of the format used by
144157
// Windows URIs. The url.Parse package does not specially handle Windows paths
145-
// (see https://golang.org/issue/6027). We check if the URI path has
146-
// a drive prefix (e.g. "/C:"). If so, we trim the leading "/".
147-
func isWindowsDriveURI(uri string) bool {
158+
// (see golang/go#6027). We check if the URI path has a drive prefix (e.g. "/C:").
159+
func isWindowsDriveURIPath(uri string) bool {
148160
if len(uri) < 4 {
149161
return false
150162
}

internal/span/uri_test.go

Lines changed: 52 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22
// Use of this source code is governed by a BSD-style
33
// license that can be found in the LICENSE file.
44

5+
// +build !windows
6+
57
package span_test
68

79
import (
8-
"path/filepath"
910
"testing"
1011

1112
"golang.org/x/tools/internal/span"
@@ -16,36 +17,58 @@ import (
1617
// tests by using only forward slashes, assuming that the standard library
1718
// functions filepath.ToSlash and filepath.FromSlash do not need testing.
1819
func TestURI(t *testing.T) {
19-
for _, test := range []string{
20-
``,
21-
`C:/Windows/System32`,
22-
`C:/Go/src/bob.go`,
23-
`c:/Go/src/bob.go`,
24-
`/path/to/dir`,
25-
`/a/b/c/src/bob.go`,
26-
`c:/Go/src/bob george/george/george.go`,
20+
for _, test := range []struct {
21+
path, wantFile string
22+
wantURI span.URI
23+
}{
24+
{
25+
path: ``,
26+
wantFile: ``,
27+
wantURI: span.URI(""),
28+
},
29+
{
30+
path: `C:/Windows/System32`,
31+
wantFile: `C:/Windows/System32`,
32+
wantURI: span.URI("file:///C:/Windows/System32"),
33+
},
34+
{
35+
path: `C:/Go/src/bob.go`,
36+
wantFile: `C:/Go/src/bob.go`,
37+
wantURI: span.URI("file:///C:/Go/src/bob.go"),
38+
},
39+
{
40+
path: `c:/Go/src/bob.go`,
41+
wantFile: `C:/Go/src/bob.go`,
42+
wantURI: span.URI("file:///C:/Go/src/bob.go"),
43+
},
44+
{
45+
path: `/path/to/dir`,
46+
wantFile: `/path/to/dir`,
47+
wantURI: span.URI("file:///path/to/dir"),
48+
},
49+
{
50+
path: `/a/b/c/src/bob.go`,
51+
wantFile: `/a/b/c/src/bob.go`,
52+
wantURI: span.URI("file:///a/b/c/src/bob.go"),
53+
},
54+
{
55+
path: `c:/Go/src/bob george/george/george.go`,
56+
wantFile: `C:/Go/src/bob george/george/george.go`,
57+
wantURI: span.URI("file:///C:/Go/src/bob george/george/george.go"),
58+
},
59+
{
60+
path: `file:///c:/Go/src/bob george/george/george.go`,
61+
wantFile: `C:/Go/src/bob george/george/george.go`,
62+
wantURI: span.URI("file:///C:/Go/src/bob george/george/george.go"),
63+
},
2764
} {
28-
testPath := filepath.FromSlash(test)
29-
expectPath := testPath
30-
if len(test) > 0 && test[0] == '/' {
31-
if abs, err := filepath.Abs(expectPath); err == nil {
32-
expectPath = abs
33-
}
34-
}
35-
expectURI := filepath.ToSlash(expectPath)
36-
if len(expectURI) > 0 {
37-
if expectURI[0] != '/' {
38-
expectURI = "/" + expectURI
39-
}
40-
expectURI = "file://" + expectURI
41-
}
42-
uri := span.FileURI(testPath)
43-
if expectURI != string(uri) {
44-
t.Errorf("ToURI: expected %s, got %s", expectURI, uri)
65+
got := span.NewURI(test.path)
66+
if got != test.wantURI {
67+
t.Errorf("ToURI: got %s, expected %s", got, test.wantURI)
4568
}
46-
filename := uri.Filename()
47-
if expectPath != filename {
48-
t.Errorf("Filename: expected %s, got %s", expectPath, filename)
69+
gotFilename := got.Filename()
70+
if gotFilename != test.wantFile {
71+
t.Errorf("Filename: got %s, expected %s", gotFilename, test.wantFile)
4972
}
5073
}
5174
}

internal/span/uri_windows_test.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Copyright 2020 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
// +build windows
6+
7+
package span_test
8+
9+
import (
10+
"testing"
11+
12+
"golang.org/x/tools/internal/span"
13+
)
14+
15+
// TestURI tests the conversion between URIs and filenames. The test cases
16+
// include Windows-style URIs and filepaths, but we avoid having OS-specific
17+
// tests by using only forward slashes, assuming that the standard library
18+
// functions filepath.ToSlash and filepath.FromSlash do not need testing.
19+
func TestURI(t *testing.T) {
20+
for _, test := range []struct {
21+
path, wantFile string
22+
wantURI span.URI
23+
}{
24+
{
25+
path: ``,
26+
wantFile: ``,
27+
wantURI: span.URI(""),
28+
},
29+
{
30+
path: `C:\Windows\System32`,
31+
wantFile: `C:\Windows\System32`,
32+
wantURI: span.URI("file:///C:/Windows/System32"),
33+
},
34+
{
35+
path: `C:\Go\src\bob.go`,
36+
wantFile: `C:\Go\src\bob.go`,
37+
wantURI: span.URI("file:///C:/Go/src/bob.go"),
38+
},
39+
{
40+
path: `c:\Go\src\bob.go`,
41+
wantFile: `C:\Go\src\bob.go`,
42+
wantURI: span.URI("file:///C:/Go/src/bob.go"),
43+
},
44+
{
45+
path: `\path\to\dir`,
46+
wantFile: `C:\path\to\dir`,
47+
wantURI: span.URI("file:///C:/path/to/dir"),
48+
},
49+
{
50+
path: `\a\b\c\src\bob.go`,
51+
wantFile: `C:\a\b\c\src\bob.go`,
52+
wantURI: span.URI("file:///C:/a/b/c/src/bob.go"),
53+
},
54+
{
55+
path: `c:\Go\src\bob george\george\george.go`,
56+
wantFile: `C:\Go\src\bob george\george\george.go`,
57+
wantURI: span.URI("file:///C:/Go/src/bob george/george/george.go"),
58+
},
59+
{
60+
path: `file:///c:/Go/src/bob george/george/george.go`,
61+
wantFile: `C:\Go\src\bob george\george\george.go`,
62+
wantURI: span.URI("file:///C:/Go/src/bob george/george/george.go"),
63+
},
64+
} {
65+
got := span.NewURI(test.path)
66+
if got != test.wantURI {
67+
t.Errorf("ToURI: got %s, expected %s", got, test.wantURI)
68+
}
69+
if got.Filename() != test.wantFile {
70+
t.Errorf("Filename: got %s, expected %s", got.Filename(), test.wantFile)
71+
}
72+
}
73+
}

0 commit comments

Comments
 (0)