语言和无关目标的特性

控制错误和警告

Clang提供了许多方式控制代码构建导致它引起错误和警告信息,并且和它们如何显示到终端。

控制Clang如何显示诊断信息

当Clang生成一个诊断,在它的输出中包含了丰富的信息,并且给你精细的控制什么信息要输出。Clang具有打印这个信息的能力,以下是控制选项:

  1. 一个 文件/行/列 预示器显示代码中生成诊断的地方 [-fshow-column, -fshow-source-location]。
  2. 一个诊断信息作为注记、警告、错误和致命错误的分类。
  3. 一个文本字符串描述问题是什么。
  4. 一个选项预示如何控制这个诊断(对于支持的诊断) [-fdiagnostics-show-option]。
  5. 一个对于诊断的的高级别分类给想要更具诊断类别分类的客户(对于支持的诊断) [-fdiagnostics-show-category]。
  6. 问题产生的源代码行,并且具有一个花括号和范围预示重要位置[-fcaret-diagnostics]。
  7. “FixIt”信息,简洁的解释如何修复问题 (当 Clang 确定它知道的时候) [-fdiagnostics-fixit-info]。
  8. 一个机器可以解析的范围表示(默认关闭的) [-fdiagnostics-print-source-range-info]。

有关更多信息,请参见格式化诊断信息。

诊断映射

所有诊断被隐射到五类中的一类:

  • Ignored 忽略
  • Note 注记
  • Warning 警告
  • Error 错误
  • Fatal 致命错误

诊断分类

虽然默认没有显示,诊断可以每个都关联到高级别的类别。 This category is intended to make it possible to triage builds that produce a large number of errors or warnings in a grouped way.

类别默认没有被显示,但是它们可以使用-fdiagnostics-show-category 选项开启。当设置到 “name” 时候,类别被以文本打印在诊断中。当被设置为“id”,将会打印类别号。 类别名称和ID的隐射可以通过执行 ‘clang —print-diagnostic-categories‘ 来获得。

通过命令行标志控制诊断

TODO: -W flags, -pedantic, etc

通过Pragmas控制诊断

Clang也可以通过源代码中的pragmas控制什么诊断是要使能的。这在关闭代码中特定区间的诊断时候是非常有用的。Clang支持GCC的pragma来兼容已经存在代码,和几个扩展。

pragma 可以控制任何命令行可以使用的警告。警告可以被设置为忽略、警告、错误或者致命错误。下面的例子代码将会告诉Clang或者GCC去忽略-Wall警告:

  1. #pragma GCC diagnostic ignored "-Wall"

作为对GCC的pragma提供的所有功能的附加,Clang还允许你push和pop当前的警告信息。当写一个头文件要被其他人编译的时候是非常有用的,因为你不知道它们提供的警告标志。

下面的例子中,-Wmultichar只被一行代码忽略,在这之后诊断返回之前存在的状态。

  1. #pragma clang diagnostic push
  2. #pragma clang diagnostic ignored "-Wmultichar"
  3. char b = 'df'; // no warning.
  4. #pragma clang diagnostic pop

push和pop pragmas将会保存和恢复编译器的全部诊断状态,不论它如何被设置。这意味着有可能push和pop围绕GCC兼容诊断和Clang将会push和pop适当的,而GCC将会忽略push和pop作为未知pragmas。注意虽然Clang支持GCC pragma,Clang和GCC不支持完全相同的警告集合,所以即使使用GCC兼容#pragmas,仍然没有保证它们在两个编译器中行为相同。

作为控制编译器生成的警告和错误的附加,有可能通过如下pragmas生成定制的警告和错误信息:

  1. // The following will produce warning messages
  2. #pragma message "some diagnostic message"
  3. #pragma GCC warning "TODO: replace deprecated feature"
  4. // The following will produce an error message
  5. #pragma GCC error "Not supported"

