Project Icon

nilaway

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

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

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许可证授权。

项目侧边栏1项目侧边栏2
推荐项目
Project Cover

豆包MarsCode

豆包 MarsCode 是一款革命性的编程助手,通过AI技术提供代码补全、单测生成、代码解释和智能问答等功能,支持100+编程语言,与主流编辑器无缝集成,显著提升开发效率和代码质量。

Project Cover

AI写歌

Suno AI是一个革命性的AI音乐创作平台,能在短短30秒内帮助用户创作出一首完整的歌曲。无论是寻找创作灵感还是需要快速制作音乐,Suno AI都是音乐爱好者和专业人士的理想选择。

Project Cover

白日梦AI

白日梦AI提供专注于AI视频生成的多样化功能,包括文生视频、动态画面和形象生成等,帮助用户快速上手,创造专业级内容。

Project Cover

有言AI

有言平台提供一站式AIGC视频创作解决方案,通过智能技术简化视频制作流程。无论是企业宣传还是个人分享,有言都能帮助用户快速、轻松地制作出专业级别的视频内容。

Project Cover

Kimi

Kimi AI助手提供多语言对话支持,能够阅读和理解用户上传的文件内容,解析网页信息,并结合搜索结果为用户提供详尽的答案。无论是日常咨询还是专业问题,Kimi都能以友好、专业的方式提供帮助。

Project Cover

讯飞绘镜

讯飞绘镜是一个支持从创意到完整视频创作的智能平台,用户可以快速生成视频素材并创作独特的音乐视频和故事。平台提供多样化的主题和精选作品,帮助用户探索创意灵感。

Project Cover

讯飞文书

讯飞文书依托讯飞星火大模型,为文书写作者提供从素材筹备到稿件撰写及审稿的全程支持。通过录音智记和以稿写稿等功能,满足事务性工作的高频需求,帮助撰稿人节省精力,提高效率,优化工作与生活。

Project Cover

阿里绘蛙

绘蛙是阿里巴巴集团推出的革命性AI电商营销平台。利用尖端人工智能技术,为商家提供一键生成商品图和营销文案的服务,显著提升内容创作效率和营销效果。适用于淘宝、天猫等电商平台,让商品第一时间被种草。

Project Cover

AIWritePaper论文写作

AIWritePaper论文写作是一站式AI论文写作辅助工具,简化了选题、文献检索至论文撰写的整个过程。通过简单设定,平台可快速生成高质量论文大纲和全文,配合图表、参考文献等一应俱全,同时提供开题报告和答辩PPT等增值服务,保障数据安全,有效提升写作效率和论文质量。

投诉举报邮箱: service@vectorlightyear.com
@2024 懂AI·鲁ICP备2024100362号-6·鲁公网安备37021002001498号