Skip to content

Commit e1d9f9c

Browse files
Merge pull request #10447 from dotty-staging/fix-10430
Fix #10430: relativize path in tasty
2 parents 67d9790 + a688003 commit e1d9f9c

File tree

11 files changed

+86
-59
lines changed

11 files changed

+86
-59
lines changed

compiler/src/dotty/tools/dotc/core/tasty/PositionPickler.scala

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,21 @@ import dotty.tools.tasty.TastyBuffer
88
import TastyBuffer._
99

1010
import ast._
11-
import ast.Trees._
12-
import ast.Trees.WithLazyField
11+
import Trees.WithLazyField
1312
import util.{SourceFile, NoSource}
1413
import core._
15-
import Contexts._, Symbols._, Annotations._, Decorators._
14+
import Annotations._, Decorators._
1615
import collection.mutable
1716
import util.Spans._
1817

1918
class PositionPickler(
2019
pickler: TastyPickler,
2120
addrOfTree: PositionPickler.TreeToAddr,
22-
treeAnnots: untpd.MemberDef => List[tpd.Tree]) {
21+
treeAnnots: untpd.MemberDef => List[tpd.Tree],
22+
relativePathReference: String){
2323

2424
import ast.tpd._
25+
2526
val buf: TastyBuffer = new TastyBuffer(5000)
2627
pickler.newSection(PositionsSection, buf)
2728

@@ -69,19 +70,8 @@ class PositionPickler(
6970

7071
def pickleSource(source: SourceFile): Unit = {
7172
buf.writeInt(SOURCE)
72-
val pathName = source.path
73-
val pickledPath =
74-
val originalPath = java.nio.file.Paths.get(pathName.toString).normalize()
75-
if originalPath.isAbsolute then
76-
val path = originalPath.toAbsolutePath().normalize()
77-
val cwd = java.nio.file.Paths.get("").toAbsolutePath().normalize()
78-
try cwd.relativize(path)
79-
catch case _: IllegalArgumentException =>
80-
warnings += "Could not relativize path for pickling: " + originalPath
81-
originalPath
82-
else
83-
originalPath
84-
buf.writeInt(pickler.nameBuffer.nameIndex(pickledPath.toString.toTermName).index)
73+
val relativePath = SourceFile.relativePath(source, relativePathReference)
74+
buf.writeInt(pickler.nameBuffer.nameIndex(relativePath.toTermName).index)
8575
}
8676

8777
/** True if x's position shouldn't be reconstructed automatically from its initial span

compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,16 @@ class TastyPrinter(bytes: Array[Byte]) {
152152
sb.append(treeStr("%10d".format(addr.index)))
153153
sb.append(s": ${offsetToInt(pos.start)} .. ${pos.end}\n")
154154
}
155+
156+
val sources = posUnpickler.sourcePaths
157+
sb.append(s"\n source paths:\n")
158+
val sortedPath = sources.toSeq.sortBy(_._1.index)
159+
for ((addr, path) <- sortedPath) {
160+
sb.append(treeStr("%10d: ".format(addr.index)))
161+
sb.append(path)
162+
sb.append("\n")
163+
}
164+
155165
sb.result
156166
}
157167
}

compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,8 @@ object PickledQuotes {
167167
treePkl.compactify()
168168
if tree.span.exists then
169169
val positionWarnings = new mutable.ListBuffer[String]()
170-
new PositionPickler(pickler, treePkl.buf.addrOfTree, treePkl.treeAnnots)
170+
val reference = ctx.settings.sourceroot.value
171+
new PositionPickler(pickler, treePkl.buf.addrOfTree, treePkl.treeAnnots, reference)
171172
.picklePositions(ctx.compilationUnit.source, tree :: Nil, positionWarnings)
172173
positionWarnings.foreach(report.warning(_))
173174

compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala

Lines changed: 11 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,23 @@ package semanticdb
55
import core._
66
import Phases._
77
import ast.tpd._
8+
import ast.untpd.given
89
import ast.Trees.mods
910
import Contexts._
1011
import Symbols._
1112
import Flags._
1213
import Names.Name
1314
import StdNames.nme
15+
import NameOps._
1416
import util.Spans.Span
1517
import util.{SourceFile, SourcePosition}
16-
import scala.jdk.CollectionConverters._
17-
import collection.mutable
18-
import java.nio.file.Paths
19-
20-
import dotty.tools.dotc.transform.SymUtils._
21-
22-
import PartialFunction.condOpt
23-
24-
import ast.untpd.given
25-
import NameOps._
18+
import transform.SymUtils._
2619

20+
import scala.jdk.CollectionConverters._
21+
import scala.collection.mutable
2722
import scala.annotation.{ threadUnsafe => tu, tailrec }
23+
import scala.PartialFunction.condOpt
24+
2825

2926
/** Extract symbol references and uses to semanticdb files.
3027
* See https://scalameta.org/docs/semanticdb/specification.html#symbol-1
@@ -590,37 +587,29 @@ object ExtractSemanticDB:
590587
import java.nio.file.Path
591588
import scala.collection.JavaConverters._
592589
import java.nio.file.Files
590+
import java.nio.file.Paths
593591

594592
val name: String = "extractSemanticDB"
595593

596594
def write(source: SourceFile, occurrences: List[SymbolOccurrence], symbolInfos: List[SymbolInformation])(using Context): Unit =
597595
def absolutePath(path: Path): Path = path.toAbsolutePath.normalize
598-
def commonPrefix[T](z: T)(i1: Iterable[T], i2: Iterable[T])(app: (T, T) => T): T =
599-
(i1 lazyZip i2).takeWhile(p => p(0) == p(1)).map(_(0)).foldLeft(z)(app)
600-
val sourcePath = absolutePath(source.file.jpath)
601-
val sourceRoot =
602-
// Here if `sourceRoot` and `sourcePath` do not share a common prefix then `relPath` will not be normalised,
603-
// containing ../.. etc, which is problematic when appending to `/META-INF/semanticdb/` and will not be accepted
604-
// by Files.createDirectories on JDK 11.
605-
val sourceRoot0 = absolutePath(Paths.get(ctx.settings.sourceroot.value))
606-
commonPrefix(sourcePath.getRoot)(sourcePath.asScala, sourceRoot0.asScala)(_ resolve _)
607596
val semanticdbTarget =
608597
val semanticdbTargetSetting = ctx.settings.semanticdbTarget.value
609598
absolutePath(
610599
if semanticdbTargetSetting.isEmpty then ctx.settings.outputDir.value.jpath
611600
else Paths.get(semanticdbTargetSetting)
612601
)
613-
val relPath = sourceRoot.relativize(sourcePath)
602+
val relPath = SourceFile.relativePath(source, ctx.settings.sourceroot.value)
614603
val outpath = semanticdbTarget
615604
.resolve("META-INF")
616605
.resolve("semanticdb")
617606
.resolve(relPath)
618-
.resolveSibling(sourcePath.getFileName().toString() + ".semanticdb")
607+
.resolveSibling(source.name + ".semanticdb")
619608
Files.createDirectories(outpath.getParent())
620609
val doc: TextDocument = TextDocument(
621610
schema = Schema.SEMANTICDB4,
622611
language = Language.SCALA,
623-
uri = Tools.mkURIstring(relPath),
612+
uri = Tools.mkURIstring(Paths.get(relPath)),
624613
text = "",
625614
md5 = internal.MD5.compute(String(source.content)),
626615
symbols = symbolInfos,

compiler/src/dotty/tools/dotc/transform/Pickler.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@ class Pickler extends Phase {
7272
Future {
7373
treePkl.compactify()
7474
if tree.span.exists then
75-
new PositionPickler(pickler, treePkl.buf.addrOfTree, treePkl.treeAnnots)
75+
val reference = ctx.settings.sourceroot.value
76+
new PositionPickler(pickler, treePkl.buf.addrOfTree, treePkl.treeAnnots, reference)
7677
.picklePositions(unit.source, tree :: Nil, positionWarnings)
7778

7879
if !ctx.settings.YdropComments.value then

compiler/src/dotty/tools/dotc/transform/PostTyper.scala

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -335,21 +335,8 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
335335
&& ctx.compilationUnit.source.exists
336336
&& sym != defn.SourceFileAnnot
337337
then
338-
def sourcerootPath =
339-
java.nio.file.Paths.get(ctx.settings.sourceroot.value)
340-
.toAbsolutePath
341-
.normalize
342-
val file = ctx.compilationUnit.source.file
343-
val jpath = file.jpath
344-
val relativePath =
345-
if jpath eq null then file.path // repl and other custom tests use abstract files with no path
346-
else if jpath.isAbsolute then
347-
val cunitPath = jpath.normalize
348-
// On Windows we can only relativize paths if root component matches
349-
// (see implementation of sun.nio.fs.WindowsPath#relativize)
350-
try sourcerootPath.relativize(cunitPath).toString
351-
catch case _: IllegalArgumentException => cunitPath.toString
352-
else jpath.normalize.toString
338+
val reference = ctx.settings.sourceroot.value
339+
val relativePath = util.SourceFile.relativePath(ctx.compilationUnit.source, reference)
353340
sym.addAnnotation(Annotation.makeSourceFile(relativePath))
354341
else (tree.rhs, sym.info) match
355342
case (rhs: LambdaTypeTree, bounds: TypeBounds) =>

compiler/src/dotty/tools/dotc/util/SourceFile.scala

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,34 @@ object SourceFile {
209209
val src = new SourceFile(new VirtualFile(name, content.getBytes(StandardCharsets.UTF_8)), scala.io.Codec.UTF8)
210210
src._maybeInComplete = maybeIncomplete
211211
src
212+
213+
/** Returns the relative path of `source` within the `reference` path
214+
*
215+
* It returns the absolute path of `source` if it is not contained in `reference`.
216+
*/
217+
def relativePath(source: SourceFile, reference: String): String = {
218+
val file = source.file
219+
val jpath = file.jpath
220+
if jpath eq null then
221+
file.path // repl and other custom tests use abstract files with no path
222+
else
223+
val sourcePath = jpath.toAbsolutePath.normalize
224+
val refPath = java.nio.file.Paths.get(reference).toAbsolutePath.normalize
225+
226+
if sourcePath.startsWith(refPath) then
227+
// On Windows we can only relativize paths if root component matches
228+
// (see implementation of sun.nio.fs.WindowsPath#relativize)
229+
//
230+
// try refPath.relativize(sourcePath).toString
231+
// catch case _: IllegalArgumentException => sourcePath.toString
232+
//
233+
// As we already check that the prefix matches, the special handling for
234+
// Windows is not needed.
235+
236+
refPath.relativize(sourcePath).toString
237+
else
238+
sourcePath.toString
239+
}
212240
}
213241

214242
@sharable object NoSource extends SourceFile(NoAbstractFile, Array[Char]()) {

language-server/test/dotty/tools/languageserver/util/server/TestFile.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@ class TestFile(val file: String) extends AnyVal {
99
}
1010

1111
object TestFile {
12-
lazy val testDir: Path = Paths.get("../out/ide-tests").toAbsolutePath
12+
lazy val testDir: Path = Paths.get("../out/ide-tests").toAbsolutePath.normalize
1313
lazy val sourceDir: Path = testDir.resolve("src")
1414
}

project/scripts/cmdTests

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,24 @@ clear_out "$OUT"
3333
"$SBT" ";scalac -d $OUT/out.jar $SOURCE; scalac -decompile -color:never $OUT/out.jar" > "$tmp"
3434
grep -qe "def main(args: scala.Array\[scala.Predef.String\]): scala.Unit =" "$tmp"
3535

36+
3637
echo "testing that paths SourceFile annotations are relativized"
3738
clear_out "$OUT"
38-
"$SBT" ";scalac -d $OUT/out.jar $(pwd)/$SOURCE; scalac -decompile -color:never $OUT/out.jar" > "$tmp"
39-
grep -qe "SourceFile(\"$SOURCE\")" "$tmp"
39+
"$SBT" "scalac -d $OUT/out.jar -sourceroot tests/pos $(pwd)/tests/pos/i10430/lib.scala $(pwd)/tests/pos/i10430/app.scala"
40+
"$SBT" "scalac -decompile -print-tasty -color:never $OUT/out.jar" > "$tmp"
41+
cat "$tmp" # for debugging
42+
grep -q ": i10430/lib.scala" "$tmp"
43+
grep -q ": i10430/app.scala" "$tmp"
44+
grep -q "[i10430/lib.scala]" "$tmp"
45+
grep -q "[i10430/app.scala]" "$tmp"
46+
if grep -q "tests/pos/i10430/lib.scala" "$tmp"; then
47+
echo "incorrect source file path in tasty"
48+
exit 1
49+
fi
50+
if grep -q "tests/pos/i10430/app.scala" "$tmp"; then
51+
echo "incorrect source file path in tasty"
52+
exit 1
53+
fi
4054

4155
echo "testing sbt scalac with suspension"
4256
clear_out "$OUT"

tests/pos/i10430/app.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
object App {
2+
val y = lib.double(5)
3+
println(y)
4+
}

tests/pos/i10430/lib.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
object lib {
2+
inline def double(x: Int): Int = x * x
3+
}

0 commit comments

Comments
 (0)