logmi・ログミーTech

エンジニア向け勉強会の書き起こしメディア

Android開発者に贈る、Javaだけでなくkotlinでも検出できるCustom Lintの作り方

Android開発者に贈る、Javaだけでなくkotlinでも検出できるCustom Lintの作り方

2018年4月13日、渋谷を中心に活動するAndroidアプリ開発者コミュニティ「Shibuya.apk」が主催するイベント「shibuya.apk #24」が開催されました。Androidの開発に携わる現役エンジニアたちが、企業の垣根を越えて知見を共有します。トークセッション「kotlinでもJavaでも検出できるCustom Lintの作り方」に登壇したのは、DMM.comの釘宮慎之介氏。Javaだけでなく、kotlinでも使うことのできるCustom Lintの作り方を解説します。

シリーズ
shibuya.apk #24 > kotlinでもJavaでも検出できるCustom Lintの作り方
2018年4月13日のログ
スピーカー
DMM.com 釘宮慎之介 氏

kotlinでlintをかける3つの方法

釘宮慎之介氏 はい、よろしくお願いします。「kotlinでもJavaでも検出できるCustom Lintの作り方」というタイトルで発表します。自己紹介なんですが、釘宮と申します。Androidエンジニアで、DMM.comというところで働いています。 0002 さっそくアジェンダですが「kotlinを使ったAndroidプロジェクトでlintをかける時にどうしたらいいんだっけ?」という話と、「Custom Lintを作るにはどうしたらいいんだっけ?」という話をします。その後に、「じゃあ実際にandroidのlintでCustom Lintを作ってみましょう。」「(作ったandroid-lintのCustom Lintを)テストしてみましょう。使ってみましょう」という流れで話していきます。 kotlinを使ったAndroidプロジェクトでlintをかけるには、以前までは主に2つの方法しかありませんでした。それが「Ktlint」と「detekt」という方法です。 android-gralde-plugin 3.1.0ぐらいから、androidのlintでもkotlinのlintをかけることができるようになりました。android-lintのいいところは、公式が作っているという点、標準のlintのタスクでちゃんとlintしてくれるという点です。あと、後述するんですけど、Custom Lintを作る際に、型情報を使ったlintとかも作れるという点と、加えてkotlinだけじゃなくて、Javaもそのままlintのチェックをかけることができるという点があります。 (Ktlint、detekt、android-lintには)それぞれいいところがあります。所感でいうと、Ktlintとdetektはそんなに大差はなく、どっちかというとStyle寄りで、インデントのサイズとかそこらへんを見てくれるのかなというところがあります。 0009 ただ、Ktlintはルールの一部無視ができないというか、やればできるんですけど、けっこう難しかったりします。なので、僕自身はKtlintを使ってるんですけど、もしかしたらdetektのほうが使いやすいかもしれないです。 androidのlintに関しては、Styleもロジックも確認はできるんですけど、標準のlintのタスクでインデントのサイズとかを見てくれないようなので、そこがちょっと弱いのかなと思っています。 どれを使えばいいのかっていう主観なんですけど、androidのlintを外すことはできないので、Styleとかを見たいのであれば、androidのlintに加えてKtlintやdetektを併用するのがいいのかなと考えています。

Custom Lintを作るにはASTを知る必要がある

Custom Lintの作り方なんですけど、これはKtlint、detekt、androidのlintの場合も一緒で、まずはASTを知らなければ作れません。(スライドを指して)このように、コードはツリーの構造で、コンパイラやIDEとかを解釈します。 0012 例えば、「引数が複数あったら絶対改行してね」みたいなCustom Lintを作りたいとします。上がNGで、下がOKの場合、こういうlintを作りたいっていう時です。 0013 NGの時もOKの時も、PSI、ASTのツリーはこのようになります。 0014 (一部省略しているのでスライド上では)一番上に、ARGUMENTのLISTというノードがあり、その下にARGUMENTである1、カンマ、スペース、2があるというツリーになります。 さっきのOKの時、つまりちゃんと改行されてる時は、この3つ目のスペース、WHITE SPACEに改行コードが含まれていて。 0015 NGの時には改行コードが含まれてません。 0016 なので、Custom Lintを作りたい時は、こういうことを確認します。このようにツリーの構造になっていて、加えて3つ目のWHITE SPACEのところには改行コードがちゃんと含まれてるかという確認をして、逆に入ってない時はNGを出すというようにやります。 それで、「なんとなくわかったけど、まぁツリーがどうなってるかわかんないっすよね」と思うと思いますが、大丈夫です。僕もわかんないんです(笑)。これに関しては、「PsiViewer」というプラグインが用意されています。どういうものかというと、左側にコードを表示して、右側にツリー構造がどうなってるのかを表示してくれるプラグインなので、これを使ってにらめっこしながら作るといいんじゃないかなと思います。 0020

