📑

IntelliJ IDEA/GoLand: Code Inspections in Go

2023/11/14に公開

JetBrains が提供する IDE では Code Inspections という機能でコードの検査を行うことができます。GoLand (または Go Plugin) でどのような検査ができるのかをざっとまとめまてみました。

基本的には以下の翻訳です。表現は簡潔に意図を伝えることを優先して書き換えており厳密ではない場合があるのでご注意ください。

https://www.jetbrains.com/help/go/code-inspections-in-go.html

概要

検査項目は以下の 6 つにカテゴライズされており、全部で 77 ありました。

それぞれのチェック項目は設定から有効/無効や重要度 (Error/Warning 等) を変更したりといったカスタマイズもできます。(記載した警告レベルはデフォルトの設定です。)

  • Probable bugs : 移植性に関するバグ (29)
  • Control flow issues : 制御構造に関する問題 (4)
  • Code style issues : コーディングスタイル違反 (14)
  • General : 一般 (9)
  • Security : セキュリティの問題 (1)
  • Declaration redundancy : 冗長 (20)

Probable bugs カテゴリー

'FailNow' in a non-test goroutine (Warning)

テストのゴルーチン内で t.FailNow() している。この場合ゴルーチンが終了するだけでテストは終了しない。

func TestFoo(t *testing.T) {
  go func() {
    t.Fatal("oops")
  }()
}

'Unmarshal' is called with the incorrect argument (Warning)

json.Unmarshal() や類似の関数にポインターやインターフェースを渡していない。

var animals []Animal
err := json.Unmarshal(jsonData, animals) // &animals が正しい

'context.CancelFunc' is not called (Warning)

context.WithCancel() 等から返ってきた context.CancelFunc を呼んでいない。

func _(ctx context.Context, cancel func()) {
  var ctx2 context.Context
  ctx2, cancel = context.WithCancel(ctx)
  _ = ctx2
}

Defer/go statement calls 'recover' or 'panic' directly (Weak Warning)

panic()recover() を直接呼び出している defergo 式がある。誤用の可能性が高い。

defer func() {
  if r := recover(); r != nil {
    fmt.Println("Recovered from: ", r)
  }
}()

Division by zero (Warning)

ゼロ除算で panic する。

s := 3 / 0

Exceeded shift expression (Warning)

ビットシフト演算が桁数を超える。

func shift(i int8) {
  fmt.Println(i << 8) // 常に 0 になる
}

Imported package name as a name identifier (Warning)

変数、定数、関数の名前が import したパッケージ名と衝突する。

Impossible interface type assertion (Warning)

不可能なインターフェースへの型アサーション。

var v interface {
  Read()
}
_ = v.(io.Reader) // io.Reader との互換なし

Incorrect 'strings.Replace' count argument (Warning)

strings.Replace() の count に 0 を指定しているので置換されない。

a := strings.Replace("SSS", "S", "H", 0) // 変化なし

Incorrect usage of 'fmt.Printf' and 'fmt.Println' functions (Weak Warning)

fmt.Printf() や類似の関数の使用方法が間違っている。

fmt.Printf("id: %s", 42) // %d が正しい

Incorrect usage of the 'errors.As' function (Warning)

errors.As() 関数の第 2 引数が正しくない。

var pathError *fs.PathError
if errors.As(err, pathError) { // &pathError が正しい
}

Incorrect usage of the 'sync/atomic' package (Warning)

アトミック関数を適切に使用していない。

x := uint64(1)
x = atomic.AddUint64(&x, 1) // アトミックでない

Integer to string type conversion (Warning)

string(x) で数値を文字列に変換しようとしているが、この場合 UTF-8 コードポイントの文字に変換される(非推奨)。

a := 1
s := string(a)

Invalid conversions of 'uintptr' to 'unsafe.Pointer' (Warning)

uintptrunsafe.Pointer() の変換が正しくない。

Irregular usage of 'iota' (Weak Warning)

const 宣言内に不要な iota() がある。

const (
  a = iota  // 0
  b         // 1
  c = iota  // 2
)

Leading whitespace in directive comment (Warning)

Go ディレクティブの前に不要な空白がある。

//  go:embed file.txt
var File string

Locks mistakenly passed by value (Warning)

コピーされた Mutex をロックしている。(ロックできてない)

type SafeInt struct {
  m sync.Mutex
  i int
}

func (s SafeInt) Inc() {
  s.m.Lock() // コピーされた Mutex をロックしている
  s.i++
  s.m.Unlock()
}

Loop variables captured by the func literal (Warning)

defer/go 文の中でループ変数を参照している。ループ変数はコピーされないため、意図しない値になる可能性がある。
注意: defer/go 文がループの最後にある場合のみ検出される。

