diff --git a/build.sbt b/build.sbt index 1336b65c..adaa4a94 100644 --- a/build.sbt +++ b/build.sbt @@ -37,7 +37,7 @@ inThisBuild(Def.settings( Developer("sjrd", "Sébastien Doeraene", "sjrdoeraene@gmail.com", url("https://github.com/sjrd/")), Developer("bishabosha", "Jamie Thompson", "bishbashboshjt@gmail.com", url("https://github.com/bishabosha")), ), - versionPolicyIntention := Compatibility.BinaryAndSourceCompatible, + versionPolicyIntention := Compatibility.BinaryCompatible, // Ignore dependencies to internal modules whose version is like `1.2.3+4...` (see https://github.com/scalacenter/sbt-version-policy#how-to-integrate-with-sbt-dynver) versionPolicyIgnoredInternalDependencyVersions := Some("^\\d+\\.\\d+\\.\\d+\\+\\d+".r) )) diff --git a/tasty-query/shared/src/main/scala/tastyquery/Printers.scala b/tasty-query/shared/src/main/scala/tastyquery/Printers.scala index b79cba61..042ba163 100644 --- a/tasty-query/shared/src/main/scala/tastyquery/Printers.scala +++ b/tasty-query/shared/src/main/scala/tastyquery/Printers.scala @@ -655,6 +655,13 @@ private[tastyquery] object Printers: case elem: TypeMember => print(elem) case elem: TypeTree => print(elem) } + + case InlinedTypeTree(caller, expansion) => + for c <- caller do + print("") + print(expansion) end print def print(tree: WildcardTypeArgTree): Unit = diff --git a/tasty-query/shared/src/main/scala/tastyquery/Traversers.scala b/tasty-query/shared/src/main/scala/tastyquery/Traversers.scala index eea9f345..d242760e 100644 --- a/tasty-query/shared/src/main/scala/tastyquery/Traversers.scala +++ b/tasty-query/shared/src/main/scala/tastyquery/Traversers.scala @@ -179,6 +179,9 @@ object Traversers: case TypeBindingsTree(bindings, body) => traverse(bindings) traverse(body) + case InlinedTypeTree(caller, expansion) => + traverse(caller) + traverse(expansion) // TypeDefinitionTree's case InferredTypeBoundsTree(bounds) => diff --git a/tasty-query/shared/src/main/scala/tastyquery/Trees.scala b/tasty-query/shared/src/main/scala/tastyquery/Trees.scala index ccee427a..068253d7 100644 --- a/tasty-query/shared/src/main/scala/tastyquery/Trees.scala +++ b/tasty-query/shared/src/main/scala/tastyquery/Trees.scala @@ -920,6 +920,21 @@ object Trees { override def withPos(pos: SourcePosition): TypeBindingsTree = TypeBindingsTree(bindings, body)(pos) end TypeBindingsTree + /** A tree representing an inlined type. + * + * @param caller + * The toplevel class from which the type was inlined. + * @param expansion + * The expanded type. + */ + final case class InlinedTypeTree(caller: Option[TypeIdent | SelectTypeTree], expansion: TypeTree)(pos: SourcePosition) + extends TypeTree(pos): + override protected def calculateType: NonEmptyPrefix = + expansion.toPrefix + + override final def withPos(pos: SourcePosition): InlinedTypeTree = InlinedTypeTree(caller, expansion)(pos) + end InlinedTypeTree + // --- TypeDefinitionTrees and TypeBoundsTrees ------------------------------ sealed abstract class TypeDefinitionTree(pos: SourcePosition) extends Tree(pos): diff --git a/tasty-query/shared/src/main/scala/tastyquery/reader/tasties/TreeUnpickler.scala b/tasty-query/shared/src/main/scala/tastyquery/reader/tasties/TreeUnpickler.scala index 494be3cd..254ddb84 100644 --- a/tasty-query/shared/src/main/scala/tastyquery/reader/tasties/TreeUnpickler.scala +++ b/tasty-query/shared/src/main/scala/tastyquery/reader/tasties/TreeUnpickler.scala @@ -1073,20 +1073,7 @@ private[tasties] class TreeUnpickler private ( reader.readByte() val end = reader.readEnd() val expr = readTerm - val caller: Option[TypeIdent | SelectTypeTree] = - reader.ifBefore(end)( - tagFollowShared match { - // The caller is not specified, this is a binding (or next val or def) - case VALDEF | DEFDEF => None - case _ => - readTypeTree match - case caller: TypeIdent => Some(caller) - case caller: SelectTypeTree => Some(caller) - case caller => - throw TastyFormatException(s"Unexpected Inlined caller $caller $posErrorMsg") - }, - None - ) + val caller = readInlinedCaller(end) val bindings = reader.until(end)(readValOrDefDef) Inlined(expr, caller, bindings)(spn) case SHAREDterm => @@ -1635,6 +1622,15 @@ private[tasties] class TreeUnpickler private ( val body = readTypeTree val bindings = readStats(end).map(_.asInstanceOf[TypeMember]) TypeBindingsTree(bindings, body)(spn) + case INLINED => + val spn = span + reader.readByte() + val end = reader.readEnd() + val expansion = readTypeTree + val caller = readInlinedCaller(end) + if reader.currentAddr != end then + throw TastyFormatException(s"Unexpected bindings in INLINED type tree $posErrorMsg") + InlinedTypeTree(caller, expansion)(spn) case SHAREDterm => val spn = span reader.readByte() @@ -1646,6 +1642,22 @@ private[tasties] class TreeUnpickler private ( TypeWrapper(readNonEmptyPrefix())(span) } + private def readInlinedCaller(end: Addr)(using SourceFile): Option[TypeIdent | SelectTypeTree] = + reader.ifBefore(end)( + tagFollowShared match { + // The caller is not specified, this is a binding (or next val or def) + case VALDEF | DEFDEF => None + case _ => + readTypeTree match + case caller: TypeIdent => Some(caller) + case caller: SelectTypeTree => Some(caller) + case caller => + throw TastyFormatException(s"Unexpected Inlined caller $caller $posErrorMsg") + }, + None + ) + end readInlinedCaller + private def protectReadDefiningTypeTree[A <: TypeTree](body: => A): A = /* It is possible to find SHAREDterm's referencing REFINEDtpt, `LAMBDAtpt`, etc. * This is bad because a these TypeTree's define symbols. If we read them diff --git a/tasty-query/shared/src/test/scala/tastyquery/ReadTreeSuite.scala b/tasty-query/shared/src/test/scala/tastyquery/ReadTreeSuite.scala index b18fec38..43802be4 100644 --- a/tasty-query/shared/src/test/scala/tastyquery/ReadTreeSuite.scala +++ b/tasty-query/shared/src/test/scala/tastyquery/ReadTreeSuite.scala @@ -1452,6 +1452,22 @@ class ReadTreeSuite extends RestrictedUnpicklingSuite { assert(containsSubtree(inlined)(clue(tree))) } + testUnpickle("inlined-path", "simple_trees.InlinedPath") { tree => + val inlinedPath: StructureCheck = { + case SelectTypeTree( + InlinedTypeTree( + Some(TypeIdent(ObjectClassTypeName(SimpleTypeName("InlinedPath")))), + InlinedTypeTree(None, TypeWrapper(TermRefInternal(NoPrefix, xSym: Symbol))) + ), + SimpleTypeName("Inner") + ) if xSym.name == termName("x") => + } + val testDef = findTree(tree) { case testDef @ DefDef(SimpleName("test"), _, _, _, _) => + testDef + } + assert(containsSubtree(inlinedPath)(clue(testDef))) + } + testUnpickle("select-tpt", "simple_trees.SelectType") { tree => val selectTpt: StructureCheck = { case ValDef( diff --git a/tasty-query/shared/src/test/scala/tastyquery/TypeSuite.scala b/tasty-query/shared/src/test/scala/tastyquery/TypeSuite.scala index 2bbe49fb..6a4c47f3 100644 --- a/tasty-query/shared/src/test/scala/tastyquery/TypeSuite.scala +++ b/tasty-query/shared/src/test/scala/tastyquery/TypeSuite.scala @@ -3677,4 +3677,25 @@ class TypeSuite extends UnrestrictedUnpicklingSuite { case arg => fail("unexpected argument to @macroImpl", clues(arg)) } + + testWithContext("inlined-path-issue-424") { + val InlinedPathClass = ctx.findTopLevelClass("simple_trees.InlinedPath") + val FooClass = ctx.findStaticClass("simple_trees.InlinedPath.Foo") + val InnerTypeMember = FooClass.findDecl(typeName("Inner")) + + val test = InlinedPathClass.findNonOverloadedDecl(termName("test")) + val List(Left(List(x)), Left(List(inner))) = test.paramSymss: @unchecked + + assert(clue(x.name) == termName("x")) + assert(clue(x).isGivenOrUsing) + assert(clue(x.declaredType).isRef(FooClass)) + + assert(clue(inner.name) == termName("inner")) + inner.declaredType match + case typeRef: TypeRef => + assert(clue(typeRef.prefix).isRef(x)) // through inlining the path + assert(typeRef.isRef(InnerTypeMember)) + case tpe => + fail("unexpected type for inner", clues(tpe)) + } } diff --git a/test-sources/src/main/scala/simple_trees/InlinedPath.scala b/test-sources/src/main/scala/simple_trees/InlinedPath.scala new file mode 100644 index 00000000..1be25bbc --- /dev/null +++ b/test-sources/src/main/scala/simple_trees/InlinedPath.scala @@ -0,0 +1,14 @@ +package simple_trees + +object InlinedPath: + trait Foo: + type Inner + + transparent inline def foo(using x: Foo): x.type = x +end InlinedPath + +class InlinedPath: + import InlinedPath.* + + def test(using x: Foo)(inner: foo.Inner): foo.Inner = inner +end InlinedPath