feat(diag): 逃逸类 LifetimeError 追加 File lifetime leak 帧,标出泄漏点
StructGenMixin: 新增 escape_leak_line——错误路径重扫被调者函数体,取指定形参的**首个逃逸点**行号; _NoteLeak 在三处置位来源采集(字段/全局写、return 形参、传递闭包调用),新增 EscLeakWant/EscLeakLine。 Diag: DiagEntry 增加 LeakFileName/LeakLine + set_leak + emit_leak_frame(标签 File lifetime leak), 打印顺序 = 调用链 → 错误点 → 泄漏点 → 类别: 消息。 TypeGenErr.fatal_error / GenApi.fail_if_stack_addr_escape 增加 leak_file/leak_line 透传; CallGenMixin 调用点按失败实参位取行号。**仅同模块**:跨模块 EscFuncTable 无 AST → 返回 0,不打该帧。
This commit is contained in:
1 parent
c98730f717
commit
2353dca8ec
6 files changed
+112
-13
No files matched your search
@@ -54,6 +54,10 @@ class DiagEntry:
|
||||
FrameCount: int # 调用链帧数
|
||||
FrameFiles: sizet* # 调用链每帧文件名(深拷贝)
|
||||
FrameLines: sizet* # 调用链每帧行号(与 FrameFiles 同容量)
|
||||
# 泄漏点(可选):被调者那边**造成逃逸的写入点**。只有逃逸类 LifetimeError 会有值;
|
||||
# 打印成独立一帧 `File lifetime leak "<file>", line N`,与调用栈帧(`File "..."`)区分。
|
||||
LeakFileName: str
|
||||
LeakLine: int
|
||||
Cap: int # 内部:FrameFiles/FrameLines 容量(不参与打印)
|
||||
|
||||
|
||||
@@ -81,6 +85,8 @@ def new_entry(max_frames: int) -> DiagEntry*:
|
||||
ent.Msg = None
|
||||
ent.Hint = None
|
||||
ent.FrameCount = 0
|
||||
ent.LeakFileName = None
|
||||
ent.LeakLine = 0
|
||||
ent.Cap = max_frames
|
||||
ent.FrameFiles = (sizet*)(stdlib.malloc(sizet(max_frames) * 8))
|
||||
ent.FrameLines = (sizet*)(stdlib.malloc(sizet(max_frames) * 8))
|
||||
@@ -98,6 +104,8 @@ def begin(ent: DiagEntry*, category: str, msg: str, hint: str = None) -> int:
|
||||
ent.ColStart = 0
|
||||
ent.ColEnd = 0
|
||||
ent.FrameCount = 0
|
||||
ent.LeakFileName = None
|
||||
ent.LeakLine = 0
|
||||
return 0
|
||||
|
||||
|
||||
@@ -111,6 +119,14 @@ def set_current(ent: DiagEntry*, file_name: str, lineno: int, col_start: int, co
|
||||
return 0
|
||||
|
||||
|
||||
# set_leak - 设置"泄漏点"帧(File lifetime leak);仅逃逸类 LifetimeError 使用
|
||||
def set_leak(ent: DiagEntry*, file_name: str, lineno: int) -> int:
|
||||
if ent is None or lineno <= 0: return 0
|
||||
ent.LeakFileName = dup_str(file_name)
|
||||
ent.LeakLine = lineno
|
||||
return 0
|
||||
|
||||
|
||||
# add_frame - 追加调用链一帧(按"自外向内"顺序追加)
|
||||
def add_frame(ent: DiagEntry*, file_name: str, lineno: int) -> int:
|
||||
if ent is None or ent.FrameFiles is None or ent.FrameLines is None: return 0
|
||||
@@ -137,6 +153,8 @@ def emit(ent: DiagEntry*, direction: int) -> int:
|
||||
emit_frame(str(ent.FrameFiles[i_u]), int(ent.FrameLines[i_u]))
|
||||
i_u += 1
|
||||
emit_current(ent.FileName, ent.Line, ent.ColStart, ent.ColEnd)
|
||||
# 泄漏点帧紧跟在"错误点"之后(往下再一行),再印类别: 消息
|
||||
if ent.LeakLine > 0: emit_leak_frame(ent.LeakFileName, ent.LeakLine)
|
||||
emit_category(ent.Category, ent.Msg)
|
||||
return 0
|
||||
|
||||
@@ -145,6 +163,7 @@ def emit(ent: DiagEntry*, direction: int) -> int:
|
||||
def free_entry(ent: DiagEntry*) -> int:
|
||||
if ent is None: return 0
|
||||
if ent.FileName is not None: stdlib.free(ent.FileName)
|
||||
if ent.LeakFileName is not None: stdlib.free(ent.LeakFileName)
|
||||
if ent.Category is not None: stdlib.free(ent.Category)
|
||||
if ent.Msg is not None: stdlib.free(ent.Msg)
|
||||
if ent.Hint is not None: stdlib.free(ent.Hint)
|
||||
@@ -222,6 +241,19 @@ def emit_frame(file_name: str, lineno: int) -> int:
|
||||
return 0
|
||||
|
||||
|
||||
# emit_leak_frame - "泄漏点"帧:标签用 `File lifetime leak`,与调用栈帧(`File "..."`)区分
|
||||
def emit_leak_frame(file_name: str, lineno: int) -> int:
|
||||
if file_name is None: return 0
|
||||
if lineno > 0:
|
||||
stdio.printf(' File lifetime leak "%s", line %d\n', file_name, lineno)
|
||||
src: str = read_source_line(file_name, lineno)
|
||||
if src is not None:
|
||||
stdio.printf(" %s\n", src)
|
||||
stdlib.free(src)
|
||||
else: stdio.printf(' File lifetime leak "%s"\n', file_name)
|
||||
return 0
|
||||
|
||||
|
||||
# emit_current - 当前帧(带 caret 列范围;行读不出时印警告)
|
||||
def emit_current(file_name: str, lineno: int, col_start: int, col_end: int) -> int:
|
||||
# Sanity defense: read the line first to verify lineno belongs to the current file.
|
||||
|
||||
Reference in new issue
Block a user