Here is my proposal for tagging messages with (the value of) an arbitrary expression at the time of message generation. The tag is placed inside the the message itself.
ClearAll[withTaggedMsg]
SetAttributes[withTaggedMsg, HoldAll]
withTaggedMsg[exp_, label_: "When"] := Function[,
Internal`InheritedBlock[{MessagePacket},
Unprotect @ MessagePacket;
mp : MessagePacket[__, _BoxData] /; !TrueQ[$tagMsg] :=
Block[{$tagMsg = True},
Style[Row[{label, HoldForm[exp], "=", exp, " "}, " "], "SBO"] /. tag_ :>
MapAt[RowBox[{ToBoxes @ tag, #}] &, mp, {-1, 1}] // Identity
];
#
],
HoldAll]
Usage:
Do[i^0, {i, -1, 1}] // withTaggedMsg[i]
Do[i^0, {i, -1, 1}] // withTaggedMsg[i, "At iteration"]
Note: this only works with variables that are either globally accessible or are scoped using Block
. For example,
f[x_] := Message[f::brains, x]
f[5] // withTaggedMsg[x]
(* At iteration x = x f::brains: -- Message text not found -- (5) *)
Module[{x = 5},
Message[f::brains, x]
] // withTaggedMsg[x]
(* At iteration x = x f::brains: -- Message text not found -- (5) *)
With[{x = 5},
Message[f::brains, x]
] // withTaggedMsg[x]
(* At iteration x = x f::brains: -- Message text not found -- (5) *)
Block[{x = 5},
Message[f::brains, x]
] // withTaggedMsg[x]
(* At iteration x = 5 f::brains: -- Message text not found -- (5) *)
This means that any variable that is scoped using Block
can be used to tag a message. So, loop variables from Do
and Table
are accessible via this method, in addition to any Block
variable. This makes it indispensable as a debugging tool.