feat(viperc/diag): 报错定位到具体表达式 + 栈帧跟踪当前语句(方案 A+B)

A:报错节点是合成节点(lineno==0,如构造器内联出的参数赋值)时,优先用“正在翻译的实参
   位置”兜底 → 报错精确指向调用处的表达式(如 `&_on_submit`),不再退回函数定义行。
   - TypeGenBase 新增 CurArgFile/CurArgLine/CurArgCol 与 set_cur_arg()
   - CallGenLib 翻译实参前调用 set_cur_arg(当前文件, arg_node 位置)
   - TypeGenErr 兜底顺序改为:节点自身行号 → CurArg → 栈最内层帧
   - 语句边界清空 CurArg,避免兜底串味到上一条语句的实参

B:栈帧跟踪“当前语句”位置(Python traceback 口径)
   - TypeGenBase.update_top_trace_position():更新最内层帧的 lineno/col
   - translate_stmt 每条语句推进时调用 → 报错时栈帧给出的是调用点所在语句,而非函数定义行
   配合 A 即可得到“调用点 + 具体表达式”两帧。
This commit is contained in:
TermiNexus committed 2026-10-14 16:40:35 +08:00
1 parent 70c271d2a4
commit 0678cc8c48
4 files changed
+41 -2

No files matched your search

+3
View File
@@ -221,6 +221,9 @@ novirtual class CallGenLib(CallGenBase.CallGenBase):
if self.Gen.ExprGenBase._str_rejects_ptr_kind(cur) != 0:
if VpiReader._vpi_param_annot_seg_is(sha1_buf, cls_nm, meth_nm, idx, "str") != 0 or VpiReader._vpi_param_annot_seg_is(sha1_buf, cls_nm, meth_nm, idx, "bytes") != 0:
self.Gen.ExprGenBase.fail_if_str_annot_agg_ptr(cur, 1, arg_node)
# 记录当前实参位置:被调者内部报错若是合成节点(无行号),可据此定位到这个实参表达式
if arg_node is not None:
self.Gen.TypeGenBase.set_cur_arg(self.Gen.TypeGenResolve.get_current_file(), arg_node.lineno, arg_node.col_offset + 1)
node_v = self._coerce_arg_value_to_ty(pool, builder, node_v, tgt_ty, arg_node, trans, tpl_src)
if new_head is None: new_head = node_v
else: new_tail.Next = node_v
+6
View File
@@ -32,6 +32,12 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
# define). Every instruction then carries ", !dbg !N".
if node is not None and trans._cur_builder is not None:
DebugInfoGen.SetStatement(trans.Pool, trans._cur_builder, node.lineno, node.col_offset + 1)
# 让最内层栈帧跟踪“当前语句”位置:报错时栈帧给出的是调用点所在语句,而非函数定义行
#(Python traceback 口径;配合 TypeGenBase.set_cur_arg 可得到“调用点 + 具体表达式”两帧)。
if node is not None:
self.Gen.TypeGenBase.update_top_trace_position(node.lineno, node.col_offset + 1)
# 语句边界处清掉上一条语句遗留的实参位置,避免兜底定位“串味”到旧实参
self.Gen.TypeGenBase.set_cur_arg(None, 0, 0)
if trans.TranslateDepth >= HT.TRANSLATE_MAX_DEPTH: self.Gen.TypeGenErr.fatal_error(node, "statement translation nested too deeply (likely an AST self-reference cycle or a translation bug)", "RecursionError")
trans.TranslateDepth += 1
mret: int = self._translate_stmt_impl(trans, node)
+24
View File
@@ -28,6 +28,11 @@ novirtual class TypeGenBase:
Gen: Generator.Generator*
TraceFrames: GenTypes.TraceFrame* # malloc array (TypeGenMixin.TRACE_MAX slots)
TraceCount: int
# --- 当前实参位置:翻译调用实参时记录。报错节点是**合成**的(lineno==0,如构造器内联出的
# 参数赋值)时,用它兜底定位到具体表达式(如 `&_on_submit`),而不是回退到函数定义行。---
CurArgFile: str
CurArgLine: int
CurArgCol: int
# --- Global generic specialization context (was _g_generic_tp_names/_g_generic_type_args) ---
GenericTpNames: list[str]*
@@ -107,6 +112,9 @@ novirtual class TypeGenBase:
if self is None: return 1
self.TraceFrames = None
self.TraceCount = 0
self.CurArgFile = None
self.CurArgLine = 0
self.CurArgCol = 0
self.GenericTpNames = None
self.GenericTypeArgs = None
self.GenericCtxStack = None
@@ -199,6 +207,22 @@ novirtual class TypeGenBase:
frame.lineno = 0
frame.col_start = 0
frame.col_end = 0
# update_top_trace_position - 让最内层栈帧跟踪“当前语句”位置(Python traceback 口径:
# 帧行号随语句推进而更新)。这样报错时栈帧给出的是**调用点所在语句**,而不是函数定义行;
# 配合 set_cur_arg,即可得到“调用点 + 具体表达式”两帧。
def update_top_trace_position(self, lineno: int, col_start: int) -> None:
"""Track the current statement on the innermost trace frame."""
if self.TraceCount <= 0 or lineno <= 0: return
frame: GenTypes.TraceFrame* = self._get_trace_frame(self.TraceCount - 1)
if frame is None: return
frame.lineno = lineno
frame.col_start = col_start
# set_cur_arg - 记录正在翻译的实参位置(file/line/col),供“合成节点无行号”时兜底定位。
def set_cur_arg(self, file_name: str, lineno: int, col_start: int) -> None:
"""Remember the argument position currently being translated."""
self.CurArgFile = file_name
self.CurArgLine = lineno
self.CurArgCol = col_start
def clear_trace(self) -> None:
"""Clear the trace stack (called before translating each file)."""
for i in range(int(self.TraceCount)):
+8 -2
View File
@@ -49,8 +49,14 @@ novirtual class TypeGenErr(TypeGenBase.TypeGenBase):
if node is not None: lineno = node.lineno
file_name: str = self.Gen.TypeGenResolve.get_current_file()
if file_name is None: file_name = "<unknown>"
# Line-number fallback: cloned/synthesized nodes may have lineno 0 (e.g. ast.Arg built by
# generic specialization hardcodes 0). Fall back to the trace stack's last valid frame.
# 合成节点无行号时的兜底顺序:
# ① 正在翻译的实参位置(精确到 `&_on_submit` 这类表达式,见 TypeGenBase.set_cur_arg);
# ② 调用栈最内层有行号的帧(现已随语句推进更新,给出调用点语句)。
if lineno <= 0 and self.Gen.TypeGenBase.CurArgLine > 0:
if self.Gen.TypeGenBase.CurArgFile is not None:
file_name = self.Gen.TypeGenBase.CurArgFile
lineno = self.Gen.TypeGenBase.CurArgLine
col_start = self.Gen.TypeGenBase.CurArgCol
if lineno <= 0 and self.Gen.TypeGenBase.TraceCount > 0:
ti_b: int = self.Gen.TypeGenBase.TraceCount - 1
while ti_b >= 0: