From d1bf121d00cc752c56c41e0ef5049120f08eb2a7 Mon Sep 17 00:00:00 2001 From: "Taro L. Saito" Date: Thu, 22 Feb 2024 21:58:16 -0800 Subject: [PATCH 1/3] internal: Remove optional braces for Scala 3 code --- .scalafmt.conf | 1 + .../wvlet/airframe/codec/CompatBase.scala | 6 +- .../wvlet/airframe/codec/AnyCodecCompat.scala | 9 +- .../wvlet/airframe/codec/ScalaCompat.scala | 28 +- .../wvlet/airframe/config/ConfigCompat.scala | 49 +-- .../scala-3/wvlet/airframe/SourceCode.scala | 9 +- .../wvlet/airframe/AirframeSessionImpl.scala | 17 +- .../scala-3/wvlet/airframe/BinderImpl.scala | 64 +-- .../scala-3/wvlet/airframe/DesignImpl.scala | 40 +- .../scala-3/wvlet/airframe/SessionImpl.scala | 7 +- .../main/scala-3/wvlet/airframe/package.scala | 70 ++- .../scala-3/wvlet/airframe/di/DI3Test.scala | 4 +- .../wvlet/airframe/di/TraitFactoryTest.scala | 11 +- .../fluentd/MetricLoggerFactoryCompat.scala | 9 +- .../airframe/http/router/RouterBase.scala | 24 +- .../wvlet/airframe/http/HttpMessageBase.scala | 35 +- .../http/HttpServerExceptionBase.scala | 35 +- .../wvlet/airframe/http/RxRouterBase.scala | 22 +- .../http/client/HttpClientCompat.scala | 20 +- .../wvlet/airframe/jmx/JMXCompat.scala | 13 +- .../airframe/launcher/LauncherCompat.scala | 23 +- .../main/scala-3/wvlet/log/LoggerBase.scala | 157 ++----- .../airframe/parquet/ParquetCompat.scala | 13 +- .../wvlet/airframe/rx/html/HtmlCompat.scala | 6 +- .../wvlet/airframe/rx/html/common.scala | 16 +- .../airframe/surface/SurfaceFactory.scala | 3 +- .../wvlet/airframe/surface/package.scala | 6 +- .../reflect/ReflectSurfaceFactory.scala | 3 +- .../surface/reflect/RuntimeSurface.scala | 3 +- .../surface/reflect/TastySurfaceFactory.scala | 13 +- .../surface/AirSpecBridgeCompat.scala | 3 +- .../reflect/TastySurfaceFactoryTest.scala | 9 +- .../surface/CompileTimeSurfaceFactory.scala | 410 +++++++----------- .../wvlet/airframe/surface/Surface.scala | 6 +- .../airframe/surface/Scala3NewTypeTest.scala | 3 +- 35 files changed, 401 insertions(+), 746 deletions(-) diff --git a/.scalafmt.conf b/.scalafmt.conf index 061ccb2834..c674079d12 100644 --- a/.scalafmt.conf +++ b/.scalafmt.conf @@ -7,6 +7,7 @@ optIn.breaksInsideChains = true docstrings.blankFirstLine = yes rewrite.scala3.convertToNewSyntax = true +rewrite.scala3.removeOptionalBraces = yes // Rewrite import _ to * runner.dialectOverride.allowStarWildcardImport = true // Disable rewrite import => to 'as' diff --git a/airframe-codec/.jvm/src/main/scala-3/wvlet/airframe/codec/CompatBase.scala b/airframe-codec/.jvm/src/main/scala-3/wvlet/airframe/codec/CompatBase.scala index 2dabf6512d..ddbaede6d9 100644 --- a/airframe-codec/.jvm/src/main/scala-3/wvlet/airframe/codec/CompatBase.scala +++ b/airframe-codec/.jvm/src/main/scala-3/wvlet/airframe/codec/CompatBase.scala @@ -15,10 +15,8 @@ package wvlet.airframe.codec import wvlet.airframe.surface.Surface import wvlet.airframe.surface.reflect.ReflectSurfaceFactory -trait CompatBase { - inline def codecOf[A]: MessageCodec[A] = { +trait CompatBase: + inline def codecOf[A]: MessageCodec[A] = MessageCodecFactory.defaultFactory.of[A] - } // TODO Remove this method usage as runtime-reflection in Scala 3 is unstable and slow def surfaceOfClass(cl: Class[?]): Surface = ReflectSurfaceFactory.ofClass(cl) -} diff --git a/airframe-codec/src/main/scala-3/wvlet/airframe/codec/AnyCodecCompat.scala b/airframe-codec/src/main/scala-3/wvlet/airframe/codec/AnyCodecCompat.scala index 926840295b..806e4ba29e 100644 --- a/airframe-codec/src/main/scala-3/wvlet/airframe/codec/AnyCodecCompat.scala +++ b/airframe-codec/src/main/scala-3/wvlet/airframe/codec/AnyCodecCompat.scala @@ -13,11 +13,8 @@ */ package wvlet.airframe.codec -trait AnyCodecCompat { - protected def isEnum(o: Any): Boolean = { - o match { +trait AnyCodecCompat: + protected def isEnum(o: Any): Boolean = + o match case _: scala.runtime.EnumValue => true case _ => false - } - } -} diff --git a/airframe-codec/src/main/scala-3/wvlet/airframe/codec/ScalaCompat.scala b/airframe-codec/src/main/scala-3/wvlet/airframe/codec/ScalaCompat.scala index 9e16c4b419..b72d963c7f 100644 --- a/airframe-codec/src/main/scala-3/wvlet/airframe/codec/ScalaCompat.scala +++ b/airframe-codec/src/main/scala-3/wvlet/airframe/codec/ScalaCompat.scala @@ -2,29 +2,21 @@ package wvlet.airframe.codec import wvlet.airframe.surface.Surface -object ScalaCompat { +object ScalaCompat: - trait MessageCodecBase { - inline def of[A]: MessageCodec[A] = { + trait MessageCodecBase: + inline def of[A]: MessageCodec[A] = MessageCodec.ofSurface(Surface.of[A]).asInstanceOf[MessageCodec[A]] - } - inline def fromJson[A](json: String): A = { + inline def fromJson[A](json: String): A = MessageCodecFactory.defaultFactory.fromJson[A](json) - } - inline def toJson[A](obj: A): String = { + inline def toJson[A](obj: A): String = MessageCodecFactory.defaultFactory.toJson[A](obj) - } - } - trait MessageCodecFactoryBase { self: MessageCodecFactory => - inline def of[A]: MessageCodec[A] = { + trait MessageCodecFactoryBase: + self: MessageCodecFactory => + inline def of[A]: MessageCodec[A] = self.ofSurface(Surface.of[A]).asInstanceOf[MessageCodec[A]] - } - inline def fromJson[A](json: String): A = { + inline def fromJson[A](json: String): A = MessageCodec.of[A].fromJson(json) - } - inline def toJson[A](obj: A): String = { + inline def toJson[A](obj: A): String = MessageCodec.of[A].toJson(obj) - } - } -} diff --git a/airframe-config/src/main/scala-3/wvlet/airframe/config/ConfigCompat.scala b/airframe-config/src/main/scala-3/wvlet/airframe/config/ConfigCompat.scala index fb289e8345..3571c0dbf2 100644 --- a/airframe-config/src/main/scala-3/wvlet/airframe/config/ConfigCompat.scala +++ b/airframe-config/src/main/scala-3/wvlet/airframe/config/ConfigCompat.scala @@ -16,40 +16,33 @@ package wvlet.airframe.config import wvlet.airframe.{Design, SourceCode} import wvlet.airframe.surface.Surface -trait ConfigPackageCompat { self: ConfigurableDesign => - inline def bindConfig[A](config: A)(implicit sourceCode: SourceCode): Design = { +trait ConfigPackageCompat: + self: ConfigurableDesign => + inline def bindConfig[A](config: A)(implicit sourceCode: SourceCode): Design = self.bindConfigInternal[A](Surface.of[A], config)(sourceCode) - } - inline def bindConfigFromYaml[A](yamlFile: String)(implicit sourceCode: SourceCode): Design = { + inline def bindConfigFromYaml[A](yamlFile: String)(implicit sourceCode: SourceCode): Design = self.bindConfigFromYamlInternal[A](Surface.of[A], yamlFile)(sourceCode) - } inline def bindConfigFromYaml[A](yamlFile: String, defaultValue: => A)(implicit sourceCode: SourceCode - ): Design = { + ): Design = self.bindConfigFromYamlInternal[A](Surface.of[A], yamlFile, defaultValue)(sourceCode) - } -} -trait ConfigCompat { +trait ConfigCompat: self: Config => - inline def of[ConfigType]: ConfigType = { + inline def of[ConfigType]: ConfigType = self.ofSurface[ConfigType](Surface.of[ConfigType]) - } - inline def getOrElse[ConfigType](default: => ConfigType): ConfigType = { + inline def getOrElse[ConfigType](default: => ConfigType): ConfigType = self.getOrElseOfSurface[ConfigType](Surface.of[ConfigType], default) - } - inline def defaultValueOf[ConfigType]: ConfigType = { + inline def defaultValueOf[ConfigType]: ConfigType = self.defaultValueOfSurface[ConfigType](Surface.of[ConfigType]) - } - inline def register[ConfigType](config: ConfigType): Config = { + inline def register[ConfigType](config: ConfigType): Config = self.registerOfSurface[ConfigType](Surface.of[ConfigType], config) - } /** * Register the default value of the object as configuration @@ -57,29 +50,21 @@ trait ConfigCompat { * @tparam ConfigType * @return */ - inline def registerDefault[ConfigType]: Config = { + inline def registerDefault[ConfigType]: Config = self.registerDefaultOfSurface[ConfigType](Surface.of[ConfigType]) - } - inline def registerFromYaml[ConfigType](yamlFile: String): Config = { + inline def registerFromYaml[ConfigType](yamlFile: String): Config = self.registerFromYaml[ConfigType](Surface.of[ConfigType], yamlFile) - } - inline def registerFromYamlOrElse[ConfigType](yamlFile: String, defaultValue: => ConfigType): Config = { + inline def registerFromYamlOrElse[ConfigType](yamlFile: String, defaultValue: => ConfigType): Config = self.registerFromYamlOrElse[ConfigType](Surface.of[ConfigType], yamlFile, defaultValue) - } -} -trait YamlReaderCompat { - inline def load[A](resourcePath: String, env: String): A = { +trait YamlReaderCompat: + inline def load[A](resourcePath: String, env: String): A = YamlReader.load[A](Surface.of[A], resourcePath, env) - } - inline def loadMapOf[A](resourcePath: String): Map[String, A] = { + inline def loadMapOf[A](resourcePath: String): Map[String, A] = YamlReader.loadMapOf[A](Surface.of[A], resourcePath) - } - inline def bind[A](prop: Map[AnyRef, AnyRef]): A = { + inline def bind[A](prop: Map[AnyRef, AnyRef]): A = YamlReader.bind[A](Surface.of[A], prop) - } -} diff --git a/airframe-di-macros/src/main/scala-3/wvlet/airframe/SourceCode.scala b/airframe-di-macros/src/main/scala-3/wvlet/airframe/SourceCode.scala index 7fa8a6cdf6..b774d7691f 100644 --- a/airframe-di-macros/src/main/scala-3/wvlet/airframe/SourceCode.scala +++ b/airframe-di-macros/src/main/scala-3/wvlet/airframe/SourceCode.scala @@ -23,18 +23,17 @@ case class SourceCode( fileName: String, line: Int, col: Int -) { +): override def toString = s"${fileName}:${line}" -} -object SourceCode { +object SourceCode: def apply()(implicit code: SourceCode) = code import scala.quoted.* inline implicit def generate: SourceCode = ${ generateImpl } - private def generateImpl(using q: Quotes): Expr[SourceCode] = { + private def generateImpl(using q: Quotes): Expr[SourceCode] = import q.reflect.* val pos = Position.ofMacroExpansion val line = Expr(pos.startLine) @@ -44,5 +43,3 @@ object SourceCode { val srcPath: java.nio.file.Path = java.nio.file.Paths.get(src.path) val fileName = Expr(srcPath.getFileName().toString) '{ SourceCode("", ${ fileName }, ${ line } + 1, ${ column }) } - } -} diff --git a/airframe-di/src/main/scala-3/wvlet/airframe/AirframeSessionImpl.scala b/airframe-di/src/main/scala-3/wvlet/airframe/AirframeSessionImpl.scala index 59f91da649..4c860a60a3 100644 --- a/airframe-di/src/main/scala-3/wvlet/airframe/AirframeSessionImpl.scala +++ b/airframe-di/src/main/scala-3/wvlet/airframe/AirframeSessionImpl.scala @@ -17,13 +17,10 @@ import wvlet.airframe.surface.Surface import java.util.concurrent.ConcurrentHashMap -private[airframe] trait AirframeSessionImpl { self: AirframeSession => - inline override def register[A](instance: A): Unit = { - { - val surface = Surface.of[A] - val owner = self.findOwnerSessionOf(surface).getOrElse(self) - owner.registerInjectee(surface, surface, instance) - () - } - } -} +private[airframe] trait AirframeSessionImpl: + self: AirframeSession => + inline override def register[A](instance: A): Unit = + val surface = Surface.of[A] + val owner = self.findOwnerSessionOf(surface).getOrElse(self) + owner.registerInjectee(surface, surface, instance) + () diff --git a/airframe-di/src/main/scala-3/wvlet/airframe/BinderImpl.scala b/airframe-di/src/main/scala-3/wvlet/airframe/BinderImpl.scala index 3ef0f3ff77..e8cb8d243b 100644 --- a/airframe-di/src/main/scala-3/wvlet/airframe/BinderImpl.scala +++ b/airframe-di/src/main/scala-3/wvlet/airframe/BinderImpl.scala @@ -7,7 +7,8 @@ import wvlet.airframe.Binder.* /** */ -private[airframe] trait BinderImpl[A] extends LogSupport { self: Binder[A] => +private[airframe] trait BinderImpl[A] extends LogSupport: + self: Binder[A] => /** * Bind a singleton instance of B to A @@ -15,30 +16,24 @@ private[airframe] trait BinderImpl[A] extends LogSupport { self: Binder[A] => * @tparam B */ inline def to[B <: A]: DesignWithContext[B] = { - { - // registerTraitFactory[B] - val to = Surface.of[B] - if self.from == to then { - wvlet.log.Logger("wvlet.airframe.Binder").warn("Binding to the same type is not allowed: " + to.toString) - throw new wvlet.airframe.AirframeException.CYCLIC_DEPENDENCY(List(to), SourceCode()) - } - self.design.addBinding[B](SingletonBinding(self.from, to, false, self.sourceCode)) - } + // registerTraitFactory[B] + val to = Surface.of[B] + if self.from == to then + wvlet.log.Logger("wvlet.airframe.Binder").warn("Binding to the same type is not allowed: " + to.toString) + throw new wvlet.airframe.AirframeException.CYCLIC_DEPENDENCY(List(to), SourceCode()) + self.design.addBinding[B](SingletonBinding(self.from, to, false, self.sourceCode)) } inline def toEagerSingletonOf[B <: A]: DesignWithContext[B] = { - { - // registerTraitFactory[B] - val to = Surface.of[B] - if self.from == to then { - wvlet.log.Logger("wvlet.airframe.Binder").warn("Binding to the same type is not allowed: " + to.toString) - throw new wvlet.airframe.AirframeException.CYCLIC_DEPENDENCY(List(to), SourceCode()) - } - self.design.addBinding[B](SingletonBinding(self.from, to, true, self.sourceCode)) - } + // registerTraitFactory[B] + val to = Surface.of[B] + if self.from == to then + wvlet.log.Logger("wvlet.airframe.Binder").warn("Binding to the same type is not allowed: " + to.toString) + throw new wvlet.airframe.AirframeException.CYCLIC_DEPENDENCY(List(to), SourceCode()) + self.design.addBinding[B](SingletonBinding(self.from, to, true, self.sourceCode)) } - inline def toProvider[D1](factory: D1 => A): DesignWithContext[A] = { + inline def toProvider[D1](factory: D1 => A): DesignWithContext[A] = // registerTraitFactory[D1] self.design.addBinding[A]( ProviderBinding( @@ -48,8 +43,7 @@ private[airframe] trait BinderImpl[A] extends LogSupport { self: Binder[A] => SourceCode() ) ) - } - inline def toProvider[D1, D2](factory: (D1, D2) => A): DesignWithContext[A] = { + inline def toProvider[D1, D2](factory: (D1, D2) => A): DesignWithContext[A] = // registerTraitFactory[D1] // registerTraitFactory[D2] self.design.addBinding[A]( @@ -60,8 +54,7 @@ private[airframe] trait BinderImpl[A] extends LogSupport { self: Binder[A] => SourceCode() ) ) - } - inline def toProvider[D1, D2, D3](factory: (D1, D2, D3) => A): DesignWithContext[A] = { + inline def toProvider[D1, D2, D3](factory: (D1, D2, D3) => A): DesignWithContext[A] = // registerTraitFactory[D1] // registerTraitFactory[D2] // registerTraitFactory[D3] @@ -73,8 +66,7 @@ private[airframe] trait BinderImpl[A] extends LogSupport { self: Binder[A] => SourceCode() ) ) - } - inline def toProvider[D1, D2, D3, D4](factory: (D1, D2, D3, D4) => A): DesignWithContext[A] = { + inline def toProvider[D1, D2, D3, D4](factory: (D1, D2, D3, D4) => A): DesignWithContext[A] = // registerTraitFactory[D1] // registerTraitFactory[D2] // registerTraitFactory[D3] @@ -87,8 +79,7 @@ private[airframe] trait BinderImpl[A] extends LogSupport { self: Binder[A] => SourceCode() ) ) - } - inline def toProvider[D1, D2, D3, D4, D5](factory: (D1, D2, D3, D4, D5) => A): DesignWithContext[A] = { + inline def toProvider[D1, D2, D3, D4, D5](factory: (D1, D2, D3, D4, D5) => A): DesignWithContext[A] = // registerTraitFactory[D1] // registerTraitFactory[D2] // registerTraitFactory[D3] @@ -106,9 +97,8 @@ private[airframe] trait BinderImpl[A] extends LogSupport { self: Binder[A] => SourceCode() ) ) - } - inline def toEagerSingletonProvider[D1](factory: D1 => A): DesignWithContext[A] = { + inline def toEagerSingletonProvider[D1](factory: D1 => A): DesignWithContext[A] = // registerTraitFactory[D1] self.design.addBinding[A]( ProviderBinding( @@ -118,8 +108,7 @@ private[airframe] trait BinderImpl[A] extends LogSupport { self: Binder[A] => SourceCode() ) ) - } - inline def toEagerSingletonProvider[D1, D2](factory: (D1, D2) => A): DesignWithContext[A] = { + inline def toEagerSingletonProvider[D1, D2](factory: (D1, D2) => A): DesignWithContext[A] = // registerTraitFactory[D1] // registerTraitFactory[D2] self.design.addBinding[A]( @@ -130,8 +119,7 @@ private[airframe] trait BinderImpl[A] extends LogSupport { self: Binder[A] => SourceCode() ) ) - } - inline def toEagerSingletonProvider[D1, D2, D3](factory: (D1, D2, D3) => A): DesignWithContext[A] = { + inline def toEagerSingletonProvider[D1, D2, D3](factory: (D1, D2, D3) => A): DesignWithContext[A] = // registerTraitFactory[D1] // registerTraitFactory[D2] // registerTraitFactory[D3] @@ -143,8 +131,7 @@ private[airframe] trait BinderImpl[A] extends LogSupport { self: Binder[A] => SourceCode() ) ) - } - inline def toEagerSingletonProvider[D1, D2, D3, D4](factory: (D1, D2, D3, D4) => A): DesignWithContext[A] = { + inline def toEagerSingletonProvider[D1, D2, D3, D4](factory: (D1, D2, D3, D4) => A): DesignWithContext[A] = // registerTraitFactory[D1] // registerTraitFactory[D2] // registerTraitFactory[D3] @@ -157,8 +144,7 @@ private[airframe] trait BinderImpl[A] extends LogSupport { self: Binder[A] => SourceCode() ) ) - } - inline def toEagerSingletonProvider[D1, D2, D3, D4, D5](factory: (D1, D2, D3, D4, D5) => A): DesignWithContext[A] = { + inline def toEagerSingletonProvider[D1, D2, D3, D4, D5](factory: (D1, D2, D3, D4, D5) => A): DesignWithContext[A] = // registerTraitFactory[D1] // registerTraitFactory[D2] // registerTraitFactory[D3] @@ -176,5 +162,3 @@ private[airframe] trait BinderImpl[A] extends LogSupport { self: Binder[A] => SourceCode() ) ) - } -} diff --git a/airframe-di/src/main/scala-3/wvlet/airframe/DesignImpl.scala b/airframe-di/src/main/scala-3/wvlet/airframe/DesignImpl.scala index 1ebf4e6b16..9b146e8097 100644 --- a/airframe-di/src/main/scala-3/wvlet/airframe/DesignImpl.scala +++ b/airframe-di/src/main/scala-3/wvlet/airframe/DesignImpl.scala @@ -8,18 +8,15 @@ import wvlet.log.LogSupport * * Design instance does not hold any duplicate bindings for the same Surface. */ -private[airframe] trait DesignImpl extends LogSupport { self: Design => - inline def bind[A]: Binder[A] = { +private[airframe] trait DesignImpl extends LogSupport: + self: Design => + inline def bind[A]: Binder[A] = registerTraitFactory[A] new Binder(self, Surface.of[A], SourceCode()).asInstanceOf[Binder[A]] - } - inline def remove[A]: Design = { - { - val target = Surface.of[A] - new Design(self.designOptions, self.binding.filterNot(_.from == target), self.hooks) - } - } + inline def remove[A]: Design = + val target = Surface.of[A] + new Design(self.designOptions, self.binding.filterNot(_.from == target), self.hooks) /** * A helper method of creating a new session and an instance of A. This method is useful when you only need to use A @@ -29,25 +26,18 @@ private[airframe] trait DesignImpl extends LogSupport { self: Design => * @tparam A * @return */ - inline def build[A](body: A => Any): Any = { - { - self.withSession { session => - val a = session.build[A] - body(a) - } + inline def build[A](body: A => Any): Any = + self.withSession { session => + val a = session.build[A] + body(a) } - } /** * Execute a given code block by building A using this design, and return B */ inline def run[A, B](body: A => B): B = { - { - self.withSession { session => - val a = session.build[A] - body(a) - } - }.asInstanceOf[B] - } - -} + self.withSession { session => + val a = session.build[A] + body(a) + } + }.asInstanceOf[B] diff --git a/airframe-di/src/main/scala-3/wvlet/airframe/SessionImpl.scala b/airframe-di/src/main/scala-3/wvlet/airframe/SessionImpl.scala index c21d3d392f..cfee4b7eab 100644 --- a/airframe-di/src/main/scala-3/wvlet/airframe/SessionImpl.scala +++ b/airframe-di/src/main/scala-3/wvlet/airframe/SessionImpl.scala @@ -2,7 +2,8 @@ package wvlet.airframe import wvlet.airframe.surface.Surface -private[airframe] trait SessionImpl { self: Session => +private[airframe] trait SessionImpl: + self: Session => /** * Build an instance of A. In general this method is necessary only when creating an entry point of your application. @@ -12,12 +13,10 @@ private[airframe] trait SessionImpl { self: Session => * @return * object */ - inline def build[A]: A = { + inline def build[A]: A = self.get[A](Surface.of[A]) - } /** * Register an instance to the session to control the life cycle of the object under this session. */ def register[A](instance: A): Unit -} diff --git a/airframe-di/src/main/scala-3/wvlet/airframe/package.scala b/airframe-di/src/main/scala-3/wvlet/airframe/package.scala index c3dab207c0..bb2029cb75 100644 --- a/airframe-di/src/main/scala-3/wvlet/airframe/package.scala +++ b/airframe-di/src/main/scala-3/wvlet/airframe/package.scala @@ -39,9 +39,8 @@ import scala.jdk.CollectionConverters.* // This will not be used in Scala 3, but left for the compatibility with Scala 2 val traitFactoryCache = new ConcurrentHashMap[Surface, Session => Any].asScala -def getOrElseUpdateTraitFactoryCache(s: Surface, factory: Session => Any): Session => Any = { +def getOrElseUpdateTraitFactoryCache(s: Surface, factory: Session => Any): Session => Any = traitFactoryCache.getOrElseUpdate(s, factory) -} @deprecated("Instantiating trait is still experimental in Scala 3.3.1", "23.9.1") inline def registerTraitFactory[A]: Unit = { @@ -53,7 +52,7 @@ import scala.quoted.* private def shouldGenerateTrait[A](using tpe: Type[A], q: Quotes -): Boolean = { +): Boolean = import quotes.* import quotes.reflect.* @@ -61,46 +60,41 @@ private def shouldGenerateTrait[A](using val a = t.typeSymbol // Find the public default constructor that has no arguments - val hasPublicDefaultConstructor: Boolean = { + val hasPublicDefaultConstructor: Boolean = val pc = a.primaryConstructor pc.paramSymss.size == 1 && pc.paramSymss(0).size == 0 - } - val hasAbstractMethods: Boolean = { + val hasAbstractMethods: Boolean = a.methodMembers.exists { x => x.flags.is(Flags.Method) && (x.flags.is(Flags.Abstract) || x.flags.is(Flags.Deferred)) } - } val isTaggedType = a.fullName.startsWith("wvlet.airframe.surface.tag.") val isSealedType = a.flags.is(Flags.Sealed) val isStatic = a.flags.is(Flags.JavaStatic) val isLocal = a.flags.is(Flags.Local) val isTrait = a.flags.is(Flags.Trait) - val shouldInstantiateTrait = if !isStatic then { - // = Non static type - // If X is non static type (= local class or trait), - // we need to instantiate it first in order to populate its $outer variables - - // We cannot instantiate path-dependent types - if a.fullName.contains("#") then { - false - } else { - !hasAbstractMethods && hasPublicDefaultConstructor - } - } else if a.isAbstractType then { - // = Abstract type - // We cannot build abstract type X that has abstract methods, so bind[X].to[ConcreteType] - // needs to be found in the design - - // If there is no abstract methods, it might be a trait without any method - !hasAbstractMethods - } else { - // We cannot instantiate any trait or class without the default constructor - // So binding needs to be found in the Design. - hasPublicDefaultConstructor - } + val shouldInstantiateTrait = + if !isStatic then + // = Non static type + // If X is non static type (= local class or trait), + // we need to instantiate it first in order to populate its $outer variables + + // We cannot instantiate path-dependent types + if a.fullName.contains("#") then false + else !hasAbstractMethods && hasPublicDefaultConstructor + else if a.isAbstractType then + // = Abstract type + // We cannot build abstract type X that has abstract methods, so bind[X].to[ConcreteType] + // needs to be found in the design + + // If there is no abstract methods, it might be a trait without any method + !hasAbstractMethods + else + // We cannot instantiate any trait or class without the default constructor + // So binding needs to be found in the Design. + hasPublicDefaultConstructor // Tagged type or sealed class binding should be found in Design val result = isTrait && !isTaggedType && !isSealedType && shouldInstantiateTrait @@ -109,18 +103,16 @@ private def shouldGenerateTrait[A](using // s"has pstr: ${hasPublicDefaultConstructor} is tagged: ${isTaggedType}, has abstract method: ${hasAbstractMethods}") result -} @experimental def registerTraitFactoryImpl[A](using tpe: Type[A], q: Quotes -): quoted.Expr[Unit] = { +): quoted.Expr[Unit] = import quotes.* import quotes.reflect.* - if !shouldGenerateTrait[A] then { - '{} - } else { + if !shouldGenerateTrait[A] then '{} + else val name = "$anon" val parents = List(TypeTree.of[Object], TypeTree.of[A], TypeTree.of[DISupport]) @@ -130,21 +122,19 @@ private def shouldGenerateTrait[A](using val cls = Symbol.newClass(Symbol.spliceOwner, name, parents = parents.map(_.tpe), decls, selfType = None) val sessionMethodSym = cls.declaredMethod("session").head def sessionMethodDef(s: Term) = DefDef(sessionMethodSym, argss => Some(s)) - def newCls(s: Term) = { + def newCls(s: Term) = val clsDef = ClassDef(cls, parents, body = List(sessionMethodDef(s))) val newCls = Typed(Apply(Select(New(TypeIdent(cls)), cls.primaryConstructor), Nil), TypeTree.of[A]) Block(List(clsDef), newCls) - } // Generate code { (s: Session) => new A with DISupport { def sesion: Session = s } } val body = Lambda( owner = Symbol.spliceOwner, tpe = MethodType(List("s"))(_ => List(TypeRepr.of[Session]), _ => TypeRepr.of[A]), - rhsFn = (sym: Symbol, paramRefs: List[Tree]) => { + rhsFn = (sym: Symbol, paramRefs: List[Tree]) => val s = paramRefs.head.asExprOf[Session].asTerm val fn = newCls(s) fn.changeOwner(sym) - } ) // Register trait factory // { (s: Session) => new A with DISupport { def session = s } } @@ -156,5 +146,3 @@ private def shouldGenerateTrait[A](using } ) } - } -} diff --git a/airframe-di/src/test/scala-3/wvlet/airframe/di/DI3Test.scala b/airframe-di/src/test/scala-3/wvlet/airframe/di/DI3Test.scala index 2a7fb26a67..6d9a0dce5d 100644 --- a/airframe-di/src/test/scala-3/wvlet/airframe/di/DI3Test.scala +++ b/airframe-di/src/test/scala-3/wvlet/airframe/di/DI3Test.scala @@ -5,7 +5,7 @@ import wvlet.airframe.newSilentDesign import wvlet.airframe.surface.Surface import wvlet.airspec.AirSpec -object DI3Test extends AirSpec { +object DI3Test extends AirSpec: trait NonAbstractTrait {} @@ -15,5 +15,3 @@ object DI3Test extends AirSpec { } e.stack.contains(Surface.of[NonAbstractTrait]) shouldBe true } - -} diff --git a/airframe-di/src/test/scala-3/wvlet/airframe/di/TraitFactoryTest.scala b/airframe-di/src/test/scala-3/wvlet/airframe/di/TraitFactoryTest.scala index e9c3113e0b..b129e8743e 100644 --- a/airframe-di/src/test/scala-3/wvlet/airframe/di/TraitFactoryTest.scala +++ b/airframe-di/src/test/scala-3/wvlet/airframe/di/TraitFactoryTest.scala @@ -18,21 +18,18 @@ import scala.language.experimental import wvlet.airframe.* import wvlet.airframe.surface.Surface -object TraitFactoryTest extends AirSpec { +object TraitFactoryTest extends AirSpec: trait A test("register trait factory") { - if isScala3 then { - pending("In Scala 3.3.1, creating a new trait instance via macro is still experimental") - } + if isScala3 then pending("In Scala 3.3.1, creating a new trait instance via macro is still experimental") registerTraitFactory[A] traitFactoryCache.get(Surface.of[A]) shouldBe defined } - trait B { + trait B: def hello: Unit - } test("do not create trait factory for abstract classes") { registerTraitFactory[B] @@ -59,5 +56,3 @@ object TraitFactoryTest extends AirSpec { registerTraitFactory[D] traitFactoryCache.get(Surface.of[B]) shouldBe empty } - -} diff --git a/airframe-fluentd/src/main/scala-3/wvlet/airframe/fluentd/MetricLoggerFactoryCompat.scala b/airframe-fluentd/src/main/scala-3/wvlet/airframe/fluentd/MetricLoggerFactoryCompat.scala index 9b4edc5a55..25487386dc 100644 --- a/airframe-fluentd/src/main/scala-3/wvlet/airframe/fluentd/MetricLoggerFactoryCompat.scala +++ b/airframe-fluentd/src/main/scala-3/wvlet/airframe/fluentd/MetricLoggerFactoryCompat.scala @@ -14,14 +14,11 @@ package wvlet.airframe.fluentd import wvlet.airframe.surface.Surface -trait MetricLoggerFactoryCompat { +trait MetricLoggerFactoryCompat: self: MetricLoggerFactory => - inline def getTypedLogger[T <: TaggedMetric]: TypedMetricLogger[T] = { + inline def getTypedLogger[T <: TaggedMetric]: TypedMetricLogger[T] = self.getTypedLoggerInternal[T](Surface.of[T], None) - } - inline def getTypedLoggerWithTagPrefix[T <: TaggedMetric](tagPrefix: String): TypedMetricLogger[T] = { + inline def getTypedLoggerWithTagPrefix[T <: TaggedMetric](tagPrefix: String): TypedMetricLogger[T] = self.getTypedLoggerInternal[T](Surface.of[T], Some(tagPrefix)) - } -} diff --git a/airframe-http/.jvm/src/main/scala-3/wvlet/airframe/http/router/RouterBase.scala b/airframe-http/.jvm/src/main/scala-3/wvlet/airframe/http/router/RouterBase.scala index 5f221fa725..0910dd7262 100644 --- a/airframe-http/.jvm/src/main/scala-3/wvlet/airframe/http/router/RouterBase.scala +++ b/airframe-http/.jvm/src/main/scala-3/wvlet/airframe/http/router/RouterBase.scala @@ -18,42 +18,36 @@ import wvlet.airframe.http.HttpFilterType import wvlet.airframe.surface.Surface import wvlet.airframe.Session -trait RouterBase { self: Router => - inline def add[Controller]: Router = { +trait RouterBase: + self: Router => + inline def add[Controller]: Router = // TODO registerTraitFactory self.addInternal(Surface.of[Controller], Surface.methodsOf[Controller]) - } - inline def andThen[Controller]: Router = { + inline def andThen[Controller]: Router = self.andThen(Router.add[Controller]) - } -} -trait RouterObjectBase { +trait RouterObjectBase: @deprecated("Use RxRouter.of[Controller] instead", "23.5.0") inline def of[Controller]: Router = ${ RouterObjectMacros.routerOf[Controller] } @deprecated("Use RxRouter.of[Controller] instead", "23.5.0") inline def add[Controller]: Router = ${ RouterObjectMacros.routerOf[Controller] } -} -private[router] object RouterObjectMacros { +private[router] object RouterObjectMacros: import scala.quoted.* - def routerOf[Controller: Type](using quotes: Quotes): Expr[Router] = { + def routerOf[Controller: Type](using quotes: Quotes): Expr[Router] = import quotes.* import quotes.reflect.* - if TypeRepr.of[Controller] <:< TypeRepr.of[HttpFilterType] then { + if TypeRepr.of[Controller] <:< TypeRepr.of[HttpFilterType] then '{ wvlet.airframe.registerTraitFactory[Controller] Router(filterSurface = Some(Surface.of[Controller])) } - } else { + else '{ wvlet.airframe.registerTraitFactory[Controller] Router.empty.add[Controller] } - } - } -} diff --git a/airframe-http/src/main/scala-3/wvlet/airframe/http/HttpMessageBase.scala b/airframe-http/src/main/scala-3/wvlet/airframe/http/HttpMessageBase.scala index a3f67cb857..ec1fa4692a 100644 --- a/airframe-http/src/main/scala-3/wvlet/airframe/http/HttpMessageBase.scala +++ b/airframe-http/src/main/scala-3/wvlet/airframe/http/HttpMessageBase.scala @@ -15,43 +15,30 @@ package wvlet.airframe.http import wvlet.airframe.codec.{MessageCodec, MessageCodecFactory} -trait HttpMessageBase[Raw] { +trait HttpMessageBase[Raw]: private def self: HttpMessage[Raw] = this.asInstanceOf[HttpMessage[Raw]] - inline def withJsonOf[A](a: A): Raw = { + inline def withJsonOf[A](a: A): Raw = self.withJson(MessageCodec.of[A].toJson(a)) - } - inline def withJsonOf[A](a: A, codecFactory: MessageCodecFactory): Raw = { + inline def withJsonOf[A](a: A, codecFactory: MessageCodecFactory): Raw = self.withJson(codecFactory.of[A].toJson(a)) - } - inline def withMsgPackOf[A](a: A): Raw = { + inline def withMsgPackOf[A](a: A): Raw = self.withMsgPack(MessageCodec.of[A].toMsgPack(a)) - } - inline def withMsgPackOf[A](a: A, codecFactory: MessageCodecFactory): Raw = { + inline def withMsgPackOf[A](a: A, codecFactory: MessageCodecFactory): Raw = self.withMsgPack(codecFactory.of[A].toMsgPack(a)) - } /** * Set the content body using a given object. Encoding can be JSON or MsgPack based on Content-Type header. */ - inline def withContentOf[A](a: A): Raw = { - if self.isContentTypeMsgPack then { - self.withMsgPack(MessageCodec.of[A].toMsgPack(a)) - } else { - self.withJson(MessageCodec.of[A].toJson(a)) - } - } + inline def withContentOf[A](a: A): Raw = + if self.isContentTypeMsgPack then self.withMsgPack(MessageCodec.of[A].toMsgPack(a)) + else self.withJson(MessageCodec.of[A].toJson(a)) /** * Set the content body using a given object and codec factory. Encoding can be JSON or MsgPack based on Content-Type * header. */ - inline def withContentOf[A](a: A, codecFactory: MessageCodecFactory): Raw = { - if self.isContentTypeMsgPack then { - self.withMsgPack(codecFactory.of[A].toMsgPack(a)) - } else { - self.withJson(codecFactory.of[A].toJson(a)) - } - } -} + inline def withContentOf[A](a: A, codecFactory: MessageCodecFactory): Raw = + if self.isContentTypeMsgPack then self.withMsgPack(codecFactory.of[A].toMsgPack(a)) + else self.withJson(codecFactory.of[A].toJson(a)) diff --git a/airframe-http/src/main/scala-3/wvlet/airframe/http/HttpServerExceptionBase.scala b/airframe-http/src/main/scala-3/wvlet/airframe/http/HttpServerExceptionBase.scala index e48765eb63..9e1b084cb0 100644 --- a/airframe-http/src/main/scala-3/wvlet/airframe/http/HttpServerExceptionBase.scala +++ b/airframe-http/src/main/scala-3/wvlet/airframe/http/HttpServerExceptionBase.scala @@ -15,45 +15,32 @@ package wvlet.airframe.http import wvlet.airframe.codec.{MessageCodec, MessageCodecFactory} -trait HttpServerExceptionBase { +trait HttpServerExceptionBase: // WARNING: Using a self reference hits compiler VerifyError https://github.com/lampepfl/dotty/issues/9270 // self: HttpServerException => private def self: HttpServerException = this.asInstanceOf[HttpServerException] - inline def withJsonOf[A](a: A): HttpServerException = { + inline def withJsonOf[A](a: A): HttpServerException = self.withJson(MessageCodec.of[A].toJson(a)) - } - inline def withJsonOf[A](a: A, codecFactory: MessageCodecFactory): HttpServerException = { + inline def withJsonOf[A](a: A, codecFactory: MessageCodecFactory): HttpServerException = self.withJson(codecFactory.of[A].toJson(a)) - } - inline def withMsgPackOf[A](a: A): HttpServerException = { + inline def withMsgPackOf[A](a: A): HttpServerException = self.withMsgPack(MessageCodec.of[A].toMsgPack(a)) - } - inline def withMsgPackOf[A](a: A, codecFactory: MessageCodecFactory): HttpServerException = { + inline def withMsgPackOf[A](a: A, codecFactory: MessageCodecFactory): HttpServerException = self.withMsgPack(codecFactory.of[A].toMsgPack(a)) - } /** * Set the content body using a given object. Encoding can be JSON or MsgPack based on Content-Type header. */ - inline def withContentOf[A](a: A): HttpServerException = { - if self.isContentTypeMsgPack then { - self.withMsgPack(MessageCodec.of[A].toMsgPack(a)) - } else { - self.withJson(MessageCodec.of[A].toJson(a)) - } - } + inline def withContentOf[A](a: A): HttpServerException = + if self.isContentTypeMsgPack then self.withMsgPack(MessageCodec.of[A].toMsgPack(a)) + else self.withJson(MessageCodec.of[A].toJson(a)) /** * Set the content body using a given object and codec factory. Encoding can be JSON or MsgPack based on Content-Type * header. */ - inline def withContentOf[A](a: A, codecFactory: MessageCodecFactory): HttpServerException = { - if self.isContentTypeMsgPack then { - self.withMsgPack(codecFactory.of[A].toMsgPack(a)) - } else { - self.withJson(codecFactory.of[A].toJson(a)) - } - } -} + inline def withContentOf[A](a: A, codecFactory: MessageCodecFactory): HttpServerException = + if self.isContentTypeMsgPack then self.withMsgPack(codecFactory.of[A].toMsgPack(a)) + else self.withJson(codecFactory.of[A].toJson(a)) diff --git a/airframe-http/src/main/scala-3/wvlet/airframe/http/RxRouterBase.scala b/airframe-http/src/main/scala-3/wvlet/airframe/http/RxRouterBase.scala index 7962d06608..d121ddc483 100644 --- a/airframe-http/src/main/scala-3/wvlet/airframe/http/RxRouterBase.scala +++ b/airframe-http/src/main/scala-3/wvlet/airframe/http/RxRouterBase.scala @@ -17,31 +17,25 @@ import wvlet.airframe.http.RxHttpFilter import wvlet.airframe.http.RxRouter import wvlet.airframe.surface.Surface -trait RxRouterObjectBase { - inline def of[Controller]: RxRouter = { +trait RxRouterObjectBase: + inline def of[Controller]: RxRouter = // wvlet.airframe.registerTraitFactory[Controller] RxRouter.EndpointNode(Surface.of[Controller], Surface.methodsOf[Controller], None) - } - inline def filter[Filter <: RxHttpFilter]: RxRouter.FilterNode = { + inline def filter[Filter <: RxHttpFilter]: RxRouter.FilterNode = // wvlet.airframe.registerTraitFactory[Filter] RxRouter.FilterNode(None, Surface.of[Filter]) - } - inline def filter[Filter <: RxHttpFilter](filterInstance: Filter): RxRouter.FilterNode = { + inline def filter[Filter <: RxHttpFilter](filterInstance: Filter): RxRouter.FilterNode = // wvlet.airframe.registerTraitFactory[Filter] RxRouter.FilterNode(None, Surface.of[Filter], Some(filterInstance)) - } -} -trait RxRouteFilterBase { self: RxRouter.FilterNode => - inline def andThen[Filter <: RxHttpFilter]: RxRouter.FilterNode = { +trait RxRouteFilterBase: + self: RxRouter.FilterNode => + inline def andThen[Filter <: RxHttpFilter]: RxRouter.FilterNode = // wvlet.airframe.registerTraitFactory[Filter] val next = RxRouter.FilterNode(None, Surface.of[Filter]) self.andThen(next) - } - inline def andThen[Filter <: RxHttpFilter](filterInstance: Filter): RxRouter.FilterNode = { + inline def andThen[Filter <: RxHttpFilter](filterInstance: Filter): RxRouter.FilterNode = self.andThen(RxRouter.FilterNode(None, Surface.of[Filter], Some(filterInstance))) - } -} diff --git a/airframe-http/src/main/scala-3/wvlet/airframe/http/client/HttpClientCompat.scala b/airframe-http/src/main/scala-3/wvlet/airframe/http/client/HttpClientCompat.scala index 567d917f1d..5d434af6cb 100644 --- a/airframe-http/src/main/scala-3/wvlet/airframe/http/client/HttpClientCompat.scala +++ b/airframe-http/src/main/scala-3/wvlet/airframe/http/client/HttpClientCompat.scala @@ -23,7 +23,8 @@ import scala.concurrent.Future /** * Scala 3 specific helper method to make an RPC request */ -trait SyncClientCompat { self: SyncClient => +trait SyncClientCompat: + self: SyncClient => /** * Read the response as a specified type @@ -35,27 +36,22 @@ trait SyncClientCompat { self: SyncClient => * @throws HttpClientException * if failed to read or process the response */ - inline def readAs[Resp](req: Request): Resp = { + inline def readAs[Resp](req: Request): Resp = self.readAsInternal[Resp](req, Surface.of[Resp]) - } inline def call[Req, Resp]( req: Request, requestContent: Req - ): Resp = { + ): Resp = self.callInternal[Req, Resp](req, Surface.of[Req], Surface.of[Resp], requestContent) - } -} -trait AsyncClientCompat { self: AsyncClient => - inline def readAs[Resp](req: Request): Rx[Resp] = { +trait AsyncClientCompat: + self: AsyncClient => + inline def readAs[Resp](req: Request): Rx[Resp] = self.readAsInternal[Resp](req, Surface.of[Resp]) - } inline def call[Req, Resp]( req: Request, requestContent: Req - ): Rx[Resp] = { + ): Rx[Resp] = self.callInternal[Req, Resp](req, Surface.of[Req], Surface.of[Resp], requestContent) - } -} diff --git a/airframe-jmx/src/main/scala-3/wvlet/airframe/jmx/JMXCompat.scala b/airframe-jmx/src/main/scala-3/wvlet/airframe/jmx/JMXCompat.scala index d0912d6526..2b8089f4c5 100644 --- a/airframe-jmx/src/main/scala-3/wvlet/airframe/jmx/JMXCompat.scala +++ b/airframe-jmx/src/main/scala-3/wvlet/airframe/jmx/JMXCompat.scala @@ -15,15 +15,12 @@ package wvlet.airframe.jmx import wvlet.airframe.surface.Surface -trait JMXMBeanCompat { - inline def of[A](obj: A): JMXMBean = { +trait JMXMBeanCompat: + inline def of[A](obj: A): JMXMBean = JMXMBean.of(obj, Surface.of[A], Surface.methodsOf[A]) - } -} -trait JMXRegistryCompat { self: JMXRegistry => - inline def register[A](obj: A): Unit = { +trait JMXRegistryCompat: + self: JMXRegistry => + inline def register[A](obj: A): Unit = val mbean = JMXMBean.of[A](obj) self.register(mbean, obj) - } -} diff --git a/airframe-launcher/src/main/scala-3/wvlet/airframe/launcher/LauncherCompat.scala b/airframe-launcher/src/main/scala-3/wvlet/airframe/launcher/LauncherCompat.scala index 36940399c2..02530cac58 100644 --- a/airframe-launcher/src/main/scala-3/wvlet/airframe/launcher/LauncherCompat.scala +++ b/airframe-launcher/src/main/scala-3/wvlet/airframe/launcher/LauncherCompat.scala @@ -18,7 +18,7 @@ import wvlet.airframe.launcher.Launcher.newCommandLauncher import wvlet.airframe.surface.reflect.ReflectSurfaceFactory import wvlet.airframe.surface.Surface -trait LauncherCompat { +trait LauncherCompat: /** * Create a new Launcher of the given type @@ -26,20 +26,18 @@ trait LauncherCompat { * @tparam A * @return */ - inline def of[A]: Launcher = { + inline def of[A]: Launcher = val cl = newCommandLauncher(Surface.of[A], Surface.methodsOf[A], name = "", description = "") Launcher(LauncherConfig(), cl) - } inline def execute[A](argLine: String): A = execute[A](CommandLineTokenizer.tokenize(argLine)) - inline def execute[A](args: Array[String]): A = { + inline def execute[A](args: Array[String]): A = val l = of[A] val result = l.execute(args) result.getRootInstance.asInstanceOf[A] - } -} -trait LauncherBaseCompat { self: Launcher => +trait LauncherBaseCompat: + self: Launcher => /** * Add a sub command module to the launcher @@ -50,14 +48,11 @@ trait LauncherBaseCompat { self: Launcher => * @tparam M * @return */ - inline def addModule[M](name: String, description: String): Launcher = { + inline def addModule[M](name: String, description: String): Launcher = Launcher(config, mainLauncher.addCommandModule[M](name, description)) - } -} -trait CommandLauncherBaseCompat { self: CommandLauncher => - inline private[launcher] def addCommandModule[B](name: String, description: String): CommandLauncher = { +trait CommandLauncherBaseCompat: + self: CommandLauncher => + inline private[launcher] def addCommandModule[B](name: String, description: String): CommandLauncher = val subLauncher = Launcher.newCommandLauncher(Surface.of[B], Surface.methodsOf[B], name, description) add(name, description, subLauncher) - } -} diff --git a/airframe-log/src/main/scala-3/wvlet/log/LoggerBase.scala b/airframe-log/src/main/scala-3/wvlet/log/LoggerBase.scala index 746aa505ea..9a13afc0d2 100644 --- a/airframe-log/src/main/scala-3/wvlet/log/LoggerBase.scala +++ b/airframe-log/src/main/scala-3/wvlet/log/LoggerBase.scala @@ -15,129 +15,68 @@ package wvlet.log /** */ -trait LoggerBase { self: Logger => +trait LoggerBase: + self: Logger => - inline def error(inline message: Any): Unit = { - if self.isEnabled(LogLevel.ERROR) then { - self.log(LogLevel.ERROR, LoggerMacros.sourcePos(), message) - } - } - inline def warn(inline message: Any): Unit = { - if self.isEnabled(LogLevel.WARN) then { - self.log(LogLevel.WARN, LoggerMacros.sourcePos(), message) - } - } - inline def info(inline message: Any): Unit = { - if self.isEnabled(LogLevel.INFO) then { - self.log(LogLevel.INFO, LoggerMacros.sourcePos(), message) - } - } - inline def debug(inline message: Any): Unit = { - if self.isEnabled(LogLevel.DEBUG) then { - self.log(LogLevel.DEBUG, LoggerMacros.sourcePos(), message) - } - } - inline def trace(inline message: Any): Unit = { - if self.isEnabled(LogLevel.TRACE) then { - self.log(LogLevel.TRACE, LoggerMacros.sourcePos(), message) - } - } - inline def error(inline message: Any, inline cause: Throwable): Unit = { - if self.isEnabled(LogLevel.ERROR) then { - self.logWithCause(LogLevel.ERROR, LoggerMacros.sourcePos(), message, cause) - } - } - inline def warn(inline message: Any, inline cause: Throwable): Unit = { - if self.isEnabled(LogLevel.WARN) then { - self.logWithCause(LogLevel.WARN, LoggerMacros.sourcePos(), message, cause) - } - } - inline def info(inline message: Any, inline cause: Throwable): Unit = { - if self.isEnabled(LogLevel.INFO) then { - self.logWithCause(LogLevel.INFO, LoggerMacros.sourcePos(), message, cause) - } - } - inline def debug(inline message: Any, inline cause: Throwable): Unit = { - if self.isEnabled(LogLevel.DEBUG) then { - self.logWithCause(LogLevel.DEBUG, LoggerMacros.sourcePos(), message, cause) - } - } - inline def trace(inline message: Any, inline cause: Throwable): Unit = { - if self.isEnabled(LogLevel.TRACE) then { - self.logWithCause(LogLevel.TRACE, LoggerMacros.sourcePos(), message, cause) - } - } -} + inline def error(inline message: Any): Unit = + if self.isEnabled(LogLevel.ERROR) then self.log(LogLevel.ERROR, LoggerMacros.sourcePos(), message) + inline def warn(inline message: Any): Unit = + if self.isEnabled(LogLevel.WARN) then self.log(LogLevel.WARN, LoggerMacros.sourcePos(), message) + inline def info(inline message: Any): Unit = + if self.isEnabled(LogLevel.INFO) then self.log(LogLevel.INFO, LoggerMacros.sourcePos(), message) + inline def debug(inline message: Any): Unit = + if self.isEnabled(LogLevel.DEBUG) then self.log(LogLevel.DEBUG, LoggerMacros.sourcePos(), message) + inline def trace(inline message: Any): Unit = + if self.isEnabled(LogLevel.TRACE) then self.log(LogLevel.TRACE, LoggerMacros.sourcePos(), message) + inline def error(inline message: Any, inline cause: Throwable): Unit = + if self.isEnabled(LogLevel.ERROR) then self.logWithCause(LogLevel.ERROR, LoggerMacros.sourcePos(), message, cause) + inline def warn(inline message: Any, inline cause: Throwable): Unit = + if self.isEnabled(LogLevel.WARN) then self.logWithCause(LogLevel.WARN, LoggerMacros.sourcePos(), message, cause) + inline def info(inline message: Any, inline cause: Throwable): Unit = + if self.isEnabled(LogLevel.INFO) then self.logWithCause(LogLevel.INFO, LoggerMacros.sourcePos(), message, cause) + inline def debug(inline message: Any, inline cause: Throwable): Unit = + if self.isEnabled(LogLevel.DEBUG) then self.logWithCause(LogLevel.DEBUG, LoggerMacros.sourcePos(), message, cause) + inline def trace(inline message: Any, inline cause: Throwable): Unit = + if self.isEnabled(LogLevel.TRACE) then self.logWithCause(LogLevel.TRACE, LoggerMacros.sourcePos(), message, cause) /** */ -trait LoggingMethods extends Serializable { +trait LoggingMethods extends Serializable: protected def logger: Logger - inline protected def error(inline message: Any): Unit = { - if logger.isEnabled(LogLevel.ERROR) then { - logger.log(LogLevel.ERROR, LoggerMacros.sourcePos(), message) - } - } - inline protected def warn(inline message: Any): Unit = { - if logger.isEnabled(LogLevel.WARN) then { - logger.log(LogLevel.WARN, LoggerMacros.sourcePos(), message) - } - } - inline protected def info(inline message: Any): Unit = { - if logger.isEnabled(LogLevel.INFO) then { - logger.log(LogLevel.INFO, LoggerMacros.sourcePos(), message) - } - } - inline protected def debug(inline message: Any): Unit = { - if logger.isEnabled(LogLevel.DEBUG) then { - logger.log(LogLevel.DEBUG, LoggerMacros.sourcePos(), message) - } - } - inline protected def trace(inline message: Any): Unit = { - if logger.isEnabled(LogLevel.TRACE) then { - logger.log(LogLevel.TRACE, LoggerMacros.sourcePos(), message) - } - } - inline protected def logAt(inline logLevel: LogLevel, inline message: Any): Unit = { - if logger.isEnabled(logLevel) then { - logger.log(logLevel, LoggerMacros.sourcePos(), message) - } - } + inline protected def error(inline message: Any): Unit = + if logger.isEnabled(LogLevel.ERROR) then logger.log(LogLevel.ERROR, LoggerMacros.sourcePos(), message) + inline protected def warn(inline message: Any): Unit = + if logger.isEnabled(LogLevel.WARN) then logger.log(LogLevel.WARN, LoggerMacros.sourcePos(), message) + inline protected def info(inline message: Any): Unit = + if logger.isEnabled(LogLevel.INFO) then logger.log(LogLevel.INFO, LoggerMacros.sourcePos(), message) + inline protected def debug(inline message: Any): Unit = + if logger.isEnabled(LogLevel.DEBUG) then logger.log(LogLevel.DEBUG, LoggerMacros.sourcePos(), message) + inline protected def trace(inline message: Any): Unit = + if logger.isEnabled(LogLevel.TRACE) then logger.log(LogLevel.TRACE, LoggerMacros.sourcePos(), message) + inline protected def logAt(inline logLevel: LogLevel, inline message: Any): Unit = + if logger.isEnabled(logLevel) then logger.log(logLevel, LoggerMacros.sourcePos(), message) - inline protected def error(inline message: Any, inline cause: Throwable): Unit = { - if logger.isEnabled(LogLevel.ERROR) then { + inline protected def error(inline message: Any, inline cause: Throwable): Unit = + if logger.isEnabled(LogLevel.ERROR) then logger.logWithCause(LogLevel.ERROR, LoggerMacros.sourcePos(), message, cause) - } - } - inline protected def warn(inline message: Any, inline cause: Throwable): Unit = { - if logger.isEnabled(LogLevel.WARN) then { - logger.logWithCause(LogLevel.WARN, LoggerMacros.sourcePos(), message, cause) - } - } - inline protected def info(inline message: Any, inline cause: Throwable): Unit = { - if logger.isEnabled(LogLevel.INFO) then { - logger.logWithCause(LogLevel.INFO, LoggerMacros.sourcePos(), message, cause) - } - } - inline protected def debug(inline message: Any, inline cause: Throwable): Unit = { - if logger.isEnabled(LogLevel.DEBUG) then { + inline protected def warn(inline message: Any, inline cause: Throwable): Unit = + if logger.isEnabled(LogLevel.WARN) then logger.logWithCause(LogLevel.WARN, LoggerMacros.sourcePos(), message, cause) + inline protected def info(inline message: Any, inline cause: Throwable): Unit = + if logger.isEnabled(LogLevel.INFO) then logger.logWithCause(LogLevel.INFO, LoggerMacros.sourcePos(), message, cause) + inline protected def debug(inline message: Any, inline cause: Throwable): Unit = + if logger.isEnabled(LogLevel.DEBUG) then logger.logWithCause(LogLevel.DEBUG, LoggerMacros.sourcePos(), message, cause) - } - } - inline protected def trace(inline message: Any, inline cause: Throwable): Unit = { - if logger.isEnabled(LogLevel.TRACE) then { + inline protected def trace(inline message: Any, inline cause: Throwable): Unit = + if logger.isEnabled(LogLevel.TRACE) then logger.logWithCause(LogLevel.TRACE, LoggerMacros.sourcePos(), message, cause) - } - } -} -object LoggerMacros { +object LoggerMacros: import scala.quoted.* inline def sourcePos(): LogSource = ${ sourcePos } - private def sourcePos(using q: Quotes): Expr[wvlet.log.LogSource] = { + private def sourcePos(using q: Quotes): Expr[wvlet.log.LogSource] = import q.reflect.* val pos = Position.ofMacroExpansion val line = Expr(pos.startLine) @@ -148,5 +87,3 @@ object LoggerMacros { // val path = Expr(srcPath.toFile.getPath) val fileName = Expr(srcPath.getFileName().toString) '{ wvlet.log.LogSource("", ${ fileName }, ${ line } + 1, ${ column }) } - } -} diff --git a/airframe-parquet/src/main/scala-3/wvlet/airframe/parquet/ParquetCompat.scala b/airframe-parquet/src/main/scala-3/wvlet/airframe/parquet/ParquetCompat.scala index a23bb71410..c676548f91 100644 --- a/airframe-parquet/src/main/scala-3/wvlet/airframe/parquet/ParquetCompat.scala +++ b/airframe-parquet/src/main/scala-3/wvlet/airframe/parquet/ParquetCompat.scala @@ -4,7 +4,7 @@ import org.apache.hadoop.conf.Configuration import org.apache.parquet.hadoop.{ParquetReader, ParquetWriter} import wvlet.airframe.surface.Surface -trait ParquetCompat { +trait ParquetCompat: inline def newWriter[A]( path: String, @@ -12,26 +12,21 @@ trait ParquetCompat { hadoopConf: Configuration = new Configuration(), config: ParquetWriterAdapter.Builder[A] => ParquetWriterAdapter.Builder[A] = identity[ParquetWriterAdapter.Builder[A]](_) - ): ParquetWriter[A] = { + ): ParquetWriter[A] = Parquet.newObjectWriter[A](Surface.of[A], path, hadoopConf, config) - } inline def newReader[A]( path: String, // Hadoop filesystem specific configuration, e.g., fs.s3a.access.key hadoopConf: Configuration = new Configuration(), config: ParquetReader.Builder[A] => ParquetReader.Builder[A] = identity[ParquetReader.Builder[A]](_) - ): ParquetReader[A] = { + ): ParquetReader[A] = Parquet.newObjectReader[A](Surface.of[A], path, hadoopConf, config) - } inline def query[A]( path: String, sql: String, hadoopConf: Configuration = new Configuration(), config: ParquetReader.Builder[A] => ParquetReader.Builder[A] = identity[ParquetReader.Builder[A]](_) - ): ParquetReader[A] = { + ): ParquetReader[A] = Parquet.queryObject[A](Surface.of[A], path, sql, hadoopConf, config) - } - -} diff --git a/airframe-rx-html/src/main/scala-3/wvlet/airframe/rx/html/HtmlCompat.scala b/airframe-rx-html/src/main/scala-3/wvlet/airframe/rx/html/HtmlCompat.scala index 5dca67587a..91e5f77f63 100644 --- a/airframe-rx-html/src/main/scala-3/wvlet/airframe/rx/html/HtmlCompat.scala +++ b/airframe-rx-html/src/main/scala-3/wvlet/airframe/rx/html/HtmlCompat.scala @@ -13,14 +13,12 @@ */ package wvlet.airframe.rx.html -object HtmlCompat { +object HtmlCompat: /** * Extracting the source code of rxElement for demoing purpose * @param rxElements * @return */ - private[rx] def extractCode(rxElements: RxElement*): RxCode = { + private[rx] def extractCode(rxElements: RxElement*): RxCode = ??? - } -} diff --git a/airframe-rx-html/src/main/scala-3/wvlet/airframe/rx/html/common.scala b/airframe-rx-html/src/main/scala-3/wvlet/airframe/rx/html/common.scala index a0be426496..436528c5ac 100644 --- a/airframe-rx-html/src/main/scala-3/wvlet/airframe/rx/html/common.scala +++ b/airframe-rx-html/src/main/scala-3/wvlet/airframe/rx/html/common.scala @@ -25,20 +25,16 @@ import scala.language.{higherKinds, implicitConversions} * @return * rendered element or empty */ -def when(cond: Boolean, body: => HtmlNode): HtmlNode = { - if cond then { - body - } else { - HtmlNode.empty - } -} +def when(cond: Boolean, body: => HtmlNode): HtmlNode = + if cond then body + else HtmlNode.empty implicit def embedAsNode[A: EmbeddableNode](v: A): RxElement = Embedded(v) @implicitNotFound(msg = "Unsupported type as an attribute value") private[html] trait EmbeddableAttribute[X] -private[html] object EmbeddableAttribute { +private[html] object EmbeddableAttribute: type EA[A] = EmbeddableAttribute[A] @inline implicit def embedNone: EA[None.type] = null @@ -66,12 +62,11 @@ private[html] object EmbeddableAttribute { @inline implicit def embedRxOption[C[x] <: RxOption[x], A: EA]: EA[C[A]] = null @inline implicit def embedSeq[C[x] <: Iterable[x], A: EA]: EA[C[A]] = null -} @implicitNotFound(msg = "Unsupported type as an HtmlNode") private[html] trait EmbeddableNode[A] -private[html] object EmbeddableNode extends compat.PlatformEmbeddableNode { +private[html] object EmbeddableNode extends compat.PlatformEmbeddableNode: type EN[A] = EmbeddableNode[A] @inline implicit def embedNil: EN[Nil.type] = null @@ -105,4 +100,3 @@ private[html] object EmbeddableNode extends compat.PlatformEmbeddableNode { @inline implicit def embedSeq[C[x] <: Iterable[x], A: EN]: EN[C[A]] = null @inline implicit def embedOption[C[x] <: Option[x], A: EN]: EN[C[A]] = null -} diff --git a/airframe-surface/.js/src/main/scala-3/wvlet/airframe/surface/SurfaceFactory.scala b/airframe-surface/.js/src/main/scala-3/wvlet/airframe/surface/SurfaceFactory.scala index 76b0f3e9a6..4232b633fe 100644 --- a/airframe-surface/.js/src/main/scala-3/wvlet/airframe/surface/SurfaceFactory.scala +++ b/airframe-surface/.js/src/main/scala-3/wvlet/airframe/surface/SurfaceFactory.scala @@ -15,10 +15,9 @@ package wvlet.airframe.surface /** */ -object SurfaceFactory { +object SurfaceFactory: inline def of[A]: Surface = ${ CompileTimeSurfaceFactory.surfaceOf[A] } inline def methodsOf[A]: Seq[MethodSurface] = ${ CompileTimeSurfaceFactory.methodsOf[A] } // TODO support inner clases in Scala.js def localSurfaceOf[A](context: Any): Surface = ??? -} diff --git a/airframe-surface/.jvm/src/main/scala-3/wvlet/airframe/surface/package.scala b/airframe-surface/.jvm/src/main/scala-3/wvlet/airframe/surface/package.scala index c7789ac1ed..e6bd2dc56f 100644 --- a/airframe-surface/.jvm/src/main/scala-3/wvlet/airframe/surface/package.scala +++ b/airframe-surface/.jvm/src/main/scala-3/wvlet/airframe/surface/package.scala @@ -4,13 +4,11 @@ import java.util.concurrent.ConcurrentHashMap import scala.collection.mutable import scala.jdk.CollectionConverters.* -package object surface { +package object surface: val surfaceCache = new ConcurrentHashMap[String, Surface]().asScala val methodSurfaceCache = new ConcurrentHashMap[String, Seq[MethodSurface]]().asScala - def getCached(fullName: String): Surface = { + def getCached(fullName: String): Surface = surfaceCache(fullName) - } def newCacheMap[A, B]: scala.collection.mutable.Map[A, B] = new mutable.WeakHashMap[A, B]() -} diff --git a/airframe-surface/.jvm/src/main/scala-3/wvlet/airframe/surface/reflect/ReflectSurfaceFactory.scala b/airframe-surface/.jvm/src/main/scala-3/wvlet/airframe/surface/reflect/ReflectSurfaceFactory.scala index ab512ffde9..2ad20c72cf 100644 --- a/airframe-surface/.jvm/src/main/scala-3/wvlet/airframe/surface/reflect/ReflectSurfaceFactory.scala +++ b/airframe-surface/.jvm/src/main/scala-3/wvlet/airframe/surface/reflect/ReflectSurfaceFactory.scala @@ -16,7 +16,6 @@ package wvlet.airframe.surface.reflect import wvlet.log.LogSupport import wvlet.airframe.surface.* -object ReflectSurfaceFactory { +object ReflectSurfaceFactory: def ofClass(cl: Class[?]): Surface = TastySurfaceFactory.ofClass(cl) def methodsOfClass(cl: Class[?]): Seq[MethodSurface] = TastySurfaceFactory.methodsOfClass(cl) -} diff --git a/airframe-surface/.jvm/src/main/scala-3/wvlet/airframe/surface/reflect/RuntimeSurface.scala b/airframe-surface/.jvm/src/main/scala-3/wvlet/airframe/surface/reflect/RuntimeSurface.scala index 0a0b749402..ca7551572c 100644 --- a/airframe-surface/.jvm/src/main/scala-3/wvlet/airframe/surface/reflect/RuntimeSurface.scala +++ b/airframe-surface/.jvm/src/main/scala-3/wvlet/airframe/surface/reflect/RuntimeSurface.scala @@ -19,6 +19,5 @@ import wvlet.airframe.surface.CompileTimeSurfaceFactory /** */ -object RuntimeSurface extends LogSupport { +object RuntimeSurface extends LogSupport: inline def of[A]: Surface = ${ CompileTimeSurfaceFactory.surfaceOf[A] } -} diff --git a/airframe-surface/.jvm/src/main/scala-3/wvlet/airframe/surface/reflect/TastySurfaceFactory.scala b/airframe-surface/.jvm/src/main/scala-3/wvlet/airframe/surface/reflect/TastySurfaceFactory.scala index 60db5df108..d6dbf4e086 100644 --- a/airframe-surface/.jvm/src/main/scala-3/wvlet/airframe/surface/reflect/TastySurfaceFactory.scala +++ b/airframe-surface/.jvm/src/main/scala-3/wvlet/airframe/surface/reflect/TastySurfaceFactory.scala @@ -8,7 +8,7 @@ import java.util.concurrent.ConcurrentHashMap import scala.quoted.* import scala.tasty.inspector.* -object TastySurfaceFactory extends LogSupport { +object TastySurfaceFactory extends LogSupport: given staging.Compiler = staging.Compiler.make(getClass.getClassLoader) @@ -17,7 +17,7 @@ object TastySurfaceFactory extends LogSupport { import scala.jdk.CollectionConverters.* private val cache = new ConcurrentHashMap[Class[_], Surface]().asScala - def ofClass(cl: Class[?]): Surface = { + def ofClass(cl: Class[?]): Surface = debug(s"ofClass: ${cl}") cache.getOrElseUpdate( cl, { @@ -28,19 +28,17 @@ object TastySurfaceFactory extends LogSupport { val tastyType = quotes.reflect.TypeRepr.typeConstructorOf(cl) debug(tastyType) val f = new CompileTimeSurfaceFactory(using quotes) - tastyType match { + tastyType match case t if t.show.endsWith(".") => // Example use case is MessageCodec.of[Any] or case objects f.surfaceFromClass(cl) case _ => f.surfaceOf(tastyType.asType) - } } } ) - } - def methodsOfClass(cl: Class[?]): Seq[MethodSurface] = { + def methodsOfClass(cl: Class[?]): Seq[MethodSurface] = // Generates Surface from a runtime class val code: Seq[MethodSurface] = staging.run { (quotes: Quotes) ?=> import quotes.reflect.* @@ -50,6 +48,3 @@ object TastySurfaceFactory extends LogSupport { f.methodsOf(tastyType.asType) } code - } - -} diff --git a/airframe-surface/.jvm/src/test/scala-3/wvlet/airframe/surface/AirSpecBridgeCompat.scala b/airframe-surface/.jvm/src/test/scala-3/wvlet/airframe/surface/AirSpecBridgeCompat.scala index 121094ed4f..a89a97d857 100644 --- a/airframe-surface/.jvm/src/test/scala-3/wvlet/airframe/surface/AirSpecBridgeCompat.scala +++ b/airframe-surface/.jvm/src/test/scala-3/wvlet/airframe/surface/AirSpecBridgeCompat.scala @@ -13,7 +13,6 @@ */ package wvlet.airframe.surface -object AirSpecBridgeCompat { +object AirSpecBridgeCompat: val isScalaJS: Boolean = false val isScala3JVM: Boolean = true -} diff --git a/airframe-surface/.jvm/src/test/scala-3/wvlet/airframe/surface/reflect/TastySurfaceFactoryTest.scala b/airframe-surface/.jvm/src/test/scala-3/wvlet/airframe/surface/reflect/TastySurfaceFactoryTest.scala index 8cee50c1e1..4766163095 100644 --- a/airframe-surface/.jvm/src/test/scala-3/wvlet/airframe/surface/reflect/TastySurfaceFactoryTest.scala +++ b/airframe-surface/.jvm/src/test/scala-3/wvlet/airframe/surface/reflect/TastySurfaceFactoryTest.scala @@ -3,16 +3,14 @@ package wvlet.airframe.surface.reflect import wvlet.log.LogSupport import wvlet.airframe.surface.AirSpecBridge -case class Person(id: Int, name: String) { +case class Person(id: Int, name: String): def hello: String = "hello" -} sealed trait Status -object Status { +object Status: case object Ok extends Status -} -class TastySurfaceFactoryTest extends munit.FunSuite with AirSpecBridge with LogSupport { +class TastySurfaceFactoryTest extends munit.FunSuite with AirSpecBridge with LogSupport: test("of[A]") { val s = TastySurfaceFactory.of[Person] @@ -37,4 +35,3 @@ class TastySurfaceFactoryTest extends munit.FunSuite with AirSpecBridge with Log TastySurfaceFactory.ofClass(classOf[Status]) TastySurfaceFactory.ofClass(classOf[Status.Ok.type]) } -} diff --git a/airframe-surface/src/main/scala-3/wvlet/airframe/surface/CompileTimeSurfaceFactory.scala b/airframe-surface/src/main/scala-3/wvlet/airframe/surface/CompileTimeSurfaceFactory.scala index c98613128e..6790f03adf 100644 --- a/airframe-surface/src/main/scala-3/wvlet/airframe/surface/CompileTimeSurfaceFactory.scala +++ b/airframe-surface/src/main/scala-3/wvlet/airframe/surface/CompileTimeSurfaceFactory.scala @@ -3,11 +3,11 @@ import java.util.concurrent.atomic.AtomicInteger import scala.collection.immutable.ListMap import scala.quoted.* -private[surface] object CompileTimeSurfaceFactory { +private[surface] object CompileTimeSurfaceFactory: type SurfaceMatcher = PartialFunction[Type[_], Expr[Surface]] - def surfaceOf[A](using tpe: Type[A], quotes: Quotes): Expr[Surface] = { + def surfaceOf[A](using tpe: Type[A], quotes: Quotes): Expr[Surface] = import quotes.* import quotes.reflect.* @@ -15,8 +15,8 @@ private[surface] object CompileTimeSurfaceFactory { val surfaceExpr = f.surfaceOf(tpe) val t = TypeRepr.of[A] val flags = t.typeSymbol.flags - if !flags.is(Flags.JavaStatic) && flags.is(Flags.NoInits) then { - t.typeSymbol.maybeOwner match { + if !flags.is(Flags.JavaStatic) && flags.is(Flags.NoInits) then + t.typeSymbol.maybeOwner match // For inner-class definitions case s: Symbol if !s.isNoSymbol && @@ -28,29 +28,22 @@ private[surface] object CompileTimeSurfaceFactory { '{ ${ surfaceExpr }.withOuter(${ This(s).asExpr }.asInstanceOf[AnyRef]) } case _ => surfaceExpr - } - } else { - surfaceExpr - } - } + else surfaceExpr - def methodsOf[A](using tpe: Type[A], quotes: Quotes): Expr[Seq[MethodSurface]] = { + def methodsOf[A](using tpe: Type[A], quotes: Quotes): Expr[Seq[MethodSurface]] = val f = new CompileTimeSurfaceFactory(using quotes) f.methodsOf(tpe) - } -} -private[surface] class CompileTimeSurfaceFactory[Q <: Quotes](using quotes: Q) { +private[surface] class CompileTimeSurfaceFactory[Q <: Quotes](using quotes: Q): import quotes.* import quotes.reflect.* - private def fullTypeNameOf(t: Type[?]): String = { + private def fullTypeNameOf(t: Type[?]): String = fullTypeNameOf(TypeRepr.of(using t)) - } - private def fullTypeNameOf(t: TypeRepr): String = { - def sanitize(symbol: Symbol): String = { - val nameParts: List[String] = symbol.fullName.split("\\.").toList match { + private def fullTypeNameOf(t: TypeRepr): String = + def sanitize(symbol: Symbol): String = + val nameParts: List[String] = symbol.fullName.split("\\.").toList match case "scala" :: "Predef$" :: tail => tail case "scala" :: "collection" :: "immutable" :: tail => @@ -59,89 +52,72 @@ private[surface] class CompileTimeSurfaceFactory[Q <: Quotes](using quotes: Q) { List(nme) case other => other - } nameParts .mkString(".").stripSuffix("$").replaceAll("\\.package\\$", ".").replaceAll("\\$+", ".") .replaceAll("\\.\\.", ".") - } - t match { + t match case a: AppliedType if a.args.nonEmpty => s"${sanitize(a.typeSymbol)}[${a.args.map(pt => fullTypeNameOf(pt.asType)).mkString(",")}]" case other => sanitize(other.typeSymbol) - } - } private type Factory = PartialFunction[TypeRepr, Expr[Surface]] - def surfaceOf(tpe: Type[?]): Expr[Surface] = { + def surfaceOf(tpe: Type[?]): Expr[Surface] = surfaceOf(TypeRepr.of(using tpe)) - } private var observedSurfaceCount = new AtomicInteger(0) private var seen = ListMap[TypeRepr, Int]() private val memo = scala.collection.mutable.Map[TypeRepr, Expr[Surface]]() private val lazySurface = scala.collection.mutable.Set[TypeRepr]() - private def surfaceOf(t: TypeRepr, useVarRef: Boolean = true): Expr[Surface] = { - def buildLazySurface: Expr[Surface] = { + private def surfaceOf(t: TypeRepr, useVarRef: Boolean = true): Expr[Surface] = + def buildLazySurface: Expr[Surface] = '{ LazySurface(${ clsOf(t) }, ${ Expr(fullTypeNameOf(t)) }) } - } - if useVarRef && surfaceToVar.contains(t) then { - if lazySurface.contains(t) then { - buildLazySurface - } else { - Ref(surfaceToVar(t)).asExprOf[Surface] - } - } else if seen.contains(t) then { - if memo.contains(t) then { - memo(t) - } else { + if useVarRef && surfaceToVar.contains(t) then + if lazySurface.contains(t) then buildLazySurface + else Ref(surfaceToVar(t)).asExprOf[Surface] + else if seen.contains(t) then + if memo.contains(t) then memo(t) + else lazySurface += t buildLazySurface - } - } else { + else seen += t -> observedSurfaceCount.getAndIncrement() // For debugging // println(s"[${typeNameOf(t)}]\n ${t}\nfull type name: ${fullTypeNameOf(t)}\nclass: ${t.getClass}") val generator = factory.andThen { expr => - if !lazySurface.contains(t) then { + if !lazySurface.contains(t) then // Generate the surface code without using the cache expr - } else { + else // Need to cache the recursive Surface to be referenced in a LazySurface - val cacheKey = if typeNameOf(t) == "scala.Any" then { - t match { - case ParamRef(TypeLambda(typeNames, _, _), _) => - // Distinguish scala.Any and type bounds (such as _) - s"${fullTypeNameOf(t)} for ${t}" - case TypeBounds(_, _) => - // This ensures different cache key for each Type Parameter (such as T and U). - // This is required because fullTypeNameOf of every Type Parameters is `scala.Any`. - s"${fullTypeNameOf(t)} for ${t}" - case _ => - fullTypeNameOf(t) - } - } else { - fullTypeNameOf(t) - } + val cacheKey = + if typeNameOf(t) == "scala.Any" then + t match + case ParamRef(TypeLambda(typeNames, _, _), _) => + // Distinguish scala.Any and type bounds (such as _) + s"${fullTypeNameOf(t)} for ${t}" + case TypeBounds(_, _) => + // This ensures different cache key for each Type Parameter (such as T and U). + // This is required because fullTypeNameOf of every Type Parameters is `scala.Any`. + s"${fullTypeNameOf(t)} for ${t}" + case _ => + fullTypeNameOf(t) + else fullTypeNameOf(t) val key = Literal(StringConstant(cacheKey)).asExprOf[String] '{ - if !wvlet.airframe.surface.surfaceCache.contains(${ key }) then { + if !wvlet.airframe.surface.surfaceCache.contains(${ key }) then wvlet.airframe.surface.surfaceCache += ${ key } -> ${ expr } - } wvlet.airframe.surface.surfaceCache.apply(${ key }) } - } } val surface = generator(t) memo += (t -> surface) surface - } - } - private def factory: Factory = { + private def factory: Factory = taggedTypeFactory orElse andOrTypeFactory orElse aliasFactory orElse @@ -155,7 +131,6 @@ private[surface] class CompileTimeSurfaceFactory[Q <: Quotes](using quotes: Q) { exisitentialTypeFactory orElse genericTypeWithConstructorFactory orElse genericTypeFactory - } private def primitiveTypeFactory: Factory = { case t if t =:= TypeRepr.of[String] => '{ Primitive.String } @@ -172,24 +147,21 @@ private[surface] class CompileTimeSurfaceFactory[Q <: Quotes](using quotes: Q) { case t if t =:= TypeRepr.of[java.math.BigInteger] => '{ Primitive.BigInteger } } - private def typeNameOf(t: TypeRepr): String = { + private def typeNameOf(t: TypeRepr): String = t.typeSymbol.fullName.stripSuffix("$").replaceAll("\\.package\\$", ".").replaceAll("\\$+", ".") - } - private def isTaggedType(t: TypeRepr): Boolean = { + private def isTaggedType(t: TypeRepr): Boolean = typeNameOf(t).startsWith("wvlet.airframe.surface.tag.") - } private def taggedTypeFactory: Factory = { case a: AppliedType if a.args.length == 2 && isTaggedType(a) => '{ TaggedSurface(${ surfaceOf(a.args(0)) }, ${ surfaceOf(a.args(1)) }) } } - private def belongsToScalaDefault(t: TypeRepr): Boolean = { + private def belongsToScalaDefault(t: TypeRepr): Boolean = val scalaDefaultPackages = Seq("scala.", "scala.Predef$.", "scala.util.") val nme = t.typeSymbol.fullName scalaDefaultPackages.exists(p => nme.startsWith(p)) - } private def andOrTypeFactory: Factory = { case t: AndType => @@ -210,17 +182,13 @@ private[surface] class CompileTimeSurfaceFactory[Q <: Quotes](using quotes: Q) { val dealiased = t.dealias // println(s"=== alias factory: ${t}, ${dealiased}, ${t.simplified}") val symbolInOwner = t.typeSymbol.maybeOwner.declarations.find(_.name.toString == t.typeSymbol.name.toString) - val inner = symbolInOwner.map(_.tree) match { + val inner = symbolInOwner.map(_.tree) match case Some(TypeDef(_, b: TypeTree)) if t == dealiased => // t.dealias does not dealias for path dependent types, so extracting the dealiased type from AST. surfaceOf(b.tpe) case _ => - if t != dealiased then { - surfaceOf(dealiased) - } else { - surfaceOf(t.simplified) - } - } + if t != dealiased then surfaceOf(dealiased) + else surfaceOf(t.simplified) val s = t.typeSymbol val name = Expr(s.name) val fullName = Expr(fullTypeNameOf(t.asType)) @@ -251,24 +219,20 @@ private[surface] class CompileTimeSurfaceFactory[Q <: Quotes](using quotes: Q) { '{ HigherKindedTypeSurface(${ Expr(name) }, ${ Expr(fullName) }, AnyRefSurface, Seq.empty) } } - private def typeArgsOf(t: TypeRepr): List[TypeRepr] = { - t match { + private def typeArgsOf(t: TypeRepr): List[TypeRepr] = + t match case a: AppliedType => a.args case other => List.empty - } - } - private def elementTypeSurfaceOf(t: TypeRepr): Expr[Surface] = { - typeArgsOf(t).map(surfaceOf(_)).headOption match { + private def elementTypeSurfaceOf(t: TypeRepr): Expr[Surface] = + typeArgsOf(t).map(surfaceOf(_)).headOption match case Some(expr) => expr case None => // FIXME: Is this right ? '{ AnyRefSurface } - } - } private def arrayFactory: Factory = { case t if typeNameOf(t) == "scala.Array" => @@ -295,9 +259,8 @@ private[surface] class CompileTimeSurfaceFactory[Q <: Quotes](using quotes: Q) { newGenericSurfaceOf(t) } - private def isEnum(t: TypeRepr): Boolean = { + private def isEnum(t: TypeRepr): Boolean = t.baseClasses.exists(x => x.fullName.startsWith("java.lang.Enum")) - } private def javaEnumFactory: Factory = { case t if isEnum(t) => @@ -310,13 +273,11 @@ private[surface] class CompileTimeSurfaceFactory[Q <: Quotes](using quotes: Q) { '{ ExistentialType } } - private def clsOf(t: TypeRepr): Expr[Class[_]] = { + private def clsOf(t: TypeRepr): Expr[Class[_]] = Literal(ClassOfConstant(t)).asExpr.asInstanceOf[Expr[Class[_]]] - } - private def newGenericSurfaceOf(t: TypeRepr): Expr[Surface] = { + private def newGenericSurfaceOf(t: TypeRepr): Expr[Surface] = '{ new GenericSurface(${ clsOf(t) }) } - } private def genericTypeWithConstructorFactory: Factory = { case t @@ -326,10 +287,9 @@ private[surface] class CompileTimeSurfaceFactory[Q <: Quotes](using quotes: Q) { val typeArgs = typeArgsOf(t.simplified).map(surfaceOf(_)) val methodParams = constructorParametersOf(t) // val isStatic = !t.typeSymbol.flags.is(Flags.Local) - val factory = createObjectFactoryOf(t) match { + val factory = createObjectFactoryOf(t) match case Some(x) => '{ Some(${ x }) } case None => '{ None } - } '{ new wvlet.airframe.surface.GenericSurface( @@ -341,14 +301,13 @@ private[surface] class CompileTimeSurfaceFactory[Q <: Quotes](using quotes: Q) { } } - private def typeMappingTable(t: TypeRepr, method: Symbol): Map[String, TypeRepr] = { - val classTypeParams: List[TypeRepr] = t match { + private def typeMappingTable(t: TypeRepr, method: Symbol): Map[String, TypeRepr] = + val classTypeParams: List[TypeRepr] = t match case a: AppliedType => a.args case _ => List.empty[TypeRepr] - } // Build a table for resolving type parameters, e.g., class MyClass[A, B] -> Map("A" -> TypeRepr, "B" -> TypeRepr) - method.paramSymss match { + method.paramSymss match // tpeArgs for case fields, methodArgs for method arguments case tpeArgs :: tail if t.typeSymbol.typeMembers.nonEmpty => val typeArgTable = tpeArgs @@ -360,46 +319,39 @@ private[surface] class CompileTimeSurfaceFactory[Q <: Quotes](using quotes: Q) { typeArgTable case _ => Map.empty - } - } // Get a constructor with its generic types are resolved - private def getResolvedConstructorOf(t: TypeRepr): Option[Term] = { + private def getResolvedConstructorOf(t: TypeRepr): Option[Term] = val ts = t.typeSymbol - ts.primaryConstructor match { + ts.primaryConstructor match case pc if pc == Symbol.noSymbol => None case pc => // val cstr = Select.apply(New(TypeIdent(ts)), "") val cstr = New(Inferred(t)).select(pc) - if ts.typeMembers.isEmpty then { - Some(cstr) - } else { + if ts.typeMembers.isEmpty then Some(cstr) + else val lookupTable = typeMappingTable(t, pc) // println(s"--- ${lookupTable}") val typeArgs = pc.paramSymss.headOption.getOrElse(List.empty).map(_.tree).collect { case t: TypeDef => lookupTable.getOrElse(t.name, TypeRepr.of[AnyRef]) } Some(cstr.appliedToTypes(typeArgs)) - } - } - } - private def createObjectFactoryOf(targetType: TypeRepr): Option[Expr[ObjectFactory]] = { + private def createObjectFactoryOf(targetType: TypeRepr): Option[Expr[ObjectFactory]] = val ts = targetType.typeSymbol val flags = ts.flags if flags.is(Flags.Abstract) || flags.is(Flags.Module) || hasAbstractMethods(targetType) || isPathDependentType( targetType ) - then { - None - } else { + then None + else getResolvedConstructorOf(targetType).map { cstr => val argListList = methodArgsOf(targetType, ts.primaryConstructor) val newClassFn = Lambda( owner = Symbol.spliceOwner, tpe = MethodType(List("args"))(_ => List(TypeRepr.of[Seq[Any]]), _ => TypeRepr.of[Any]), - rhsFn = (sym: Symbol, paramRefs: List[Tree]) => { + rhsFn = (sym: Symbol, paramRefs: List[Tree]) => val args = paramRefs.head.asExprOf[Seq[Any]].asTerm var index = 0 val fn = argListList.foldLeft[Term](cstr) { (prev, argList) => @@ -414,26 +366,21 @@ private[surface] class CompileTimeSurfaceFactory[Q <: Quotes](using quotes: Q) { } // println(s"== ${fn.show}") fn.changeOwner(sym) - } ) val expr = '{ ObjectFactory.newFactory(${ newClassFn.asExprOf[Seq[Any] => Any] }) } expr } - } - } - private def hasAbstractMethods(t: TypeRepr): Boolean = { + private def hasAbstractMethods(t: TypeRepr): Boolean = t.typeSymbol.methodMembers.exists(_.flags.is(Flags.Abstract)) - } - private def isPathDependentType(t: TypeRepr): Boolean = { - !t.typeSymbol.flags.is(Flags.JavaStatic) && (t match { + private def isPathDependentType(t: TypeRepr): Boolean = + !t.typeSymbol.flags.is(Flags.JavaStatic) && (t match case t: TypeBounds => true case _ => false - }) - } + ) private def genericTypeFactory: Factory = { case t if t =:= TypeRepr.of[Any] => @@ -453,18 +400,17 @@ private[surface] class CompileTimeSurfaceFactory[Q <: Quotes](using quotes: Q) { * Try(EnumType.valueOf(s)).toOption }} */ val enumType = t.typeSymbol.companionModule - val valueOfMethod = enumType.methodMember("valueOf").headOption match { + val valueOfMethod = enumType.methodMember("valueOf").headOption match case Some(m) => m case None => sys.error(s"valueOf method not found in ${t}") - } val newFn = Lambda( owner = Symbol.spliceOwner, tpe = MethodType(List("cl", "s"))( _ => List(TypeRepr.of[Class[_]], TypeRepr.of[String]), _ => TypeRepr.of[Option[Any]] ), - rhsFn = (sym: Symbol, paramRefs: List[Tree]) => { + rhsFn = (sym: Symbol, paramRefs: List[Tree]) => val strVarRef = paramRefs(1).asExprOf[String].asTerm val expr: Term = Select @@ -476,7 +422,6 @@ private[surface] class CompileTimeSurfaceFactory[Q <: Quotes](using quotes: Q) { scala.util.Try(${ expr.asExprOf[Any] }).toOption }).asExprOf[Option[Any]].asTerm expr2.changeOwner(sym) - } ) '{ @@ -496,11 +441,10 @@ private[surface] class CompileTimeSurfaceFactory[Q <: Quotes](using quotes: Q) { _ => List(TypeRepr.of[Class[_]], TypeRepr.of[String]), _ => TypeRepr.of[Option[Any]] ), - rhsFn = (sym: Symbol, paramRefs: List[Tree]) => { + rhsFn = (sym: Symbol, paramRefs: List[Tree]) => val strVarRef = paramRefs(1).asExprOf[String].asTerm val expr = Select.unique(Apply(m, List(strVarRef)), "asInstanceOf").appliedToType(TypeRepr.of[Option[Any]]) expr.changeOwner(sym) - } ) '{ EnumSurface( @@ -512,43 +456,34 @@ private[surface] class CompileTimeSurfaceFactory[Q <: Quotes](using quotes: Q) { newGenericSurfaceOf(t) } - private def hasOptionReturnType(d: DefDef, retElementType: TypeRepr): Boolean = { - if d.returnTpt.tpe <:< TypeRepr.of[Option[_]] then { + private def hasOptionReturnType(d: DefDef, retElementType: TypeRepr): Boolean = + if d.returnTpt.tpe <:< TypeRepr.of[Option[_]] then val typeArgs = typeArgsOf(d.returnTpt.tpe) - typeArgs.headOption match { + typeArgs.headOption match case Some(t) if t =:= retElementType => true case _ => false - } - } else { - false - } - } + else false - private def hasStringUnapply(t: TypeRepr): Boolean = { + private def hasStringUnapply(t: TypeRepr): Boolean = getStringUnapply(t).isDefined - } - private def getStringUnapply(t: TypeRepr): Option[Symbol] = { - t.typeSymbol.companionClass match { + private def getStringUnapply(t: TypeRepr): Option[Symbol] = + t.typeSymbol.companionClass match case cp: Symbol => val methodOpt = cp.methodMember("unapply").headOption - methodOpt.map(_.tree) match { + methodOpt.map(_.tree) match case Some(m: DefDef) if m.paramss.size == 1 && hasOptionReturnType(m, t) => val args: List[ParamClause] = m.paramss - args.headOption.flatMap(_.params.headOption) match { + args.headOption.flatMap(_.params.headOption) match // Is the first argument type String? def unapply(s: String) case Some(v: ValDef) if v.tpt.tpe =:= TypeRepr.of[String] => methodOpt case _ => None - } case _ => None - } case null => None - } - } private case class MethodArg( name: String, @@ -560,7 +495,7 @@ private[surface] class CompileTimeSurfaceFactory[Q <: Quotes](using quotes: Q) { isSecret: Boolean ) - private def methodArgsOf(t: TypeRepr, method: Symbol): List[List[MethodArg]] = { + private def methodArgsOf(t: TypeRepr, method: Symbol): List[List[MethodArg]] = // println(s"==== method args of ${fullTypeNameOf(t)}") val defaultValueMethods = t.typeSymbol.companionClass.declaredMethods.filter { m => @@ -573,12 +508,9 @@ private[surface] class CompileTimeSurfaceFactory[Q <: Quotes](using quotes: Q) { val origParamSymss = method.paramSymss val declaredTypes = t.typeSymbol.declaredTypes.filterNot(_.flags.is(Flags.Module)) - val paramss = { + val paramss = if origParamSymss.nonEmpty && declaredTypes.nonEmpty then origParamSymss.tail - else { - origParamSymss - } - } + else origParamSymss paramss.map { params => params.zipWithIndex @@ -587,7 +519,7 @@ private[surface] class CompileTimeSurfaceFactory[Q <: Quotes](using quotes: Q) { // E.g. case class Foo(a: String)(implicit b: Int) // println(s"=== ${v.show} ${s.flags.show} ${s.flags.is(Flags.Implicit)}") // Substitue type param to actual types - val resolved: TypeRepr = v.tpt.tpe match { + val resolved: TypeRepr = v.tpt.tpe match case a: AppliedType => val resolvedTypeArgs = a.args.map { case p if p.typeSymbol.isTypeParam && typeArgTable.contains(p.typeSymbol.name) => @@ -601,38 +533,32 @@ private[surface] class CompileTimeSurfaceFactory[Q <: Quotes](using quotes: Q) { typeArgTable(name) case other => other - } val isSecret = hasSecretAnnotation(s) val isRequired = hasRequiredAnnotation(s) val isImplicit = s.flags.is(Flags.Implicit) val defaultValueGetter = defaultValueMethods.find(m => m.name.endsWith(s"$$${i}")) - val defaultMethodArgGetter = { + val defaultMethodArgGetter = val targetMethodName = method.name + "$default$" + i t.typeSymbol.declaredMethods.find { m => // println(s"=== target: ${m.name}, ${m.owner.name}") m.name == targetMethodName } - } MethodArg(v.name, resolved, defaultValueGetter, defaultMethodArgGetter, isImplicit, isRequired, isSecret) } } - } - private def hasSecretAnnotation(s: Symbol): Boolean = { + private def hasSecretAnnotation(s: Symbol): Boolean = val t = TypeRepr.of[wvlet.airframe.surface.secret] s.hasAnnotation(t.typeSymbol) - } - private def hasRequiredAnnotation(s: Symbol): Boolean = { + private def hasRequiredAnnotation(s: Symbol): Boolean = val t = TypeRepr.of[wvlet.airframe.surface.required] s.hasAnnotation(t.typeSymbol) - } - private def constructorParametersOf(t: TypeRepr): Expr[Seq[MethodParameter]] = { + private def constructorParametersOf(t: TypeRepr): Expr[Seq[MethodParameter]] = methodParametersOf(t, t.typeSymbol.primaryConstructor) - } - private def methodParametersOf(t: TypeRepr, method: Symbol): Expr[Seq[MethodParameter]] = { + private def methodParametersOf(t: TypeRepr, method: Symbol): Expr[Seq[MethodParameter]] = val methodName = method.name val methodArgs = methodArgsOf(t, method).flatten val argClasses = methodArgs.map { arg => @@ -650,41 +576,37 @@ private[surface] class CompileTimeSurfaceFactory[Q <: Quotes](using quotes: Q) { // println(s"======= ${t.typeSymbol.memberMethods}") - val paramExprs = for ((field, i) <- methodArgs.zipWithIndex) yield { + val paramExprs = for ((field, i) <- methodArgs.zipWithIndex) yield val paramType = field.tpe val paramName = field.name // Related example: // https://github.com/lampepfl/dotty-macro-examples/blob/aed51833db652f67741089721765ad5a349f7383/defaultParamsInference/src/macro.scala - val defaultValue: Expr[Option[Any]] = field.defaultValueGetter match { + val defaultValue: Expr[Option[Any]] = field.defaultValueGetter match case Some(m) => val dv = Ref(m.owner.companionModule).select(m) '{ Some(${ dv.asExprOf[Any] }) } case _ => '{ None } - } // Generate a field accessor { (x:Any) => x.asInstanceOf[A].(field name) } - val paramIsAccessible = { - t.typeSymbol.fieldMember(paramName) match { + val paramIsAccessible = + t.typeSymbol.fieldMember(paramName) match case nt if nt == Symbol.noSymbol => false case m if m.flags.is(Flags.Private) => false case m if m.flags.is(Flags.Artifact) => false case _ => true - } - } // println(s"${paramName} ${paramIsAccessible}") - val accessor: Expr[Option[Any => Any]] = if method.isClassConstructor && paramIsAccessible then { + val accessor: Expr[Option[Any => Any]] = if method.isClassConstructor && paramIsAccessible then // MethodParameter.accessor[(owner type), (parameter type]] val accessorMethod: Symbol = TypeRepr.of[MethodParameter.type].typeSymbol.methodMember("accessor").head val objRef = Ref(TypeRepr.of[MethodParameter].typeSymbol.companionModule) - def resolveType(tpe: TypeRepr): TypeRepr = tpe match { + def resolveType(tpe: TypeRepr): TypeRepr = tpe match case b: TypeBounds => TypeRepr.of[Any] case _ => tpe - } val t1 = resolveType(t) val t2 = resolveType(paramType) @@ -695,11 +617,10 @@ private[surface] class CompileTimeSurfaceFactory[Q <: Quotes](using quotes: Q) { val lambda = Lambda( owner = Symbol.spliceOwner, tpe = MethodType(List("x"))(_ => List(t1), _ => t2), - rhsFn = (sym, params) => { + rhsFn = (sym, params) => val x = params.head.asInstanceOf[Term] val expr = Select.unique(x, paramName) expr.changeOwner(sym) - } ) val accMethod = methodCall.appliedToArgs(List(lambda)) // println(s"=== ${accMethod.show}") @@ -709,25 +630,21 @@ private[surface] class CompileTimeSurfaceFactory[Q <: Quotes](using quotes: Q) { // MethodParameter.accessor[t1, t2](classOf[t1]){(x:t1) => x.(field name) } // }}} '{ Some(${ accMethod.asExprOf[Any => Any] }) } - } else { - '{ None } - } + else '{ None } - val methodArgAccessor: Expr[Option[Any => Any]] = field.defaultMethodArgGetter match { + val methodArgAccessor: Expr[Option[Any => Any]] = field.defaultMethodArgGetter match case None => '{ None } case Some(m) => val lambda = Lambda( owner = Symbol.spliceOwner, tpe = MethodType(List("x"))(_ => List(TypeRepr.of[Any]), _ => TypeRepr.of[Any]), - rhsFn = (sym, params) => { + rhsFn = (sym, params) => val x = params.head.asInstanceOf[Term] val expr = clsCast(x, t).select(m) expr.changeOwner(sym) - } ) '{ Some(${ lambda.asExprOf[Any => Any] }) } - } // Using StaticMethodParameter when supportin Scala.js in Scala 3. // TODO: Deprecate RuntimeMethodParameter @@ -744,25 +661,21 @@ private[surface] class CompileTimeSurfaceFactory[Q <: Quotes](using quotes: Q) { methodArgAccessor = ${ methodArgAccessor } ) } - } Expr.ofSeq(paramExprs) - } - private def getTree(e: Expr[?]): Tree = { + private def getTree(e: Expr[?]): Tree = val f = e.getClass().getDeclaredField("tree") f.setAccessible(true) val tree = f.get(e) tree.asInstanceOf[Tree] - } - def methodsOf(t: Type[?]): Expr[Seq[MethodSurface]] = { + def methodsOf(t: Type[?]): Expr[Seq[MethodSurface]] = methodsOf(TypeRepr.of(using t)) - } // To reduce the byte code size, we need to memoize the generated surface bound to a variable private var surfaceToVar = ListMap.empty[TypeRepr, Symbol] - private def methodsOf(t: TypeRepr): Expr[Seq[MethodSurface]] = { + private def methodsOf(t: TypeRepr): Expr[Seq[MethodSurface]] = // Run just for collecting known surfaces. seen variable will be updated methodsOfInternal(t) @@ -782,13 +695,13 @@ private[surface] class CompileTimeSurfaceFactory[Q <: Quotes](using quotes: Q) { // Use alphabetically ordered variable names f"__s${surfaceVarCount}%03X", TypeRepr.of[Surface], - if lazySurface.contains(tpe) then { + if lazySurface.contains(tpe) then // If the surface itself is lazy, we need to eagerly initialize it to update the surface cache Flags.EmptyFlags - } else { + else // Use lazy val to avoid forward reference error Flags.Lazy - }, + , Symbol.noSymbol ) surfaceVarCount += 1 @@ -817,14 +730,13 @@ private[surface] class CompileTimeSurfaceFactory[Q <: Quotes](using quotes: Q) { // println(s"=== methodOf: ${t.typeSymbol.fullName} => \n${expr.show}") expr - } private val seenMethodParent = scala.collection.mutable.Set[TypeRepr]() - private def methodsOfInternal(targetType: TypeRepr): Expr[Seq[MethodSurface]] = { - if seenMethodParent.contains(targetType) then { + private def methodsOfInternal(targetType: TypeRepr): Expr[Seq[MethodSurface]] = + if seenMethodParent.contains(targetType) then sys.error(s"recursive method found in: ${targetType.typeSymbol.fullName}") - } else { + else seenMethodParent += targetType val localMethods = localMethodsOf(targetType).distinct.sortBy(_.name) val methodSurfaces = localMethods.map(m => (m, m.tree)).collect { case (m, df: DefDef) => @@ -843,39 +755,33 @@ private[surface] class CompileTimeSurfaceFactory[Q <: Quotes](using quotes: Q) { } val expr = Expr.ofSeq(methodSurfaces) expr - } - } - private def isTypeParam(t: TypeRepr): Boolean = { - t match { + private def isTypeParam(t: TypeRepr): Boolean = + t match case TypeRef(prefix, typeName) if prefix.toString == "NoPrefix" => true case _ => false - } - } - private def clsCast(term: Term, t: TypeRepr): Term = { + private def clsCast(term: Term, t: TypeRepr): Term = Select.unique(term, "asInstanceOf").appliedToType(t) - } private def createMethodCaller( objectType: TypeRepr, m: Symbol, methodArgs: Seq[MethodArg] - ): Expr[Option[(Any, Seq[Any]) => Any]] = { + ): Expr[Option[(Any, Seq[Any]) => Any]] = // Build { (x: Any, args: Seq[Any]) => x.asInstanceOf[t].(.. args) } - val methodTypeParams: List[TypeParamClause] = m.tree match { + val methodTypeParams: List[TypeParamClause] = m.tree match case df: DefDef => df.paramss.collect { case t: TypeParamClause => t } case _ => List.empty - } val lambda = Lambda( owner = Symbol.spliceOwner, tpe = MethodType(List("x", "args"))(_ => List(TypeRepr.of[Any], TypeRepr.of[Seq[Any]]), _ => TypeRepr.of[Any]), - rhsFn = (sym, params) => { + rhsFn = (sym, params) => val x = params(0).asInstanceOf[Term] val args = params(1).asInstanceOf[Term] val expr = clsCast(x, objectType).select(m) @@ -885,37 +791,32 @@ private[surface] class CompileTimeSurfaceFactory[Q <: Quotes](using quotes: Q) { val extracted = Select.unique(args, "apply").appliedTo(Literal(IntConstant(i))) clsCast(extracted, arg.tpe) } - if argList.isEmpty then { - val newExpr = m.tree match { + if argList.isEmpty then + val newExpr = m.tree match case d: DefDef if d.trailingParamss.nonEmpty => // An empty arg method, e.g., def methodName() expr.appliedToNone case _ => // No arg method, e.g., def methodName: Unit expr - } newExpr.changeOwner(sym) - } else { + else // Bind to function arguments - val newExpr = if methodTypeParams.isEmpty then { - expr.appliedToArgs(argList.toList) - } else { - // For generic functions, type params also need to be applied - val dummyTypeParams = methodTypeParams.map(x => TypeRepr.of[Any]) - // println(s"---> ${m.name} type param count: ${methodTypeParams.size}, arg size: ${argList.size}") - expr - .appliedToTypes(dummyTypeParams) - .appliedToArgs(argList.toList) - } + val newExpr = + if methodTypeParams.isEmpty then expr.appliedToArgs(argList.toList) + else + // For generic functions, type params also need to be applied + val dummyTypeParams = methodTypeParams.map(x => TypeRepr.of[Any]) + // println(s"---> ${m.name} type param count: ${methodTypeParams.size}, arg size: ${argList.size}") + expr + .appliedToTypes(dummyTypeParams) + .appliedToArgs(argList.toList) newExpr.changeOwner(sym) - } - } ) '{ Some(${ lambda.asExprOf[(Any, Seq[Any]) => Any] }) } - } - private def localMethodsOf(t: TypeRepr): Seq[Symbol] = { - def allMethods = { + private def localMethodsOf(t: TypeRepr): Seq[Symbol] = + def allMethods = t.typeSymbol.methodMembers .filter { x => nonObject(x.owner) && @@ -938,53 +839,37 @@ private[surface] class CompileTimeSurfaceFactory[Q <: Quotes](using quotes: Q) { !name.startsWith("$") && name != "" } - } allMethods.filter(m => isOwnedByTargetClass(m, t)) - } - private def nonObject(x: Symbol): Boolean = { + private def nonObject(x: Symbol): Boolean = !x.flags.is(Flags.Synthetic) && - !x.flags.is(Flags.Artifact) && - x.fullName != "scala.Any" && - x.fullName != "java.lang.Object" && - // Exclude caes class methods - x.fullName != "scala.Product" - } + !x.flags.is(Flags.Artifact) && + x.fullName != "scala.Any" && + x.fullName != "java.lang.Object" && + // Exclude caes class methods + x.fullName != "scala.Product" - private def isOwnedByTargetClass(m: Symbol, t: TypeRepr): Boolean = { + private def isOwnedByTargetClass(m: Symbol, t: TypeRepr): Boolean = m.owner == t.typeSymbol || t.baseClasses.filter(nonObject).exists(_ == m.owner) - } - private def modifierBitMaskOf(m: Symbol): Int = { + private def modifierBitMaskOf(m: Symbol): Int = var mod = 0 - if !m.flags.is(Flags.Private) && !m.flags.is(Flags.Protected) && !m.flags.is(Flags.PrivateLocal) then { + if !m.flags.is(Flags.Private) && !m.flags.is(Flags.Protected) && !m.flags.is(Flags.PrivateLocal) then mod |= MethodModifier.PUBLIC - } - if m.flags.is(Flags.Private) then { - mod |= MethodModifier.PRIVATE - } - if m.flags.is(Flags.Protected) then { - mod |= MethodModifier.PROTECTED - } - if m.flags.is(Flags.JavaStatic) then { - mod |= MethodModifier.STATIC - } - if m.flags.is(Flags.Final) then { - mod |= MethodModifier.FINAL - } - if m.flags.is(Flags.Abstract) then { - mod |= MethodModifier.ABSTRACT - } + if m.flags.is(Flags.Private) then mod |= MethodModifier.PRIVATE + if m.flags.is(Flags.Protected) then mod |= MethodModifier.PROTECTED + if m.flags.is(Flags.JavaStatic) then mod |= MethodModifier.STATIC + if m.flags.is(Flags.Final) then mod |= MethodModifier.FINAL + if m.flags.is(Flags.Abstract) then mod |= MethodModifier.ABSTRACT mod - } - def surfaceFromClass(cl: Class[?]): Expr[Surface] = { + def surfaceFromClass(cl: Class[?]): Expr[Surface] = val name = cl.getName val rawType = Class.forName(name) val constructors = rawType.getConstructors - val (typeArgs, params) = if constructors.nonEmpty then { + val (typeArgs, params) = if constructors.nonEmpty then val primaryConstructor = constructors(0) val paramSurfaces: Seq[Expr[Surface]] = primaryConstructor.getParameterTypes.map { paramType => val tastyType = quotes.reflect.TypeRepr.typeConstructorOf(paramType) @@ -993,9 +878,7 @@ private[surface] class CompileTimeSurfaceFactory[Q <: Quotes](using quotes: Q) { // FIXME: Use TastyInspector as runtime-like reflection for Scala 3 val params: Seq[Expr[Parameter]] = Seq.empty (Expr.ofSeq(paramSurfaces), Expr.ofSeq(params)) - } else { - ('{ Seq.empty[Surface] }, '{ Seq.empty[Parameter] }) - } + else ('{ Seq.empty[Surface] }, '{ Seq.empty[Parameter] }) '{ new wvlet.airframe.surface.GenericSurface( @@ -1003,6 +886,3 @@ private[surface] class CompileTimeSurfaceFactory[Q <: Quotes](using quotes: Q) { typeArgs = ${ typeArgs } ) } - } - -} diff --git a/airframe-surface/src/main/scala-3/wvlet/airframe/surface/Surface.scala b/airframe-surface/src/main/scala-3/wvlet/airframe/surface/Surface.scala index b770fbe754..d64a7035f5 100644 --- a/airframe-surface/src/main/scala-3/wvlet/airframe/surface/Surface.scala +++ b/airframe-surface/src/main/scala-3/wvlet/airframe/surface/Surface.scala @@ -18,7 +18,7 @@ package wvlet.airframe.surface * Note: This interface is the same with scala-2 Surface interface, but Scala compiler requires defining Surface object * in the same file, so this interface is copied. */ -trait Surface extends Serializable { +trait Surface extends Serializable: def rawType: Class[?] def typeArgs: Seq[Surface] def params: Seq[Parameter] @@ -41,16 +41,14 @@ trait Surface extends Serializable { def objectFactory: Option[ObjectFactory] = None def withOuter(outer: AnyRef): Surface = this -} /** * Scala 3 implementation of Surface */ -object Surface { +object Surface: private[surface] val scalaMajorVersion: Int = 3 import scala.quoted.* inline def of[A]: Surface = ${ CompileTimeSurfaceFactory.surfaceOf[A] } inline def methodsOf[A]: Seq[MethodSurface] = ${ CompileTimeSurfaceFactory.methodsOf[A] } -} diff --git a/airframe-surface/src/test/scala-3/wvlet/airframe/surface/Scala3NewTypeTest.scala b/airframe-surface/src/test/scala-3/wvlet/airframe/surface/Scala3NewTypeTest.scala index a69baf0240..abe6b2aa4a 100644 --- a/airframe-surface/src/test/scala-3/wvlet/airframe/surface/Scala3NewTypeTest.scala +++ b/airframe-surface/src/test/scala-3/wvlet/airframe/surface/Scala3NewTypeTest.scala @@ -62,9 +62,8 @@ object Scala3NewTypeTest extends AirSpec: s.params(0).surface.name shouldBe "MyEnv" } - class A { + class A: def hello(env: MyEnv): String = env - } test("opaque type in method args") { val m = Surface.methodsOf[A].find(_.name == "hello") From 30ce248ba1a620c0e34d01a80f70d9e1e4119853 Mon Sep 17 00:00:00 2001 From: "Taro L. Saito" Date: Thu, 22 Feb 2024 22:17:14 -0800 Subject: [PATCH 2/3] Reformat integration test projects --- .../scala-3/wvlet/airframe/BinderImpl.scala | 6 ++---- .../wvlet/airframe/test/api/HelloRPC.scala | 9 +++------ .../airframe/test/api/HelloRPCImpl.scala | 12 ++++-------- .../airframe/test/api/HelloRPCTest.scala | 6 ++---- .../wvlet/airframe/test/api/Http1Test.scala | 3 +-- build.sbt | 19 ++++++++++++++++++- 6 files changed, 30 insertions(+), 25 deletions(-) rename airframe-integration-test-api/src/main/{scala => scala-3}/wvlet/airframe/test/api/HelloRPC.scala (91%) rename airframe-integration-test/src/main/{scala => scala-3}/wvlet/airframe/test/api/HelloRPCImpl.scala (83%) diff --git a/airframe-di/src/main/scala-3/wvlet/airframe/BinderImpl.scala b/airframe-di/src/main/scala-3/wvlet/airframe/BinderImpl.scala index e8cb8d243b..0a8e8c54d4 100644 --- a/airframe-di/src/main/scala-3/wvlet/airframe/BinderImpl.scala +++ b/airframe-di/src/main/scala-3/wvlet/airframe/BinderImpl.scala @@ -15,23 +15,21 @@ private[airframe] trait BinderImpl[A] extends LogSupport: * * @tparam B */ - inline def to[B <: A]: DesignWithContext[B] = { + inline def to[B <: A]: DesignWithContext[B] = // registerTraitFactory[B] val to = Surface.of[B] if self.from == to then wvlet.log.Logger("wvlet.airframe.Binder").warn("Binding to the same type is not allowed: " + to.toString) throw new wvlet.airframe.AirframeException.CYCLIC_DEPENDENCY(List(to), SourceCode()) self.design.addBinding[B](SingletonBinding(self.from, to, false, self.sourceCode)) - } - inline def toEagerSingletonOf[B <: A]: DesignWithContext[B] = { + inline def toEagerSingletonOf[B <: A]: DesignWithContext[B] = // registerTraitFactory[B] val to = Surface.of[B] if self.from == to then wvlet.log.Logger("wvlet.airframe.Binder").warn("Binding to the same type is not allowed: " + to.toString) throw new wvlet.airframe.AirframeException.CYCLIC_DEPENDENCY(List(to), SourceCode()) self.design.addBinding[B](SingletonBinding(self.from, to, true, self.sourceCode)) - } inline def toProvider[D1](factory: D1 => A): DesignWithContext[A] = // registerTraitFactory[D1] diff --git a/airframe-integration-test-api/src/main/scala/wvlet/airframe/test/api/HelloRPC.scala b/airframe-integration-test-api/src/main/scala-3/wvlet/airframe/test/api/HelloRPC.scala similarity index 91% rename from airframe-integration-test-api/src/main/scala/wvlet/airframe/test/api/HelloRPC.scala rename to airframe-integration-test-api/src/main/scala-3/wvlet/airframe/test/api/HelloRPC.scala index f6b5e773d9..9445571e67 100644 --- a/airframe-integration-test-api/src/main/scala/wvlet/airframe/test/api/HelloRPC.scala +++ b/airframe-integration-test-api/src/main/scala-3/wvlet/airframe/test/api/HelloRPC.scala @@ -16,16 +16,15 @@ package wvlet.airframe.test.api import wvlet.airframe.http.* @RPC -trait HelloRPC { +trait HelloRPC: import HelloRPC.* def hello(name: String): String def serverStatus: Status def ackStatus(status: Status): Status def variousParams(params: VariousParams): VariousParams -} -object HelloRPC extends RxRouterProvider { +object HelloRPC extends RxRouterProvider: override def router: RxRouter = RxRouter.of[HelloRPC] case class VariousParams( @@ -33,10 +32,8 @@ object HelloRPC extends RxRouterProvider { p2: Boolean, p3: Double ) -} -enum Status(isDone: Boolean) { +enum Status(isDone: Boolean): def name: String = this.toString case OK extends Status(isDone = true) case NG extends Status(isDone = true) -} diff --git a/airframe-integration-test/src/main/scala/wvlet/airframe/test/api/HelloRPCImpl.scala b/airframe-integration-test/src/main/scala-3/wvlet/airframe/test/api/HelloRPCImpl.scala similarity index 83% rename from airframe-integration-test/src/main/scala/wvlet/airframe/test/api/HelloRPCImpl.scala rename to airframe-integration-test/src/main/scala-3/wvlet/airframe/test/api/HelloRPCImpl.scala index ac4305b2d1..a208c03644 100644 --- a/airframe-integration-test/src/main/scala/wvlet/airframe/test/api/HelloRPCImpl.scala +++ b/airframe-integration-test/src/main/scala-3/wvlet/airframe/test/api/HelloRPCImpl.scala @@ -16,19 +16,15 @@ package wvlet.airframe.test.api import wvlet.airframe.test.api.HelloRPC.VariousParams import wvlet.log.LogSupport -class HelloRPCImpl extends HelloRPC with LogSupport { - override def hello(name: String): String = { +class HelloRPCImpl extends HelloRPC with LogSupport: + override def hello(name: String): String = s"Hello ${name}!" - } override def serverStatus: Status = Status.OK - override def ackStatus(status: Status): Status = { + override def ackStatus(status: Status): Status = info(s"acked: ${status}") status - } - override def variousParams(params: VariousParams): VariousParams = { + override def variousParams(params: VariousParams): VariousParams = info(s"received: ${params}") params - } -} diff --git a/airframe-integration-test/src/test/scala-3/wvlet/airframe/test/api/HelloRPCTest.scala b/airframe-integration-test/src/test/scala-3/wvlet/airframe/test/api/HelloRPCTest.scala index 3d69cf4cfb..c8381b4e3a 100644 --- a/airframe-integration-test/src/test/scala-3/wvlet/airframe/test/api/HelloRPCTest.scala +++ b/airframe-integration-test/src/test/scala-3/wvlet/airframe/test/api/HelloRPCTest.scala @@ -19,9 +19,9 @@ import wvlet.airframe.http.{Http, HttpServer, RPCEncoding, RxRouter} import wvlet.airframe.test.api.HelloRPC.VariousParams import wvlet.airspec.AirSpec -class HelloRPCTest extends AirSpec { +class HelloRPCTest extends AirSpec: - override protected def design: Design = { + override protected def design: Design = Netty.server .withName("hello-rpc-test") .withRouter(RxRouter.of[HelloRPCImpl]) @@ -32,7 +32,6 @@ class HelloRPCTest extends AirSpec { .bind[ServiceRPC.RPCAsyncClient].toProvider { (server: HttpServer) => ServiceRPC.newRPCAsyncClient(Http.client.newAsyncClient(server.localAddress)) } - } test("rpc") { (server: HttpServer) => test("sync client") { (client: ServiceRPC.RPCSyncClient) => @@ -76,4 +75,3 @@ class HelloRPCTest extends AirSpec { } } } -} diff --git a/airframe-integration-test/src/test/scala-3/wvlet/airframe/test/api/Http1Test.scala b/airframe-integration-test/src/test/scala-3/wvlet/airframe/test/api/Http1Test.scala index edbab2af50..c1e3f396bc 100644 --- a/airframe-integration-test/src/test/scala-3/wvlet/airframe/test/api/Http1Test.scala +++ b/airframe-integration-test/src/test/scala-3/wvlet/airframe/test/api/Http1Test.scala @@ -19,7 +19,7 @@ import wvlet.airframe.http.netty.Netty import wvlet.airframe.test.api.ServiceRPC.RPCSyncClient import wvlet.airspec.AirSpec -class Http1Test extends AirSpec { +class Http1Test extends AirSpec: initDesign { _ + Netty.server @@ -35,4 +35,3 @@ class Http1Test extends AirSpec { test("rpc") { (client: RPCSyncClient) => client.HelloRPC.hello("RPC") shouldBe "Hello RPC!" } -} diff --git a/build.sbt b/build.sbt index dfa052e158..a7674fab12 100644 --- a/build.sbt +++ b/build.sbt @@ -195,7 +195,7 @@ lazy val root = } } ) - .aggregate((jvmProjects ++ jsProjects): _*) + .aggregate((jvmProjects ++ jsProjects ++ itProjects): _*) // JVM projects for scala-community build. This should have no tricky setup and should support Scala 2.12 and Scala 3 lazy val communityBuildProjects: Seq[ProjectReference] = Seq( @@ -252,6 +252,14 @@ lazy val jsProjects: Seq[ProjectReference] = Seq( widgetJS ) +// Integration test projects +lazy val itProjects: Seq[ProjectReference] = Seq( + integrationTestApi.jvm, + integrationTestApi.js, + integrationTest, + integrationTestJs +) + // For community-build lazy val communityBuild = project @@ -283,6 +291,15 @@ lazy val projectJS = ) .aggregate(jsProjects: _*) +lazy val projectIt = + project + .settings(noPublish) + .settings( + // Skip importing aggregated projects in IntelliJ IDEA + ideSkipProject := true + ) + .aggregate(itProjects: _*) + // A scoped project only for Dotty (Scala 3). // This is a workaround as projectJVM/test shows compile errors for non Scala 3 ready projects lazy val projectDotty = From 660b3b7ba6e1a520ffc192a1f138f11896c426f9 Mon Sep 17 00:00:00 2001 From: "Taro L. Saito" Date: Thu, 22 Feb 2024 23:15:12 -0800 Subject: [PATCH 3/3] Exclude LoggerMacros from optional brace format --- .scalafmt.conf | 4 +++ .../main/scala-3/wvlet/log/LoggerBase.scala | 32 +++++++++++++++---- build.sbt | 8 ++--- 3 files changed, 34 insertions(+), 10 deletions(-) diff --git a/.scalafmt.conf b/.scalafmt.conf index c674079d12..288dc584c2 100644 --- a/.scalafmt.conf +++ b/.scalafmt.conf @@ -23,4 +23,8 @@ fileOverride { "glob:**/scala-2/**" { runner.dialect = scala213source3 } + "glob:**/scala-3/wvlet/log/LoggerBase.scala" { + // optional brace didn't work for code with inline/macro methods + rewrite.scala3.removeOptionalBraces = no + } } diff --git a/airframe-log/src/main/scala-3/wvlet/log/LoggerBase.scala b/airframe-log/src/main/scala-3/wvlet/log/LoggerBase.scala index 9a13afc0d2..ad94098205 100644 --- a/airframe-log/src/main/scala-3/wvlet/log/LoggerBase.scala +++ b/airframe-log/src/main/scala-3/wvlet/log/LoggerBase.scala @@ -13,70 +13,88 @@ */ package wvlet.log -/** - */ -trait LoggerBase: +trait LoggerBase { self: Logger => inline def error(inline message: Any): Unit = if self.isEnabled(LogLevel.ERROR) then self.log(LogLevel.ERROR, LoggerMacros.sourcePos(), message) + inline def warn(inline message: Any): Unit = if self.isEnabled(LogLevel.WARN) then self.log(LogLevel.WARN, LoggerMacros.sourcePos(), message) + inline def info(inline message: Any): Unit = if self.isEnabled(LogLevel.INFO) then self.log(LogLevel.INFO, LoggerMacros.sourcePos(), message) + inline def debug(inline message: Any): Unit = if self.isEnabled(LogLevel.DEBUG) then self.log(LogLevel.DEBUG, LoggerMacros.sourcePos(), message) + inline def trace(inline message: Any): Unit = if self.isEnabled(LogLevel.TRACE) then self.log(LogLevel.TRACE, LoggerMacros.sourcePos(), message) + inline def error(inline message: Any, inline cause: Throwable): Unit = if self.isEnabled(LogLevel.ERROR) then self.logWithCause(LogLevel.ERROR, LoggerMacros.sourcePos(), message, cause) + inline def warn(inline message: Any, inline cause: Throwable): Unit = if self.isEnabled(LogLevel.WARN) then self.logWithCause(LogLevel.WARN, LoggerMacros.sourcePos(), message, cause) + inline def info(inline message: Any, inline cause: Throwable): Unit = if self.isEnabled(LogLevel.INFO) then self.logWithCause(LogLevel.INFO, LoggerMacros.sourcePos(), message, cause) + inline def debug(inline message: Any, inline cause: Throwable): Unit = if self.isEnabled(LogLevel.DEBUG) then self.logWithCause(LogLevel.DEBUG, LoggerMacros.sourcePos(), message, cause) + inline def trace(inline message: Any, inline cause: Throwable): Unit = if self.isEnabled(LogLevel.TRACE) then self.logWithCause(LogLevel.TRACE, LoggerMacros.sourcePos(), message, cause) +} /** */ -trait LoggingMethods extends Serializable: +trait LoggingMethods extends Serializable { protected def logger: Logger inline protected def error(inline message: Any): Unit = if logger.isEnabled(LogLevel.ERROR) then logger.log(LogLevel.ERROR, LoggerMacros.sourcePos(), message) + inline protected def warn(inline message: Any): Unit = if logger.isEnabled(LogLevel.WARN) then logger.log(LogLevel.WARN, LoggerMacros.sourcePos(), message) + inline protected def info(inline message: Any): Unit = if logger.isEnabled(LogLevel.INFO) then logger.log(LogLevel.INFO, LoggerMacros.sourcePos(), message) + inline protected def debug(inline message: Any): Unit = if logger.isEnabled(LogLevel.DEBUG) then logger.log(LogLevel.DEBUG, LoggerMacros.sourcePos(), message) + inline protected def trace(inline message: Any): Unit = if logger.isEnabled(LogLevel.TRACE) then logger.log(LogLevel.TRACE, LoggerMacros.sourcePos(), message) + inline protected def logAt(inline logLevel: LogLevel, inline message: Any): Unit = if logger.isEnabled(logLevel) then logger.log(logLevel, LoggerMacros.sourcePos(), message) inline protected def error(inline message: Any, inline cause: Throwable): Unit = if logger.isEnabled(LogLevel.ERROR) then logger.logWithCause(LogLevel.ERROR, LoggerMacros.sourcePos(), message, cause) + inline protected def warn(inline message: Any, inline cause: Throwable): Unit = if logger.isEnabled(LogLevel.WARN) then logger.logWithCause(LogLevel.WARN, LoggerMacros.sourcePos(), message, cause) + inline protected def info(inline message: Any, inline cause: Throwable): Unit = if logger.isEnabled(LogLevel.INFO) then logger.logWithCause(LogLevel.INFO, LoggerMacros.sourcePos(), message, cause) + inline protected def debug(inline message: Any, inline cause: Throwable): Unit = if logger.isEnabled(LogLevel.DEBUG) then logger.logWithCause(LogLevel.DEBUG, LoggerMacros.sourcePos(), message, cause) + inline protected def trace(inline message: Any, inline cause: Throwable): Unit = if logger.isEnabled(LogLevel.TRACE) then logger.logWithCause(LogLevel.TRACE, LoggerMacros.sourcePos(), message, cause) +} -object LoggerMacros: +object LoggerMacros { import scala.quoted.* inline def sourcePos(): LogSource = ${ sourcePos } - private def sourcePos(using q: Quotes): Expr[wvlet.log.LogSource] = + private def sourcePos(using q: Quotes): Expr[wvlet.log.LogSource] = { import q.reflect.* val pos = Position.ofMacroExpansion val line = Expr(pos.startLine) @@ -87,3 +105,5 @@ object LoggerMacros: // val path = Expr(srcPath.toFile.getPath) val fileName = Expr(srcPath.getFileName().toString) '{ wvlet.log.LogSource("", ${ fileName }, ${ line } + 1, ${ column }) } + } +} diff --git a/build.sbt b/build.sbt index a7674fab12..9b1f489334 100644 --- a/build.sbt +++ b/build.sbt @@ -254,10 +254,10 @@ lazy val jsProjects: Seq[ProjectReference] = Seq( // Integration test projects lazy val itProjects: Seq[ProjectReference] = Seq( - integrationTestApi.jvm, - integrationTestApi.js, - integrationTest, - integrationTestJs + integrationTestApi.jvm, + integrationTestApi.js, + integrationTest, + integrationTestJs ) // For community-build