预处理前后 C 或 C++ 代码中行号:列号之间的映射

Mapping between line:column numbers in C or C++ code before and after preprocessing

提问人:Bass 提问时间:10/27/2023 更新时间:10/27/2023 访问量:62

问:

当 C 或 C++ 源文件中出现语法错误时,GCCClang 都会报告错误所在位置的确切行和列。例如,如果我们尝试编译以下代码片段:

#include <stdio.h>

static void f(char *arg, int ignored) {
        printf("%s\n", arg);
}

#define FUNCTION(x) f(x)

#define ARG "test"

int main() {
        FUNCTION(ARG);
        return 0;
}

— 然后 GCC 将在(宏正文)处报告错误:7:21

a.c: In function ‘main’:
a.c:7:21: error: too few arguments to function ‘f’
    7 | #define FUNCTION(x) f(x)
      |                     ^
a.c:12:9: note: in expansion of macro ‘FUNCTION’
   12 |         FUNCTION(ARG);
      |         ^~~~~~~~
a.c:3:13: note: declared here
    3 | static void f(char *arg, int ignored) {
      |

而 Clang 将指向宏调用站点 ():12:2

a.c:12:2: error: too few arguments to function call, expected 2, have 1
        FUNCTION(ARG);
        ^~~~~~~~~~~~~
a.c:7:24: note: expanded from macro 'FUNCTION'
#define FUNCTION(x) f(x)
                    ~  ^
a.c:3:13: note: 'f' declared here
static void f(char *arg, int ignored) {

不同编译器风格和版本的输出可能略有不同,但 GCC 和 Clang 或多或少是一致的。

现在,据我了解,每当调用编译器驱动程序(如 or)时,都会首先运行预处理器 (, or , or ),并且特定于语言的编译器前端处理已经预处理的代码。gccclang++gcc -Eclang -Ecpp

然而,如果我们看一下预处理的代码,它确实会有大量的注释,但原始文件中只有号,完全缺少列号信息。例:

# 885 "/usr/include/stdio.h" 3 4
extern int __uflow (FILE *);
extern int __overflow (FILE *, int);
# 909 "/usr/include/stdio.h" 3 4

# 2 "a.c" 2


# 3 "a.c"
static void f(char *arg, int ignored) {
 printf("%s\n", arg);
}

因此,从上面的内容来看,我们可以推断出实际上声明为 at(这是真的),并且 starts at 的定义(这也是真的)。您还可以看到代码已重新格式化(缩进级别已更改)。此处更详细地介绍了预处理器输出的格式。__uflow(...)/usr/include/stdio.h:885f(...)a.c:3

现在的问题是:在向用户报告反馈时,编译器前端究竟如何在预处理代码和原始代码之间执行位置转换,因为预处理器输出显然包含的信息不足?

如果第三方工具(例如:静态分析器)只能访问原始代码和预处理代码(或者预处理代码的 AST),它如何执行相同的操作?

是否可以使用 Liblang 的 API?

gcc clang c-preprocessor libclang

评论

1赞 Scott McPeak 10/27/2023
这个问题有点太宽泛了,无法按原样回答。核心是预处理器是“先运行”的误解,这里消除了这种误解。这个问题还询问了列信息,但对于两个编译器,两者的答案并不相同。它还询问如何从 libclang 获取信息,这应该是一个单独的问题。我建议通过询问一个编译器如何跟踪宏/列信息,或者只询问 libclang 来缩小问题范围。(请注意,Clang 有两个 API:用于 C 的 libclang,用于 C++ 的 libtooling。
0赞 Bass 10/28/2023
@ScottMcPeak,你是对的,谢谢。直到现在,我才意识到 Libclang 实际上在原始代码中报告了位置(在运行预处理器之前)。让我们结束这个问题。

答: 暂无答案