nilaway

nilaway

编译时捕获Go代码nil指针异常

NilAway是一款静态分析工具,专门用于在编译时检测Go代码中的nil指针异常。它无需额外注释,能够跨包分析nil流,并提供详细错误报告。NilAway以低开销和高精度著称,适合大型Go项目使用。该工具支持独立运行,也可与golangci-lint和Bazel/nogo集成,为开发者提供灵活的使用方式。

NilAwayGo语言静态分析空指针检测编译时检查Github开源项目

NilAway

GoDoc 构建状态 覆盖率状态

[!警告]
NilAway 目前正在积极开发中:可能会出现误报和破坏性更改。 我们非常感谢任何反馈和贡献!

NilAway 是一个静态分析工具,旨在通过在编译时而非运行时捕获空指针异常,帮助开发人员避免在生产环境中出现空指针崩溃。NilAway 类似于标准的nilness 分析器,但它采用了更复杂和强大的静态分析技术来跟踪包内以及跨包的空值流,并报告错误,为用户提供空值流以便更容易调试。

NilAway 具有三个关键特性,使其脱颖而出:

  • 它是全自动的:NilAway 配备了推理引擎,除了标准的 Go 代码外,不需要开发人员提供任何额外信息(如注释)。

  • 速度快:我们设计 NilAway 时考虑了速度和可扩展性,使其适用于大型代码库。在我们的测量中,启用 NilAway 时观察到的构建时间开销不到 5%。我们还在不断应用优化以进一步减少其占用。

  • 它是实用的:它不会阻止代码中所有可能的空指针异常,但它能捕获我们在生产中观察到的大多数潜在空指针异常,使 NilAway 能够在实用性和构建时间开销之间保持良好平衡。

:star2: 有关更详细的技术讨论,请查看我们的 Wiki工程博客 和论文(进行中)。

运行 NilAway

NilAway 使用标准的 go/analysis 实现,使其易于与现有的分析器驱动程序集成(即 golangci-lintnogo作为独立检查器运行)。

[!重要]
默认情况下,NilAway 分析所有 Go 代码,包括标准库和依赖项。这有助于 NilAway 更好地理解依赖项的代码并减少误报。然而,对于有大量依赖项的大型 Go 项目,这也会带来显著的性能成本(对于支持模块化的驱动程序来说只需一次),并增加依赖项中不可操作错误的数量。

我们强烈建议使用 include-pkgs 标志将分析范围缩小到仅限于您的项目代码。这指示 NilAway 跳过分析依赖项(例如第三方库),让您专注于 NilAway 在您的一方代码中报告的潜在空指针异常!

独立检查器

[!重要]
由于 NilAway 进行的分析较为复杂,NilAway 通过 go/analysis 框架的 Fact 机制 缓存其对特定包的发现。因此,强烈建议使用支持模块化分析的驱动程序(即 bazel/nogo 或 golangci-lint,但不是独立检查器,因为它将所有事实存储在内存中)以获得更好的大型项目性能。提供独立检查器更多是为了评估目的,因为它易于上手。

通过运行以下命令从源代码安装二进制文件:

go install go.uber.org/nilaway/cmd/nilaway@latest

然后,运行 linter:

nilaway -include-pkgs="<YOUR_PKG_PREFIX>,<YOUR_PKG_PREFIX_2>" ./...

golangci-lint (>= v1.57.0)

NilAway 在其当前形式下可能会报告误报。这不幸阻碍了它立即合并到 golangci-lint 并作为一个 linter 提供(参见 PR#4045)。因此,您需要将 NilAway 构建为 golangci-lint 的插件,以作为私有 linter 执行。golangci-lint 中有两个插件系统,使用 模块插件系统(自 v1.57.0 版本引入)要容易得多,这是在 golangci-lint 中运行 NilAway 的唯一支持方法。

(1) 如果您还没有在仓库根目录创建 .custom-gcl.yml 文件,请创建它并添加以下内容:

# 这必须是 >= v1.57.0 以支持模块插件系统。 version: v1.57.0 plugins: - module: "go.uber.org/nilaway" import: "go.uber.org/nilaway/cmd/gclplugin" version: latest # 或固定版本以实现可重现构建。

(2) 将 NilAway 添加到 linter 配置文件 .golangci.yaml 中:

linters-settings: custom: nilaway: type: "module" description: 检测 Go 代码中潜在空指针异常的静态分析工具。 settings: # 设置必须是"字符串到字符串的映射"以模拟命令行标志:键是标志名,值是特定标志的值。 include-pkgs: "<YOUR_PACKAGE_PREFIXES>" # NilAway 可以像其他 golangci-lint 分析器一样在配置文件的其他部分中被称为 `nilaway`。

(3) 构建包含 NilAway 的自定义 golangci-lint 二进制文件:

# 注意,用于引导自定义二进制文件的 `golangci-lint` 版本也必须 >= v1.57.0。 $ golangci-lint custom

默认情况下,自定义二进制文件将在 . 目录下构建,名称为 custom-gcl,这可以在 .custom-gcl.yml 文件中进一步自定义(参见 模块插件系统 的说明)。

[!提示]
缓存自定义二进制文件以避免重新构建,节省资源。如果您使用的是 NilAway 的固定版本,可以使用 .custom-gcl.yml 文件的哈希值作为缓存键。如果您使用 latest 作为 NilAway 版本,可以将构建日期附加到缓存键上,以强制在特定时间段后使缓存过期。

(4) 运行自定义二进制文件而不是 golangci-lint

# 参数与 `golangci-lint` 相同。 $ ./custom-gcl run ./...

Bazel/nogo

使用 bazel/nogo 运行需要稍多的努力。首先按照 rules_gogazellenogo 的说明设置您的 Go 项目,使其可以使用 bazel/nogo 构建,不配置或使用默认的 linter 集。然后,

(1) 在您的 tools.go 文件中添加 import _ "go.uber.org/nilaway"(或您用于配置工具依赖的其他文件,参见 Go 模块文档中的 如何跟踪模块的工具依赖?),以避免 go mod tidy 删除 NilAway 作为工具依赖。 (2) 运行以下命令将NilAway作为工具依赖添加到您的项目中:

# 将NilAway作为依赖项获取,并在go.mod文件中获取其传递依赖项。 $ go get go.uber.org/nilaway@latest # 这不应该从go.mod文件中删除NilAway作为依赖项。 $ go mod tidy # 运行gazelle以从go.mod同步依赖项到WORKSPACE文件。 $ bazel run //:gazelle -- update-repos -from_file=go.mod

(3) 将NilAway添加到nogo配置中(通常在顶级BUILD.bazel文件中):

nogo( name = "my_nogo", visibility = ["//visibility:public"], # 必须具有公共可见性 deps = [ +++ "@org_uber_go_nilaway//:go_default_library", ], config = "config.json", )

(4) 运行bazel build以查看NilAway的工作情况(任何nogo错误都会停止bazel构建,您可以使用--keep_going标志要求bazel尽可能多地构建):

$ bazel build --keep_going //...

(5) 查看nogo文档了解如何向nogo驱动程序传递配置JSON,并查看我们的wiki页面了解如何向NilAway传递配置。

代码示例

让我们看几个例子,了解NilAway如何帮助防止空指针引起的panic。

// 示例1: var p *P if someCondition { p = &P{} } print(p.f) // nilness在这里不报错,但NilAway会报错。

在这个例子中,局部变量p只在someCondition为真时被初始化。在访问字段p.f时,如果someCondition为假,可能会发生panic。NilAway能够捕获这个潜在的空指针流,并报告以下错误,显示这个空指针流:

go.uber.org/example.go:12:9: error: 检测到潜在的空指针panic。观察到从源到解引用点的空指针流:
    - go.uber.org/example.go:12:9: 未赋值的变量`p`访问了字段`f`

如果我们用空指针检查(if p != nil)来保护这个解引用,错误就会消失。

NilAway还能捕获跨函数的空指针流。例如,考虑以下代码片段:

// 示例2: func foo() *int { return nil } func bar() { print(*foo()) // nilness在这里不报错,但NilAway会报错。 }

在这个例子中,函数foo返回一个空指针,在bar中直接解引用,导致每次调用bar时都会panic。NilAway能够捕获这个潜在的空指针流,并报告以下错误,描述了跨函数边界的空指针流:

go.uber.org/example.go:23:13: error: 检测到潜在的空指针panic。观察到从源到解引用点的空指针流:
    - go.uber.org/example.go:20:14: 字面量`nil`从`foo()`在位置0返回
    - go.uber.org/example.go:23:13: `foo()`的结果0被解引用

请注意,在上面的例子中,foo不一定要与bar位于同一个包中。NilAway能够跟踪跨包的空指针流。此外,NilAway还处理Go特有的语言结构,如接收器、接口、类型断言、类型switch等。

配置

我们通过go/analysis中的标准标志传递机制公开了一组标志。请查看wiki/Configuration以了解可用的标志以及如何使用不同的linter驱动程序传递它们。

支持

我们遵循与Go项目相同的版本支持政策:我们支持并测试Go的最后两个主要版本。

如果您有任何问题、错误报告和功能请求,请随时在GitHub上提出问题

贡献

我们非常欢迎您为NilAway做出贡献!请注意,一旦您创建拉取请求,您将被要求签署我们的Uber贡献者许可协议

许可证

本项目版权归2023 Uber Technologies, Inc.所有,并根据Apache 2.0许可证授权。

编辑推荐精选

音述AI

音述AI

全球首个AI音乐社区

音述AI是全球首个AI音乐社区,致力让每个人都能用音乐表达自我。音述AI提供零门槛AI创作工具,独创GETI法则帮助用户精准定义音乐风格,AI润色功能支持自动优化作品质感。音述AI支持交流讨论、二次创作与价值变现。针对中文用户的语言习惯与文化背景进行专门优化,支持国风融合、C-pop等本土音乐标签,让技术更好地承载人文表达。

lynote.ai

lynote.ai

一站式搞定所有学习需求

不再被海量信息淹没,开始真正理解知识。Lynote 可摘要 YouTube 视频、PDF、文章等内容。即时创建笔记,检测 AI 内容并下载资料,将您的学习效率提升 10 倍。

AniShort

AniShort

为AI短剧协作而生

专为AI短剧协作而生的AniShort正式发布,深度重构AI短剧全流程生产模式,整合创意策划、制作执行、实时协作、在线审片、资产复用等全链路功能,独创无限画布、双轨并行工业化工作流与Ani智能体助手,集成多款主流AI大模型,破解素材零散、版本混乱、沟通低效等行业痛点,助力3人团队效率提升800%,打造标准化、可追溯的AI短剧量产体系,是AI短剧团队协同创作、提升制作效率的核心工具。

seedancetwo2.0

seedancetwo2.0

能听懂你表达的视频模型

Seedance two是基于seedance2.0的中国大模型,支持图像、视频、音频、文本四种模态输入,表达方式更丰富,生成也更可控。

nano-banana纳米香蕉中文站

nano-banana纳米香蕉中文站

国内直接访问,限时3折

输入简单文字,生成想要的图片,纳米香蕉中文站基于 Google 模型的 AI 图片生成网站,支持文字生图、图生图。官网价格限时3折活动

扣子-AI办公

扣子-AI办公

职场AI,就用扣子

AI办公助手,复杂任务高效处理。办公效率低?扣子空间AI助手支持播客生成、PPT制作、网页开发及报告写作,覆盖科研、商业、舆情等领域的专家Agent 7x24小时响应,生活工作无缝切换,提升50%效率!

堆友

堆友

多风格AI绘画神器

堆友平台由阿里巴巴设计团队创建,作为一款AI驱动的设计工具,专为设计师提供一站式增长服务。功能覆盖海量3D素材、AI绘画、实时渲染以及专业抠图,显著提升设计品质和效率。平台不仅提供工具,还是一个促进创意交流和个人发展的空间,界面友好,适合所有级别的设计师和创意工作者。

图像生成AI工具AI反应堆AI工具箱AI绘画GOAI艺术字堆友相机AI图像热门
码上飞

码上飞

零代码AI应用开发平台

零代码AI应用开发平台,用户只需一句话简单描述需求,AI能自动生成小程序、APP或H5网页应用,无需编写代码。

Vora

Vora

免费创建高清无水印Sora视频

Vora是一个免费创建高清无水印Sora视频的AI工具

Refly.AI

Refly.AI

最适合小白的AI自动化工作流平台

无需编码,轻松生成可复用、可变现的AI自动化工作流

下拉加载更多