Skip to content

[Feature] 关于RT-Thread的编码规范 #11653

Description

@CYFS3

背景

本文以当前 masterf19da943aa)为基线,对照仓库根目录的 .clang-formatRT-Thread 编程风格,梳理仓库当前的代码格式规则。

本 Issue 的目的不是立即进行全仓库格式化,而是先确定 RT-Thread 希望长期保持的格式风格,并明确哪些规则由 clang-format 自动执行,哪些规则需要代码审查或独立检查。

一、目前基本一致的规则

以下规则在手册和根目录配置中基本一致:

  • 使用 4 个空格缩进,不使用 Tab;
  • 控制语句、函数、结构体、枚举等的大括号通常换行;
  • switch 中的 caseswitch 对齐;
  • 控制语句关键字与左括号之间保留空格;
  • 普通函数名与左括号之间不留空格;
  • 括号内部不保留多余空格;
  • 多行函数参数按照左括号位置进行续行缩进;
  • 二元运算符通常在两侧保留空格。

这些规则可以继续作为基础风格,但仍需要补充例外和边界条件。

二、手册与配置存在差异的规则

主题 手册中的描述或示例 当前 .clang-format 行为 需要确认的结论
指针符号位置 同时出现 type* nametype *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: NeverIncludeBlocks: Preserve 是否按系统头文件、组件头文件和本地头文件分组或排序
表达式换行 二元运算、三元表达式、字符串、参数打包均由配置决定 长表达式应如何换行
尾部注释 AlignTrailingComments: Leave 尾部注释是否需要统一列对齐
转义换行 AlignEscapedNewlines: Left 宏续行反斜杠是否需要对齐
C++ 语法 配置包含 namespace、lambda、template、enum 等规则 C++ 文件是否与 C 文件采用同一套风格
配置例外 仓库存在目录级 .clang-formatDisableFormat 目录 哪些目录是第三方代码例外,哪些属于项目代码

这些内容不一定都需要修改,但应该明确哪些是有意设计,避免格式结果由 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 没有一个独立选项,可以同时满足以下两个条件:

  1. 只对函数参数列表启用类型和变量名的列对齐;
  2. 对函数体内的连续局部声明保持普通间距。

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 检查的编码规则”。

六、建议的后续方式

  1. 逐项确定上述格式风格和例外;
  2. 为指针、参数、局部声明、宏、位域、预处理指令和注释建立最小 C/C++ 示例;
  3. 用固定的 clang-format 版本验证这些示例,并将预期结果作为格式基准;
  4. 根据结论同步修改 .clang-format 和编码规范中的示例;
  5. 在规则确定前不进行全仓库格式化,避免引入难以区分的空格和换行变更。

附带问题

BSP 自查页面目前仍主要介绍 astyle,与仓库当前使用的 clang-format 不完全一致。待本 Issue 确定最终风格后,再统一更新该页面、旧命令和相关忽略规则说明。

参考

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions