package sa5001 import ( "fmt" "go/ast" "go/types" "honnef.co/go/tools/analysis/code" "honnef.co/go/tools/analysis/lint" "honnef.co/go/tools/analysis/report" "golang.org/x/tools/go/analysis" "golang.org/x/tools/go/analysis/passes/inspect" ) var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ Analyzer: &analysis.Analyzer{ Name: "SA5001", Run: run, Requires: []*analysis.Analyzer{inspect.Analyzer}, }, Doc: &lint.RawDocumentation{ Title: `Deferring \'Close\' before checking for a possible error`, Since: "2017.1", Severity: lint.SeverityWarning, MergeIf: lint.MergeIfAny, }, }) var Analyzer = SCAnalyzer.Analyzer func run(pass *analysis.Pass) (interface{}, error) { fn := func(node ast.Node) { block := node.(*ast.BlockStmt) if len(block.List) < 2 { return } for i, stmt := range block.List { if i == len(block.List)-1 { break } assign, ok := stmt.(*ast.AssignStmt) if !ok { continue } if len(assign.Rhs) != 1 { continue } if len(assign.Lhs) < 2 { continue } if lhs, ok := assign.Lhs[len(assign.Lhs)-1].(*ast.Ident); ok && lhs.Name == "_" { continue } call, ok := assign.Rhs[0].(*ast.CallExpr) if !ok { continue } sig, ok := pass.TypesInfo.TypeOf(call.Fun).(*types.Signature) if !ok { continue } if sig.Results().Len() < 2 { continue } last := sig.Results().At(sig.Results().Len() - 1) // FIXME(dh): check that it's error from universe, not // another type of the same name if last.Type().String() != "error" { continue } lhs, ok := assign.Lhs[0].(*ast.Ident) if !ok { continue } def, ok := block.List[i+1].(*ast.DeferStmt) if !ok { continue } sel, ok := def.Call.Fun.(*ast.SelectorExpr) if !ok { continue } ident, ok := selectorX(sel).(*ast.Ident) if !ok { continue } if pass.TypesInfo.ObjectOf(ident) != pass.TypesInfo.ObjectOf(lhs) { continue } if sel.Sel.Name != "Close" { continue } report.Report(pass, def, fmt.Sprintf("should check error returned from %s() before deferring %s", report.Render(pass, call.Fun), report.Render(pass, def.Call))) } } code.Preorder(pass, fn, (*ast.BlockStmt)(nil)) return nil, nil } func selectorX(sel *ast.SelectorExpr) ast.Node { switch x := sel.X.(type) { case *ast.SelectorExpr: return selectorX(x) default: return x } }