Skip to content

Commit c4c51d7

Browse files
authored
unittest: show proper stack trace for 'check' (#25212)
1 parent 440b55a commit c4c51d7

File tree

1 file changed

+38
-7
lines changed

1 file changed

+38
-7
lines changed

lib/pure/unittest.nim

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -657,10 +657,6 @@ macro check*(conditions: untyped): untyped =
657657

658658
let checked = callsite()[1]
659659

660-
template asgn(a: untyped, value: typed) =
661-
var a = value # XXX: we need "var: var" here in order to
662-
# preserve the semantics of var params
663-
664660
template print(name: untyped, value: typed) =
665661
when compiles(string($value)):
666662
checkpoint(name & " was " & $value)
@@ -684,8 +680,16 @@ macro check*(conditions: untyped): untyped =
684680
if exp[i].kind in nnkCallKinds + {nnkDotExpr, nnkBracketExpr, nnkPar} and
685681
(exp[i].typeKind notin {ntyTypeDesc} or $exp[0] notin ["is", "isnot"]):
686682
let callVar = newIdentNode(":c" & $counter)
687-
result.assigns.add getAst(asgn(callVar, paramAst))
683+
# Construct AST directly instead of using getAst to preserve line info
684+
let asgnNode = newNimNode(nnkVarSection, exp[i])
685+
let identDef = newNimNode(nnkIdentDefs, exp[i])
686+
identDef.add callVar
687+
identDef.add newEmptyNode()
688+
identDef.add paramAst
689+
asgnNode.add identDef
690+
result.assigns.add asgnNode
688691
result.check[i] = callVar
692+
result.check[^1].setLineInfo exp.lineInfoObj
689693
result.printOuts.add getAst(print(argStr, callVar))
690694
if exp[i].kind == nnkExprEqExpr:
691695
# ExprEqExpr
@@ -694,8 +698,16 @@ macro check*(conditions: untyped): untyped =
694698
result.check[i] = exp[i][1]
695699
if exp[i].typeKind notin {ntyTypeDesc}:
696700
let arg = newIdentNode(":p" & $counter)
697-
result.assigns.add getAst(asgn(arg, paramAst))
701+
# Construct AST directly instead of using getAst to preserve line info
702+
let asgnNode = newNimNode(nnkVarSection, exp[i])
703+
let identDef = newNimNode(nnkIdentDefs, exp[i])
704+
identDef.add arg
705+
identDef.add newEmptyNode()
706+
identDef.add paramAst
707+
asgnNode.add identDef
708+
result.assigns.add asgnNode
698709
result.printOuts.add getAst(print(argStr, arg))
710+
result.printOuts[^1].setLineInfo exp.lineInfoObj
699711
if exp[i].kind != nnkExprEqExpr:
700712
result.check[i] = arg
701713
else:
@@ -707,9 +719,28 @@ macro check*(conditions: untyped): untyped =
707719
let (assigns, check, printOuts) = inspectArgs(checked)
708720
let lineinfo = newStrLitNode(checked.lineInfo)
709721
let callLit = checked.toStrLit
722+
723+
# Wrap assigns in a line pragma block to preserve stack trace location
724+
let pragmaBlock = newNimNode(nnkPragmaBlock)
725+
let pragma = newNimNode(nnkPragma)
726+
let exprColonExpr = newNimNode(nnkExprColonExpr)
727+
exprColonExpr.add newIdentNode("line")
728+
729+
# Create a tuple literal with (filename, line, column) from checked
730+
let lineInfoObj = checked.lineInfoObj
731+
let tupleLit = newNimNode(nnkTupleConstr)
732+
tupleLit.add newLit(lineInfoObj.filename)
733+
tupleLit.add newLit(lineInfoObj.line.int)
734+
tupleLit.add newLit(lineInfoObj.column.int)
735+
exprColonExpr.add tupleLit
736+
737+
pragma.add exprColonExpr
738+
pragmaBlock.add pragma
739+
pragmaBlock.add assigns
740+
710741
result = quote do:
711742
block:
712-
`assigns`
743+
`pragmaBlock`
713744
if `check`:
714745
discard
715746
else:

0 commit comments

Comments
 (0)