swift/dart代码规范检查工具介绍

swift/dart代码规范检查工具介绍

1
2
简介:
本篇主要介绍swift和dart代码规范检查工具,以及他们的工作原理,操作过程,代码规范规则。

1 swift代码检查工具-swiftlint

1.1 介绍swiftlint

1
SwiftLint 是 realm 公司开发的一个插件,专门用于管理 Swift 代码的规范。

1.2 swiftlint工作原理

SwiftLint 的工作原理是检查 Swift 代码编译过程中的 AST 和 SourceKit 环节,从而可以摆脱不同版本 Swift 语法变化的影响。AST 是编译前端形成的抽象语法树(Abstract Symbolic Tree), SourceKit 过程用来对 AST 进行代码优化,减少内存开销,提高执行效率。如果对编译过程理解不太清楚,可以参考:Clang官方文档以及网络上两篇比较好的文章ASTLLVM优点以及戴铭的深入剖析 iOS 编译 Clang / LLVM

1.2.1 llvm编译过程

llvm架构

编译过程

预处理 -> 词法分析 -> Token -> 语法分析 -> AST -> 代码生成 -> LLVM IR -> 优化 -> 生成汇编代码 -> Link -> 目标文件

Ast,什么是抽象语法树,在llvm架构中,分为前端,优化器和后端三块,而clang是llvm的编译前端,当编译源代码c,c++,oc,swift时候。会对源代码进行词法分析,进行分词,变成token,包括(关键字,if,else,while等。标志符,函数名称。字面量,值,数字,字符串等,特殊符号+-*/)然后进行语法分析将token组合成语义生成节点。然后将这些节点根据层级关系生成抽象语法树。

抽象语法树,顶层是一个编译单元。又包含很多个字节点,其中有一些内置定义。然后函数声明。

Sourcekit是独立于Xcode的存在,它主要对swift源代码层面的操作性进行一个支持,例如语法解析,代码高亮,排版,自动补全等等。sourcekit主要借助与clang的符号解析usr,为源代码令牌(token,类,属性,方法)生成唯一的标志符,然后通过commad+点击源代码任意一处代码令牌,导航到对应的定义处。
如果有些时候你的代码全变白了,commad+点击失效了,类型推断失效了,这时候就是sourcekit出现了问题,可能是内存占用过高了。

1.2.2 SourceKit

SourceKit 是一套工具集,使得大多数 Swift 源代码层面的操作特性得以支持,例如源代码解析、语法高亮、排版(typesetting)、自动补全、跨语言头文件生成,等等
由于 SourceKit 是独立于Xcode之外的,所以可以利用以构建从 Swift IDE 到文档生成器等任何东西
例如第三方的工具

1.jazzy是一个命令行实用程序,可为Swift或Objective-C生成文档

1.3 swiftlint配置

1.3.1 swiftlint配置
具体环境安装配置详见官方文档,写的比较详细就不一一赘述了 swiftlint官方文档以及触发规则文档

swiftlint操作大体流程:
->安装swiftlint
->配置工程脚本
->配置.swiftlint.yml规则文件到项目根目录
->编译代码
->触发规则的项目报error和warning
->通过命令行进行具体操作(见下文)
->根据触发的规则对比触发规则文档修改代码

看一下配置的文件.swiftlint.yml(通过配置.swiflint.yml,控制禁用/启用哪个规则,并为给定规则设置警告和错误的阈值)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
disabled_rules: # 执行时排除掉的规则
- colon
- comma
- control_statement
opt_in_rules: # 启用非默认设置的规则
- empty_count
- missing_docs
# 可以通过执行如下指令来查找所有可用的规则:
# swiftlint rules
included: # 执行 linting 时包含的路径。如果出现这个 `--path` 会被忽略。
- Source
excluded: # 执行 linting 时忽略的路径。 优先级比 `included` 更高。
- Carthage
- Pods
- Source/ExcludedFolder
- Source/ExcludedFile.swift

# 可配置的规则可以通过这个配置文件来自定义
# 强制转换规则,可以设置他们的严重程度error,warning
force_cast: warning # 隐式
force_try:
severity: warning # 显式
# 同时有警告和错误等级的规则,可以只设置它的警告等级
# 隐式
line_length: 110
# 可以通过一个数组同时进行隐式设置
type_body_length:
- 300 # warning
- 400 # error
# 或者也可以同时进行显式设置
file_length:
warning: 500
error: 1200
# 命名规则可以设置最小长度和最大程度的警告/错误
# 此外它们也可以设置排除在外的名字
type_name:
min_length: 4 # 只是警告
max_length: # 警告和错误
warning: 40
error: 50
excluded: iPhone # 排除某个名字
identifier_name:
min_length: # 只有最小长度
error: 4 # 只有错误
excluded: # 排除某些名字
- id
- URL
- GlobalAPIKey
reporter: "xcode" # 报告类型 (xcode, json, csv, checkstyle, codeclimate, junit, html, emoji, sonarqube, markdown, github-actions-logging)

