From fbc98ed72e88ef201942d1b3ad43078b6ab2e8a4 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Mon, 17 Oct 2022 22:53:07 -0400 Subject: [PATCH] Use ClassTag instead of Manifest --- .../src/main/scala/sbt/Structure.scala | 86 ++++++++++++------- .../src/main/scala/sbt/std/KeyMacro.scala | 9 +- main/src/main/scala/sbt/Main.scala | 8 +- main/src/main/scala/sbt/ScopedKeyData.scala | 3 +- .../sbt/internal/SettingCompletions.scala | 30 ++++--- main/src/main/scala/sbt/nio/Settings.scala | 10 +-- project/Dependencies.scala | 2 +- .../actions/aggregate/project/Marker.scala | 20 +++-- sbt-app/src/sbt-test/actions/aggregate/test | 14 +-- .../scala/sbt/internal/util/Attributes.scala | 66 +++++++------- 10 files changed, 141 insertions(+), 107 deletions(-) diff --git a/main-settings/src/main/scala/sbt/Structure.scala b/main-settings/src/main/scala/sbt/Structure.scala index 6cc12cb6b..75db2e9dd 100644 --- a/main-settings/src/main/scala/sbt/Structure.scala +++ b/main-settings/src/main/scala/sbt/Structure.scala @@ -16,7 +16,7 @@ import sbt.ConcurrentRestrictions.Tag import sbt.Def.{ Initialize, ScopedKey, Setting, setting } import std.TaskMacro import std.TaskExtra.{ task => mktask, _ } -import scala.reflect.ManifestFactory +import scala.reflect.{ ClassTag, ManifestFactory } /** An abstraction on top of Settings for build configuration and task definition. */ sealed trait Scoped extends Equals: @@ -261,6 +261,11 @@ object Scoped: implicit def inputScopedToKey[T](s: InputKey[T]): ScopedKey[InputTask[T]] = ScopedKey(s.scope, s.key) + private[sbt] def coerceTag[A1: ClassTag]: Manifest[A1] = + summon[ClassTag[A1]] match + case mf: Manifest[A1] => mf + case tag => ManifestFactory.classType[A1](tag.runtimeClass) + /** * Mixin trait for adding convenience vocabulary associated with specifying the [[Scope]] of a setting. * Allows specification of the Scope or part of the [[Scope]] of a setting being referenced. @@ -593,6 +598,12 @@ object Scoped: // format: off type ST[X] = Taskable[X] + final class RichTaskable1[A1](t1: ST[A1]) extends RichTaskables[[F[_]] =>> F[A1]](t1)(using AList.single[A1]): + type Fun[M[_], Ret] = M[A1] => Ret + def identityMap = mapN(identity) + protected def convert[M[_], R](f: M[A1] => R) = f + end RichTaskable1 + final class RichTaskable2[A, B](t2: (ST[A], ST[B])) extends RichTaskables[AList.Tuple2K[A, B]](t2)(using AList.tuple2[A, B]) { type Fun[M[_], Ret] = (M[A], M[B]) => Ret def identityMap = mapN(mkTuple2) @@ -736,6 +747,7 @@ trait TupleSyntax: // this is the least painful arrangement I came up with type ST[T] = Taskable[T] + implicit def taskableToTable1[A1](t1: ST[A1]): RichTaskable1[A1] = new RichTaskable1(t1) implicit def t2ToTable2[A, B](t2: (ST[A], ST[B])): RichTaskable2[A, B] = new RichTaskable2(t2) implicit def t3ToTable3[A, B, C](t3: (ST[A], ST[B], ST[C])): RichTaskable3[A, B, C] = new RichTaskable3(t3) implicit def t4ToTable4[A, B, C, D](t4: (ST[A], ST[B], ST[C], ST[D])): RichTaskable4[A, B, C, D] = new RichTaskable4(t4) @@ -763,35 +775,36 @@ end TupleSyntax object TupleSyntax extends TupleSyntax -import Scoped.extendScoped +import Scoped.{ coerceTag, extendScoped } /** Constructs InputKeys, which are associated with input tasks to define a setting. */ object InputKey: - def apply[A1: Manifest]( + + def apply[A1: ClassTag]( label: String, description: String = "", rank: Int = KeyRanks.DefaultInputRank ): InputKey[A1] = - given mf: Manifest[InputTask[A1]] = - ManifestFactory.classType[InputTask[A1]](classOf[InputTask[A1]], manifest[A1]) + given mf: ClassTag[InputTask[A1]] = + ManifestFactory.classType[InputTask[A1]](classOf[InputTask[A1]], coerceTag[A1]) apply(AttributeKey[InputTask[A1]](label, description, rank)) - def apply[A1: Manifest]( + def apply[A1: ClassTag]( label: String, description: String, extend1: Scoped, extendN: Scoped* ): InputKey[A1] = apply(label, description, KeyRanks.DefaultInputRank, extend1, extendN: _*) - def apply[A1: Manifest]( + def apply[A1: ClassTag]( label: String, description: String, rank: Int, extend1: Scoped, extendN: Scoped* ): InputKey[A1] = - given mf: Manifest[InputTask[A1]] = - ManifestFactory.classType[InputTask[A1]](classOf[InputTask[A1]], manifest[A1]) + given mf: ClassTag[InputTask[A1]] = + ManifestFactory.classType[InputTask[A1]](classOf[InputTask[A1]], coerceTag[A1]) apply(AttributeKey[InputTask[A1]](label, description, extendScoped(extend1, extendN), rank)) def apply[A1](akey: AttributeKey[InputTask[A1]]): InputKey[A1] = @@ -801,49 +814,63 @@ end InputKey /** Constructs TaskKeys, which are associated with tasks to define a setting. */ object TaskKey: - def apply[A1: Manifest]( + def apply[A1: ClassTag](label: String): TaskKey[A1] = + apply[A1]( + label = label, + description = "", + rank = Int.MaxValue, + ) + + def apply[A1: ClassTag](label: String, description: String): TaskKey[A1] = + apply[A1]( + label = label, + description = description, + rank = Int.MaxValue, + ) + + def apply[A1: ClassTag]( label: String, - description: String = "", - rank: Int = KeyRanks.DefaultTaskRank + description: String, + rank: Int, ): TaskKey[A1] = - given mf: Manifest[Task[A1]] = - ManifestFactory.classType[Task[A1]](classOf[Task[A1]], manifest[A1]) + given mf: ClassTag[Task[A1]] = + ManifestFactory.classType[Task[A1]](classOf[Task[A1]], coerceTag[A1]) apply(AttributeKey[Task[A1]](label, description, rank)) - def apply[A1: Manifest]( + def apply[A1: ClassTag]( label: String, description: String, extend1: Scoped, extendN: Scoped* ): TaskKey[A1] = - given mf: Manifest[Task[A1]] = - ManifestFactory.classType[Task[A1]](classOf[Task[A1]], manifest[A1]) + given mf: ClassTag[Task[A1]] = + ManifestFactory.classType[Task[A1]](classOf[Task[A1]], coerceTag[A1]) apply(AttributeKey[Task[A1]](label, description, extendScoped(extend1, extendN))) - def apply[A1: Manifest]( + def apply[A1: ClassTag]( label: String, description: String, rank: Int, extend1: Scoped, extendN: Scoped* ): TaskKey[A1] = - given mf: Manifest[Task[A1]] = - ManifestFactory.classType[Task[A1]](classOf[Task[A1]], manifest[A1]) + given mf: ClassTag[Task[A1]] = + ManifestFactory.classType[Task[A1]](classOf[Task[A1]], coerceTag[A1]) apply(AttributeKey[Task[A1]](label, description, extendScoped(extend1, extendN), rank)) def apply[A1](akey: AttributeKey[Task[A1]]): TaskKey[A1] = Scoped.scopedTask(Scope.ThisScope, akey) - def local[A1: Manifest]: TaskKey[A1] = - given mf: Manifest[Task[A1]] = - ManifestFactory.classType[Task[A1]](classOf[Task[A1]], manifest[A1]) + def local[A1: ClassTag]: TaskKey[A1] = + given mf: ClassTag[Task[A1]] = + ManifestFactory.classType[Task[A1]](classOf[Task[A1]], coerceTag[A1]) apply[A1](AttributeKey.local[Task[A1]]) end TaskKey /** Constructs SettingKeys, which are associated with a value to define a basic setting. */ object SettingKey: - def apply[A1: Manifest: OptJsonWriter]( + def apply[A1: ClassTag: OptJsonWriter]( label: String, ): SettingKey[A1] = apply[A1]( @@ -852,7 +879,7 @@ object SettingKey: rank = KeyRanks.DefaultSettingRank ) - def apply[A1: Manifest: OptJsonWriter]( + def apply[A1: ClassTag: OptJsonWriter]( label: String, description: String, ): SettingKey[A1] = @@ -862,14 +889,14 @@ object SettingKey: rank = KeyRanks.DefaultSettingRank, ) - def apply[A1: Manifest: OptJsonWriter]( + def apply[A1: ClassTag: OptJsonWriter]( label: String, description: String, rank: Int ): SettingKey[A1] = apply(AttributeKey[A1](label, description, rank)) - def apply[A1: Manifest: OptJsonWriter]( + def apply[A1: ClassTag: OptJsonWriter]( label: String, description: String, extend1: Scoped, @@ -877,7 +904,7 @@ object SettingKey: ): SettingKey[A1] = apply(AttributeKey[A1](label, description, extendScoped(extend1, extendN))) - def apply[A1: Manifest: OptJsonWriter]( + def apply[A1: ClassTag: OptJsonWriter]( label: String, description: String, rank: Int, @@ -889,7 +916,8 @@ object SettingKey: def apply[A1](akey: AttributeKey[A1]): SettingKey[A1] = Scoped.scopedSetting(Scope.ThisScope, akey) - def local[A1: Manifest: OptJsonWriter]: SettingKey[A1] = apply[A1](AttributeKey.local[A1]) + def local[A1: ClassTag: OptJsonWriter]: SettingKey[A1] = + apply[A1](AttributeKey.local[A1]) end SettingKey diff --git a/main-settings/src/main/scala/sbt/std/KeyMacro.scala b/main-settings/src/main/scala/sbt/std/KeyMacro.scala index 3b4b5e1f8..7ee065b76 100644 --- a/main-settings/src/main/scala/sbt/std/KeyMacro.scala +++ b/main-settings/src/main/scala/sbt/std/KeyMacro.scala @@ -11,6 +11,7 @@ package std import java.io.File import scala.annotation.tailrec import scala.quoted.* +import scala.reflect.ClassTag import sbt.util.OptJsonWriter @@ -42,21 +43,21 @@ private[sbt] object KeyMacro: } private def keyImpl[A1: Type, A2: Type](methodName: String)( - f: (String, Expr[Manifest[A1]]) => Expr[A2] + f: (String, Expr[ClassTag[A1]]) => Expr[A2] )(using qctx: Quotes): Expr[A2] = val tpe = summon[Type[A1]] f( definingValName(errorMsg(methodName)), - Expr.summon[Manifest[A1]].getOrElse(sys.error("Manifest[A] not found for $tpe")) + Expr.summon[ClassTag[A1]].getOrElse(sys.error("ClassTag[A] not found for $tpe")) ) private def keyImpl2[A1: Type, A2: Type](methodName: String)( - f: (String, Expr[Manifest[A1]], Expr[OptJsonWriter[A1]]) => Expr[A2] + f: (String, Expr[ClassTag[A1]], Expr[OptJsonWriter[A1]]) => Expr[A2] )(using qctx: Quotes): Expr[A2] = val tpe = summon[Type[A1]] f( definingValName(errorMsg(methodName)), - Expr.summon[Manifest[A1]].getOrElse(sys.error("Manifest[A] not found for $tpe")), + Expr.summon[ClassTag[A1]].getOrElse(sys.error("ClassTag[A] not found for $tpe")), Expr.summon[OptJsonWriter[A1]].getOrElse(sys.error("OptJsonWriter[A] not found for $tpe")), ) diff --git a/main/src/main/scala/sbt/Main.scala b/main/src/main/scala/sbt/Main.scala index 043bfe05f..74d4957a2 100644 --- a/main/src/main/scala/sbt/Main.scala +++ b/main/src/main/scala/sbt/Main.scala @@ -39,6 +39,7 @@ import xsbti.compile.CompilerCache import scala.annotation.{ nowarn, tailrec } import scala.concurrent.ExecutionContext import scala.concurrent.duration.Duration +import scala.reflect.ClassTag import scala.util.control.NonFatal /** This class is the entry point for sbt. */ @@ -517,11 +518,14 @@ object BuiltinCommands { def sortByRank(keys: Seq[AttributeKey[_]]): Seq[AttributeKey[_]] = keys.sortBy(_.rank) def withDescription(keys: Seq[AttributeKey[_]]): Seq[AttributeKey[_]] = keys.filter(_.description.isDefined) + def isTask( - mf: Manifest[_] - )(implicit taskMF: Manifest[Task[_]], inputMF: Manifest[InputTask[_]]): Boolean = + mf: ClassTag[_] + )(using taskMF: ClassTag[Task[_]], inputMF: ClassTag[InputTask[_]]): Boolean = mf.runtimeClass == taskMF.runtimeClass || mf.runtimeClass == inputMF.runtimeClass + def topNRanked(n: Int) = (keys: Seq[AttributeKey[_]]) => sortByRank(keys).take(n) + def highPass(rankCutoff: Int) = (keys: Seq[AttributeKey[_]]) => sortByRank(keys).takeWhile(_.rank <= rankCutoff) diff --git a/main/src/main/scala/sbt/ScopedKeyData.scala b/main/src/main/scala/sbt/ScopedKeyData.scala index f09a935a4..50b99e3ac 100644 --- a/main/src/main/scala/sbt/ScopedKeyData.scala +++ b/main/src/main/scala/sbt/ScopedKeyData.scala @@ -22,11 +22,10 @@ final case class ScopedKeyData[A](scoped: ScopedKey[A], value: Any) { "Setting: %s = %s" format (key.manifest.toString, value.toString) ) def fold[T](targ: OptManifest[_] => T, itarg: OptManifest[_] => T, s: => T): T = - key.manifest.runtimeClass match { + key.manifest.runtimeClass match case TaskClass => targ(key.manifest.typeArguments.head) case InputTaskClass => itarg(key.manifest.typeArguments.head) case _ => s - } def fmtMf(s: String): OptManifest[_] => String = s format _ private val TaskClass = classOf[Task[_]] diff --git a/main/src/main/scala/sbt/internal/SettingCompletions.scala b/main/src/main/scala/sbt/internal/SettingCompletions.scala index f0c43d4e9..24f5b1ab5 100644 --- a/main/src/main/scala/sbt/internal/SettingCompletions.scala +++ b/main/src/main/scala/sbt/internal/SettingCompletions.scala @@ -19,6 +19,7 @@ import Types.idFun import complete._ import DefaultParsers._ import scala.annotation.nowarn +import scala.reflect.ClassTag /** * The resulting `session` and verbose and quiet summaries of the result of a set operation. @@ -352,32 +353,33 @@ private[sbt] object SettingCompletions { def configScalaID(c: String): String = Util.quoteIfKeyword(c.capitalize) /** Applies a function on the underlying manifest for T for `key` depending if it is for a `Setting[T]`, `Task[T]`, or `InputTask[T]`. */ + @nowarn def keyType[S](key: AttributeKey[_])( - onSetting: Manifest[_] => S, - onTask: Manifest[_] => S, - onInput: Manifest[_] => S - )(implicit tm: Manifest[Task[_]], im: Manifest[InputTask[_]]): S = { - def argTpe = key.manifest.typeArguments.head + onSetting: ClassTag[_] => S, + onTask: ClassTag[_] => S, + onInput: ClassTag[_] => S + )(using tm: ClassTag[Task[_]], im: ClassTag[InputTask[_]]): S = + def argTpe = key.manifest.typeArguments.head match + case m: Manifest[_] => m + case _ => sys.error(s"Manifest not found for ${key} typeArgument") val TaskClass = tm.runtimeClass val InputTaskClass = im.runtimeClass - key.manifest.runtimeClass match { + key.manifest.runtimeClass match case TaskClass => onTask(argTpe) case InputTaskClass => onInput(argTpe) case _ => onSetting(key.manifest) - } - } /** For a Task[T], InputTask[T], or Setting[T], this returns the manifest for T. */ - def keyUnderlyingType(key: AttributeKey[_]): Manifest[_] = keyType(key)(idFun, idFun, idFun) + def keyUnderlyingType(key: AttributeKey[_]): ClassTag[_] = + keyType(key)(idFun, idFun, idFun) /** * Returns a string representation of the underlying type T for a `key` representing a `Setting[T]`, `Task[T]`, or `InputTask[T]`. - * This string representation is currently a cleaned up toString of the underlying Manifest. + * This string representation is currently a cleaned up toString of the underlying ClassTag. */ - def keyTypeString[T](key: AttributeKey[_]): String = { - val mfToString = (mf: Manifest[_]) => complete.TypeString.cleanup(mf.toString) - keyType(key)(mfToString, mfToString, mfToString) - } + def keyTypeString[T](key: AttributeKey[_]): String = + val tagToString = (tag: ClassTag[_]) => complete.TypeString.cleanup(tag.toString) + keyType(key)(tagToString, tagToString, tagToString) /** True if the `key` represents a setting or task that may be appended using an assignment method such as `+=`. */ def appendable(key: AttributeKey[_]): Boolean = { diff --git a/main/src/main/scala/sbt/nio/Settings.scala b/main/src/main/scala/sbt/nio/Settings.scala index 83cacb396..5a8724a76 100644 --- a/main/src/main/scala/sbt/nio/Settings.scala +++ b/main/src/main/scala/sbt/nio/Settings.scala @@ -93,15 +93,15 @@ private[sbt] object Settings { addTaskDefinition(Def.setting[Task[Seq[Path]]](key, init, setting.pos)) :: outputsAndStamps(taskKey) } - ak.manifest.typeArguments match { - case t :: Nil if seqClass.isAssignableFrom(t.runtimeClass) => + ak.manifest.typeArguments match + case (t: Manifest[_]) :: Nil if seqClass.isAssignableFrom(t.runtimeClass) => t.typeArguments match { case p :: Nil if pathClass.isAssignableFrom(p.runtimeClass) => mkSetting[Seq[Path]] case _ => default } - case t :: Nil if pathClass.isAssignableFrom(t.runtimeClass) => mkSetting[Path] - case _ => default - } + case (t: Manifest[_]) :: Nil if pathClass.isAssignableFrom(t.runtimeClass) => + mkSetting[Path] + case _ => default case _ => Nil } } diff --git a/project/Dependencies.scala b/project/Dependencies.scala index f1c23ed20..ee9569b33 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -82,7 +82,7 @@ object Dependencies { // val lmCoursierShaded = "io.get-coursier" %% "lm-coursier-shaded" % "2.0.10" val lmCoursierShaded = "org.scala-sbt" %% "librarymanagement-coursier" % "2.0.0-alpha2" - lazy val sjsonNewVersion = "0.10.0" + lazy val sjsonNewVersion = "0.11.0" def sjsonNew(n: String) = Def.setting( "com.eed3si9n" %% n % sjsonNewVersion ) // contrabandSjsonNewVersion.value diff --git a/sbt-app/src/sbt-test/actions/aggregate/project/Marker.scala b/sbt-app/src/sbt-test/actions/aggregate/project/Marker.scala index ad1697df4..bffdbc093 100644 --- a/sbt-app/src/sbt-test/actions/aggregate/project/Marker.scala +++ b/sbt-app/src/sbt-test/actions/aggregate/project/Marker.scala @@ -1,19 +1,21 @@ import sbt._, Keys._ import Def.Initialize +import sbt.TupleSyntax.* -object Marker extends AutoPlugin { +object Marker extends AutoPlugin: override def trigger = allRequirements override def requires = sbt.plugins.JvmPlugin object autoImport { final lazy val Mark = TaskKey[Unit]("mark") final def mark: Initialize[Task[Unit]] = mark(baseDirectory) - final def mark(project: Reference): Initialize[Task[Unit]] = mark(baseDirectory in project) - final def mark(baseKey: SettingKey[File]): Initialize[Task[Unit]] = baseKey map { base => - val toMark = base / "ran" - if(toMark.exists) - sys.error("Already ran (" + toMark + " exists)") - else - IO touch toMark + final def mark(project: Reference): Initialize[Task[Unit]] = mark(project / baseDirectory) + final def mark(baseKey: SettingKey[File]): Initialize[Task[Unit]] = baseKey.toTaskable mapN { + base => + val toMark = base / "ran" + if (toMark.exists) + sys.error("Already ran (" + toMark + " exists)") + else + IO touch toMark } } -} +end Marker diff --git a/sbt-app/src/sbt-test/actions/aggregate/test b/sbt-app/src/sbt-test/actions/aggregate/test index d1088241a..fe508af1f 100644 --- a/sbt-app/src/sbt-test/actions/aggregate/test +++ b/sbt-app/src/sbt-test/actions/aggregate/test @@ -11,13 +11,13 @@ $ exists ran $ delete ran # single project, aggregate = true on Mark -> set aggregate in Mark := true +> set Mark / aggregate := true > mark $ exists ran $ delete ran # single project, aggregate = false on Mark -> set aggregate in Mark := false +> set Mark / aggregate := false > mark $ exists ran $ delete ran @@ -58,8 +58,8 @@ $ touch aggregate > reload # add tasks to each subproject -> set Mark in sub := mark(sub).value -> set Mark in sub2 := mark(sub2).value +> set sub / Mark := mark(sub).value +> set sub2 / Mark := mark(sub2).value # check that aggregation works when root project has no task > mark @@ -77,15 +77,15 @@ $ delete sub/ran sub/sub/ran > set Mark := mark.value # disable aggregation for sub/mark so that sub2/mark doesn't run -> set aggregate in (sub,Mark) := false +> set sub / Mark / aggregate := false > mark $ exists ran sub/ran $ absent sub/sub/ran $ delete ran sub/ran # the aggregation setting in a leaf shouldn't affect whether it can be run directly -> set aggregate in (sub2, Mark) := false +> set sub2 / Mark / aggregate := false > sub2/mark $ exists sub/sub/ran $ absent ran sub/ran -$ delete sub/sub/ran \ No newline at end of file +$ delete sub/sub/ran diff --git a/util-collection/src/main/scala/sbt/internal/util/Attributes.scala b/util-collection/src/main/scala/sbt/internal/util/Attributes.scala index 773527601..65bea142e 100644 --- a/util-collection/src/main/scala/sbt/internal/util/Attributes.scala +++ b/util-collection/src/main/scala/sbt/internal/util/Attributes.scala @@ -22,7 +22,7 @@ import sbt.util.OptJsonWriter sealed trait AttributeKey[A]: /** The runtime evidence for `A`. */ - def manifest: Manifest[A] + def manifest: ClassTag[A] // def classTag: ClassTag[A] /** The label is the identifier for the key and is camelCase by convention. */ @@ -52,7 +52,7 @@ sealed trait AttributeKey[A]: end AttributeKey -private[sbt] abstract class SharedAttributeKey[T] extends AttributeKey[T] { +private[sbt] abstract class SharedAttributeKey[A] extends AttributeKey[A]: override final def toString = label override final def hashCode = label.hashCode override final def equals(o: Any) = @@ -61,74 +61,72 @@ private[sbt] abstract class SharedAttributeKey[T] extends AttributeKey[T] { case _ => false }) final def isLocal: Boolean = false -} +end SharedAttributeKey object AttributeKey { - def apply[T: Manifest: OptJsonWriter](name: String): AttributeKey[T] = + def apply[A: ClassTag: OptJsonWriter](name: String): AttributeKey[A] = make(name, None, Nil, Int.MaxValue) - def apply[T: Manifest: OptJsonWriter](name: String, rank: Int): AttributeKey[T] = + def apply[A: ClassTag: OptJsonWriter](name: String, rank: Int): AttributeKey[A] = make(name, None, Nil, rank) - def apply[T: Manifest: OptJsonWriter](name: String, description: String): AttributeKey[T] = + def apply[A: ClassTag: OptJsonWriter](name: String, description: String): AttributeKey[A] = apply(name, description, Nil) - def apply[T: Manifest: OptJsonWriter]( + def apply[A: ClassTag: OptJsonWriter]( name: String, description: String, rank: Int - ): AttributeKey[T] = + ): AttributeKey[A] = apply(name, description, Nil, rank) - def apply[T: Manifest: OptJsonWriter]( + def apply[A: ClassTag: OptJsonWriter]( name: String, description: String, extend: Seq[AttributeKey[_]] - ): AttributeKey[T] = + ): AttributeKey[A] = apply(name, description, extend, Int.MaxValue) - def apply[T: Manifest: OptJsonWriter]( + def apply[A: ClassTag: OptJsonWriter]( name: String, description: String, extend: Seq[AttributeKey[_]], rank: Int - ): AttributeKey[T] = + ): AttributeKey[A] = make(name, Some(description), extend, rank) - private[sbt] def copyWithRank[T](a: AttributeKey[T], rank: Int): AttributeKey[T] = - make(a.label, a.description, a.extend, rank)(a.manifest, a.optJsonWriter) + private[sbt] def copyWithRank[A](a: AttributeKey[A], rank: Int): AttributeKey[A] = + make(a.label, a.description, a.extend, rank)(using a.manifest, a.optJsonWriter) - private[this] def make[T]( + private[this] def make[A]( name: String, description0: Option[String], extend0: Seq[AttributeKey[_]], rank0: Int - )(implicit mf: Manifest[T], ojw: OptJsonWriter[T]): AttributeKey[T] = - new SharedAttributeKey[T] { + )(using mf: ClassTag[A], ojw: OptJsonWriter[A]): AttributeKey[A] = + new SharedAttributeKey[A]: require( name.headOption.exists(_.isLower), s"A named attribute key must start with a lowercase letter: $name" ) - def manifest = mf - val label = Util.hyphenToCamel(name) - def description = description0 - def extend = extend0 - def rank = rank0 - def optJsonWriter = ojw - } + override def manifest: ClassTag[A] = mf + override val label: String = Util.hyphenToCamel(name) + override def description: Option[String] = description0 + override def extend: Seq[AttributeKey[_]] = extend0 + override def rank: Int = rank0 + override def optJsonWriter: OptJsonWriter[A] = ojw - private[sbt] def local[T](implicit mf: Manifest[T], ojw: OptJsonWriter[T]): AttributeKey[T] = - new AttributeKey[T] { - def manifest = mf - def label = LocalLabel - def description = None - def extend = Nil + private[sbt] def local[A](using ct: ClassTag[A], ojw: OptJsonWriter[A]): AttributeKey[A] = + new AttributeKey[A]: + override def manifest: ClassTag[A] = ct + override def label: String = LocalLabel + override def description: Option[String] = None + override def extend: Seq[AttributeKey[_]] = Nil override def toString = label - def isLocal: Boolean = true - def rank = Int.MaxValue - val optJsonWriter = ojw - } + override def isLocal: Boolean = true + override def rank: Int = Int.MaxValue + override val optJsonWriter: OptJsonWriter[A] = ojw private[sbt] final val LocalLabel = "$" + "local"