関数呼び出しの実引数にリテラルをコメント無しで使った場合に警告するLinterを書いてみた #go

以下のリンク先にuberのgoのコーディングスタイルのガイドがあるのですが そこにあるルールに基づいたエラーを吐くLinterを書いてみました。

github.com

よく見ると元のルールとは微妙に違うような気がします。 "foo"という文字列を渡してるけど、Goodではそこにコメントは書かれてないですね。 今回実装したLinterでは、そこもエラーにしています。

今回の記事では、どういったものを実装したかを軽く説明し Experimentalな機能である、SuggestedFixというAPIを紹介します。

実装したのはどんなLinterか

関数呼び出しの実引数としてリテラルを与えた場合にエラーとして検知して警告し、コメントを挿入するFixをするものです。

例として以下のようなコードでエラーを検知してコメントを挿入します。

func x(b bool) {}
func f(names []string) {}

func main() {
    // trueを引数として渡しているのでエラーとする
    x(true)
    // sliceのリテラルを直接渡した場合もエラーとして検知する
    f([]string{"name"})
}

挿入されるコメントは以下のようなものです。

func x(b bool) {}
func f(names []string) {}

func main() {
    // trueを引数として渡しているのでエラーとする
-  x(true)
+   x(true /* b */)
    // sliceのリテラルを直接渡した場合もエラーとして検知する
-  f([]string{"name"})
+   f([]string{"name"} /* names */)
}

自作LinterでFixを実装する方法 (この機能はexperimentalです)

goにはLinterを自作するためのライブラリがあります。 下の記事に詳しく書かれており、非常に参考になりました。 基本的な実装などは以下を参考にするとよいです。

budougumi0617.github.io

最初は、skeletonというLinterのスケルトンを吐いてくれるツールを使って リポジトリを生成しました。

この記事ではFixを実装する方法を書いていきます。 Fixを実装するAPIは現状experimentalの機能なので注意してください。 今後APIが変わる可能性があります。

golang.org/x/tools/go/analysis の analysis.Passには ReportReportfという関数があります。

Fixを実装する時に使うのは、Report関数です。 この関数に渡すのは、analysis.Diagnosticというstructなんですが SuggestedFixesというフィールドがあります。

Diagnostic

このフィールドに必要なfixを渡してやることで 自動でFixしてくれるようになります。

type Diagnostic struct {
    Pos      token.Pos
    End      token.Pos // optional
    Category string    // optional
    Message  string

    // SuggestedFixes contains suggested fixes for a diagnostic which can be used to perform
    // edits to a file that address the diagnostic.
    // TODO(matloob): Should multiple SuggestedFixes be allowed for a diagnostic?
    // Diagnostics should not contain SuggestedFixes that overlap.
    // Experimental: This API is experimental and may change in the future.
    SuggestedFixes []SuggestedFix // optional
}

SuggestedFix

実際には、SuggestedFixにはTextEditsというフィールドがまたネストしていて ここにデータを与えてやる必要があります。

type SuggestedFix struct {
    // A description for this suggested fix to be shown to a user deciding
    // whether to accept it.
    Message   string
    TextEdits []TextEdit
}

TextEdit

今度はTextEditを見てみます。 TextEditはこういうstructになっています。

type TextEdit struct {
    // For a pure insertion, End can either be set to Pos or token.NoPos.
    Pos     token.Pos
    End     token.Pos
    NewText []byte
}

実際に実装したコード

実際に実装したコードは、大体以下のコード部分です。 litcomments/litcomments.go at 090842748cf52a6f3e12c7560f3ec0e20dddbf54 · wreulicke/litcomments · GitHub 抽出して記事に貼り付けます。

d := analysis.Diagnostic{
    Pos:     e.Pos(),
    Message: "Nil literal without comments is found.",
}
// 関数の引数名があったらFixをサジェストする
if name := params.At(i).Name(); name != "" {
    d.SuggestedFixes = []analysis.SuggestedFix{
        {
            Message: "Add comments",
            TextEdits: []analysis.TextEdit{
                    {
                        Pos:     e.End(), // 変更は引数の後ろに挿入したいので、PosとEndを同じ値にしている。
                        End:     e.End(),
                        NewText: []byte(fmt.Sprintf(" /* %s */", name)),
                    },
            },
        },
    }
}
pass.Report(d)

実装としては、こんな感じになりました。 関数の引数名を省略できる仕様があるので、上みたいなコードになりましたが そこまで難しくはないと思います。

まとめ

Goにおいて関数呼び出しの実引数にリテラルを書くと警告するLinterを書いてみました。 今回初めてLinterを書いてみたんですが、Linterを自作する方法はわかった、というところですね。

書いてて便利だなと思ったのは、GoのAnalyzerのAPIから 型がちゃんと取れるのは便利だなぁと思いました。 JavaでAnnotationProcessorで、Lombokの実装の中身みたいなコードを書いたときは型取れないので・・・。

experimentalが外れるのはいつなのでしょうか。 使ってみた感想としては、このSuggestedFixのAPI便利だけど もうちょっと洗練されると良いですね、って感じですね。

以下のリポジトリに色々LinterやUtilやらがホストされていて 参考になるので、覗いてみるといいと思います。 github.com

もうちょっと複雑なLinter書いてみたいですね。

そーだい本、「失敗から学ぶRDBの正しい歩き方」を読みました

「そーだい本」こと、曽根壮大さんが書かれた「失敗から学ぶRDBの正しい歩き方」を読みました。 読み終えたので感想を書いてみます。

gihyo.jp

感想

感想を書く前に、まずは書籍紹介を引用してみます。

「データベースがよく落ちる」「前任者が残したテーブル,SQLが読み解けない」「RDBMSを入れ替えたら予期せぬバグが」――MySQLPostgreSQLといったRDBMSリレーショナルデータベース管理システム)を使った業務システム,Webサービスを設計・運用していると,こういった問題によく直面するのではないでしょうか。 本書はRDB(リレーショナルデータベース)の間違った使い方(=アンチパターン)を紹介しながら,アンチパターンを生まないためのノウハウを解説します。それぞれの章では,問題解決に必要なRDBSQLの基礎知識も押さえるので,最近RDBMSを触り始めた新人の方にもお勧めです。

この本は、書籍紹介にもある通り、RDBを使う上で陥る失敗・アンチパターンについて運用を含めて、まとめられた本です。 RDBの概要については知っているけれど、設計や運用で不安がある、といった方にはオススメです。 特に運用面での落とし穴は非常に勉強になりました。

この本は全20章で書き上げられており、それぞれの章には、独特でキャッチ―な章タイトルがつけられています。 次の章の内容は、どんな内容なんだろう、と興味を惹かれるタイトルになっています。 また、それぞれの章の中では、対話形式でのアンチパターンの紹介やコラムによってより詳しく紹介したりといった形で 飽きないような工夫がされており、サクサクと読めるのでオススメです。 図もしっかり挿入されており、読みやすい本でした。 また、事例やより詳しい説明などは、外部のサイトに適宜、脚注で案内しながら書かれており 非常に丁寧に書かれている本です。

本の内容としては、1つの章の中で、アンチパターンの解説とポイントを紹介しながら PostgreSQLMySQLの実装の話を交えて、RDBの設計や運用について詳しく書かれています。 個人的には、「強すぎる制約」という9章の内容が非常に勉強になりました。

この9章では、email_address型という、RFCに準拠したデータ型を使うと 現実的にはRFC違反のメールアドレスを受け入れる可能性があるので、ALTER文を使ってデータ型の変更をする必要がある、といった話が紹介されています。 ここに、大量のレコードとindexが組み合わさると長期間のシステムの停止時間が必要になると書かれており、非常に勉強になりました。 こういう設計から来る失敗で、システムの停止時間が発生するのは機会損失に繋がるので厳しいですね。

ちなみに例で挙げられていたのは、「RFC違反のメールアドレスを使ったユーザがアプリに登録できない」といった例が挙げられており どっちみち機会損失になっているのですが・・・。

この他にも20章を通して、色々なアンチパターンがキャッチ―なタイトルと共に紹介されていて 基礎的な知識を抑えながらも、読みやすい1冊になっています。 RDBの監視の話なども書かれていますよ!

まとめ

この記事では、「失敗から学ぶRDBの正しい歩き方」を読んだ感想を書きました。 自分は、RDBについては「基本的なことは知ってるけど・・・」という状態だったので より1歩を踏み出したい、といった気持ちで手に取りました。 SQLアンチパターンはまだ読んでいません。

結論としては、非常に読みやすい本で、設計で陥りがちな失敗や運用面での落とし穴がしっかり紹介されていると同時に実装を語る上で基本的な知識も紹介されています。 「RDBの概要やRDBに関する基本的なことは知っているけど・・・」といった方にはオススメの1冊です。 PostgreSQLMySQLの実装の話も交えつつ、RDBの基礎知識を交えながら色々なアンチパターンの話を勉強できる良い本でした。

良い本だったので、前職の同僚にプレゼントしました。 SQLアンチパターンからの話もいくつか書かれており、そちらも読みたいなと思える内容でした。

次はSQLアンチパターン読もうかな?

無職になりました

9月末で無職になりました。 正社員として地方からフルリモートで働ける・雇っていただける会社を探しています。

直近のお仕事では、Java/Spring Bootを使ったAWS ECS上で動くアプリケーションの開発や TerraformやAnsibleを使った運用に関わってきました。また、ScalaでGatling書いて、負荷試験などもやっていました。 当初はバックエンドのエンジニアとして働いてたのですが、色々弄ってたことや積極的に運用に関わっていたこともあり、後半はSREとして働いていました。 SREとしては、変わらずJavaを弄ったり、Terraformを弄ったり、Golangを弄ったりしていました。 趣味でSpotbugsのコミッタをやったりOSSのメンテナンスをやったりもしました。 その他にも色々自由に動けて楽しかったです。

今後の予定は決まっていません。 正社員として、地方からフルリモートで働ける職場を探しています。 個人的なスキルの話は別途書こうかなと思っています。

最後に、一緒に働いた同僚の皆様からは色んなことを教えていただきました。 感謝しております。 私自身は至らない点が多数ありましたが 一緒に仕事を出来たことは嬉しく思います。

というわけで無職です。 次回作にご期待ください。