Ruff ty 静态检查规则解析:unsupported-bool-conversion 如何捕捉 __bool__ 实现错误导致的运行时崩溃

发布时间:2026/9/10 8:08:03
Ruff ty 静态检查规则解析:unsupported-bool-conversion 如何捕捉 __bool__ 实现错误导致的运行时崩溃 Ruff ty 静态检查规则解析unsupported-bool-conversion 如何捕捉bool实现错误导致的运行时崩溃【免费下载链接】ruffAn extremely fast Python linter and code formatter, written in Rust.项目地址: https://gitcode.com/GitHub_Trending/ru/ruff本文基于 ruff 仓库中 ty 类型检查器的规则文档 unsupported-bool-conversion.md系统讲解unsupported-bool-conversion这条静态诊断规则它检测什么、在哪些布尔上下文中触发、误报与漏报边界如何划定并结合 bool.rs 的try_bool实现与 diagnostic.rs 的规则注册代码深入剖析 ty 如何把__bool__实现不正确这一类会在运行时抛异常的问题提前暴露出来。规则概览定位、严重级别与文档来源unsupported-bool-conversion是 tyruff 仓库内置的 Python 静态类型检查器见 ty/README.md中的一条内建诊断规则其文档正是本文的核心来源declare_lint! { #[doc include_str!(../../resources/lint_docs/unsupported-bool-conversion.md)] pub(crate) static UNSUPPORTED_BOOL_CONVERSION { summary: detects boolean conversion where the object incorrectly implements __bool__, status: LintStatus::stable(0.0.1-alpha.1), default_level: Level::Error, } }从这段注册代码diagnostic.rs可以确认三个关键事实规则语义summary 明确写为检测对象错误实现__bool__的布尔转换默认级别Level::Error即默认作为错误报告不需要额外配置稳定性自0.0.1-alpha.1起即为 stable 状态属于 ty 最早期就提供并长期保持稳定的规则之一。该文档通过include_str!宏在编译期被嵌入到 lint 元数据中diagnostic.rs这也是 ruff 项目中所有规则文档的统一组织方式——文档放在 crates/ty_python_semantic/resources/lint_docs/ 目录下由declare_lint!宏引用最终进入规则注册表diagnostic.rs 中registry.register_lint(UNSUPPORTED_BOOL_CONVERSION)。规则语义什么情况下会触发规则文档原文给出了三段核心说明这里完整继承并展开What it does检测目标Checks for bool conversions where the object doesnt correctly implement__bool__.即当代码把一个对象放进需要布尔求值的位置而该对象的类型没有正确地实现__bool__协议时ty 会报告此诊断。正确实现在 ty 的判定里有严格标准__bool__必须存在、可调用、参数只能是self、返回值必须可赋值给bool。Why is this bad?危害If an exception is raised when you attempt to evaluate the truthiness of an object, using the object in a boolean context will fail at runtime.如果一个对象在被判定真假时会在运行时抛出异常例如TypeError那么任何依赖隐式布尔转换的代码路径都会在运行期崩溃——而这类错误在纯语法检查中完全不可见只有类型级分析才能提前发现。Examples文档自带示例class NotBoolable: __bool__ None def __lt__(self, other: object) - NotBoolable: return self b1 NotBoolable() b2 NotBoolable() # exception raised here if b1: # error pass # exception raised here b1 and b2 # error # exception raised here not b1 # error # A chained comparison converts the result of b1 b2 to bool. # exception raised here b1 b2 b1 # error这个示例覆盖了四类典型触发位置也正好对应 ty 源码中try_bool的主要调用点示例位置对应 ty 源码调用点if b1:if语句条件求值builder.rsb1 and b2二元逻辑运算的短路求值路径builder.rsnot b1一元not运算builder.rsb1 b2 b1链式比较第一步比较结果必须转换为 bool 来决定是否继续转换点见 comparisons.rs除上表外从 builder.rs 的调用点结构看触发位置还包括while循环条件约 L2410、推导式comprehension的过滤条件约 L5263、L5282、match语句的case守卫约 L2734以及条件表达式等——凡是 Python 语言语义上会隐式执行bool(x)的位置ty 都会走同一条try_bool判定路径。底层实现一try_bool 与 BoolError 的四类失败原因规则的核心判定逻辑在 bool.rs 中。入口函数是Type::try_boolbool.rs/// Resolves the boolean value of a type. /// /// This is used to determine the value that would be returned /// when bool(x) is called on an object x. /// /// Returns an error if the type doesnt implement __bool__ correctly. pub(crate) fn try_bool( self, db: db dyn Db, env: ProgramEnvironmentdb, ) - ResultTruthiness, BoolErrordb { ... }返回类型为ResultTruthiness, BoolError成功时给出静态可判定的真假性AlwaysTrue/AlwaysFalse/Ambiguous失败时给出结构化的BoolError。失败原因被细分为五种变体bool.rspub(crate) enum BoolErrordb { /// The type has a __bool__ attribute but it cant be called. NotCallable { not_boolable_type: Typedb }, /// The type has a callable __bool__ attribute, but it isnt callable /// with the given arguments. IncorrectArguments { not_boolable_type: Typedb, truthiness: Truthiness }, /// The type has a __bool__ method, is callable with the given arguments, /// but the return type isnt assignable to bool. IncorrectReturnType { not_boolable_type: Typedb, return_type: Typedb }, /// A union type doesnt implement __bool__ correctly. Union { union: UnionTypedb, truthiness: Truthiness }, /// Any other reason why the type cant be converted to a bool. Other { not_boolable_type: Typedb }, }这五种失败与用户可见的诊断一一对应。BoolError::report_diagnostic_implbool.rs通过context.report_lint(UNSUPPORTED_BOOL_CONVERSION, condition)把错误落到条件表达式的源码区间上并针对不同变体生成不同的诊断消息与附注sub-diagnosticIncorrectArguments消息为 Boolean conversion is not supported for typeX附注__bool__methods must only have aselfparameter并会在源码上标注出错误的参数位置primary和方法定义处secondaryIncorrectReturnType附注说明Xis not assignable tobool并标注返回类型标注的位置NotCallable说明__bool__onXmust be callableUnion消息形如 Boolean conversion is not supported for unionA | BbecauseBdoesnt implement__bool__correctly——注意 ty 会找出联合体中第一个不正确的成员明确指出而不是笼统提示Other兜底提示it incorrectly implements__bool__例如__bool__指向一个__call__可能缺失的类型。底层实现二truthiness 解析的优先级与特判try_bool_implbool.rs的判定顺序体现了对 Python 语义的完整建模理解它有助于把握规则的判定边界字面量短路bool字面量直接返回其值整数字面量按! 0判定这些类型根本不查__bool__bool.rs。常规实例调用try_call_dunder(__bool__, ...)并校验返回类型必须可赋值给bool——这正是文档示例中__bool__ None会报错的原因属性存在但不可调用落入NotCallable若返回非bool类型则落入IncorrectReturnType见 bool.rs。元组特判若类型是 tuple 规格且没有可用__bool__直接使用tuple_spec.truthiness()——空元组恒假、非空元组恒真bool.rs。final类的__len__回退只对final类型才会在缺少__bool__时回退到__len__因为非 final 类可能被子类加上__bool__且要求__len__返回值可实现SupportsIndex若两者都无则恒真bool.rs。特殊类型函数对象、模块、BoundMethod等按 Python 语义直接判AlwaysTrueType::Never、Dynamic等判Ambiguousbool.rs。联合体try_union逐成员求值只要任一本体报错就在非短路模式下汇总为BoolError::Unionbool.rs。这里还有一个值得注意的设计try_bool与bool是成对 API。Type::boolbool.rs用于纯静态分支评估不产生诊断出错时通过BoolError::fallback_truthiness回退为Ambiguous而try_bool才是类型检查路径出错时会走到report_diagnostic报告unsupported-bool-conversion。源码注释还说明了性能考量bool支持allow_short_circuit提前返回在其基准测试中有 1–2% 的性能收益bool.rs。官方测试用例四类典型错误场景仓库自带一套 mdtest 测试文件 unsupported_bool_conversion.md标题为Different ways thatunsupported-bool-conversioncan occur覆盖了规则的全部主要触发形态每例都标注# error: [unsupported-bool-conversion]场景 1__bool__参数不正确class NotBoolable: def __bool__(self, foo): return False a NotBoolable() # error: [unsupported-bool-conversion] 10 and a and True场景 2__bool__返回类型不正确class NotBoolable: def __bool__(self) - str: return wat a NotBoolable() # error: [unsupported-bool-conversion] 10 and a and True场景 3__bool__属性存在但不可调用class NotBoolable: __bool__: int 3 a NotBoolable() # error: [unsupported-bool-conversion] 10 and a and True场景 4联合体中至少一个成员实现不正确class NotBoolable1: def __bool__(self) - str: return wat class NotBoolable2: pass class NotBoolable3: __bool__: int 3 def get() - NotBoolable1 | NotBoolable2 | NotBoolable3: return NotBoolable2() # error: [unsupported-bool-conversion] 10 and get() and True这四类场景分别映射到BoolError的IncorrectArguments、IncorrectReturnType、NotCallable与Union变体。测试快照存放在 crates/ty_python_semantic/resources/mdtest/snapshots/ 目录中可以查看每条诊断的实际渲染效果。此外从源码结构看该规则还会间接出现在比较、成员判断、循环、条件表达式等多个 mdtest 文件中例如 comparison/tuples.md、expression/boolean.md、loops/while_loop.md说明它是这些表达式类型分析链条中共享的基础诊断。实用要点与边界说明结合文档与实现使用这条规则时有几点值得注意触发条件是实现不正确而不是没有定义__bool__。普通类未定义__bool__且不可调用时Python 会回退到__len__或恒真语义ty 对此判Ambiguous而不报错只有定义了但定义错了不可调用、参数错误、返回类型错误或联合体成员错误时才报告。诊断定位在条件表达式处而非__bool__定义处但对参数错误和返回类型错误两种情况附注会额外标注方法定义中的具体 spanbool.rs便于跳转修复。__bool__返回Never的情形在bool的错误回退路径中被保守处理见 bool.rs 的注释这是为条件分析保留短路语义的边界设计。规则默认级别为Errorty 以稳定状态自0.0.1-alpha.1提供本文所有结论均以当前仓库代码为准涉及具体版本的适用性以仓库中 crates/ty 的发布状态为准。小结unsupported-bool-conversion规则把 Python 中对象被放进布尔上下文时__bool__协议实现不正确这一运行时隐患转化为静态诊断入口是 builder.rs 与 comparisons.rs 中所有隐式布尔转换点对try_bool的调用判定核心是 bool.rs 中严格的__bool__调用校验存在性、可调用性、参数、返回类型错误经由结构化BoolError落到 diagnostic.rs 注册的UNSUPPORTED_BOOL_CONVERSION规则上。配套文档 unsupported-bool-conversion.md 与测试 diagnostics/unsupported_bool_conversion.md 共同构成该规则的语义契约是理解 ty 布尔求值分析的入口。【免费下载链接】ruffAn extremely fast Python linter and code formatter, written in Rust.项目地址: https://gitcode.com/GitHub_Trending/ru/ruff创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考