のんびり精進

調べた情報などをおすそ分けできれば。

【Dart/Flutter】静的解析の強化と注意

静的解析の機能は Dart や Flutter のツールに組み込まれていて、IDE でもプラグインで提供されていますが、そのまま使っても足りないことがわかりました。

例えば、const を付けるのが望ましい箇所であっても Android StudioVS Code は教えてくれませんでした。

Container(
  padding: const EdgeInsets.all(16.0),
  ...
)

アプリのパフォーマンスに影響するので、付け忘れないようにしたいものですね。

ということで、analysis_options.yaml にはどう書けばいいのかを調べました。 自分的な結論は記事の末尾に書いています。 が新たなパッケージが最近出てきたので、このすぐ後の最新情報のみでほぼ足ります。

最新情報(2021/7/1 追記)

厳選されたルールをまとめた公式のパッケージが数ヶ月前に登場しました。

Dart 用は lint と Flutter 用は flutter_lints です。

pub.dev

pub.dev

その一方で、この記事に登場する pedantic は deprecated となりました。

flutter_lints 等を使ってみたところ、今までの自分でまとめたルールと変わらない満足度でした。 今後はこれをベースにして好みで特定のルールを有効化/無効化するのが良いと思います。

※足りないルールが結構多いことに後で気づきました。

近いうちにデフォルトに

Dart 2.13 のアナウンスの記事 によれば、次の stable リリース以降はプロジェクトを作成するとデフォルトで適用される予定のようです。

We’re currently working on defining two canonical sets of lints that we’ll apply by default in Dart and Flutter projects. We expect this to be enabled by default in the next stable release.

使い方

pubspec.yamldev_dependencies にパッケージを追記し、バージョンを指定します。

dev_dependencies:
  flutter_lints: ^x.x.x  # 適切なバージョンを指定してください

あとはプロジェクトのルートに analysis_options.yaml を作成して include 一行を書くだけです。

include: package:flutter_lints/flutter.yaml

これで効くはずですが、すぐに反映されなければ解析サーバを再起動してみてください。 Android Studio / IntelliJ IDEA では「Dart Analysis」のタブの左端にある更新風の赤いアイコンを押すと再起動できます。

カスタマイズ

analysis_options.yamlprefer_const_constructors 等の個々のルール設定の他に implicit-casts の有効化/無効化などの analyzer の設定を行えます。

lintsflutter_lints を使うだけではその設定は自動的に行われないので、そこは自分で設定します。

また、個人的に use_key_in_widget_constructors のルールが有効になっているのが邪魔だったので、そのルールだけを無効化する指定をしました。 それ以外に困ること(不足など)はまだ出てきていません。

include: package:flutter_lints/flutter.yaml

analyzer:
  strong-mode:
    implicit-casts: false
    implicit-dynamic: false
  errors:
    missing_required_param: error
    missing_return: error
    todo: ignore
    deprecated_member_use_from_same_package: ignore

linter:
  rules:
    use_key_in_widget_constructors: false

個々のルールを上書きする形で true / false を設定する場合、通常のルール追加と違って先頭に - を付けてはいけません。 ご注意ください。

※strong-mode は deprecated になりました。

利用できないケース

package(アプリプロジェクト等)の中に別の package が入れ子になっているプロジェクトでは analysis_options.yaml で include するところでエラーが出てしまいます。

気になる方は下の issue を subscribe しておきましょう。

github.com


最新情報の追記は以上です。

lintsflutter_lints でルール設定が楽になって嬉しいですね。 それらでほぼ足りるようになったので、この先はじっくり読まなくて大丈夫です。

① Flutter のリポジトリにある analysis_options.yaml

flutter/analysis_options.yaml at master · flutter/flutter · GitHub

一つはこれです。 Flutter のプロジェクトのルートに置くと、Android Studio でも VS Code でもちゃんと問題点を解析して教えてくれました。

ただ、型を指定する必要のないところまでいちいち言われたり *1、コンストラクタを書く位置についてうるさかったり *2 します。 そこで、次の2つを自分でコメントアウトしました。

  • always_specify_types
  • sort_constructors_first

2020/8/8 追記

  • always_specify_types
    • 後述の pedantic にも Effective Dart にも含まれていないことに後で気づきました。 この逆のルールである omit_local_variable_types を有効にするのが良いようです。
  • sort_constructors_first
    • そのルールに従って書くようになったので今では有効にしています。

なぜ有効化されているのか理解しにくいルールも一部あるものの、それら以外は「not yet tested」のようなコメントが添えられていて理由がわかりやすいです。 カスタマイズする際のベースとして使うと良さそうです。

