Skip to content

Conversation

@lihaoyi
Copy link
Contributor

@lihaoyi lihaoyi commented Oct 29, 2025

Fixes #24300

Tested manually with the reproduction in the original ticket, seems that quotes.reflect.Position.ofMacroExpansion.startLine and .sourceFile now correctly considers the ///MAGIC:build.sbt comment

lihaoyi scala3$ bin/scala -Ymagic-offset-header:MAGIC
The `--offline` option is experimental
Please bear in mind that non-ideal user experience should be expected.
If you encounter any bugs or have feedback to share, make sure to reach out to the maintenance team at https://github.com/VirtusLab/scala-cli
Welcome to Scala 3.8.0-RC1-bin-SNAPSHOT-git-840804f (17.0.14, Java OpenJDK 64-Bit Server VM).
Type in expressions for evaluation. Or try :help.

scala> import scala.quoted.*
     | def lineImpl(using Quotes): Expr[Int] = {
     |   val sourceFile = quotes.reflect.Position.ofMacroExpansion.sourceFile
     |   val line = quotes.reflect.Position.ofMacroExpansion.startLine + 1
     |   Expr(line)
     | }
     | inline implicit def line: Int = ${ lineImpl }
     |
     | def fileImpl(using Quotes): Expr[String] = {
     |   import quotes.reflect._
     |   Expr(quotes.reflect.Position.ofMacroExpansion.sourceFile.path)
     | }
     |
     | inline implicit def file: String = ${ fileImpl }
def lineImpl(using x$1: scala.quoted.Quotes): scala.quoted.Expr[Int]
def line: Int
def fileImpl(using x$1: scala.quoted.Quotes): scala.quoted.Expr[String]
def file: String

scala> {
     |
     |
     | ///MAGIC:build.sbt
     | println(file)
     | println(line)
     | def bar = ???
     | bar
     | }
build.sbt
1
scala.NotImplementedError: an implementation is missing
  at scala.Predef$.$qmark$qmark$qmark(Predef.scala:344)
  at rs$line$3$.bar(rs$line$3:2)
  ... 32 elided

Copy link
Contributor

@Linyxus Linyxus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. But I wonder why we created a new SourcePos in the first place. I am not super familiar with the macro part, so I will want for @jchyb to approve.

@lihaoyi
Copy link
Contributor Author

lihaoyi commented Nov 1, 2025

Ping @jchyb @Linyxus

@Linyxus Linyxus merged commit d20dd28 into scala:main Nov 1, 2025
51 checks passed

def context(inlinedFrom: tpd.Tree)(using Context): Context =
QuotesCache.init(ctx.fresh).setProperty(MacroExpansionPosition, SourcePosition(inlinedFrom.source, inlinedFrom.span)).setTypeAssigner(new Typer(ctx.nestingLevel + 1)).withSource(inlinedFrom.source)
QuotesCache.init(ctx.fresh).setProperty(MacroExpansionPosition, inlinedFrom.sourcePos).setTypeAssigner(new Typer(ctx.nestingLevel + 1)).withSource(inlinedFrom.source)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fine for the Quotes. What I don't understand is why the logic to handle the magic offset is done in sourcePos rather than in SourceFile.offsetToLine. @Linyxus?

def offsetToLine(offset: Int): Int = {
lastLine = Util.bestFit(lineIndices, lineIndices.length, offset, lastLine)
if (offset >= length) lastLine -= 1 // compensate for the sentinel
lastLine
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe even in SourcePosition if it is easier to manipulate the Span. sourcePos feels like a non general place to do that logic.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds right. I will give it a try.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Linyxus For example, we define a method sourcePos over Symbol which doesn't take into account the logic you added. If we were to report an error message where we recover the position from the symbol, it would not work.

Sounds right. I will give it a try.

Thanks!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I started to remember. I tried to do that initially but the main blocker is that lineToOffset and offsetToLine does not have a Context parameter, which is needed to read from compiler options. So I didn't manage to do that. Do you have an idea on how to do this properly?

Copy link
Member

@hamzaremmal hamzaremmal Nov 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. In this case, just add the (using Context) where it is needed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I remember I tried that too and it turns out that this function is called where no context is available yet.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We propagate it where it is needed. If calling a method is context dependent because of a setting, then it should be reflected in its signature with (using Context).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

-Ymagic-offset-header does not affect quotes.reflect.Position.ofMacroExpansion

4 participants