这些pragmas操作与#warning和#error预处理指令相似,除了它们可能通过C99 _Pragma操作被潜入预处理器宏:

  1. #define STR(X) #X
  2. #define DEFER(M,...) M(__VA_ARGS__)
  3. #define CUSTOM_ERROR(X) _Pragma(STR(GCC error(X " at line " DEFER(STR,__LINE__))))
  4. CUSTOM_ERROR("Feature not available");

通过系统头文件控制诊断

当警告发生在系统头文件的时候会被压制。默认的, 一个被包含的文件被当作是系统头文件,如果它是在-isystem指定的include 路径中找打的话,但是这也可以被通过好几种方式重载。

system_header pragma可以被用于标记当前文件是系统头文件。从pragma位置开始在这个文件中将不会有警告产生。

  1. char a = 'xy'; // warning
  2. #pragma clang system_header
  3. char b = 'ab'; // no warning

-isystem-prefix 和 -ino-system-prefix 命令行参数可以被用来重载一个include路径的子集作为系统头文件。当名称在一个#include指令被发现在一个头文件搜索路径并且具有system前缀,这个头文件就被当作一个系统头文件。最后一个后缀在命令行上匹配指定的头文件名。如下:

  1. $ clang -Ifoo -isystem bar -isystem-prefix x/ -ino-system-prefix x/y/

这里,#include “x/a.h” 被当作包含一个系统头文件,即使这个文件在foo中被找到, #include “x/y/b.h” 不被当作系统头文件,即使这个文件在bar中。

一个 #include 指令找到一个文件相对于当前目录被当作系统头文件,如果包含文件被当作系统头文件。

使能所有警告

作为对传统-W标志的附加,可以通过传递-Weverything使能所有警告。这个工作和使用-Werror预期相同,并且也包含从-pedantic而来的警告。

注意当组合-w(禁用所有警告)时,起作用的是这个标志。

控制静态分析器诊断

虽然不是编译器的严格部分,Clang的静态分析器产生的诊断也可以被用户通过改变源代码影响。更多信息见于可用的注释和分析器的FAQ页面。

预编译的头文件

预编译的头文件是一种很多编译器都使用来减少编译时间的通用方式。该方法的基本动机是相同的头文件(并且通常很大)被包含进不同的源文件,是很常见的。结果,编译时间通常可以被很大成都的提高,通过抓住一些编译器处理头文件的(多余的)工作。预编译的头文件, 代表了很多种方式中的一种来实现这种优化, 是逐字文件代表一个磁盘上的包含大量减少处理对应的头文件的相同工作的信息缓存。每个编译器的预编译的头文件细节都不相同,预编译的头文件在具有大量头文件的系统上加速程序的编译是非常高效的(例如 Mac OS/X)。

生成PCH文件

使用Clang生成一个PCH文件,可以使用-x -header 选项调用Clang。这映射到GCC中生成PCH文件的接口:

  1. $ gcc -x c-header test.h -o test.h.gch
  2. $ clang -x c-header test.h -o test.h.pch

使用PCH文件

一个PCH文件可以被用作一个前缀头文件,当一个-include选项被传递到clang时:

  1. $ clang -include test.h test.c -o test

clang驱动将会首先检查是否一个PCH文件test.h可用;如果是,test.h 的内容 (和它包含的文件) 将被从这个PCH文件处理。否则, Clang 回溯到直接处理 test.h 的内容。这映射到GCC的行为。


  1. 注意


    Clang不会自动使用源文件中直接包含的PCH文件。举例如下:


  2. $ clang -x c-header test.h -o test.h.pch
    $ cat test.c

  3. #include "test.h"
  4. $ clang test.c -o test

  5. 在这个例子中,clang将不会自动使用PCH文件test.h,由于test.h被直接包含进源文件并没有使用-include指令在命令行上指定。

重定位PCH文件

有时候需要从还不是最终头文件位置的头文件创建一个预编译的头文件。举个例子,有人可能像创建一个预编译的头文件在编译树,然后想要安装头文件。Clang允许创建“relocatable” 预编译的头文件,使用一个给定的位置(在编译目录中),后边可以从一个安装的位置使用。

