diff --git a/pkg/linters/uncheckedtypeassertion/testdata/src/uncheckedtypeassertion/uncheckedtypeassertion.go b/pkg/linters/uncheckedtypeassertion/testdata/src/uncheckedtypeassertion/uncheckedtypeassertion.go index ebff00dbe42..aa1757dd5d7 100644 --- a/pkg/linters/uncheckedtypeassertion/testdata/src/uncheckedtypeassertion/uncheckedtypeassertion.go +++ b/pkg/linters/uncheckedtypeassertion/testdata/src/uncheckedtypeassertion/uncheckedtypeassertion.go @@ -35,6 +35,54 @@ func GoodTwoValueBlankOk(v interface{}) string { return s } +// Good: two-value var declaration is safe. +func GoodTwoValueVarDecl(v interface{}) { + var s, ok = v.(string) + if ok { + fmt.Println(s) + } +} + +// Good: parenthesized two-value assignment is safe. +func GoodTwoValueParen(v interface{}) { + s, ok := (v.(string)) + if ok { + fmt.Println(s) + } +} + +// Good: parenthesized two-value re-assignment is safe. +func GoodTwoValueParenAssign(v interface{}) { + var s string + var ok bool + s, ok = (v.(string)) + if ok { + fmt.Println(s) + } +} + +// Good: parenthesized two-value var declaration is safe. +func GoodTwoValueVarDeclParen(v interface{}) { + var s, ok = (v.(string)) + if ok { + fmt.Println(s) + } +} + +// Good: double-parenthesized two-value assignment is safe. +func GoodTwoValueDoubleParen(v interface{}) { + s, ok := ((v.(string))) + if ok { + fmt.Println(s) + } +} + +// Bad: single-value var declaration may panic. +func BadSingleValueVarDecl(v interface{}) { + var s = v.(string) // want `type assertion x\.\(string\) is unchecked and may panic` + fmt.Println(s) +} + func SuppressedPreviousLine(v interface{}) string { //nolint:uncheckedtypeassertion return v.(string) diff --git a/pkg/linters/uncheckedtypeassertion/uncheckedtypeassertion.go b/pkg/linters/uncheckedtypeassertion/uncheckedtypeassertion.go index a795b9d4923..f7b4ec47677 100644 --- a/pkg/linters/uncheckedtypeassertion/uncheckedtypeassertion.go +++ b/pkg/linters/uncheckedtypeassertion/uncheckedtypeassertion.go @@ -74,10 +74,8 @@ func inspectTypeAssertExpr(pass *analysis.Pass, noLintLinesByFile map[string]map // Skip the safe two-value form: v, ok := x.(T) or v, ok = x.(T) if parents != nil { - if assign, ok := parents[typeAssert].(*ast.AssignStmt); ok { - if isSafeTwoValueAssertion(assign) { - return - } + if isSafeTwoValueAssertion(typeAssert, parents) { + return } } @@ -96,8 +94,24 @@ func inspectTypeAssertExpr(pass *analysis.Pass, noLintLinesByFile map[string]map ) } -func isSafeTwoValueAssertion(assign *ast.AssignStmt) bool { - return len(assign.Lhs) == 2 && len(assign.Rhs) == 1 +func isSafeTwoValueAssertion(typeAssert *ast.TypeAssertExpr, parents map[ast.Node]ast.Node) bool { + parent := parents[typeAssert] + for parent != nil { + paren, ok := parent.(*ast.ParenExpr) + if !ok { + break + } + parent = parents[paren] + } + + switch p := parent.(type) { + case *ast.AssignStmt: + return len(p.Lhs) == 2 && len(p.Rhs) == 1 + case *ast.ValueSpec: + return len(p.Names) == 2 && len(p.Values) == 1 + default: + return false + } } // buildParentMap constructs a map from each AST node to its direct parent node.