目录

自定义静态分析

静态分析让你的代码问题能在运行前被发现,它在防止问题产生和代码风格指南的遵循上很有帮助。

在分析器的帮助下,你可以发现简单的拼写错误。例如,可能在 if 语句中不小心多打了一个分号:

dart
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 了:

dart
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 文件同样的目录下。

这是一个分析配置文件的示例:

analysis_options.yaml
yaml
include: package:lints/recommended.yaml

analyzer:
  exclude: [build/**]
  language:
    strict-casts: true
    strict-raw-types: true

linter:
  rules:
    - cancel_subscriptions

该示例说明了一些最常用的顶级配置入口:

如果分析器在 package 的根目录下无法找到一个分析配置文件,它将会往下查找整个目录树。如果还是没有可用的配置文件,分析器则默认使用标准检查规则。

对于如下所示的一个大型项目的目录结构而言:

project root contains analysis_options.yaml (#1) and 3 packages, one of which (my_package) contains an analysis_options.yaml file (#2).

分析器使用 #1 文件来分析 my_other_packagemy_other_other_package 中的代码,使用 #2 文件来分析 my_package 中的代码。

启用更严格的类型检查

#

如果你想要比 Dart 类型系统 所要求的更加严格的类型检查,考虑开启 strict-castsstrict-inference,和 strict-raw-types 语言模式:

analysis_options.yaml
yaml
analyzer:
  language:
    strict-casts: true
    strict-inference: true
    strict-raw-types: true

你可以单独或一起使用这些模式,他们默认都为 false 状态。

strict-casts: <bool>
设为 true 可确保类型推理引擎不再将 dynamic 进行隐式类型转换。下方的 Dart 代码在 List<String> 参数上传递了一个 jsonDecode 方法的返回值,实际是将返回的 dynamic 做了隐式向下转换,在运行时可能导致错误。该模式会报告此类潜在的错误,要求你添加一个显示的类型转换或者调整你的代码。

✗ static analysis: failuredart
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 提示:

✗ static analysis: failuredart
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 提示。

✗ static analysis: failuredart
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 文件来引入你想要的规则集合:

yaml
include: package:lints/<RULE_SET>.yaml

例如,你可以像这样引入推荐规则的集合:

yaml
include: package:lints/recommended.yaml

启用单条规则

#

在分析配置文件中添加顶层 key linter: 来启用单条规则,紧跟着用 rules: 作为二级 key。在后续行中,以短横杠为前缀(YAML 列表的语法),指定你想要添加的规则。例如:

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。

analysis_options.yaml
yaml
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 and analysis_options.yaml files.

You can find a few analyzer plugins on pub.dev.

To enable a plugin:

  1. Add the package containing the plugin as a dev dependency.

    $ dart pub add --dev <your_favorite_analyzer_plugin_package>
  2. Edit your analysis_options.yaml file to enable the plugin.

    yaml
    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 的路径。

yaml
analyzer:
  exclude:
    - lib/client.dart
    - lib/server/*.g.dart
    - test/_data/**

对单个文件忽略诊断

#

通过在文件中添加 ignore_for_file 注释,使得特定的非错误诊断对该文件忽略。

dart
// ignore_for_file: unused_local_variable

该操作对整个文件都生效,不论代码是在注释之前还是之后,对于生成的代码尤其有用。

使用逗号分隔的列表,可以忽略多条诊断:

dart
// ignore_for_file: unused_local_variable, duplicate_ignore, dead_code

添加 type=lint 以忽略所有的 linter 规则:

dart
// ignore_for_file: type=lint

对一行代码忽略诊断

#

通过在单行代码的上方添加 ignore 注释,使得特定的非错误诊断对该行 Dart 代码忽略。这是一个忽略代码导致运行时错误的例子,你可能会在一个开发语言的测试上使用。

dart
// ignore: invalid_assignment
int x = '';

使用逗号分隔的列表,可以忽略多条规则:

dart
// ignore: invalid_assignment, const_initialized_with_non_constant_value
const x = y;

或者,将需要忽略的规则追加到相应的行后:

dart
int x = ''; // ignore: invalid_assignment

Suppressing diagnostics in a pubspec file

#

If you need to suppress a non-error diagnostic from the analyzer in a pubspec.yaml file, add an ignore comment above the affected line.

The following example ignores the sort_pub_dependencies lint as it wants to put the flutter dependency first:

pubspec.yaml
yaml
dependencies:
  flutter:
    sdk: flutter

  # ignore: sort_pub_dependencies
  collection: ^1.18.0

自定义 analysis 规则

#

每个 分析器错误linter 规则 都有一个默认的警告等级。你可以使用分析配置文件来改变单个规则的警告等级,或者总是忽略某些规则。

分析器支持三种警告等级:

info
消息,不会造成 analysis 验证失败。例如:dead_code

warning
警告,一般不会造成 analysis 验证失败,除非分析器被配置了对待警告与错误一致。例如:invalid_null_aware_operator

error
错误,会造成 analysis 验证失败。例如:invalid_assignment

忽略规则

#

你可以通过使用 errors: 字段,来忽略特定的 分析器错误 and linter 规则。列出规则,在后面加上 : ignore。例如下方的分析配置文件,指示分析器工具忽略了 TODO 规则:

yaml
analyzer:
  errors:
    todo: ignore

修改规则的警告等级

#

你可以全局修改单个规则的警告等级。这项技术对于常规的 analysis 问题和 lints 问题都有效。例如下方的分析配置文件,指示分析器工具把无效的赋值配置为警告,把缺少返回值配置为错误,而对于无法执行到的代码,仅提供并非警告和错误的信息通知。

yaml
analyzer:
  errors:
    invalid_assignment: warning
    missing_return: error
    dead_code: info

更多资源

#

你还可以通过以下资源来深入了解 Dart 的静态分析: