ClangFormat
相关工具推荐
| 工具 | 说明 |
|---|---|
| Clang-Tidy | C/C++ 静态分析与代码检查,与 ClangFormat 构成代码质量双支柱 |
| Cppcheck | 独立的 C/C++ 静态分析工具,无需编译即可检查 |
| CodeQL | GitHub 语义代码分析平台,支持 C/C++ 安全与质量分析 |
1. 简介
ClangFormat 是 LLVM/Clang 工具链中的代码自动格式化工具,是 C/C++ 代码格式化领域的事实标准。它基于 Clang 编译器前端的抽象语法树(AST)分析能力,能够语义感知地格式化代码,而非简单的文本替换。这使得它能准确识别变量声明、函数调用、模板参数、宏展开上下文等复杂语言结构,并做出符合语义逻辑的排版决策。
- 主要检查语言:C、C++、Objective-C、Java、JavaScript、JSON、Protobuf、C#
- 主要检查能力:格式化 C/C++/Objective-C/Java/JavaScript/JSON/Protobuf/C# 等多种语言的代码(缩进、换行、对齐);涵盖代码格式化
- 核心检查原理:基于 Clang AST 解析,提供 LLVM、Google、Chromium、Microsoft、WebKit、Mozilla、GNU 7 种内置风格预设
- 检查规则/选项:约 100+ 项可配置格式化选项(如 ColumnLimit、IndentWidth、PointerAlignment 等),并内置 LLVM、Google、Chromium、Mozilla、WebKit 等预定义风格,全量选项列表
- GitHub 仓库:https://github.com/llvm/llvm-project(LLVM 子项目,38,776 stars,LLVM 主仓库,ClangFormat 无独立仓库)
- 开源协议:Apache 2.0 License with LLVM Exceptions
- 最新稳定版本:随 LLVM 发布
- 运行环境要求:需 LLVM/Clang 工具链;随 LLVM 版本发布
- 误报率:无
2. 官方文档
| 资源 | 链接 | 说明 |
|---|---|---|
| ClangFormat 官方文档 | https://clang.llvm.org/docs/ClangFormat.html | 工具使用说明、命令行参数、编辑器集成指南、Git 集成、.clang-format-ignore 文件说明 |
| 格式化风格选项完整文档 | https://clang.llvm.org/docs/ClangFormatStyleOptions.html | 所有可配置选项的详细说明(200+ 选项),含示例代码 |
| LLVM 官方下载页 | https://releases.llvm.org/ | 各平台预编译二进制包下载 |
| LLVM GitHub 仓库 | https://github.com/llvm/llvm-project | 源码仓库(含 clang-format 源码) |
| ClangFormat 配置器(在线) | https://clang-format-configurator.site/ | 交互式 .clang-format 配置生成工具 |
3. 社区优秀实践
3.1 LLVM 项目
LLVM 是 ClangFormat 的开发项目,ClangFormat 的默认风格(LLVM Style)即源自此项目的编码规范。LLVM 项目在根目录维护 .clang-format 文件,所有 C/C++ 代码提交必须通过格式化检查。
- 仓库地址:https://github.com/llvm/llvm-project
- 配置文件:项目根目录下的
.clang-format,使用BasedOnStyle: LLVM作为基础 - 实践特点:
- 使用 2 空格缩进
- 大括号采用 Attach 风格(K&R 变种)
- 列宽限制 80 字符
- 通过 CI 自动检查格式合规性
3.2 Chromium 项目
Chromium 是 Google Chrome 的开源基础,是 ClangFormat 在工业级项目中应用的标杆。Chromium 通过 depot_tools 工具集内置了 clang_format_merge_driver,在代码合并时自动格式化,确保代码风格一致性。
- 仓库地址:https://chromium.googlesource.com/chromium/src
- 配置文件:项目根目录下的
.clang-format,使用BasedOnStyle: Chromium - 实践特点:
- 使用
clang_format_merge_driver作为 Git merge driver,在合并代码时自动格式化冲突区域 - 通过
depot_tools统一管理 clang-format 版本 - CI 中强制执行格式检查,不合规的 CL 无法提交
- 使用
3.3 WebKit 项目
WebKit 是 Apple Safari 和其他浏览器使用的渲染引擎,其编码风格同样通过 ClangFormat 来强制执行。
- 仓库地址:https://github.com/WebKit/WebKit
- 配置文件:项目根目录下的
.clang-format,使用BasedOnStyle: WebKit - 实践特点:
- 使用 WebKit 内置风格(基于 Attach 风格,函数定义大括号换行)
- Tab 缩进(
UseTab: ForIndentation) - 列宽限制 120 字符
- 通过 CI 自动格式化检查
3.4 Linux Kernel
Linux Kernel 从 6.11 版本开始正式引入 clang-format 支持,在 Documentation/dev-tools/clang-format.rst 中记录了使用指南。
- 仓库地址:https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
- 配置文件:项目根目录下的
.clang-format - 实践特点:
- 使用
git clang-format仅格式化变更行,避免对历史代码的大规模改动 - 配置文件尽量逼近内核编码风格
- 支持子目录覆盖:不同子系统可在子目录放置独立的
.clang-format - 主要用于代码审查辅助和风格错误发现,而非强制全量格式化
- 使用
4. 工具配置说明
4.1 配置文件说明
.clang-format 文件采用 YAML 格式。
| 配置文件 | 用途 | 使用场景 |
|---|---|---|
.clang-format |
代码格式化规则配置 | 放置在项目根目录,从当前文件目录向上逐级查找 |
_clang-format |
同 .clang-format(Windows 兼容文件名) |
Windows 环境下使用 |
.clang-format-ignore |
格式化排除规则 | 排除第三方库、生成代码等无需格式化的文件 |
文件查找与继承机制:
- 从当前文件所在目录向上逐级查找
.clang-format文件 - 子目录可放置独立的
.clang-format覆盖父级配置 - 通过
---分隔符和Language:字段为不同语言设置不同规则 BasedOnStyle指定基础风格,再覆盖特定选项
配置文件查找优先级(从高到低):
- 命令行
-style=file:<path>显式指定 - 当前文件所在目录的
.clang-format - 父级目录的
.clang-format(逐级向上查找) -fallback-style指定的回退风格(默认为 LLVM)
4.2 .clang-format 配置详解
配置项说明:
| 配置项 | 类型 | 说明 |
|---|---|---|
BasedOnStyle |
string | 基础风格(LLVM、Google、Chromium、Microsoft、WebKit、Mozilla、GNU) |
IndentWidth |
int | 缩进宽度(空格数) |
ColumnLimit |
int | 单行最大字符数,超过则自动换行 |
Language |
string | 指定配置适用的语言(Cpp、JavaScript、Proto、CSharp 等),用于多语言配置分隔 |
DisableFormat |
bool | 是否禁用格式化 |
PointerAlignment |
string | 指针对齐方式(Left、Right、Middle) |
DerivePointerAlignment |
bool | 是否根据已有代码推导指针对齐方式 |
最小配置示例:
# .clang-format
BasedOnStyle: LLVM
IndentWidth: 4
ColumnLimit: 100
多语言配置示例:
---
# 默认配置(适用于所有语言)
BasedOnStyle: LLVM
IndentWidth: 4
---
Language: Cpp
# C++ 特定配置
PointerAlignment: Left
DerivePointerAlignment: false
---
Language: JavaScript
# JavaScript 特定配置
ColumnLimit: 100
---
Language: Proto
# 不格式化 .proto 文件
DisableFormat: true
---
Language: CSharp
ColumnLimit: 100
文件排除示例:
ClangFormat 支持 .clang-format-ignore 文件来排除特定文件或目录,格式类似 .gitignore:
# .clang-format-ignore
# 排除第三方库
third_party/
external/
build/
out/
# 排除生成的文件
generated/
*.pb.h
*.pb.cc
# 排除特定文件
src/vendor/special_module.cpp
支持 glob 模式(*、**)和否定模式(!):
# 排除所有第三方代码
third_party/**
# 但不排除 third_party/mylib(使用 ! 取反)
!third_party/mylib/**
ClangFormat 内置 7 种预设风格(LLVM、Google、Chromium、Microsoft、WebKit、Mozilla、GNU),可通过
BasedOnStyle指定基础风格后进行微调。推荐使用 ClangFormat 配置器 交互式生成适合团队的.clang-format配置文件。
5. 主流集成方式
5.1 构建工具集成(CMake)
CMake 是 C/C++ 项目的主流构建工具,ClangFormat 通常通过 CMake 自定义 Target 或第三方模块集成到构建流程中。
方式一:使用 CMake 自定义 Target
# CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(MyProject LANGUAGES CXX)
# 查找 clang-format
find_program(CLANG_FORMAT_EXE clang-format)
if(CLANG_FORMAT_EXE)
# 收集所有源文件
file(GLOB_RECURSE ALL_SOURCE_FILES
"${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/src/*.h"
"${CMAKE_CURRENT_SOURCE_DIR}/include/*.h"
)
# 添加格式化检查目标(只检查不修改)
add_custom_target(check-format
COMMAND ${CLANG_FORMAT_EXE} --dry-run --Werror --style=file ${ALL_SOURCE_FILES}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "Checking code formatting with clang-format"
)
# 添加格式化修复目标(自动修改)
add_custom_target(fix-format
COMMAND ${CLANG_FORMAT_EXE} -i --style=file ${ALL_SOURCE_FILES}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "Fixing code formatting with clang-format"
)
endif()
使用方式:
cmake -B build
cmake --build build --target check-format # CI 中使用:检查不合规则失败
cmake --build build --target fix-format # 本地使用:自动修复格式
方式二:使用 Format.cmake 第三方模块
Format.cmake 是一个轻量级 CMake 模块,提供开箱即用的 format、check-format、fix-format 三个 Target。
# CMakeLists.txt
include(FetchContent)
FetchContent_Declare(
format
GIT_REPOSITORY https://github.com/TheLartians/Format.cmake
GIT_TAG v2.0.0
)
FetchContent_MakeAvailable(format)
# 自动注册 format / check-format / fix-format 目标
参考文档:
5.2 pre-commit 集成
使用 pre-commit 框架集成 ClangFormat 可以在代码提交前自动格式化,适合作为本地补充门禁。C/C++ 项目仍应在 CI 或构建流程中固定 LLVM/Clang 版本并执行格式检查。
依赖说明:pre-commit 镜像仓库
pre-commit/mirrors-clang-format提供预编译的 clang-format 二进制文件(language: python,通过 setup.py 安装),无需本地安装 LLVM 工具链。但如果需要使用特定 LLVM 版本的 clang-format,建议使用language: system+ 本地安装的方式。
方式一:使用官方镜像仓库(推荐)
# .pre-commit-config.yaml
repos:
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: v22.1.5
hooks:
- id: clang-format
types_or: [c++, c, cuda]
限制只检查变更文件:pre-commit 框架默认仅将暂存区中变更文件传给 hook,无需额外配置即可实现增量检查。
方式二:使用本地系统 clang-format
# .pre-commit-config.yaml
repos:
- repo: local
hooks:
- id: clang-format
name: clang-format
entry: clang-format
language: system
types: [c++]
args: [-i, --style=file]
适用前提:此方式依赖系统中已安装的 clang-format(
language: system),需确保团队成员和 CI 环境中 clang-format 版本一致。
安装与使用:
# 安装 pre-commit
pip install pre-commit
# 安装 git hooks
pre-commit install
# 手动运行所有文件的检查
pre-commit run --all-files
参考文档:
5.3 IDE 集成
VS Code
安装 Clang-Format 扩展(xaver.clang-format),并在 settings.json 中配置:
{
"editor.formatOnSave": true,
"clang-format.executable": "${workspaceRoot}/tools/clang-format"
}
ClangFormat 扩展会自动检测项目中的
.clang-format文件并使用其配置。也可通过clang-format.fallbackStyle设置回退风格。
CLion
CLion 原生支持 ClangFormat,检测到项目根目录的 .clang-format 文件时会自动启用。
- 自动启用:项目根目录下放置
.clang-format文件即可 - 手动启用:Settings | Editor | Code Style | C++ | 勾选 "Enable ClangFormat"
- 提交时格式化:在 Commit 界面勾选 "Reformat code" 作为 Before Commit 操作
- 使用外部 clang-format:Settings | Editor | Code Style | C++ | Clang-Format | 勾选 "Use external clang-format instead of the built-in one"
Vim/Neovim
方式一:使用内置脚本
" .vimrc
if has('python3')
" 格式化当前行或选中区域
map <C-K> :py3f <path-to-clang-format.py><cr>
imap <C-K> <c-o>:py3f <path-to-clang-format.py><cr>
endif
" 保存时自动格式化
function! Formatonsave()
let l:formatdiff = 1
py3f <path-to-clang-format.py>
endfunction
autocmd BufWritePre *.h,*.cc,*.cpp call Formatonsave()
方式二:使用 neoformat 插件
" 使用 neoformat 插件
Plug 'sbdchd/neoformat'
let g:neoformat_enabled_cpp = ['clang-format']
let g:neoformat_cpp_clang_format = {
\ 'exe': 'clang-format',
\ 'args': ['-style=file', '-fallback-style=none'],
\ }
参考文档:ClangFormat 官方文档 - Vim Integration
Visual Studio
Visual Studio 原生集成 ClangFormat(版本 5.0+ 支持)。
- 默认启用:Tools | Options | Text Editor | C/C++ | Code Style | Formatting | General
- 可选风格:LLVM、Google、Chromium、Mozilla、WebKit
- 自定义配置:项目文件夹中放置
.clang-format文件即可自动识别
5.4 命令行使用方式
基本用法
# 格式化单个文件(输出到 stdout)
clang-format main.cpp
# 格式化单个文件(就地修改)
clang-format -i main.cpp
# 使用指定内置风格格式化
clang-format -style=Google main.cpp
clang-format -style=LLVM -i main.cpp
# 使用项目 .clang-format 文件格式化
clang-format -style=file -i main.cpp
# 找不到 .clang-format 时跳过格式化(而非回退到默认风格)
clang-format -style=file -fallback-style=none -i main.cpp
高级用法
# 格式化指定行范围(1-based)
clang-format -lines=10:20 main.cpp
# 格式化多个不连续的行范围
clang-format -lines=1:5 -lines=20:30 main.cpp
# 导出内置风格的完整配置
clang-format -style=llvm -dump-config > .clang-format
clang-format -style=google -dump-config > .clang-format
# 检查文件是否合规(不修改文件,有差异则返回非零退出码)
clang-format --dry-run --Werror main.cpp
# 从文件列表批量格式化
clang-format -i --files=file_list.txt
# 显示详细处理信息
clang-format -i --verbose main.cpp
批量格式化项目
# Linux/macOS:格式化项目中所有 C/C++ 文件
find . -name '*.cpp' -o -name '*.cc' -o -name '*.c' -o -name '*.h' -o -name '*.hpp' |
xargs clang-format -i --style=file
# 排除第三方库目录
find . \( -name '*.cpp' -o -name '*.cc' -o -name '*.c' -o -name '*.h' -o -name '*.hpp' \) \
-not -path '*/third_party/*' \
-not -path '*/build/*' \
-not -path '*/external/*' |
xargs clang-format -i --style=file --fallback-style=none
增量检查 -- ClangFormat 提供多种原生增量检查方式:
git clang-format(推荐) -- ClangFormat 自带的 Git 集成脚本,可仅格式化 Git 中变更的行:
# 格式化暂存区中变更的行(已 git add 的文件)
git clang-format
# 格式化最近一次提交中变更的行
git clang-format HEAD~1
# 格式化当前分支相对于 main 分支的所有变更
git clang-format main
# 仅预览差异,不实际修改文件
git clang-format --diff
# 查看变更统计
git clang-format --diffstat
参考文档:ClangFormat 官方文档 - Git integration
clang-format-diff.py -- 解析 unified diff 输出,仅格式化 diff 中包含的变更行:
# 格式化最近一次提交的变更行
git diff -U0 --no-color HEAD^ | clang-format-diff.py -p1 -i
# 格式化工作目录中未提交的变更行
git diff -U0 --no-color | clang-format-diff.py -p1 -i
# 仅预览格式化差异(不修改文件)
git diff -U0 --no-color | clang-format-diff.py -p1
参考文档:ClangFormat 官方文档 - Script for patch reformatting
基于 Git 的命令行增量方案:
# 获取相对于 main 分支的 C/C++ 变更文件,仅检查这些文件
git diff --name-only --diff-filter=ACMR main...HEAD |
grep -E '\.(cpp|cc|c|h|hpp)$' |
grep -vE '(third_party|external|build)' |
xargs -r clang-format --dry-run --Werror --style=file
CI 脚本调用:
GitHub Actions(手动方式)
直接使用 clang-format 命令进行格式检查:
# .github/workflows/format-check.yml
name: Format Check
on: [push, pull_request]
jobs:
format:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install clang-format
run: sudo apt-get install -y clang-format
- name: Check formatting
run: |
git ls-files '*.h' '*.hpp' '*.cpp' '*.cc' '*.c' |
grep -vE '(third_party|external|build|\.git)' |
xargs -r clang-format --dry-run --Werror --style=file
GitLab CI
# .gitlab-ci.yml
format-check:
stage: test
image: ubuntu:latest
before_script:
- apt-get update && apt-get install -y clang-format
script:
- git ls-files '*.h' '*.hpp' '*.cpp' '*.cc' '*.c' |
grep -vE '(third_party|external|build)' |
xargs -r clang-format --dry-run --Werror --style=file
增量检查 -- 按 PR/MR 变更文件触发增量检查:
GitHub Actions 中按 PR 变更文件触发增量检查:
# .github/workflows/format-check.yml
name: Incremental Format Check
on: [pull_request]
jobs:
format-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install clang-format
run: sudo apt-get install -y clang-format
- name: Check formatting on changed files
run: |
git diff --name-only origin/${{ github.base_ref }}...HEAD |
grep -E '\.(cpp|cc|c|h|hpp)$' |
grep -vE '(third_party|external|build)' |
xargs -r clang-format --dry-run --Werror --style=file
GitLab CI 中按 MR 变更文件触发增量检查:
# .gitlab-ci.yml
format-check:
stage: test
image: ubuntu:latest
before_script:
- apt-get update && apt-get install -y clang-format
script:
- |
git diff --name-only --diff-filter=ACMR origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME...HEAD |
grep -E '\.(cpp|cc|c|h|hpp)$' |
grep -vE '(third_party|external|build)' |
xargs -r clang-format --dry-run --Werror --style=file
rules:
- if: $CI_PIPELINE_SOURCE == 'merge_request_event'
5.5 GitHub Action 插件
使用 clang-format-lint-action 在 GitHub Actions 中执行格式检查:
# .github/workflows/format-check.yml
name: ClangFormat Check
on: [push, pull_request]
jobs:
format-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run clang-format lint
uses: DoozyX/clang-format-lint-action@v0.20
with:
source: "."
exclude: "third_party|external|build"
extensions: "h,cpp,cc,c,hpp"
clangFormatVersion: 20
style: file
inplace: false
参考文档:
6. 告警抑制(屏蔽)方法
ClangFormat 提供了以下几种方式来抑制或屏蔽格式化行为,每种方式均附官方文档链接。
6.1 代码块级注释禁用(// clang-format off / on)
在代码中使用特殊注释来禁用指定区域的格式化。这是最常用、最推荐的抑制方式。
int formatted_code;
// clang-format off
void unformatted_code ;
int weird_layout ;
// clang-format on
void formatted_code_again;
支持 C++ 行注释(//)和块注释(/* */)两种形式:
// clang-format off
int unformatted;
// clang-format on
/* clang-format off */
int unformatted;
/* clang-format on */
可在 off/on 后添加说明文字(冒号分隔):
// clang-format off: 手动优化的矩阵布局,格式化会破坏对齐
float matrix[4][4] = {
{1.0f, 0.0f, 0.0f, 0.0f},
{0.0f, 1.0f, 0.0f, 0.0f},
{0.0f, 0.0f, 1.0f, 0.0f},
{0.0f, 0.0f, 0.0f, 1.0f}
};
// clang-format on
官方文档:Clang-Format Style Options - Disabling Formatting on a Piece of Code
6.2 单行格式禁用(OneLineFormatOffRegex)
通过 OneLineFormatOffRegex 选项,使用正则表达式匹配特定行并跳过格式化。
# .clang-format
BasedOnStyle: LLVM
# 匹配包含 NOLINT 或 SPECIAL_FORMAT 的行,跳过格式化
OneLineFormatOffRegex: "NOLINT|SPECIAL_FORMAT"
int formatted_code;
int unformatted_code; // NOLINT: 此行不会被格式化
int another_formatted;
6.3 文件级排除(.clang-format-ignore)
使用 .clang-format-ignore 文件排除特定文件或目录,使其完全不被 ClangFormat 处理。
# .clang-format-ignore
third_party/
build/
generated/
*.pb.h
*.pb.cc
6.4 按语言禁用格式化(DisableFormat)
在 .clang-format 中通过 Language: 字段和 DisableFormat: true 组合,禁用特定语言文件的格式化。
---
BasedOnStyle: LLVM
---
Language: Proto
DisableFormat: true
---
Language: TextProto
DisableFormat: true
6.5 通过命令行参数屏蔽
通过命令行参数控制文件类型识别,或通过脚本逻辑跳过特定文件。
# 仅格式化 .cpp 和 .h 文件,跳过其他类型
find . \( -name '*.cpp' -o -name '*.h' \) -print0 |
xargs -0 clang-format -i --style=file
6.6 回退风格跳过(--fallback-style=none)
当找不到 .clang-format 文件时,使用 --fallback-style=none 让 ClangFormat 直接跳过格式化,而非回退到默认 LLVM 风格。
# 找不到 .clang-format 时跳过格式化(而非使用默认风格)
clang-format -i --style=file --fallback-style=none main.cpp
6.7 通过集成调度工具屏蔽
CMake 排除目录:在 file(GLOB_RECURSE ...) 中排除特定目录,或在 CI 中通过路径过滤排除。
pre-commit 排除文件:通过 .pre-commit-config.yaml 中的 exclude 或 files 字段控制。
# .pre-commit-config.yaml - hook 级排除
repos:
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: v22.1.5
hooks:
- id: clang-format
types_or: [c++, c, cuda]
exclude: ^(third_party|external|build)/
# .pre-commit-config.yaml - 顶层排除(全局)
exclude: ^(third_party|external|build)/
repos:
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: v22.1.5
hooks:
- id: clang-format
types_or: [c++, c, cuda]