fixes #22950; Poor error message on cast effect violation (#25839)

fixes #22950

This pull request improves the tracking and reporting of effect
annotations (such as `raises`, `tags`, and `forbids`) in pragma blocks,
particularly when using the `cast` pragma. It ensures that the source of
these effect annotations is correctly preserved and referenced, which
improves error reporting and effect analysis. Additionally, a new test
was added to check for violations when using `cast` with effect
annotations.

Effect annotation source tracking and propagation:

* Added new fields (`excSource`, `tagsSource`, `forbidsSource`) to the
`PragmaBlockContext` type to store the original source node for each
effect annotation.
* Updated `castBlock` to set these new source fields when processing
`raises`, `tags`, and `forbids` pragmas, ensuring the source node is
preserved for later error reporting.
* Modified `unapplyBlockContext` to use the stored source node (if
available) when calling `addRaiseEffect`, `addTag`, and `addNotTag`,
improving the accuracy of effect tracking and diagnostics.

Pragma handling improvements:

* Changed the call to `castBlock` in the main pragma processing loop to
pass the entire pragma node, enabling access to the original source for
effect annotations.

Testing:

* Added a new test (`tests/effects/tcast_effect_violation.nim`) to
verify that using `cast(raises: ValueError)` inside a procedure with
`.raises: [].` correctly triggers an error message about an unlisted
exception.
This commit is contained in:
ringabout
2026-05-28 05:28:27 +08:00
committed by GitHub
parent 8771451701
commit cfa769fefc
2 changed files with 18 additions and 5 deletions

View File

@@ -1208,6 +1208,7 @@ type
enforcedGcSafety, enforceNoSideEffects: bool
oldExc, oldTags, oldForbids: int
exc, tags, forbids: PNode
excSource, tagsSource, forbidsSource: PNode
proc createBlockContext(tracked: PEffects): PragmaBlockContext =
var oldForbidsLen = 0
@@ -1230,17 +1231,18 @@ proc unapplyBlockContext(tracked: PEffects; bc: PragmaBlockContext) =
# anything about 'raises' in the 'cast' at all. Same applies for 'tags'.
setLen(tracked.exc.sons, bc.oldExc)
for e in bc.exc:
addRaiseEffect(tracked, e, e)
addRaiseEffect(tracked, e, if bc.excSource != nil: bc.excSource else: e)
if bc.tags != nil:
setLen(tracked.tags.sons, bc.oldTags)
for t in bc.tags:
addTag(tracked, t, t)
addTag(tracked, t, if bc.tagsSource != nil: bc.tagsSource else: t)
if bc.forbids != nil:
setLen(tracked.forbids.sons, bc.oldForbids)
for t in bc.forbids:
addNotTag(tracked, t, t)
addNotTag(tracked, t, if bc.forbidsSource != nil: bc.forbidsSource else: t)
proc castBlock(tracked: PEffects, pragma: PNode, bc: var PragmaBlockContext) =
proc castBlock(tracked: PEffects, castPragma: PNode, bc: var PragmaBlockContext) =
let pragma = castPragma[1]
case whichPragma(pragma)
of wGcSafe:
bc.enforcedGcSafety = true
@@ -1253,6 +1255,7 @@ proc castBlock(tracked: PEffects, pragma: PNode, bc: var PragmaBlockContext) =
else:
bc.tags = newNodeI(nkArgList, pragma.info)
bc.tags.add n
bc.tagsSource = castPragma
of wForbids:
let n = pragma[1]
if n.kind in {nkCurly, nkBracket}:
@@ -1260,6 +1263,7 @@ proc castBlock(tracked: PEffects, pragma: PNode, bc: var PragmaBlockContext) =
else:
bc.forbids = newNodeI(nkArgList, pragma.info)
bc.forbids.add n
bc.forbidsSource = castPragma
of wRaises:
let n = pragma[1]
if n.kind in {nkCurly, nkBracket}:
@@ -1267,6 +1271,7 @@ proc castBlock(tracked: PEffects, pragma: PNode, bc: var PragmaBlockContext) =
else:
bc.exc = newNodeI(nkArgList, pragma.info)
bc.exc.add n
bc.excSource = castPragma
of wUncheckedAssign:
discard "handled in sempass1"
else:
@@ -1520,7 +1525,7 @@ proc track(tracked: PEffects, n: PNode) =
of wNoSideEffect:
bc.enforceNoSideEffects = true
of wCast:
castBlock(tracked, pragmaList[i][1], bc)
castBlock(tracked, pragmaList[i], bc)
else:
discard
applyBlockContext(tracked, bc)

View File

@@ -0,0 +1,8 @@
discard """
errormsg: "cast(raises: ValueError) can raise an unlisted exception: ValueError"
line: 7
"""
proc fff() {.raises: [].} =
{.cast(raises: ValueError).}:
discard