目錄

自訂靜態分析

靜態分析讓你的程式碼問題能在執行前被發現,它在防止問題產生和程式碼風格指南的遵循上很有幫助。

在分析器的幫助下,你可以發現簡單的拼寫錯誤。例如,可能在 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

該範例說明了一些最常用的最上層配置入口:

如果分析器在 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 語言模式:

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
    - collection_methods_unrelated_type
    - combinators_ordering
    - comment_references
    - dangling_library_doc_comments
    - implicit_call_tearoffs
    - invalid_case_patterns

停用單條規則

如果你引入了一個分析配置檔案(比如 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 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.

    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 的靜態分析: