Edited at
Elm 2Day 3

Elm Analyse でコード品質を担保する

More than 1 year has passed since last update.

Elm2 アドベントカレンダー 2017 の3日目です。

Elm Analyse は Elm コードの良くない匂いを嗅ぎつけて指摘してくれるツールです。


使い方

npm (または yarn)でインストールします。

npm install -g elm-analyse

プロジェクトのルートディレクトリでコマンドを打つと警告が一覧で出力されます。

elm-analyse

(場合によっては多少待ちます)


チェック事項

以下の項目をチェックします。

CoreArrayUsage

Elm 0.18 の Array にはバグがあるので、今は Skinney/elm-array-exploration を使った方がいい(0.19 でコアにマージされます)。

DebugCrash

Debug.crash が使われている。

DebugLog

Debug.log が使われている。

DropConcatOfLists

リストを無駄な結合 [1,2,3] ++ [4,5][1,2,3,4,5] のようにまとめた方が良い。

DropConsOfItemAndList

リストを無駄な結合 1::[2,3,4][1,2,3,4] のようにまとめた方が良い。

DuplicateImport

同じモジュールを2回インポートしている。

-- BAD

import Html exposing (text)
import Html exposing (Html)

DuplicateImportedVariable

同じ値を2度インポートしている。

-- BAD

import Html exposing (Html, text, Html)

DuplicateRecordFieldUpdate

レコードの同じフィールドを重複して更新している。

-- BAD

{ person | name = "John", name = "Jane" }

ExposeAll

できる限りモジュールが公開するインターフェイスを絞った方が良い。

-- BAD

module Foo exposing (..)

NoUncurriedPrefix

(++) "Hello " "World" のように中置演算子を無駄に関数として使わず、素直に "Hello " ++ "World" とした方が良い。

FunctionInLet

let ~ in 中に関数を宣言する必要はあまりないので、トップレベルに置けるなら置いた方が良い。

-- BAD

foo : Int -> Int
foo x =
let
somethingIShouldDefineOnTopLevel : Int -> Int
somethingIShouldDefineOnTopLevel y =
y + 1
in
somethingIShouldDefineOnTopLevel x

ImportAll

ワイルドカードで import すると他の人が読んだときにどのモジュールの関数かわかりにくくなるので、避けた方がいい。

-- BAD

import Html exposing (..)

LineLengthExceeded

一行の文字数が長すぎる(デフォルトの閾値は 150 文字)

MultiLineRecordFormatting

レコードは複数行で宣言した方が読みやすい。

-- BAD

type alias Person =
{ name : String , age : string , address : Address }

-- GOOD
type alias Person =
{ name : String
, age : string
, address : Address
}

NoTopLevelSignature

トップレベルの関数や値には型をつけた方がいい。

-- BAD

foo =
1

NonStaticRegex

動的に正規表現を作ると実行時エラーを起こす可能性があるため、トップレベルに宣言した方がいい。

-- BAD

foo x =
let
myInvalidRegex = Regex.regex "["
in
(myInvalidRegex, x)

RedefineVariable

同じ変数名で外の変数を上書きしない方が混乱が少ない。

-- BAD

foo : Maybe Int -> Int
foo x =
case x of
Just x ->
x
Nothing ->
1

SingleFieldRecord

1つのフィールドしかないレコードは obsolete (廃れた習慣?)だ。(意図が良くわからないが Haskell でそういう書き方をするから?)

-- BAD

type Model =
Model { input : String }

TriggerWords

TODOFIXME のようなコメントを残したままにしない方が良い(単語は設定で変えられる)。

UnformattedFile

elm-format されていない。

UnnecessaryListConcat

不必要に List.concat している。単に ++ で繋げた方が良い。

-- BAD

foo : List Int
foo =
List.concat [ [ 1, 2 ,3 ], [ a, b, c] ]

UnnecessaryParens

不必要な括弧は取り除いた方が良い。(それでも括弧が好きな人は Lisp をやろうと書いてある)

-- BAD

someCall =
(foo 1) 2

UnnecessaryPortModule

port module と不必要に宣言されている。

-- BAD

port module Foo exposing (notAPort)

notAPort : Int
notAPort = 1

UnusedImport

インポートされているが使われていないモジュールがある。

-- BAD

import Unused

UnusedImportAlias

import ... as ... でエイリアスをつけたモジュール名が使われていない。

module Foo exposing (main)

-- BAD( H が使われていない)
import Html as H exposing (Html, text)

main : Html a
main =
text "Hello"

UnusedImportedVariable

module Foo exposing (thing)

-- BAD ( div が使われていない)
import Html exposing (Html, div, text)

main : Html a
main =
text "Hello World!"

UnusedPatternVariable

パターンマッチで束縛した変数が使われていない。

-- BAD ( age が使われていない)

sayHello {name, age} = "Hello " ++ name

UnusedTopLevel

トップレベルの関数が使われていない。

UnusedTypeAlias

type alias を宣言しているが使われていない。

UnusedVariable

変数が使われていない。

-- BAD

foo : String -> Int
foo x =
1

UseConsOverConcat

[ a ] ++ foo ではなく a :: foo と書いた方がいい。


チェック項目の設定

異議あり!という項目が多々あったと思うので、プロジェクトのルートディレクトリに elm-anayse.json を置いてください。true のものだけがチェックされます。

{

"checks": {
"DebugCrash": false,
"DebugLog": false,
"DropConcatOfLists": false,
"DropConsOfItemAndList": false,
"DuplicateImport": true,
"DuplicateImportedVariable": true,
"ExposeAll": false,
"ImportAll": false,
"LineLengthExceeded": false,
"MultiLineRecordFormatting": false,
"NoTopLevelSignature": false,
"NoUncurriedPrefix": true,
"NonStaticRegex": true,
"RedefineVariable": false,
"SingleFieldRecord": false,
"UnnecessaryListConcat": false,
"UnnecessaryParens": true,
"UnusedImport": true,
"UnusedImportAlias": true,
"UnusedImportedVariable": true,
"UnusedPatternVariable": false,
"UnusedTopLevel": true,
"UnusedTypeAlias": true,
"UnusedVariable": false,
"UseConsOverConcat": false,
"UnformattedFile": true
},
"TriggerWords": {
"words": ["todo", "fixme"]
}
}


レポートをブラウザで表示

--serve オプションで結果をブラウザで表示します。

elm-analyse --serve

localhost:3000 にアクセスすると、警告の一覧やモジュールの依存関係が表示されます。

Screen Shot 2017-12-03 at 1.10.26.png

問題のコードの位置を教えてくれるのが嬉しかったりします。

Screen Shot 2017-12-03 at 1.07.46.png


まとめ

チーム開発のときにプロジェクトでルールを決めて使うと、自動でチェックできて便利かもしれませんよ!