Skip to content

Commit 39fa7dd

Browse files
committed
Fix -Wconf:src Windows path conversion
Updates the -Wconf:src filter to avoid using `java.nio.file.Path.toURI` in order to fix Windows source path conversions. `Path.toURI` prepends the current working directory to Windows-like paths unconditionally, and converts backslashes in such paths to `%5C` escape sequences This can cause `-Wconf:src` filters that work on non-Windows platforms to fail on Windows. For example, before this change, the `Path.toURI` conversion in the `SourcePattern` case from `MessageFilter.matches()` produced: ```txt original: Optional[C:\foo\bar\myfile.scala] resolved: /Users/mbland/src/scala/scala3/C:%5Cfoo%5Cbar%5Cmyfile.scala ``` After this change, it produces the following, which still prepends the current working directory, but properly converts path separators to `/`: ```txt original: Optional[C:\foo\bar\myfile.scala] resolved: /Users/mbland/src/scala/scala3/C:/foo/bar/myfile.scala ``` This change is based on scala/scala#11192, and also adapts some test cases from that pull request to validate symlink and normalized path handling. This change also extracts the `diagnosticWarning` helper method to reduce duplication between new and existing test cases.
1 parent c3f9d62 commit 39fa7dd

File tree

2 files changed

+59
-34
lines changed

2 files changed

+59
-34
lines changed

compiler/src/dotty/tools/dotc/reporting/WConf.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,9 @@ enum MessageFilter:
3030
case SourcePattern(pattern) =>
3131
val source = message.position.orElse(NoSourcePosition).source()
3232
val path = source.jfile()
33-
.map(_.toPath.toAbsolutePath.toUri.normalize().getRawPath)
33+
.map(_.toPath.toAbsolutePath.normalize.toString)
3434
.orElse(source.path())
35+
.replace("\\", "/")
3536
pattern.findFirstIn(path).nonEmpty
3637
case Origin(pattern) =>
3738
message match

compiler/test/dotty/tools/dotc/config/ScalaSettingsTests.scala

Lines changed: 57 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ import core.Decorators.toMessage
1010
import dotty.tools.io.{Path, PlainFile}
1111

1212
import java.net.URI
13-
import java.nio.file.Files
14-
import scala.util.Using
13+
import java.nio.file.{Files, Paths}
14+
import scala.util.{Success, Using}
1515

1616
import scala.annotation.nowarn
1717

@@ -215,28 +215,25 @@ class ScalaSettingsTests:
215215
val wconf = reporting.WConf.fromSettings(wconfStr)
216216
wconf.map(_.action(warning))
217217