创建一个可重定位的预编译的头文件,把你的文件放进一个模拟安装位置的子目录。举个例子,你想要创建一个可重定位的预编译的头文件mylib.h,它将会被安装到/usr/include,那么创建一个子目录build/usr/include 并放 mylib.h 进这个子目录。如果 mylib.h 取决于其他头文件,它们可以存储在 build/usr/include 中模拟安装位置。

创建一个可重定位的预编译的头文件需要两个附加参数,首先,传递 —relocatable-pch 标志来预示结果PCH文件应当可重定位。第二,传递 -isysroot /path/to/build,它将使得所有你的库依赖到编译目录。举个例子:

  1. # clang -x c-header --relocatable-pch -isysroot /path/to/build /path/to/build/mylib.h mylib.h.pch

在加载可重定位PCH文件的时候,在PCH文件中使用的各种头文件将在系统头文件根目录找到。举个例子,mylib.h可以在/usr/include/mylib.h找到。如果头文件安装在其他系统根目录,-isysroot选项可以用来提供一个不同的系统根目录来找头文件。举个例子,-isysroot /Developer/SDKs/MacOSX10.4u.sdk 将会在/Developer/SDKs/MacOSX10.4u.sdk/usr/include/mylib.hmylib.h

可重定位预编译的头文件被用于有限的情形,编译环境被高度控制并且预编译的头文件不能在头文件被安装之后生成。

控制代码生成

Clang提供了很多种方式来控制代码生成。选项列表如下:

-fsanitize=check1,check2,…

开启运行时对于多种未定义的或者可疑的行为进行检查。

这个选项控制Clang是否添加运行时对于多种未定义的或者可疑的行为进行检查,默认是关闭的。如果一个检查失败,将会有一个运行时的一个诊断信息生成来解释这个问题。主要检查有:

  • -fsanitize=address: AddressSanitizer, 一个内存错误检查器。
  • -fsanitize=init-order: 使得 AddressSanitizer 检查动态初始化顺序问题。Implied by -fsanitize=address.
  • -fsanitize=address-full: AddressSanitizer 具有所有下面所列的实验性质的特性。
  • -fsanitize=integer: 使能检查未定义的或者可疑的整数行为。
  • -fsanitize=thread: ThreadSanitizer, 一个数据竞争检测器。
  • -fsanitize=memory: MemorySanitizer, 一个实验性质的未初始化读检查器。还不适合广泛使用。
  • -fsanitize=undefined: 快速的,兼容的未定义行为监测器。使能未定义行为检测,具有很小的运行环境开销并对地址空间布局或ABI没有影响。这包含了下面所列的所有的检测而不单单是 unsigned-integer-overflow 。
  • -fsanitize=undefined-trap: 这包含所有被 -fsantiize=undefined 包含的 sanitizers, 除了那些需要运行环境支持的。这一组的 sanitizers 通常与 -fsanitize-undefined-trap-on-error 标志结合使用, 将会导致陷阱被触发, 而不是到运行库的调用。这包含了下面所列的所有检查而不单单是 unsigned-integer-overflow 和 vptr 。

