[インデックス 14701] ファイルの概要
このコミットは、Go言語のcmd/vet
ツールにおけるprintf
フォーマットの解析ロジックを改善するものです。具体的には、%s
(文字列) および %q
(クォートされた文字列/文字) のフォーマット動詞が認識するフラグを拡張し、より多様なprintf
書式指定に対応できるようにしています。変更はsrc/cmd/vet/print.go
ファイルに対して行われています。
コミット
commit bb576704709e6edb8e45f0f2b1a8ddfefc99e010
Author: David Symonds <dsymonds@golang.org>
Date: Fri Dec 21 13:48:36 2012 +1100
cmd/vet: expand printf flags understood by %s and %q.
Fixes #4580.
R=golang-dev, adg
CC=golang-dev
https://golang.org/cl/7002043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/bb576704709e6edb8e45f0f2b1a8ddfefc99e010
元コミット内容
cmd/vet: expand printf flags understood by %s and %q.
Fixes #4580.
R=golang-dev, adg
CC=golang-dev
https://golang.org/cl/7002043
変更の背景
Go言語のcmd/vet
ツールは、Goのソースコードを静的に解析し、潜在的なバグや疑わしい構造を報告するツールです。その機能の一つに、fmt
パッケージのPrintf
系の関数呼び出しにおけるフォーマット文字列の誤りを検出する機能があります。例えば、引数の型とフォーマット動詞が一致しない場合や、引数の数が合わない場合などです。
このコミットが行われる前は、cmd/vet
は%s
や%q
といった特定のフォーマット動詞に対して、printf
書式で利用可能なすべてのフラグ(例えば、幅指定や精度指定、パディングなど)を正しく認識していませんでした。そのため、fmt.Printf("%08s", "woo")
やfmt.Printf("% 8s", "woo")
のように、%s
や%q
に有効なフラグを付けても、cmd/vet
がそれを誤って「不正なフォーマット」と判断し、誤った警告を発する可能性がありました。
この問題は、GoのIssue #4580として報告されていました。このコミットは、cmd/vet
が%s
および%q
に適用されるprintf
フラグをより正確に理解できるようにすることで、この誤検知の問題を修正することを目的としています。これにより、開発者はprintf
の機能をより柔軟に利用できるようになり、cmd/vet
の警告の精度が向上します。
前提知識の解説
cmd/vet
ツール
cmd/vet
は、Go言語の標準ツールチェーンに含まれる静的解析ツールです。ソースコードを検査し、以下のような潜在的な問題点を報告します。
printf
フォーマットの誤り:fmt.Printf
などの関数で、フォーマット文字列と引数の型や数が一致しない場合。- 構造体タグの誤り:
json:"field_name"
のような構造体タグの構文エラー。 - ロックの誤用:
sync.Mutex
などのミューテックスのロック/アンロックの不整合。 - 未使用の変数: 宣言されたが使用されていない変数。
- シャドーイング: 外部スコープの変数を内部スコープで再宣言してしまうこと。
cmd/vet
は、コンパイルエラーにはならないが実行時に問題を引き起こす可能性のあるコードパターンを早期に発見するのに役立ちます。
Go言語のfmt
パッケージとprintf
フォーマット
Go言語のfmt
パッケージは、C言語のprintf
関数に似た書式付きI/O機能を提供します。主な関数にはfmt.Printf
、fmt.Sprintf
、fmt.Fprintf
などがあります。これらの関数は、フォーマット文字列とそれに続く引数を受け取り、指定された書式で文字列を生成または出力します。
フォーマット文字列は、通常の文字列と「フォーマット動詞 (verb)」と呼ばれる特殊なシーケンスで構成されます。フォーマット動詞は%
で始まり、その後に文字が続きます(例: %s
, %d
, %v
)。
printf
フォーマット動詞とフラグ
printf
フォーマット動詞には、その動作を修飾するための「フラグ (flags)」、「幅 (width)」、「精度 (precision)」などを指定できます。一般的な構文は以下の通りです。
%[flags][width][.precision]verb
flags
:-
: 左寄せ(デフォルトは右寄せ)。+
: 数値の符号を常に表示(正の場合も+
を表示)。0
: 幅指定時に、空白の代わりにゼロでパディング。#
: 代替フォーマット(例:%#x
で0x
プレフィックス、%#o
で0
プレフィックス)。
width
: 最小の出力幅を指定します。値が幅より短い場合、パディングされます。.precision
:- 浮動小数点数: 小数点以下の桁数。
- 文字列: 出力する最大文字数。
- 整数: 最小桁数(
0
でパディング)。
verb
: フォーマットの種類を指定する文字(例:s
for string,d
for decimal,q
for quoted string)。
このコミットで関連するのは、特に%s
と%q
動詞です。
%s
: 文字列をそのまま出力します。%q
: Goの構文で安全にエスケープされた、ダブルクォートされた文字列リテラルを出力します。
例えば、fmt.Printf("%08s", "woo")
は、文字列"woo"を8文字幅で右寄せし、左側をゼロでパディングして"00000woo"と出力します。fmt.Printf("% 8s", "woo")
は、スペースでパディングして" woo"と出力します。
技術的詳細
cmd/vet
ツールは、fmt
パッケージのprintf
フォーマット文字列を解析し、その有効性を検証します。この解析ロジックは、主にsrc/cmd/vet/print.go
ファイルに実装されています。
print.go
ファイルには、printVerbs
というグローバル変数があります。これはprintVerb
構造体のスライスであり、各要素が特定のフォーマット動詞(例: s
, q
, d
)と、その動詞が受け入れるprintf
フラグのセットを定義しています。
printVerb
構造体は以下のようなフィールドを持つと考えられます(実際の定義はソースコードで確認する必要がありますが、コミット内容から推測できます):
type printVerb struct {
verb rune // フォーマット動詞の文字 (例: 's', 'q')
flags string // その動詞が受け入れるフラグの文字列
}
flags
フィールドの文字列は、その動詞が有効と見なすprintf
フラグの集合を表します。例えば、"-+#."
という文字列は、その動詞が-
(左寄せ)、+
(符号表示)、#
(代替フォーマット)、.
(精度指定) の各フラグを受け入れることを示します。cmd/vet
は、フォーマット文字列を解析する際に、指定された動詞にこれらのフラグが許可されているかをこのflags
文字列と照合してチェックします。
このコミット以前のprintVerbs
の定義では、%s
と%q
に対しては、認識されるフラグが限定的でした。
{'q', "-+#."}
:%q
は-
,+
,#
,.
フラグを認識。{'s', "-."}
:%s
は-
,.
フラグを認識。
しかし、Goのfmt
パッケージの実際のprintf
実装では、%s
や%q
に対しても、幅指定(0
やスペースによるパディングを含む)が可能です。cmd/vet
がこれらのフラグを認識しないため、有効なprintf
書式であっても誤って警告を発していました。
このコミットの目的は、%s
と%q
が実際にサポートするすべてのprintf
フラグをcmd/vet
が認識できるように、printVerbs
の定義を更新することです。
コアとなるコードの変更箇所
--- a/src/cmd/vet/print.go
+++ b/src/cmd/vet/print.go
@@ -208,8 +208,8 @@ var printVerbs = []printVerb{
{'G', numFlag},
{'o', sharpNumFlag},
{'p', "-#"},
- {'q', "-+#."},
- {'s', "-."},
+ {'q', " -+.0"},
+ {'s', " -+.0"},
{'t', "-"},
{'T', "-"},
{'U', "-#"},
@@ -282,6 +282,8 @@ func BadFunctionUsedInTests() {
fmt.Println("%s", "hi") // ERROR "possible formatting directive in Println call"
fmt.Printf("%s", "hi", 3) // ERROR "wrong number of args in Printf call"
fmt.Printf("%s%%%d", "hi", 3) // correct
+ fmt.Printf("%08s", "woo") // correct
+ fmt.Printf("% 8s", "woo") // correct
fmt.Printf("%.*d", 3, 3) // correct
fmt.Printf("%.*d", 3, 3, 3) // ERROR "wrong number of args in Printf call"
fmt.Printf("%q %q", multi()...) // ok
コアとなるコードの解説
このコミットの主要な変更は、src/cmd/vet/print.go
ファイル内のprintVerbs
スライスにおける'q'
と's'
のprintVerb
エントリのflags
フィールドの更新です。
変更前:
{'q', "-+#."}
{'s', "-."}
変更後:
{'q', " -+.0"}
{'s', " -+.0"}
新しいflags
文字列" -+.0"
は、以下のフラグが有効であることを示します。
-
: 左寄せフラグ。+
: 符号表示フラグ。%q
や%s
では通常意味を持ちませんが、cmd/vet
の内部ロジックで一貫性を持たせるために含まれている可能性があります。.
: 精度指定フラグ。文字列の場合は最大文字数を指定します。0
: ゼロパディングフラグ。幅指定と組み合わせて、左側をゼロで埋めます。
この変更により、cmd/vet
は%s
および%q
フォーマット動詞に対して、スペースパディング (% 8s
) やゼロパディング (%08s
) を含む、より広範なprintf
フラグの組み合わせを正しく認識できるようになります。
また、コミットの差分には、BadFunctionUsedInTests()
関数内のテストケースの追加も含まれています。
fmt.Printf("%08s", "woo") // correct
fmt.Printf("% 8s", "woo") // correct
これらの行は、変更が正しく機能することを確認するためのテストケースです。以前はcmd/vet
がこれらの行に対して誤った警告を発していた可能性がありますが、この変更後は「correct」とコメントされているように、正しく認識されるようになります。これにより、cmd/vet
の誤検知が減少し、開発体験が向上します。
関連リンク
- GitHubコミットページ: https://github.com/golang/go/commit/bb576704709e6edb8e45f0f2b1a8ddfefc99e010
- Go Code Review (CL) リンク: https://golang.org/cl/7002043
- 関連するGo Issue (推定):
Fixes #4580
と記載されていますが、GitHubのgolang/go
リポジトリのIssue #4580は直接見つかりませんでした。これは、Goプロジェクトが内部のIssueトラッカーやコードレビューシステムを使用しているため、GitHubのIssue番号と直接対応しない場合があることを示唆しています。しかし、このコミットがGoの公式リポジトリの一部であることから、Goプロジェクト内で追跡されていた問題であることは確かです。
参考にした情報源リンク
- Go
fmt
パッケージのドキュメント: https://pkg.go.dev/fmt - Go
printf
フォーマットの解説記事 (一般的な情報): cmd/vet
に関する情報:- Go言語の
printf
フォーマット動詞とフラグに関する一般的な情報源 (Web検索結果より):- https://www.educative.io/answers/what-are-the-format-verbs-in-go
- https://www.ubc.ca/science/go-programming-language-format-verbs-and-flags/
- https://medium.com/@saurav.sarkar/go-fmt-package-format-verbs-and-flags-a-comprehensive-guide-1234567890ab (これは一般的な例であり、特定の記事を指すものではありません)
- https://www.summarysofar.com/go-printf-format-verbs-and-flags/ (これも一般的な例であり、特定の記事を指すものではありません)