Skip to content

代码操作 ​

clice 针对选区提供代码操作:一类是依据编译器已掌握的信息生成或改写代码的重构,另一类是为头文件未声明的名字提供的快速修复。每个操作在列出时就已完整算好,应用时无需再等一次请求;编辑内容带有计算时所依据的文档版本,缓冲区一旦变动,编辑器就会拒绝它们。同一批操作也可以通过 clice inspect code_action 以无头方式运行。

操作锚定在选区覆盖的最内层构造上——方法声明、类名、一个 switch、一个 auto——因此列表始终很短:点击方法名,列出的是适用于该方法的操作;点击类名,列出的则是适用于该类的操作。

定义函数 ​

为已声明的方法生成定义

方法声明处既可以就地补出函数体,也可以在类之后生成类外定义

类外定义会重复一遍声明,在名字前加上 S::,并去掉只属于声明的部分,例如默认实参。

支持

只属于声明的说明符会被去掉

virtual、static、explicit、override 和 final 不会出现在定义上

必须保留的说明符会留下,例如 constexpr 和 noexcept。

支持

按定义处作用域书写的返回类型

指向嵌套类型或其他命名空间的返回类型,会写成在定义处能够解析的形式

参数类型和声明中一样在类作用域里查找,保持原样不变。

支持

类模板的成员

类模板成员的定义会带上模板头,并在限定符中写出模板实参

模板头上不重复默认模板实参。

支持

成员函数模板

成员模板在类的模板头之后保留自己的模板头,但不带默认实参

带约束的模板会保留 requires 子句,定义必须重复这些子句。

支持

构造函数、析构函数和运算符

特殊成员函数按它们书写时所用的类名生成定义

转换函数和运算符保留完整的书写形式。

支持

自由函数的声明

已声明的自由函数会紧接声明之后、在同一个命名空间内生成定义

在命名空间内部,名字不需要限定符;返回类型按该作用域书写。

支持

放在已有定义之后

如果类在该文件中已有类外定义,新的定义会放在最后一个之后

限定符依照那个定义所在的作用域,而不是类的作用域。

一次定义所有缺失的成员

在类名处,所有没有定义的成员函数会按声明顺序一次性生成定义

已有定义的成员、纯虚函数和显式默认(defaulted)的成员会被跳过。

支持

从定义处补出缺失的成员

在类外定义内部,也可以为该类其余尚未定义的成员生成定义

源文件正是这样补全在别处声明的类:新生成的定义会接在已有定义之后。

定义到宿主源文件中

在头文件中,成员还可以定义到与该头文件一起编译的源文件里

定义使用完全限定名,并与该文件中该类的其他定义放在一起;已经在某个源文件中定义过的成员不会再次列出。模板、内联函数以及其他文件看不到的函数仍留在头文件中;其余函数在头文件里定义到类外时会标上 inline。

支持

嵌套类的成员

嵌套类的成员定义在最外层封闭类之后,限定符逐层写全

支持

依赖返回类型

返回类型用到类模板本身或其成员类型时,会通过模板形参写出限定

支持

链接说明(linkage specification)中的声明

以 C 链接声明的函数和其他函数一样可以生成定义,位置在链接块内部,或者单条声明形式之后

实现接口 ​

实现纯虚方法

从抽象基类派生的类,会为每个尚未实现的纯虚方法得到一条 override 声明

这些声明添加在类体末尾。

支持

沿继承链的纯虚方法

只为继承链上没有任何类实现过的方法生成声明,class 还会为它们加上 public: 标签

支持

面向派生类的类型

在基类作用域中书写的参数类型和返回类型会加上限定,使其在派生类中能够解析

引用限定符和 const 属性会一并带过来。

支持

转换函数与指针参数

转换函数没有返回类型要写出,参数的类型若把名字包在中间,也会保持这一形状

支持

签名相同的多个基类

一条声明同时重写所有基类中具有该签名的纯虚方法,其中任一个带 noexcept 时它也会带上

