clang-format可以破解我的代码吗?

5us2dqdw  于 2023-05-16  发布在  其他
关注(0)|答案(8)|浏览(148)

由于clang-format是一个只重新格式化代码的工具,这种格式化是否可能会破坏工作代码或至少改变它的工作方式?是否存在某种契约,它将/不能改变代码的工作方式?
我们有很多代码要用clang-format化。这意味着许多代码行将发生变化。不需要检查每一行只因为clang-format而改变的代码,这将大大简化这个过程。
clang-format不会改变代码的工作方式。我不是100%确定,但这是可以保证的。

nkkqxpd9

nkkqxpd91#

简答:是的。

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

restyled.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行。

fixed-original.c:

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

#include "MyStruct.h"

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

固定-重新设置样式。c:

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

#include "MyStruct.h"

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

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

然而...

如果重新排序#include指令会破坏代码,那么您可能还是应该执行以下操作之一:
1.在头文件中显式包含每个头的依赖项。在我的示例中,我需要在MyStruct.h中包含stdint.h
1.在包含组之间添加一个注解行,显式地声明排序依赖项。请记住,任何非#include行都应该将组分开,因此注解行也可以工作。下面代码中的注解行还阻止clang-format在标准库头文件之前包含MyStruct.h

alternate-original.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;
}
bgibtngc

bgibtngc2#

当然,它可以改变你的代码的工作方式。原因是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
z4bn682m

z4bn682m3#

由于clang-format只影响空白字符,因此您可以检查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在他的评论中提到的,请注意,在理想情况下,您仍然必须检查重要的空格,例如。字符串文字。但在真实的世界中,我相信这个测试已经足够了。

xxhby3vn

xxhby3vn4#

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

#define ASM _asm

ASM {

  ...

}
vktxenjb

vktxenjb5#


不会中断工作流程

系统具有配置开关:**“C_Cpp.clang_format_sortIncludes”:我不知道是什么原因,我也不知道该怎么办。
我的版本是:ms-vscode.cpptools-0.13.1
这是我的解决方案:
对于稳定的工作流程,使用语法:
// clang-format off
。。。这是你的密码
// clang-format on

gt0wga4j

gt0wga4j6#

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

内联汇编器

如果你通常使用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所做的更改。否则,您可能会遇到隐藏的错误。

v2g6jxz6

v2g6jxz67#

是的,很不幸。在使用clang-format多年之后,今天我面对的是this issue。我的用例与bug中描述的用例类似:

int main() {
  int x = 0;
  int y = 0;
  return x < y - 1 >> 1;
}

可以重新格式化为:

int main() {
  int x = 0;
  int y = 0;
  return x < y - 1 > > 1; /*<< Code is broken*/
}

幸运的是,它已经被修复,工具更新帮助了!

iq0todco

iq0todco8#

我想它不会,因为它是建立在clang的静态分析之上的,因此知道代码本身的结构,而不仅仅是一个只对文本进行操作的愚蠢的源代码格式化程序(能够使用编译器库的好处之一)。假定格式化程序使用与编译器本身相同的解析器和词法分析器,我会感到足够安全,它不会有任何问题,吐出的代码与您提供给它的代码行为相同。
你可以在这里看到C++格式化程序的源代码:http://clang.llvm.org/doxygen/Format_8cpp_source.html

相关问题