diff --git a/compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala b/compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala index 8b3b217cb0fc..a0b4811e4311 100644 --- a/compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala +++ b/compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala @@ -165,6 +165,26 @@ private class ExtractDependenciesCollector(rec: DependencyRecorder) extends tpd. rec.addClassDependency(parent.tpe.classSymbol, depContext) } + // Only reference DependencyByMacroExpansion if it an be found on the classpath, + // as it was added later to the zinc.apiinfo DependencyContext enum + // e.g. pre 1.10.x sbt would throw java.lang.NoSuchFieldError errors here + lazy val allowsDependencyByMacroExpansion = + classOf[DependencyContext].getFields().exists(_.getName() == "DependencyByMacroExpansion") + + private def addMacroDependency(trees: List[Tree])(using Context): Unit = + if (allowsDependencyByMacroExpansion) { + val traverser = new TypeDependencyTraverser { + def addDependency(symbol: Symbol) = + if (!ignoreDependency(symbol)) { + val enclOrModuleClass = if (symbol.is(ModuleVal)) symbol.moduleClass else symbol.enclosingClass + assert(enclOrModuleClass.isClass, s"$enclOrModuleClass, $symbol") + + rec.addClassDependency(enclOrModuleClass, DependencyByMacroExpansion) + } + } + trees.foreach(tree => traverser.traverse(tree.tpe)) + } + private def depContextOf(cls: Symbol)(using Context): DependencyContext = if cls.isLocal then LocalDependencyByInheritance else DependencyByInheritance @@ -226,6 +246,11 @@ private class ExtractDependenciesCollector(rec: DependencyRecorder) extends tpd. case _ => } + tree match + case TypeApply(fun, args) if fun.symbol.is(Inline) => + addMacroDependency(args) + case _ => + tree match { case tree: Inlined if !tree.inlinedFromOuterScope => // The inlined call is normally ignored by TreeTraverser but we need to diff --git a/project/Build.scala b/project/Build.scala index 9c4fbafbb330..0abce52f0a41 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -1428,7 +1428,7 @@ object Build { Compile / scalacOptions ++= Seq("--java-output-version", Versions.minimumJVMVersion), // Add all the project's external dependencies libraryDependencies ++= Seq( - ("org.scala-sbt" %% "zinc-apiinfo" % "1.8.0" % Test).cross(CrossVersion.for3Use2_13), + ("org.scala-sbt" %% "zinc-apiinfo" % "1.10.8" % Test).cross(CrossVersion.for3Use2_13), "com.github.sbt" % "junit-interface" % "0.13.3" % Test, ), // Exclude the transitive dependencies from `zinc-apiinfo` that causes issues at the moment diff --git a/sbt-test/macros/i23852/Components.scala b/sbt-test/macros/i23852/Components.scala new file mode 100644 index 000000000000..320425eb6394 --- /dev/null +++ b/sbt-test/macros/i23852/Components.scala @@ -0,0 +1,9 @@ +object Components extends App { + try { + wire[Dep] + } catch { + case e: Throwable => + e.printStackTrace() + sys.exit(-1) + } +} diff --git a/sbt-test/macros/i23852/Dep.scala b/sbt-test/macros/i23852/Dep.scala new file mode 100644 index 000000000000..3b773053c37b --- /dev/null +++ b/sbt-test/macros/i23852/Dep.scala @@ -0,0 +1 @@ +class Dep() \ No newline at end of file diff --git a/sbt-test/macros/i23852/Dep.scala-added b/sbt-test/macros/i23852/Dep.scala-added new file mode 100644 index 000000000000..e4faf7baa67e --- /dev/null +++ b/sbt-test/macros/i23852/Dep.scala-added @@ -0,0 +1 @@ +class Dep(int: Int) \ No newline at end of file diff --git a/sbt-test/macros/i23852/macro.scala b/sbt-test/macros/i23852/macro.scala new file mode 100644 index 000000000000..5412da389947 --- /dev/null +++ b/sbt-test/macros/i23852/macro.scala @@ -0,0 +1,16 @@ +import scala.quoted.* + +inline def wire[T]: T = ${ wireImpl[T] } +def wireImpl[T: Type](using q: Quotes): Expr[T] = { + import q.reflect.* + + lazy val targetType = TypeRepr.of[T] + val constructorValue = targetType.typeSymbol.primaryConstructor + val constructionMethodTree: Term = { + val ctor = Select(New(TypeIdent(targetType.typeSymbol)), constructorValue) + if (targetType.typeArgs.isEmpty) ctor else ctor.appliedToTypes(targetType.typeArgs) + } + val constructorArgsValue = List(Nil) + val code: Tree = constructorArgsValue.foldLeft(constructionMethodTree)((acc: Term, args: List[Term]) => Apply(acc, args)) + code.asExprOf[T] +} diff --git a/sbt-test/macros/i23852/project/DottyInjectedPlugin.scala b/sbt-test/macros/i23852/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..fb946c4b8c61 --- /dev/null +++ b/sbt-test/macros/i23852/project/DottyInjectedPlugin.scala @@ -0,0 +1,11 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion") + ) +} diff --git a/sbt-test/macros/i23852/test b/sbt-test/macros/i23852/test new file mode 100644 index 000000000000..0035a5b71806 --- /dev/null +++ b/sbt-test/macros/i23852/test @@ -0,0 +1,3 @@ +> run +$ copy-file Dep.scala-added Dep.scala +-> compile \ No newline at end of file diff --git a/sbt-test/macros/macro-type-change-2/A/A.scala b/sbt-test/macros/macro-type-change-2/A/A.scala new file mode 100644 index 000000000000..6c173e289b07 --- /dev/null +++ b/sbt-test/macros/macro-type-change-2/A/A.scala @@ -0,0 +1,2 @@ +package A +class A diff --git a/sbt-test/macros/macro-type-change-2/app/App.scala b/sbt-test/macros/macro-type-change-2/app/App.scala new file mode 100644 index 000000000000..84c136359e00 --- /dev/null +++ b/sbt-test/macros/macro-type-change-2/app/App.scala @@ -0,0 +1,11 @@ +package app + +import Macros.* +import A.A + +object App { + @main def hasFields(expected: Boolean): Unit = { + val actual = Macros.hasAnyField[A] + assert(expected == actual, s"Expected $expected, obtained $actual") + } +} diff --git a/sbt-test/macros/macro-type-change-2/build.sbt b/sbt-test/macros/macro-type-change-2/build.sbt new file mode 100644 index 000000000000..471462bbaa0b --- /dev/null +++ b/sbt-test/macros/macro-type-change-2/build.sbt @@ -0,0 +1,27 @@ +// { +// "projects": [ +// { +// "name": "app", +// "dependsOn": [ +// "macros", +// "A" +// ], +// "scalaVersion": "2.13.12" +// }, +// { +// "name": "macros", +// "scalaVersion": "2.13.12" +// }, +// { +// "name": "A", +// "scalaVersion": "2.13.12" +// } +// ] +// } + +lazy val app = project.in(file("app")) + .dependsOn(macros, A) + +lazy val macros = project.in(file("macros")) + +lazy val A = project.in(file("A")) diff --git a/sbt-test/macros/macro-type-change-2/changes/A1.scala b/sbt-test/macros/macro-type-change-2/changes/A1.scala new file mode 100644 index 000000000000..3b937fceae93 --- /dev/null +++ b/sbt-test/macros/macro-type-change-2/changes/A1.scala @@ -0,0 +1,4 @@ +package A +class A { + val hello: String = "" +} diff --git a/sbt-test/macros/macro-type-change-2/macros/Macros.scala b/sbt-test/macros/macro-type-change-2/macros/Macros.scala new file mode 100644 index 000000000000..8cbfcb6d4cd3 --- /dev/null +++ b/sbt-test/macros/macro-type-change-2/macros/Macros.scala @@ -0,0 +1,15 @@ +package Macros + +import scala.quoted.* + +object Macros { + inline def hasAnyField[T]: Boolean = ${ hasAnyFieldImpl[T] } + + def hasAnyFieldImpl[T: Type](using Quotes): Expr[Boolean] = { + import quotes.reflect.* + + val hasField = TypeRepr.of[T].typeSymbol.fieldMembers.nonEmpty + + Expr(hasField) + } +} diff --git a/sbt-test/macros/macro-type-change-2/project/DottyInjectedPlugin.scala b/sbt-test/macros/macro-type-change-2/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..fb946c4b8c61 --- /dev/null +++ b/sbt-test/macros/macro-type-change-2/project/DottyInjectedPlugin.scala @@ -0,0 +1,11 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion") + ) +} diff --git a/sbt-test/macros/macro-type-change-2/test b/sbt-test/macros/macro-type-change-2/test new file mode 100644 index 000000000000..e6962a9ca9e1 --- /dev/null +++ b/sbt-test/macros/macro-type-change-2/test @@ -0,0 +1,4 @@ +# adapted from https://github.com/sbt/zinc/blob/1e422e5525c698aa71cc35b30c275c8c1c3135b2/zinc/src/sbt-test/macros/macro-type-change-2/test +> app/run false +$ copy-file changes/A1.scala A/A.scala +> app/run true diff --git a/sbt-test/macros/macro-type-change-3/A/A.scala b/sbt-test/macros/macro-type-change-3/A/A.scala new file mode 100644 index 000000000000..6c173e289b07 --- /dev/null +++ b/sbt-test/macros/macro-type-change-3/A/A.scala @@ -0,0 +1,2 @@ +package A +class A diff --git a/sbt-test/macros/macro-type-change-3/A/B.scala b/sbt-test/macros/macro-type-change-3/A/B.scala new file mode 100644 index 000000000000..46c6b2966824 --- /dev/null +++ b/sbt-test/macros/macro-type-change-3/A/B.scala @@ -0,0 +1,2 @@ +package A +class B extends A diff --git a/sbt-test/macros/macro-type-change-3/app/App.scala b/sbt-test/macros/macro-type-change-3/app/App.scala new file mode 100644 index 000000000000..b9eb51715be6 --- /dev/null +++ b/sbt-test/macros/macro-type-change-3/app/App.scala @@ -0,0 +1,11 @@ +package app + +import Macros.* +import A.B + +object App { + @main def hasFields(expected: Boolean): Unit = { + val actual = Macros.hasAnyField[B] + assert(expected == actual, s"Expected $expected, obtained $actual") + } +} diff --git a/sbt-test/macros/macro-type-change-3/build.sbt b/sbt-test/macros/macro-type-change-3/build.sbt new file mode 100644 index 000000000000..471462bbaa0b --- /dev/null +++ b/sbt-test/macros/macro-type-change-3/build.sbt @@ -0,0 +1,27 @@ +// { +// "projects": [ +// { +// "name": "app", +// "dependsOn": [ +// "macros", +// "A" +// ], +// "scalaVersion": "2.13.12" +// }, +// { +// "name": "macros", +// "scalaVersion": "2.13.12" +// }, +// { +// "name": "A", +// "scalaVersion": "2.13.12" +// } +// ] +// } + +lazy val app = project.in(file("app")) + .dependsOn(macros, A) + +lazy val macros = project.in(file("macros")) + +lazy val A = project.in(file("A")) diff --git a/sbt-test/macros/macro-type-change-3/changes/A1.scala b/sbt-test/macros/macro-type-change-3/changes/A1.scala new file mode 100644 index 000000000000..3b937fceae93 --- /dev/null +++ b/sbt-test/macros/macro-type-change-3/changes/A1.scala @@ -0,0 +1,4 @@ +package A +class A { + val hello: String = "" +} diff --git a/sbt-test/macros/macro-type-change-3/macros/Macros.scala b/sbt-test/macros/macro-type-change-3/macros/Macros.scala new file mode 100644 index 000000000000..8cbfcb6d4cd3 --- /dev/null +++ b/sbt-test/macros/macro-type-change-3/macros/Macros.scala @@ -0,0 +1,15 @@ +package Macros + +import scala.quoted.* + +object Macros { + inline def hasAnyField[T]: Boolean = ${ hasAnyFieldImpl[T] } + + def hasAnyFieldImpl[T: Type](using Quotes): Expr[Boolean] = { + import quotes.reflect.* + + val hasField = TypeRepr.of[T].typeSymbol.fieldMembers.nonEmpty + + Expr(hasField) + } +} diff --git a/sbt-test/macros/macro-type-change-3/project/DottyInjectedPlugin.scala b/sbt-test/macros/macro-type-change-3/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..fb946c4b8c61 --- /dev/null +++ b/sbt-test/macros/macro-type-change-3/project/DottyInjectedPlugin.scala @@ -0,0 +1,11 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion") + ) +} diff --git a/sbt-test/macros/macro-type-change-3/test b/sbt-test/macros/macro-type-change-3/test new file mode 100644 index 000000000000..452578a8461d --- /dev/null +++ b/sbt-test/macros/macro-type-change-3/test @@ -0,0 +1,4 @@ +# adapted from https://github.com/sbt/zinc/blob/1e422e5525c698aa71cc35b30c275c8c1c3135b2/zinc/src/sbt-test/macros/macro-type-change-3/test +> app/run false +$ copy-file changes/A1.scala A/A.scala +> app/run true diff --git a/sbt-test/macros/macro-type-change-4/A/A.scala b/sbt-test/macros/macro-type-change-4/A/A.scala new file mode 100644 index 000000000000..6c173e289b07 --- /dev/null +++ b/sbt-test/macros/macro-type-change-4/A/A.scala @@ -0,0 +1,2 @@ +package A +class A diff --git a/sbt-test/macros/macro-type-change-4/app/App.scala b/sbt-test/macros/macro-type-change-4/app/App.scala new file mode 100644 index 000000000000..12d0a6e4bfff --- /dev/null +++ b/sbt-test/macros/macro-type-change-4/app/App.scala @@ -0,0 +1,11 @@ +package app + +import Macros.* +import A.A + +object App { + @main def hasFields(expected: Boolean): Unit = { + val actual = Macros.hasAnyField[A](true) + assert(expected == actual, s"Expected $expected, obtained $actual") + } +} diff --git a/sbt-test/macros/macro-type-change-4/build.sbt b/sbt-test/macros/macro-type-change-4/build.sbt new file mode 100644 index 000000000000..471462bbaa0b --- /dev/null +++ b/sbt-test/macros/macro-type-change-4/build.sbt @@ -0,0 +1,27 @@ +// { +// "projects": [ +// { +// "name": "app", +// "dependsOn": [ +// "macros", +// "A" +// ], +// "scalaVersion": "2.13.12" +// }, +// { +// "name": "macros", +// "scalaVersion": "2.13.12" +// }, +// { +// "name": "A", +// "scalaVersion": "2.13.12" +// } +// ] +// } + +lazy val app = project.in(file("app")) + .dependsOn(macros, A) + +lazy val macros = project.in(file("macros")) + +lazy val A = project.in(file("A")) diff --git a/sbt-test/macros/macro-type-change-4/changes/A1.scala b/sbt-test/macros/macro-type-change-4/changes/A1.scala new file mode 100644 index 000000000000..3b937fceae93 --- /dev/null +++ b/sbt-test/macros/macro-type-change-4/changes/A1.scala @@ -0,0 +1,4 @@ +package A +class A { + val hello: String = "" +} diff --git a/sbt-test/macros/macro-type-change-4/macros/Macros.scala b/sbt-test/macros/macro-type-change-4/macros/Macros.scala new file mode 100644 index 000000000000..ca930c91cc36 --- /dev/null +++ b/sbt-test/macros/macro-type-change-4/macros/Macros.scala @@ -0,0 +1,15 @@ +package Macros + +import scala.quoted.* + +object Macros { + inline def hasAnyField[T](placeholder: Boolean): Boolean = ${ hasAnyFieldImpl[T]('placeholder) } + + def hasAnyFieldImpl[T: Type](placeholder: Expr[Boolean])(using Quotes): Expr[Boolean] = { + import quotes.reflect.* + + val hasField = TypeRepr.of[T].typeSymbol.fieldMembers.nonEmpty + + Expr(hasField) + } +} diff --git a/sbt-test/macros/macro-type-change-4/project/DottyInjectedPlugin.scala b/sbt-test/macros/macro-type-change-4/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..fb946c4b8c61 --- /dev/null +++ b/sbt-test/macros/macro-type-change-4/project/DottyInjectedPlugin.scala @@ -0,0 +1,11 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion") + ) +} diff --git a/sbt-test/macros/macro-type-change-4/test b/sbt-test/macros/macro-type-change-4/test new file mode 100644 index 000000000000..4f439762d45e --- /dev/null +++ b/sbt-test/macros/macro-type-change-4/test @@ -0,0 +1,4 @@ +# adapted from https://github.com/sbt/zinc/blob/1e422e5525c698aa71cc35b30c275c8c1c3135b2/zinc/src/sbt-test/macros/macro-type-change-4/test +> app/run false +$ copy-file changes/A1.scala A/A.scala +> app/run true diff --git a/sbt-test/macros/macro-type-change/app/A.scala b/sbt-test/macros/macro-type-change/app/A.scala new file mode 100644 index 000000000000..93511b0f1ca1 --- /dev/null +++ b/sbt-test/macros/macro-type-change/app/A.scala @@ -0,0 +1,2 @@ +package app +class A diff --git a/sbt-test/macros/macro-type-change/app/App.scala b/sbt-test/macros/macro-type-change/app/App.scala new file mode 100644 index 000000000000..eb63f362bc3e --- /dev/null +++ b/sbt-test/macros/macro-type-change/app/App.scala @@ -0,0 +1,10 @@ +package app + +import Macros.* + +object App { + @main def hasFields(expected: Boolean): Unit = { + val actual = Macros.hasAnyField[A] + assert(expected == actual, s"Expected $expected, obtained $actual") + } +} diff --git a/sbt-test/macros/macro-type-change/build.sbt b/sbt-test/macros/macro-type-change/build.sbt new file mode 100644 index 000000000000..b8b37d4d6148 --- /dev/null +++ b/sbt-test/macros/macro-type-change/build.sbt @@ -0,0 +1,20 @@ +// { +// "projects": [ +// { +// "name": "app", +// "dependsOn": [ +// "macros" +// ], +// "scalaVersion": "2.13.12" +// }, +// { +// "name": "macros", +// "scalaVersion": "2.13.12" +// } +// ] +// } + +lazy val app = project.in(file("app")) + .dependsOn(macros) + +lazy val macros = project.in(file("macros")) diff --git a/sbt-test/macros/macro-type-change/changes/A1.scala b/sbt-test/macros/macro-type-change/changes/A1.scala new file mode 100644 index 000000000000..3ff1b325fe04 --- /dev/null +++ b/sbt-test/macros/macro-type-change/changes/A1.scala @@ -0,0 +1,4 @@ +package app +class A { + val hello: String = "" +} diff --git a/sbt-test/macros/macro-type-change/macros/Macros.scala b/sbt-test/macros/macro-type-change/macros/Macros.scala new file mode 100644 index 000000000000..8cbfcb6d4cd3 --- /dev/null +++ b/sbt-test/macros/macro-type-change/macros/Macros.scala @@ -0,0 +1,15 @@ +package Macros + +import scala.quoted.* + +object Macros { + inline def hasAnyField[T]: Boolean = ${ hasAnyFieldImpl[T] } + + def hasAnyFieldImpl[T: Type](using Quotes): Expr[Boolean] = { + import quotes.reflect.* + + val hasField = TypeRepr.of[T].typeSymbol.fieldMembers.nonEmpty + + Expr(hasField) + } +} diff --git a/sbt-test/macros/macro-type-change/project/DottyInjectedPlugin.scala b/sbt-test/macros/macro-type-change/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..fb946c4b8c61 --- /dev/null +++ b/sbt-test/macros/macro-type-change/project/DottyInjectedPlugin.scala @@ -0,0 +1,11 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion") + ) +} diff --git a/sbt-test/macros/macro-type-change/test b/sbt-test/macros/macro-type-change/test new file mode 100644 index 000000000000..16a37a2d8d4a --- /dev/null +++ b/sbt-test/macros/macro-type-change/test @@ -0,0 +1,4 @@ +# adapted from https://github.com/sbt/zinc/blob/1e422e5525c698aa71cc35b30c275c8c1c3135b2/zinc/src/sbt-test/macros/macro-type-change/test +> app/run false +$ copy-file changes/A1.scala app/A.scala +> app/run true