Skip to content

Commit 96bf85e

Browse files
committed
Show inlined positions with source code
This gives more context to the users on what happen and where the code came from.
1 parent 0a834ff commit 96bf85e

27 files changed

+196
-70
lines changed

compiler/src/dotty/tools/dotc/reporting/MessageRendering.scala

+11-10
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,9 @@ trait MessageRendering {
2525
def stripColor(str: String): String =
2626
str.replaceAll("\u001b\\[.*?m", "")
2727

28-
/** When inlining a method call, if there's an error we'd like to get the
29-
* outer context and the `pos` at which the call was inlined.
30-
*
31-
* @return a list of strings with inline locations
32-
*/
33-
def outer(pos: SourcePosition, prefix: String)(using Context): List[String] =
34-
if (pos.outer.exists)
35-
i"$prefix| This location contains code that was inlined from $pos" ::
36-
outer(pos.outer, prefix)
28+
/** List of all the inline calls that surround the position */
29+
def inlinePosStack(pos: SourcePosition): List[SourcePosition] =
30+
if pos.outer.exists then pos :: inlinePosStack(pos.outer)
3731
else Nil
3832

3933
/** Get the sourcelines before and after the position, as well as the offset
@@ -173,10 +167,17 @@ trait MessageRendering {
173167
if (pos.exists) {
174168
val pos1 = pos.nonInlined
175169
if (pos1.exists && pos1.source.file.exists) {
170+
// Print error message at inline position
176171
val (srcBefore, srcAfter, offset) = sourceLines(pos1, levelString)
177172
val marker = columnMarker(pos1, offset, levelString)
178173
val err = errorMsg(pos1, msg.message, offset)
179-
sb.append((srcBefore ::: marker :: err :: outer(pos, " " * (offset - 1)) ::: srcAfter).mkString(EOL))
174+
sb.append((srcBefore ::: marker :: err :: srcAfter).mkString(EOL))
175+
// print inline stack trace
176+
for inlinedPos <- inlinePosStack(pos) if inlinedPos != pos1 do
177+
val (srcBefore, srcAfter, offset) = sourceLines(inlinedPos, levelString)
178+
val marker = columnMarker(inlinedPos, offset, levelString)
179+
val prefix = " " * (offset - 1)
180+
sb.append((i"\nThis location contains code that was inlined from $pos" :: srcBefore ::: marker :: srcAfter).mkString(EOL))
180181
}
181182
else sb.append(msg.message)
182183
}

compiler/src/dotty/tools/dotc/transform/Splicer.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ object Splicer {
4949
val oldContextClassLoader = Thread.currentThread().getContextClassLoader
5050
Thread.currentThread().setContextClassLoader(classLoader)
5151
try {
52-
val interpreter = new Interpreter(spliceExpansionPos, classLoader)
52+
val interpreter = new Interpreter(splicePos, classLoader)
5353

5454
// Some parts of the macro are evaluated during the unpickling performed in quotedExprToTree
5555
val interpretedExpr = interpreter.interpret[Quotes => scala.quoted.Expr[Any]](tree)

compiler/src/dotty/tools/dotc/typer/Inliner.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -853,7 +853,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) {
853853
evidence.tpe match
854854
case fail: Implicits.SearchFailureType =>
855855
val msg = evTyper.missingArgMsg(evidence, tpt.tpe, "")
856-
errorTree(tpt, em"$msg")
856+
errorTree(call, em"$msg")
857857
case _ =>
858858
evidence
859859
return searchImplicit(callTypeArgs.head)

compiler/test-resources/repl/i9227

-1
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,4 @@ scala> import scala.quoted._; inline def myMacro[T]: Unit = ${ myMacroImpl[T] };
33
1 | import scala.quoted._; inline def myMacro[T]: Unit = ${ myMacroImpl[T] }; def myMacroImpl[T](using Quotes): Expr[Unit] = '{}; println(myMacro[Int])
44
| ^^^^^^^^^^^^
55
| Cannot call macro method myMacroImpl defined in the same source file
6-
| This location contains code that was inlined from rs$line$1:1
76
1 error found

tests/neg-macros/delegate-match-1.check

-1
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,3 @@
44
| ^
55
| AmbiguousImplicits
66
| both value a1 in class Test1 and value a2 in class Test1 match type A
7-
| This location contains code that was inlined from Test_2.scala:6

tests/neg-macros/delegate-match-2.check

-1
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,3 @@
44
| ^
55
| DivergingImplicit
66
| method a1 in class Test produces a diverging implicit search when trying to match type A
7-
| This location contains code that was inlined from Test_2.scala:5

tests/neg-macros/delegate-match-3.check

-1
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,3 @@
44
| ^
55
| NoMatchingImplicits
66
| no implicit values were found that match type A
7-
| This location contains code that was inlined from Test_2.scala:3

tests/neg-macros/i11386.check

+6-4
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@
33
6 | dummy(0) // error
44
| ^
55
| test
6-
| This location contains code that was inlined from Test_2.scala:6
7-
| This location contains code that was inlined from Macro_1.scala:7
6+
This location contains code that was inlined from Test_2.scala:6
7+
7 | notNull(i)
8+
| ^^^^^^^^^^
89
-- Error: tests/neg-macros/i11386/Test_2.scala:8:20 --------------------------------------------------------------------
910
8 | dummy(int2String(0)) // error
1011
| ^^^^^^^^^^^^^
1112
| test
12-
| This location contains code that was inlined from Test_2.scala:8
13-
| This location contains code that was inlined from Macro_1.scala:7
13+
This location contains code that was inlined from Test_2.scala:8
14+
7 | notNull(i)
15+
| ^^^^^^^^^^

tests/neg-macros/i13991.check

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
2+
-- Error: tests/neg-macros/i13991/Test_2.scala:6:5 ---------------------------------------------------------------------
3+
6 | v2 // error
4+
| ^^
5+
| Error
6+
This location contains code that was inlined from Test_2.scala:3
7+
3 | inline def v2 = InlineMac.sample("foo")
8+
| ^^^^^
9+
This location contains code that was inlined from Test_2.scala:3
10+
3 | inline def v2 = InlineMac.sample("foo")
11+
| ^^^^^^^^^^^^^^^^^^^^^^^

tests/neg-macros/i13991/Macro_1.scala

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import scala.quoted.*
2+
3+
object InlineMac:
4+
5+
inline def sample(inline expr: String): Int =
6+
${ sampleImpl('expr) }
7+
8+
def sampleImpl(expr: Expr[String])(using Quotes): Expr[Int] =
9+
import quotes.reflect.*
10+
report.errorAndAbort("Error", expr)

tests/neg-macros/i13991/Test_2.scala

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
object Main:
2+
def main(args: Array[String]): Unit =
3+
inline def v2 = InlineMac.sample("foo")
4+
inline def v1 = v2
5+
6+
v2 // error

tests/neg-macros/i6432.check

-3
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,11 @@
33
4 | foo"abc${"123"}xyz${"456"}fgh" // error // error // error
44
| ^^^
55
| abc
6-
| This location contains code that was inlined from Test_2.scala:4
76
-- Error: tests/neg-macros/i6432/Test_2.scala:4:17 ---------------------------------------------------------------------
87
4 | foo"abc${"123"}xyz${"456"}fgh" // error // error // error
98
| ^^^
109
| xyz
11-
| This location contains code that was inlined from Test_2.scala:4
1210
-- Error: tests/neg-macros/i6432/Test_2.scala:4:28 ---------------------------------------------------------------------
1311
4 | foo"abc${"123"}xyz${"456"}fgh" // error // error // error
1412
| ^^^
1513
| fgh
16-
| This location contains code that was inlined from Test_2.scala:4

tests/neg-macros/i6432b.check

-3
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,11 @@
33
4 | foo"""abc${"123"}xyz${"456"}fgh""" // error // error // error
44
| ^^^
55
| abc
6-
| This location contains code that was inlined from Test_2.scala:4
76
-- Error: tests/neg-macros/i6432b/Test_2.scala:4:19 --------------------------------------------------------------------
87
4 | foo"""abc${"123"}xyz${"456"}fgh""" // error // error // error
98
| ^^^
109
| xyz
11-
| This location contains code that was inlined from Test_2.scala:4
1210
-- Error: tests/neg-macros/i6432b/Test_2.scala:4:30 --------------------------------------------------------------------
1311
4 | foo"""abc${"123"}xyz${"456"}fgh""" // error // error // error
1412
| ^^^
1513
| fgh
16-
| This location contains code that was inlined from Test_2.scala:4

tests/neg-macros/i6976.check

+3-1
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,6 @@
66
| scala.MatchError: Inlined(EmptyTree,List(),Literal(Constant(2))) (of class dotty.tools.dotc.ast.Trees$Inlined)
77
| at playground.macros$.mcrImpl(Macro_1.scala:10)
88
|
9-
| This location contains code that was inlined from Test_2.scala:5
9+
This location contains code that was inlined from Macro_1.scala:6
10+
6 | inline def mcr(x: => Any) = ${mcrImpl('x)}
11+
| ^^^^^^^^^^^^^^

tests/neg-macros/i9014.check

-1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,3 @@
33
1 |val tests = summon[Bar] // error
44
| ^
55
| Failed to expand!
6-
| This location contains code that was inlined from Test_2.scala:1

tests/neg-macros/ill-abort.check

+3-1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,6 @@
33
1 |def test = fail() // error
44
| ^^^^^^
55
|Macro expansion was aborted by the macro without any errors reported. Macros should issue errors to end-users to facilitate debugging when aborting a macro expansion.
6-
| This location contains code that was inlined from quoted_1.scala:3
6+
This location contains code that was inlined from quoted_1.scala:3
7+
3 |inline def fail(): Unit = ${ impl }
8+
| ^^^^^^^^^

tests/neg-macros/macro-class-not-found-1.check

+3-1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,6 @@
55
| java.lang.NoClassDefFoundError
66
| at Foo$.aMacroImplementation(Foo.scala:8)
77
|
8-
| This location contains code that was inlined from Bar.scala:4
8+
This location contains code that was inlined from Foo.scala:5
9+
5 | inline def myMacro(): Unit = ${ aMacroImplementation }
10+
| ^^^^^^^^^^^^^^^^^^^^^^^^^

tests/neg-macros/macro-class-not-found-2.check

+3-1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,6 @@
55
| java.lang.NoClassDefFoundError: this.is.not.a.Class
66
| at Foo$.aMacroImplementation(Foo.scala:8)
77
|
8-
| This location contains code that was inlined from Bar.scala:4
8+
This location contains code that was inlined from Foo.scala:5
9+
5 | inline def myMacro(): Unit = ${ aMacroImplementation }
10+
| ^^^^^^^^^^^^^^^^^^^^^^^^^

tests/neg-macros/macros-in-same-project-6.check

-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,3 @@
22
4 | Foo.myMacro() // error
33
| ^^^^^^^^^^^^^
44
| some error
5-
| This location contains code that was inlined from Bar.scala:4

tests/neg/cannot-reduce-inline-match.check

+5-1
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,8 @@
44
| cannot reduce inline match with
55
| scrutinee: "f" : ("f" : String)
66
| patterns : case _:Int
7-
| This location contains code that was inlined from cannot-reduce-inline-match.scala:3
7+
This location contains code that was inlined from cannot-reduce-inline-match.scala:3
8+
3 | inline x match {
9+
| ^
10+
4 | case _: Int =>
11+
5 | }

tests/neg/i11225.check

+3-1
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,6 @@
4242
30 | var x7: Int = uni // error
4343
| ^^^
4444
| `uninitialized` can only be used as the right hand side of a mutable field definition
45-
| This location contains code that was inlined from i11225.scala:25
45+
This location contains code that was inlined from i11225.scala:25
46+
25 | transparent inline def uni = uninitialized
47+
| ^^^^^^^^^^^^^

tests/neg/i13044.check

+96-32
Original file line numberDiff line numberDiff line change
@@ -4,41 +4,105 @@
44
| given instance gen is declared as `inline`, but was not inlined
55
|
66
| Try increasing `-Xmax-inlines` above 32
7-
| This location contains code that was inlined from i13044.scala:17
8-
| This location contains code that was inlined from i13044.scala:31
9-
| This location contains code that was inlined from i13044.scala:37
10-
| This location contains code that was inlined from i13044.scala:17
11-
| This location contains code that was inlined from i13044.scala:31
12-
| This location contains code that was inlined from i13044.scala:37
13-
| This location contains code that was inlined from i13044.scala:17
14-
| This location contains code that was inlined from i13044.scala:31
15-
| This location contains code that was inlined from i13044.scala:37
16-
| This location contains code that was inlined from i13044.scala:17
17-
| This location contains code that was inlined from i13044.scala:31
18-
| This location contains code that was inlined from i13044.scala:37
19-
| This location contains code that was inlined from i13044.scala:17
20-
| This location contains code that was inlined from i13044.scala:18
21-
| This location contains code that was inlined from i13044.scala:31
22-
| This location contains code that was inlined from i13044.scala:37
7+
This location contains code that was inlined from i13044.scala:17
8+
17 | val builder = summonInline[Schema[t]].asInstanceOf[Schema[Any]]
9+
| ^
10+
This location contains code that was inlined from i13044.scala:17
11+
31 | lazy val fields = recurse[m.MirroredElemTypes]
12+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
13+
This location contains code that was inlined from i13044.scala:17
14+
37 | inline given gen[A]: Schema[A] = derived
15+
| ^^^^^^^
16+
This location contains code that was inlined from i13044.scala:17
17+
17 | val builder = summonInline[Schema[t]].asInstanceOf[Schema[Any]]
18+
| ^
19+
This location contains code that was inlined from i13044.scala:17
20+
31 | lazy val fields = recurse[m.MirroredElemTypes]
21+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
22+
This location contains code that was inlined from i13044.scala:17
23+
37 | inline given gen[A]: Schema[A] = derived
24+
| ^^^^^^^
25+
This location contains code that was inlined from i13044.scala:17
26+
17 | val builder = summonInline[Schema[t]].asInstanceOf[Schema[Any]]
27+
| ^
28+
This location contains code that was inlined from i13044.scala:17
29+
31 | lazy val fields = recurse[m.MirroredElemTypes]
30+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
31+
This location contains code that was inlined from i13044.scala:17
32+
37 | inline given gen[A]: Schema[A] = derived
33+
| ^^^^^^^
34+
This location contains code that was inlined from i13044.scala:17
35+
17 | val builder = summonInline[Schema[t]].asInstanceOf[Schema[Any]]
36+
| ^
37+
This location contains code that was inlined from i13044.scala:17
38+
31 | lazy val fields = recurse[m.MirroredElemTypes]
39+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
40+
This location contains code that was inlined from i13044.scala:17
41+
37 | inline given gen[A]: Schema[A] = derived
42+
| ^^^^^^^
43+
This location contains code that was inlined from i13044.scala:17
44+
17 | val builder = summonInline[Schema[t]].asInstanceOf[Schema[Any]]
45+
| ^
46+
This location contains code that was inlined from i13044.scala:17
47+
18 | builder :: recurse[ts]
48+
| ^^^^^^^^^^^
49+
This location contains code that was inlined from i13044.scala:17
50+
31 | lazy val fields = recurse[m.MirroredElemTypes]
51+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
52+
This location contains code that was inlined from i13044.scala:17
53+
37 | inline given gen[A]: Schema[A] = derived
54+
| ^^^^^^^
2355
-- Error: tests/neg/i13044.scala:50:40 ---------------------------------------------------------------------------------
2456
50 | implicit def typeSchema: Schema[A] = Schema.gen // error // error
2557
| ^^^^^^^^^^
2658
| method recurse is declared as `inline`, but was not inlined
2759
|
2860
| Try increasing `-Xmax-inlines` above 32
29-
| This location contains code that was inlined from i13044.scala:18
30-
| This location contains code that was inlined from i13044.scala:31
31-
| This location contains code that was inlined from i13044.scala:37
32-
| This location contains code that was inlined from i13044.scala:17
33-
| This location contains code that was inlined from i13044.scala:31
34-
| This location contains code that was inlined from i13044.scala:37
35-
| This location contains code that was inlined from i13044.scala:17
36-
| This location contains code that was inlined from i13044.scala:31
37-
| This location contains code that was inlined from i13044.scala:37
38-
| This location contains code that was inlined from i13044.scala:17
39-
| This location contains code that was inlined from i13044.scala:31
40-
| This location contains code that was inlined from i13044.scala:37
41-
| This location contains code that was inlined from i13044.scala:17
42-
| This location contains code that was inlined from i13044.scala:18
43-
| This location contains code that was inlined from i13044.scala:31
44-
| This location contains code that was inlined from i13044.scala:37
61+
This location contains code that was inlined from i13044.scala:18
62+
18 | builder :: recurse[ts]
63+
| ^^^^^^^
64+
This location contains code that was inlined from i13044.scala:18
65+
31 | lazy val fields = recurse[m.MirroredElemTypes]
66+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
67+
This location contains code that was inlined from i13044.scala:18
68+
37 | inline given gen[A]: Schema[A] = derived
69+
| ^^^^^^^
70+
This location contains code that was inlined from i13044.scala:18
71+
17 | val builder = summonInline[Schema[t]].asInstanceOf[Schema[Any]]
72+
| ^
73+
This location contains code that was inlined from i13044.scala:18
74+
31 | lazy val fields = recurse[m.MirroredElemTypes]
75+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
76+
This location contains code that was inlined from i13044.scala:18
77+
37 | inline given gen[A]: Schema[A] = derived
78+
| ^^^^^^^
79+
This location contains code that was inlined from i13044.scala:18
80+
17 | val builder = summonInline[Schema[t]].asInstanceOf[Schema[Any]]
81+
| ^
82+
This location contains code that was inlined from i13044.scala:18
83+
31 | lazy val fields = recurse[m.MirroredElemTypes]
84+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
85+
This location contains code that was inlined from i13044.scala:18
86+
37 | inline given gen[A]: Schema[A] = derived
87+
| ^^^^^^^
88+
This location contains code that was inlined from i13044.scala:18
89+
17 | val builder = summonInline[Schema[t]].asInstanceOf[Schema[Any]]
90+
| ^
91+
This location contains code that was inlined from i13044.scala:18
92+
31 | lazy val fields = recurse[m.MirroredElemTypes]
93+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
94+
This location contains code that was inlined from i13044.scala:18
95+
37 | inline given gen[A]: Schema[A] = derived
96+
| ^^^^^^^
97+
This location contains code that was inlined from i13044.scala:18
98+
17 | val builder = summonInline[Schema[t]].asInstanceOf[Schema[Any]]
99+
| ^
100+
This location contains code that was inlined from i13044.scala:18
101+
18 | builder :: recurse[ts]
102+
| ^^^^^^^^^^^
103+
This location contains code that was inlined from i13044.scala:18
104+
31 | lazy val fields = recurse[m.MirroredElemTypes]
105+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
106+
This location contains code that was inlined from i13044.scala:18
107+
37 | inline given gen[A]: Schema[A] = derived
108+
| ^^^^^^^

tests/neg/i13570.check

+6-1
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,9 @@
66
| patterns : case s @ _:Seq[Int] if s.isEmpty
77
| case s @ _:Seq[Int]
88
| case _
9-
| This location contains code that was inlined from i13570.scala:3
9+
This location contains code that was inlined from i13570.scala:3
10+
3 | inline seq match
11+
| ^
12+
4 | case s: Seq[Int] if s.isEmpty => println("seq is empty")
13+
5 | case s: Seq[Int] => println("seq is not empty")
14+
6 | case _ => println("somthing hinky happened")

tests/neg/i13991.check

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
-- Error: tests/neg/i13991.scala:8:15 ----------------------------------------------------------------------------------
2+
8 |def foo = first[String] // error
3+
| ^^^^^^^^^^^^^
4+
| no implicit argument of type Foo[String] was found
5+
This location contains code that was inlined from i13991.scala:4
6+
4 |inline def second[A]: Int = compiletime.summonInline[Foo[A]].foo
7+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
8+
This location contains code that was inlined from i13991.scala:4
9+
6 |inline def first[A]: Int = second[A] + 42
10+
| ^^^^^^^^^

tests/neg/i13991.scala

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
trait Foo[X]:
2+
def foo: Int
3+
4+
inline def second[A]: Int = compiletime.summonInline[Foo[A]].foo
5+
6+
inline def first[A]: Int = second[A] + 42
7+
8+
def foo = first[String] // error

tests/neg/inline-error-pos.check

+4-1
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,7 @@
44
| cannot reduce inline match with
55
| scrutinee: 2 : (2 : Int)
66
| patterns : case 1
7-
| This location contains code that was inlined from inline-error-pos.scala:3
7+
This location contains code that was inlined from inline-error-pos.scala:3
8+
3 | inline x match
9+
| ^
10+
4 | case 1 => 9

0 commit comments

Comments
 (0)