KDOC 23: unusedを読む

unusedは使われてないパッケージの識別子を探すGoツールである。

memo

検知部分

func run(pass *analysis.Pass) (interface{}, error) { m := pass.ResultOf[ident.Analyzer].(ident.Map) for o := range m { if !skip(o) && len(m[o]) = 1 { n : m[o][0] pass.Reportf(n.Pos(), “%s is unused”, n.Name) } } return nil, nil }

  • mにはmapで識別子が入っている
  • どこか別で呼び出されるとlen(m[o])は1より大きくなる
  • 一度しか出現してないと、スライスの長さは1になる(定義の1回分)
  • m[o]には*ast.Identの配列が入ってる
  • skip()はいったんおいておいて、ロジックは明快
    • 識別子の数が1なら定義でしか出てきてないので、呼び出されてないことになる

skip関数

本質的な関数。

if o = nil || o.Parent() = types.Universe || o.Exported() { return true }

  • なんで代入なんだろう
  • objectがnilであれスキップ
  • parentがUniverseスコープ…つまり組み込みの識別子であればスキップ
  • 公開した識別子であればスキップ。パッケージ外で使われてる可能性があるから

case *types.PkgName: return true

  • types.Objectで分岐する
    • パッケージ名の場合
      • 飛ばす
      • パッケージ名は明らかに1回しか出てこないので、まあわかる
    • 変数の場合
      • 特定のケースでスキップ
      • types.Varではvarがくるときとfieldが来るときがある。フィールドを初期化するときのものか
      • オブジェクトのフィールド、無名関数、などが関係する
    • 関数の場合
      • object名がmainかつパッケージ名がmainのときはスキップ
        • mainパッケージのmain()は呼び出しはない
      • object名がinitかつスコープが親と同じ場合スキップ
      • メソッドだと、インターフェース内の関数を実装していれば使っているということになる
        • インターフェース定義の中に来るのは関数
  • o.Parent()ってなんだろう。
  • オブジェクトの名前と、オブジェクトが属するパッケージの名前がある。

case *types.Var: if o.Pkg().Scope() != o.Parent() && !(o.IsField() && !o.Anonymous() && isFieldInNamedStruct(o)) { return true }

  • scopeの知識が必要そう
    • scopeは木構造で、parentが取れる
    • 一致しなければスルー(skip) … trueを返す可能性
      • (フィールドであるかつ、無名関数ではないかつ、構造体の名前付きフィールドである(不明))…ではない
    • 一致してればskipしない … falseを返す
    • 2回目として出てきたなら、parentが取れるはずで、一致しないだろう。なのでskip