Skip to content

Commit 34daa66

Browse files
authored
Merge pull request #10 from maratori/shadow-var
respect variable shadowing and type aliasing
2 parents bc2e315 + 8435743 commit 34daa66

File tree

2 files changed

+72
-16
lines changed

2 files changed

+72
-16
lines changed

analyzer/analyzer.go

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ func flags() flag.FlagSet {
2828

2929
func run(pass *analysis.Pass) (interface{}, error) {
3030
allowErrorInDefer := pass.Analyzer.Flags.Lookup(FlagAllowErrorInDefer).Value.String() == "true"
31+
errorType := types.Universe.Lookup("error").Type()
3132

3233
inspector := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
3334

@@ -70,12 +71,10 @@ func run(pass *analysis.Pass) (interface{}, error) {
7071
continue
7172
}
7273

73-
if allowErrorInDefer {
74-
if ident, ok := p.Type.(*ast.Ident); ok {
75-
if ident.Name == "error" && findDeferWithErrorAssignment(funcBody, n.Name) {
76-
continue
77-
}
78-
}
74+
if allowErrorInDefer &&
75+
types.Identical(pass.TypesInfo.TypeOf(p.Type), errorType) &&
76+
findDeferWithVariableAssignment(funcBody, pass.TypesInfo, pass.TypesInfo.ObjectOf(n)) {
77+
continue
7978
}
8079

8180
pass.Reportf(node.Pos(), "named return %q with type %q found", n.Name, types.ExprString(p.Type))
@@ -86,7 +85,7 @@ func run(pass *analysis.Pass) (interface{}, error) {
8685
return nil, nil
8786
}
8887

89-
func findDeferWithErrorAssignment(body *ast.BlockStmt, name string) bool {
88+
func findDeferWithVariableAssignment(body *ast.BlockStmt, info *types.Info, variable types.Object) bool {
9089
found := false
9190

9291
ast.Inspect(body, func(node ast.Node) bool {
@@ -96,7 +95,7 @@ func findDeferWithErrorAssignment(body *ast.BlockStmt, name string) bool {
9695

9796
if d, ok := node.(*ast.DeferStmt); ok {
9897
if fn, ok2 := d.Call.Fun.(*ast.FuncLit); ok2 {
99-
if findErrorAssignment(fn.Body, name) {
98+
if findVariableAssignment(fn.Body, info, variable) {
10099
found = true
101100
return false
102101
}
@@ -109,7 +108,7 @@ func findDeferWithErrorAssignment(body *ast.BlockStmt, name string) bool {
109108
return found
110109
}
111110

112-
func findErrorAssignment(body *ast.BlockStmt, name string) bool {
111+
func findVariableAssignment(body *ast.BlockStmt, info *types.Info, variable types.Object) bool {
113112
found := false
114113

115114
ast.Inspect(body, func(node ast.Node) bool {
@@ -120,7 +119,7 @@ func findErrorAssignment(body *ast.BlockStmt, name string) bool {
120119
if a, ok := node.(*ast.AssignStmt); ok {
121120
for _, lh := range a.Lhs {
122121
if i, ok2 := lh.(*ast.Ident); ok2 {
123-
if i.Name == name {
122+
if info.ObjectOf(i) == variable {
124123
found = true
125124
return false
126125
}

testdata/src/allow-error-in-defer/allow_error_in_defer.go

Lines changed: 63 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package main
22

3+
import "errors"
4+
35
func simple() (err error) {
46
defer func() {
57
err = nil
@@ -43,32 +45,87 @@ func errorIsNoAssigned() (err error) { // want `named return "err" with type "er
4345
return
4446
}
4547

46-
func shadowVariable() (err error) {
48+
func shadowVariable() (err error) { // want `named return "err" with type "error" found`
4749
defer func() {
48-
err := 123 // linter doesn't understand that this is different variable (even if different type) (yet?)
50+
err := errors.New("xxx")
4951
_ = err
5052
}()
5153
return
5254
}
5355

54-
func shadowVariable2() (err error) {
56+
func shadowVariableButAssign() (err error) {
57+
defer func() {
58+
{
59+
err := errors.New("xxx")
60+
_ = err
61+
}
62+
err = nil
63+
}()
64+
return
65+
}
66+
67+
func shadowVariable2() (err error) { // want `named return "err" with type "error" found`
5568
defer func() {
56-
a, err := doSomething() // linter doesn't understand that this is different variable (yet?)
69+
a, err := doSomething()
5770
_ = a
5871
_ = err
5972
}()
6073
return
6174
}
6275

63-
type myError = error // linter doesn't understand that this is the same type (yet?)
76+
type errorAlias = error
77+
78+
func errorAliasIsTheSame() (err errorAlias) {
79+
defer func() {
80+
err = nil
81+
}()
82+
return
83+
}
84+
85+
type myError error // linter doesn't check underlying type (yet?)
86+
87+
func customTypeWithErrorUnderline() (err myError) { // want `named return "err" with type "myError" found`
88+
defer func() {
89+
err = nil
90+
}()
91+
return
92+
}
93+
94+
type myError2 interface{ error } // linter doesn't check interfaces
6495

65-
func customType() (err myError) { // want `named return "err" with type "myError" found`
96+
func customTypeWithTheSameInterface() (err myError2) { // want `named return "err" with type "myError2" found`
6697
defer func() {
6798
err = nil
6899
}()
69100
return
70101
}
71102

103+
var _ error = myError3{}
104+
105+
type myError3 struct{} // linter doesn't check interfaces
106+
107+
func (m myError3) Error() string { return "" }
108+
109+
func customTypeImplementingErrorInterface() (err myError3) { // want `named return "err" with type "myError3" found`
110+
defer func() {
111+
err = struct{}{}
112+
}()
113+
return
114+
}
115+
116+
func shadowErrorType() {
117+
type error interface { // linter understands that this is not built-in error, even if it has the same name
118+
Error() string
119+
}
120+
do := func() (err error) { // want `named return "err" with type "error" found`
121+
defer func() {
122+
err = nil
123+
}()
124+
return
125+
}
126+
do()
127+
}
128+
72129
func notTheLast() (err error, _ int) {
73130
defer func() {
74131
err = nil

0 commit comments

Comments
 (0)