35

作为clang-format仅重新格式化代码的工具,这种格式化是否有可能破坏工作代码或至少改变它的工作方式?是否有某种合同会/不能改变代码的工作方式?

我们有很多要格式化的代码clang-format。这意味着,许多代码行将发生变化。不必审查仅因 a 而更改的每一行代码clang-format将大大简化此过程。

我会说这clang-format不会改变代码的工作方式。另一方面,我不是 100% 确定,如果这可以保证。

4

7 回答 7

73

简短的回答:是的。


clang-format工具有一个-sort-includes选项。更改#include指令的顺序肯定会改变现有代码的行为,并可能破坏现有代码。

由于相应的SortIncludes选项是true由几个内置样式设置的,因此clang-format重新排序您的包含可能并不明显。

MyStruct.h:

struct MyStruct {
    uint8_t value;
};

原始.c:

#include <stdint.h>
#include <stddef.h>
#include "MyStruct.h"

int main (int argc, char **argv) {
    struct MyStruct s = { 0 };
    return s.value;
}

现在假设我们运行clang-format -style=llvm original.c > restyled.c.

重新设计的.c:

#include "MyStruct.h"
#include <stddef.h>
#include <stdint.h>

int main(int argc, char **argv) {
  struct MyStruct s = {0};
  return s.value;
}

由于头文件的重新排序,编译时出现以下错误restyled.c

In file included from restyled.c:1:
./MyStruct.h:2:5: error: unknown type name 'uint8_t'
    uint8_t value;
    ^
1 error generated.

但是,这个问题应该很容易解决。您不太可能有这样的顺序依赖包含,但如果您这样做,您可以通过在需要特定顺序的标题组之间放置一个空行来解决问题,因为显然clang-format#include对没有非#include行的指令组进行排序之间。

固定原始.c:

#include <stdint.h>
#include <stddef.h>

#include "MyStruct.h"

int main (int argc, char **argv) {
    struct MyStruct s = { 0 };
    return s.value;
}

固定restyled.c:

#include <stddef.h>
#include <stdint.h>

#include "MyStruct.h"

int main(int argc, char **argv) {
  struct MyStruct s = {0};
  return s.value;
}

请注意,stdint.h并且stddef.h仍然重新排序,因为它们的包含仍然是“分组”的,但是新的空白行在MyStruct.h标准库包含之前被阻止移动。


然而...

如果重新排序#include指令会破坏您的代码,您可能应该执行以下操作之一:

  1. 在头文件中显式包含每个头的依赖项。在我的示例中,我需要包含stdint.hMyStruct.h.

  2. 在显式声明排序依赖的包含组之间添加注释行。请记住,任何非#include行都应该分解一个组,因此注释行也可以。以下代码中的注释行也防止在标准库头文件之前clang-format包含。MyStruct.h

替代原始.c:

#include <stdint.h>
#include <stddef.h>
// must come after stdint.h
#include "MyStruct.h"

int main (int argc, char **argv) {
    struct MyStruct s = { 0 };
    return s.value;
}
于 2016-06-23T18:21:47.193 回答
11

当然,它可以改变你的代码的工作方式。原因是C程序可以查看其源代码的一些属性。我在想的是__LINE__宏观,但我不确定有没有其他方法。

考虑1.c

#include <stdio.h>
int main(){printf("%d\n", __LINE__);}

然后:

> clang 1.c -o 1.exe & 1.exe
2

现在做一些clang-format

> clang-format -style=Chromium 1.c >2.c

并且2.c是:

#include <stdio.h>
int main() {
  printf("%d\n", __LINE__);
}

当然,输出也发生了变化:

> clang 2.c -o 2.exe & 2.exe
3
于 2016-06-21T06:44:24.097 回答
4

由于clang-format仅影响空白字符,因此您可以检查 ing 之前和之后的文件clang-format是否与空格相同。在 Linux/BSD/OS X 中,您可以使用difftr为此:

$ diff --ignore-all-space <(tr '\n' ' ' < 2.c ) <(tr '\n' ' ' < 1.c)

1.c:

#include <stdio.h>
int main() {printf("Hello, world!\n"); return 0;}

2.c:

#include <stdio.h>
int main() {
    printf("Hello, world!\n");
    return 0;
}

命令的输出diff为空,这意味着文件1.c2.c空格相同。

正如Karoly在他的评论中提到的,请注意,在理想条件下,您仍然必须检查重要的空格,例如字符串文字。但在现实世界中,我相信这个测试绰绰有余。

于 2016-06-20T16:48:28.227 回答
3

clang-format 在项目中重新格式化了 ASM 代码,因为我们有效地这样做了:

#define ASM _asm

ASM {

  ...

}
于 2017-07-01T07:31:26.160 回答
1

是的

它不会破坏工作流程

系统有配置开关: "C_Cpp.clang_format_sortIncludes": false,但它不起作用,我不知道出了什么问题......

我的版本是:ms-vscode.cpptools-0.13.1

这是我的解决方案:

为了稳定的工作流程,请使用以下语法:

// clang 格式关闭

...这是你的代码

// clang 格式开启

于 2017-10-07T02:47:27.003 回答
0

如果您在代码中使用特殊结构和格式化设置,它可能会破坏您的代码。

内联汇编器

如果您通常使用 gcc 编译代码并使用 gcc 样式的内联汇编程序,clang-format 很可能会破坏寄存器变量的命名,因为它将 % 字符视为运算符。

asm_movq(%[val2], %%mm0)

将被重新格式化为

asm_movq(% [val2], % % mm0)

这将不再编译。

在宏中构造路径

如果您使用宏而不使用字符串来构建路径,clang-format 将再次将“/”字符视为运算符并在其周围放置空格。

Boost 例如使用这样的结构:

#   define AUX778076_PREPROCESSED_HEADER \
    BOOST_MPL_CFG_COMPILER_DIR/BOOST_MPL_PREPROCESSED_HEADER

构造头文件的路径。'/' 在这里不是运算符,但由于它不在字符串中,clang-format 将其视为运算符并在其周围放置空格,从而创建不同的路径。头文件的包含显然会失败。

结论

是的,clang-format可以破坏您的代码。如果您正在使用非常特殊的结构,这些结构是边缘情况或语言标准之外的,或者只是您非常特定的编译器(不是 clang)的扩展,那么您将需要检查 clang-format 所做的更改。否则,您可能会遇到隐藏的错误。

于 2021-09-30T07:44:33.183 回答
-5

我想它不会,因为它是建立在 clang 的静态分析之上的,因此它了解代码本身的结构,而不仅仅是一个只对文本进行操作的愚蠢的源代码格式化程序(能够使用编译器库)。鉴于格式化程序使用与编译器本身相同的解析器和词法分析器,我会感到足够安全,因为它不会有任何问题吐出与您提供的行为相同的代码。

您可以在此处查看 C++ 格式化程序的源代码:http: //clang.llvm.org/doxygen/Format_8cpp_source.html

于 2016-06-20T16:43:08.403 回答