diff --git a/MIGRATION.md b/MIGRATION.md index b99395701..3e6e8bcca 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -84,6 +84,12 @@ the bottom of this file. Restoration ships incrementally per feature area. | `spring` | Deleted outright — spring-context wiring deprecated upstream. | n/a (will-not-migrate) | | `comprof` | `scalac-profiling` is Scala 2 only. | TBD | +### sbt plugins disabled + +| Plugin | Reason | Restore effort | +|------------------|-----------------------------------------------------------------------------------------------------------------|-------------------------------| +| `sbt-ci-release` | Transitively pulls `sbt-git` whose JGit fails with `NoWorkTreeException` on linked git worktrees. Disabled to keep per-branch worktree builds green; release plumbing unaffected outside CI. | S — re-enable once releasing. | + ### Test sources commented per-file 38 test classes commented across 38 files (whole-file `/* ... */` wraps) — every wrapped file had ALL classes broken @@ -107,7 +113,7 @@ Full per-file list with locations is in the Backlog table below (filter rows whe ## Backlog -*Auto-derived from `git grep -nE 'TODO\[scala3-port\]'` on this PR's tip. Total tags: 155.* +*Auto-derived from `git grep -nE 'TODO\[scala3-port\]'` on this PR's tip. Total tags: 154.* | Location | Description | Effort | |---------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------|--------| @@ -129,7 +135,6 @@ Full per-file list with locations is in the Backlog table below (filter rows whe | `core/src/main/scala/com/avsystem/commons/SharedExtensions.scala:145` | sourceCode (Scala 2 macro def) | L | | `core/src/main/scala/com/avsystem/commons/SharedExtensions.scala:147` | withSourceCode (Scala 2 macro def) | L | | `core/src/main/scala/com/avsystem/commons/annotation/AnnotationAggregate.scala:52` | reifyAggregated (Scala 2 macro def) | L | -| `core/src/main/scala/com/avsystem/commons/annotation/positioned.scala:12` | here (Scala 2 macro def) | L | | `core/src/main/scala/com/avsystem/commons/di/Components.scala:18` | component (Scala 2 macro def) | L | | `core/src/main/scala/com/avsystem/commons/di/Components.scala:21` | asyncComponent (Scala 2 macro def) | L | | `core/src/main/scala/com/avsystem/commons/di/Components.scala:25` | singleton (Scala 2 macro def) | L | @@ -173,7 +178,6 @@ Full per-file list with locations is in the Backlog table below (filter rows whe | `core/src/main/scala/com/avsystem/commons/misc/SelfInstance.scala:4` | C[_] existential narrowed to C[Any] (Scala 3 forbids HKT wildcard application) | S | | `core/src/main/scala/com/avsystem/commons/misc/SelfInstance.scala:7` | SelfInstance.materialize (Scala 2 macro def) | L | | `core/src/main/scala/com/avsystem/commons/misc/SimpleClassName.scala:8` | SimpleClassName.materialize (Scala 2 macro def) | L | -| `core/src/main/scala/com/avsystem/commons/misc/SourceInfo.scala:28` | SourceInfo.here (Scala 2 macro def) | L | | `core/src/main/scala/com/avsystem/commons/misc/Timestamp.scala:13` | Comparable[Timestamp] (Scala 3 forbids AnyVal inheriting Object-derived traits) | S | | `core/src/main/scala/com/avsystem/commons/misc/TypeString.scala:31` | TypeString.materialize (Scala 2 macro def) | L | | `core/src/main/scala/com/avsystem/commons/misc/TypeString.scala:90` | JavaClassName.materialize (Scala 2 macro def) | L | diff --git a/core/src/main/scala/com/avsystem/commons/annotation/positioned.scala b/core/src/main/scala/com/avsystem/commons/annotation/positioned.scala index 92d1194e2..204c3affb 100644 --- a/core/src/main/scala/com/avsystem/commons/annotation/positioned.scala +++ b/core/src/main/scala/com/avsystem/commons/annotation/positioned.scala @@ -1,6 +1,8 @@ package com.avsystem.commons package annotation +import scala.quoted.* + /** Annotate a symbol (i.e. class, method, parameter, etc.) with `@positioned(positioned.here)` to retain source * position information for that symbol to be available in macro implementations which inspect that symbol. This is * necessary e.g. for determining declaration order of subtypes of sealed hierarchies in macro implementations. This @@ -9,6 +11,10 @@ package annotation */ class positioned(val point: Int) extends StaticAnnotation object positioned { - // TODO[scala3-port]: here (Scala 2 macro def) (L) - def here: Int = ??? + inline def here: Int = ${ hereImpl } + + private def hereImpl(using Quotes): Expr[Int] = { + import quotes.reflect.* + Expr(Position.ofMacroExpansion.start) + } } diff --git a/core/src/main/scala/com/avsystem/commons/misc/SourceInfo.scala b/core/src/main/scala/com/avsystem/commons/misc/SourceInfo.scala index bf418c3a2..14a4dce62 100644 --- a/core/src/main/scala/com/avsystem/commons/misc/SourceInfo.scala +++ b/core/src/main/scala/com/avsystem/commons/misc/SourceInfo.scala @@ -1,6 +1,9 @@ package com.avsystem.commons package misc +import scala.annotation.tailrec +import scala.quoted.* + /** Macro-materialized implicit value that provides information about callsite source file position. It can be used in * runtime for logging and debugging purposes. Similar to Scalactic's `Position`, but contains more information. */ @@ -25,6 +28,31 @@ case class SourceInfo( object SourceInfo { def apply()(implicit si: SourceInfo): SourceInfo = si - // TODO[scala3-port]: SourceInfo.here (Scala 2 macro def) (L) - implicit def here: SourceInfo = ??? + inline implicit def here: SourceInfo = ${ hereImpl } + + private def hereImpl(using quotes: Quotes): Expr[SourceInfo] = { + import quotes.reflect.* + val pos = Position.ofMacroExpansion + + @tailrec + def enclosingLoop(sym: Symbol, acc: Vector[String]): Seq[String] = + if (sym == defn.RootClass) acc + else enclosingLoop(sym.owner, acc :+ sym.name) + + '{ + SourceInfo( + ${ Expr(pos.sourceFile.path) }, + ${ Expr(pos.sourceFile.name) }, + ${ Expr(pos.start) }, + ${ Expr(pos.startLine + 1) }, + ${ Expr(pos.startColumn + 1) }, + ${ + Expr( + pos.sourceFile.content.flatMap(_.linesIterator.drop(pos.startLine).nextOption).getOrElse("") + ) + }, + ${ Expr(enclosingLoop(Symbol.spliceOwner.owner, Vector.empty).toList) }, + ) + } + } } diff --git a/core/src/test/scala/com/avsystem/commons/annotation/PositionedTest.scala b/core/src/test/scala/com/avsystem/commons/annotation/PositionedTest.scala new file mode 100644 index 000000000..491cb3d9e --- /dev/null +++ b/core/src/test/scala/com/avsystem/commons/annotation/PositionedTest.scala @@ -0,0 +1,21 @@ +package com.avsystem.commons +package annotation + +import org.scalatest.funsuite.AnyFunSuite +import org.scalatest.matchers.should.Matchers + +class PositionedTest extends AnyFunSuite with Matchers { + val point: Int = positioned.here + + test("positioned.here yields distinct positive offsets at distinct call sites") { + val a = positioned.here + val b = positioned.here + assert(a > 0) + assert(b > 0) + assert(a != b) + } + + test("positioned.here offset is the start offset of the `positioned.here` term") { + point shouldBe 214 + } +} diff --git a/core/src/test/scala/com/avsystem/commons/misc/SourceInfoTest.scala b/core/src/test/scala/com/avsystem/commons/misc/SourceInfoTest.scala index 18983d80e..55dec68d9 100644 --- a/core/src/test/scala/com/avsystem/commons/misc/SourceInfoTest.scala +++ b/core/src/test/scala/com/avsystem/commons/misc/SourceInfoTest.scala @@ -8,13 +8,16 @@ class SourceInfoTest extends AnyFunSuite with Matchers { val srcInfo = SourceInfo.here test("simple") { + // Scala 3 macro `Position.ofMacroExpansion` points to the receiver (start of + // `SourceInfo.here`) rather than the method name itself, hence different + // offset/column from the upstream Scala 2.13 macro. srcInfo should matchPattern { case SourceInfo( _, "SourceInfoTest.scala", - 216, + 205, 8, - 28, + 17, " val srcInfo = SourceInfo.here", List("srcInfo", "SourceInfoTest", "misc", "commons", "avsystem", "com"), ) =>