静态分析让你的代码问题能在运行前被发现,它在防止问题产生和代码风格指南的遵循上很有帮助。
在分析器的帮助下,你可以发现简单的拼写错误。例如,可能在 if
语句中不小心多打了一个分号:
void increment() { if (count < 10) ; count++; }
如果配置得当,分析器会指出这个分号的位置并输出如下警告:
info - example.dart:9:19 - Unnecessary empty statement. Try removing the empty statement or restructuring the code. - empty_statements
分析器也能帮你找出更多细节问题。例如,也许你忘记关闭一个 sink 了:
var controller = StreamController<String>();
info - Close instances of `dart.core.Sink`. - close_sinks
在 Dart 生态系统中,Dart 分析服务和其他相关工具使用了 analyzer 来提供静态分析。
你可以自定义静态分析以寻找各种潜在的问题,包括在 Dart 编程语言规范 中规定的错误和警告。你同样能通过配置 linter ——分析器的一个插件,来确保你的代码遵循 Dart 代码风格指南
和 高效 Dart 中其他建议的准则。诸如 dart analyze
,
flutter analyze
,
以及 IDE 和编辑器
等 Dart 工具都会使用 analyzer package 来评估你的代码。
这篇文档解释了如何通过使用分析配置文件,或在 Dart 源代码中添加注释来自定义分析器的行为。如果你想在工具中添加静态分析规则,请参考 analyzer package 的文档和 Analysis Server API 规范。
分析配置文件
将分析配置文件 analysis_options.yaml
放在包的根目录,即和 pubspec 文件同样的目录下。
这是一个分析配置文件的示例:
include: package:lints/recommended.yaml
analyzer:
exclude: [build/**]
language:
strict-casts: true
strict-raw-types: true
linter:
rules:
- cancel_subscriptions
该示例说明了一些最常用的顶级配置入口:
-
使用
include: url
来从指定的 URL 引入选项 —— 在这种情况下,通常是引入来自 lints 包中的文件。由于 YAML 不支持多个重复的 key,你只能引入最多一个文件; -
使用
analyzer:
入口来自定义静态分析: 启用更严格的类型检查, 排除文件, 忽略特定规则, 改变规则的警告等级, or 开启实验性功能; -
使用
linter:
入口来配置 linter 规则。
如果分析器在 package 的根目录下无法找到一个分析配置文件,它将会往下查找整个目录树。如果还是没有可用的配置文件,分析器则默认使用标准检查规则。
对于如下所示的一个大型项目的目录结构而言:
分析器使用 #1 文件来分析 my_other_package
和 my_other_other_package
中的代码,使用 #2 文件来分析 my_package
中的代码。
启用更严格的类型检查
如果你想要比 Dart 类型系统 所要求的更加严格的类型检查,考虑开启 strict-casts
,strict-inference
,和 strict-raw-types
语言模式:
analyzer:
language:
strict-casts: true
strict-inference: true
strict-raw-types: true
你可以单独或一起使用这些模式,他们默认都为 false
状态。
strict-casts: <bool>
设为 true
可确保类型推理引擎不再将 dynamic
进行隐式类型转换。下方的 Dart 代码在 List<String>
参数上传递了一个 jsonDecode
方法的返回值,实际是将返回的 dynamic
做了隐式向下转换,在运行时可能导致错误。该模式会报告此类潜在的错误,要求你添加一个显示的类型转换或者调整你的代码。
void foo(List<String> lines) {
...
}
void bar(String jsonText) {
foo(jsonDecode(jsonText)); // Implicit cast
}
error - The argument type 'dynamic' can't be assigned to the parameter type 'List<String>'. - argument_type_not_assignable
strict-inference: <bool>
设为 true
可确保当类型推理引擎无法确定静态类型时,不再选择dynamic
类型。下方合法 Dart 代码创建了一个类型参数无法被推断的 Map
,在该模式下会触发推断失败的 hint 提示:
final lines = {}; // Inference failure
lines['Dart'] = 10000;
lines['C++'] = 'one thousand';
lines['Go'] = 2000;
print('Lines: ${lines.values.reduce((a, b) => a + b)}'); // Runtime error
info - The type argument(s) of 'Map' can't be inferred - inference_failure_on_collection_literal
strict-raw-types: <bool>
设为 true
可确保当类型推理引擎,由于省略类型参数而无法确定静态类型时,不再选择dynamic
类型。下方合法 Dart 代码中有一个原始类型的 List
变量,导致在该模式下触发了原始类型 hint 提示。
List numbers = [1, 2, 3]; // List with raw type
for (final n in numbers) {
print(n.length); // Runtime error
}
info - The generic type 'List<dynamic>' should have explicit type arguments but doesn't - strict_raw_type
启用和停用 linter 规则
analyzer 包同样提供一个代码 linter,并包含一份广泛多样的 linter 规则。提示规则之间往往是无关联性的,各种规则之间不必彼此遵守。例如,有些规则更合适支持库,而另一些则是为 Flutter 应用设计的。注意,linter 规则可能会触发误报,静态分析则不会。
启用 Dart 团队推荐的 linter 规则
Dart 团队在 lints package 中提供了 2 个推荐的 linter 规则集合:
核心规则
帮助确认可能导致在运行或使用 Dart 代码时引发问题的关键事项。所有类型的代码都应该符合这些 linter 规则。被上传至 pub.dev 的 package,会有一个部分基于通过这些规则的情况而生成的 package 评分。
推荐规则
帮助确认其他可能导致运行或使用 Dart 代码时引发问题的事项,并强制使用单一、惯用的代码风格和代码格式化。作为一个核心规则的超集,我们推荐所有的 Dart 代码使用它。
将 lints package 作为 dev dependency 添加,来启用任意 lints 集合。
$ dart pub add --dev lints
然后编辑 analysis_options.yaml
文件来引入你想要的规则集合:
include: package:lints/<RULE_SET>.yaml
例如,你可以像这样引入推荐规则的集合:
include: package:lints/recommended.yaml
启用单条规则
在分析配置文件中添加顶层 key linter:
来启用单条规则,紧跟着用 rules:
作为二级 key。在后续行中,以短横杠为前缀(YAML 列表的语法),指定你想要添加的规则。例如:
linter:
rules:
- always_declare_return_types
- cancel_subscriptions
- close_sinks
- comment_references
- one_member_abstracts
- only_throw_errors
- package_api_docs
- prefer_final_in_for_each
- prefer_single_quotes
停用单条规则
如果你引入了一个分析配置文件(比如 lints
中的某一个),你可能会想要停用其中的一部分。停用单条规则和启用单条规则是类似的,但要求使用键值对而不是列表来作为 rules:
的值。因此每一行应该包括规则的名字,后面跟上 : false
或者 : true
。
这里是一个分析配置文件的示例,其中使用了来自 lints
的所有推荐规则,除了 avoid_shadowing_type_parameters
被单独停用。这里同样单独启用了 await_only_futures
这条 lint。
include: package:lints/recommended.yaml
linter:
rules:
avoid_shadowing_type_parameters: false
await_only_futures: true
从 analysis 中排除代码
有时候,部分代码可能允许包含分析出的警告和提示。例如,你也许依赖于某个不属于你 package 所生成的代码,这些代码可以正常运行,但是在静态检查中会产生警告。或者某个 linter 规则可能会出现你想关掉的误报。
有几种方法可以从 analysis 中排除代码:
-
从 analysis 中排除整个文件。
-
在单个文件中停止特定的非错误规则的生效。
-
在单个文件的某几行中,停止特定的非错误规则的生效。
你同样可以对所有文件 停用特定的规则,或者 改变规则的警告等级。
排除文件
使用分析器选项 exclude:
在静态分析中排除文件。你可以列出单独的文件,或者用 glob 语法:
analyzer:
exclude:
- lib/client.dart
- lib/server/*.g.dart
- test/_data/**
对单个文件忽略规则
通过在文件中添加 ignore_for_file
注释,使得特定的非错误规则对该文件忽略。
// ignore_for_file: unused_local_variable
该操作对整个文件都生效,不论代码是在注释之前还是之后,对于生成的代码尤其有用。
使用逗号分隔的列表,可以忽略多条规则:
// ignore_for_file: unused_local_variable, duplicate_ignore, dead_code
添加 type=lint
以忽略所有的 linter 规则:
// ignore_for_file: type=lint
对一行代码忽略规则
通过在单行代码的上方添加 ignore
注释,使得特定的非错误规则对该行代码忽略。这是一个忽略代码导致运行时错误的例子,你可能会在一个开发语言的测试上使用。
// ignore: invalid_assignment
int x = '';
使用逗号分隔的列表,可以忽略多条规则:
// ignore: invalid_assignment, const_initialized_with_non_constant_value
const x = y;
或者,将需要忽略的规则追加到相应的行后:
int x = ''; // ignore: invalid_assignment
自定义 analysis 规则
每个 分析器错误码 和 linter 规则 都有一个默认的警告等级。你可以使用分析配置文件来改变单个规则的警告等级,或者总是忽略某些规则。
分析器支持三种警告等级:
info
消息,不会造成 analysis 验证失败。例如:dead_code
warning
警告,一般不会造成 analysis 验证失败,除非分析器被配置了对待警告与错误一致。例如:invalid_null_aware_operator
error
错误,会造成 analysis 验证失败。例如:invalid_assignment
忽略规则
你可以通过使用 errors:
字段,来忽略特定的 分析器错误码 and linter 规则。列出规则,在后面加上 : ignore
。例如下方的分析配置文件,指示分析器工具忽略了 TODO 规则:
analyzer:
errors:
todo: ignore
修改规则的警告等级
你可以全局修改单个规则的警告等级。这项技术对于常规的 analysis 问题和 lints 问题都有效。例如下方的分析配置文件,指示分析器工具把无效的赋值配置为警告,把缺少返回值配置为错误,而对于无法执行到的代码,仅提供并非警告和错误的信息通知。
analyzer:
errors:
invalid_assignment: warning
missing_return: error
dead_code: info
更多资源
你还可以通过以下资源来深入了解 Dart 的静态分析: