From 96d8e13c5ae71820e469657967f6bdfac359ce6b Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Mon, 28 Sep 2026 16:48:58 +0800 Subject: [PATCH] =?UTF-8?q?fix(err):=20=E6=88=90=E5=91=98=E6=9F=A5?= =?UTF-8?q?=E6=89=BE=20fail-fast=20=E6=94=B9=E7=94=A8=E5=8F=AF=E8=AF=BB?= =?UTF-8?q?=E5=BC=82=E5=B8=B8=20+=20=E6=8A=A5=E9=94=99=E8=AF=AD=E5=8F=A5?= =?UTF-8?q?=E6=A0=88=E4=B8=8D=E5=86=8D=E9=87=8D=E5=A4=8D=E9=95=BF=E6=8A=A5?= =?UTF-8?q?=E6=96=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ① 新增 MemberLookupFailFast(KeyError):_get_member_offset 的 fail-fast 原用 raise KeyError(消息),而 KeyError.__str__ = repr(消息) ⇒ 报文被加上引号、换行被转义成字面 \n(含"源代码 (文件, 行号): 行号 | 源码"的多行报文彻底不可读)。新类继承 KeyError(保住 HandlesExprAttr 两处 except KeyError 兜底点)但 __str__ 返回裸消息。 ② 顺带删除该处的 traceback.print_stack 调试转储(遗留调试语句,输出噪音)。 ③ Phase2Translator 语句栈渲染:原格式把同一条异常消息贴在每一层帧后面 ⇒ 一句话被打印 N 次;改为外层→内层的调用路径逐帧印位置、异常消息只在最内层印一次。 --- lib/Projectrans/Phase2Translator.py | 15 +++++++++++---- lib/core/LLVMCG/FuncGen.py | 28 ++++++++++++++++++++++------ 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/lib/Projectrans/Phase2Translator.py b/lib/Projectrans/Phase2Translator.py index d37723f..69ac04c 100644 --- a/lib/Projectrans/Phase2Translator.py +++ b/lib/Projectrans/Phase2Translator.py @@ -2055,11 +2055,18 @@ class Phase2Translator: error_stack = getattr(trans.translator, '_ErrorStack', []) if error_stack: self._last_ErrorStack = error_stack - chain_lines = [] - for entry in reversed(error_stack): - if len(entry) >= 3: - exc_msg, line_info = entry[1], entry[2] + # 语句栈渲染:error_stack 按"由内向外"入栈(内层语句先 append), + # 这里反转为"外层 → 内层"的调用路径;**异常消息只在最内层印一次**, + # 其余帧只印位置——否则同一段长 fail-fast 报文会被每个帧重复一遍 + # (旧格式把消息贴在每一帧后面,输出里同一句话出现 N 次,不可读)。 + entries: list = [entry for entry in error_stack if len(entry) >= 3] + chain_lines: list[str] = [] + for chain_idx, entry in enumerate(reversed(entries)): + exc_msg, line_info = entry[1], entry[2] + if chain_idx == len(entries) - 1: chain_lines.append(" %s\n %s" % (line_info, exc_msg)) + else: + chain_lines.append(" %s" % (line_info,)) if chain_lines: enriched = str(e) + "\n" + "\n".join(chain_lines) raise type(e)(enriched) from e diff --git a/lib/core/LLVMCG/FuncGen.py b/lib/core/LLVMCG/FuncGen.py index 6ff5828..51fae57 100644 --- a/lib/core/LLVMCG/FuncGen.py +++ b/lib/core/LLVMCG/FuncGen.py @@ -19,6 +19,24 @@ class FailFastError(Exception): """ +class MemberLookupFailFast(KeyError): + """成员偏移查找失败的 fail-fast 异常(**继承 KeyError** 以保持既有兜底点可用)。 + + 为什么继承 KeyError:本仓有两处 `except KeyError` 把 `_get_member_offset` 的 + 失败当作"该字段类型未知"继续回查(HandlesExprAttr 的字段类型三级回退),改成 + 其它异常类型会把这些兜底点击穿。 + + 为什么重写 `__str__`:KeyError 的默认 `__str__` 是 `repr(消息)` —— 会在报文两端 + 加引号、并把换行/制表转义成字面 `\\n`,多行 fail-fast 报文(含"源代码 (文件, 行号): + 行号 | 源码")因此彻底不可读。这里原样返回裸消息,与 FailFastError 的观感一致。 + """ + + def __str__(self) -> str: + if self.args and isinstance(self.args[0], str): + return self.args[0] + return super().__str__() + + class FuncGenMixin: def setup_from_symbol_table(self, SymbolTable: Any) -> None: @@ -737,12 +755,10 @@ class FuncGenMixin: if isinstance(_st_diag, ir.IdentifiedStructType): _struct_diag = f"{len(_st_diag.elements)} elems" if _st_diag.elements is not None else 'opaque' _member_names_diag: list = [n for n, _ in _members_diag][:16] - try: - import traceback as _tb - _tb.print_stack(file=sys.stderr) - except Exception: - pass - raise KeyError( + # 异常类型:MemberLookupFailFast(KeyError 子类 + 裸消息 __str__)—— + # 既保留 `except KeyError` 兜底点,又让报文不再被 repr 成"带引号 + \n 转义" + # 的不可读形式(原有 traceback.print_stack 调试转储一并移除,保持输出干净)。 + raise MemberLookupFailFast( f"[fast-fail] 属性 '{field_name}' 在类 '{ClassName}' 中找不到" f"(class_members={len(_members_diag)} 个: {_member_names_diag}, struct={_struct_diag})" )