for _, v := range []string{"a", "b", "c"} {
  go func() {
    fmt.Println(v) // c c c になる可能性がある
  }()
}

Malformed build tag (Weak Warning)

build タグの場所が正しくない。

package main

// +build ignore

func main() {}

Malformed struct tag (Warning)

struct タグの書式が正しくない。

type Example struct {
  Field int `json:"field" xml:"demo"` // 正しい
}

Mixed value and pointer receivers (Weak Warning)

レシーバーの型にポインターと値の両方が混在している。

type S struct{
}

func (s *S) fun() {}
func (s S) fun2() {}

Nilness analyzer (Disabled)

nil が正しく使われていない。

Non-standard signature for well-known function names (Warning)

標準的な名前の関数だが一般的なシグネチャーと異なる。

type MyReader []byte

func (r MyReader) ReadByte(data []byte) (byte, error) {
}

Reserved word used as name (Warning)

ビルトイン関数や予約語と同じ名前のものを宣言している。

type byte struct{}
type string interface{}

Shadowing variable

外側のスコープの宣言と重複(シャドーイング)している。

for i := 0; i < len(nums); i++ {
    for i := 0; i < len(nums); i++ {
    }
  }

Unhandled error (Warning)

エラーを無視している。

os.Remove("non-existing") // 復帰値 error を無視している

Unused function or method call result (Warning)

関数やメソッドの戻り値を使用していない。

fmt.Errorf("error: %s", reason)

Control flow issues カテゴリー

'defer' in the loop (Weak Warning)

ループ内に defer 文がある。defer 文は関数が終了するまで実行されないため、スタックオーバーフローなどの問題が発生する可能性がある。

func main() {
  for {
    field, err := db.Query("SELECT 1")
    if err != nil {
      // ...
    }
    defer field.Close()
    // ...
  }
}

Assignment to a receiver (Weak Warning)

レシーバー内で構造体自信を変更しても呼び元には反映しない。

func (d demo) change() {
  d = demo{} // 呼び元には影響なし
}

sInfinite 'for' loop (Warning)

空のループがある。

for {
}

Unreachable code (Warning)

実行されない処理がある。

func _() int {
  print(1)
  return 2
  println() // ここには来ない
  return 0
}

Code style issues カテゴリー

Comment has no leading space (Weak Warning)

コメントの前にスペースがない。
※コードスタイルの「Add a leading space to comments」が有効な場合のみ検出される。

//こめんと

Comment of exported element starts with the incorrect name (Weak Warning)

コメントがエクスポート要素の名前で始まっていない。

// represents a request to run a command.
type Request struct {}

Convert string literals (Info)

ダブルクォート文字列と raw string は相互に変換できる。

var s = "Hello\n   \"World\""
After the quick fix is applied:

var s = `Hello
 "World"`

Error string should not be capitalized or end with punctuation (Weak Warning)

エラーメッセージが大文字で始まったり句読点で終わらないこと。

err := fmt.Errorf("Cannot read the file!")

Exported element should have a comment (Info)

エクスポートしている宣言にコメントがない。

Exported element should have its own declaration (Weak Warning)

エクスポートしている変数や定数をまとめて宣言している。

const C1, C3, C2, C44, C9, C11, C6 = 1, 2, 3, 1, 3, 2, 1

Name starts with a package name (Weak Warning)

エクスポートしている名前がパッケージ名と同じ名前で始まっている。

package myPackage

func MyPackageGetIP() {
}

Receiver has a generic name (Weak Warning)

典型的なオブジェクト指向言語で使われる this/self のような名前をレシーバーとして使用している。

func (self *MeterSnapshot) Rate5() float64 { return math.Float64frombits(self.rate5) }

Struct initialization without field names (Weak Warning)

フィールド名を指定せず構造体を初期化している。

_ = io.LimitedReader{nil, 10}

Type parameter is declared in lowercase (Info)

型パラメーターを小文字で定義している。

func PrintSlice[t any](s []t) {
    for _, v := range s{
        print(v)
    }
}

Unit-specific suffix for 'time.Duration' (Weak Warning)

time.Duration 型の定数や変数名に、単位を表す名前がついている。

var timeoutSeconds = 5 * time.Second

Unsorted imports (Weak Warning)

import 順がソートされていない。

import (
  "net"
  "errors"
  "fmt"
)

Usage of Snake_Case (Weak Warning)

名前がスネークケースになっている。

func get_external_IP() (string, error) {}

General カテゴリー

Deprecated element (Warning)

非推奨とマークされたものを使用している。

