go のパッケージで特定の型のメンバをもった構造体が定義されていないことを確認したい

モチベ

swagger 定義から go client を生成するときに、 type: "number" はデフォルトでは float32 型として定義される。これは type: "number", format: "double" とすることにより float64 型に変更可能である。

該当プロジェクトでは精度的な観点から float64 を使うことが望ましかった1。定義ファイルの変更は完了したが、生成したクライアントライブラリが float32 型を含まないことを確認したくなったので静的に解析することにした。

制約

swagger による go client は構造体のメンバの型に alias された型を使わないのでその前提とする。すなわち type f32 float32 のようなことは起きないものとする。

また構造体のメンバのみに限定し、関数のシグネチャや定数・変数の類も気にしないことにする。

実装

以下のようなコードになる

func TestType(t *testing.T) {
    subPackage := "pkgname"
  set := token.NewFileSet()
    packs, err := parser.ParseDir(set, subPackage, nil, 0)
    if err != nil {
        t.Fatal("Failed to parse package:", err)
    }
  for _, pack := range packs {
        for path, f := range pack.Files {
            tokenFile := set.File(f.Pos())
            bs, err := ioutil.ReadFile(path)
            if err != nil {
                t.Fatal(err)
            }
          for _, d := range f.Decls {
                if g, ok := d.(*ast.GenDecl); ok {
                    if g.Tok == token.TYPE {
                        for _, v := range g.Specs {
                            if ts, ok := v.(*ast.TypeSpec); ok {
                                if st, ok := ts.Type.(*ast.StructType); ok {
                                    for _, f := range st.Fields.List {
                                        bp := tokenFile.Position(f.Pos())
                                        ep := tokenFile.Position(f.End())
                                        s := string(bs[bp.Offset:ep.Offset])
                                        if strings.Index(s, "float32") >= 0 {
                                            t.Errorf("should not contain float32 type at %s:%d", tokenFile.Name(), bp.Line)
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

今後の課題

ioutil.ReadFile は必要になるまで呼び出しを遅延したほうがよいのだろうが面倒なので必ず読むようにしている。これはまあやたらファイル数が多くなければ問題にならないだろう。

最終的に strings.Index で判定しているのがダサいので別の手法を試したい。


  1. 本当は decimal がほしいので10進数表現の文字列がほしいのだが