218+
private def diagnosticWarning(source: util.SourceFile) = reporting.Diagnostic.Warning(
219+
"A warning".toMessage,
220+
util.SourcePosition(source = source, span = util.Spans.Span(1L))
221+
)
222+
218223
@Test def `WConf src filter silences warnings from a matching path for virtual file`: Unit =
219224
val result = wconfSrcFilterTest(
220225
argsStr = "-Wconf:src=path/.*:s",
221-
warning = reporting.Diagnostic.Warning(
222-
"A warning".toMessage,
223-
util.SourcePosition(
224-
source = util.SourceFile.virtual(new URI("file:///some/path/file.scala"), ""),
225-
span = util.Spans.Span(1L)
226-
)
226+
warning = diagnosticWarning(
227+
util.SourceFile.virtual(new URI("file:///some/path/file.scala"), "")
227228
)
228229
)
229230
assertEquals(result, Right(reporting.Action.Silent))
230231

231232
@Test def `WConf src filter doesn't silence warnings from a non-matching path`: Unit =
232233
val result = wconfSrcFilterTest(
233234
argsStr = "-Wconf:src=another/.*:s",
234-
warning = reporting.Diagnostic.Warning(
235-
"A warning".toMessage,
236-
util.SourcePosition(
237-
source = util.SourceFile.virtual(new URI("file:///some/path/file.scala"), ""),
238-
span = util.Spans.Span(1L)
239-
)
235+
warning = diagnosticWarning(
236+
util.SourceFile.virtual(new URI("file:///some/path/file.scala"), "")
240237
)
241238
)
242239
assertEquals(result, Right(reporting.Action.Warning))
@@ -245,12 +242,8 @@ class ScalaSettingsTests:
245242
val result = Using.resource(Files.createTempFile("myfile", ".scala").nn) { file =>
246243
wconfSrcFilterTest(
247244
argsStr = "-Wconf:src=myfile.*?\\.scala:s",
248-
warning = reporting.Diagnostic.Warning(
249-
"A warning".toMessage,
250-
util.SourcePosition(
251-
source = util.SourceFile(new PlainFile(Path(file)), "UTF-8"),
252-
span = util.Spans.Span(1L)
253-
)
245+
warning = diagnosticWarning(
246+
util.SourceFile(new PlainFile(Path(file)), "UTF-8")
254247
)
255248
)
256249
}(using Files.deleteIfExists(_))
@@ -260,27 +253,58 @@ class ScalaSettingsTests:
260253
val result = Using.resource(Files.createTempFile("myfile", ".scala").nn) { file =>
261254
wconfSrcFilterTest(
262255
argsStr = "-Wconf:src=another.*?\\.scala:s",
263-
warning = reporting.Diagnostic.Warning(
264-
"A warning".toMessage,
265-
util.SourcePosition(
266-
source = util.SourceFile(new PlainFile(Path(file)), "UTF-8"),
267-
span = util.Spans.Span(1L)
268-
)
256+
warning = diagnosticWarning(
257+
util.SourceFile(new PlainFile(Path(file)), "UTF-8")
269258
)
270259
)
271260
}(using Files.deleteIfExists(_))
272261
assertEquals(result, Right(reporting.Action.Warning))
273262

263+
@Test def `Wconf src filter matches symbolic links without resolving them`: Unit =
264+
val result = Using.Manager { use =>
265+
def f(file: java.nio.file.Path) = use(file)(using Files.deleteIfExists(_))
266+
267+
val tempDir = f(Files.createTempDirectory("wconf-src-symlink-test"))
268+
val externalDir = f(Files.createDirectory(Paths.get(tempDir.toString, "external")))
269+
val cacheDir = f(Files.createDirectory(Paths.get(tempDir.toString, "cache")))
270+
val actualFile = f(Files.createFile(Paths.get(cacheDir.toString, "myfile.scala")))
271+
val symlinkPath = Paths.get(externalDir.toString, "myfile.scala")
272+
273+
// This may fail with an IOException if symlinks are disabled on Windows:
274+
// https://docs.oracle.com/javase/8/docs/api/java/nio/file/Files.html#createSymbolicLink-java.nio.file.Path-java.nio.file.Path-java.nio.file.attribute.FileAttribute...-
275+
val symlink = f(Files.createSymbolicLink(symlinkPath, actualFile))
276+
277+
wconfSrcFilterTest(
278+
argsStr = "-Wconf:src=external/.*\\.scala:s",
279+
warning = diagnosticWarning(
280+
util.SourceFile(new PlainFile(Path(symlink)), "UTF-8"),
281+
)
282+
)
283+
}
284+
assertEquals(result, Success(Right(reporting.Action.Silent)))
285+
286+
@Test def `Wconf src filter handles Windows paths`: Unit =
287+
val path = Path("C:\\foo\\bar\\myfile.scala")
288+
val result = wconfSrcFilterTest(
289+
argsStr = "-Wconf:src=foo/bar/.*\\.scala:s",
290+
warning = diagnosticWarning(util.SourceFile(new PlainFile(path), "UTF-8"))
291+
)
292+
assertEquals(result, Right(reporting.Action.Silent))
293+
294+
@Test def `Wconf src filter normalizes paths`: Unit =
295+
val path = Path("foo/./bar/../quux/../baz/File.scala")
296+
val result = wconfSrcFilterTest(
297+
argsStr = "-Wconf:src=foo/baz/.*\\.scala:s",
298+
warning = diagnosticWarning(util.SourceFile(new PlainFile(path), "UTF-8"))
299+
)
300+
assertEquals(result, Right(reporting.Action.Silent))
301+
274302
@Test def `WConf src filter reports an error on an invalid regex`: Unit =
275303
val result = wconfSrcFilterTest(
276304
argsStr = """-Wconf:src=\:s""",
277-
warning = reporting.Diagnostic.Warning(
278-
"A warning".toMessage,
279-
util.SourcePosition(
280-
source = util.SourceFile.virtual(new URI("file:///some/path/file.scala"), ""),
281-
span = util.Spans.Span(1L)
282-
)
283-
),
305+
warning = diagnosticWarning(
306+
util.SourceFile.virtual(new URI("file:///some/path/file.scala"), "")
307+
)
284308
)
285309
assertTrue(
286310
result.left.exists(errors =>

0 commit comments

Comments
 (0)