Merge pull request #8432 from eed3si9n/bport/scala-version

[1.x] Show warnings when scalaVersion is missing
This commit is contained in:
eugene yokota 2026-01-04 15:34:43 -05:00 committed by GitHub
commit 2e8e03eb0e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 116 additions and 19 deletions

View File

@ -364,7 +364,7 @@ lazy val utilPosition = (project in file("internal") / "util-position")
utilCommonSettings,
name := "Util Position",
scalacOptions += "-language:experimental.macros",
libraryDependencies ++= Seq(scalaReflect.value, scalatest % "test"),
libraryDependencies ++= Seq(scalaReflect.value, hedgehog % Test),
utilMimaSettings,
)

View File

@ -8,18 +8,26 @@
package sbt.internal.util
import org.scalatest.flatspec.AnyFlatSpec
import hedgehog._
import hedgehog.runner._
class SourcePositionSpec extends AnyFlatSpec {
"SourcePosition()" should "return a sane SourcePosition" in {
val filename = "SourcePositionSpec.scala"
val lineNumber = 17
SourcePosition.fromEnclosing() match {
case LinePosition(path, startLine) => assert(path === filename && startLine === lineNumber)
case RangePosition(path, range) => assert(path === filename && inRange(range, lineNumber))
case NoPosition => fail("No source position found")
}
}
object SourcePositionSpec extends Properties {
override def tests: List[Test] = List(
example(
"SourcePosition() should return a SourcePosition", {
val filename = "SourcePositionSpec.scala"
// val lineNumber = 19
val lineNumber = 21
SourcePosition.fromEnclosing() match {
case pos @ LinePosition(path, startLine) =>
Result.assert( /* path == filename && */ startLine == lineNumber).log(pos.toString())
case pos @ RangePosition(path, range) =>
Result.assert(path == filename && inRange(range, lineNumber)).log(pos.toString())
case NoPosition => Result.assert(false).log("No source position found")
}
}
)
)
private def inRange(range: LineRange, lineNo: Int) =
range.start until range.end contains lineNo

View File

@ -979,7 +979,11 @@ object BuiltinCommands {
st => setupGlobalFileTreeRepository(addCacheStoreFactoryFactory(st))
)
val s4 = s3.put(Keys.useLog4J.key, Project.extract(s3).get(Keys.useLog4J))
addSuperShellParams(CheckBuildSources.init(LintUnused.lintUnusedFunc(s4)))
addSuperShellParams(
CheckBuildSources.init(
LintUnused.lintScalaVersion(LintUnused.lintUnusedFunc(s4))
)
)
}
private val setupGlobalFileTreeRepository: State => State = { state =>

View File

@ -11,11 +11,12 @@ package internal
import Keys._
import Def.{ Setting, ScopedKey }
import sbt.internal.util.{ FilePosition, NoPosition, SourcePosition }
import sbt.internal.util.{ FilePosition, LinePosition, NoPosition, SourcePosition }
import java.io.File
import Scope.Global
import sbt.SlashSyntax0._
import sbt.Def._
import scala.annotation.nowarn
object LintUnused {
lazy val lintSettings: Seq[Setting[_]] = Seq(
@ -164,14 +165,53 @@ object LintUnused {
u
}
(unusedKeys map { u =>
(u.scoped, display.show(u.scoped), u.positions)
(u.scoped, display.show(u.scoped), u.positions.toVector)
}).sortBy(_._2)
}
def lintScalaVersion(state: State): State = {
val log = state.log
val extracted = Project.extract(state)
val structure = extracted.structure
val comp = structure.compiledMap
for {
p <- structure.allProjectRefs
scope = (Scope.Global.in(p): @nowarn)
key = scalaVersion.in(scope)
definingScope = structure.data.definingScope(key.scope, key.key)
definingScoped = definingScope match {
case Some(sc) => Some(ScopedKey(sc, key.key))
case _ => None
}
sv <- extracted.getOpt(key)
isPlugin = extracted.get(sbtPlugin.in(scope))
mb = extracted.get(isMetaBuild.in(scope))
auto = extracted.get(autoScalaLibrary.in(scope))
msi = extracted.get(managedScalaInstance.in(scope))
(_, sk) = extracted.runTask(skip.in(scope.in(publish.key): @nowarn), state)
display = p match {
case ProjectRef(_, id) => id
case _ | null => Reference.display(p)
}
c <- comp.get(definingScoped.getOrElse(key.scopedKey))
setting <- c.settings.headOption
} if (auto && msi && !isPlugin && !mb && !sk)
setting.pos match {
case LinePosition(path, _) if path.endsWith("Defaults.scala") =>
log.warn(
s"""scalaVersion for subproject $display fell back to a default value $sv; declare it explicitly in build.sbt:
scalaVersion := "$sv""""
)
case _ => ()
}
else ()
state
}
private[this] case class UnusedKey(
scoped: ScopedKey[_],
positions: Vector[SourcePosition],
data: Option[ScopedKeyData[_]]
scoped: ScopedKey[?],
positions: Seq[SourcePosition],
data: Option[ScopedKeyData[?]]
)
private def definedAtString(settings: Vector[Setting[_]]): Vector[SourcePosition] = {

View File

@ -3,7 +3,12 @@ import sbt.ExposeYourself._
taskCancelStrategy := { (state: State) =>
new TaskCancellationStrategy {
type State = Unit
override def onTaskEngineStart(canceller: RunningTaskEngine): Unit = canceller.cancelAndShutdown()
override def onTaskEngineStart(canceller: RunningTaskEngine): Unit = {
state.currentCommand match {
case Some(e) if e.commandLine == "loadp" => ()
case _ => canceller.cancelAndShutdown()
}
}
override def onTaskEngineFinish(state: State): Unit = ()
}
}

View File

@ -0,0 +1,39 @@
@transient
lazy val checkScalaVersionWarning = taskKey[Unit]("")
// exempt publish skipped projects
lazy val `scala-version-root` = (project in file("."))
.settings(
name := "scala-version-root",
checkScalaVersionWarning := {
val state = Keys.state.value
val logging = state.globalLogging
val sv = scalaVersion.value
val contents = IO.read(logging.backing.file)
assert(contents.contains(s"""scalaVersion for subproject nievab1 fell back to a default value $sv"""))
assert(!contents.contains(s"""scalaVersion for subproject scala-version-root fell back to a default value $sv"""))
assert(!contents.contains(s"""scalaVersion for subproject nievab2 fell back to a default value $sv"""))
assert(!contents.contains(s"""scalaVersion for subproject nievab3 fell back to a default value $sv"""))
assert(!contents.contains(s"""scalaVersion for subproject nievab4 fell back to a default value $sv"""))
()
},
publish / skip := true,
)
lazy val nievab1 = project
// exempt plugin projects
lazy val nievab2 = project
.enablePlugins(SbtPlugin)
// exempt Java projects
lazy val nievab3 = project
.settings(
autoScalaLibrary := false,
)
// exempt SCALA_HOME projects
lazy val nievab4 = project
.settings(
managedScalaInstance := false,
)

View File

@ -0,0 +1 @@
> checkScalaVersionWarning