clang-format与cpplint配合使用导致cpplint头文件包含顺序错误的解决咨询
嗨,我来帮你搞定这个clang-format和cpplint打架的问题~ 你遇到的情况其实很典型:两个工具对「头文件包含顺序」的规则理解不一样,clang-format自动调整后的顺序刚好踩中了cpplint的检查红线。下面我先结合你的具体情况拆解问题,再给出几个实用的解决办法:
问题根源
先理清楚你遇到的具体矛盾:
你的原始头顺序是符合cpplint要求的:
#include "header/doc.hpp" #include "header/node.hpp" #include <string> #include <cstdint> #include <catch2/catch_test_macros.hpp>
但clang-format默认会把所有用<>包裹的头(包括catch2这类第三方库头)都归为「系统头」,并把它们排在""包裹的项目头前面,还会把C系统头、C++系统头和第三方库头混排,最终输出的顺序是:
#include <catch2/catch_test_macros.hpp> #include <cstdint> #include <string> #include "header/doc.hpp" #include "header/node.hpp"
而cpplint的头顺序规则是:自身头文件 → C系统头 → C++系统头 → 其他头(包括第三方库、项目其他模块头),它认为catch2属于「其他头」应该放在系统头之后,所以直接抛出了build/include_order错误:
cpplint..................................................................Failed - hook id: cpplint - exit code: 1 tests/test_main.cpp:6: Found C++ system header after other header. Should be: test_main.h, c system, c++ system, other. [build/include_order] [4] tests/test_main.cpp:7: Found C++ system header after other header. Should be: test_main.h, c system, c++ system, other. [build/include_order] [4] Total errors found: 2
具体解决办法
方案1:修改clang-format配置,对齐cpplint的头顺序规则
这是从根源解决的方案,让clang-format直接按照cpplint的要求排序头文件。你需要在项目根目录的.clang-format文件中添加/修改以下配置:
# 定义头文件分类及优先级(数字越小,排序越靠前) IncludeCategories: # 优先级1:当前cpp对应的自身头文件(比如test_main.cpp对应test_main.h) - Regex: '^"(test_main\.h|.*\.h)$' Priority: 1 # 优先级2:C系统头(比如<cstdint>、<stdio.h>这类C标准库头) - Regex: '^<(c[^+].*|.*\.h)>$' Priority: 2 # 优先级3:C++系统头(比如<string>、<vector>这类C++标准库头) - Regex: '^<(string|vector|iostream|.*)>$' Priority: 3 # 优先级4:第三方库头(比如catch2)和项目其他模块的私有头 - Regex: '^<catch2/.*>$' Priority: 4 - Regex: '^".*"' Priority: 4 # 识别当前cpp对应的主头文件的规则(适配测试文件命名,比如test_main.cpp) IncludeIsMainRegex: '([-_](test|unittest))?$' # 开启头文件自动排序 SortIncludes: true
配置完成后,重新跑pre-commit run --all-files,clang-format就会按照cpplint认可的顺序排序头文件,两个工具就不会再打架了。
方案2:修改cpplint配置,兼容clang-format的排序结果
如果你不想动clang-format的配置,可以让cpplint放宽规则,把第三方库头归为系统头:
在项目根目录创建CPPLINT.cfg文件,添加以下内容:
# 把catch2这类第三方库头识别为C++系统头,允许排在项目头前面 build/include_order:system_headers=catch2/.*
这样cpplint就会把<catch2/catch_test_macros.hpp>视为C++系统头,不再因为它的位置报错,完美兼容clang-format的默认排序结果。
如果只是想临时忽略单个文件的头顺序检查,也可以在test_main.cpp的头文件块前后加注释:
// NOLINTBEGIN(build/include_order) #include <catch2/catch_test_macros.hpp> #include <cstdint> #include <string> #include "header/doc.hpp" #include "header/node.hpp" // NOLINTEND(build/include_order)
cpplint会跳过这段代码的头顺序检查,不过这个方案只适合临时救急,不推荐长期使用。
方案3:禁用其中一个工具的头顺序检查
如果你的项目对其中一个工具的规则没那么严格,也可以直接关闭对应检查:
- 关闭clang-format的头排序:在
.clang-format里设置SortIncludes: false,这样它就不会乱动你的头顺序了 - 关闭cpplint的头顺序检查:在
CPPLINT.cfg里加filter=-build/include_order,不过这个是全局关闭,会失去cpplint对头顺序的检查能力,谨慎使用。
验证方法
修改配置后,你可以手动跑以下命令快速验证:
# 用clang-format格式化文件 clang-format -i tests/test_main.cpp # 用cpplint检查 cpplint tests/test_main.cpp
如果没有报错,就说明配置生效了,再跑pre-commit钩子应该就能全通过了。
内容来源于stack exchange

