-
Notifications
You must be signed in to change notification settings - Fork 3.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Unfortunately `returncheck` is not implemented as an `Analyzer` so it can't be integrated with `nogo` directly. Instead I've copied/adapted the existing code from https://github.com/cockroachdb/returncheck. The vendored `returncheck` can be deleted from tree when we no longer need to support `make lint`. Closes #73391. Release note: None
- Loading branch information
1 parent
e251e61
commit 64cb8f2
Showing
5 changed files
with
132 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
load("@io_bazel_rules_go//go:def.bzl", "go_library") | ||
|
||
go_library( | ||
name = "returncheck", | ||
srcs = ["returncheck.go"], | ||
importpath = "github.com/cockroachdb/cockroach/pkg/testutils/lint/passes/returncheck", | ||
visibility = ["//visibility:public"], | ||
deps = [ | ||
"@org_golang_x_tools//go/analysis", | ||
"@org_golang_x_tools//go/analysis/passes/inspect", | ||
"@org_golang_x_tools//go/ast/inspector", | ||
], | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
// Copyright 2022 The Cockroach Authors. | ||
// | ||
// Use of this software is governed by the Business Source License | ||
// included in the file licenses/BSL.txt. | ||
// | ||
// As of the Change Date specified in that file, in accordance with | ||
// the Business Source License, use of this software will be governed | ||
// by the Apache License, Version 2.0, included in the file | ||
// licenses/APL.txt. | ||
|
||
// Package returncheck defines an Analyzer that detects unused or | ||
// discarded roachpb.Error objects. | ||
package returncheck | ||
|
||
import ( | ||
"go/ast" | ||
"go/types" | ||
|
||
"golang.org/x/tools/go/analysis" | ||
"golang.org/x/tools/go/analysis/passes/inspect" | ||
"golang.org/x/tools/go/ast/inspector" | ||
) | ||
|
||
// Analyzer is an analysis.Analyzer that checks for unused or discarded | ||
// roachpb.Error objects from function calls. | ||
var Analyzer = &analysis.Analyzer{ | ||
Name: "returncheck", | ||
Doc: "`returncheck` : `roachpb.Error` :: `errcheck` : (stdlib)`error`", | ||
Requires: []*analysis.Analyzer{inspect.Analyzer}, | ||
Run: runAnalyzer, | ||
} | ||
|
||
func runAnalyzer(pass *analysis.Pass) (interface{}, error) { | ||
inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) | ||
inspect.Preorder([]ast.Node{ | ||
(*ast.AssignStmt)(nil), | ||
(*ast.DeferStmt)(nil), | ||
(*ast.ExprStmt)(nil), | ||
(*ast.GoStmt)(nil), | ||
}, func(n ast.Node) { | ||
switch stmt := n.(type) { | ||
case *ast.AssignStmt: | ||
// Find "_" in the left-hand side of the assigments and check if the corressponding | ||
// right-hand side expression is a call that returns the target type. | ||
for i := 0; i < len(stmt.Lhs); i++ { | ||
if id, ok := stmt.Lhs[i].(*ast.Ident); ok && id.Name == "_" { | ||
var rhs ast.Expr | ||
if len(stmt.Rhs) == 1 { | ||
// ..., stmt.Lhs[i], ... := stmt.Rhs[0] | ||
rhs = stmt.Rhs[0] | ||
} else { | ||
// ..., stmt.Lhs[i], ... := ..., stmt.Rhs[i], ... | ||
rhs = stmt.Rhs[i] | ||
} | ||
if call, ok := rhs.(*ast.CallExpr); ok { | ||
recordUnchecked(pass, call, i) | ||
} | ||
} | ||
} | ||
case *ast.ExprStmt: | ||
if call, ok := stmt.X.(*ast.CallExpr); ok { | ||
recordUnchecked(pass, call, -1) | ||
} | ||
case *ast.GoStmt: | ||
recordUnchecked(pass, stmt.Call, -1) | ||
case *ast.DeferStmt: | ||
recordUnchecked(pass, stmt.Call, -1) | ||
} | ||
}) | ||
return nil, nil | ||
} | ||
|
||
// recordUnchecked records an error if a given calls has an unchecked | ||
// return. If pos is not a negative value and the call returns a | ||
// tuple, check if the return value at the specified position is of type | ||
// roachpb.Error. | ||
func recordUnchecked(pass *analysis.Pass, call *ast.CallExpr, pos int) { | ||
isTarget := false | ||
switch t := pass.TypesInfo.Types[call].Type.(type) { | ||
case *types.Named: | ||
isTarget = isTargetType(t) | ||
case *types.Pointer: | ||
isTarget = isTargetType(t.Elem()) | ||
case *types.Tuple: | ||
for i := 0; i < t.Len(); i++ { | ||
if pos >= 0 && i != pos { | ||
continue | ||
} | ||
switch et := t.At(i).Type().(type) { | ||
case *types.Named: | ||
isTarget = isTargetType(et) | ||
case *types.Pointer: | ||
isTarget = isTargetType(et.Elem()) | ||
} | ||
} | ||
} | ||
|
||
if isTarget { | ||
pass.Reportf(call.Pos(), "unchecked roachpb.Error value") | ||
} | ||
} | ||
|
||
func isTargetType(t types.Type) bool { | ||
return t.String() == "github.com/cockroachdb/cockroach/pkg/roachpb.Error" | ||
} |