This is an automated email from the ASF dual-hosted git repository.
He-Pin pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/pekko-http.git
The following commit(s) were added to refs/heads/main by this push:
new 4784d0bf5 fix: Scala 3.9 compatibility fixes (#1074)
4784d0bf5 is described below
commit 4784d0bf5654f7d871efd74302436e61b4ff25ad
Author: He-Pin(kerr) <[email protected]>
AuthorDate: Fri Jun 19 17:22:14 2026 +0800
fix: Scala 3.9 compatibility fixes (#1074)
* build: fix sbt lint warnings by excluding unused keys
Motivation:
sbt lint reported 45 unused keys across sub-projects.
Modification:
Add projectInfoVersion, mimaReportSignatureProblems, autoAPIMappings,
and unidocProjectFilter to excludeLintKeys in project/Common.scala
globalSettings.
Result:
sbt lint warnings resolved.
Tests:
- sbt "http-core / compile; http / compile; parsing / compile" - sbt lint
warnings resolved
- sbt "http-caching / compile; http-cors / compile; http-testkit / compile"
- sbt lint warnings resolved
References:
None - build configuration cleanup
* fix: Scala 3.9 compatibility fixes
Motivation:
Scala 3.9.0-RC1 introduces several breaking changes:
1. -language:_ wildcard option is no longer accepted
2. -Yfuture-lazy-vals option removed (now default behavior)
3. -Ywarn-dead-code is Scala 2 only
4. Context bound desugaring changes, breaking explicit ClassTag/Tuple/
LogSource passing patterns
Modification:
- build.sbt: make -language:_ conditional on Scala 3 < 3.9
- project/Common.scala: move -Ywarn-dead-code to Scala 2 only,
conditionally include -Yfuture-lazy-vals for Scala 3 < 3.9
- HttpMessage.scala, RejectionHandler.scala,
PredefinedToResponseMarshallers.scala:
use implicit val ClassTag instead of context bound [T: ClassTag]
- Directive.scala, BasicDirectives.scala, PathMatcher.scala:
use implicit val Tuple instead of context bound [L: Tuple]
- PoolInterface.scala, StageLoggingWithOverride.scala:
use implicit val LogSource instead of explicit passing
Result:
pekko-http parsing, http-core, http, and http-testkit modules compile
cleanly with Scala 3.9.0-RC1.
Tests:
- sbt "++3.9.0-RC1!; parsing/compile; http-core/compile; http/compile;
http-testkit/compile" passes
References: None - Scala 3.9 forward compatibility
* refactor: extract shared notOnScala39Plus helper for scalacOptions
Motivation:
The same scalacOptions block for `-language:_` (excluded on Scala 3.9+)
was repeated inline in 3 places in build.sbt.
Modification:
Add `Common.notOnScala39Plus` helper and replace all 3 inline blocks.
Result:
Single source of truth for the Scala 3.9+ language option exclusion.
Tests:
Not run - build config only
References:
Refs apache/pekko-http#1074
* fix: qualify notOnScala39Plus with Common. and apply scalafmt
Motivation:
The previous commit used unqualified `notOnScala39Plus` which is not
in scope in build.sbt, causing compilation failure.
Modification:
Prefix all 3 call sites with `Common.` and run scalafmt.
Result:
build.sbt compiles successfully with the shared helper.
Tests:
Not run - build config fix
References:
Refs apache/pekko-http#1074
---
build.sbt | 6 +++---
.../pekko/http/impl/engine/client/PoolInterface.scala | 3 ++-
.../pekko/http/impl/util/StageLoggingWithOverride.scala | 3 ++-
.../apache/pekko/http/scaladsl/model/HttpMessage.scala | 6 +++---
.../marshalling/PredefinedToResponseMarshallers.scala | 3 ++-
.../org/apache/pekko/http/scaladsl/server/Directive.scala | 9 +++++----
.../apache/pekko/http/scaladsl/server/PathMatcher.scala | 8 ++++----
.../pekko/http/scaladsl/server/RejectionHandler.scala | 4 ++--
.../http/scaladsl/server/directives/BasicDirectives.scala | 2 +-
project/Common.scala | 15 ++++++++++++---
10 files changed, 36 insertions(+), 23 deletions(-)
diff --git a/build.sbt b/build.sbt
index bed8bb90c..260676678 100644
--- a/build.sbt
+++ b/build.sbt
@@ -108,7 +108,7 @@ lazy val parsing = project("parsing")
.settings(AutomaticModuleName.settings("pekko.http.parsing"))
.addPekkoModuleDependency("pekko-actor", "provided",
PekkoCoreDependency.default)
.settings(Dependencies.parsing)
- .settings(scalacOptions += "-language:_")
+ .settings(scalacOptions ++=
Common.notOnScala39Plus(Seq("-language:_")).value)
.settings(scalaMacroSupport)
.enablePlugins(ScaladocNoVerificationOfDiagrams)
.enablePlugins(ReproducibleBuildsPlugin)
@@ -136,7 +136,7 @@ lazy val http = project("http")
.addPekkoModuleDependency("pekko-stream", "provided",
PekkoCoreDependency.default)
.settings(Dependencies.http)
.settings(
- Compile / scalacOptions += "-language:_")
+ Compile / scalacOptions ++=
Common.notOnScala39Plus(Seq("-language:_")).value)
.settings(scalaMacroSupport)
.enablePlugins(BootstrapGenjavadoc, BoilerplatePlugin)
.enablePlugins(ReproducibleBuildsPlugin)
@@ -191,7 +191,7 @@ lazy val httpTestkit = project("http-testkit")
.settings(
// don't ignore Suites which is the default for the junit-interface
testOptions += Tests.Argument(TestFrameworks.JUnit, "--ignore-runners="),
- Compile / scalacOptions ++= Seq("-language:_"),
+ Compile / scalacOptions ++=
Common.notOnScala39Plus(Seq("-language:_")).value,
Test / run / mainClass :=
Some("org.apache.pekko.http.javadsl.SimpleServerApp"))
.enablePlugins(BootstrapGenjavadoc, MultiNodeScalaTest,
ScaladocNoVerificationOfDiagrams)
.enablePlugins(ReproducibleBuildsPlugin)
diff --git
a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/client/PoolInterface.scala
b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/client/PoolInterface.scala
index 837fd3c2e..107aeab04 100644
---
a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/client/PoolInterface.scala
+++
b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/client/PoolInterface.scala
@@ -67,7 +67,8 @@ private[http] object PoolInterface {
import hcps._
import setup.{ connectionContext, settings }
implicit val system = fm.system
- val log: LoggingAdapter = Logging(system, poolId)(PoolLogSource)
+ implicit val logSource: LogSource[PoolId] = PoolLogSource
+ val log: LoggingAdapter = Logging(system, poolId)
log.debug("Creating pool.")
diff --git
a/http-core/src/main/scala/org/apache/pekko/http/impl/util/StageLoggingWithOverride.scala
b/http-core/src/main/scala/org/apache/pekko/http/impl/util/StageLoggingWithOverride.scala
index 731d1e7f4..2ac4084e5 100644
---
a/http-core/src/main/scala/org/apache/pekko/http/impl/util/StageLoggingWithOverride.scala
+++
b/http-core/src/main/scala/org/apache/pekko/http/impl/util/StageLoggingWithOverride.scala
@@ -42,7 +42,8 @@ private[pekko] trait StageLoggingWithOverride extends
GraphStageLogic {
_log =
logOverride match {
case DefaultNoLogging =>
- pekko.event.Logging(materializer.system,
logSource)(LogSource.fromClass)
+ implicit val ls: LogSource[Class[?]] = LogSource.fromClass
+ pekko.event.Logging(materializer.system, logSource)
case x => x
}
case _ =>
diff --git
a/http-core/src/main/scala/org/apache/pekko/http/scaladsl/model/HttpMessage.scala
b/http-core/src/main/scala/org/apache/pekko/http/scaladsl/model/HttpMessage.scala
index 97e6f916b..cc6d4fd45 100644
---
a/http-core/src/main/scala/org/apache/pekko/http/scaladsl/model/HttpMessage.scala
+++
b/http-core/src/main/scala/org/apache/pekko/http/scaladsl/model/HttpMessage.scala
@@ -136,8 +136,8 @@ sealed trait HttpMessage extends jm.HttpMessage {
}
/** Returns the first header of the given type if there is one */
- def header[T >: Null <: jm.HttpHeader: ClassTag]: Option[T] = {
- val clazz = classTag[T].runtimeClass.asInstanceOf[Class[T]]
+ def header[T >: Null <: jm.HttpHeader](implicit ct: ClassTag[T]): Option[T]
= {
+ val clazz = ct.runtimeClass.asInstanceOf[Class[T]]
HttpHeader.fastFind[T](clazz, headers) match {
case OptionVal.Some(h) => Some(h)
case _ if clazz == classOf[`Content-Type`] =>
Some(`Content-Type`(entity.contentType)).asInstanceOf[Option[T]]
@@ -146,7 +146,7 @@ sealed trait HttpMessage extends jm.HttpMessage {
}
/** Returns all the headers of the given type * */
- def headers[T <: jm.HttpHeader: ClassTag]: immutable.Seq[T] =
headers.collect {
+ def headers[T <: jm.HttpHeader](implicit ct: ClassTag[T]): immutable.Seq[T]
= headers.collect {
case h: T => h
}
diff --git
a/http/src/main/scala/org/apache/pekko/http/scaladsl/marshalling/PredefinedToResponseMarshallers.scala
b/http/src/main/scala/org/apache/pekko/http/scaladsl/marshalling/PredefinedToResponseMarshallers.scala
index 9287678cc..8f4ffc698 100755
---
a/http/src/main/scala/org/apache/pekko/http/scaladsl/marshalling/PredefinedToResponseMarshallers.scala
+++
b/http/src/main/scala/org/apache/pekko/http/scaladsl/marshalling/PredefinedToResponseMarshallers.scala
@@ -92,7 +92,8 @@ trait PredefinedToResponseMarshallers extends
LowPriorityToResponseMarshallerImp
}
})
- implicit def fromEntityStreamingSupportAndByteStringMarshaller[T: ClassTag,
M](implicit s: EntityStreamingSupport,
+ implicit def fromEntityStreamingSupportAndByteStringMarshaller[T,
M](implicit ct: ClassTag[T],
+ s: EntityStreamingSupport,
m: ToByteStringMarshaller[T]): ToResponseMarshaller[Source[T, M]] =
fromEntityStreamingSupportAndByteStringSourceMarshaller(s,
m.map(Source.single))
}
diff --git
a/http/src/main/scala/org/apache/pekko/http/scaladsl/server/Directive.scala
b/http/src/main/scala/org/apache/pekko/http/scaladsl/server/Directive.scala
index 2e5493da7..7b3a801ea 100644
--- a/http/src/main/scala/org/apache/pekko/http/scaladsl/server/Directive.scala
+++ b/http/src/main/scala/org/apache/pekko/http/scaladsl/server/Directive.scala
@@ -99,7 +99,7 @@ abstract class Directive[L](implicit val ev: Tuple[L]) {
/**
* Flatmaps this directive using the given function.
*/
- def tflatMap[R: Tuple](f: L => Directive[R]): Directive[R] =
+ def tflatMap[R](f: L => Directive[R])(implicit ev: Tuple[R]): Directive[R] =
Directive[R] { inner => tapply { values => f(values).tapply(inner) } }
/**
@@ -135,7 +135,7 @@ abstract class Directive[L](implicit val ev: Tuple[L]) {
* Creates a new directive that is able to recover from rejections that were
produced by `this` Directive
* **before the inner route was applied**.
*/
- def recover[R >: L: Tuple](recovery: immutable.Seq[Rejection] =>
Directive[R]): Directive[R] =
+ def recover[R >: L](recovery: immutable.Seq[Rejection] =>
Directive[R])(implicit ev: Tuple[R]): Directive[R] =
Directive[R] { inner => ctx =>
import ctx.executionContext
@volatile var rejectedFromInnerRoute = false
@@ -148,7 +148,8 @@ abstract class Directive[L](implicit val ev: Tuple[L]) {
/**
* Variant of `recover` that only recovers from rejections handled by the
given PartialFunction.
*/
- def recoverPF[R >: L: Tuple](recovery:
PartialFunction[immutable.Seq[Rejection], Directive[R]]): Directive[R] =
+ def recoverPF[R >: L](recovery: PartialFunction[immutable.Seq[Rejection],
Directive[R]])(
+ implicit ev: Tuple[R]): Directive[R] =
recover { rejections =>
recovery.applyOrElse(rejections, (rejs: Seq[Rejection]) =>
RouteDirectives.reject(rejs: _*))
}
@@ -162,7 +163,7 @@ object Directive {
/**
* Constructs a directive from a function literal.
*/
- def apply[T: Tuple](f: (T => Route) => Route): Directive[T] =
+ def apply[T](f: (T => Route) => Route)(implicit ev: Tuple[T]): Directive[T] =
new Directive[T] { def tapply(inner: T => Route) = f(inner) }
/**
diff --git
a/http/src/main/scala/org/apache/pekko/http/scaladsl/server/PathMatcher.scala
b/http/src/main/scala/org/apache/pekko/http/scaladsl/server/PathMatcher.scala
index e1e5fd1f8..147d22fd9 100644
---
a/http/src/main/scala/org/apache/pekko/http/scaladsl/server/PathMatcher.scala
+++
b/http/src/main/scala/org/apache/pekko/http/scaladsl/server/PathMatcher.scala
@@ -156,20 +156,20 @@ object PathMatcher extends
ImplicitPathMatcherConstruction {
/**
* Creates a PathMatcher that always matches, consumes nothing and extracts
the given Tuple of values.
*/
- def provide[L: Tuple](extractions: L): PathMatcher[L] =
+ def provide[L](extractions: L)(implicit ev: Tuple[L]): PathMatcher[L] =
new PathMatcher[L] {
- def apply(path: Path) = Matched(path, extractions)(ev)
+ def apply(path: Path) = Matched(path, extractions)
}
/**
* Creates a PathMatcher that matches and consumes the given path prefix and
extracts the given list of extractions.
* If the given prefix is empty the returned PathMatcher matches always and
consumes nothing.
*/
- def apply[L: Tuple](prefix: Path, extractions: L): PathMatcher[L] =
+ def apply[L](prefix: Path, extractions: L)(implicit ev: Tuple[L]):
PathMatcher[L] =
if (prefix.isEmpty) provide(extractions)
else new PathMatcher[L] {
def apply(path: Path) =
- if (path.startsWith(prefix)) Matched(path.dropChars(prefix.charCount),
extractions)(ev)
+ if (path.startsWith(prefix)) Matched(path.dropChars(prefix.charCount),
extractions)
else Unmatched
}
diff --git
a/http/src/main/scala/org/apache/pekko/http/scaladsl/server/RejectionHandler.scala
b/http/src/main/scala/org/apache/pekko/http/scaladsl/server/RejectionHandler.scala
index f8b4051c5..7f82d742e 100644
---
a/http/src/main/scala/org/apache/pekko/http/scaladsl/server/RejectionHandler.scala
+++
b/http/src/main/scala/org/apache/pekko/http/scaladsl/server/RejectionHandler.scala
@@ -90,8 +90,8 @@ object RejectionHandler {
* Handles several Rejections of the same type at the same time.
* The seq passed to the given function is guaranteed to be non-empty.
*/
- def handleAll[T <: Rejection: ClassTag](f: immutable.Seq[T] => Route):
this.type = {
- val runtimeClass = implicitly[ClassTag[T]].runtimeClass
+ def handleAll[T <: Rejection](f: immutable.Seq[T] => Route)(implicit ct:
ClassTag[T]): this.type = {
+ val runtimeClass = ct.runtimeClass
cases += TypeHandler[T](runtimeClass, f)
this
}
diff --git
a/http/src/main/scala/org/apache/pekko/http/scaladsl/server/directives/BasicDirectives.scala
b/http/src/main/scala/org/apache/pekko/http/scaladsl/server/directives/BasicDirectives.scala
index 6d44a607f..c39c7a8d4 100644
---
a/http/src/main/scala/org/apache/pekko/http/scaladsl/server/directives/BasicDirectives.scala
+++
b/http/src/main/scala/org/apache/pekko/http/scaladsl/server/directives/BasicDirectives.scala
@@ -152,7 +152,7 @@ trait BasicDirectives {
*
* @group basic
*/
- def tprovide[L: Tuple](values: L): Directive[L] =
+ def tprovide[L](values: L)(implicit ev: Tuple[L]): Directive[L] =
Directive { _(values) }
/**
diff --git a/project/Common.scala b/project/Common.scala
index ad9c54820..006807d7d 100644
--- a/project/Common.scala
+++ b/project/Common.scala
@@ -31,19 +31,19 @@ object Common extends AutoPlugin {
"-deprecation",
"-encoding", "UTF-8", // yes, this is 2 args
"-unchecked",
- "-Ywarn-dead-code",
"-Wconf:msg=reached max recursion depth:s",
"-Wconf:msg=Prefer the Scala annotation over Java's `@Deprecated`:s",
"-release:" + javacTarget),
scalacOptions ++= onlyOnScala2(Seq(
"-Xlint",
+ "-Ywarn-dead-code",
// Exhaustivity checking is only useful for simple sealed hierarchies
and matches without filters.
// In all other cases, the warning is non-actionable: you get spurious
warnings that need to be suppressed
// verbosely. So, opt out of those in general.
"-Wconf:cat=other-match-analysis&msg=match may not be
exhaustive:s")).value,
scalacOptions ++= onlyOnScala3(Seq(
- "-Wconf:cat=deprecation:s",
- "-Yfuture-lazy-vals")).value,
+ "-Wconf:cat=deprecation:s")).value,
+ scalacOptions ++= onlyOnScala3Below39(Seq("-Yfuture-lazy-vals")).value,
javacOptions ++=
Seq("-encoding", "UTF-8", "--release", javacTarget),
mimaReportSignatureProblems := true,
@@ -56,6 +56,15 @@ object Common extends AutoPlugin {
def onlyOnScala3[T](values: Seq[T]): Def.Initialize[Seq[T]] = Def.setting {
if (scalaVersion.value.startsWith("3")) values else Seq.empty[T]
}
+ def onlyOnScala3Below39[T](values: Seq[T]): Def.Initialize[Seq[T]] =
Def.setting {
+ if (scalaVersion.value.startsWith("3") &&
CrossVersion.partialVersion(scalaVersion.value).exists(_._2 < 9)) values
+ else Seq.empty[T]
+ }
+ def notOnScala39Plus[T](values: Seq[T]): Def.Initialize[Seq[T]] =
Def.setting {
+ if (scalaVersion.value.startsWith("3") &&
CrossVersion.partialVersion(scalaVersion.value).exists(_._2 >= 9))
+ Seq.empty[T]
+ else values
+ }
def scalaMinorVersion: Def.Initialize[Long] = Def.setting {
CrossVersion.partialVersion(scalaVersion.value).get._2 }
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]