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 指定基础风格,再覆盖特定选项

配置文件查找优先级(从高到低):

  1. 命令行 -style=file:<path> 显式指定
  2. 当前文件所在目录的 .clang-format
  3. 父级目录的 .clang-format(逐级向上查找)
  4. -fallback-style 指定的回退风格(默认为 LLVM)

4.2 .clang-format 配置详解

配置项说明

配置项 类型 说明
BasedOnStyle string 基础风格(LLVMGoogleChromiumMicrosoftWebKitMozillaGNU
IndentWidth int 缩进宽度(空格数)
ColumnLimit int 单行最大字符数,超过则自动换行
Language string 指定配置适用的语言(CppJavaScriptProtoCSharp 等),用于多语言配置分隔
DisableFormat bool 是否禁用格式化
PointerAlignment string 指针对齐方式(LeftRightMiddle
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 配置文件。

官方文档ClangFormat 官方文档 - .clang-format-ignore

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 模块,提供开箱即用的 formatcheck-formatfix-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"

参考文档:CLion 官方文档 - ClangFormat

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;

官方文档:Clang-Format Style Options - OneLineFormatOffRegex

6.3 文件级排除(.clang-format-ignore)

使用 .clang-format-ignore 文件排除特定文件或目录,使其完全不被 ClangFormat 处理。

# .clang-format-ignore
third_party/
build/
generated/
*.pb.h
*.pb.cc

官方文档:ClangFormat 官方文档 - .clang-format-ignore

6.4 按语言禁用格式化(DisableFormat)

.clang-format 中通过 Language: 字段和 DisableFormat: true 组合,禁用特定语言文件的格式化。

---
BasedOnStyle: LLVM
---
Language: Proto
DisableFormat: true
---
Language: TextProto
DisableFormat: true

官方文档:Clang-Format Style Options - DisableFormat

6.5 通过命令行参数屏蔽

通过命令行参数控制文件类型识别,或通过脚本逻辑跳过特定文件。

# 仅格式化 .cpp 和 .h 文件,跳过其他类型
find . \( -name '*.cpp' -o -name '*.h' \) -print0 |
  xargs -0 clang-format -i --style=file

官方文档:ClangFormat 官方文档 - Command Line Options

6.6 回退风格跳过(--fallback-style=none)

当找不到 .clang-format 文件时,使用 --fallback-style=none 让 ClangFormat 直接跳过格式化,而非回退到默认 LLVM 风格。

# 找不到 .clang-format 时跳过格式化(而非使用默认风格)
clang-format -i --style=file --fallback-style=none main.cpp

官方文档:ClangFormat 官方文档 - --fallback-style

6.7 通过集成调度工具屏蔽

CMake 排除目录:在 file(GLOB_RECURSE ...) 中排除特定目录,或在 CI 中通过路径过滤排除。

pre-commit 排除文件:通过 .pre-commit-config.yaml 中的 excludefiles 字段控制。

# .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]

官方文档:pre-commit - Configuring hooks


← 返回目录 > ← 返回总览