自定义静态分析
分析配置文件
启用更严格的类型检查
启用和停用 linter 规则
- Enabling analyzer plugins (experimental)
从 analysis 中排除代码
自定义 analysis 规则
更多资源
静态分析让你的代码问题能在运行前被发现,它在防止问题产生和代码风格指南的遵循上很有帮助。
在分析器的帮助下,你可以发现简单的拼写错误。例如,可能在 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 - Unclosed instance of 'Sink'. Try invoking 'close' in the function in which the 'Sink' was created. - 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
warning - 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
}
warning - 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
- combinators_ordering
- comment_references
- invalid_case_patterns
- library_annotations
- one_member_abstracts
- only_throw_errors
停用单条规则
如果你引入了一个分析配置文件(比如 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
Enabling analyzer plugins (experimental)
The analyzer has experimental support for plugins.
These plugins integrate with the analyzer to add functionality
such as new diagnostics, quick fixes, and custom code completion.
You can enable only one plugin per analysis_options.yaml
file.
Enabling an analyzer plugin increases how much memory the analyzer uses.
Don’t use analyzer plugins if your situation meets either of the following conditions:
- You use a development machine with less than 16 GB of memory.
- You use a mono-repo with more than 10
pubspec.yaml
andanalysis_options.yaml
files.
You can find a few analyzer plugins on pub.dev.
To enable a plugin:
-
Add the package containing the plugin as a dev dependency.
$ dart pub add --dev <your_favorite_analyzer_plugin_package>
-
Edit your
analysis_options.yaml
file to enable the plugin.analyzer: plugins: - your_favorite_analyzer_plugin_package
To indicate specific plugin functionality to enable, such as new diagnostics, additional setup might be required.
从 analysis 中排除代码
有时候,部分代码可能允许包含分析出的警告和提示。例如,你也许依赖于某个不属于你 package 所生成的代码,这些代码可以正常运行,但是在静态检查中会产生警告。或者某个 linter 规则可能会出现你想关掉的误报。
有几种方法可以从 analysis 中排除代码:
-
从 analysis 中排除整个文件。
-
在单个文件中停止特定的非错误规则的生效。
-
在单个文件的某几行中,停止特定的非错误规则的生效。
你同样可以对所有文件 停用特定的规则,或者 改变规则的警告等级。
排除文件
使用分析器选项 exclude:
在静态分析中排除文件。你可以列出单独的文件,或者用 glob 语法。所有 glob 都使用相对于 analysis_options.yaml
的路径。
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 的静态分析: