コメントディレクティブ
Goには、//go:noescape
や//go:linkname
などのコメントでコンパイラへの指示を記述するコメントディレクティブがあります。一応、ドキュメントには記載がありますが、多くのGoエンジニアはあまり機会はないでしょう。
ドキュメントを読むと//line
で始まるコメントは特別な意味を持つことが分かります。ここではこのlineディレクティブについて解説します。
lineディレクティブ
ドキュメントには、以下のように記述した場合に、lineディレクティブとして認識されるようです。行コメントだけはなく、ブロックコメントも対象となります。//
や/*
の後ろにスペースを含んでは行けなかったり、:
が必ず含まれていなければならなかったりと、細かなルールがあります。
//line :line
//line :line:col
//line filename:line
//line filename:line:col
/*line :line*/
/*line :line:col*/
/*line filename:line*/
/*line filename:line:col*/
lineディレクティブには、ファイル名、行、列を書けます。//line
で始まる行コメントの場合は、次の行の先頭の文字が、そのファイル名、行、列であるようにコンパイラに指示します。
ファイル名を省略すると、直前のlineディレクティブによる指示と同じ、はじめてのlineディレクティブの場合は元のファイル名になります。
列を省略すると、次に列が判明する(他のlineディレクティブで)まで、その範囲は列が不明となり、コンパイラはエラーなどの報告で列情報を使用しません。
ブロックコメントで書いた場合は、*/
直後の文字から対象となります。それでは、実際に書いてみましょう。次のように、2行目にpackage
句も書かずにいきなりError
と書いてみます。
//line hoge.go:100
Error
1行目にlineディレクティブがあるため、本来なら2行目でコンパイラエラーになるはずですが、コンパイルしようとすると次のようなエラーがでます。
$ go build a.go
hoge.go:100: expected 'package', found Error
hoge.go
の100行目でエラーが発生していることになっています。ファイル名だけではなく、行数も変わっています。また、列は表示されていません。
それでは、次のコードはどうなるでしょうか。1行目にエラーがあります。
/*line fuga.go:200:10*/Error
ビルドしてみましょう。次のようなエラーが表示されます。
$ go run b.go
fuga.go:200:10: expected 'package', found Error
今度は列も表示されました。面白いですね。
何に使われているのか?
それではこの機能は何のためにあるのでしょうか?
実際に使われている箇所はあるのでしょうか?
実は、go test -cover
を実行した際に内部で実行されるgo tool cover
というカバレッジを計測するためのコードを対象のGoファイルに差し込むためのツールで使われています。
たとえば、次のようなコードa.go
があった場合を考えます。
package a
func f() {
if true {
println("A")
}
println("B")
}
go tool cover -mode set a.go
を実行してみます。そうすると、次のようなソースコードが生成されます。
//line a.go:1
package a
func f() {GoCover.Count[0] = 1;
if true {GoCover.Count[2] = 1;
println("A")
}
GoCover.Count[1] = 1;println("B")
}
var GoCover = struct {
Count [3]uint32
Pos [3 * 3]uint32
NumStmt [3]uint16
} {
Pos: [3 * 3]uint32{
3, 4, 0xa000a, // [0]
7, 7, 0xe0002, // [1]
4, 6, 0x3000a, // [2]
},
NumStmt: [3]uint16{
1, // 0
1, // 1
1, // 2
},
}
1行目にlineディレクティブが記述されています。go test -cover
を実行すると、go tool cover
が生成したコードが元のコードの代わりにコンパイルに使用されます。そのため、コンパイルエラーがあった場合、元のファイルと違う名前でエラーメッセージが表示されてしまうため、lineディレクティブでファイル名を変更することで違和感が無いようにしています。
生成されたコードをよく見ると、ファイルの末尾以外では改行を増やさないようにコードを追加しているのが分かります。コンパイラエラーなどで行数を表示する際に変わってしまわないように工夫がされています。
go tool cover
と同じように、元のソースコードと差し替えてコード生成したソースコードをコンパイルに用いるようなツールを作っている場合は、lineディレクティブは便利です。
go tool cover
については、Gopher塾 #1でも扱っています。今後のアドベントカレンダーでgo tool cover
の挙動を追う方法についても書く予定ですので、楽しみにしててください。