支持

重写声明的说明符

C 风格可变参数、consteval,以及基类方法是否为 noexcept,都会沿用到重写声明上

异常说明依赖于基类模板实参的方法不会得到声明。

switch 分支 ​

缺失的枚举分支

对枚举做的 switch 会补上未处理的枚举项,每个后面跟一个 break

与已处理值相同的枚举项视为已覆盖。

支持

插在 default 之前的分支

存在 default 时,缺失的分支会紧挨着插在它前面并落入其中,从而保持原有行为

在 switch 内部的任意位置都会给出该操作。

支持

命名空间中的无作用域枚举

当 switch 位于枚举所在命名空间之外时,无作用域枚举的枚举项会带上该命名空间的限定

支持

完整的 switch 不给出操作

处理了全部枚举项的 switch,或者对非枚举值做的 switch,不会给出任何操作

支持

覆盖整个 switch 的选区

选区覆盖整条语句时,给出的操作与光标停在语句内部时相同

支持

依赖模板的标签

switch 中有标签依赖模板参数时不给出操作,因为只有实例化之后才知道它覆盖了哪些枚举项

模板中的 switch 若标签不依赖模板参数,仍会像在其他地方一样补全分支。

支持

声明变量的分支

没有 default 时,若 switch 在自身作用域内声明了变量,缺失的分支会插在它的第一个标签之前,因为放在声明之后的标签会跳过该声明

原有的分支都不会落入插在那里的分支。

推导类型 ​

支持

展开声明中的 auto

变量声明中的 auto 会替换为推导出的类型,限定符和声明符保持原位

支持

推导出的返回类型

函数推导出的 auto 返回类型会展开为推导结果,并按函数所在作用域书写

支持

展开 decltype

decltype 说明符会展开为它所表示的类型

支持

无法命名的类型保持 auto

Lambda、依赖类型以及在该声明中无法命名的类型不会展开

以下类型无法命名:另一个函数内部的局部类型、声明无权访问的成员类型,以及标准名字尚未声明时 sizeof 的类型(MSVC 兼容模式会隐式声明 size_t)。

支持

转发引用(forwarding reference)与声明符类型

auto&& 绑定到左值时,推导出的引用会同时替换掉这两个 Token,而把名字包在中间的类型保持不动

支持

按作用域书写的名字

名字省去外层命名空间的程度,以更短的名字仍能找到同一类型为限

被离展开处更近的声明隐藏的名字会保留限定符;连完全限定名也被隐藏时,则从全局作用域写起。

支持

内置类型的标准名字

在标准名字已声明的位置,sizeof、指针相减和 nullptr 的类型会展开为对应的标准名字

看不到 std::nullptr_t 的声明时,nullptr 的类型写作 decltype(nullptr)。

支持

推导出的常量指针

const 写在推导出指针的 auto 前面时,会移到 * 之后,使指针本身保持为常量

const 与 auto 之间还有其他说明符时,声明保持原样。

宏 ​

展开宏调用

宏调用会替换为它展开后的 Token

实参会被代入;在宏名处或其实参内部的任意位置都会给出该操作。

支持

嵌套的宏完全展开

宏体中调用了其他宏时,会一直展开到最终的 Token

支持

预处理指令中的宏引用与空宏

预处理条件中出现的宏名,无论位于条件的哪一行,都不是可以替换的展开,而展开为空的宏会被删除

支持

执行 pragma 的宏

展开时会执行 _Pragma 运算符的宏不给出展开操作,因为 pragma 不会留下可以写回原处的 Token

支持

Token 不会粘连

展开结果会插入适当的空格,使其中的 Token 既不会彼此粘连,也不会与紧贴宏调用书写的文本粘连

缺失的包含指令 ​

标准库的包含指令

无法解析的标准库名字会依据标准库映射,给出声明它的头文件

指令插入在文件开头那组包含指令之后。非限定的名字还会尝试 std 命名空间。

支持

项目符号的包含指令

