Skip to content

Conversation

@alexarchambault
Copy link
Collaborator

@alexarchambault alexarchambault commented Jan 13, 2026

Not sure why, the position of the warning in the added test is a RangePosition (with a start, an end, and a "point" that may lie outside of the [start,end] range), and its point value doesn't get offset by the LineNumberModifier plugin. This makes Position.formatError throw an IndexOutOfBoundsException if we don't fix that point value ourselves.

@alexarchambault
Copy link
Collaborator Author

In more detail, we get the following in Scala 2.13

@ import java.io._; import java.util.Map; class Map
java.lang.IndexOutOfBoundsException: 949
  scala.reflect.internal.util.BatchSourceFile.offsetToLine(SourceFile.scala:213)
  scala.reflect.internal.util.InternalPositionImpl.line0(Position.scala:212)
  scala.reflect.internal.util.InternalPositionImpl.line(Position.scala:214)
  scala.reflect.internal.util.InternalPositionImpl.line$(Position.scala:214)
  scala.reflect.internal.util.Position.line(Position.scala:19)
  scala.reflect.internal.util.InternalPositionImpl.showError(Position.scala:252)
  scala.reflect.internal.util.InternalPositionImpl.showError$(Position.scala:238)
  scala.reflect.internal.util.Position.showError(Position.scala:19)
  scala.reflect.internal.util.Position$.formatMessage(Position.scala:57)
  ammonite.compiler.Compiler$$anon$1.$anonfun$x$20$5(Compiler.scala:204)
  ammonite.compiler.Compiler$$anon$1.$anonfun$x$20$5$adapted(Compiler.scala:204)
  ammonite.compiler.MakeReporter$$anon$1.display(MakeReporter.scala:41)
  ammonite.compiler.MakeReporter$$anon$1.doReport(MakeReporter.scala:33)
  scala.reflect.internal.Reporter.filteredInfo(Reporting.scala:126)
  scala.reflect.internal.Reporter.warning(Reporting.scala:118)
  scala.tools.nsc.Reporting$PerRunReporting.scala$tools$nsc$Reporting$PerRunReporting$$issueWarning(Reporting.scala:229)
  scala.tools.nsc.Reporting$PerRunReporting.$anonfun$runFinished$2(Reporting.scala:170)
  scala.tools.nsc.Reporting$PerRunReporting.$anonfun$runFinished$2$adapted(Reporting.scala:170)
  scala.collection.mutable.LinkedHashSet.foreach(LinkedHashSet.scala:128)
  scala.tools.nsc.Reporting$PerRunReporting.$anonfun$runFinished$1(Reporting.scala:170)
  scala.tools.nsc.Reporting$PerRunReporting.$anonfun$runFinished$1$adapted(Reporting.scala:170)
  scala.collection.IterableOnceOps.foreach(IterableOnce.scala:630)
  scala.collection.IterableOnceOps.foreach$(IterableOnce.scala:628)
  scala.collection.AbstractIterator.foreach(Iterator.scala:1313)
  scala.tools.nsc.Reporting$PerRunReporting.runFinished(Reporting.scala:170)
  scala.tools.nsc.Global$Run.compileUnitsInternal(Global.scala:1607)
  scala.tools.nsc.Global$Run.compileUnits(Global.scala:1543)
  scala.tools.nsc.Global$Run.compileSources(Global.scala:1535)
  scala.tools.nsc.Global$Run.compileFiles(Global.scala:1651)
  ammonite.compiler.Compiler$$anon$1.compile(Compiler.scala:290)
  ammonite.compiler.CompilerLifecycleManager.compileClass(CompilerLifecycleManager.scala:146)
  ammonite.interp.Interpreter.$anonfun$evaluateLine$2(Interpreter.scala:290)
  ammonite.util.Catching.flatMap(Res.scala:110)
  ammonite.interp.Interpreter.evaluateLine(Interpreter.scala:285)
  ammonite.interp.Interpreter.$anonfun$processLine$6(Interpreter.scala:272)
  ammonite.util.Res$Success.flatMap(Res.scala:58)
  ammonite.interp.Interpreter.$anonfun$processLine$4(Interpreter.scala:254)
  ammonite.util.Res$Success.flatMap(Res.scala:58)
  ammonite.interp.Interpreter.$anonfun$processLine$2(Interpreter.scala:247)
  ammonite.util.Catching.flatMap(Res.scala:110)
  ammonite.interp.Interpreter.processLine(Interpreter.scala:246)
  ammonite.repl.Repl.$anonfun$action$10(Repl.scala:214)
  ammonite.util.Res$Success.flatMap(Res.scala:58)
  ammonite.repl.Repl.$anonfun$action$6(Repl.scala:193)
  ammonite.repl.Scoped.$anonfun$flatMap$1(Signaller.scala:45)
  ammonite.repl.Signaller.apply(Signaller.scala:28)
  ammonite.repl.Scoped.flatMap(Signaller.scala:45)
  ammonite.repl.Scoped.flatMap$(Signaller.scala:45)
  ammonite.repl.Signaller.flatMap(Signaller.scala:16)
  ammonite.repl.Repl.$anonfun$action$4(Repl.scala:185)
  ammonite.util.Res$Success.flatMap(Res.scala:58)
  ammonite.repl.Repl.action(Repl.scala:175)
  ammonite.repl.Repl.loop$1(Repl.scala:226)
  ammonite.repl.Repl.run(Repl.scala:241)
  ammonite.Main.run(Main.scala:249)
  ammonite.MainRunner.$anonfun$runRepl$1(MainRunner.scala:76)
  ammonite.MainRunner.watchLoop(MainRunner.scala:57)
  ammonite.MainRunner.runRepl(MainRunner.scala:76)
  ammonite.AmmoniteMain$.main0(AmmoniteMain.scala:98)
  ammonite.AmmoniteMain$.main(AmmoniteMain.scala:31)
  ammonite.Main$.main(Main.scala:284)
  ammonite.Main.main(Main.scala)

and the following in Scala 2.12 (different invalid array index)

@ import java.io._; import java.util.Map; class Map
java.lang.ArrayIndexOutOfBoundsException: Index 28 out of bounds for length 28
  scala.reflect.internal.util.BatchSourceFile.findLine$1(SourceFile.scala:201)
  scala.reflect.internal.util.BatchSourceFile.offsetToLine(SourceFile.scala:204)
  scala.reflect.internal.util.InternalPositionImpl.line(Position.scala:175)
  scala.reflect.internal.util.InternalPositionImpl.line$(Position.scala:175)
  scala.reflect.internal.util.Position.line(Position.scala:19)
  scala.reflect.internal.util.InternalPositionImpl.where$1(Position.scala:193)
  scala.reflect.internal.util.InternalPositionImpl.errorAt$1(Position.scala:196)
  scala.reflect.internal.util.InternalPositionImpl.showError(Position.scala:201)
  scala.reflect.internal.util.InternalPositionImpl.showError$(Position.scala:182)
  scala.reflect.internal.util.Position.showError(Position.scala:19)
  scala.reflect.internal.util.Position$.formatMessage(Position.scala:54)
  ammonite.compiler.Compiler$$anon$1.$anonfun$x$20$5(Compiler.scala:204)
  ammonite.compiler.Compiler$$anon$1.$anonfun$x$20$5$adapted(Compiler.scala:204)
  ammonite.compiler.MakeReporter$$anon$1.display(MakeReporter.scala:40)
  ammonite.compiler.MakeReporter$$anon$1.doReport(MakeReporter.scala:32)
  scala.tools.nsc.reporters.FilteringReporter.info0(Reporter.scala:92)
  scala.reflect.internal.Reporter.filteredInfo(Reporting.scala:116)
  scala.reflect.internal.Reporter.warning(Reporting.scala:110)
  scala.tools.nsc.Reporting$PerRunReporting.issueWarning(Reporting.scala:112)
  scala.tools.nsc.Reporting$PerRunReporting.issueIfNotSuppressed(Reporting.scala:131)
  scala.tools.nsc.Reporting$PerRunReporting.$anonfun$reportSuspendedMessages$4(Reporting.scala:94)
  scala.tools.nsc.Reporting$PerRunReporting.$anonfun$reportSuspendedMessages$4$adapted(Reporting.scala:94)
  scala.collection.mutable.LinkedHashSet.foreach(LinkedHashSet.scala:95)
  scala.tools.nsc.Reporting$PerRunReporting.$anonfun$reportSuspendedMessages$3(Reporting.scala:94)
  scala.tools.nsc.Reporting$PerRunReporting.reportSuspendedMessages(Reporting.scala:94)
  scala.tools.nsc.typechecker.Analyzer$typerFactory$TyperPhase.apply(Analyzer.scala:125)
  scala.tools.nsc.Global$GlobalPhase.applyPhase(Global.scala:465)
  scala.tools.nsc.typechecker.Analyzer$typerFactory$TyperPhase.run(Analyzer.scala:102)
  scala.tools.nsc.Global$Run.compileUnitsInternal(Global.scala:1521)
  scala.tools.nsc.Global$Run.compileUnits(Global.scala:1505)
  scala.tools.nsc.Global$Run.compileSources(Global.scala:1498)
  scala.tools.nsc.Global$Run.compileFiles(Global.scala:1609)
  ammonite.compiler.Compiler$$anon$1.compile(Compiler.scala:290)
  ammonite.compiler.CompilerLifecycleManager.compileClass(CompilerLifecycleManager.scala:146)
  ammonite.interp.Interpreter.$anonfun$evaluateLine$2(Interpreter.scala:290)
  ammonite.util.Catching.flatMap(Res.scala:110)
  ammonite.interp.Interpreter.evaluateLine(Interpreter.scala:285)
  ammonite.interp.Interpreter.$anonfun$processLine$6(Interpreter.scala:272)
  ammonite.util.Res$Success.flatMap(Res.scala:58)
  ammonite.interp.Interpreter.$anonfun$processLine$4(Interpreter.scala:254)
  ammonite.util.Res$Success.flatMap(Res.scala:58)
  ammonite.interp.Interpreter.$anonfun$processLine$2(Interpreter.scala:247)
  ammonite.util.Catching.flatMap(Res.scala:110)
  ammonite.interp.Interpreter.processLine(Interpreter.scala:246)
  ammonite.repl.Repl.$anonfun$action$10(Repl.scala:214)
  ammonite.util.Res$Success.flatMap(Res.scala:58)
  ammonite.repl.Repl.$anonfun$action$6(Repl.scala:193)
  ammonite.repl.Scoped.$anonfun$flatMap$1(Signaller.scala:45)
  ammonite.repl.Signaller.apply(Signaller.scala:28)
  ammonite.repl.Scoped.flatMap(Signaller.scala:45)
  ammonite.repl.Scoped.flatMap$(Signaller.scala:45)
  ammonite.repl.Signaller.flatMap(Signaller.scala:16)
  ammonite.repl.Repl.$anonfun$action$4(Repl.scala:185)
  ammonite.util.Res$Success.flatMap(Res.scala:58)
  ammonite.repl.Repl.action(Repl.scala:175)
  ammonite.repl.Repl.loop$1(Repl.scala:226)
  ammonite.repl.Repl.run(Repl.scala:241)
  ammonite.Main.run(Main.scala:249)
  ammonite.MainRunner.$anonfun$runRepl$1(MainRunner.scala:76)
  ammonite.MainRunner.watchLoop(MainRunner.scala:57)
  ammonite.MainRunner.runRepl(MainRunner.scala:76)
  ammonite.AmmoniteMain$.main0(AmmoniteMain.scala:98)
  ammonite.AmmoniteMain$.main(AmmoniteMain.scala:31)
  ammonite.Main$.main(Main.scala:284)
  ammonite.Main.main(Main.scala)

