Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions passes/wraperr/testdata/src/a/a.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"

"cloud.google.com/go/spanner"
"google.golang.org/grpc/status"
)

type wrapErr struct {
Expand Down Expand Up @@ -59,3 +60,34 @@ func f4(ctx context.Context, client *spanner.Client) {
return nil
})
}

type grpcStatusErr struct {
error
}

// see: https://github.com/googleapis/google-cloud-go/issues/1223
func (*grpcStatusErr) GRPCStatus() *status.Status {
return nil
}

func f5(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 &grpcStatusErr{err} // OK
}
return nil
})
}

func f6(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 &spanner.Error{} // OK
}
return nil
})
}
5 changes: 4 additions & 1 deletion passes/wraperr/testdata/src/a/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,7 @@ module a

go 1.12

require cloud.google.com/go v0.37.1
require (
cloud.google.com/go v0.37.1
google.golang.org/grpc v1.19.0
)
4 changes: 2 additions & 2 deletions passes/wraperr/testdata/src/a/vendor/modules.txt
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,13 @@ google.golang.org/appengine/internal/urlfetch
# google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19
google.golang.org/genproto/googleapis/rpc/errdetails
google.golang.org/genproto/googleapis/spanner/v1
google.golang.org/genproto/googleapis/api/annotations
google.golang.org/genproto/googleapis/rpc/status
google.golang.org/genproto/googleapis/api/annotations
# google.golang.org/grpc v1.19.0
google.golang.org/grpc/status
google.golang.org/grpc
google.golang.org/grpc/codes
google.golang.org/grpc/metadata
google.golang.org/grpc/status
google.golang.org/grpc/credentials
google.golang.org/grpc/credentials/oauth
google.golang.org/grpc/balancer
Expand Down
58 changes: 52 additions & 6 deletions passes/wraperr/wraperr.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package wraperr
import (
"go/ast"
"go/token"
"go/types"

"github.com/gcpug/zagane/zaganeutils"
"github.com/gostaticanalysis/analysisutil"
Expand All @@ -16,7 +17,7 @@ import (
var Analyzer = &analysis.Analyzer{
Name: "wraperr",
Doc: Doc,
Run: run,
Run: new(runner).run,
Requires: []*analysis.Analyzer{
buildssa.Analyzer,
commentmap.Analyzer,
Expand All @@ -25,7 +26,14 @@ var Analyzer = &analysis.Analyzer{

const Doc = "wraperr finds ReadWriteTransaction calls which returns wrapped errors"

func run(pass *analysis.Pass) (interface{}, error) {
type runner struct {
spannerError types.Type
grpcStatusInterface *types.Interface
}

func (r *runner) run(pass *analysis.Pass) (interface{}, error) {
r.grpcStatusInterface = newGRPCStatusInterface(pass)
r.spannerError = zaganeutils.TypeOf(pass, "*Error")
cmaps := pass.ResultOf[commentmap.Analyzer].(comment.Maps)
funcs := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).SrcFuncs

Expand All @@ -50,7 +58,7 @@ func run(pass *analysis.Pass) (interface{}, error) {
continue
}

if pos := wrapped(instr); pos != token.NoPos {
if pos := r.wrapped(instr); pos != token.NoPos {
if !cmaps.IgnorePos(pos, "zagane") &&
!cmaps.IgnorePos(pos, "wraperr") {
pass.Reportf(pos, "must not be wrapped")
Expand All @@ -63,7 +71,7 @@ func run(pass *analysis.Pass) (interface{}, error) {
return nil, nil
}

func wrapped(instr ssa.Instruction) token.Pos {
func (r *runner) wrapped(instr ssa.Instruction) token.Pos {
call, ok := instr.(ssa.CallInstruction)
if !ok {
return token.NoPos
Expand All @@ -80,13 +88,13 @@ func wrapped(instr ssa.Instruction) token.Pos {

switch fnc := common.Args[2].(type) {
case *ssa.MakeClosure:
return returnedWrappedErr(fnc.Fn)
return r.returnedWrappedErr(fnc.Fn)
}

return token.NoPos
}

func returnedWrappedErr(v ssa.Value) token.Pos {
func (r *runner) returnedWrappedErr(v ssa.Value) token.Pos {
for _, ret := range analysisutil.Returns(v) {
if len(ret.Results) == 0 {
continue
Expand All @@ -100,6 +108,10 @@ func returnedWrappedErr(v ssa.Value) token.Pos {
continue
}

if r.implementsGRPCStatus(v) || r.isSpannerError(v) {
continue
}

if !zaganeutils.FromSpanner(v) {
switch v := v.(type) {
case *ssa.MakeInterface:
Expand All @@ -111,3 +123,37 @@ func returnedWrappedErr(v ssa.Value) token.Pos {
}
return token.NoPos
}

func (r *runner) implementsGRPCStatus(v ssa.Value) bool {
if r.grpcStatusInterface == nil {
return false
}
switch v := v.(type) {
case *ssa.MakeInterface:
return types.Implements(v.X.Type(), r.grpcStatusInterface)
}
return types.Implements(v.Type(), r.grpcStatusInterface)
}

func (r *runner) isSpannerError(v ssa.Value) bool {
if r.spannerError == nil {
return false
}
switch v := v.(type) {
case *ssa.MakeInterface:
return types.Identical(v.X.Type(), r.spannerError)
}
return types.Identical(v.Type(), r.spannerError)
}

func newGRPCStatusInterface(pass *analysis.Pass) *types.Interface {
typStatus := analysisutil.TypeOf(pass, "google.golang.org/grpc/status", "*Status")
if typStatus == nil {
return nil
}

ret := types.NewTuple(types.NewParam(token.NoPos, pass.Pkg, "", typStatus))
sig := types.NewSignature(nil, types.NewTuple(), ret, false)
grpcStatusFunc := types.NewFunc(token.NoPos, pass.Pkg, "GRPCStatus", sig)
return types.NewInterfaceType([]*types.Func{grpcStatusFunc}, nil).Complete()
}