Google 内部で使われている analysis_options.yaml

pub.dartlang.org

これ自体はパッケージで、Google 内部で使われる Dart の静的解析のドキュメントなども含んだものです。 この中に、そのパッケージの各メジャーバージョンの analysis_options.yaml がすべて残っています。

pedantic/lib at master · google/pedantic · GitHub

特定のバージョン、あるいはバージョン指定なしのもの(= 最新)をインポートして使えます。

include: package:pedantic/analysis_options.1.8.0.yaml

使ってみたのですが、const の付け忘れは指摘してくれませんでした。それどころか unawaited_futures というオプションが有効になっていて邪魔くさかったです。*3

これの Readme に書かれている説明はとても参考になります。

2020/8/8 追記

  • ①と違ってコメントがないが、README の Unused Lints のところに不採用になったルールの説明がある
  • 大半は Effective Dart に沿っているが、含まれないルールも多数あるため、pedantic だけでは不十分
  • 更新頻度が低い(この追記時点から見て最後の更新は8ヶ月前)

③ 様々なサンプルで使われている analysis_options.yaml

github.com

https://github.com/flutter/samples/blob/master/INDEX.md

Flutter チームがメンテしているリンク集で、Flutter チーム以外の人が作ったものも含まれています。
どのようなルールにしているか参考になると思います。

2020/8/8 追記

リンク集が消えていたので Flutter 公式のサンプルのリポジトリへのリンクを貼り直しました。

Flutter Gallery 等の個々のサンプル内に置かれている analysis_options.yaml がどうなっているかを確認して取り入れると良いでしょう。

④ effective_dart パッケージ

2019 年 10 月にリリースされたパッケージです。

公式のパッケージではないようですが、公式の Customizing static analysis のページで紹介されています。 これをベースにしても良いと思います。

注意

① で良さそうに思えたのですが、yaml ファイル内には次のように書かれていました。

Until there are meta linter rules, each desired lint must be explicitly enabled. See: https://github.com/dart-lang/linter/issues/288

リンク先の Issue を見ると、Linter のルールについては様々な意見があって決着していないようです。

また、Dart のドキュメント には次のような説明があります。

A wide variety of linter rules are available. Linters tend to be nondenominational—rules don’t have to agree with each other. For example, some rules are more appropriate for library packages and others are designed for Flutter apps.

多数のルールがあって、用途によって適切なルールが異なるということですね。

さらに、② の Readme の最後 には次の注意書きがあります。

Note on Flutter SDK Style: some lints were created specifically to support Flutter SDK development. Flutter app developers should instead use standard Dart style as described in Effective Dart, and should not use these lints.

② は Flutter SDK 自体の開発のためのものを含んでいるので、アプリ開発者はそれを使わないで代わりに Effective Dart に書かれている Dart の標準スタイルを使うように、ということです。

結論(記事初出時版)

  • まず第一に Effective Dart をちゃんと読む!
  • そこに書かれているルールに沿うように analysis-options.yaml に自分で設定する
  • ② の Readme の説明も参考にする

私は結局、① を少しアレンジして使っています。 ベースとしてどれがいいかというだけなので、② や ③ が合っていればそれをベースにアレンジしてもいいかと思います。

結論(2020年8月版)

基本は同じですが、カスタマイズ方法を改善しています。

  • Effective Dart が大事だと捉えておく
  • ①をベースにする
    • ①の更新を時々確認する
  • Lint ルールのページ の各ルールに付いているバッジを参考にする
    • flutterpedanticeffective dart のいずれかのバッジがあれば基本的に有効にする
    • ただし、避けたほうが良さそうな説明書きが①のファイル内にあれば外す
      • not yet tested」「too many false positives」「conflicts with xxxxx」など
  • 迷ったら③などを参考にする
  • Dart変更履歴 で新たなルールを見つけたら追加を検討する

おまけ1

他の analysys_options.yaml をインポートしてからカスタマイズするような場合に個々のルールを有効化/無効化するには、ルールの後ろに : true: false を付けます。 詳しくは こちら をご覧ください。

おまけ2

ファイルの先頭にある analyzererrors の部分は①では warning になっていますが、私は厳しいルールが好みなので error にしています。

analyzer:
  (略)
  errors:
    missing_required_param: error
    missing_return: error

*1:② の Readme では「always_specify_types violates Effective Dart "AVOID type annotating initialized local variables" and others.」とされています。

*2:② の Readme では「sort_constructors_first does not reflect common usage.」とされています。

*3:unawaited_futures は ① のほうのファイルでは「too many false positives」という説明付きで無効にされていました。