reassign はパッケージレベルの変数が再代入されていないかをチェックするツールです。
解決する問題
io.EOF や sql.ErrNoRows のようなセンチネルエラーや、パッケージレベルの変数を再代入すると、その変数を参照する全箇所の動作が変わってしまいます。特にセンチネルエラーの再代入は errors.Is による比較が壊れる原因になります。
1 2 3 4 5 6 7 8 9 10 11
| func init() { sql.ErrNoRows = errors.New("custom error") }
func main() { _, err := db.Query("SELECT 1") if errors.Is(err, sql.ErrNoRows) { } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| var ErrNotFound = errors.New("not found")
func FindUser(ctx context.Context, id string) (*User, error) { row := db.QueryRow(ctx, "SELECT * FROM users WHERE id = $1", id) var user User if err := row.Scan(&user.ID, &user.Name); err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, ErrNotFound } return nil, fmt.Errorf("scan user: %w", err) } return &user, nil }
|
設定
公式ドキュメント
1 2 3 4 5 6 7 8 9 10 11
| version: "2"
linters: enable: - reassign settings: reassign: patterns: - "EOF" - "Err.*"
|
オプション
| オプション |
型 |
デフォルト |
説明 |
patterns |
[]string |
["EOF", "Err.*"] |
チェック対象の変数名パターン(正規表現) |
patterns の詳細
デフォルトでは EOF と Err で始まる変数名が対象です。
1 2 3 4 5 6 7 8 9
| linters: settings: reassign: patterns: - "EOF" - "Err.*" - "Default.*" - "Std(out|err)"
|
サンプル
検出例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
import ( "io" "net/http" "os" )
func example1() { io.EOF = errors.New("custom eof") }
func example2() { http.ErrServerClosed = nil }
func TestSomething(t *testing.T) { originalErr := os.ErrNotExist os.ErrNotExist = errors.New("mock") defer func() { os.ErrNotExist = originalErr }() }
|
修正例
1 2 3 4 5 6 7 8 9 10 11 12
| func example1(err error) bool { return errors.Is(err, io.EOF) }
var ErrCustomServerClosed = errors.New("custom server closed")
type FileChecker interface { Exists(path string) (bool, error) }
|
注意点
- デフォルトの
patterns は ["EOF", "Err.*"] で、ほとんどのセンチネルエラーをカバーする
- テスト内での一時的な再代入(save/restore パターン)も検出される。テストではインターフェースによる抽象化やモックを使用すべき
patterns に追加することで、DefaultClient や Stdout などの重要なパッケージレベル変数も保護できる
- パッケージ内部の変数(小文字始まり)は外部から再代入できないため、通常はエクスポートされた変数が問題になる
参考リンク