Gopls: アナライザー

Goplsには、go vetで使用されるような、プラグイン可能なモジュール式静的アナライザーのドライバーが含まれています。

ほとんどのアナライザーはコードの誤りを報告し、一部はエディターに直接適用できる「クイックフィックス」を提案します。コードを編集するたびに、goplsはアナライザーを再実行します。アナライザーの診断は、テストを実行する前、またはファイルを保存する前に、バグをより早く検出するのに役立ちます。

このドキュメントでは、さまざまなソースからアナライザーを集約するgoplsで利用可能なアナライザーのスイートについて説明します。

  • go vetスイートからの通常のバグ検出アナライザー(例: printf; 完全なリストはgo tool vet helpを参照);
  • go vetでの使用を妨げる、より実質的な依存関係を持ついくつかのアナライザー(例: nilness);
  • 一般的な誤りに対するクイックフィックスを提案することで、コンパイルエラーを補強するアナライザー(例: fillreturns); および
  • 可能なスタイル改善を提案するいくつかのアナライザー(例: simplifyrange)。

アナライザーを有効または無効にするには、analyses設定を使用します。

さらに、goplsにはstaticcheckスイートが含まれています。 staticcheckブールオプションが設定されていない場合、これらのアナライザーの半分強がデフォルトで有効になります。このサブセットは、精度と効率のために選択されています。 staticchecktrueに設定すると完全なセットが有効になり、falseに設定すると完全なセットが無効になります。

Staticcheckアナライザーは、他のすべてのアナライザーと同様に、analyzers構成設定を使用して明示的に有効または無効にできます。この設定はstaticcheck設定よりも優先されるため、staticcheckの値(true/false/未設定)に関係なく、好みに合わせてアナライザーのセットを調整できます。

QF1001: ド・モルガンの法則を適用

2021.1以降利用可能

デフォルト: オフ。 "analyses": {"QF1001": true}を設定して有効にします。

パッケージドキュメント: QF1001

QF1002: タグなしスイッチをタグ付きスイッチに変換

単一の変数を一連の値と比較するタグなしスイッチは、タグ付きスイッチに置き換えることができます。

変更前

switch {
case x == 1 || x == 2, x == 3:
    ...
case x == 4:
    ...
default:
    ...
}

変更後

switch x {
case 1, 2, 3:
    ...
case 4:
    ...
default:
    ...
}

2021.1以降利用可能

デフォルト: オン。

パッケージドキュメント: QF1002

QF1003: if/else-ifチェーンをタグ付きスイッチに変換

同じ変数を値と比較する一連のif/else-ifチェックは、タグ付きスイッチに置き換えることができます。

変更前

if x == 1 || x == 2 {
    ...
} else if x == 3 {
    ...
} else {
    ...
}

変更後

switch x {
case 1, 2:
    ...
case 3:
    ...
default:
    ...
}

2021.1以降利用可能

デフォルト: オン。

パッケージドキュメント: QF1003

QF1004: n == -1のstrings.Replaceの代わりにstrings.ReplaceAllを使用

2021.1以降利用可能

デフォルト: オン。

パッケージドキュメント: QF1004

QF1005: math.Powの呼び出しを展開

math.Powの一部の使用は、基本的な乗算に単純化できます。

変更前

math.Pow(x, 2)

変更後

x * x

2021.1以降利用可能

デフォルト: オフ。 "analyses": {"QF1005": true}を設定して有効にします。

パッケージドキュメント: QF1005

QF1006: if+breakをループ条件に移動

変更前

for {
    if done {
        break
    }
    ...
}

変更後

for !done {
    ...
}

2021.1以降利用可能

デフォルト: オフ。 "analyses": {"QF1006": true}を設定して有効にします。

パッケージドキュメント: QF1006

QF1007: 条件付き代入を変数宣言に結合

変更前

x := false
if someCondition {
    x = true
}

変更後

x := someCondition

2021.1以降利用可能

デフォルト: オフ。 "analyses": {"QF1007": true}を設定して有効にします。

パッケージドキュメント: QF1007

QF1008: セレクター式から埋め込みフィールドを省略

2021.1以降利用可能

デフォルト: オフ。 "analyses": {"QF1008": true}を設定して有効にします。

パッケージドキュメント: QF1008

QF1009: ==演算子の代わりにtime.Time.Equalを使用

2021.1以降利用可能

デフォルト: オン。

パッケージドキュメント: QF1009

QF1010: バイトスライスを印刷するときに文字列に変換

2021.1以降利用可能

デフォルト: オン。

パッケージドキュメント: QF1010

QF1011: 変数宣言から冗長な型を省略

2021.1以降利用可能

デフォルト: オフ。 "analyses": {"QF1011": true}を設定して有効にします。

パッケージドキュメント: QF1011

QF1012: x.Write(fmt.Sprintf(…))の代わりにfmt.Fprintf(x, …)を使用

2022.1以降利用可能

デフォルト: オン。

パッケージドキュメント: QF1012

S1000: シングルケースセレクトの代わりにプレーンなチャネル送受信を使用

単一のケースを持つselectステートメントは、単純な送受信に置き換えることができます。

変更前

select {
case x := <-ch:
    fmt.Println(x)
}

変更後

x := <-ch
fmt.Println(x)

2017.1以降利用可能

デフォルト: オン。

パッケージドキュメント: S1000

S1001: forループをcopyの呼び出しに置き換える

あるスライスから別のスライスに要素をコピーするにはcopy()を使用します。同じサイズの配列の場合、単純な代入を使用できます。

変更前

for i, x := range src {
    dst[i] = x
}

変更後

copy(dst, src)

2017.1以降利用可能

デフォルト: オン。

パッケージドキュメント: S1001

S1002: ブール定数との比較を省略

変更前

if x == true {}

変更後

if x {}

2017.1以降利用可能

デフォルト: オフ。 "analyses": {"S1002": true}を設定して有効にします。

パッケージドキュメント: S1002

S1003: strings.Indexの呼び出しをstrings.Containsに置き換える

変更前

if strings.Index(x, y) != -1 {}

変更後

if strings.Contains(x, y) {}

2017.1以降利用可能

デフォルト: オン。

パッケージドキュメント: S1003

S1004: bytes.Compareの呼び出しをbytes.Equalに置き換える

変更前

if bytes.Compare(x, y) == 0 {}

変更後

if bytes.Equal(x, y) {}

2017.1以降利用可能

デフォルト: オン。

パッケージドキュメント: S1004

S1005: 不要なブランク識別子の使用を削除

多くの場合、ブランク識別子への割り当ては不要です。

変更前

for _ = range s {}
x, _ = someMap[key]
_ = <-ch

変更後

for range s{}
x = someMap[key]
<-ch

2017.1以降利用可能

デフォルト: オフ。 "analyses": {"S1005": true}を設定して有効にします。

パッケージドキュメント: S1005

S1006: 無限ループには「for { ... }」を使用

無限ループの場合、「for { ... }」を使用するのが最も慣用的な選択です。

2017.1以降利用可能

デフォルト: オフ。 "analyses": {"S1006": true}を設定して有効にします。

パッケージドキュメント: S1006

S1007: 生文字列リテラルを使用して正規表現を単純化

生文字列リテラルは、引用符の代わりにバッククォートを使用し、エスケープシーケンスをサポートしません。これは、バックスラッシュをエスケープする必要なく自由に使用できることを意味します。

正規表現は独自のエスケープシーケンスを持つため、生文字列は読みやすさを向上させることができます。

変更前

regexp.Compile("\\A(\\w+) profile: total \\d+\\n\\z")

変更後

regexp.Compile(`\A(\w+) profile: total \d+\n\z`)

2017.1以降利用可能

デフォルト: オン。

パッケージドキュメント: S1007

S1008: ブール式のリターンを単純化

変更前

if <expr> {
    return true
}
return false

変更後

return <expr>

2017.1以降利用可能

デフォルト: オフ。 "analyses": {"S1008": true}を設定して有効にします。

パッケージドキュメント: S1008

S1009: スライス、マップ、チャネルでの冗長なnilチェックを省略

len関数は、nilのものも含め、すべてのスライス、マップ、チャネルに対して定義されており、それらの長さはゼロです。長さがゼロでないことを確認する前にnilをチェックする必要はありません。

変更前

if x != nil && len(x) != 0 {}

変更後

if len(x) != 0 {}

2017.1以降利用可能

デフォルト: オン。

パッケージドキュメント: S1009

S1010: デフォルトのスライスインデックスを省略

スライスするとき、2番目のインデックスは値の長さにデフォルト設定されるため、s[n:len(s)]とs[n:]は同等です。

2017.1以降利用可能

デフォルト: オン。

パッケージドキュメント: S1010

S1011: 2つのスライスを連結するために単一のappendを使用

変更前

for _, e := range y {
    x = append(x, e)
}

for i := range y {
    x = append(x, y[i])
}

for i := range y {
    v := y[i]
    x = append(x, v)
}

変更後

x = append(x, y...)
x = append(x, y...)
x = append(x, y...)

2017.1以降利用可能

デフォルト: オフ。 "analyses": {"S1011": true}を設定して有効にします。

パッケージドキュメント: S1011

S1012: time.Now().Sub(x)をtime.Since(x)に置き換える

time.Sinceヘルパーはtime.Now().Sub(x)を使用するのと同じ効果がありますが、より読みやすいです。

変更前

time.Now().Sub(x)

変更後

time.Since(x)

2017.1以降利用可能

デフォルト: オン。

パッケージドキュメント: S1012

S1016: 構造体フィールドを手動でコピーする代わりに型変換を使用

同一のフィールドを持つ2つの構造体型は、相互に変換できます。Goの古いバージョンでは、フィールドは同一の構造体タグを持っている必要がありました。しかし、Go 1.8以降では、変換中に構造体タグは無視されます。したがって、すべてのフィールドを個別に手動でコピーする必要はありません。

変更前

var x T1
y := T2{
    Field1: x.Field1,
    Field2: x.Field2,
}

変更後

var x T1
y := T2(x)

2017.1以降利用可能

デフォルト: オフ。 "analyses": {"S1016": true}を設定して有効にします。

パッケージドキュメント: S1016

S1017: 手動トリミングをstrings.TrimPrefixに置き換える

strings.HasPrefixと手動スライスを使用する代わりに、strings.TrimPrefix関数を使用します。文字列がプレフィックスで始まっていない場合、元の文字列が返されます。strings.TrimPrefixを使用すると、複雑さが軽減され、オフバイワンエラーなどの一般的なバグを回避できます。

変更前

if strings.HasPrefix(str, prefix) {
    str = str[len(prefix):]
}

変更後

str = strings.TrimPrefix(str, prefix)

2017.1以降利用可能

デフォルト: オン。

パッケージドキュメント: S1017

S1018: 要素のスライドには「copy」を使用

copy()は、重複する範囲でも同じソースと宛先スライスを使用できます。これにより、スライス内の要素をスライドさせるのに理想的です。

変更前

for i := 0; i < n; i++ {
    bs[i] = bs[offset+i]
}

変更後

copy(bs[:n], bs[offset:])

2017.1以降利用可能

デフォルト: オン。

パッケージドキュメント: S1018

S1019: 冗長な引数を省略して「make」呼び出しを単純化

「make」関数は、長さと容量の引数にデフォルト値を持っています。チャネルの場合、長さはゼロにデフォルト設定され、スライスの場合、容量は長さにデフォルト設定されます。

2017.1以降利用可能

デフォルト: オン。

パッケージドキュメント: S1019

S1020: 型アサーションにおける冗長なnilチェックを省略

変更前

if _, ok := i.(T); ok && i != nil {}

変更後

if _, ok := i.(T); ok {}

2017.1以降利用可能

デフォルト: オン。

パッケージドキュメント: S1020

S1021: 変数宣言と代入を結合

変更前

var x uint
x = 1

変更後

var x uint = 1

2017.1以降利用可能

デフォルト: オフ。 "analyses": {"S1021": true}を設定して有効にします。

パッケージドキュメント: S1021

S1023: 冗長な制御フローを省略

戻り値のない関数は、関数の最後のステートメントとしてreturnステートメントを必要としません。

Goのスイッチは、Cのような言語とは異なり、自動的なフォールスルーがありません。ケースブロックの最後のステートメントとしてbreakステートメントを持つ必要はありません。

2017.1以降利用可能

デフォルト: オン。

パッケージドキュメント: S1023

S1024: x.Sub(time.Now())をtime.Until(x)に置き換える

time.Untilヘルパーはx.Sub(time.Now())を使用するのと同じ効果がありますが、より読みやすいです。

変更前

x.Sub(time.Now())

変更後

time.Until(x)

2017.1以降利用可能

デフォルト: オン。

パッケージドキュメント: S1024

S1025: fmt.Sprintf("%s", x)を不必要に使用しない

多くの場合、値の文字列表現を取得するためのより簡単で効率的な方法があります。値の基礎となる型がすでに文字列である場合、または型がStringメソッドを持っている場合は、それらを直接使用する必要があります。

以下の共有定義が与えられた場合

type T1 string
type T2 int

func (T2) String() string { return "Hello, world" }

var x string
var y T1
var z T2

私たちは単純化することができます

fmt.Sprintf("%s", x)
fmt.Sprintf("%s", y)
fmt.Sprintf("%s", z)

から

x
string(y)
z.String()

2017.1以降利用可能

デフォルト: オフ。 "analyses": {"S1025": true}を設定して有効にします。

パッケージドキュメント: S1025

S1028: fmt.Errorfでエラー構築を単純化

変更前

errors.New(fmt.Sprintf(...))

変更後

fmt.Errorf(...)

2017.1以降利用可能

デフォルト: オン。

