GCC 16 的新特性:改进的错误信息与 SARIF 输出
摘要
GCC 16 引入了针对 C++ 模板的改进分层错误信息以及更新的 SARIF 机器可读诊断输出,从而提升开发者体验。
暂无内容
查看缓存全文
缓存时间: 2026/05/21 06:23
# GCC 16 新特性:改进的错误信息与 SARIF 输出 | Red Hat 开发者
来源:https://developers.redhat.com/articles/2026/04/28/gcc-16-improved-error-messages-sarif-output
我在 Red Hat 从事 GNU 编译器套件(GCC)(https://gcc.gnu.org/)的工作。GCC 16 即将发布,因此我来分享一些今年我参与开发的新特性。有些变化对用户是可见的,另一些则以更微妙的方式改进了系统。
## 新的 C++ 错误信息改进
C++ 开发者面临的一个众所周知难题是模板相关错误信息的可读性。C++ 编译器要么提供的信息太少,要么给你输出满屏的文本。无论哪种情况,这些错误都很难解读。
GCC 的错误信息具有层次结构。在 GCC 15 中,我添加了一个实验性选项(https://developers.redhat.com/articles/2025/04/10/6-usability-improvements-gcc-15?source=sso#2__a_new_look_for_c___template_errors),将该结构显示为嵌套的项目符号集合。在 GCC 16 中,这种行为现已成为默认行为。你可以通过使用 `-fno-diagnostics-show-nesting`(https://gcc.gnu.org/onlinedocs/gcc/Diagnostic-Message-Formatting-Options.html#index-fno-diagnostics-show-nesting)或 `-fdiagnostics-plain-output`(https://gcc.gnu.org/onlinedocs/gcc/Diagnostic-Message-Formatting-Options.html#index-fdiagnostics-plain-output)恢复到之前的行为。
我修复了几个错误,并在更多地方利用了层次结构。例如,当手动给参数添加 `const` 时,声明和定义很容易不同步:
```cpp
class foo {
public:
void test(int i, int j, void *ptr, int k);
};
// 参数 3 的 "const" 性错误
void foo::test(int i, int j, const void *ptr, int k) {
}
```
在 GCC 15(https://godbolt.org/z/9qMxEz3Pe)中,我们输出以下内容:
```
:8:6: error: no declaration matches 'void foo::test(int, int, const void*, int)'
8 | void foo::test(int i, int j, const void *ptr, int k)
| ^~~
:4:10: note: candidate is: 'void foo::test(int, int, void*, int)'
4 | void test(int i, int j, void *ptr, int k);
| ^~~~
:1:7: note: 'class foo' defined here
1 | class foo
| ^~~
```
在 GCC 16(https://godbolt.org/z/ceMTc8zTj)中,我们现在输出:
```
:8:6: error: no declaration matches 'void foo::test(int, int, const void*, int)'
8 | void foo::test(int i, int j, const void *ptr, int k)
| ^~~
• there is 1 candidate
• candidate is: 'void foo::test(int, int, void*, int)'
:4:10:
4 | void test(int i, int j, void *ptr, int k);
| ^~~~
• parameter 3 of candidate has type 'void*'...
:4:35:
4 | void test(int i, int j, void *ptr, int k);
| ~~~~~~^~~
• ...which does not match type 'const void*'
:8:42:
8 | void foo::test(int i, int j, const void *ptr, int k)
| ~~~~~~~~~~~~^~~
:1:7: note: 'class foo' defined here
1 | class foo
| ^~~
```
这精确定位了问题的具体位置。使用此 Compiler Explorer 链接(https://godbolt.org/z/v1hrbWf77)查看颜色高亮是如何在消息和引用源代码中对比不匹配的类型的。
## 更新的 SARIF 机器可读输出
默认情况下,GCC 以文本形式将诊断信息(错误和警告)写入 `stderr`。随着编译器功能的增长,使用正则表达式解析该输出变得越来越困难。在 GCC 13 中,我添加了使用静态分析结果交换格式(SARIF)(https://gcc.gnu.org/wiki/SARIF)以机器可读形式写入诊断信息的能力。这种基于 JSON 的格式允许我们将诊断数据与诊断的呈现方式分离开来。
GCC 16 对生成的 SARIF 输出进行了多项改进。例如,当报告缺少 `return *this` 的赋值运算符时:
```cpp
namespace foo {
namespace bar {
class foo {
foo& operator= (const foo &other) {
m_val = other.m_val;
}
int m_val;
};
} // namespace bar
} // namespace foo
```
现在的 SARIF 输出(https://godbolt.org/z/4jTv8ocv1)捕获了逻辑位置的嵌套结构。这使得 SARIF 查看器能够过滤 `foo::bar` 命名空间内的诊断信息:
```json
"logicalLocations": [
{"name": "foo", "fullyQualifiedName": "foo", "kind": "namespace", "index": 0},
{"name": "bar", "fullyQualifiedName": "foo::bar", "kind": "namespace", "parentIndex": 0, "index": 1},
{"name": "baz", "kind": "type", "parentIndex": 1, "index": 2},
{"name": "operator=", "fullyQualifiedName": "foo::bar::baz::operator=", "decoratedName": "_ZN3foo3bar3bazaSERKS1_", "kind": "function", "parentIndex": 2, "index": 3}
]
```
GCC 16 还向 SARIF 输出添加了数据,以更好地表达代码路径中的非标准控制流(例如异常处理和 `longjmp`)。这些数据包含在即将发布的 SARIF 2.2 标准(https://github.com/oasis-tcs/sarif-spec/wiki/What%27s-new-in-SARIF-2.2)中。
## 新的 HTML 输出选项
在 GCC 15 中,我添加了 `-fdiagnostics-add-output=`(https://gcc.gnu.org/onlinedocs/gcc/Diagnostic-Message-Formatting-Options.html#index-fdiagnostics-add-output)以允许同时使用多种诊断输出方式。纯文本输出有其局限性,因此 GCC 16 包含了一个新的实验性 `-html` 选项(https://gcc.gnu.org/onlinedocs/gcc/Diagnostic-Message-Formatting-Options.html#index-fdiagnostics-add-output_003dexperimental-html)。
图 1 展示了第一个使用 `-fdiagnostics-add-output=experimental-html` 的示例。

图 1:GCC 16 中的实验性 HTML 诊断信息,显示了一个 "no declaration matches" 错误,并带有高亮代码片段和标注。
你可以在此处(https://dmalcolm.fedorapeople.org/gcc/2026-04-24/bad-def.cc.html)查看生成的完整页面。
顾名思义,该功能是实验性的,但我已经发现它对调试 GCC 内置的静态分析器(https://gcc.gnu.org/wiki/StaticAnalyzer)很有帮助。当你使用 `-fanalyzer` 选项(https://developers.redhat.com/blog/2020/03/26/static-analysis-in-gcc-10)启用该工具时,它会通过你的源代码探索过程间路径,以在编译时发现错误。
我经常需要调试这段代码中的棘手问题,可视化越多越好。HTML 输出在一条执行路径中显示嵌套的栈帧,使用投影阴影直观地表示栈(图 2)。

图 2:GCC 静态分析器的实验性 HTML 输出,展示了一个包含 26 个事件的执行路径,带有嵌套帧和视觉投影阴影来表示调用栈。
完整示例在此处(https://dmalcolm.fedorapeople.org/gcc/2026-04-24/linked-list.c.html)。该版本还包含一个彩蛋(通过 `-fdiagnostics-add-output=experimental-html:show-state-diagrams=yes` 生成)。如果你按下 **j** 和 **k**,可以沿路径向前和向后移动,并显示每个事件时内存的预测状态图,以及哪些指针指向哪些缓冲区(图 3)。

图 3:一个实验性的 GCC 状态图,可视化了堆和栈的内存转换,展示了一个指向已释放缓冲区的指针。
## 静态分析器改进
可视化使得更容易发现和修复分析器中的问题,从而在 GCC 16 中带来了一些内部改进。分析器用于跟踪代码的核心数据结构("supergraph")变得难以使用,存在各种隐蔽的 bug 藏身之处。我已经在 GCC 16 中对其进行了重写(https://gcc.gnu.org/git/?p=gcc.git;a=commit;h=0b786d961d4426db15b3f681f4e7c6fcaeb296c2)。新代码在"用户代码中的位置"与"发生在这些位置之间过渡上的操作"之间具有更清晰的关注点分离,而且我已经发现它使得添加新功能更加容易。
我还更新了用于跟踪模拟内存缓冲区内容的数据结构,用一种简单的 `std::map`(位范围到内容)替换了相当笨拙的哈希方法。新方法(https://gcc.gnu.org/git/?p=gcc.git;a=commit;h=310a70ef6db45d2fd574daa77b5128cd1f4edbce)既更容易理解,也更快。
我的同事 Andrew MacLeod 花了几年时间在一个名为 Ranger 的项目(https://developers.redhat.com/blog/2021/04/28/value-range-propagation-in-gcc-with-project-ranger)上,以改进 GCC 如何跟踪用户代码中值的属性供优化器使用。这涵盖了诸如:知道某个整数是否在可以作为数组索引的有效范围内,或者某些位是否已知为真或假。在 GCC 16 中,我开始将 `-fanalyzer` 连接到这些数据结构上。和上述更改一样,这可能不会直接可见,但应该会带来更准确的分析和更少的误报。
在 GCC 16 之前,`-fanalyzer` 仅适用于 C 代码;在 C++ 代码上运行它通常会产生无关的结果。问题在于我的代码忽略了 GCC 内部如何表示 (a) 异常处理,导致它发明了通过代码的不可能路径,以及 (b) C++ 的命名返回值优化(NRVO),导致大量关于所谓内存泄漏的误报。
这里有好消息也有坏消息。好消息:在 GCC 16 中,我实现了异常处理和 NRVO,使得 `-fanalyzer` 可以在 C++ 代码上工作。坏消息是,该功能目前仅限于小型示例。在复杂代码上运行它可能会导致扩展问题,分析器将其整个分析预算花费在代码的一小部分上然后放弃,浪费 CPU 周期而不产生有用结果。误报已被漏报取代,因此我仍然不能建议在 C++ 代码上使用它。正确性比速度更重要,我正在研究在 GCC 17 中扩展它的方法,希望能够使 `-fanalyzer` 在生产 C++ 代码中实用。
## 尝试 GCC 16
这些特性只是 GCC 16(https://gcc.gnu.org/gcc-16/changes.html)众多改进中的一小部分,它即将在上游正式发布。你现在可以在 Compiler Explorer(https://godbolt.org/z/sPf7erEbq)中尝试新版本;在我撰写本文时,它被列为 **GCC trunk**。戴上我的下游帽子,你也可以在 Fedora 44 中尝试。玩得开心!
相似文章
GCC 16及以后版本中的BPF支持
何塞·马奇西(José Marchesi)和GCC-BPF团队提供了GCC 16中BPF支持的更新,突出了在与LLVM功能对等方面取得的进展,以及内核BPF自测通过率的提升。
C++26:标准库强化
C++26 引入了标准化的库强化机制,用于在运行时捕获常见的未定义行为(如越界访问)。基于 Google 的生产经验,此举仅带来 0.30% 的性能开销,同时将段错误减少了 30%。
Zig ELF 链接器改进开发日志
新的 Zig ELF 链接器现在支持外部库和 C 源码的快速增量编译,在 x86_64 Linux 上能够实现毫秒级重建。
关于C扩展、可移植性和替代编译器
本文讨论了编写可移植C代码的实际挑战,这些挑战源于对非标准编译器扩展和glibc条件头文件的依赖,并通过构建C编译器的示例进行说明。
ROCm 7.13 夜间版新增 Strix Halo 优化
AMD 的 ROCm 7.13 技术预览版为 Strix Halo(Ryzen AI Max 300)新增优化,并将 ROCprof Trace Decoder 开源。