Not sure why, this warning's position is a RangePosition (with a start, an end,
and a "point" that may lie outside of the [start,end] range), and its point
value doesn't get offset by the LineNumberModifier plugin. This makes
Position.formatError throw an ArrayIndexOutOfBoundException if we don't
fix that point value ourselves.
@alexarchambault alexarchambault marked this pull request as ready for review January 13, 2026 15:35
@alexarchambault
Copy link
Collaborator Author

Don't know if anyone feels like reviewing, @lihaoyi @lefou @lolgab ?

def mvnDeps: T[Seq[Dep]] = super.mvnDeps() ++ amm.compiler().mvnDeps() ++ Seq(
Deps.scalazCore
Deps.scalazCore,
Deps.coursierVersions(scalaVersion())
Copy link
Member

Choose a reason for hiding this comment

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

I don't see how this change is related to the position fix.
Can you help me?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The dependency is only added in the tests of the repl module. I use it to compare Scala versions (some test data is different up to 2.12.12 vs after it), rather than flaky hand made version comparison.


def fixPos(pos: Position): Position =
pos match {
case r: RangePosition if r.point > r.source.length && r.point - importsLen >= 0 && r.point - importsLen <= r.source.length =>
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is kind of a hack. This substracts importsLen when it feels it's the right thing to do (point is higher than the source length, and offsetting importsLen from it brings it back into the source).

@alexarchambault
Copy link
Collaborator Author

alexarchambault commented Jan 13, 2026

Don't know if anyone feels like reviewing, @lihaoyi @lefou @lolgab ?

Reviewing and approving ideally haha, seems that's required to merge

@lolgab lolgab self-requested a review January 14, 2026 05:45
@alexarchambault alexarchambault merged commit 5d1b556 into com-lihaoyi:main Jan 14, 2026
13 checks passed
@alexarchambault alexarchambault deleted the fix-position-crash branch January 14, 2026 07:45
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.

2 participants