Skip to content
/ zagane Public

zagane is a static analysis tool which can find bugs in spanner's code

License

Notifications You must be signed in to change notification settings

gcpug/zagane

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

85 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

zagane

CircleCI GoDoc

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

  • unstopiter: it finds iterators which did not stop.
  • unclosetx: it finds transactions which does not close
  • wraperr: it finds (*spanner.Client).ReadWriteTransaction calls which returns wrapped errors

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.