fix: 修复建议质量打磨 (用户审查发现)

- W004: 修复代码里的「默认值」占位符 → ast.unparse 提取真实默认值 (def f(items=None): items = items if items is not None else [])
- E004 del 场景: 专门建议「删了别再用 / 去掉 del」而非笼统赋初值 (del_flag 通过 analyzer._flow_del_names 传递)
- 拼写纠错保持「你是不是想写」提示性质 (语义上不一定正确, 措辞已保守)
- 304 passed 零回归
This commit is contained in:
shuiyu67 committed 2026-08-07 09:14:40 +08:00
1 parent 1049370231
commit 260eb23cb1
1 file changed
+24 -5
+24 -5
View File
@@ -86,7 +86,8 @@ def _smart_explain(code: str, ctx: dict) -> Tuple[str, str]:
func = ctx.get("func", "") func = ctx.get("func", "")
arg = ctx.get("arg", "") arg = ctx.get("arg", "")
if func and arg: if func and arg:
fix = (f"修复: 改写成 def {func}({arg}=None): {arg} = {arg} if {arg} is not None else 默认值" default_text = ctx.get("default_text", "默认值")
fix = (f"修复: 改写成 def {func}({arg}=None): {arg} = {arg} if {arg} is not None else {default_text}"
f"——避免所有调用共享同一个可变对象。") f"——避免所有调用共享同一个可变对象。")
return title, fix return title, fix
@@ -138,8 +139,12 @@ def _smart_explain(code: str, ctx: dict) -> Tuple[str, str]:
elif code == "E004": elif code == "E004":
name = ctx.get("name", "") name = ctx.get("name", "")
if name: if name:
fix = (f"修复: {name} 在某条执行路径上可能没赋值就被使用(运行时 UnboundLocalError)。" if ctx.get("del_flag"):
f"建议在使用前先赋初值: {name} = None(或按逻辑给默认值),或确保所有分支都赋值。") fix = (f"修复: {name} 被 del 之后又使用(运行时 UnboundLocalError)。"
f"若不再需要该变量,删除 del 语句;若仍需使用,请去掉 del 或在使用后再删。")
else:
fix = (f"修复: {name} 在某条执行路径上可能没赋值就被使用(运行时 UnboundLocalError)。"
f"建议在使用前先赋初值: {name} = None(或按逻辑给默认值),或确保所有分支都赋值。")
return title, fix return title, fix
return title, base_fix return title, base_fix
@@ -331,9 +336,13 @@ class _ScopeAnalyzer(ast.NodeVisitor):
if n_defaults: if n_defaults:
for arg, d in zip(node.args.args[-n_defaults:], node.args.defaults): for arg, d in zip(node.args.args[-n_defaults:], node.args.defaults):
if isinstance(d, (ast.List, ast.Dict, ast.Set)): if isinstance(d, (ast.List, ast.Dict, ast.Set)):
try:
default_text = ast.unparse(d)
except Exception:
default_text = "默认值"
self._add("W004", node.lineno, node.col_offset, self._add("W004", node.lineno, node.col_offset,
extra=f"可变默认参数: {node.name}.{arg.arg}", extra=f"可变默认参数: {node.name}.{arg.arg}",
func=node.name, arg=arg.arg) func=node.name, arg=arg.arg, default_text=default_text)
for d in node.args.kw_defaults: for d in node.args.kw_defaults:
if d is not None and isinstance(d, (ast.List, ast.Dict, ast.Set)): if d is not None and isinstance(d, (ast.List, ast.Dict, ast.Set)):
self._add("W004", node.lineno, node.col_offset, self._add("W004", node.lineno, node.col_offset,
@@ -562,8 +571,10 @@ def _flow_maybe_report(node, locals_set, defined, analyzer):
if isinstance(node, ast.Name) and isinstance(node.ctx, ast.Load): if isinstance(node, ast.Name) and isinstance(node.ctx, ast.Load):
nid = node.id nid = node.id
if nid in locals_set and nid not in defined: if nid in locals_set and nid not in defined:
del_names = getattr(analyzer, "_flow_del_names", set()) or set()
analyzer._add("E004", node.lineno, node.col_offset, analyzer._add("E004", node.lineno, node.col_offset,
extra=f"可能未定义: {nid}", name=nid) extra=f"可能未定义: {nid}", name=nid,
del_flag=nid in del_names)
def _flow_check_loads(node, locals_set, defined, analyzer): def _flow_check_loads(node, locals_set, defined, analyzer):
@@ -826,6 +837,14 @@ def _dataflow_check(tree, analyzer):
if isinstance(sub, (ast.Global, ast.Nonlocal)): if isinstance(sub, (ast.Global, ast.Nonlocal)):
for n in sub.names: for n in sub.names:
locals_set.discard(n) locals_set.discard(n)
# 收集被 del 的变量 (E004 建议区分「del 后使用」场景)
del_names = set()
for sub in ast.walk(fn):
if isinstance(sub, ast.Delete):
for t in sub.targets:
if isinstance(t, ast.Name):
del_names.add(t.id)
analyzer._flow_del_names = del_names
defined = {a.arg for a in fn.args.posonlyargs + fn.args.args + fn.args.kwonlyargs} defined = {a.arg for a in fn.args.posonlyargs + fn.args.args + fn.args.kwonlyargs}
if fn.args.vararg: if fn.args.vararg: