1
1
package dotty .tools .debug
2
2
3
3
import com .sun .jdi .Location
4
- import dotty .tools .io .JFile
4
+ import dotty .tools .io .JPath
5
5
import dotty .tools .readLines
6
6
7
+ import scala .annotation .tailrec
8
+
7
9
/**
8
10
* A debug step and an associated assertion to validate the step.
9
11
* A sequence of DebugStepAssert is parsed from the check file in tests/debug
10
12
*/
11
- private [debug] case class DebugStepAssert [T ](step : DebugStep [T ], assert : T => Unit )
13
+ private [debug] case class DebugStepAssert [T ](step : DebugStep [T ], assertion : T => Unit )(
14
+ using val location : CheckFileLocation
15
+ )
16
+
17
+ /** A location in the check file */
18
+ private [debug] case class CheckFileLocation (checkFile : JPath , line : Int ):
19
+ override def toString : String = s " $checkFile: $line"
20
+
21
+ /** When a DebugStepAssert fails it throws a DebugStepException */
22
+ private [debug] case class DebugStepException (message : String , location : CheckFileLocation ) extends Exception
23
+
24
+ private [debug] enum DebugStep [T ]:
25
+ case Break (className : String , line : Int ) extends DebugStep [Location ]
26
+ case Step extends DebugStep [Location ]
27
+ case Next extends DebugStep [Location ]
28
+ case Eval (expression : String ) extends DebugStep [Either [String , String ]]
12
29
13
30
private [debug] object DebugStepAssert :
31
+ private val sym = " [a-zA-Z0-9$.]+"
32
+ private val line = raw " \d+ "
33
+ private val trailing = raw " *(?://.*)? " .r // empty or comment
34
+ private val break = s " break ( $sym) ( $line) $trailing" .r
35
+ private val step = s " step ( $sym| $line) $trailing" .r
36
+ private val next = s " next ( $sym| $line) $trailing" .r
37
+ private val multiLineEval = s " eval $trailing" .r
38
+ private val eval = s " eval (.*) " .r
39
+ private val result = " result (.*)" .r
40
+ private val error = " error (.*)" .r
41
+ private val multiLineError = s " error $trailing" .r
42
+
14
43
import DebugStep .*
15
- def parseCheckFile (checkFile : JFile ): Seq [DebugStepAssert [? ]] =
16
- val sym = " [a-zA-Z0-9$.]+"
17
- val line = " \\ d+"
18
- val trailing = s " \\ s*(?: \\ / \\ /.*)? " .r // empty or comment
19
- val break = s " break ( $sym) ( $line) $trailing" .r
20
- val step = s " step ( $sym| $line) $trailing" .r
21
- val next = s " next ( $sym| $line) $trailing" .r
22
- val multiLineEval = s " eval $trailing" .r
23
- val eval = s " eval (.*) " .r
24
- val result = " result (.*)" .r
25
- val error = " error (.*)" .r
26
- val multiLineError = s " error $trailing" .r
44
+ def parseCheckFile (checkFile : JPath ): Seq [DebugStepAssert [? ]] =
45
+ val allLines = readLines(checkFile.toFile)
27
46
47
+ @ tailrec
28
48
def loop (lines : List [String ], acc : List [DebugStepAssert [? ]]): List [DebugStepAssert [? ]] =
49
+ given location : CheckFileLocation = CheckFileLocation (checkFile, allLines.size - lines.size + 1 )
29
50
lines match
30
51
case Nil => acc.reverse
31
52
case break(className , lineStr) :: tail =>
32
- val line = lineStr.toInt
33
- val step = DebugStepAssert (Break (className, line ), checkClassAndLine(className, line ))
53
+ val breakpointLine = lineStr.toInt
54
+ val step = DebugStepAssert (Break (className, breakpointLine ), checkClassAndLine(className, breakpointLine ))
34
55
loop(tail, step :: acc)
35
56
case step(pattern) :: tail =>
36
57
val step = DebugStepAssert (Step , checkLineOrMethod(pattern))
@@ -49,70 +70,70 @@ private[debug] object DebugStepAssert:
49
70
val step = DebugStepAssert (Eval (expr), assertion)
50
71
loop(tail2, step :: acc)
51
72
case trailing() :: tail => loop(tail, acc)
52
- case invalid :: tail => throw new Exception (s " Cannot parse debug step: $invalid" )
73
+ case invalid :: tail =>
74
+ throw new Exception (s " Cannot parse debug step: $invalid ( $location) " )
53
75
54
76
def parseEvalAssertion (lines : List [String ]): (Either [String , String ] => Unit , List [String ]) =
77
+ given location : CheckFileLocation = CheckFileLocation (checkFile, allLines.size - lines.size + 1 )
55
78
lines match
56
79
case Nil => throw new Exception (s " Missing result or error " )
80
+ case trailing() :: tail => parseEvalAssertion(tail)
57
81
case result(expected) :: tail => (checkResult(expected), tail)
58
82
case error(expected) :: tail => (checkError(Seq (expected)), tail)
59
83
case multiLineError() :: tail0 =>
60
84
val (expected, tail1) = tail0.span(_.startsWith(" " ))
61
85
(checkError(expected.map(_.stripPrefix(" " ))), tail1)
62
- case invalid :: _ => throw new Exception (s " Cannot parse as result or error: $invalid" )
86
+ case invalid :: _ =>
87
+ throw new Exception (s " Cannot parse as result or error: $invalid ( $location) " )
63
88
64
- loop(readLines(checkFile) , Nil )
89
+ loop(allLines , Nil )
65
90
end parseCheckFile
66
91
67
- private def checkClassAndLine (className : String , line : Int )(location : Location ): Unit =
68
- assert(className == location.declaringType.name, s " obtained ${location.declaringType.name} , expected ${ className} " )
69
- checkLine(line )(location)
92
+ private def checkClassAndLine (className : String , breakpointLine : Int )( using CheckFileLocation )(location : Location ): Unit =
93
+ debugStepAssertEquals( location.declaringType.name, className)
94
+ checkLine(breakpointLine )(location)
70
95
71
- private def checkLineOrMethod (pattern : String ): Location => Unit =
72
- if " ( \\ d+) " .r.matches(pattern) then checkLine(pattern.toInt) else checkMethod(pattern)
96
+ private def checkLineOrMethod (pattern : String )( using CheckFileLocation ) : Location => Unit =
97
+ pattern.toIntOption.map( checkLine).getOrElse( checkMethod(pattern) )
73
98
74
- private def checkLine (line : Int )(location : Location ): Unit =
75
- assert (location.lineNumber == line, s " obtained ${location.lineNumber} , expected $line " )
99
+ private def checkLine (expected : Int )( using CheckFileLocation )(location : Location ): Unit =
100
+ debugStepAssertEquals (location.lineNumber, expected)
76
101
77
- private def checkMethod (methodName : String )(location : Location ): Unit = assert(methodName == location.method.name)
102
+ private def checkMethod (expected : String )(using CheckFileLocation )(location : Location ): Unit =
103
+ debugStepAssertEquals(location.method.name, expected)
78
104
79
- private def checkResult (expected : String )(obtained : Either [String , String ]): Unit =
105
+ private def checkResult (expected : String )(using CheckFileLocation )( obtained : Either [String , String ]): Unit =
80
106
obtained match
81
107
case Left (obtained) =>
82
- val message =
108
+ debugStepFailed(
83
109
s """ |Evaluation failed:
84
110
| ${obtained.replace(" \n " , " \n |" )}""" .stripMargin
85
- throw new AssertionError (message)
86
- case Right (obtained) =>
87
- val message =
88
- s """ |Expected: $expected
89
- |Obtained: $obtained""" .stripMargin
90
- assert(expected.r.matches(obtained.toString), message)
111
+ )
112
+ case Right (obtained) => debugStepAssertEquals(obtained, expected)
91
113
92
- private def checkError (expected : Seq [String ])(obtained : Either [String , String ]): Unit =
114
+ private def checkError (expected : Seq [String ])(using CheckFileLocation )( obtained : Either [String , String ]): Unit =
93
115
obtained match
94
116
case Left (obtained) =>
95
- val message =
117
+ debugStepAssert(
118
+ expected.forall(e => e.r.findFirstMatchIn(obtained).isDefined),
96
119
s """ |Expected:
97
- | ${expected.mkString(" \n " )}
120
+ | ${expected.mkString(" \n | " )}
98
121
|Obtained:
99
122
| ${obtained.replace(" \n " , " \n |" )}""" .stripMargin
100
- assert(expected.forall(e => e.r.findFirstMatchIn(obtained).isDefined), message )
123
+ )
101
124
case Right (obtained) =>
102
- val message =
125
+ debugStepFailed(
103
126
s """ |Evaluation succeeded but failure expected.
104
127
|Obtained: $obtained
105
128
| """ .stripMargin
106
- throw new AssertionError (message)
107
-
108
-
109
- end DebugStepAssert
110
-
111
- private [debug] enum DebugStep [T ]:
112
- case Break (className : String , line : Int ) extends DebugStep [Location ]
113
- case Step extends DebugStep [Location ]
114
- case Next extends DebugStep [Location ]
115
- case Eval (expression : String ) extends DebugStep [Either [String , String ]]
129
+ )
116
130
131
+ private def debugStepAssertEquals [T ](obtained : T , expected : T )(using CheckFileLocation ): Unit =
132
+ debugStepAssert(obtained == expected, s " Obtained $obtained, Expected: $expected" )
117
133
134
+ private def debugStepAssert (assertion : Boolean , message : String )(using CheckFileLocation ): Unit =
135
+ if ! assertion then debugStepFailed(message)
118
136
137
+ private def debugStepFailed (message : String )(using location : CheckFileLocation ): Unit =
138
+ throw DebugStepException (message, location)
139
+ end DebugStepAssert
0 commit comments