gocritic

Table of Contents

  1. 解決する問題
  2. 設定
  3. オプション
    1. タグによる一括設定
    2. 設定パラメータ
    3. チェック固有の設定例
  4. 主要なチェック
    1. diagnostic(バグ検出)
    2. style(コードスタイル)
    3. performance(パフォーマンス)
  5. サンプル
    1. 検出例
    2. 修正例
  6. 注意点
  7. 参考リンク

gocritic は診断・スタイル・パフォーマンスなど幅広いカテゴリの約 100 個のチェックを提供する包括的な Linter です。

解決する問題

Go のコードには、コンパイルは通るが非効率だったり、バグの温床になるパターンが多数存在します。gocritic は単一の Linter で広範なチェックを行い、コードの品質・パフォーマンス・一貫性を総合的に向上させます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Before: gocritic が検出するさまざまな問題
func process(items []Item) {
// rangeValCopy: range 変数でコピーが発生(大きな構造体で非効率)
for _, item := range items {
fmt.Println(item.Name)
}

// ifElseChain: if-else チェーンは switch に置き換え可能
if x == 1 {
doA()
} else if x == 2 {
doB()
} else if x == 3 {
doC()
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// After: 改善されたコード
func process(items []Item) {
for i := range items {
fmt.Println(items[i].Name)
}

switch x {
case 1:
doA()
case 2:
doB()
case 3:
doC()
}
}

設定

公式ドキュメント

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# .golangci.yml 設定例
version: "2"

linters:
enable:
- gocritic
settings:
gocritic:
enabled-tags:
- diagnostic
- style
- performance
disabled-checks:
- hugeParam
- rangeValCopy

オプション

タグによる一括設定

タグ 説明 デフォルト
diagnostic バグや論理エラーの検出 有効
style コードスタイルの一貫性 有効
performance パフォーマンスの最適化 有効
experimental 開発中のチェック(誤検知の可能性あり) 無効
opinionated 好みが分かれるチェック 無効

設定パラメータ

オプション 説明
enabled-tags []string 有効にするタグ
disabled-tags []string 無効にするタグ
enabled-checks []string 個別に有効にするチェック
disabled-checks []string 個別に無効にするチェック
settings map 各チェックの詳細設定

チェック固有の設定例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
linters:
settings:
gocritic:
enabled-tags:
- diagnostic
- style
- performance
settings:
rangeValCopy:
sizeThreshold: 128
hugeParam:
sizeThreshold: 256
captLocal:
paramsOnly: true

主要なチェック

diagnostic(バグ検出)

チェック 検出内容
appendAssign append の結果を別の変数に代入
badCond 常に true/false になる条件式
nilValReturn nil チェック後に nil を返す
sloppyReassign 不要な再代入
weakCond 弱い条件(意図しない部分一致など)

style(コードスタイル)

チェック 検出内容
ifElseChain switch に置き換え可能な if-else チェーン
singleCaseSwitch case が 1 つだけの switch
typeAssertChain switch に置き換え可能な型アサーションチェーン
unslice 不要なスライス操作 s[:]
paramTypeCombine 同じ型のパラメータをまとめられる

performance(パフォーマンス)

チェック 検出内容
rangeValCopy range ループで大きな構造体のコピー
rangeExprCopy range 式の不要なコピー
hugeParam 大きな値のパラメータ渡し
indexAlloc 不要なインデックスアロケーション

サンプル

検出例

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
// gocritic が警告するパターン

// 1. appendAssign: append 結果を別変数に代入
func example1(a []int) {
b := append(a, 1) // appendAssign: append result not assigned to the same slice
_ = b
}

// 2. singleCaseSwitch: case が 1 つだけ
func example2(x int) {
switch x { // singleCaseSwitch: should rewrite switch statement to if statement
case 1:
doSomething()
}
}

// 3. unslice: 不要なスライス操作
func example3(s []int) []int {
return s[:] // unslice: could simplify s[:] to s
}

// 4. paramTypeCombine: 同型パラメータをまとめられる
func example4(a string, b string) { // paramTypeCombine: func(a, b string)
fmt.Println(a, b)
}

修正例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func example1(a []int) {
a = append(a, 1) // 同じ変数に代入
_ = a
}

func example2(x int) {
if x == 1 {
doSomething()
}
}

func example3(s []int) []int {
return s
}

func example4(a, b string) {
fmt.Println(a, b)
}

注意点

  • デフォルトで diagnosticstyleperformance タグが有効。experimentalopinionated は明示的に有効化が必要
  • enabled-tagsdisabled-checks を組み合わせて、タグ単位で有効化しつつ特定チェックを除外するのが一般的
  • rangeValCopyhugeParamsizeThreshold で閾値を調整できる。プロジェクトの構造体サイズに合わせて設定する
  • 約 100 個のチェックがあるため、有効なチェックの確認は GL_DEBUG=gocritic golangci-lint run で確認できる

参考リンク