下面是更多的更细的可用检查:

  • -fsanitize=alignment: 使用一个未对齐的指针或者引用。
  • -fsanitize=bool: 加载一个既不是真也不是假的bool值。
  • -fsanitize=bounds: 数组索引越界, 以防数组边界可以静态检测。
  • -fsanitize=enum: 加载一个枚举类型的值,但是值不在那个枚举类型范围内。
  • -fsanitize=float-cast-overflow: 转换到, 从, 或者浮点类型之间,其目标可能会溢出。
  • -fsanitize=float-divide-by-zero: 浮点除零。
  • -fsanitize=integer-divide-by-zero: 整数除零。
  • -fsanitize=null: 使用一个空指针或者创建一个空引用。
  • -fsanitize=object-size: 尝试使用优化器可以探测到不属于访问对象的字节。 对象的大小使用 __builtin_object_size 检测, 并且结果可能会探测到多个问题在高层次的优化。
  • -fsanitize=return: 在 C++ 中, 到达一个具有返回值类型函数的末尾而没有返回值。
  • -fsanitize=shift: 移位操作符的移位大小超过了位宽或者小于零,或者左边是负值。 对于有符号数移位, 检查C中的有符号溢出,在C++中检查无符号溢出。
  • -fsanitize=signed-integer-overflow: 有符号整数溢出, 包含所有通过 -ftrapv 添加的检查, 并且检查有符号除法溢出 (INT_MIN / -1)。
  • -fsanitize=unreachable: 如果控制流到达 __builtin_unreachable.
  • -fsanitize=unsigned-integer-overflow: 无符号整数溢出。
  • -fsanitize=vla-bound: 可变长数组边界值非正。
  • -fsanitize=vptr: 使用一个vptr预示着具有错误动态类型的对象,或者它的生命长度还未开始或者已经结束。与 -fno-rtti 兼容。

AddressSanitizer 的实验性质的特性(还未准备好被广泛使用, 需要明确指定 -fsanitize=address):

  • -fsanitize=use-after-return: 检查 use-after-return 错误 (在函数退出之后访问局部变量)。
  • -fsanitize=use-after-scope: 检查 use-after-scope 错误 (在有效域范围外访问局部变量)。

MemorySanitizer 的额外特性(需要明确指定 -fsanitize=memory):

  • -fsanitize-memory-track-origins: 使能MemorySanitizer中的原始跟踪。 加上一个额外的区域到 MemorySanitizer 报告指向堆或者栈分配未初始化位的来源。执行速度减慢 1.5x-2x.

为了连接到合适的运行库,连接时必须提供 -fsanitize= 参数。不可能在相同的程序中结合 -fsanitize=address 与 -fsanitize=thread 检查。

-f[no-]address-sanitizer

过时的 -f[no-]sanitize=address 的同义词。

-f[no-]thread-sanitizer

过时的 -f[no-]sanitize=thread 的同义词。

-fcatch-undefined-behavior

过时的 -fsanitize=undefined 的同义词。

-fno-assume-sane-operator-new

不要假定C++的新操作是健壮的。

这个选项告诉编译器不要假定C++全局新操作符将会一直返回一个指针而不是其他指针的别名,在函数返回的时候。

-ftrap-function=[name]

指令代码生成器发出一个函数调用到指定的函数名称 __builtin_trap()。

LLVM代码生成器翻译 __builtin_trap() 到一个陷阱指令,如果被目标ISA支持的话。否则,内建被翻译为到abort的调用。如果这个选项被设置,代码生成器将会一直使用内建到一个指定函数而不论是否目标ISA具有陷阱指令。这个选项对于那些不能正确处理陷阱,或者需要定制行为的环境(例如深层嵌入)。

-ftls-model=[model]

选择要使用的TLS模式。

有效值为: global-dynamic, local-dynamic, initial-exec 和 local-exec. 默认值为 global-dynamic。 编译器可能会使用一个不同的模式,如果选择的模式不被目标支持或者具有更具效率的模式可用。TLS模式可以通过每个变量使用tls_model属性来重载。

控制调试信息大小

clang的调试信息类型生成可以设置为下表中的一种。如果有多个标志,使用最后一个。

-g0

不生成任何调试信息(默认)。

-gline-tables-only

只生成行号表。

这种调试信息允许使用函数名,文件名和行号进行观察栈跟踪(通过这类工具例如 gdb 或者 addr2line)。它不包含任何其他数据 (举个例子: 局部变量的描述或者函数参数)。

-g

生成完整的调试信息。

注释解析选项

Clang解析Doxygen和non-Doxygen风格的文档注释并且把它们附加到合适的声明注记。默认的,只解析Doxygen风格注释并且忽略以 // and /* 开头的普通注释。

-fparse-all-comments

解析所有注释作为文档注释(包括以// and /* 开头的普通注释)。