作为clang-format
仅重新格式化代码的工具,这种格式化是否有可能破坏工作代码或至少改变它的工作方式?是否有某种合同会/不能改变代码的工作方式?
我们有很多要格式化的代码clang-format
。这意味着,许多代码行将发生变化。不必审查仅因 a 而更改的每一行代码clang-format
将大大简化此过程。
我会说这clang-format
不会改变代码的工作方式。另一方面,我不是 100% 确定,如果这可以保证。
作为clang-format
仅重新格式化代码的工具,这种格式化是否有可能破坏工作代码或至少改变它的工作方式?是否有某种合同会/不能改变代码的工作方式?
我们有很多要格式化的代码clang-format
。这意味着,许多代码行将发生变化。不必审查仅因 a 而更改的每一行代码clang-format
将大大简化此过程。
我会说这clang-format
不会改变代码的工作方式。另一方面,我不是 100% 确定,如果这可以保证。
该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
指令会破坏您的代码,您可能应该执行以下操作之一:
在头文件中显式包含每个头的依赖项。在我的示例中,我需要包含stdint.h
在MyStruct.h
.
在显式声明排序依赖的包含组之间添加注释行。请记住,任何非#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;
}
当然,它可以改变你的代码的工作方式。原因是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
由于clang-format
仅影响空白字符,因此您可以检查 ing 之前和之后的文件clang-format
是否与空格相同。在 Linux/BSD/OS X 中,您可以使用diff
并tr
为此:
$ 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.c
和2.c
空格相同。
正如Karoly在他的评论中提到的,请注意,在理想条件下,您仍然必须检查重要的空格,例如字符串文字。但在现实世界中,我相信这个测试绰绰有余。
clang-format 在项目中重新格式化了 ASM 代码,因为我们有效地这样做了:
#define ASM _asm
ASM {
...
}
是的
它不会破坏工作流程
系统有配置开关: "C_Cpp.clang_format_sortIncludes": false,但它不起作用,我不知道出了什么问题......
我的版本是:ms-vscode.cpptools-0.13.1
这是我的解决方案:
为了稳定的工作流程,请使用以下语法:
// clang 格式关闭
...这是你的代码
// clang 格式开启
如果您在代码中使用特殊结构和格式化设置,它可能会破坏您的代码。
内联汇编器
如果您通常使用 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 所做的更改。否则,您可能会遇到隐藏的错误。
我想它不会,因为它是建立在 clang 的静态分析之上的,因此它了解代码本身的结构,而不仅仅是一个只对文本进行操作的愚蠢的源代码格式化程序(能够使用编译器库)。鉴于格式化程序使用与编译器本身相同的解析器和词法分析器,我会感到足够安全,因为它不会有任何问题吐出与您提供的行为相同的代码。
您可以在此处查看 C++ 格式化程序的源代码:http: //clang.llvm.org/doxygen/Format_8cpp_source.html