背景
本文以当前 master(f19da943aa)为基线,对照仓库根目录的 .clang-format 和 RT-Thread 编程风格,梳理仓库当前的代码格式规则。
本 Issue 的目的不是立即进行全仓库格式化,而是先确定 RT-Thread 希望长期保持的格式风格,并明确哪些规则由 clang-format 自动执行,哪些规则需要代码审查或独立检查。
一、目前基本一致的规则
以下规则在手册和根目录配置中基本一致:
- 使用 4 个空格缩进,不使用 Tab;
- 控制语句、函数、结构体、枚举等的大括号通常换行;
switch 中的 case 与 switch 对齐;
- 控制语句关键字与左括号之间保留空格;
- 普通函数名与左括号之间不留空格;
- 括号内部不保留多余空格;
- 多行函数参数按照左括号位置进行续行缩进;
- 二元运算符通常在两侧保留空格。
这些规则可以继续作为基础风格,但仍需要补充例外和边界条件。
二、手册与配置存在差异的规则
| 主题 |
手册中的描述或示例 |
当前 .clang-format 行为 |
需要确认的结论 |
| 指针符号位置 |
同时出现 type* name 和 type *name |
PointerAlignment: Right,通常输出 type *name |
统一采用哪一种写法 |
| 自增运算符 |
示例使用 index ++ |
通常输出 index++ |
是否接受无空格形式 |
| 连续空行 |
建议不要连续使用两个以上空行 |
MaxEmptyLinesToKeep: 2,最多保留两个空行 |
配置是否应改为最多一个空行 |
| 大括号 |
倾向于每个大括号独占一行 |
空函数、短 lambda、extern 块和 do ... while 存在例外 |
是否把这些例外写入手册 |
| 预处理指令 |
示例没有明确说明预处理指令的缩进规则 |
IndentPPDirectives: None,通常顶格 |
#if 块内部是否需要缩进 |
| 行尾 |
手册要求使用 \n |
LineEnding: DeriveLF,会根据输入内容推导,不是无条件强制 LF |
是否需要单独的 LF 检查 |
其中指针风格在手册内部也不完全一致,例如结构体成员和 Doxygen 示例采用了不同的写法,需要先确定意图,而不是简单地把某一个示例作为唯一标准。
三、配置已经决定、但手册没有明确规定的规则
以下行为会影响代码的最终排版,但当前编码规范没有给出明确要求:
| 配置项或行为 |
当前设置 |
尚未明确的问题 |
| 连续声明对齐 |
AlignConsecutiveDeclarations: false |
函数参数、局部变量是否需要列对齐 |
| 连续赋值对齐 |
AlignConsecutiveAssignments: false |
赋值号是否需要对齐 |
| 位域对齐 |
AlignConsecutiveBitFields: true |
位域名称和冒号是否应对齐 |
| 宏对齐 |
AlignConsecutiveMacros: true |
宏值是否应按列对齐 |
| 行宽 |
ColumnLimit: 0 |
是否需要统一的最大行宽 |
| include 顺序 |
SortIncludes: Never、IncludeBlocks: Preserve |
是否按系统头文件、组件头文件和本地头文件分组或排序 |
| 表达式换行 |
二元运算、三元表达式、字符串、参数打包均由配置决定 |
长表达式应如何换行 |
| 尾部注释 |
AlignTrailingComments: Leave |
尾部注释是否需要统一列对齐 |
| 转义换行 |
AlignEscapedNewlines: Left |
宏续行反斜杠是否需要对齐 |
| C++ 语法 |
配置包含 namespace、lambda、template、enum 等规则 |
C++ 文件是否与 C 文件采用同一套风格 |
| 配置例外 |
仓库存在目录级 .clang-format 和 DisableFormat 目录 |
哪些目录是第三方代码例外,哪些属于项目代码 |
这些内容不一定都需要修改,但应该明确哪些是有意设计,避免格式结果由 clang-format 默认行为或历史配置偶然决定。
四、重点案例:函数参数与局部变量声明的对齐范围
希望多行函数定义的参数保持类型和变量名的列对齐,例如:
static rt_err_t _thread_init(struct rt_thread *thread,
const char *name,
void (*entry)(void *parameter),
void *parameter,
void *stack_start,
rt_uint32_t stack_size,
rt_uint8_t priority,
rt_uint32_t tick)
但函数体内的局部变量希望保持普通间距,不因为类型长度不同而产生较大的空格差距:
rt_base_t critical_level;
int err;
在当前根配置下,clang-format 会保留参数的续行缩进,但不会形成参数内部的类型列和变量名列:
static rt_err_t _thread_init(struct rt_thread *thread,
const char *name,
void (*entry)(void *parameter),
void *parameter,
void *stack_start,
rt_uint32_t stack_size,
rt_uint8_t priority,
rt_uint32_t tick)
{
rt_base_t critical_level;
int err;
}
标准 clang-format 没有一个独立选项,可以同时满足以下两个条件:
- 只对函数参数列表启用类型和变量名的列对齐;
- 对函数体内的连续局部声明保持普通间距。
AlignConsecutiveDeclarations 不能限定为“仅函数参数”,AlignAfterOpenBracket 只能控制续行位置,不能产生参数内部的列对齐。如果重新开启全局声明对齐,函数参数可以得到目标效果,但局部变量也会出现类似下面的空格填充:
rt_base_t critical_level;
int err;
需要讨论以下取舍:
- 是否接受当前配置,即函数参数只做续行缩进对齐,局部声明不做列对齐;
- 是否认为函数参数的类型和变量名列对齐是必须保留的风格;
- 如果必须保留参数列对齐,是否接受局部声明也进行全局列对齐;
- 是否允许使用少量
clang-format off/on 或额外脚本实现局部例外。
不建议在全仓库范围内使用大量 clang-format off/on 标记,因为这会降低自动格式化的连续性和可维护性。
五、手册规定但 clang-format 无法完整执行的规则
以下内容属于编码规范,但不应期待由 clang-format 单独保证:
- 目录、文件、函数、结构体、宏和 API 的命名规则;
- 头文件保护符的命名格式;
- 文件头版权和 Change Log;(可以在发布版本的时候统一使用脚本刷一遍)
- 英文注释、Doxygen 标签语义和注释位置;
- 日志接口和日志内容要求;
- 函数长度、对象设计和接口命名约定。
建议在手册中明确区分“自动格式化规则”和“需要人工审查或独立 lint 检查的编码规则”。
六、建议的后续方式
- 逐项确定上述格式风格和例外;
- 为指针、参数、局部声明、宏、位域、预处理指令和注释建立最小 C/C++ 示例;
- 用固定的 clang-format 版本验证这些示例,并将预期结果作为格式基准;
- 根据结论同步修改
.clang-format 和编码规范中的示例;
- 在规则确定前不进行全仓库格式化,避免引入难以区分的空格和换行变更。
附带问题
BSP 自查页面目前仍主要介绍 astyle,与仓库当前使用的 clang-format 不完全一致。待本 Issue 确定最终风格后,再统一更新该页面、旧命令和相关忽略规则说明。
参考
背景
本文以当前
master(f19da943aa)为基线,对照仓库根目录的.clang-format和 RT-Thread 编程风格,梳理仓库当前的代码格式规则。本 Issue 的目的不是立即进行全仓库格式化,而是先确定 RT-Thread 希望长期保持的格式风格,并明确哪些规则由 clang-format 自动执行,哪些规则需要代码审查或独立检查。
一、目前基本一致的规则
以下规则在手册和根目录配置中基本一致:
switch中的case与switch对齐;这些规则可以继续作为基础风格,但仍需要补充例外和边界条件。
二、手册与配置存在差异的规则
.clang-format行为type* name和type *namePointerAlignment: Right,通常输出type *nameindex ++index++MaxEmptyLinesToKeep: 2,最多保留两个空行extern块和do ... while存在例外IndentPPDirectives: None,通常顶格#if块内部是否需要缩进\nLineEnding: DeriveLF,会根据输入内容推导,不是无条件强制 LF其中指针风格在手册内部也不完全一致,例如结构体成员和 Doxygen 示例采用了不同的写法,需要先确定意图,而不是简单地把某一个示例作为唯一标准。
三、配置已经决定、但手册没有明确规定的规则
以下行为会影响代码的最终排版,但当前编码规范没有给出明确要求:
AlignConsecutiveDeclarations: falseAlignConsecutiveAssignments: falseAlignConsecutiveBitFields: trueAlignConsecutiveMacros: trueColumnLimit: 0SortIncludes: Never、IncludeBlocks: PreserveAlignTrailingComments: LeaveAlignEscapedNewlines: Left.clang-format和DisableFormat目录这些内容不一定都需要修改,但应该明确哪些是有意设计,避免格式结果由 clang-format 默认行为或历史配置偶然决定。
四、重点案例:函数参数与局部变量声明的对齐范围
希望多行函数定义的参数保持类型和变量名的列对齐,例如:
但函数体内的局部变量希望保持普通间距,不因为类型长度不同而产生较大的空格差距:
在当前根配置下,clang-format 会保留参数的续行缩进,但不会形成参数内部的类型列和变量名列:
标准 clang-format 没有一个独立选项,可以同时满足以下两个条件:
AlignConsecutiveDeclarations不能限定为“仅函数参数”,AlignAfterOpenBracket只能控制续行位置,不能产生参数内部的列对齐。如果重新开启全局声明对齐,函数参数可以得到目标效果,但局部变量也会出现类似下面的空格填充:需要讨论以下取舍:
clang-format off/on或额外脚本实现局部例外。不建议在全仓库范围内使用大量
clang-format off/on标记,因为这会降低自动格式化的连续性和可维护性。五、手册规定但 clang-format 无法完整执行的规则
以下内容属于编码规范,但不应期待由 clang-format 单独保证:
建议在手册中明确区分“自动格式化规则”和“需要人工审查或独立 lint 检查的编码规则”。
六、建议的后续方式
.clang-format和编码规范中的示例;附带问题
BSP 自查页面目前仍主要介绍
astyle,与仓库当前使用的 clang-format 不完全一致。待本 Issue 确定最终风格后,再统一更新该页面、旧命令和相关忽略规则说明。参考
.clang-format