强制转换,严重级别设置warning和error两个等级

1
2
3
force_cast: warning # 隐式
force_try:
severity: warning # 显式
可以查看规则文档SwiftLint-ruls示例

force_cast
非触发例子

1
NSNumber() as? Int

触发例子

1
NSNumber() as! Int

force_try 非触发例子

1
2
3
4
func a() throws {}
do {
try a()
} catch {}

触发例子

1
2
func a() throws {}
try! a()

也可以自定义规则:

1
2
3
4
5
6
7
8
9
10
11
12
custom_rules:
pirates_beat_ninjas: # 规则标识符
name: "Pirates Beat Ninjas" # 规则名称,可选
regex: "([nN]inja)" # 匹配的模式
match_kinds: # 需要匹配的语法类型,可选
- comment
- identifier
message: "Pirates are better than ninjas." # 提示信息,可选
severity: error # 提示的级别,可选
no_hiding_in_strings:
regex: "([nN]inja)"
match_kinds: string

输出样式:

自定义规则触发

1.3.2 swiftlint 命令

swiftlint命令行可用的命令

// 输出所有的警告和错误

swiftlint lint
swiftlint lint

生成错误报告
1
swiftlint lint  --path ~/Desktop --reporter html > swiftlint.html

// 自动修复已知的Error和Warning

swiftlint autocorrect

// 打开swiftlint文档,直接打开 SwiftLint官方文档

swiftlint docs

// 为所有规则生成文档

swiftlint generate-docs --path

生成的规则文档

// 输出swiftlint的所有规则以及目前工程配置情况

swiftlint rules

输出swiftlint的所有规则

identifier:规则名称
Opt-in:启用非默认设置的规则
correctable:可纠正的规则
enabled in your config:配置中已经启用
Kind:规则的种类

1.3.3 fastlane支持

也可以用fastlane官方的SwiftLint功能来运行SwiftLint作为你的Fastlane程序的一部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
swiftlint(
mode: :lint, # SwiftLint模式: :lint (默认) 或者 :autocorrect
executable: "Pods/SwiftLint/swiftlint", # SwiftLint的程序路径 (可选的). 对于用CocoaPods集成SwiftLint时很重要
path: "/path/to/lint", # 特殊的检查路径 (可选的)
output_file: "swiftlint.result.json", # 检查结果输出路径 (可选的)
reporter: "json", # 输出格式 (可选的)
config_file: ".swiftlint-ci.yml", # 配置文件的路径 (可选的)
files: [ # 指定检查文件列表 (可选的)
"AppDelegate.swift",
"path/to/project/Model.swift"
],
ignore_exit_status: true, # 允许fastlane可以继续执行甚至是Swiftlint返回一个非0的退出状态(默认值: false)
quiet: true, # 不输出像‘Linting’和‘Done Linting’的状态日志 (默认值: false)
strict: true # 发现警告时报错? (默认值: false)
)

2.dart代码规范检查

2.1 都有哪些代码规范检查的工具

1.Flutter工程沿用Dart的静态代码检查器dartanalyzer, 并使用内置在Flutter Tools内的终端工具进行代码扫描和分析, 相关代码位于:commands

2.2 如何使用

一般情况下, 在工程文件下配有分析脚本的情况下, 可以直接使用flutter analyze命令进行静态检查, 这和开发工具Android Studio自带的检查插件的效果是一样的, 最终结果会显示在AS下方的Dart Analysis Tab下.

dart Analysis

2.2.1 分析脚本长啥样放在哪

检查器会根据项目根目录下的分析脚本执行检查, 该文件通常会与pubspec文件处于同一层级.
老版本的分析脚本命名为.analysis_options, 没有yaml后缀, 新版本统一命名为:analysis_options.yaml

我的analysis_options位置

除了这么放还能怎么放,如果遇到更加复杂的情况怎么办,比如说不同的文件夹下适应不同的规则,见下图:

引用flutter官方图片工程目录结构

检查器会使用#1检查other package和other other package, 使用#2检查my package.

2.2.2 分析脚本怎么写

1). 默认使用官方

在flutter 1.22 版本后有默认的分析脚本,内置在flutter包中analysis_options_user.yml

2). 集成其他的分析脚本

如果不想使用默认的分析脚本,当然也可以自己进行配置,如果嫌一条条配置lint规则麻烦, 可以直接集成Google的开源项目pedantic或者effective dart(二选一).

2.2.3 定制分析脚本 analysis_options.yaml

analysis_options.yaml配置

dart语言的代码规范以及相关触发规则详见linter

如果嫌一条条配置lint规则麻烦, 可以直接集成下面的

1). pedantic: Google内部执行的规则