パッケージドキュメント: S1028

S1029: 文字列を直接ループする

文字列をループすると、バイトオフセットとルーンが生成されます。オフセットが使用されない場合、これは文字列をルーンのスライスに変換してそれをループすることと機能的に同等です。ただし、文字列を直接ループする方が、文字列の長さに依存する新しいスライスの割り当てを回避できるため、パフォーマンスが向上します。

変更前

for _, r := range []rune(s) {}

変更後

for _, r := range s {}

2017.1以降利用可能

デフォルト: オフ。 "analyses": {"S1029": true}を設定して有効にします。

パッケージドキュメント: S1029

S1030: bytes.Buffer.Stringまたはbytes.Buffer.Bytesを使用

bytes.BufferはStringメソッドとBytesメソッドの両方を持っています。string(buf.Bytes())や[]byte(buf.String())を使用する必要はほとんどありません。単に別のメソッドを使用してください。

唯一の例外はマップのルックアップです。コンパイラの最適化により、m[string(buf.Bytes())]はm[buf.String()]よりも効率的です。

2017.1以降利用可能

デフォルト: オン。

パッケージドキュメント: S1030

S1031: ループ周囲の冗長なnilチェックを省略

nilスライスとマップでrangeを使用できます。ループは単に実行されません。これにより、ループ周囲の追加のnilチェックが不要になります。

変更前

if s != nil {
    for _, x := range s {
        ...
    }
}

変更後

for _, x := range s {
    ...
}

2017.1以降利用可能

デフォルト: オン。

パッケージドキュメント: S1031

S1032: sort.Ints(x)、sort.Float64s(x)、sort.Strings(x)を使用

sort.Ints、sort.Float64s、sort.Strings関数は、sort.Sort(sort.IntSlice(x))、sort.Sort(sort.Float64Slice(x))、sort.Sort(sort.StringSlice(x))よりも読みやすいです。

変更前

sort.Sort(sort.StringSlice(x))

変更後

sort.Strings(x)

2019.1以降利用可能

デフォルト: オン。

パッケージドキュメント: S1032

S1033: 「delete」の呼び出しを不必要に保護

nilマップでdeleteを呼び出すのは何もしません。

2019.2以降利用可能

デフォルト: オン。

パッケージドキュメント: S1033

S1034: 型アサーションの結果を使用してケースを単純化

2019.2以降利用可能

デフォルト: オン。

パッケージドキュメント: S1034

S1035: net/http.Headerのメソッド呼び出しにおけるnet/http.CanonicalHeaderKeyへの冗長な呼び出し

net/http.Headerのメソッド、すなわちAdd、Del、Get、Setは、与えられたヘッダー名をすでに正規化しています。

2020.1以降利用可能

デフォルト: オン。

パッケージドキュメント: S1035

S1036: マップアクセスを不必要に保護

まだ存在しないマップキーにアクセスすると、ゼロ値が返されます。多くの場合、ゼロ値は適切な値であり、たとえばappendを使用したり整数演算を行ったりする場合などです。

以下は

if _, ok := m["foo"]; ok {
    m["foo"] = append(m["foo"], "bar")
} else {
    m["foo"] = []string{"bar"}
}

に単純化できます

m["foo"] = append(m["foo"], "bar")

if _, ok := m2["k"]; ok {
    m2["k"] += 4
} else {
    m2["k"] = 4
}

に単純化できます

m["k"] += 4

2020.1以降利用可能

デフォルト: オン。

パッケージドキュメント: S1036

S1037: 複雑なスリープ方法

time.Afterの結果から受信する単一のケースを持つselectステートメントを使用することは、非常に複雑なスリープ方法であり、time.Sleepへの単純な呼び出しでより簡単に表現できます。

2020.1以降利用可能

デフォルト: オン。

パッケージドキュメント: S1037

S1038: フォーマット済み文字列を出力する不必要に複雑な方法

fmt.Print(fmt.Sprintf(...))を使用する代わりに、fmt.Printf(...)を使用できます。

2020.1以降利用可能

デフォルト: オン。

パッケージドキュメント: S1038

S1039: fmt.Sprintの不必要な使用

単一の文字列引数でfmt.Sprintを呼び出すことは不要であり、文字列を直接使用することと同じです。

2020.1以降利用可能

デフォルト: オン。

パッケージドキュメント: S1039

S1040: 現在の型への型アサーション

xが既にSomeInterface型である場合の型アサーションx.(SomeInterface)は、xがnilの場合にのみ失敗します。通常、これはxが異なる型であったときの残りのコードであり、型アサーションを安全に削除できます。xがnilでないことを確認したい場合は、型アサーションがパニックに陥ることに頼るのではなく、実際のif x == nil比較を使用して明示的にすることを検討してください。

2021.1以降利用可能

デフォルト: オン。

パッケージドキュメント: S1040

SA1000: 無効な正規表現

2017.1以降利用可能

デフォルト: オフ。 "analyses": {"SA1000": true}を設定して有効にします。

パッケージドキュメント: SA1000

SA1001: 無効なテンプレート

2017.1以降利用可能

デフォルト: オン。

パッケージドキュメント: SA1001

SA1002: time.Parseにおける無効なフォーマット

2017.1以降利用可能

デフォルト: オフ。 "analyses": {"SA1002": true}を設定して有効にします。

パッケージドキュメント: SA1002

SA1003: encoding/binaryの関数への未サポートの引数

encoding/binaryパッケージは、既知のサイズを持つ型のみをシリアル化できます。これは、intおよびuint型の使用を排除します。それらのサイズは異なるアーキテクチャで異なるためです。さらに、マップ、チャネル、文字列、または関数のシリアル化はサポートしていません。

Go 1.8以前では、boolもサポートされていませんでした。

2017.1以降利用可能

デフォルト: オフ。 "analyses": {"SA1003": true}を設定して有効にします。

パッケージドキュメント: SA1003

SA1004: time.Sleepにおける疑わしいほど小さな型なし定数

time.Sleep関数は、唯一の引数としてtime.Durationを取ります。期間はナノ秒で表現されます。したがって、time.Sleep(1)を呼び出すと1ナノ秒スリープします。これは、他の言語のスリープ関数が秒またはミリ秒を受け取ることが多いため、バグの一般的な原因となります。

timeパッケージは、time.Secondなどの定数を提供して、大きな期間を表現します。これらは、たとえば5秒の場合は5 * time.Secondのように、算術演算と組み合わせて任意の期間を表現できます。

本当にごくわずかな時間スリープさせたい場合は、n * time.Nanosecondを使用して、Staticcheckにナノ秒単位でスリープさせたかったことを伝えてください。

2017.1以降利用可能

デフォルト: オン。

パッケージドキュメント: SA1004

SA1005: exec.Commandへの無効な最初の引数

os/execはプログラムを直接実行します(Unixシステムではforkとexecシステムコールのバリアントを使用)。これはシェルでコマンドを実行することと混同しないでください。シェルは、入力リダイレクト、パイプ、一般的なスクリプトなどの機能を提供します。シェルは、ユーザーの入力をプログラム名とその引数に分割する役割も担います。たとえば、以下と同等のもの

ls / /tmp

は次のようになります

exec.Command("ls", "/", "/tmp")

シェルでコマンドを実行したい場合は、次のようなものを使用することを検討してください。ただし、すべてのシステム、特にWindowsでは/bin/shプログラムがないことに注意してください。

exec.Command("/bin/sh", "-c", "ls | grep Awesome")

2017.1以降利用可能

デフォルト: オン。

パッケージドキュメント: SA1005

SA1007: net/url.Parse内の無効なURL

2017.1以降利用可能

デフォルト: オフ。 "analyses": {"SA1007": true}を設定して有効にします。

パッケージドキュメント: SA1007

SA1008: http.Headerマップ内の非正規キー

http.Headerマップのキーは正規化されており、大文字と小文字の特定の組み合わせに従います。http.Header.Addやhttp.Header.Delなどのメソッドは、マップを操作する前に、入力をこの正規形式に変換します。

提供されたメソッドを使用するのではなく、http.Headerマップを直接操作する場合、矛盾を避けるために正規形式に固執するように注意する必要があります。以下のコードは、そのような矛盾の1つを示しています。

h := http.Header{}
h["etag"] = []string{"1234"}
h.Add("etag", "5678")
fmt.Println(h)

// Output:
// map[Etag:[5678] etag:[1234]]

キーの正規形式を取得する最も簡単な方法は、http.CanonicalHeaderKeyを使用することです。

2017.1以降利用可能

デフォルト: オン。

パッケージドキュメント: SA1008

SA1010: (*regexp.Regexp).FindAllがn == 0で呼び出され、常にゼロの結果を返す

n >= 0の場合、関数は最大n個の一致/サブマッチを返します。すべての結果を返すには、負の数を指定してください。

2017.1以降利用可能

デフォルト: オフ。 "analyses": {"SA1010": true}を設定して有効にします。

パッケージドキュメント: SA1010

SA1011: 「strings」パッケージのさまざまなメソッドは有効なUTF-8を期待するが、無効な入力が提供される

2017.1以降利用可能

デフォルト: オフ。 "analyses": {"SA1011": true}を設定して有効にします。

パッケージドキュメント: SA1011

SA1012: nilのcontext.Contextが関数に渡されている。代わりにcontext.TODOの使用を検討

2017.1以降利用可能

デフォルト: オン。

パッケージドキュメント: SA1012

SA1013: io.Seeker.Seekがwhence定数を最初の引数として呼び出されているが、これは2番目の引数であるべき

2017.1以降利用可能

デフォルト: オン。

パッケージドキュメント: SA1013

SA1014: UnmarshalまたはDecodeに渡された非ポインター値

2017.1以降利用可能

デフォルト: オフ。 "analyses": {"SA1014": true}を設定して有効にします。

パッケージドキュメント: SA1014

SA1015: time.Tickをリークする可能性のある方法で使用している。time.NewTickerの使用を検討し、テスト、コマンド、無限関数でのみtime.Tickを使用

Go 1.23より前では、time.Tickersをガベージコレクションできるように閉じる必要がありました。time.Tickは基になるティッカーを閉じることができないため、繰り返し使用するとメモリリークが発生していました。

Go 1.23では、ティッカーが閉じられなくても収集できるようになったため、この問題が修正されました。

2017.1以降利用可能

デフォルト: オフ。 "analyses": {"SA1015": true}を設定して有効にします。

パッケージドキュメント: SA1015

SA1016: トラップできないシグナルをトラップしようとしている

すべてのシグナルがプロセスによって傍受できるわけではありません。特に、UNIX系のシステムでは、syscall.SIGKILLおよびsyscall.SIGSTOPシグナルはプロセスに渡されず、カーネルによって直接処理されます。したがって、これらのシグナルを処理しようとすることは無意味です。

2017.1以降利用可能

デフォルト: オン。

パッケージドキュメント: SA1016

SA1017: os/signal.Notifyで使用されるチャネルはバッファリングされるべき

os/signalパッケージは、シグナルを配信する際に非ブロッキングチャネル送信を使用します。チャネルの受信側が準備できておらず、チャネルがバッファなしまたは満杯の場合、シグナルは破棄されます。シグナルを見逃さないように、チャネルはバッファリングされ、適切なサイズであるべきです。1つのシグナル値の通知のみに使用されるチャネルの場合、サイズ1のバッファで十分です。

2017.1以降利用可能

デフォルト: オフ。 "analyses": {"SA1017": true}を設定して有効にします。

パッケージドキュメント: SA1017

SA1018: strings.Replaceがn == 0で呼び出され、何もしない

n == 0の場合、ゼロインスタンスが置換されます。すべてのインスタンスを置換するには、負の数を指定するか、strings.ReplaceAllを使用してください。

2017.1以降利用可能

デフォルト: オフ。 "analyses": {"SA1018": true}を設定して有効にします。

パッケージドキュメント: SA1018

2017.1以降利用可能

デフォルト: オフ。 "analyses": {"SA1020": true}を設定して有効にします。

パッケージドキュメント: SA1020

SA1021: 2つのnet.IPを比較するためにbytes.Equalを使用している

net.IPは、IPv4またはIPv6アドレスをバイトスライスとして格納します。ただし、IPv4アドレスのスライスの長さは、IPv4アドレスを表現する異なる方法を使用して、4バイトまたは16バイトのいずれかになります。2つのnet.IPを正しく比較するには、net.IP.Equalメソッドを使用する必要があります。これは両方の表現を考慮に入れるためです。

2017.1以降利用可能

デフォルト: オフ。 "analyses": {"SA1021": true}を設定して有効にします。

パッケージドキュメント: SA1021

SA1023: io.Writerの実装でバッファを変更している

Writeは、一時的であってもスライスデータを変更してはなりません。

2017.1以降利用可能

デフォルト: オフ。 "analyses": {"SA1023": true}を設定して有効にします。

パッケージドキュメント: SA1023

SA1024: 文字列のカットセットに重複する文字が含まれている

strings.TrimLeftとstrings.TrimRight関数は、プレフィックスではなくカットセットを取ります。カットセットは、文字列から削除する文字のセットとして扱われます。たとえば、

strings.TrimLeft("42133word", "1234")

は文字列「word」を返します。つまり、1、2、3、または4の文字は文字列の左側から切り取られます。

ある文字列から別の文字列を削除するには、代わりにstrings.TrimPrefixを使用してください。

2017.1以降利用可能

デフォルト: オフ。 "analyses": {"SA1024": true}を設定して有効にします。

パッケージドキュメント: SA1024

SA1025: (*time.Timer).Resetの戻り値を正しく使用できない

2019.1以降利用可能

デフォルト: オフ。 "analyses": {"SA1025": true}を設定して有効にします。

