跳到主要内容

自定义静态分析

静态分析允许你在执行任何一行代码之前发现问题。它是一个强大的工具,用于防止 bug 并确保代码符合风格指南。

借助分析器,你可以发现简单的拼写错误。例如,一个意外的分号可能进入了 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 Analysis Server 和其他工具使用 analyzer 包来执行静态分析。

你可以自定义静态分析以查找各种潜在问题,包括 Dart 语言规范中指定的错误和警告。你还可以配置 Linter 规则,以确保你的代码符合 Dart 风格指南Effective Dart 中建议的其他准则。像 dart analyzeflutter analyze 以及 IDE 和编辑器这样的工具使用 analyzer 包来评估你的代码。

本文档解释了如何使用分析选项文件或 Dart 源代码中的注释来自定义分析器的行为。如果你想在你的工具中添加静态分析,请参阅 analyzer 包文档和 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

该示例说明了最常见的顶级条目

如果分析器在包的根目录找不到分析选项文件,它会向上遍历目录树查找。如果找不到文件,分析器将默认为标准检查。

考虑一个大型项目的以下目录结构

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-inferencestrict-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 代码包含从 jsonDecode 返回的 dynamic 值到 List<String> 的隐式向下转型,这可能在运行时失败。此模式会报告潜在错误,要求你添加显式转型或以其他方式调整代码。
✗ 静态分析:失败dart
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,此模式会因此产生推断失败提示
✗ 静态分析:失败dart
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 变量,此模式会因此产生原始类型提示
✗ 静态分析:失败dart
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 规则。Linter 往往是非宗派的——规则之间不必相互一致。例如,有些规则更适合常规 Dart 包,而另一些规则则专为 Flutter 应用设计。请注意,与静态分析不同,Linter 规则可能会产生误报。

启用 Dart 团队推荐的 Linter 规则

#

Dart 团队在 lints 包中提供了两套推荐的 Linter 规则

核心规则
帮助识别在使用或运行 Dart 代码时可能导致问题的关键问题。所有代码都应通过这些 Linter 规则。上传到 pub.dev 的包会有一个包评分,该评分部分基于通过这些规则的情况。
推荐规则
帮助识别在使用或运行 Dart 代码时可能导致的其他问题,并强制执行单一、惯用的风格和格式。我们建议所有 Dart 代码都使用这些规则,它们是核心规则的超集。

要启用任一组 Lints,请将 lints 包添加为开发依赖项

dart pub add --dev lints

然后编辑你的 analysis_options.yaml 文件以包含你偏好的规则集

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

例如,你可以像这样包含推荐的规则集

yaml
include: package:lints/recommended.yaml

启用单个规则

#

要启用单个 Linter 规则,请将 linter: 作为顶级键添加到分析选项文件中,后跟 rules: 作为二级键。在后续行中,指定要应用的规则,前面加上破折号(YAML 列表的语法)。例如

yaml
linter:
  rules:
    - always_declare_return_types
    - annotate_redeclares
    - cancel_subscriptions
    - close_sinks
    - combinators_ordering
    - comment_references
    - invalid_case_patterns
    - one_member_abstracts
    - only_throw_errors

禁用单个规则

#

如果你包含一个分析选项文件,例如 lints 中的文件,你可能想禁用一些包含的规则。禁用单个规则与启用它们类似,但需要使用映射而不是列表作为 rules: 条目的值,因此每行应包含规则名称,后跟 : false: true

这是一个分析选项文件的示例,它使用了 lints 中除 avoid_shadowing_type_parameters 外的所有推荐规则。它还启用了 await_only_futures 规则

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

linter:
  rules:
    avoid_shadowing_type_parameters: false
    await_only_futures: true

包含共享选项

#

分析选项文件可以包含在其他选项文件或甚至其他选项文件列表中指定的选项。你可以使用顶层 include: 字段来指定这些文件

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

包含的选项文件可以通过 package: 路径或相对路径指定。可以在列表中指定多个分析选项文件

analysis_options.yaml
yaml
include:
  - package:flutter_lints/recommended.yaml
  - ../team_options.yaml

包含文件中的选项可以在包含文件中被覆盖,也可以被后续包含的文件覆盖。换句话说,分析选项文件指定的选项是通过首先按列表中出现的顺序应用每个包含文件(通过递归应用此算法)中指定的选项,然后用本地定义的任何选项覆盖它们来计算的。

例如,给定以下选项文件

three.yaml
yaml
include: two.yaml
# ...

以及一个包含这些选项文件的最终选项文件

analysis_options.yaml
yaml
include:
  - one.yaml
  - three.yaml
# ...

然后,通过依次应用 one.yamltwo.yamlthree.yaml 中找到的选项,最后应用 analysis_options.yaml 中的选项来计算组合的分析选项。

启用分析器插件 (实验性)

#

分析器对插件提供实验性支持。这些插件与分析器集成,以添加新诊断、快速修复和自定义代码补全等功能。每个 analysis_options.yaml 文件只能启用一个插件。启用分析器插件会增加分析器使用的内存。

如果你的情况符合以下任一条件,请不要使用分析器插件

  • 你的开发机器内存少于 16 GB。
  • 你使用包含超过 10 个 pubspec.yamlanalysis_options.yaml 文件的 mono-repo。

你可以在 pub.dev 上找到一些分析器插件。

要启用插件

  1. 将包含插件的包添加为开发依赖项。

    dart pub add --dev <your_favorite_analyzer_plugin_package>
  2. 编辑你的 analysis_options.yaml 文件以启用插件。

    yaml
    analyzer:
      plugins:
        - your_favorite_analyzer_plugin_package

    要指定启用的特定插件功能,例如新诊断,可能需要额外的设置。

从分析中排除代码

#

有时,某些代码未能通过分析是可以接受的。例如,你可能依赖于一个你未拥有的包生成的代码——生成的代码可以工作,但在静态分析期间会产生警告。或者 Linter 规则可能导致你想要抑制的误报。

你有几种方法可以从分析中排除代码

  • 从分析中排除整个文件。
  • 阻止特定的非错误规则应用于单个文件。
  • 阻止特定的非错误规则应用于单个代码行。

你还可以禁用所有文件的特定规则更改规则的严重性

排除文件

#

要从静态分析中排除文件,请使用 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

要抑制所有 Linter 规则,请添加 type=lint 指定符

dart
// ignore_for_file: type=lint

抑制代码行的诊断

#

要抑制 Dart 代码特定行上的特定非错误诊断,请在该行代码上方放置 ignore 注释。这是一个忽略导致运行时错误的示例代码,你可以在语言测试中这样做

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

要抑制多个诊断,请提供逗号分隔的列表

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

或者,将 ignore 注释附加到它应用的行

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

抑制 pubspec 文件中的诊断

#

如果你需要在 pubspec.yaml 文件中抑制分析器报告的非错误诊断,请在受影响的行上方添加 ignore 注释。

以下示例忽略了 sort_pub_dependencies 规则,因为它想把 flutter 依赖项放在首位

pubspec.yaml
yaml
dependencies:
  flutter:
    sdk: flutter

  # ignore: sort_pub_dependencies
  collection: ^1.19.0

自定义分析规则

#

每个分析器诊断Linter 规则都有一个默认的严重性。你可以使用分析选项文件来更改单个规则的严重性,或始终忽略某些规则。

分析器支持三个严重性级别

info
不会导致分析失败的信息性消息。示例:dead_code
warning
不会导致分析失败的警告,除非分析器被配置为将警告视为错误。示例:invalid_null_aware_operator
error
导致分析失败的错误。示例:invalid_assignment

忽略规则

#

你可以使用 errors: 字段忽略特定的分析器诊断Linter 规则。列出规则,后跟 : ignore。例如,以下分析选项文件指示分析工具忽略 TODO 规则

yaml
analyzer:
  errors:
    todo: ignore

更改规则的严重性

#

你可以全局更改特定规则的严重性。此技术适用于常规分析问题和 Lints。例如,以下分析选项文件指示分析工具将无效赋值视为警告,将缺少返回值视为错误,并提供关于死代码的信息(但不是警告或错误)

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

配置 dart format

#

你可以通过在分析选项文件中添加 formatter 部分并指定你偏好的 page_width 来配置 dart format 的行为。

有关更多信息,请阅读配置格式化程序页面宽度

资源

#

使用以下资源了解有关 Dart 中静态分析的更多信息