From b634e1b507f87b3edf0b2b933828b742c05cbc9a Mon Sep 17 00:00:00 2001 From: bitloi <89318445+bitloi@users.noreply.github.com> Date: Tue, 17 Feb 2026 20:06:53 -0500 Subject: [PATCH] [2.x] fix: Relax non-delegation for settings on shell (#8751) Allow settings to delegate when the user specifies an explicit scope (config or task axis), so that e.g. Compile/console/fork resolves when console/fork is defined in a delegated scope. Tasks continue to not delegate (getDirect only) so non-existent scopes like Compile/update still fail as in 2.0.0. --- main/src/main/scala/sbt/internal/Act.scala | 19 +++++++++++++------ .../project/settings-delegate/build.sbt | 1 + .../sbt-test/project/settings-delegate/test | 6 ++++++ 3 files changed, 20 insertions(+), 6 deletions(-) create mode 100644 sbt-app/src/sbt-test/project/settings-delegate/build.sbt create mode 100644 sbt-app/src/sbt-test/project/settings-delegate/test diff --git a/main/src/main/scala/sbt/internal/Act.scala b/main/src/main/scala/sbt/internal/Act.scala index 21d1e6c9c..6996b8cba 100644 --- a/main/src/main/scala/sbt/internal/Act.scala +++ b/main/src/main/scala/sbt/internal/Act.scala @@ -600,14 +600,21 @@ object Act { keys.flatMap(key => getValue(structure.data, key).map(KeyValue(key, _))) /** - * Starting sbt 2.0.0, we will not delegate when a non-existent scoping - * such as Compile / update is required. + * Resolves a key from settings data. Starting sbt 2.0.0 we do not delegate for + * tasks when the user specified an explicit scope (config or task axis), so that + * non-existent scopes like `Compile / update` fail. Settings may still delegate + * so that e.g. `Compile/console/fork` resolves when `console/fork` is defined + * in a delegated scope (see #8757). */ private def getValue[T](data: Def.Settings, key: ScopedKey[T]): Option[T] = - if key.scope.config.isSelect || - key.scope.task.isSelect - then data.getDirect(key) - else data.get(key) + val scopeExplicit = key.scope.config.isSelect || key.scope.task.isSelect + if !scopeExplicit then data.get(key) + else + data + .getDirect(key) + .orElse: + if key.key.tag.isSetting then data.get(key) + else None def requireSession[T](s: State, p: => Parser[T]): Parser[T] = if s.get(sessionSettings).isEmpty then failure("No project loaded") else p diff --git a/sbt-app/src/sbt-test/project/settings-delegate/build.sbt b/sbt-app/src/sbt-test/project/settings-delegate/build.sbt new file mode 100644 index 000000000..9e6b3a95c --- /dev/null +++ b/sbt-app/src/sbt-test/project/settings-delegate/build.sbt @@ -0,0 +1 @@ +scalaVersion := "3.8.1" diff --git a/sbt-app/src/sbt-test/project/settings-delegate/test b/sbt-app/src/sbt-test/project/settings-delegate/test new file mode 100644 index 000000000..7e36c6103 --- /dev/null +++ b/sbt-app/src/sbt-test/project/settings-delegate/test @@ -0,0 +1,6 @@ +# Fix #8757: settings may delegate when scope is explicit (tasks still must not). +# console/fork is defined in default scope; Compile/console/fork should resolve via delegation. +> compile + +# Setting with explicit config scope delegates (e.g. Compile/console/fork -> console/fork) +> show Compile/console/fork