Может ли clang-format разбить мой код?

Как clang-format является инструментом только для форматирования кода, возможно ли, что такое форматирование может нарушить рабочий код или хотя бы изменить его работу? Есть ли какой-то контракт, который он/не может изменить, как работает код?

У нас есть много кода, который мы хотим отформатировать с помощью clang-format. Это означает, что многие строки кода будут меняться. Невозможность пересмотреть каждую строку кода, которая изменилась только из-за clang-format, будет большим упрощением этого процесса.

Я бы сказал, что clang-format не изменит работу кода. С другой стороны, я не уверен на 100%, если это можно гарантировать.

Ответы

Ответ 1

Краткий ответ: ДА.


Инструмент clang-format имеет параметр -sort-includes. Изменение порядка директив #include может определенно изменить поведение существующего кода, а может нарушить существующий код.

Так как соответствующая опция SortIncludes установлена ​​на true несколькими встроенными стилями, может быть не очевидно, что clang-format собирается изменить порядок ваших включений.

MyStruct.h:

struct MyStruct {
    uint8_t value;
};

original.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.

фиксированной original.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 до стандартных заголовков библиотек.

чередуются-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;
}

Ответ 2

Так как clang-format влияет только на символы пробела, вы можете проверить, что файлы до и после 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, упомянутый в его комментарии, обратите внимание, что в идеальных условиях вам все равно нужно проверять пробелы, которые имеют значение, например. строковые литералы. Но в реальном мире я считаю, что этого теста более чем достаточно.

Ответ 3

Конечно, это может изменить способ работы вашего кода. И причина в том, что программа 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

Ответ 4

clang-format переформатировал код ASM в проекте, потому что мы эффективно это сделали:

#define ASM _asm

ASM {

  ...

}

Ответ 5

да

он не нарушит рабочий поток

система имеет переключатель конфигурации: "C_Cpp.clang_format_sortIncludes": false, но он не работает, я не знаю, что не так...

моя версия: ms-vscode.cpptools-0.13.1

это мое решение:

для устойчивого рабочего потока используйте грамматику:

//clang-format off

... вот ваш код

//clang-format на

Ответ 6

Я предполагаю, что это не так, учитывая, что он построен на статическом анализе клана и, следовательно, имеет представление о самой структуре кода, а не о простом неформальном форматировании исходного кода, который работает только с текстом (одно из благ возможность использования библиотеки компилятора). Учитывая, что форматтер использует тот же синтаксический анализатор и лексер, что и сам компилятор, я бы чувствовал себя достаточно безопасно, чтобы у него не было проблем с выделением кода, который ведет себя так же, как то, что вы его кормите.

Здесь вы можете увидеть исходный код форматирования С++: http://clang.llvm.org/doxygen/Format_8cpp_source.html