前言
早在1979年,在贝尔实验室发表的UNIX第七版中出现了 lint,这是一个用于 C 语言静态分析的小工具,静态分析允许我们在执行代码之前发现问题, 以此来帮助开发者写出更可靠更规范的程序。
2021 了,不会还没用上吧?/狗头
Linter
Linter 衍生于 lint,关于 lint,我们来看看维基百科的解释:
在计算机科学中,lint 是一种工具程序的名称,它用来标记源代码中,某些可疑的、不具结构性(可能造成 bug )的段落。它是一种静态程序分析工具,最早适用于 C 语言,在 UNIX 平台上开发出来。后来它成为通用术语,可用于描述在任何一种计算机程序语言中,用来标记源代码中有疑义段落的工具。
基本上每种语言都有自己的 Linter,例如:Python - flake8,JavaScript - jshint,CSS - csslint,Ruby - ruby-wc。
Dart 也不例外。Dart 本身就集成了 linter 工具,通过执行命令 dart analyze
来触发分析。对于 Flutter 来说,flutter analyze
会用来分析文件。
Lint Rules
每一个 linter 必须要有相应的规则才能分析文件,Dart 为我们提供了一系列的规则(Lint Rules),分成三个组(每组只举例了几种,其他可以参考 Linter for Dart ):
errors - 可能会出现的编译错误
always_use_package_imports
Bad:
import 'baz.dart';
import 'src/bag.dart'
import '../lib/baz.dart';
复制代码
Good:
import 'package:foo/bar.dart';
import 'package:foo/baz.dart';
import 'package:foo/src/baz.dart';
复制代码
avoid_print
Bad:
void f(int x) {
print('debug: $x');
...
}
复制代码
style - 代码风格问题
always_declare_return_types
Bad:
main() { }
_bar() => _Foo();
class _Foo {
_foo() => 42;
}
复制代码
Good:
void main() { }
_Foo _bar() => _Foo();
class _Foo {
int _foo() => 42;
}
typedef predicate = bool Function(Object o);
复制代码
avoid_returning_null_for_void
Bad:
void f1() {
return null;
}
Future<void> f2() async {
return null;
}
复制代码
Good:
void f1() {
return;
}
Future<void> f2() async {
return;
}
复制代码
pub - pub 相关规则
package_names
Bad:
JustLikeThis
12Jsl
import
复制代码
Good:
just_like_this
复制代码
常用的 Lint Rules Group
由于有些 rule 可能会相互冲突,所以一般我们只使用他的子集,例如:
- pedantic 谷歌内部所用的规则
- effective_dart Effective Dart 风格指南的规则
- flutter Flutter 项目推荐的规则
应用以及自定义配置
上面介绍了分析的命令和规则,那我们怎么应用到自己的项目中去呢?
只需要我们在根目录创建一个 analysis_options.yaml
文件,在文件里面配置一些相应的规则,这样执行 flutter analyze
的时候就会按照我们指定的规则去分析文件。不同的 package 可以有不同的 analysis_options.yaml
文件
my_other_package 和 my_other_other_package 会通过 #1 分析代码,my_package 会通过 #2 分析代码。
可以看到,当前 package 没有 analysis_options.yaml
文件的时候,会在目录树中寻找其他的文件,如果最后都没找到,则会按照标准的规则去分析文件。
我们可以在 analysis_options.yaml
文件添加想要的 lint rule 或者自定义一些配置,常见的文件内容如下:
# 1
# url to bring in options from the specified URL — in this case, from a file in the lints package.
include: package:lints/recommended.yaml
# include: package:pedantic/analysis_options.1.8.0.yaml
# 2
# entry to customize static analysis — enabling stricter type checks, excluding files, ignoring specific rules, changing the severity of rules, or enabling experiments.
analyzer:
# strong-mode:
# implicit-casts: false
# implicit-dynamic: false
errors:
todo: ignore
invalid_assignment: warning
missing_return: error
dead_code: info
exclude:
- lib/src/checkout/data/ptp_checkout_repository.dart
- lib/src/components/search_form/ui/search_form.dart
# 3
# entry to configure linter rules.
linter:
rules:
- avoid_empty_else
# - avoid_function_literals_in_foreach_calls
- avoid_print
- avoid_relative_lib_imports
- avoid_returning_null_for_future
- prefer_const_constructors
复制代码
include
# url to bring in options from the specified URL — in this case, from a file in the lints package.
include: package:lints/recommended.yaml
# include: package:pedantic/analysis_options.1.8.0.yaml
复制代码
可以直接引入一些 lint rules,例如上面常用的 Lint Rules Group。
⚠️ 注意
当前文件重写 analyzer: 时,即使 analyzer: 下什么都不写,都会整体覆盖 include 的文件的 analyzer: ,
当前文件重写 rules: 会和 include 文件的 rules: 取并集,当某条具体 rule 存在多个文件时,则以当前文件的值为准。
analyzer
# entry to customize static analysis — enabling stricter type checks, excluding files, ignoring specific rules, changing the severity of rules, or enabling experiments.
analyzer:
strong-mode:
implicit-casts: false
implicit-dynamic: false
errors:
todo: ignore
invalid_assignment: warning
missing_return: error
dead_code: info
exclude:
- lib/src/checkout/data/ptp_checkout_repository.dart
- lib/src/components/search_form/ui/search_form.dart
复制代码
strong-mode
比 Dart type system 更严格的类型检查:
# 隐式转换
implicit-casts: false
# 隐式 dynamic
implicit-dynamic: false
复制代码
errors
每一个analyzer error code 和 linter rule 都有默认的等级:
-
info
An informational message that doesn’t cause analysis to fail. Example:
dead_code
-
warning
A warning that doesn’t cause analysis to fail unless the analyzer is configured to treat warnings as errors. Example:
invalid_null_aware_operator
-
error
An error that causes analysis to fail. Example:
invalid_assignment
你可以修改他们的等级,甚至直接忽略:
errors:
todo: ignore
invalid_assignment: warning
missing_return: error
dead_code: info
复制代码
exclude
可以统一配置忽略某些文件:
exclude:
- lib/src/file1.dart
- lib/src/components/file2.dart
复制代码
!所有基于 analysis_server 分析文件的工具都将忽略 exclude 下所添加的文件。
也可以去某个文件里面单独配置:
// ignore_for_file: unused_import, unused_local_variable
// ignore: invalid_assignment
int x = '';
int x = ''; // ignore: invalid_assignment
复制代码
linter
添加一些规则
# entry to configure linter rules.
linter:
rules:
- avoid_empty_else
# - avoid_function_literals_in_foreach_calls
- avoid_print
- avoid_relative_lib_imports
- avoid_returning_null_for_future
- prefer_const_constructors
# 另外一种写法,可用于重写 include 文件存在的规则
linter:
rules:
avoid_empty_else: true
avoid_print: false
复制代码
其他规则可以参考 all lint rules
一些问题
-
flutter analyze
,dart analyze
,idea analyze(vscode 等)为什么出现不同的结果?上述三者本质上都是基于 analysis_server 去分析文件,但是上层的逻辑可能会有所差异,所以导致结果不一样。例如
flutter analyze
会忽略 TODO 类型的提示 。也有可能是它们依赖的 lint rules 版本不同,Why is flutter analyze different from dart analyze?
-
analysis_options.yaml
文件是如何起作用的? -
dart migrate
是基于 analysis_server ?