pubspec.yaml 中添加如下,再flutter pub get

1
2
dev_dependencies:
pedantic: ^1.11.0

analysis_options.yaml 中引入

1
include: package:pedantic/analysis_options.yaml

2). effective_dart : 与 effective-dart指南 相对应的规则

在pubspec.yaml中

1
2
dev_dependencies:
effective_dart: ^1.0.0

执行 flutter pub get 然后在 analysis_options.yaml 中引入

1
include: package:effective_dart/analysis_options.yaml

3). Flutter官方在1.22版本默认使用的分析脚本 : analysis_options_user.yml

在flutter 1.22 版本默认使用的分析脚本 (注:flutter版本查看 flutter --version)当然也可以导入之后进行自定义,如下

1
include: package:flutter/analysis_options_user.yaml

示例一个以google的pedantic为例 自定义 analysis_options.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 指定引用第三方库中预设文件的路径
include: package:pedantic/analysis_options.1.8.0.yaml

## analyzer : 配置static analysis的条目,包括启用更严格的类型检查,排除文件,忽略特定规则,更改规则的严重程度等。

## exclude 排除文件
analyzer:
exclude:
- [build/**]
- lib/http_client.dart
- lib/models/*.g.dart
- lib/mock/**

strong-mode:
implicit-casts: false
implicit-dynamic: false

errors:
todo: ignore

## linter : 配置linter规则
linter:
rules:
- camel_case_types
- cancel_subscriptions
严格类型检查

我们知道dart使用语法糖实现了伪动态类型 - dynamic, 可以在编译器不确定类型时, 使用dynamic代替, implicit-dynamic可以阻止这种操作. implicit-casts表示禁止Object类型隐式转换为其他类型.

1
2
3
4
analyzer:
strong-mode:
implicit-casts: false
implicit-dynamic: false

lint规则有两种配置方式一种是list方式 另一种是key-value方式,两者不能混用
list方式

1
2
3
4
linter:
rules:
-- annotate_overrides
-- await_only_futures

key-value方式

1
2
3
4
linter:
rules:
annotate_overrides: false #禁用规则
await_only_futures: true #开启规则

对于具体规则什么意思呢,咱们以上文中提到的 annotate_overridesawait_only_futures为例

annotate_overrides规则

await_only_futures规则

可以看到文档写的非常清楚,其他规则触发详见 dart语言的代码规范以及相关触发规则linter

忽略特定文件或者代码行

1).忽略文件
要忽略特定文件的特定非错误规则,请在文件中添加ignore_for_file注释

1
//ignore_for_file: unnecessary_new

这在注释之前或之后对整个文件起作用,并且对于生成的代码特别有用。

忽略多个规则

1
//ignore_for_file: unnecessary_new, unused_import, unused_local_variable

忽略单行

1
// ignore: unnecessary_new

忽略单行-1

忽略单行-2

自定义规则

每个linter规则都有默认的严重性。您可以使用分析选项文件来更改单个规则的严重性,或者始终忽略某些规则。
支持三种严重性级别:

  • info
    不会导致分析失败的参考消息。例子:todo
  • warning
    警告,例如:analysis_option_deprecated
  • error
    严重错误,导致失败的错误,例如:invalid_assignment

忽略规则

1
2
3
analyzer:
errors:
todo: ignore

更改规则的严重性

1
2
3
4
5
analyzer:
errors:
invalid_assignment: warning
missing_return: error
dead_code: info

2.2.4 进行代码规范检查 - dart analyzer 命令

万事具备只欠东风 通过 dart analyze

通过命令行进行代码静态分析

挑出其中两条.

1).第一条 info 级别的.

触发规则

1
info • Unnecessary new keyword at lib/business/home/home_page.dart:36:40 • (unnecessary_new)

具体代码

unnecessary_new

查看规则文档可知unnecessary_new应避免使用new关键字创建实例。

2).第二条 error级别的
1
error • Missing return type for 'saveStringList' at lib/core/storage/shared_preferences_storage.dart:25:3 • (implicit_dynamic_return)

具体代码

implicit_dynamic_return

查看所给的提示可知没有添加函数的返回值,找了规则文档没有找到这条implicit_dynamic_return规则。
原来我启用了更为严格的类型检查,所以才会触发这个error级别的错误。

严格类型检查

上文说过这里就不多解释了。

可以使用查看全部命令
dart analyzer –help

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Global options:
-h, --help Print this usage information.
-v, --verbose Show additional command output.
--version Print the Dart SDK version.
--enable-analytics Enable anonymous analytics.
--disable-analytics Disable anonymous analytics.

Available commands:
analyze Analyze the project's Dart code.
compile Compile Dart to various formats.
create Create a new project.
format Idiomatically format Dart source code.
pub Work with packages.
run Run a Dart program.
test Run tests in this package.