AI 助手生成的类型注解在语法上通过 mypy 检查,但语义错误会在运行时暴露。提出 Pyre/Pysa 等防护工具方案。
AI 代码助手生成的类型提示满足 mypy 的结构验证器 —— 注解在句法上正确且能通过类型检查 —— 但其语义意义与代码的实际行为不匹配,原因是 mypy 验证的是注解的一致性,而不是注解者意图的正确性。BrassCoders 将 Pyre/Pysa 作为其 12 个扫描器之一运行,添加了污点分析,可以跟踪跨越类型注解边界的数据流,而不仅仅是检查注解的一致性。
根本原因在于 AI 助手学习注解模式的方式。一个在数百万 Python 文件上训练的助手已经在数千个上下文中见过 Optional[str]。它根据函数签名的形状进行模式匹配 —— 而不是根据每个调用方的行为。当你的其余代码库在没有 None 检查的情况下解引用返回值时,助手不会跟踪该调用图。mypy 按照其文档化设计,验证的是注解在结构上的一致性,而不是其行为正确性。
这种区别对大规模类型化代码库很重要。当注解撒谎时,mypy 的绿灯会训练审查者将类型检查输出视为行为证明。但事实并非如此。
BrassCoders 的 Pyre/Pysa 扫描器对类型注解代码执行污点分析,可以标记类型不安全的数据流,但 AI 生成且 mypy 无声接受的四种模式是:可选字段上的 Optional[str](但下游代码从不处理 None)、折叠结构的 Dict[str, Any](mypy 本可以强制执行)、异步函数上的不正确返回类型注解,以及不进行转换而撒谎的 cast() 调用。
Optional[str] 最为常见。AI 助手看到一个理论上可能缺失的字段,将其注解为 Optional[str] —— 在隔离情况下技术上正确。问题在于每个下游调用方都直接解引用它:user.name.upper()。mypy 根据调用方自己的注解来检查调用方,而不是检查 None 分支是否被处理。AttributeError 就此埋下。
Dict[str, Any] 是完全折叠类型安全的注解。Python 的 typing 模块提供了 TypedDict 恰好用于这种情况 —— 一个具有已知键形状的字典,mypy 可以强制执行。AI 助手默认使用 Dict[str, Any],因为它从不会导致类型检查失败。它也不提供任何强制执行。
异步函数上的返回类型不匹配更微妙。一个注解为 -> str 的异步函数如果返回协程对象会编译并通过类型检查。在运行时,等待一个字符串的调用代码会收到异常,而不是字符串。只有当函数在类型检查的上下文中被显式调用时,mypy 才会捕获这种不一致。未注解的调用方会让它通过。
cast() 是最直接的谎言。它告诉 mypy"相信我,这个值是类型 T"而不在运行时修改该值。cast() 在运行时是纯无操作。AI 助手使用它来静音类型检查器无法解决的类型错误。底层的不一致仍然存在。
mypy 的类型检查器在分析时操作注解:它从不执行代码。一个声称函数返回 str 的注解被 mypy 信任,即使该函数在 mypy 没有跟踪的代码路径中返回 None。BrassCoders 的 Pysa 扫描器跟踪这些流 —— 跟随 None 通过函数调用,直到它到达会导致崩溃或产生不正确行为的汇点。
范围上的差异很重要。mypy 执行局部类型推断:它检查每个函数的主体是否与函数声明的签名一致,以及调用站点是否与签名匹配。它不执行全局可达性分析来了解什么值在运行时实际通过调用图。当一个函数注解为 -> str 但有一个代码路径返回 None 时,mypy 仅在 None 返回在 mypy 能分析的分支中时才标记不一致。通过返回类型为 Any 的第三方 API 调用到达的 None 会无法检测地通过。
Pysa 的污点分析从源开始 —— 用户输入、外部 API 响应、数据库读取 —— 并跟踪这些值通过调用图进行的污点流向。它不仅仅检查注解。它跟踪可达性。当被污染的 None 跨越一个标记为 str 的类型注解边界时,那是一个发现,而不是绿灯。
BrassCoders 将 Pyre/Pysa 作为其 12 个扫描器之一运行 —— Pysa 对类型化 Python 代码执行跨程序污点分析,标记污点跨越类型边界的数据流。它也标记 cast() 使用模式和与安全敏感操作相邻的未注解代码路径。
在同一代码库上与 mypy 一起运行 BrassCoders 会给你两种不同的分析。mypy 在类型不匹配的调用站点发现注解不一致。Pysa 发现可达性问题:危险值到达敏感汇点的代码路径,无论注解如何。这两个工具是互补的,而不是冗余的。
使用 --strict 和编辑器中的 Pyright 将 BrassCoders 添加到你的 CI 管道中以进行实时注解验证。在 macOS、Linux 和 Windows (WSL2) 上,使用以下命令安装:
pip install brasscoders
brasscoders scan .
OSS 核心在本地运行,没有出站网络调用。Apache 2.0。无需账户。Pyre/Pysa 作为 12 个扫描器之一被包含,所以污点分析在每次扫描时自动运行。
BrassCoders 付费版在 OSS 核心基础上添加基于嵌入的噪声消减,费用为每个开发者每月 $12 —— 一旦你的扫描输出超过你想要手动分类的数量就很有用。OSS 核心配合 Pysa 足以捕获这里描述的类型提示模式。
基准:BrassCoders 在已发布语料库中捕获了 12 个 AI 生成的 bug 中的 11 个。Bandit 捕获了其中 6 个。类型提示失败是 Bandit 根本看不到的类别。