目錄

自訂靜態分析

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

在分析器的幫助下,你可以發現簡單的拼寫錯誤。例如,可能在 if 陳述式中不小心多打了一個分號:

void increment() {
  if (count < 10) ;
  count++;
}

如果配置得當,分析器會指出這個分號的位置並輸出如下警告:

info - example.dart:9:19 - Avoid empty statements. - empty_statements

分析器也能幫你找出更多細節問題。例如,也許你忘記關閉一個 sink 了:

var controller = StreamController<String>();
info - Close instances of `dart.core.Sink`. - close_sinks

在 Dart 生態系統中,Dart 分析服務和其他相關工具使用了 analyzer 來提供靜態分析。

你可以自訂靜態分析以尋找各種潛在的問題,包括在 Dart 程式設計語言規範 中規定的錯誤和警告。你同樣能透過配置 linter ——分析器的一個外掛,來確保你的程式碼遵循 Dart 程式碼風格指南高效 Dart 中其他建議的準則。諸如 Dart 編譯器 (dart compile)dart analyze, flutter analyze, 和 JetBrains IDEs 等 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
info - 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
}
info - 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
    - comment_references
    - one_member_abstracts
    - only_throw_errors
    - package_api_docs
    - prefer_final_in_for_each
    - prefer_single_quotes

停用單條規則

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

從 analysis 中排除程式碼

有時候,部分程式碼可能允許包含分析出的警告和提示。例如,你也許依賴於某個不屬於你 package 所產生的程式碼,這些程式碼可以正常執行,但是在靜態檢查中會產生警告。或者某個 linter 規則可能會出現你想關掉的誤報。

有幾種方法可以從 analysis 中排除程式碼:

  • 從 analysis 中排除整個檔案。

  • 在單個檔案中停止特定的非錯誤規則的生效。

  • 在單個檔案的某幾行中,停止特定的非錯誤規則的生效。

你同樣可以對所有檔案 停用特定的規則,或者 改變規則的警告等級

排除檔案

使用分析器選項 exclude: 在靜態分析中排除檔案。你可以列出單獨的檔案,或者用 glob 語法:

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