// Deprecated: Use io.SeekStart, io.SeekCurrent, and io.SeekEnd.
const (
  SEEK_SET int = 0 // seek relative to the origin of the file
  SEEK_CUR int = 1 // seek relative to the current offset
  SEEK_END int = 2 // seek relative to the end
)

Disabled GOPATH indexing (Weak Warning)

TODO

Fuzzing is supported starting with Go 1.18 (Warning)

fuzzing テストが書かれているが、Go 1.18 未満では動作しない。

func Div(a, b int) int {
  return a / b
}

func FuzzDiv(f *testing.F) {
  f.Fuzz(func(t *testing.T, a, b int) {
    Div(a, b) // reports runtime error: integer divide by zero
  })
}

Malformed test function name (Warning)

Test, Benchmark, Example 関数の名前になっていない。

func Testfoo(*testing.T) {} // the 'go' tool will not run this test

Missing trailing comma before a newline in a composite literal (Error)

末尾のカンマがない。

func f(f int) (
  int,
  bool   // missing a trailing comma
){
  println(1, 2  // missing a trailing comma
  )
}

Redundant parentheses (Weak Warning)

括弧が冗長。

func _(x (int), y ((string))) {
}
func _() {
  _ = (1 + 1)
  _ = (((1 + 1)))
  _ = (((1 + 1))) + (((2 + 2)))
}

Unexported return type of the exported function (Warning)

エクスポートしている関数の戻り値が非公開型。

type hidden struct{}

func Exported() hidden { // Exported function with the `hidden` unexported return type
 return hidden{}
}

Unnecessarily exported identifier (Disabled)

エクスポートしている識別子が外部から使用されていない。

Usage of 'interface{}' as a type (Info)

型として interface{} を使用しているが、Go 1.18 からは any が使える。

Usage of context.TODO() (Disabled)

?

Security カテゴリー

Vulnerable API usage (Warning)

脆弱な API を使用している。

Declaration redundancy カテゴリー

Bool condition (Warning)

無駄な判定式。常に結果が同じか、冗長。

func isNonZero(x, y int) bool {
  return x > 0 && x > 0
}

Empty declaration (Warning)

空の宣言。

func main() {
 const () // 不要
}

Empty slice declared using a literal (Weak Warning)

nil スライスでなく空スライスで初期化している。

s := []string{}

補足: Quick fix をおこなうと nil スライスで初期化するよう書き換えられます。意図的に使い分ける必要がある場合は注意が必要です。この点は「Go 言語 100 Tips」No.22 にも記載がありました。

Redundant blank argument in range (Warning)

range ループで使用しない変数がある。

for a, _ = range v {} // for a = range v {} でよい

Redundant comma (Weak Warning)

カンマは不要。

s := []int{1, 2,}

Redundant import alias (Weak Warning)

import のエイリアス指定が冗長。

import fmt "fmt"

Redundant second index in slices (Warning)

スライスの第二インデックスは指定不要。

var a []int
a = a[0:len(a)] // a[0:] でよい

Redundant semicolon (Weak Warning)

セミコロンが不要。

i := 1;

Redundant type conversion (Weak Warning)

型変換が不要。

var s = string("hello") // "hello" でよい

Redundant types in composite literals (Warning)

型が省略できる。

nums := [][]int{[]int{1}, []int{2}} // [][]int{{1},{2}} でよい

Self assignment (Weak Warning)

自分自身に代入している。

func importedVarSelfAssignment() {
  http.ErrNotSupported = http.ErrNotSupported
}

Type can be omitted (Weak Warning)

型は省略できる。

var s string = fmt.Sprintln("hi")

Unused constant (Warning)

定数が使用されていない。

func main() {
  const i = 100
}

Unused exported function (Warning)

エクスポートされた関数が使用されていない。

// Unused exported function
func ExportedUnusedFunc()  {
}

func main() {
  fmt.Println("Hello")
}

Unused exported type (Warning)

エクスポートされた型が使用されていない。

type User struct {}

func main() {}

Unused function (Warning)

エクスポートされた関数が使用されていない。

// Unused unexported function
func unExportedUnusedFunc()  {
}

func main() {
 fmt.Println("Hello")
}

Unused global variable (Warning)

変数が使用されていない。

func main() {
  a := 422
}

Unused parameter (Warning)

関数に使用されないパラメーターがある。

func printAll(
  i int,
  s string, // 未使用
) {
  fmt.Println(i)
}

Unused type (Warning)

使用されていない型がある。

Unused type parameter (Warning)

使用されていない型パラメーターがある。

func printAll[I int, S string]( // S は未使用
  i I,
  s string,
) {
  fmt.Println(i)
  fmt.Println(s)
}

Discussion