尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Infer 的 Swift/Obj-C Nullability 检查器:在编译期发现缺失的可空性注解,消灭隐式解包崩溃

Infer 的 Swift/Obj-C Nullability 检查器:在编译期发现缺失的可空性注解,消灭隐式解包崩溃 静态分析代码质量开发工具【免费下载链接】inferA static analyzer for Java, C, C, and Objective-C项目地址https://gitcode.com/gh_mirrors/infer/infer点击查看免费下载本篇技术指南围绕 Facebook Infer当前仓库内置的Swift/Obj-C Nullability 检查器展开讲解它如何检测「Swift 调用未标注_Nullable/_Nonnull的 Objective-C 指针返回方法」这一高危互操作模式。读者读完将掌握如何用--swift-objc-nullability激活该检查器、MISSING_NULLABILITY_ANNOTATION问题的完整修复方案注解头文件或as?防御式转换、检查器刻意忽略的调用场景以及其底层基于抽象解释的源码实现原理。问题的根源ObjC 的“约定式可空性”与 Swift 的“类型系统强制可空性”Objective-C 使用原始指针表达返回值“可能为 nil”仅仅是一种口头约定编译器并不参与验证而 Swift 则要求在类型系统中显式表达可空性。当两种语言通过 clang importer 互操作时未标注可空性的 ObjC 指针返回会被 Swift 导入为Implicitly Unwrapped OptionalIUO即T!——它看起来像非空类型T允许直接调用其成员但底层若返回nil就会在运行时直接 trap 崩溃且崩溃点往往远离缺失注解的声明处。Objective-C 声明Swift 导入结果遇到nil时的行为- (NSString *)foo;func foo() - String!隐式解包运行时 trap- (NSString * _Nonnull)foo;func foo() - String契约上不可能为 nil- (NSString * _Nullable)foo;func foo() - String?安全调用方必须解包如上表源自 MISSING_NULLABILITY_ANNOTATION 问题文档所示未注解的- (NSString *)foo是危险形态Swift 会静默允许你写api.foo().count即使底层调用可能返回nil。激活方式与支持的语言该检查器由官方文档 checker-swift-objc-nullability.md 描述激活方式为在infer run/infer analyze的命令行中追加infer run --swift-objc-nullability -- 构建命令支持语言矩阵依据官方文档C/C/ObjC支持Swift支持C#/.Net、Erlang、Hack、Java、Python、Rust不支持从源码结构看检查器被注册为仅面向 Swift 前端的 intraprocedural过程内分析器见 registerCheckers.ml{ checker SwiftObjCNullability ; callbacks [(intraprocedural SwiftObjCNullabilityChecker.checker, Swift)] }同时在 Checker.ml 中登记了SwiftObjCNullability这一检查器枚举值。检查器的工作原理基于 Flat 格子的过程内分析该检查器的核心实现在 SwiftObjCNullabilityChecker.ml是一个标准的抽象解释过程内分析器由三部分组成调用状态域CallStatus每次 Swift→ObjC 的跨语言调用被归类为Unannotated未注解指针、Annotated已注解或NotAPointer非指针返回抽象域StatusDomain通过AbstractDomain.Flat (CallStatus)构造 Flat 格再以调用点位置Location为键组织成Domain映射记录每个边界的判定状态转移函数TransferFunctions在指令流中识别Call指令执行边界判定与问题上报。判定逻辑什么才算“缺失注解”转移函数在每条Call指令上做如下判断对应exec_instr与get_status边界条件调用方是 Swift 过程Procname.is_swift caller_pname且被调用方是 ObjC 方法Procname.is_objc_method callee_pname返回类型先检查attrs.ret_type是否为指针类型Typ.is_pointer非指针返回直接归为NotAPointer不做上报——这保证了 Cocoa 的NSError**输出参数约定BOOL返回 NSError**尾参不会被误报注解合并将call_flags.cf_caller_ret_annots前端在调用点恢复出的注解例如 Swift 已把结果当OptionalT处理与 callee 过程级attrs.ret_annots合并后统一判定三类“已注解”只要命中Annotations.ia_is_nullable、ia_is_nonnull或ia_is_null_unspecified中的任意一类即视为Annotated。值得注意的细节是_Null_unspecified的处理源码注释SwiftObjCNullabilityChecker.ml明确指出_Null_unspecified是维护者刻意把返回值暴露为 Swift IUO 的显式选择例如 IGListKit 的IGListSectionController.collectionContext就声明为null_unspecified目的是不让惯用 Swift 的调用方被迫写as!/fatalError因此它被当作“明确注解”而非“缺失注解”。上报条件当状态为Unannotated且同时满足以下条件时才上报MISSING_NULLABILITY_ANNOTATIONnot call_flags.cf_return_null_checked返回值未被 Swift 调用方做过空值检查SwiftObjCNullabilityIssue.should_report_at loc调用点不属于刻意忽略的边界not (SwiftObjCNullabilityIssue.is_system_framework_callee attrs)被调用方不属于系统框架。每次边界判定还会通过L.d_printfln输出调试日志Boundary at %a: %a并写入 HTML trace 便于开发者调试同时将状态加入抽象域供过程后摘要post-state summary使用。刻意忽略的场景保持报告可执行性为使报告可执行、可行动检查器刻意不在以下调用点上报见 SwiftObjCNullability.mdApple SDK 或预编译 framework 公共头目录中声明的 callee消费者无法编辑这些声明上报只会制造噪音返回值已被 Swift 调用方空值检查后使用的调用例如if let s api.foo() { ... }通过as?显式 Optional 转换的调用例如if let s api.foo() as? NSString { ... }。裸强制解包api.foo()!刻意不被视为修复——它只是把 trap 变得显式并没有消除 trap。修复方案优先注解 ObjC 头文件官方推荐的修复方式位于 MISSING_NULLABILITY_ANNOTATION 问题文档核心是让 ObjC 声明的契约显式化。若方法可能返回nil标记_Nullable否则标记_Nonnull// LegacyAPI.h interface LegacyAPI : NSObject - (NSString * _Nullable)getOptionalThing; end修复后 Swift 将结果导入为String?编译器强制调用方处理 nil 分支func good(api: LegacyAPI) { if let s api.getOptionalThing() { print(s.count) } }对于已用NS_ASSUME_NONNULL_BEGIN/NS_ASSUME_NONNULL_END包裹的整段头文件只需为例外情况显式补_Nullable。测试头文件 LegacyAPI.h 验证了这一约定块内裸NSString*被隐式视为_Nonnull块内显式nullable覆盖则优先级更高两种情况都不该触发上报。无法修改头文件时的降级方案as?防御式转换当声明位于无法编辑的第三方框架中时可在 Swift 调用点用as?把 IUO 抬升为真正的Optionalfunc goodWorkaround(api: LegacyAPI) { if let s api.getOptionalThing() as? NSString { print(s.length) } }其底层原理依据问题文档与测试注释as?转换在 LLAIR 中产生对_bridgeToObjectiveC的下游调用桥接分析预扫描bridge-analysis prepass以此为显式 Optional 转换的签名标记随后 Llair-to-Textual 翻译器会把Nullable挂到该调用的cf_caller_ret_annots上从而抑制对本应上报的未注解方法调用——这正是检查器上报条件中合并call_flags.cf_caller_ret_annots的设计用意。测试用例验证行为边界被逐一钉死仓库在 infer/tests/codetoanalyze/swift/interop-nullability/ 下提供了完整测试面NullabilityTests.swiftLegacyAPI.hissues.exp从预期输出 issues.exp 可以确认检查器的真实行为应上报bad 用例共 9 处unannotatedReturn_bad、macroAnnotatedUnannotatedProp_bad未注解指针返回 强制解包classMethodUnannotated_bad类方法与实例方法-一视同仁optionalGetterDerefMidBody_bad、optionalChainEligibleButForceUnwrapped_bad中途解引用未注解结果argPositionForceUnwrappedAtCall_bad、closureReturnIntoNonOptional_badT!流入非 Optional 参数/闭包返回边界时强制解包nonBindingNullCheckBeforeForceUnwrap_partialBad同一方法二次调用的解包仍上报2 处。不应上报good 用例if let/guard let空值检查、as?转换、??默认值合并、Optional 返回直通passthrough、?.可选链、NS_ASSUME_NONNULL块、工厂方法安全使用、NSError**输出参数约定、显式_Null_unspecified见explicitlyUnspecifiedChained_good等。其中swiftRefinedNullableString_good_FP是一个已知的假阳性FP钉死用例.h的 category 接口被#ifdef __swift__门控Swift 侧看到的是String?而 Infer 的 ObjC 捕获只看到.m的裸NSString*导致!解包被误报——测试注释明确标注“Today it does (FP), so the test is_FP_*until we fix the checker”说明检查器仍在持续迭代。问题上报与文档映射MISSING_NULLABILITY_ANNOTATION在 IssueType.ml 注册归类为NullPointerDereference空指针解引用类别、严重级别Error并挂接 MISSING_NULLABILITY_ANNOTATION.md 用户文档该文档同时被 Pulse 变体MISSING_NULLABILITY_ANNOTATION_PULSE复用IssueType.ml表明同一类问题也有对应的基于符号执行的 Pulse 路径相关实现见 PulseDiagnostic.ml 与 PulseModelsSwift.ml。小结Swift/Obj-C Nullability 检查器的价值在于把「运行时才暴露的 IUO 崩溃」提前到编译期静态分析阶段让拥有 ObjC 声明的团队显式决定可空性契约。通过--swift-objc-nullability一行参数即可启用配合_Nullable/_Nonnull注解或as?防御式转换即可闭环修复其刻意忽略系统框架、已空值检查和as?转换场景的设计则保证了报告的高信噪比与可执行性。感兴趣的读者可进一步阅读 checker 官方文档、问题类型总览以及上述测试用例了解其在真实 fbobjc 代码形态下的判定细节。赞分享静态分析代码质量开发工具【免费下载链接】inferA static analyzer for Java, C, C, and Objective-C项目地址https://gitcode.com/gh_mirrors/infer/infer点击查看免费下载相关推荐Infer 的 SWIFT_NPE 检查器Swift Optional 强制解包的空值崩溃静态检测指南Infer 的 SWIFT_NPE 检查器Swift Optional 强制解包的空值崩溃静态检测指南 导读 SWIFT_NPE 是 Infer 的 Puls静态分析代码质量开发工具Infer 的 Swift/Objective-C 空值性检查器SwiftObjCNullability 与 MISSING_NULLABILITY_ANNOTATION 实战指南Infer 的 Swift/Objective C 空值性检查器SwiftObjCNullability 与 MISSING_NULLABILITY_ANNO静态分析代码质量开发工具Infer 注解可达性检查器CHECKERS_ANNOTATION_REACHABILITY_ERROR完全指南Infer 注解可达性检查器CHECKERS_ANNOTATION_REACHABILITY_ERROR完全指南 导读 本文深入讲解 Facebook I静态分析代码质量开发工具创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表