android-lintでのCustom Lintの作り方

次に、androidのlintでCustom Lintを作ってみます。例えば、RxJavaを使っているとして、Disposableを適切に処理してないでそのままにしている場合は警告を出すというCustom Lintを作ります。 0022 手順は5つです。まずプロジェクトを作る。次にDetectorを作って、Issueを作って、Registryを作る。最後にこのRegistryを登録するという手順です。 プロジェクトを作るところについては、いつも通り作ればいいんですが、「googlesamples」というgithubがあって、そこにテンプレートみたいなものがあるので、それを持ってくるとか、ならうのがいいと思います。 Detectorを作るところについては、(スライドを指して)完成形がこんなコードになります。 0025 まず、Detectorというものをextendして、UastScannerというものをimplementします。Uastというのは、Unified ASTの略です。 0026 その次に、createUastHandlerというものをオーバーライドします。つまり実際は何をやるかというと、UIElementHandlerというものを実装することになります。 0027 このUIElementHandlerは、ツリーの上から順にスキャンしていってくれて、その各種ノードが見つかった時に、各種のvisitメソッドを呼びます。このvisitメソッドをオーバーライドして使うようになります。 今回はPsiViewerを見ると、DOT_QUALIFIED_EXPRESSIONというものが来た時に確認するだけで済みそうなので、それに対応するvisitメソッドをオーバーライドするようにします。 0029 visit対象のノードクラスで、さっきのDOT_QUALIFIED_EXPRESSIONというもののクラス情報を、上のgetApplicableUastTypesというところに返してあげるようにします。これはお約束になっていて、これをしないとvisitメソッドをオーバーライドしても呼ばれないということがあるのでやります。 0030 実際のvisitメソッドの中身は、こうなっています。 0031 これ、ソースって見れますかね? 大丈夫かな。この中身は何かというと、親がBLOCKになっている時にメソッドチェーンの最後がsubscribeというものになっています。そのsubscribeのメソッドの返り値がDisposableになっている時にレポートする、となっています。 1つ(話を)戻すと、このDisposableの型が返ってくる情報がKtlintやdetektだと取れなくて、androidのlintでできる点がメリットだと思っています。 加えて、kotlinの場合以外に、Javaの場合も書くようにします。UastScannerはkotlinとJavaと区別なくvisitしてくれるんですけど、kotlinとJavaの時でPSIの型が違うので、別途こちらもPsiViewerとにらめっこしながら作ることになります。 Detectorができたら、Issueというものを作っていきます。このIssueの中には、ID情報や説明とかが入っています。 0037 その後に、作ったIssueをRegistryというところに登録して。 0038 このRegistryをbuild.gradleに記述すればCustom Lintは完成です。 0039

作ったCustom Lintのテストの書き方

作ったCustom Lintをテストしていきます。テストメソッドの書き方はこのようになっています。 0042 対象のコードを文字列で用意します。これは長くなることが多いので、ファイル読み込みにするのがいいと思います。用意したら、それをkotlinファイルとして渡す、つまりkotlinメソッドを呼んで文字列を渡します。そして、lintにかけたらエラー文言が出てくるので、適切なエラー文言になっているかをexpectメソッドで確認します。Javaの時はこの2箇所をJavaに変えればOKです。 0045 作ったCustom Lintの使い方です。これは簡単で、同一プロジェクトにCustom Lintのモジュールがある場合は、dependenciesにlintChecks projectのモジュール名を入れればOKです。他のプロジェクトとかでも同じCustom Lintを使いたい時には、JARにしてあげて、そのJARのパスをlintChecksで渡してあげればOKです。ここまでできれば無事動くことが確認できました。 まとめです。kotlinでlintをかける方法は3つあるっていうことを説明しました。kotlinでもdetektでもandroid-lintの場合でも、Custom Lintを作る際はASTを知らなければいけませんということを説明しました。最後にCustom Lintの作り方を説明しました。ご静聴ありがとうございました。 (会場拍手)

  
×
×
×
×
×
×
×
×
この話をシェアしよう!
シェア ツイート はてブ
Android開発者に贈る、Javaだけでなくkotlinでも検出できるCustom Lintの作り方

話題のログ

編集部のオススメ

人気ログランキング

TOPへ戻る