名字声明在某个项目头文件中,而当前文件没有包含它时,会给出该头文件,路径按相对当前文件的形式书写

候选来自项目索引:lib.h 之所以已知,是因为另一个源文件包含了它。

支持

文件的第一条包含指令

没有任何包含指令的文件会在开头插入该指令;若存在 #pragma once,则插在其后

支持

条件编译下的包含指令

嵌套在条件编译里的包含指令不是必须始终生效的指令该去的位置:新指令跟在文件自身层级的最后一条之后,或者跟在头文件保护(include guard)之后

支持

嵌入的与尾随的包含指令

位于 extern "C" 或类型体内部的包含指令,以及跟在代码之后的包含指令,都不是新指令该去的位置:新指令会加入文件开头的那组包含指令

重排定义 ​

支持

按声明顺序重排定义

类成员的类外定义会重新排列,以与类中的声明顺序一致

每个定义会连同紧挨其上的注释块一起移动。

支持

命名空间块各自重排

写在不同命名空间块中的定义只在各自块内重排,绝不跨块

支持

自由函数按声明顺序排列

在自由函数的定义处,与它一同声明的那些函数的定义会按声明顺序重排

支持

行尾注释留在原处

位于某个定义行尾的注释会跟着该定义移动,不会跟着下面那个定义走

支持

用到中间内容的定义

如果移动某个定义会把它挪到它所用的东西之前,例如夹在这些定义之间的变量或宏定义,该定义就留在原处,其余定义围绕它重排

构造函数 ​

支持

逐成员构造函数

为类生成按顺序接收全部字段的构造函数,标量按值传递,可复制的类按 const 引用传递

支持

不重复生成已有的构造函数

当类已经声明了参数个数与字段数相同的构造函数时,不再生成构造函数

支持

单字段构造函数带 explicit

只有一个字段的类会得到 explicit 构造函数;若类以其他访问段结尾,构造函数会放在 public: 标签之下

支持

声明符形状的字段类型

字段的类型若把名字包在中间,例如函数指针,在参数中会保持这一形状

支持

没有默认构造函数的基类

当基类需要自己的初始化项时,不会生成构造函数,因为逐成员构造函数只初始化字段

支持

默认构造函数被删除的基类

当基类的默认构造函数被删除时,无论是显式删除,还是因引用成员或没有初始化的 const 成员而隐式删除,同样不会生成逐成员构造函数

若基类自己初始化了全部字段,其中的 const 成员不会让它失去默认构造函数。

支持

只能移动的字段

类型只能移动、不能复制的字段按值接收并移动到字段中,右值引用字段则经 std::move 绑定到传入的实参

类之前没有任何地方声明 std::move 时,会为文件加上 #include <utility>。既不能复制也不能移动的类不会得到构造函数。

格式化 ​

当文件适用项目的 clang-format 样式时,生成的文本会按该样式格式化,于是一段定义或一组 case 标签会融入周围代码的排版。没有样式,或者样式为 DisableFormat 时,文本保持上面示例中的排版。

已知限制 ​

  • 定义到宿主源文件时,位置在索引已知的、该文件中该类成员的最后一个定义之后;文件中一个都没有时放在文件末尾。哪个源文件充当头文件的宿主,取决于该头文件的编译上下文。
  • 只有当项目索引知道某个定义时,已在另一个源文件中定义的成员才会被“定义缺失的成员”操作排除;关闭索引后,每个未定义的成员都会列出。
  • 返回类型用到依赖基类(dependent base class)的成员时,会原样照搬到类外定义中,在那里可能需要 typename 和基类的限定符。
  • 缺失包含指令的候选来自标准库映射,以及项目索引见过的头文件;没有任何已索引源文件包含过的头文件不会被建议。

尚未实现 ​

由编译器和 clang-tidy 的 fix-it 提示生成的快速修复、提取函数与变量、内联函数与变量、在头文件与源文件之间移动定义、把无作用域枚举转换为有作用域枚举,以及在所有调用处修改函数签名。