durationcheck は time.Duration 同士の誤った乗算を検出するツールです。
解決する問題
time.Duration はナノ秒を表す int64 型のエイリアスです。time.Duration 同士を乗算すると、結果が天文学的な値になります。例えば 5 * time.Second は 5 秒ですが、(5 * time.Second) * time.Second は約 138 万時間になります。この誤りはコンパイルエラーにならないため、実行時まで気づきにくいバグです。
1 2 3 4 5 6 7 8 9 10
| func waitFor(d time.Duration) { timeout := d * time.Second time.Sleep(timeout) }
func main() { waitFor(5) waitFor(5 * time.Second) }
|
1 2 3 4 5 6 7 8 9
| func waitFor(seconds int) { timeout := time.Duration(seconds) * time.Second time.Sleep(timeout) }
func main() { waitFor(5) }
|
設定
公式ドキュメント
設定オプションはありません。有効化するだけで動作します。
1 2 3 4 5 6
| version: "2"
linters: enable: - durationcheck
|
オプション
golangci-lint 経由では設定オプションはありません。
サンプル
検出例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
|
func example1(interval time.Duration) { ticker := time.NewTicker(interval * time.Second) defer ticker.Stop() }
func example2() { d := getTimeout() time.Sleep(d * time.Millisecond) }
func getTimeout() time.Duration { return 500 * time.Millisecond }
type Config struct { Timeout time.Duration }
func example3(cfg Config) { ctx, cancel := context.WithTimeout(ctx, cfg.Timeout * time.Second) defer cancel() }
|
修正例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| func example1(intervalSec int) { ticker := time.NewTicker(time.Duration(intervalSec) * time.Second) defer ticker.Stop() }
func example2() { d := getTimeout() time.Sleep(d) }
func example3(cfg Config) { ctx, cancel := context.WithTimeout(ctx, cfg.Timeout) defer cancel() }
|
注意点
time.Duration は内部的にナノ秒を表す int64 型。time.Second は 1000000000(10 億ナノ秒)なので、Duration 同士の乗算は天文学的な値になる
- 引数や設定値が
time.Duration 型か数値型かを明確にすることで防げる。環境変数名に単位を含める(例: TIMEOUT_SEC)のも有効
- 定数同士の乗算(
2 * time.Second)は正しい使い方であり、検出されない。型なし定数 2 は int 型として扱われるため
- テスト内のスリープ(
time.Sleep(100 * time.Millisecond))は正しく、検出されない
参考リンク