パッケージドキュメント: SA1025

SA1026: チャネルまたは関数をマーシャルできない

2019.2以降利用可能

デフォルト: オフ。 "analyses": {"SA1026": true}を設定して有効にします。

パッケージドキュメント: SA1026

SA1027: 64ビット変数へのアトミックアクセスは64ビットアラインメントされている必要がある

ARM、x86-32、および32ビットMIPSでは、64ビットワードへのアトミックアクセスが64ビットアラインメントされるようにするのは呼び出し側の責任です。変数または割り当てられた構造体、配列、またはスライス内の最初のワードは、64ビットアラインメントされていると見なすことができます。

structlayoutツールを使用して、構造体内のフィールドのアラインメントを検査できます。

2019.2以降利用可能

デフォルト: オフ。 "analyses": {"SA1027": true}を設定して有効にします。

パッケージドキュメント: SA1027

SA1028: sort.Sliceはスライスでのみ使用可能

sort.Sliceの最初の引数はスライスである必要があります。

2020.1以降利用可能

デフォルト: オフ。 "analyses": {"SA1028": true}を設定して有効にします。

パッケージドキュメント: SA1028

SA1029: context.WithValueの呼び出しにおける不適切なキー

指定されたキーは比較可能であり、contextを使用するパッケージ間の衝突を避けるために、string型またはその他の組み込み型であってはなりません。WithValueのユーザーは、キーのために独自の型を定義する必要があります。

interface{}への割り当て時に割り当てを避けるために、コンテキストキーはしばしば具体的な型struct{}を持ちます。あるいは、エクスポートされたコンテキストキー変数の静的型はポインタまたはインターフェースであるべきです。

2020.1以降利用可能

デフォルト: オフ。 "analyses": {"SA1029": true}を設定して有効にします。

パッケージドキュメント: SA1029

SA1030: strconv関数への呼び出しにおける無効な引数

このチェックは、strconvのさまざまな解析および書式設定関数のフォーマット、基数、ビットサイズ引数を検証します。

2021.1以降利用可能

デフォルト: オフ。 "analyses": {"SA1030": true}を設定して有効にします。

パッケージドキュメント: SA1030

SA1031: エンコーダに渡された重複するバイトスライス

Encode(dst, src)の形式のエンコード関数で、dstとsrcが同じメモリを参照していることが判明しました。これは、エンコーダがsrcバイトあたり1バイト以上書き込む場合に、srcバイトが読み取られる前に上書きされる可能性があります。

2024.1以降利用可能

デフォルト: オフ。 "analyses": {"SA1031": true}を設定して有効にします。

パッケージドキュメント: SA1031

SA1032: errors.Isの引数の順序が間違っている

errors.Is関数の最初の引数は私たちが持っているエラーであり、2番目の引数は一致させようとしているエラーです。たとえば、

if errors.Is(err, io.EOF) { ... }

このチェックは、2つの引数が入れ替わっているいくつかのケースを検出します。これは、最初の引数がパッケージレベルのエラー変数(例:)を参照している呼び出しをフラグ付けします。

if errors.Is(io.EOF, err) { /* this is wrong */ }

2024.1以降利用可能

デフォルト: オフ。 "analyses": {"SA1032": true}を設定して有効にします。

パッケージドキュメント: SA1032

SA2001: 空のクリティカルセクション、ロック解除を遅延させるつもりでしたか?

次のような空のクリティカルセクション

mu.Lock()
mu.Unlock()

は非常に多くの場合タイプミスであり、代わりに以下を意図していました。

mu.Lock()
defer mu.Unlock()

ただし、空のクリティカルセクションが、別のゴルーチンを待つためのシグナルとして役立つ場合があることに注意してください。多くの場合、同じ効果を達成するより簡単な方法があります。そうでない場合は、混乱を避けるためにコードに十分なコメントを付ける必要があります。そのようなコメントと//lint:ignoreディレクティブを組み合わせることで、このまれな誤検知を抑制できます。

2017.1以降利用可能

デフォルト: オン。

パッケージドキュメント: SA2001

SA2002: ゴルーチン内でtesting.T.FailNowまたはSkipNowが呼び出されたが、これは許可されていない

2017.1以降利用可能

デフォルト: オフ。 "analyses": {"SA2002": true}を設定して有効にします。

パッケージドキュメント: SA2002

SA2003: ロック直後にLockを遅延、代わりにUnlockを遅延させるつもりだった可能性が高い

2017.1以降利用可能

デフォルト: オフ。 "analyses": {"SA2003": true}を設定して有効にします。

パッケージドキュメント: SA2003

SA3000: TestMainがos.Exitを呼び出しておらず、テストの失敗を隠している

テスト実行可能ファイル(および「go test」)は、テストが失敗した場合にゼロ以外のステータスコードで終了します。独自のTestMain関数を指定する場合、正しいコードでos.Exitを呼び出すことによって、これを手配するのはあなたの責任です。正しいコードは(*testing.M).Runによって返されるため、TestMainを実装する通常のGOPLSはos.Exit(m.Run())で終了することです。

2017.1以降利用可能

デフォルト: オン。

パッケージドキュメント: SA3000

SA3001: ベンチマークでb.Nに代入すると結果が歪められる

testingパッケージは、ベンチマークの信頼性を向上させるためにb.Nを動的に設定し、単一操作の期間を決定するための計算でこれを使用します。ベンチマークコードはb.Nを変更してはなりません。そうしないと結果が偽りになります。

2017.1以降利用可能

デフォルト: オン。

パッケージドキュメント: SA3001

SA4000: 二項演算子の両側に同一の式がある

2017.1以降利用可能

デフォルト: オン。

パッケージドキュメント: SA4000

SA4001: &*xはxに単純化され、xをコピーしない

2017.1以降利用可能

デフォルト: オン。

パッケージドキュメント: SA4001

SA4003: 符号なし値を負の値と比較するのは無意味

2017.1以降利用可能

デフォルト: オン。

パッケージドキュメント: SA4003

SA4004: ループは1回の繰り返し後に無条件に終了する

2017.1以降利用可能

デフォルト: オン。

パッケージドキュメント: SA4004

SA4005: 決して観察されないフィールド代入。ポインターレシーバを使用するつもりでしたか?

2021.1以降利用可能

デフォルト: オフ。 "analyses": {"SA4005": true}を設定して有効にします。

パッケージドキュメント: SA4005

SA4006: 変数に割り当てられた値が上書きされる前に決して読み取られない。エラーチェックを忘れたか、デッドコードか?

2017.1以降利用可能

デフォルト: オフ。 "analyses": {"SA4006": true}を設定して有効にします。

パッケージドキュメント: SA4006

SA4008: ループ条件の変数が決して変更されない。間違った変数をインクリメントしていますか?

for i := 0; i < 10; j++ { ... }

これは、ループを終了する無条件の制御フローのため、ループが一度しか実行できない場合にも発生する可能性があります。たとえば、ループボディに無条件のbreak、return、またはpanicが含まれている場合など。

func f() {
    panic("oops")
}
func g() {
    for i := 0; i < 10; i++ {
        // f unconditionally calls panic, which means "i" is
        // never incremented.
        f()
    }
}

2017.1以降利用可能

デフォルト: オフ。 "analyses": {"SA4008": true}を設定して有効にします。

パッケージドキュメント: SA4008

SA4009: 関数引数が最初の使用前に上書きされる

2017.1以降利用可能

デフォルト: オフ。 "analyses": {"SA4009": true}を設定して有効にします。

パッケージドキュメント: SA4009

SA4010: appendの結果はどこでも決して観察されない

2017.1以降利用可能

デフォルト: オフ。 "analyses": {"SA4010": true}を設定して有効にします。

パッケージドキュメント: SA4010

SA4011: 効果のないbreak文。外側のループをbreakするつもりでしたか?

2017.1以降利用可能

デフォルト: オン。

パッケージドキュメント: SA4011

SA4012: 値をNaNと比較しているが、NaNに等しい値は存在しない

2017.1以降利用可能

デフォルト: オフ。 "analyses": {"SA4012": true}を設定して有効にします。

パッケージドキュメント: SA4012

SA4013: ブール値を2回否定する(!!b)のはbを書くのと同じです。これは冗長であるか、タイプミスです。

2017.1以降利用可能

デフォルト: オン。

パッケージドキュメント: SA4013

SA4014: if/else ifチェーンに繰り返しの条件があり、副作用がない。条件が最初に一致しなかった場合、2回目も一致しない

2017.1以降利用可能

デフォルト: オン。

パッケージドキュメント: SA4014

SA4015: 整数から変換された浮動小数点数に対してmath.Ceilのような関数を呼び出しても役に立たない

2017.1以降利用可能

デフォルト: オフ。 "analyses": {"SA4015": true}を設定して有効にします。

パッケージドキュメント: SA4015

SA4016: x ^ 0のような特定のビット演算は役に立たない

2017.1以降利用可能

デフォルト: オン。

パッケージドキュメント: SA4016

SA4017: 副作用のない関数の戻り値を破棄し、呼び出しを無意味にする

2017.1以降利用可能

デフォルト: オフ。 "analyses": {"SA4017": true}を設定して有効にします。

パッケージドキュメント: SA4017

SA4018: 変数の自己代入

2017.1以降利用可能

デフォルト: オフ。 "analyses": {"SA4018": true}を設定して有効にします。

パッケージドキュメント: SA4018

SA4019: 同じファイル内に複数の同一のビルド制約

2017.1以降利用可能

デフォルト: オン。

パッケージドキュメント: SA4019

SA4020: 型スイッチで到達不能なcase句

以下のような型スイッチの場合

type T struct{}
func (T) Read(b []byte) (int, error) { return 0, nil }

var v any = T{}

switch v.(type) {
case io.Reader:
    // ...
case T:
    // unreachable
}

Tはio.Readerを実装しており、case句はソース順に評価されるため、2番目のcase句は決して到達できません。

別の例

type T struct{}
func (T) Read(b []byte) (int, error) { return 0, nil }
func (T) Close() error { return nil }

var v any = T{}

switch v.(type) {
case io.Reader:
    // ...
case io.ReadCloser:
    // unreachable
}

TがCloseメソッドを持ち、したがってio.ReadCloserを実装しているにもかかわらず、io.Readerが常に最初に一致します。io.Readerのメソッドセットはio.ReadCloserのサブセットです。したがって、最初のケースに一致することなく2番目のケースに一致することは不可能です。

構造的に同等のインターフェース

前の例の特殊なケースとして、構造的に同一のインターフェースがあります。これらの宣言が与えられた場合

type T error
type V error

func doSomething() error {
    err, ok := doAnotherThing()
    if ok {
        return T(err)
    }

    return U(err)
}

以下の型スイッチには到達不能なcase句が含まれます

switch doSomething().(type) {
case T:
    // ...
case V:
    // unreachable
}

TとVは構造的に同等であり、したがってdoSomething()の戻り値は両方を実装しているため、TがVよりも常に先に一致します。

2019.2以降利用可能

デフォルト: オン。

パッケージドキュメント: SA4020

SA4022: 変数のアドレスをnilと比較している

「if &x == nil」のようなコードは無意味です。なぜなら、変数のアドレスを取得すると常に非nilポインタが返されるからです。

2020.1以降利用可能

デフォルト: オン。

パッケージドキュメント: SA4022

SA4023: インターフェース値を型なしnilと比較する不可能

内部的には、インターフェースは型Tと値Vの2つの要素として実装されます。Vはint、struct、ポインタなどの具体的な値であり、インターフェース自体ではありません。また、型Tを持ちます。たとえば、int値3をインターフェースに格納すると、結果のインターフェース値は概略的に(T=int, V=3)となります。値Vは、プログラムの実行中に特定のインターフェース変数が異なる値V(および対応する型T)を保持する可能性があるため、インターフェースの動的な値とも呼ばれます。

インターフェース値は、VとTの両方が未設定の場合にのみnilです(T=nil, Vは設定されていない)。特に、nilインターフェースは常にnil型を保持します。*int型のnilポインタをインターフェース値内に格納すると、ポインタの値Vがnilであっても内部型は*intになります。(T=*int, V=nil)。したがって、そのようなインターフェース値は、ポインタ値Vがnilであっても非nilになります。

この状況は混乱を招く可能性があり、エラー戻り値などのインターフェース値内にnil値が格納されると発生します。

func returnsError() error {
    var p *MyError = nil
    if bad() {
        p = ErrBad
    }
    return p // Will always return a non-nil error.
}

すべてがうまくいけば、関数はnil pを返します。したがって、戻り値は(T=*MyError, V=nil)を保持するエラーインターフェース値です。これは、呼び出し元が返されたエラーをnilと比較すると、何も問題がなかったとしても常にエラーがあったように見えることを意味します。呼び出し元に適切なnilエラーを返すには、関数は明示的なnilを返す必要があります。

func returnsError() error {
    if bad() {
        return ErrBad
    }
    return nil
}

エラーを返す関数は、エラーが正しく作成されることを保証するために、*MyErrorのような具体的な型ではなく、常にシグネチャにerror型を使用することをお勧めします(上記のように)。例として、os.Openは、nilでない場合でも、常に具体的な型*os.PathErrorのエラーを返します。

インターフェースが使用されるたびに、ここで説明したような同様の状況が発生する可能性があります。インターフェースに具体的な値が格納されている場合、インターフェースはnilではないことを覚えておいてください。詳細については、https://go.dokyumento.jp/doc/articles/laws_of_reflection.htmlの「The Laws of Reflection」を参照してください。

このテキストは、Creative Commons Attribution 3.0 Licenseの下でライセンスされたhttps://go.dokyumento.jp/doc/faq#nil_errorからコピーされています。

2020.2以降利用可能

デフォルト: オフ。 "analyses": {"SA4023": true}を設定して有効にします。

パッケージドキュメント: SA4023

SA4024: 組み込み関数のありえない戻り値をチェックしている

lenとcap組み込み関数の戻り値は負になることはできません。

https://go.dokyumento.jp/pkg/builtin/#lenhttps://go.dokyumento.jp/pkg/builtin/#cap を参照してください。

if len(slice) < 0 {
    fmt.Println("unreachable code")
}

2021.1以降利用可能

デフォルト: オン。

パッケージドキュメント: SA4024

SA4025: ゼロになるリテラルの整数除算

2つの整数定数を除算すると、結果も整数になります。したがって、2 / 3のような除算は0になります。これは以下のすべての例に当てはまります。

_ = 2 / 3
const _ = 2 / 3
const _ float64 = 2 / 3
_ = float64(2 / 3)

Staticcheckは、除算の両側が整数リテラルである場合、そのような除算をフラグ付けします。これは、除算がゼロに切り捨てられることを意図した可能性が非常に低いためです。Staticcheckは、ノイズの多い誤検知を避けるため、名前付き定数を含む整数除算はフラグ付けしません。

2021.1以降利用可能

デフォルト: オン。

パッケージドキュメント: SA4025

SA4026: Goの定数は負のゼロを表現できない

IEEE 754浮動小数点演算では、ゼロは符号を持ち、正または負になることができます。これは特定の数値計算コードで役立つことがあります。

しかし、Goの定数は負のゼロを表現できません。これは、リテラル-0.0と0.0が同じ理想値(ゼロ)を持ち、両方とも実行時に正のゼロを表すことを意味します。

負のゼロを明示的かつ確実に作成するには、math.Copysign関数を使用できます:math.Copysign(0, -1)。

2021.1以降利用可能

デフォルト: オン。

パッケージドキュメント: SA4026

SA4027: (*net/url.URL).Queryはコピーを返すため、変更してもURLは変更されない

(*net/url.URL).Queryはnet/url.URL.RawQueryの現在の値を解析し、それをnet/url.Values型のマップとして返します。このマップへのその後の変更は、マップがエンコードされてURLのRawQueryに割り当てられない限り、URLには影響しません。

その結果、以下のコードパターンは高価なno-opです: u.Query().Add(key, value)。

2021.1以降利用可能

デフォルト: オン。

パッケージドキュメント: SA4027

SA4028: x % 1は常にゼロ

2022.1以降利用可能

デフォルト: オン。

パッケージドキュメント: SA4028

SA4029: スライスのソートの非効率的な試み

sort.Float64Slice、sort.IntSlice、およびsort.StringSliceは型であり、関数ではありません。x = sort.StringSlice(x)とすると、何もせず、特に値をソートしません。正しい使用方法はsort.Sort(sort.StringSlice(x))またはsort.StringSlice(x).Sort()ですが、より便利なヘルパーとしてsort.Float64s、sort.Ints、sort.Stringsがあります。

2022.1以降利用可能

デフォルト: オン。

パッケージドキュメント: SA4029

SA4030: 乱数生成の非効率的な試み

Intnなどの上限を受け入れるmath/randパッケージの関数は、半開区間[0,n)で乱数を生成します。言い換えれば、生成される数値は>= 0かつ< nであり、nは含まれません。したがって、rand.Intn(1)は0または1を生成せず、常に0を生成します。

2022.1以降利用可能

デフォルト: オン。

パッケージドキュメント: SA4030

SA4031: 決してnilではない値をnilと比較している

2022.1以降利用可能

デフォルト: オフ。 "analyses": {"SA4031": true}を設定して有効にします。

パッケージドキュメント: SA4031

SA4032: runtime.GOOSまたはruntime.GOARCHを不可能な値と比較している

2024.1以降利用可能

デフォルト: オン。

パッケージドキュメント: SA4032

SA5000: nilマップへの代入

2017.1以降利用可能

デフォルト: オフ。 "analyses": {"SA5000": true}を設定して有効にします。

パッケージドキュメント: SA5000

SA5001: 起こりうるエラーをチェックする前にCloseを遅延している

2017.1以降利用可能

デフォルト: オン。

パッケージドキュメント: SA5001

SA5002: 空のforループ(「for {}」)はスピンし、スケジューラをブロックする可能性がある

2017.1以降利用可能

デフォルト: オフ。 "analyses": {"SA5002": true}を設定して有効にします。

パッケージドキュメント: SA5002

SA5003: 無限ループ内のdeferは決して実行されない

deferは、周囲のブロックではなく、周囲の関数にスコープされます。無限ループを含む、決して戻らない関数では、deferは決して実行されません。

2017.1以降利用可能

デフォルト: オン。

パッケージドキュメント: SA5003

