gcpug / zagane

zagane is a static analysis tool which can find bugs in spanner's code
MIT License
89 stars 15 forks source link
golang linter spanner staticanalysis

zagane

CircleCI GoDoc

zagane is a static analysis tool which can find bugs in spanner's code. zagane consists of several analyzers.

Install

You can get zagane by go get command.

$ go get -u github.com/gcpug/zagane

How to use

zagane run with go vet as below when Go is 1.12 and higher.

$ go vet -vettool=$(which zagane) github.com/gcpug/spshovel/...
# github.com/gcpug/spshovel/spanner
spanner/spanner_service.go:29:29: iterator must be stop

When Go is lower than 1.12, just run zagane command with the package name (import path). But it cannot accept some options such as --tags.

$ zagane github.com/gcpug/spshovel/...
~/go/src/github.com/gcpug/spshovel/spanner/spanner_service.go:29:29: iterator must be stop

Analyzers

unstopiter

unstopiter finds spanner.RowIterator which is not calling Stop method or Do method such as below code.

iter := client.Single().Query(ctx, stmt)
for {
    row, err := iter.Next()
    // ...
}

This code must be fixed as below.

iter := client.Single().Query(ctx, stmt)
defer iter.Stop()
for {
    row, err := iter.Next()
    // ...
}

unclosetx

unclosetx finds spanner.ReadOnlyTransaction which is not calling Close method such as below code.

tx := client.ReadOnlyTransaction()
// ...

This code must be fixed as below.

tx := client.ReadOnlyTransaction()
defer tx.Close()
// ...

When a transaction is created by (*spanner.Client).Single, unclosetx ignore it.

wraperr

wraperr finds ReadWriteTransaction calls which returns wrapped errors such as the below code.

func f(ctx context.Context, client *spanner.Client) {
    client.ReadWriteTransaction(ctx, func(ctx context.Context, txn *spanner.ReadWriteTransaction) error {
        stmt := spanner.Statement{SQL: `SELECT 1`}
        _, err := client.Single().Query(ctx, stmt).Next()
        if err != nil {
            return errors.Wrap(err, "wrapped") // want "must not be wrapped"
        }
        return nil
    })
}

This code must be fixed as below.

func f(ctx context.Context, client *spanner.Client) {
    client.ReadWriteTransaction(ctx, func(ctx context.Context, txn *spanner.ReadWriteTransaction) error {
        stmt := spanner.Statement{SQL: `SELECT 1`}
        _, err := client.Single().Query(ctx, stmt).Next()
        if err != nil {
            return err
        }
        return nil
    })
}

Ignore Checks

Analyzers ignore nodes which are annotated by staticcheck's style comments as belows. A ignore comment includes analyzer names and reason of ignoring checking. If you specify zagane as analyzer name, all analyzers ignore corresponding code.

//lint:ignore zagane reason
var n int

//lint:ignore unstopiter reason
_, _ = client.Single().Query(ctx, stmt).Next()

Analyze with golang.org/x/tools/go/analysis

You can get analyzers of zagane from zagane.Analyzers. And you can use them with unitchecker.

Why name is "zagane"?

"zagane" (座金) means "washer" in Japanese. A washer works between a spanner and other parts. zagane also works between Cloud Spanner and your applications.