跳到主要内容

自定义静态分析

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

借助分析器,你可以找到简单的拼写错误。例如,可能不小心在 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 代码都使用这些规则,这些规则是核心规则的超集。

要启用任何一组 linter 规则,请将 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
    - cancel_subscriptions
    - close_sinks
    - combinators_ordering
    - comment_references
    - invalid_case_patterns
    - one_member_abstracts
    - only_throw_errors
    - prefer_single_quotes

禁用单个规则

#

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

这是一个分析选项文件的示例,该文件使用 lints 中的所有推荐规则,除了 avoid_shadowing_type_parameters。它还启用了 linter 规则 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.yaml、然后是 two.yaml、然后是 three.yaml 以及最后的 analysis_options.yaml 中找到的选项来计算的。

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

#

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

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

  • 你使用的开发机器内存少于 16 GB。
  • 你使用一个 monorepo,其中包含超过 10 个 pubspec.yamlanalysis_options.yaml 文件。

你可以在 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 linter 规则,因为它希望将 flutter 依赖项放在首位

pubspec.yaml
yaml
dependencies:
  flutter:
    sdk: flutter

  # ignore: sort_pub_dependencies
  collection: ^1.19.0

自定义分析规则

#

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

分析器支持三个严重程度级别

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

忽略规则

#

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

yaml
analyzer:
  errors:
    todo: ignore

更改规则的严重程度

#

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

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

配置 dart format

#

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

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

资源

#

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