SA5004: 「for { select { ...」で空のdefaultブランチはスピンする

2017.1以降利用可能

デフォルト: オン。

パッケージドキュメント: SA5004

SA5005: ファイナライザーがファイナライズ対象オブジェクトを参照しており、ガーベージコレクションを妨げている

ファイナライザーは、ガベージコレクタがオブジェクトを収集する準備ができたとき、つまりオブジェクトが何も参照されなくなったときに実行される、オブジェクトに関連付けられた関数です。

しかし、ファイナライザーがオブジェクトを参照している場合、それは常にそのオブジェクトへの最後の参照として残り、ガベージコレクタがオブジェクトを収集するのを妨げます。ファイナライザーは決して実行されず、オブジェクトは決して収集されず、メモリリークにつながります。そのため、ファイナライザーは代わりに最初の引数を使用してオブジェクトを操作する必要があります。そうすることで、オブジェクトがファイナライザーに渡される前に、参照の数が一時的にゼロになることができます。

2017.1以降利用可能

デフォルト: オフ。 "analyses": {"SA5005": true}を設定して有効にします。

パッケージドキュメント: SA5005

SA5007: 無限再帰呼び出し

再帰的に自身を呼び出す関数には、終了条件が必要です。そうでなければ、システムがメモリ不足になるまで無限に再帰します。

この問題は、終了条件を追加し忘れるなどの単純なバグによって引き起こされることがあります。また、「意図的に」発生することもあります。一部の言語には末尾再帰最適化があり、特定の無限再帰呼び出しを安全に使用できるようにします。しかし、GoはTCOを実装していないため、代わりにループを使用する必要があります。

2017.1以降利用可能

デフォルト: オフ。 "analyses": {"SA5007": true}を設定して有効にします。

パッケージドキュメント: SA5007

SA5008: 無効な構造体タグ

2019.2以降利用可能

デフォルト: オン。

パッケージドキュメント: SA5008

SA5010: 不可能な型アサーション

いくつかの型アサーションは静的に不可能であることが証明できます。これは、型アサーションの両方の引数のメソッドセットが互いに競合する場合、例えば異なるシグネチャを持つ同じメソッドを含む場合です。

Goコンパイラは、インターフェース値から具象型へのアサーションを行う際にすでにこのチェックを適用しています。具象型がインターフェースのメソッドを欠いている場合、または関数シグネチャが一致しない場合、型アサーションは決して成功しません。

このチェックは、1つのインターフェースから別のインターフェースへのアサーションを行う際にも同じロジックを適用します。両方のインターフェース型が同じメソッドを含んでいても、シグネチャが異なる場合、型アサーションは決して成功しません。

2020.1以降利用可能

デフォルト: オフ。 "analyses": {"SA5010": true}を設定して有効にします。

パッケージドキュメント: SA5010

SA5011: nilポインタのデリファレンスが発生する可能性

ポインタは無条件にデリファレンスされていますが、別の場所でnilチェックも行われています。これは、ポインタがnilである可能性があり、デリファレンスするとパニックになる可能性があることを示唆しています。これは通常、コードの順序が不適切であったり、returnステートメントが欠落していたりすることが原因です。以下の例を考えてみましょう。

func fn(x *int) {
    fmt.Println(*x)

    // This nil check is equally important for the previous dereference
    if x != nil {
        foo(*x)
    }
}

func TestFoo(t *testing.T) {
    x := compute()
    if x == nil {
        t.Errorf("nil pointer received")
    }

    // t.Errorf does not abort the test, so if x is nil, the next line will panic.
    foo(*x)
}

Staticcheckは、どの関数が制御フローを中断するかを推測しようとします。たとえば、panicまたはlog.Fatalの呼び出し後に関数が実行を継続しないことを認識しています。ただし、特に条件が存在する場合、この検出が失敗することがあります。以下の例を考えてみましょう。

func Log(msg string, level int) {
    fmt.Println(msg)
    if level == levelFatal {
        os.Exit(1)
    }
}

func Fatal(msg string) {
    Log(msg, levelFatal)
}

func fn(x *int) {
    if x == nil {
        Fatal("unexpected nil pointer")
    }
    fmt.Println(*x)
}

Staticcheckは、xのデリファレンスをフラグ付けしますが、これは完全に安全です。Staticcheckは、Fatalの呼び出しがプログラムを終了することを推測できません。当面の間、最も簡単な回避策は、Fatalの定義を次のように変更することです。

func Fatal(msg string) {
    Log(msg, levelFatal)
    panic("unreachable")
}

logrusなどの一般的なロギングパッケージからの関数もハードコードしています。人気のあるパッケージのサポートが不足している場合は、問題を報告してください。

2020.1以降利用可能

デフォルト: オフ。 "analyses": {"SA5011": true}を設定して有効にします。

パッケージドキュメント: SA5011

SA5012: 偶数サイズを期待する関数に奇数サイズのスライスを渡している

スライスをパラメータとして受け取る一部の関数は、スライスが偶数個の要素を持つことを期待します。多くの場合、これらの関数はスライス内の要素をペアとして扱います。たとえば、strings.NewReplacerは古い文字列と新しい文字列のペアを受け取り、奇数個の要素で呼び出すとエラーになります。

2020.2以降利用可能

デフォルト: オフ。 "analyses": {"SA5012": true}を設定して有効にします。

パッケージドキュメント: SA5012

2017.1以降利用可能

デフォルト: オフ。 "analyses": {"SA6000": true}を設定して有効にします。

パッケージドキュメント: SA6000

SA6001: バイトスライスでマップをインデックス付けする際の最適化の機会を逃している

マップキーは比較可能である必要があり、バイトスライスの使用を排除します。これは通常、文字列キーを使用し、バイトスライスを文字列に変換することにつながります。

通常、バイトスライスを文字列に変換するにはデータをコピーする必要があり、割り当てが発生します。しかし、コンパイラはm[string(b)]を認識し、マップルックアップ中にデータが変更されないことがわかっているため、データをコピーせずにbのデータを直接使用します。これは、次のような直感に反する状況につながります。

k := string(b)
println(m[k])
println(m[k])

は、次よりも効率が悪いでしょう。

println(m[string(b)])
println(m[string(b)])

最初のバージョンはコピーと割り当てが必要ですが、2番目のバージョンは必要ありません。

この最適化の経緯については、Goリポジトリのコミットf5f5a8b6209f84961687d993b93ea0d397f5d5bfを参照してください。

2017.1以降利用可能

デフォルト: オフ。 "analyses": {"SA6001": true}を設定して有効にします。

パッケージドキュメント: SA6001

SA6002: sync.Poolに非ポインタ値を格納するとメモリを割り当てる

sync.Poolは、不要な割り当てを回避し、ガベージコレクタの作業量を減らすために使用されます。

ポインタではない値をインターフェースを受け入れる関数に渡すと、値はヒープに配置される必要があり、これは追加の割り当てを意味します。スライスはsync.Poolによく入れられるものであり、それらは3つのフィールド(長さ、容量、配列へのポインタ)を持つ構造体です。余分な割り当てを避けるために、スライスへのポインタを格納する必要があります。

この問題について議論しているhttps://go-review.googlesource.com/c/go/+/24371のコメントを参照してください。

2017.1以降利用可能

デフォルト: オフ。 "analyses": {"SA6002": true}を設定して有効にします。

パッケージドキュメント: SA6002

SA6003: 文字列をループする前にルーンのスライスに変換している

文字列内のルーンをループしたい場合があります。文字列をルーンのスライスに変換してそれをループする代わりに、文字列自体をループすることができます。つまり、

for _, r := range s {}

for _, r := range []rune(s) {}

は同じ値を生成します。ただし、最初のバージョンはより高速であり、不要なメモリ割り当てを回避します。

インデックスに関心がある場合、文字列とルーンのスライスをループすると、異なるインデックスが生成されることに注意してください。最初のものはバイトオフセットを生成し、2番目のものはルーンのスライス内のインデックスを生成します。

2017.1以降利用可能

デフォルト: オフ。 "analyses": {"SA6003": true}を設定して有効にします。

パッケージドキュメント: SA6003

SA6005: strings.ToLowerまたはstrings.ToUpperを使用した非効率な文字列比較

2つの文字列を同じケースに変換して次のように比較する

if strings.ToLower(s1) == strings.ToLower(s2) {
    ...
}

は、strings.EqualFold(s1, s2)で比較するよりもはるかにコストがかかります。これは、メモリ使用量と計算複雑度の両方によるものです。

strings.ToLowerは、新しい文字列のためにメモリを割り当てる必要があり、最初のバイトが異なる場合でも両方の文字列を完全に変換します。一方、strings.EqualFoldは、文字列を一度に1文字ずつ比較します。2つの中間文字列を作成する必要はなく、最初の一致しない文字が見つかり次第すぐに戻ることができます。

この問題の詳細な説明については、https://blog.digitalocean.com/how-to-efficiently-compare-strings-in-go/を参照してください。

2019.2以降利用可能

デフォルト: オン。

パッケージドキュメント: SA6005

SA6006: []byteを書き込むためにio.WriteStringを使用している

次のようにバイトスライスを書き込むためにio.WriteStringを使用する

io.WriteString(w, string(b))

は不要であり、非効率的です。[]byteからstringへの変換はデータを割り当ててコピーする必要があり、代わりにw.Write(b)を単純に使用できます。

2024.1以降利用可能

デフォルト: オン。

パッケージドキュメント: SA6006

SA9001: rangeループ内のdeferは期待通りに実行されない可能性がある

2017.1以降利用可能

デフォルト: オフ。 "analyses": {"SA9001": true}を設定して有効にします。

パッケージドキュメント: SA9001

SA9002: 8進数で意図されたと思われる非8進数のos.FileModeを使用している。

2017.1以降利用可能

デフォルト: オン。

パッケージドキュメント: SA9002

SA9003: ifまたはelseブランチの空のボディ

2017.1以降利用可能、非デフォルト

デフォルト: オフ。 "analyses": {"SA9003": true}を設定して有効にします。

パッケージドキュメント: SA9003

SA9004: 最初の定数のみが明示的な型を持つ

次のような定数宣言の場合

const (
    First byte = 1
    Second     = 2
)

定数Secondは定数Firstと同じ型を持ちません。この構文は以下と混同してはいけません。

const (
    First byte = iota
    Second
)

ここでFirstとSecondは確かに同じ型を持ちます。型は明示的な値が定数に割り当てられていない場合にのみ引き継がれます。

明示的な値を持つ列挙型を宣言する際には、したがって次のように書かないことが重要です。

const (
      EnumFirst EnumType = 1
      EnumSecond         = 2
      EnumThird          = 3
)

この型の不一致は、様々な混乱を招く挙動やバグの原因となる可能性があります。

変数宣言における誤った型

このような誤った列挙型の最も明白な問題は、コンパイルエラーとして現れます。

package pkg

const (
    EnumFirst  uint8 = 1
    EnumSecond       = 2
)

func fn(useFirst bool) {
    x := EnumSecond
    if useFirst {
        x = EnumFirst
    }
}

はコンパイルに失敗します。

./const.go:11:5: cannot use EnumFirst (type uint8) as type int in assignment

メソッドセットを失う

より微妙な問題は、メソッドとオプションのインターフェースを持つ型で発生します。以下を検討してください。

package main

import "fmt"

type Enum int

func (e Enum) String() string {
    return "an enum"
}

const (
    EnumFirst  Enum = 1
    EnumSecond      = 2
)

func main() {
    fmt.Println(EnumFirst)
    fmt.Println(EnumSecond)
}

このコードは以下を出力します。

an enum
2

EnumSecondには明示的な型がないため、デフォルトでintになります。

2019.1以降利用可能

デフォルト: オン。

パッケージドキュメント: SA9004

SA9005: 公開フィールドもカスタムマーシャリングもない構造体をマーシャルしようとしている

encoding/jsonおよびencoding/xmlパッケージは、構造体のエクスポートされたフィールドのみを操作し、非エクスポートされたフィールドは操作しません。非エクスポートされたフィールドのみで構成される構造体を(アン)マーシャルしようとすることは、通常エラーです。

このチェックは、MarshalJSONメソッドなどを介してカスタムマーシャリング動作を定義する型を含む呼び出しにはフラグを立てません。また、空の構造体にもフラグを立てません。

2019.2以降利用可能

デフォルト: オフ。 "analyses": {"SA9005": true}を設定して有効にします。

パッケージドキュメント: SA9005

SA9006: 固定サイズ整数値の疑わしいビットシフト

値をそのサイズを超えてビットシフトすると、常に値がクリアされます。

たとえば、

v := int8(42)
v >>= 8

は常に0になります。

このチェックは、固定サイズ整数値に対するビットシフト操作のみをフラグ付けします。つまり、int、uint、およびuintptrは、やや珍しいが有効なビット操作トリックでの誤検知を避けるために決してフラグ付けされません。

// Clear any value above 32 bits if integers are more than 32 bits.
func f(i int) int {
    v := i >> 32
    v = v << 32
    return i-v
}

2020.2以降利用可能

デフォルト: オン。

パッケージドキュメント: SA9006

SA9007: 削除すべきではないディレクトリを削除している

/tmpやユーザーのホームディレクトリのようなシステムディレクトリを削除することは、事実上決して正しいことではありません。しかし、たとえばioutil.TempDirの代わりにos.TempDirを誤って使用したり、os.UserHomeDirの結果にサフィックスを追加し忘れたりすることによって、誤って簡単に実行される可能性があります。

以下を記述すると

d := os.TempDir()
defer os.RemoveAll(d)

ユニットテストでシステム​​の安定性に壊滅的な影響を与えます。

このチェックは、以下のディレクトリを削除しようとする試みにフラグを立てます。

  • os.TempDir
  • os.UserCacheDir
  • os.UserConfigDir
  • os.UserHomeDir

2022.1以降利用可能

デフォルト: オフ。 "analyses": {"SA9007": true}を設定して有効にします。

パッケージドキュメント: SA9007

SA9008: 型アサーションのelseブランチが正しい値を読んでいない可能性が高い

ifステートメントの一部として変数を宣言する場合(「if foo := ...; foo {」のように)、同じ変数がelseブランチのスコープにも含まれます。これは、以下の例の場合

if x, ok := x.(int); ok {
    // ...
} else {
    fmt.Printf("unexpected type %T", x)
}

elseブランチのxはx, ok :=; のxを参照し、型アサーションされているxを参照しません。失敗した型アサーションの結果は、アサートされている型のゼロ値であるため、elseブランチのxは常に値0と型intを持ちます。

2022.1以降利用可能

デフォルト: オフ。 "analyses": {"SA9008": true}を設定して有効にします。

パッケージドキュメント: SA9008

SA9009: 効果のないGoコンパイラディレクティブ

Goコンパイラディレクティブの可能性が見つかりましたが、空白で始まるため効果がありません。

2024.1以降利用可能

デフォルト: オン。

パッケージドキュメント: SA9009

ST1000: パッケージコメントが不正または欠落している

パッケージは、https://go.dokyumento.jp/wiki/CodeReviewComments#package-commentsに記載されているガイドラインに従って書式設定されたパッケージコメントを持つ必要があります。

2019.1以降利用可能、非デフォルト

デフォルト: オフ。 "analyses": {"ST1000": true}を設定して有効にします。

パッケージドキュメント: ST1000

ST1001: ドットインポートは推奨されない

外部テストパッケージに含まれないドットインポートは推奨されません。

dot_import_whitelistオプションを使用して、特定のインポートをホワイトリストに登録できます。

Go Code Review Commentsを引用

インポート.形式は、循環依存関係のためにテスト対象パッケージの一部にできないテストで役立ちます。

package foo_test

import (
    "bar/testutil" // also imports "foo"
    . "foo"
)

この場合、テストファイルはbar/testutilを使用しているため、パッケージfoo内に存在できません。bar/testutilはfooをインポートしています。そのため、インポート.形式を使用して、ファイルが実際にはパッケージfooの一部ではないにもかかわらず、その一部であるかのように見せかけます。この1つのケースを除いて、プログラムでインポート.を使用しないでください。Quuxのような名前が現在のパッケージまたはインポートされたパッケージのトップレベル識別子であるかどうかが不明確になるため、プログラムの読みやすさが大幅に低下します。

2019.1以降利用可能

オプション dot_import_whitelist

デフォルト: オフ。 "analyses": {"ST1001": true}を設定して有効にします。

パッケージドキュメント: ST1001

ST1003: 不適切な識別子

変数名やパッケージ名などの識別子には、特定のルールがあります。

詳細については、以下のリンクを参照してください。

2019.1以降利用可能、非デフォルト

オプション initialisms

デフォルト: オフ。 "analyses": {"ST1003": true}を設定して有効にします。

パッケージドキュメント: ST1003

ST1005: 不適切な形式のエラース​​トリング

エラース​​トリングは、均一性と良好な構成可能性を確保するために一連のガイドラインに従います。

Go Code Review Commentsを引用

エラース​​トリングは、通常、他のコンテキストに続いて出力されるため、大文字で始まったり(固有名詞または頭字語で始まる場合を除く)、句読点で終わったりしてはいけません。つまり、fmt.Errorf("Something bad")ではなく、fmt.Errorf("something bad")を使用してください。そうすることで、log.Printf("Reading %s: %v", filename, err)がメッセージの途中で不必要な大文字を付けずにフォーマットされます。

2019.1以降利用可能

デフォルト: オフ。 "analyses": {"ST1005": true}を設定して有効にします。

パッケージドキュメント: ST1005

ST1006: 不適切なレシーバー名

Go Code Review Commentsを引用

メソッドのレシーバーの名前は、そのアイデンティティを反映している必要があります。多くの場合、その型の1文字または2文字の省略形(「Client」の「c」または「cl」など)で十分です。「me」、「this」、「self」のような一般的な名前は使用しないでください。これらは、関数よりもメソッドに重点を置くオブジェクト指向言語に典型的な識別子です。その役割は明白であり、文書化の目的はないため、メソッドの引数ほど説明的である必要はありません。型のすべてのメソッドのほぼすべての行に表示されるため、非常に短くすることができます。また、一貫性を保ってください。あるメソッドでレシーバーを「c」と呼んだ場合、別のメソッドで「cl」と呼んではいけません。

2019.1以降利用可能

デフォルト: オフ。 "analyses": {"ST1006": true}を設定して有効にします。

パッケージドキュメント: ST1006

ST1008: 関数のエラー値は最後の戻り値であるべき

関数のエラー値は最後の戻り値であるべきです。

2019.1以降利用可能

デフォルト: オフ。 "analyses": {"ST1008": true}を設定して有効にします。

パッケージドキュメント: ST1008

ST1011: time.Duration型の変数名が不適切

time.Durationの値は時間の量を表し、ナノ秒のカウントとして表現されます。5 * time.Microsecondのような式は5000という値を生成します。したがって、time.Duration型の変数にMsecやMilliのような時間単位のサフィックスを付けるのは適切ではありません。

2019.1以降利用可能

デフォルト: オフ。 "analyses": {"ST1011": true}を設定して有効にします。

パッケージドキュメント: ST1011

ST1012: エラー変数の名前が不適切

APIの一部であるエラー変数は、errFooまたはErrFooと呼ぶべきです。

2019.1以降利用可能

デフォルト: オフ。 "analyses": {"ST1012": true}を設定して有効にします。

パッケージドキュメント: ST1012

ST1013: HTTPエラーコードにはマジックナンバーではなく定数を使用すべき

HTTPには非常に多くのステータスコードがあります。その中にはよく知られているもの(200、400、404、500)もありますが、ほとんどはそうではありません。net/httpパッケージは、さまざまな仕様の一部であるすべてのステータスコードの定数を提供します。コードの読みやすさを大幅に向上させるために、マジックナンバーをハードコードする代わりにこれらの定数を使用することをお勧めします。

2019.1以降利用可能

オプション http_status_code_whitelist

デフォルト: オフ。 "analyses": {"ST1013": true}を設定して有効にします。

パッケージドキュメント: ST1013

ST1015: スイッチのdefaultケースは最初または最後のケースであるべき

2019.1以降利用可能

デフォルト: オフ。 "analyses": {"ST1015": true}を設定して有効にします。

パッケージドキュメント: ST1015

ST1016: 一貫したメソッドレシーバ名を使用

2019.1以降利用可能、非デフォルト

デフォルト: オフ。 "analyses": {"ST1016": true}を設定して有効にします。

パッケージドキュメント: ST1016

ST1017: Yoda条件を使用しない

Yoda条件とは、「if 42 == x」のような、リテラルが比較の左側にある条件です。これは、代入が式である言語で、「if (x = 42)」のようなバグを避けるための一般的なイディオムです。この種のバグを許容しないGoでは、より慣用的な「if x == 42」を好みます。

2019.2以降利用可能

デフォルト: オフ。 "analyses": {"ST1017": true}を設定して有効にします。

パッケージドキュメント: ST1017

ST1018: 文字列リテラルでゼロ幅および制御文字を避ける

2019.2以降利用可能

デフォルト: オフ。 "analyses": {"ST1018": true}を設定して有効にします。

パッケージドキュメント: ST1018

ST1019: 同じパッケージを複数回インポートしている

Goは、異なるインポートエイリアスが使用されている限り、同じパッケージを複数回インポートすることを許可します。つまり、以下のコードは有効です。

import (
    "fmt"
    fumpt "fmt"
    format "fmt"
    _ "fmt"
)

しかし、これは意図的に行われることは非常にまれです。通常、コードがリファクタリングされ、誤って重複したインポートステートメントが追加された兆候です。また、これはあまり知られていない機能であり、混乱の原因となる可能性があります。

ただし、この機能が意図的に使用される場合があることに注意してください(たとえば、https://github.com/golang/go/commit/3409ce39bfd7584523b7a8c150a310cea92d879dを参照)。コードベースでこのパターンを許可したい場合は、このチェックを無効にすることをお勧めします。

2020.1以降利用可能

デフォルト: オフ。 "analyses": {"ST1019": true}を設定して有効にします。

パッケージドキュメント: ST1019

ST1020: エクスポートされた関数のドキュメントは関数名で始まるべき

Docコメントは、さまざまな自動プレゼンテーションを可能にする完全な文として最適に機能します。最初の文は、宣言されている名前で始まる1文の要約であるべきです。

すべてのdocコメントが記述するアイテムの名前で始まる場合、goツールのdocサブコマンドを使用して、出力をgrepにパイプすることができます。

良いドキュメントの書き方に関する詳細については、https://go.dokyumento.jp/doc/effective_go#commentaryを参照してください。

2020.1以降利用可能、非デフォルト

デフォルト: オフ。 "analyses": {"ST1020": true}を設定して有効にします。

パッケージドキュメント: ST1020

ST1021: エクスポートされた型のドキュメントは型名で始まるべき

Docコメントは、さまざまな自動プレゼンテーションを可能にする完全な文として最適に機能します。最初の文は、宣言されている名前で始まる1文の要約であるべきです。

すべてのdocコメントが記述するアイテムの名前で始まる場合、goツールのdocサブコマンドを使用して、出力をgrepにパイプすることができます。

良いドキュメントの書き方に関する詳細については、https://go.dokyumento.jp/doc/effective_go#commentaryを参照してください。

2020.1以降利用可能、非デフォルト

デフォルト: オフ。 "analyses": {"ST1021": true}を設定して有効にします。

パッケージドキュメント: ST1021

ST1022: エクスポートされた変数または定数のドキュメントは変数名で始まるべき

Docコメントは、さまざまな自動プレゼンテーションを可能にする完全な文として最適に機能します。最初の文は、宣言されている名前で始まる1文の要約であるべきです。

すべてのdocコメントが記述するアイテムの名前で始まる場合、goツールのdocサブコマンドを使用して、出力をgrepにパイプすることができます。

良いドキュメントの書き方に関する詳細については、https://go.dokyumento.jp/doc/effective_go#commentaryを参照してください。

2020.1以降利用可能、非デフォルト

デフォルト: オフ。 "analyses": {"ST1022": true}を設定して有効にします。

パッケージドキュメント: ST1022

ST1023: 変数宣言の冗長な型

2021.1以降利用可能、非デフォルト

デフォルト: オフ。 "analyses": {"ST1023": true}を設定して有効にします。

パッケージドキュメント: ST1023

appends: append後の値の欠落をチェック

このチェッカーは、スライスに追加する値を渡さないappendの呼び出しを報告します。

s := []string{"a", "b", "c"}
_ = append(s)

このような呼び出しは常にno-opであり、しばしば根本的な間違いを示します。

デフォルト: オン。

パッケージドキュメント: appends

asmdecl: アセンブリファイルとGo宣言間の不一致を報告

デフォルト: オン。

パッケージドキュメント: asmdecl

assign: 無意味な代入をチェック

このチェッカーは、x = xまたはa[i] = a[i]のような代入を報告します。これらはほとんどの場合無意味であり、そうでない場合でも通常は間違いです。

デフォルト: オン。

パッケージドキュメント: assign

atomic: sync/atomicパッケージを使用する際の一般的な間違いをチェック

atomicチェッカーは、次の形式の代入ステートメントを探します。

x = atomic.AddUint64(&x, 1)

これはアトミックではありません。

デフォルト: オン。

パッケージドキュメント: atomic

atomicalign: sync/atomic関数への非64ビットアラインメント引数をチェック

デフォルト: オン。

パッケージドキュメント: atomicalign

bools: 論理演算子に関する一般的な間違いをチェック

デフォルト: オン。

パッケージドキュメント: bools

buildtag: //go:buildおよび// +buildディレクティブをチェック

デフォルト: オン。

パッケージドキュメント: buildtag

cgocall: cgoポインタ渡し規則のいくつかの違反を検出

無効なcgoポインタ渡しをチェックします。これは、cgoを使用してCコードを呼び出す際に、cgoポインタ共有規則に従って型がほとんど常に無効な値を渡すコードを探します。具体的には、Goのchan、map、func、またはsliceをCに直接、またはポインタ、配列、構造体を介して渡す試みについて警告します。

デフォルト: オン。

パッケージドキュメント: cgocall

composites: キーなし複合リテラルをチェックします

このアナライザーは、別のパッケージからインポートされた構造体型の複合リテラルで、フィールドキー付き構文を使用していないものについて診断レポートを行います。このようなリテラルは、構造体に新しいフィールド(たとえ非エクスポートであっても)が追加されるとコンパイルが失敗するため、脆いです。

例として、

err = &net.DNSConfigError{err}

は、次のように置き換える必要があります。

err = &net.DNSConfigError{Err: err}

デフォルト: オン。

パッケージドキュメント: composites

copylocks: ロックが誤って値渡しされているかどうかを確認します

sync.Mutex や sync.WaitGroup のようなロックを含む値を不注意にコピーすると、両方のコピーが誤動作する可能性があります。通常、このような値はポインタを介して参照されるべきです。

デフォルト: オン。

パッケージドキュメント: copylocks

deepequalerrors: エラー値に対する reflect.DeepEqual の呼び出しをチェックします

deepequalerrors チェッカーは、次の形式の呼び出しを探します。

reflect.DeepEqual(err1, err2)

ここで、err1 と err2 はエラーです。エラーを比較するために reflect.DeepEqual を使用することは推奨されません。

デフォルト: オン。

パッケージドキュメント: deepequalerrors

defers: defer ステートメントの一般的な誤りを報告します

defers アナライザーは、defer ステートメントが time.Since の非遅延呼び出しをもたらす場合に診断レポートを行います。経験上、これはほとんど常に間違いであることが示されているためです。

start := time.Now()
...
defer recordLatency(time.Since(start)) // error: call to time.Since is not deferred

正しいコードは次のとおりです。

defer func() { recordLatency(time.Since(start)) }()

デフォルト: オン。

パッケージドキュメント: defers

deprecated: 非推奨の識別子の使用をチェックします

deprecated アナライザーは、非推奨のシンボルとパッケージのインポートを探します。

Go の非推奨識別子の文書化と通知の慣習については、https://go.dokyumento.jp/wiki/Deprecated を参照してください。

デフォルト: オン。

パッケージドキュメント: deprecated

directive: //go:debug などの Go ツールチェーンドキュメントをチェックします

このアナライザーは、パッケージディレクトリ内のすべての Go ソースファイル(//go:build 制約で除外されたものも含む)およびすべての非 Go ソースファイルで、既知の Go ツールチェーンドキュメントに関する問題をチェックします。

//go:debug(https://go.dokyumento.jp/doc/godebug) について、アナライザーは、ディレクティブが Go ソースファイルにのみ、パッケージコメントの上にのみ、そしてパッケージ main または *_test.go ファイルにのみ配置されていることをチェックします。

他の既知のディレクティブのサポートは、今後追加される可能性があります。

このアナライザーは、buildtag アナライザーが処理する //go:build はチェックしません。

デフォルト: オン。

パッケージドキュメント: directive

embed: //go:embed ディレクティブの使用法をチェックします

このアナライザーは、//go:embed ディレクティブが存在する場合に embed パッケージがインポートされていることをチェックし、インポートが不足している場合は追加の修正を提案します。

このアナライザーは、//go:embed ディレクティブが単一の変数の宣言の前に来ていることもチェックします。

デフォルト: オン。

パッケージドキュメント: embed

errorsas: ポインタではない、またはエラーではない値を errors.As に渡すことを報告します

errorsas 分析は、2番目の引数の型が error を実装する型へのポインタではない errors.As の呼び出しを報告します。

デフォルト: オン。

パッケージドキュメント: errorsas

fillreturns: 戻り値の数が正しくないことによるエラーの修正を提案します

このチェッカーは、「戻り値の数が間違っています (want %d, got %d)」という種類の型エラーに対する修正を提案します。例として、

func m() (int, string, *bool, error) {
    return
}

は、次のように変換されます。

func m() (int, string, *bool, error) {
    return 0, "", nil, nil
}

この機能は https://github.com/sqs/goreturns と似ています。

デフォルト: オン。

パッケージドキュメント: fillreturns

framepointer: フレームポインタを保存する前に破壊するアセンブリを報告します

デフォルト: オン。

パッケージドキュメント: framepointer

gofix: go:fix コメントディレクティブに基づいて修正を適用します

gofix アナライザーは、インライン化のマークが付けられた関数と定数をインライン化します。

関数

このように、インライン化のマークが付けられた関数がある場合、

//go:fix inline
func Square(x int) int { return Pow(x, 2) }

このアナライザーは、同じパッケージまたは他のパッケージでのその関数への呼び出しをインライン化することを推奨します。

インライン化は、非推奨の関数から移行するために使用できます。

// Deprecated: prefer Pow(x, 2).
//go:fix inline
func Square(x int) int { return Pow(x, 2) }

インポートパスが変更された場合や、より上位のメジャーバージョンが利用可能になった場合など、廃止されたパッケージから移行するためにも使用できます。

package pkg

import pkg2 "pkg/v2"

//go:fix inline
func F() { pkg2.F(nil) }

pkg.F() の呼び出しを pkg2.F(nil) に置き換えてもプログラムに影響がないため、このメカニズムは多数の呼び出しを更新するための低リスクな方法を提供します。可能な場合は、自動移行を可能にするために、古い API を新しい API の観点から表現することを推奨します。

インライナーは、引数式の評価順序の変更など、微妙なものも含めて、動作の変更を避けるように注意します。すべてのパラメータ変数を安全に排除できない場合、次のような「バインディング宣言」を導入することがあります。

var params = args

引数式を正しい順序で評価し、パラメータ変数にバインドするためです。結果として得られるコード変換はスタイル的に最適ではない可能性があるため、このようなインライン化は、アナライザードライバーに -gofix.allow_binding_decl=false フラグを指定することで無効にできます。

(呼び出しを「縮小」することが安全でない場合、つまり f(x) の呼び出しを関数 f の本体に適切に置き換えることが安全でない場合、インライナーのメカニズムは f を関数リテラル func(){…}() に置き換えることができます。しかし、gofix アナライザーは、スタイル上の理由から、このような「リテラル化」を無条件にすべて破棄します。)

定数

このように、インライン化のマークが付けられた定数がある場合、

//go:fix inline
const Ptr = Pointer

このアナライザーは、Ptr の使用を Pointer に置き換えることを推奨します。

関数と同様に、インライン化は非推奨の定数や廃止されたパッケージの定数を置き換えるために使用できます。

定数定義は、他の名前付き定数を参照している場合にのみ、インライン化のマークを付けることができます。

「//go:fix inline」コメントは、上記のように単一の const 宣言の前に、または次のようにグループの一部である const 宣言の前に

const (
   C = 1
   //go:fix inline
   Ptr = Pointer
)

または、グループ全体に適用されるグループの前に

//go:fix inline
const (
    Ptr = Pointer
    Val = Value
)

提案 https://go.dokyumento.jp/issue/32816 は、「//go:fix」ディレクティブを導入します。

この(公式にはサポートされていない)コマンドを使用して、gofix の修正を一括適用できます。

$ go run golang.org/x/tools/internal/gofix/cmd/gofix@latest -test ./...

(gopls をモジュールの依存関係として追加するために「go get -tool」を使用しないでください。gopls コマンドはリリースブランチからビルドする必要があります。)

デフォルト: オン。

パッケージドキュメント: gofix

hostport: net.Dial に渡されるアドレスの形式をチェックします

このアナライザーは、この例のように fmt.Sprintf を使用してネットワークアドレス文字列を生成するコードをフラグ立てします。

addr := fmt.Sprintf("%s:%d", host, 12345) // "will not work with IPv6"
...
conn, err := net.Dial("tcp", addr)       // "when passed to dial here"

アナライザーは、正しいアプローチ、net.JoinHostPort への呼び出しを使用するための修正を提案します。

addr := net.JoinHostPort(host, "12345")
...
conn, err := net.Dial("tcp", addr)

同様の診断と修正は、「%s:%s」のフォーマット文字列に対しても生成されます。

デフォルト: オン。

パッケージドキュメント: hostport

httpresponse: HTTP レスポンスの使用における間違いをチェックします

net/http パッケージを使用する際の一般的な間違いは、レスポンスが有効かどうかを判断するエラーをチェックする前に、http.Response Body を閉じる関数呼び出しを defer することです。

resp, err := http.Head(url)
defer resp.Body.Close()
if err != nil {
    log.Fatal(err)
}
// (defer statement belongs here)

このチェッカーは、このような間違いを診断レポートすることで、潜在的な nil デリファレンスバグを発見するのに役立ちます。

デフォルト: オン。

パッケージドキュメント: httpresponse

ifaceassert: 不可能なインターフェースからインターフェースへの型アサーションを検出します

このチェッカーは、v の静的型 V が、ターゲットインターフェース T を実装できないインターフェースである型アサーション v.(T) および対応する型スイッチケースにフラグを立てます。これは、V と T が同じ名前で異なるシグネチャを持つメソッドを含む場合に発生します。例:

var v interface {
    Read()
}
_ = v.(io.Reader)

v の Read メソッドは io.Reader の Read メソッドとは異なるシグネチャを持つため、このアサーションは成功しません。

デフォルト: オン。

パッケージドキュメント: ifaceassert

infertypeargs: 呼び出し式における不要な型引数をチェックします

明示的な型引数は、関数引数または他の型引数から推論できる場合は、呼び出し式から省略できます。

func f[T any](T) {}

func _() {
    f[string]("foo") // string could be inferred
}

デフォルト: オン。

パッケージドキュメント: infertypeargs

loopclosure: ネストされた関数内からのループ変数への参照をチェックします

このアナライザーは、関数リテラルが囲むループのイテレーション変数を参照し、ループがその関数を(go または defer などで)呼び出すことで、ループのイテレーションより長く存続し、変数の誤った値を観測する可能性がある場所を報告します。

注: イテレーション変数は、Go バージョン <=1.21 ではループのイテレーションよりも長く存続できます。Go 1.22 以降では、ループ変数のライフタイムが変更され、ループのイテレーションごとに新しいイテレーション変数が作成されます。(go.dev/issue/60078 を参照してください。)

この例では、すべての遅延関数はループが完了した後に実行されるため、すべて v の最終値を観測します [

for _, v := range list {
    defer func() {
        use(v) // incorrect
    }()
}

1つの修正方法は、ループの各イテレーションで新しい変数を作成することです。

for _, v := range list {
    v := v // new var per iteration
    defer func() {
        use(v) // ok
    }()
}

Go バージョン 1.22 以降、前の2つの for ループは同等であり、両方とも正しいです。

次の例では go ステートメントを使用しており、同様の問題があります [

for _, v := range elem {
    go func() {
        use(v)  // incorrect, and a data race
    }()
}

修正は以前と同じです。チェッカーは、golang.org/x/sync/errgroup.Group によって開始された goroutine の問題も報告します。この形式の発見しにくいバリアントは、並列テストでよく見られます。

func Test(t *testing.T) {
    for _, test := range tests {
        t.Run(test.name, func(t *testing.T) {
            t.Parallel()
            use(test) // incorrect, and a data race
        })
    }
}

t.Parallel() 呼び出しにより、関数の残りの部分がループと同時に実行されます [

アナライザーは最後のステートメント内の参照のみを報告します。後続のステートメントが参照を無害にする可能性がある影響を理解するほど深くはないためです。(「最後のステートメント」は、if、switch、select などの複合ステートメントで再帰的に定義されます。)

参照: https://go.dokyumento.jp/doc/go_faq.html#closures_and_goroutines

デフォルト: オン。

パッケージドキュメント: loopclosure

lostcancel: context.WithCancel によって返されるキャンセル関数が呼び出されていることをチェックします

context.WithCancel、WithTimeout、WithDeadline、および WithCancelCause などのバリアントによって返されるキャンセル関数は呼び出す必要があります。そうしないと、新しいコンテキストは親コンテキストがキャンセルされるまでアクティブなままになります。(バックグラウンドコンテキストは決してキャンセルされません。)

デフォルト: オン。

パッケージドキュメント: lostcancel

maprange: range ステートメントでの maps.Keys および maps.Values への不要な呼び出しをチェックします

次のように記述されたループを検討してください。

for val := range maps.Values(m) {
    fmt.Println(val)
}

これは代わりに maps.Values への呼び出しなしで記述されるべきです。

for _, val := range m {
    fmt.Println(val)
}

golang.org/x/exp/maps はイテレータではなく Keys/Values のスライスを返しますが、不要な呼び出しも同様に削除されるべきです。

for _, key := range maps.Keys(m) {
    fmt.Println(key)
}

は次のように書き換えるべきです。

for key := range m {
    fmt.Println(key)
}

デフォルト: オン。

パッケージドキュメント: maprange

modernize: 最新の構成要素を使用してコードを簡素化します

このアナライザーは、Go とその標準ライブラリのより現代的な機能を使用することで、既存のコードを簡素化し、明確にする機会を報告します。

各診断は修正を提供します。私たちの意図は、これらの修正がプログラムの動作を変更することなく安全に一括適用できることです。場合によっては、提案された修正が不完全で、(例えば) 未使用のインポートや未使用のローカル変数を引き起こし、ビルドの破損につながる可能性があります。しかし、これらの問題は通常、修正が簡単です。私たちは、修正がプログラムの動作を変更するすべてのモダナイザーに重大なバグがあると見なし、それを修正するために努力します。

すべての近代化修正を一括適用するには、次のコマンドを使用できます。

$ go run golang.org/x/tools/gopls/internal/analysis/modernize/cmd/modernize@latest -fix -test ./...

(gopls をモジュールの依存関係として追加するために「go get -tool」を使用しないでください。gopls コマンドはリリースブランチからビルドする必要があります。)

ツールが競合する修正について警告する場合、すべての修正がクリーンに適用されるまで、複数回実行する必要がある場合があります。このコマンドは公式にサポートされているインターフェースではなく、将来変更される可能性があります。

このツールによって生成された変更は、マージする前に通常通りレビューする必要があります。場合によっては、ループが単純な関数呼び出しに置き換えられ、ループ内のコメントが破棄されることがあります。価値のあるコメントを失わないように、人間の判断が必要となる場合があります。

modernize によって報告される各診断には特定のカテゴリがあります。(カテゴリは以下にリストされています。) 「efaceany」(安全な場合に「interface{}」を「any」に置き換える) などの一部のカテゴリの診断は特に多数あります。修正を2つのパスで適用すると、コードレビューの負担が軽減される場合があります。最初の変更はカテゴリ「efaceany」の修正のみで構成され、2番目の変更はその他のすべての修正で構成されます。これは -category フラグを使用して実現できます。

$ modernize -category=efaceany  -fix -test ./...
$ modernize -category=-efaceany -fix -test ./...

modernize 診断のカテゴリ

  • forvar: go1.22 の新しいループセマンティクスによって不要になった x := x 変数宣言を削除します。

  • slicescontains: go1.21 で追加された slices.Contains への呼び出しによって「for i, elem := range s { if elem == needle { …; break }」を置き換えます。

  • minmax: go1.21 で追加された組み込みの min または max 関数への呼び出しによって、if/else 条件付き代入を置き換えます。

  • sortslice: go1.21 で追加された slices.Sort(s) への呼び出しによって、sort.Slice(s, func(i, j int) bool) { return s[i] < s[j] } を置き換えます。

  • efaceany: go1.18 で追加された「any」型によって interface{} を置き換えます。

  • mapsloop: go1.21 で追加された maps パッケージの Collect、Copy、Clone、または Insert 関数のいずれかの呼び出しによって、m[k]=v マップ更新の周りのループを置き換えます。

  • fmtappendf: go1.19 で追加された fmt.Appendf(nil, …) によって []byte(fmt.Sprintf…) を置き換えます。

  • testingcontext: go1.24 で追加された t.Context でテスト内の context.WithCancel の使用を置き換えます。

  • omitzero: go1.24 で追加された構造体上の omitempty を omitzero に置き換えます。

  • bloop: ベンチマーク内の「for i := range b.N」または「for range b.N」を「for b.Loop()」に置き換え、b.StopTimer、b.StartTimer、b.ResetTimer への先行する呼び出しを削除します。

    B.Loop は、コンパイラの最適化 (インライン化など) を意図的に無効にし、ベンチマークが完全に最適化されないようにします。ただし、現在、割り当てが増加するため、ベンチマークが遅くなる場合があります。詳細は https://go.dokyumento.jp/issue/73137 を参照してください。

  • rangeint: go1.22 で追加された「for i := 0; i < n; i++」の3節ループを「for i := range n」に置き換えます。

  • stringsseq: go1.24 のより効率的な SplitSeq で「for range strings.Split(…)」の Split を置き換え、または FieldSeq で Fields を置き換えます。

  • stringscutprefix: go1.20 で strings パッケージに追加された CutPrefix で、HasPrefix の後に TrimPrefix を使用する一部のケースを置き換えます。

  • waitgroup: go1.25 のより複雑でない WaitGroup.Go メソッドで、sync.WaitGroup の古い複雑な使用法を置き換えます。

デフォルト: オン。

パッケージドキュメント: modernize

nilfunc: 関数と nil の間の無用な比較をチェックします

無用な比較とは、f() == nil とは対照的に f == nil のようなものです。

デフォルト: オン。

パッケージドキュメント: nilfunc

nilness: 冗長または不可能な nil 比較をチェックします

nilness チェッカーは、パッケージ内の各関数の制御フローグラフを検査し、nil ポインタのデリファレンス、退化した nil ポインタ、nil 値によるパニックを報告します。退化した比較とは、x が静的に nil または非 nil であることがわかっている場合の x==nil または x!=nil の形式です。これらはしばしば間違いであり、特にエラーに関連する制御フローで発生します。nil 値によるパニックは、これらが次の方法では検出できないためチェックされます。

if r := recover(); r != nil {

このチェックは、次のような条件を報告します。

if f == nil { // impossible condition (f is a function)
}

p := &v
...
if p != nil { // tautological condition
}

if p == nil {
    print(*p) // nil dereference
}

if p == nil {
    panic(p)
}

制御フローが非常に複雑で、バグが見つけにくい場合があります。以下の例では、最初の return の後、err は nil でなければならないため、err.Error 式は必ずパニックを引き起こします。途中のループは単なる注意散漫です。

...
err := g.Wait()
if err != nil {
    return err
}
partialSuccess := false
for _, err := range errs {
    if err == nil {
        partialSuccess = true
        break
    }
}
if partialSuccess {
    reportStatus(StatusMessage{
        Code:   code.ERROR,
        Detail: err.Error(), // "nil dereference in dynamic method call"
    })
    return nil
}

デフォルト: オン。

パッケージドキュメント: nilness

nonewvars: 「:= の左側に新しい変数がありません」というエラーに対する修正を提案します

このチェッカーは、「:= の左側に新しい変数がありません」という種類の型エラーに対する修正を提案します。例として、

z := 1
z := 2

は、次のように変換されます。

z := 1
z = 2

デフォルト: オン。

パッケージドキュメント: nonewvars

noresultvalues: 予期しない戻り値の修正を提案します

このチェッカーは、「戻り値が予期されていません」または「戻り値が多すぎます」という型の型エラーに対する修正を提案します。例として、

func z() { return nil }

は、次のように変換されます。

func z() { return }

デフォルト: オン。

パッケージドキュメント: noresultvalues

printf: Printf フォーマット文字列と引数の一貫性をチェックします

このチェックは、[fmt.Printf] や [fmt.Sprintf] などの書式設定関数の呼び出し、および [log.Printf] などのこれらの関数の検出されたラッパーに適用されます。書式設定文字列の構文エラーや、動詞と引数との不一致(数と型)など、さまざまな間違いを報告します。

フォーマット演算子とそのオペランド型の完全なセットについては、fmt パッケージのドキュメントを参照してください。

デフォルト: オン。

パッケージドキュメント: printf

recursiveiter: 非効率的な再帰イテレータをチェックします

このアナライザーは、イテレータ (iter.Seq または iter.Seq2) を返す関数が、range ステートメントのオペランドとして自身を呼び出している場合に報告します。これは非効率的であるためです。

ツリーやリンクリストのような再帰データ型のためにイテレータ (例: iter.Seq[T]) を実装する場合、各子要素のイテレータを再帰的に反復したくなる誘惑に駆られます。

以下は、二分木を naive に反復するイテレータの例です。

type tree struct {
    value       int
    left, right *tree
}

func (t *tree) All() iter.Seq[int] {
    return func(yield func(int) bool) {
        if t != nil {
            for elem := range t.left.All() { // "inefficient recursive iterator"
                if !yield(elem) {
                    return
                }
            }
            if !yield(t.value) {
                return
            }
            for elem := range t.right.All() { // "inefficient recursive iterator"
                if !yield(elem) {
                    return
                }
            }
        }
    }
}

これはツリーの要素を正しく列挙しますが、重大なパフォーマンス問題(実際には2つ)を隠しています。N個のノードを持つバランスの取れたツリーを考えてみましょう。ルートノードを反復すると、ツリーのすべてのノードで All が1回呼び出されます。これにより、リーフノードで yield(t.value) が呼び出されたときに、ネストされたアクティブな range-over-func ステートメントの連鎖が発生します。

最初のパフォーマンス問題は、各 range-over-func ステートメントが通常ヒープに変数割り当てを必要とするため、ツリーの反復ではツリー内の要素と同じ数の変数が割り当てられ、合計で O(N) の割り当てが発生し、すべて不要であることです。

2番目の問題は、ツリーのリーフに対する yield の各呼び出しにより、囲む各 range ループが値を受け取り、それをすぐにそれぞれの yield 関数に渡すことです。これにより、要素ごとに log(N) の動的 yield 呼び出しの連鎖が発生し、全体で O(N*log N) の動的呼び出しが発生しますが、必要なのは O(N) だけです。

再帰イテレータのより良い実装戦略は、まず再帰データ型の「every」演算子を定義することです。ここで every(f) は、データ型のすべての要素 x について f(x) が true であるかどうかを報告します。私たちのツリーの場合、every 関数は次のようになります。

func (t *tree) every(f func(int) bool) bool {
    return t == nil ||
        t.left.every(f) && f(t.value) && t.right.every(f)
}

イテレータは、この関数の単純なラッパーとして簡単に表現できます。

func (t *tree) All() iter.Seq[int] {
    return func(yield func(int) bool) {
        _ = t.every(yield)
    }
}

実際、tree.All は、各要素に対して yield が true を返すかどうかを計算し、false を返す場合はショートサーキットし、最終的なブール結果を破棄します。

これにより、パフォーマンス特性が大幅に向上します。ツリーの要素ごとに1つの動的呼び出しを行い、何もヒープ割り当てを行いません。また、より明確です。

デフォルト: オン。

パッケージドキュメント: recursiveiter

shadow: 変数の意図しないシャドウイングの可能性をチェックします

このアナライザーは、シャドウされた変数をチェックします。シャドウされた変数とは、外側のスコープの変数と同じ名前と型を持ち、内側のスコープで宣言された変数であり、外側の変数が内側の変数が宣言された後に参照されるものです。

(この定義は洗練される可能性があります。このモジュールは誤検知が多すぎるため、まだデフォルトでは有効になっていません。)

func BadRead(f *os.File, buf []byte) error {
    var err error
    for {
        n, err := f.Read(buf) // shadows the function variable 'err'
        if err != nil {
            break // causes return of wrong value
        }
        foo(buf)
    }
    return err
}

デフォルト: オフ。"analyses": {"shadow": true} を設定して有効にします。

パッケージドキュメント: shadow

shift: 整数の幅と等しいかそれ以上のシフトをチェックします

デフォルト: オン。

パッケージドキュメント: shift

sigchanyzer: os.Signal のバッファなしチャネルをチェックします

このチェッカーは、次のような形式の呼び出し式を報告します。

signal.Notify(c <-chan os.Signal, sig ...os.Signal),

ここで c はバッファなしチャネルであり、シグナルを見逃すリスクがあります。

デフォルト: オン。

パッケージドキュメント: sigchanyzer

simplifycompositelit: 複合リテラルの簡素化をチェックします

次のような形式の配列、スライス、またはマップの複合リテラルは、

[]T{T{}, T{}}

次のように簡素化されます。

[]T{{}, {}}

これは、「gofmt -s」が適用する簡素化の1つです。

このアナライザーは生成されたコードを無視します。

デフォルト: オン。

パッケージドキュメント: simplifycompositelit

simplifyrange: range ステートメントの簡素化をチェックします

次のような形式の range は、

for x, _ = range v {...}

次のように簡素化されます。

for x = range v {...}

次のような形式の range は、

for _ = range v {...}

次のように簡素化されます。

for range v {...}

これは、「gofmt -s」が適用する簡素化の1つです。

このアナライザーは生成されたコードを無視します。

デフォルト: オン。

パッケージドキュメント: simplifyrange

simplifyslice: スライスの簡素化をチェックします

次のような形式のスライス式は、

s[a:len(s)]

次のように簡素化されます。

s[a:]

これは、「gofmt -s」が適用する簡素化の1つです。

このアナライザーは生成されたコードを無視します。

デフォルト: オン。

パッケージドキュメント: simplifyslice

slog: 無効な構造化ロギング呼び出しをチェックします

slog チェッカーは、log/slog パッケージの関数への、キーと値のペアが交互に現れる呼び出しを探します。キー位置の引数が文字列でも slog.Attr でもなく、最後のキーに値がない呼び出しを報告します。例えば、次のような呼び出しを報告します。

slog.Warn("message", 11, "k") // slog.Warn arg "11" should be a string or a slog.Attr

slog.Info("message", "k1", v1, "k2") // call to slog.Info missing a final value

デフォルト: オン。

パッケージドキュメント: slog

sortslice: sort.Slice の引数型をチェックします

sort.Slice はスライス型の引数を必要とします。sort.Slice に渡される interface{} の値が実際にスライスであることを確認します。

デフォルト: オン。

パッケージドキュメント: sortslice

stdmethods: 著名なインターフェースのメソッドのシグネチャをチェックします

型がインターフェースを満たすことを意図していても、そのメソッドシグネチャの間違いのために失敗することがあります。例えば、この WriteTo メソッドの結果は、io.WriterTo を満たすために error ではなく (int64, error) であるべきです。

type myWriterTo struct{...}
func (myWriterTo) WriteTo(w io.Writer) error { ... }

このチェックは、標準ライブラリのいくつかの著名なインターフェースメソッドの名前と一致する各メソッドが、そのインターフェースに対して正しいシグネチャを持っていることを保証します。

チェックされるメソッド名には以下が含まれます。

Format GobEncode GobDecode MarshalJSON MarshalXML
Peek ReadByte ReadFrom ReadRune Scan Seek
UnmarshalJSON UnreadByte UnreadRune WriteByte
WriteTo

デフォルト: オン。

パッケージドキュメント: stdmethods

stdversion: 新しすぎる標準ライブラリシンボルの使用を報告します

stdversion アナライザーは、参照元のファイルで有効な Go リリースよりも新しい Go リリースで導入された標準ライブラリのシンボルへの参照を報告します。(ファイルの Go バージョンは、モジュールの go.mod ファイルの 'go' ディレクティブ、またはファイルの先頭にある「//go:build go1.X」ビルドタグによって定義されることを思い出してください。)

アナライザーは、「新しすぎる」型自体の「新しすぎる」フィールドまたはメソッドへの参照に対しては診断レポートを行いません。これは誤検知を引き起こす可能性があるためです。例えば、フィールドまたはメソッドが Go バージョン制約によってガードされている型エイリアスを介してアクセスされる場合などです。

デフォルト: オン。

パッケージドキュメント: stdversion

stringintconv: string(int) 変換をチェックします

このチェッカーは、x が整数 (ただしバイトまたはルーンではない) 型である string(x) 形式の変換にフラグを立てます。このような変換は、期待されるような x の10進数文字列表現ではなく、Unicode コードポイント x の UTF-8 表現を返すため、推奨されません。さらに、x が無効なコードポイントを表す場合、変換は静的に拒否できません。

コードポイントを使用する意図がある変換の場合は、string(rune(x)) に置き換えることを検討してください。そうでない場合は、strconv.Itoa とその同等のものが、目的の基数で値の文字列表現を返します。

デフォルト: オン。

パッケージドキュメント: stringintconv

structtag: 構造体フィールドタグが reflect.StructTag.Get に準拠していることをチェックします

また、エクスポートされていないフィールドで使用されている特定の構造体タグ (json, xml) も報告します。

デフォルト: オン。

パッケージドキュメント: structtag

testinggoroutine: テストによって開始されたゴルーチンからの (*testing.T).Fatal の呼び出しを報告します

テストを突然終了する関数、例えば *testing.T の Fatal、Fatalf、FailNow、Skip{,f,Now} メソッドは、テストゴルーチン自体から呼び出す必要があります。このチェッカーは、テストによって開始されたゴルーチン内で発生するこれらの関数への呼び出しを検出します。例えば、

func TestFoo(t *testing.T) {
    go func() {
        t.Fatal("oops") // error: (*T).Fatal called from non-test goroutine
    }()
}

デフォルト: オン。

パッケージドキュメント: testinggoroutine

tests: テストと例の一般的な誤った使用法をチェックします

tests チェッカーは、Test、Benchmark、Fuzzing、および Example 関数を巡回し、不正な名前、誤ったシグネチャ、および存在しない識別子を説明する例をチェックします。

Test、Benchmark、および Example に強制される規則については、golang.org/pkg/testing の testing パッケージのドキュメントを参照してください。

デフォルト: オン。

パッケージドキュメント: tests

timeformat: 2006-02-01 での (time.Time).Format または time.Parse の呼び出しをチェックします

timeformat チェッカーは、2006-02-01 (yyyy-dd-mm) 形式の時間フォーマットを探します。国際的には、「yyyy-dd-mm」は一般的なカレンダーの日付標準には存在せず、したがって 2006-01-02 (yyyy-mm-dd) が意図されていた可能性が高いです。

デフォルト: オン。

パッケージドキュメント: timeformat

unmarshal: ポインタではない、またはインターフェースではない値を unmarshal に渡すことを報告します

unmarshal 分析は、json.Unmarshal などの関数への呼び出しで、引数型がポインタまたはインターフェースではない場合に報告します。

デフォルト: オン。

パッケージドキュメント: unmarshal

unreachable: 到達不能なコードをチェックします

unreachable アナライザーは、return ステートメント、panic の呼び出し、無限ループ、または同様の構造によって先行されているため、実行が到達できないステートメントを検出します。

デフォルト: オン。

パッケージドキュメント: unreachable

unsafeptr: uintptr から unsafe.Pointer への無効な変換をチェックします

unsafeptr アナライザーは、整数をポインタに変換するために unsafe.Pointer を誤って使用している可能性のある箇所を報告します。uintptr から unsafe.Pointer への変換は、メモリ内にポインタ値を保持する uintptr 型のワードがあることを意味する場合、無効です。なぜなら、そのワードはスタックコピーやガベージコレクタに見えないためです。

デフォルト: オン。

パッケージドキュメント: unsafeptr

unusedfunc: 未使用の関数、メソッドなどをチェックします

unusedfunc アナライザーは、自身の宣言以外で参照されていない関数とメソッドを報告します。

関数は、エクスポートされておらず、(自身の宣言内を除いて) 参照されていない場合に未使用と見なされます。

メソッドは、エクスポートされておらず、(自身の宣言内を除いて) 参照されておらず、その名前が同じパッケージ内で宣言されたインターフェース型のどのメソッドの名前とも一致しない場合に未使用と見なされます。

このツールは、次のような一部の状況で誤検知を報告する場合があります。

  • go:linkname メカニズムを使用して別のパッケージから参照される非エクスポート関数の宣言で、宣言のドキュメントコメントに go:linkname コメントがない場合。

    (このようなコードは、いずれにせよ強く推奨されません。linkname アノテーションは、どうしても使用する必要がある場合でも、宣言とエイリアスの両方に使用されるべきです。)

  • 「runtime」パッケージ内のコンパイラ組み込み関数で、参照されていないにもかかわらずコンパイラに認識され、コンパイルされたオブジェクトコードによって間接的に呼び出されるもの。

  • アセンブリからのみ呼び出される関数。

  • 現在のビルド構成でビルドタグが選択されていないファイルからのみ呼び出される関数。

これらの制限事項については、https://github.com/golang/go/issues/71686 を参照してください。

unusedfunc アルゴリズムは golang.org/x/tools/cmd/deadcode ツールほど正確ではありませんが、モジュール分析フレームワーク内で実行されるため、gopls 内でほぼリアルタイムのフィードバックが可能であるという利点があります。

unusedfunc アナライザーは、未使用の型、変数、定数も報告します。iota で定義された列挙型定数は、未使用の値でも論理的な順序を維持するために存在し続ける必要があるため、無視されます。

デフォルト: オン。

パッケージドキュメント: unusedfunc

unusedparams: 関数の未使用のパラメータをチェックします

unusedparams アナライザーは、関数に未使用のパラメータがあるかどうかをチェックします。

健全性を確保するために、以下を無視します。

  • 「アドレス取得された」関数、つまり直接呼び出されるのではなく値として使用される関数。これらのシグネチャは func 型に準拠する必要がある場合があります。
  • エクスポートされた関数またはメソッド。これらは別のパッケージでアドレス取得される可能性があるため。
  • 名前が同じパッケージで宣言されたインターフェースメソッドと一致する非エクスポートメソッド。メソッドのシグネチャがインターフェース型に準拠する必要がある場合があるため。
  • 空のボディを持つ関数、または panic への呼び出しのみを含む関数。
  • 名前がない、または「_」というブランク識別子で名前が付けられたパラメータ。

アナライザーは、パラメータ名を「_」に置き換える修正を提案しますが、このような場合、より深い修正は「Refactor: remove unused parameter」コードアクションを呼び出すことで得られます。これにより、パラメータ全体とその呼び出しサイトでの対応するすべての引数が削除され、引数式内の副作用が維持されます。詳細については、https://github.com/golang/tools/releases/tag/gopls%2Fv0.14 を参照してください。

このアナライザーは生成されたコードを無視します。

デフォルト: オン。

パッケージドキュメント: unusedparams

unusedresult: 一部の関数呼び出しの未使用の結果をチェックします

fmt.Errorf のような一部の関数は結果を返し、副作用がないため、結果を破棄することは常に間違いです。他の関数は、無視してはならないエラーや、呼び出す必要があるクリーンアップ操作を返す場合があります。このアナライザーは、呼び出しの結果が無視された場合に、これらの関数への呼び出しを報告します。

関数のセットはフラグを使用して制御できます。

デフォルト: オン。

パッケージドキュメント: unusedresult

unusedvariable: 未使用の変数をチェックし、修正を提案します

デフォルト: オン。

パッケージドキュメント: unusedvariable

unusedwrite: 未使用の書き込みをチェックします

このアナライザーは、読み取られることのない構造体フィールドや配列への書き込みインスタンスを報告します。具体的には、構造体オブジェクトや配列がコピーされると、その要素はコンパイラによって暗黙的にコピーされ、このコピーへの要素書き込みは元のオブジェクトに何も影響しません。

type T struct { x int }

func f(input []T) {
    for i, v := range input {  // v is a copy
        v.x = i  // unused write to field x
    }
}

もう1つの例は、ポインタ以外のレシーバーに関するものです。

type T struct { x int }

func (t T) f() {  // t is a copy
    t.x = i  // unused write to field x
}

デフォルト: オン。

パッケージドキュメント: unusedwrite

waitgroup: sync.WaitGroup の誤用をチェックします

このアナライザーは、新しいゴルーチン内からの (*sync.WaitGroup).Add メソッドへの誤った呼び出しを検出し、Add が Wait と競合する原因となります。

// WRONG
var wg sync.WaitGroup
go func() {
        wg.Add(1) // "WaitGroup.Add called from inside new goroutine"
        defer wg.Done()
        ...
}()
wg.Wait() // (may return prematurely before new goroutine starts)

正しいコードは、ゴルーチンを開始する前に Add を呼び出します。

// RIGHT
var wg sync.WaitGroup
wg.Add(1)
go func() {
    defer wg.Done()
    ...
}()
wg.Wait()

デフォルト: オン。

パッケージドキュメント: waitgroup

yield: 結果が無視される yield への呼び出しを報告します

yield 関数が false を返した後、呼び出し元は yield 関数を再度呼び出すべきではありません。通常、イテレータはすぐに戻るべきです。

この例は、yield への呼び出しの結果をチェックしないため、このアナライザーが診断レポートを行います。

yield(1) // yield may be called again (on L2) after returning false
yield(2)

修正されたコードは次のいずれかです。

if yield(1) { yield(2) }

または単に

_ = yield(1) && yield(2)

yield の結果を無視することが常に間違いであるとは限りません。たとえば、これは有効な単一要素イテレータです。

yield(1) // ok to ignore result
return

false を返した yield 呼び出しの後に別の呼び出しが続く可能性がある場合にのみ間違いです。

デフォルト: オン。

パッケージドキュメント: yield


このドキュメントのソースファイルは、golang.org/x/tools/gopls/doc の下にあります。