From e292d053229b8055437a4d155dd32718716e1e2a Mon Sep 17 00:00:00 2001 From: Rikito Taniguchi Date: Wed, 8 Apr 2026 11:45:01 +0900 Subject: [PATCH] [2.x] Fix Scala 3 .previous expansion for unstable path (#9041) **problem** .previous was implemented as an inline expansion to wrapInitTask(Previous.runtime(in)( Parser[T]] to be used in the inputTask macro as an input with an ultimate diff --git a/main-settings/src/main/scala/sbt/std/TaskMacro.scala b/main-settings/src/main/scala/sbt/std/TaskMacro.scala index 378a7d84e..50cdf7aea 100644 --- a/main-settings/src/main/scala/sbt/std/TaskMacro.scala +++ b/main-settings/src/main/scala/sbt/std/TaskMacro.scala @@ -17,6 +17,7 @@ import sbt.internal.util.{ SourcePosition, SourcePositionImpl } import language.experimental.macros import scala.quoted.* import sbt.util.BuildWideCacheConfiguration +import sjsonnew.JsonFormat object TaskMacro: @deprecated("will be removed", "2.0.0") @@ -128,6 +129,15 @@ object TaskMacro: val convert1 = new FullConvert(qctx, 1000) convert1.contFlatMap[A1, F, Id](t, convert1.appExpr, None) + def previousImpl[A1: Type](t: Expr[TaskKey[A1]])(using qctx: Quotes): Expr[Option[A1]] = + import qctx.reflect.* + Expr.summon[JsonFormat[A1]] match + case Some(ev) => + '{ + InputWrapper.`wrapInitTask_\u2603\u2603`[Option[A1]](Previous.runtime[A1]($t)(using $ev)) + } + case _ => report.errorAndAbort(s"JsonFormat[${Type.show[A1]}] missing") + /** Implementation of := macro for settings. */ def settingAssignMacroImpl[A1: Type](rec: Expr[Scoped.DefinableSetting[A1]], v: Expr[A1])(using qctx: Quotes diff --git a/main-settings/src/test/scala/sbt/std/TaskPosSpec.scala b/main-settings/src/test/scala/sbt/std/TaskPosSpec.scala index da3a22327..c82916515 100644 --- a/main-settings/src/test/scala/sbt/std/TaskPosSpec.scala +++ b/main-settings/src/test/scala/sbt/std/TaskPosSpec.scala @@ -106,6 +106,23 @@ class TaskPosSpec { } } + locally { + // .previous should compile for task with complex return type like `Seq[String]` + // https://github.com/sbt/sbt/issues/9037 + import sbt.*, Def.* + import sjsonnew.BasicJsonProtocol.given + val link = taskKey[Int]("") + val fingerprints = taskKey[Seq[String]]("") + Def.taskDyn[Int] { + val currentFingerprints = (link / fingerprints).value + val previousFingerprints = (link / fingerprints).previous + Def.task { + if previousFingerprints.exists(_ != currentFingerprints) then currentFingerprints.size + else 0 + } + } + } + locally { // missing .value error should not happen inside task dyn import sbt.*, Def.*