11package dotty .tools .debug
22
33import com .sun .jdi .Location
4- import dotty .tools .io .JFile
4+ import dotty .tools .io .JPath
55import dotty .tools .readLines
66
7+ import scala .annotation .tailrec
8+
79/**
810 * A debug step and an associated assertion to validate the step.
911 * A sequence of DebugStepAssert is parsed from the check file in tests/debug
1012 */
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 ]]
1229
1330private [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+
1443 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)
2746
47+ @ tailrec
2848 def loop (lines : List [String ], acc : List [DebugStepAssert [? ]]): List [DebugStepAssert [? ]] =
49+ given location : CheckFileLocation = CheckFileLocation (checkFile, allLines.size - lines.size + 1 )
2950 lines match
3051 case Nil => acc.reverse
3152 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 ))
3455 loop(tail, step :: acc)
3556 case step(pattern) :: tail =>
3657 val step = DebugStepAssert (Step , checkLineOrMethod(pattern))
@@ -49,70 +70,70 @@ private[debug] object DebugStepAssert:
4970 val step = DebugStepAssert (Eval (expr), assertion)
5071 loop(tail2, step :: acc)
5172 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) " )
5375
5476 def parseEvalAssertion (lines : List [String ]): (Either [String , String ] => Unit , List [String ]) =
77+ given location : CheckFileLocation = CheckFileLocation (checkFile, allLines.size - lines.size + 1 )
5578 lines match
5679 case Nil => throw new Exception (s " Missing result or error " )
80+ case trailing() :: tail => parseEvalAssertion(tail)
5781 case result(expected) :: tail => (checkResult(expected), tail)
5882 case error(expected) :: tail => (checkError(Seq (expected)), tail)
5983 case multiLineError() :: tail0 =>
6084 val (expected, tail1) = tail0.span(_.startsWith(" " ))
6185 (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) " )
6388
64- loop(readLines(checkFile) , Nil )
89+ loop(allLines , Nil )
6590 end parseCheckFile
6691
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)
7095
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) )
7398
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)
76101
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)
78104
79- private def checkResult (expected : String )(obtained : Either [String , String ]): Unit =
105+ private def checkResult (expected : String )(using CheckFileLocation )( obtained : Either [String , String ]): Unit =
80106 obtained match
81107 case Left (obtained) =>
82- val message =
108+ debugStepFailed(
83109 s """ |Evaluation failed:
84110 | ${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)
91113
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 =
93115 obtained match
94116 case Left (obtained) =>
95- val message =
117+ debugStepAssert(
118+ expected.forall(e => e.r.findFirstMatchIn(obtained).isDefined),
96119 s """ |Expected:
97- | ${expected.mkString(" \n " )}
120+ | ${expected.mkString(" \n | " )}
98121 |Obtained:
99122 | ${obtained.replace(" \n " , " \n |" )}""" .stripMargin
100- assert(expected.forall(e => e.r.findFirstMatchIn(obtained).isDefined), message )
123+ )
101124 case Right (obtained) =>
102- val message =
125+ debugStepFailed(
103126 s """ |Evaluation succeeded but failure expected.
104127 |Obtained: $obtained
105128 | """ .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+ )
116130
131+ private def debugStepAssertEquals [T ](obtained : T , expected : T )(using CheckFileLocation ): Unit =
132+ debugStepAssert(obtained == expected, s " Obtained $obtained, Expected: $expected" )
117133
134+ private def debugStepAssert (assertion : Boolean , message : String )(using CheckFileLocation ): Unit =
135+ if ! assertion then debugStepFailed(message)
118136
137+ private def debugStepFailed (message : String )(using location : CheckFileLocation ): Unit =
138+ throw DebugStepException (message, location)
139+ end DebugStepAssert
0 commit comments