Documentation ¶
Index ¶
Constants ¶
This section is empty.
Variables ¶
View Source
var WrapErrorAnalyzer = &analysis.Analyzer{ Name: "wrap_error", Doc: "check that new errors wrap context from existing errors in the call stack", Requires: []*analysis.Analyzer{inspect.Analyzer}, Run: func(pass *analysis.Pass) (interface{}, error) { inspector := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) nodeFilter := []ast.Node{ (*ast.IfStmt)(nil), (*ast.File)(nil), (*ast.GenDecl)(nil), } modified := false errorsNameConflict := false inspector.Nodes(nodeFilter, func(node ast.Node, push bool) bool { action := analyzers.Action(push) switch node := node.(type) { case *ast.File: switch action { case analyzers.Visit: for _, imp := range node.Imports { path := strings.Trim(imp.Path.Value, `"`) if strings.HasSuffix(path, "errors") && path != "github.com/pkg/errors" { errorsNameConflict = true } } modified = false return true case analyzers.Leave: if !modified { return true } importGenDecl, ok := findImportGenDecl(node) if !ok { panic("Expected to find an import generic declaration node") } pos := importGenDecl.Pos() end := importGenDecl.End() importSpec := &ast.ImportSpec{ Path: &ast.BasicLit{ Kind: token.STRING, Value: `"github.com/pkg/errors"`, }, } if errorsNameConflict { importSpec.Name = ast.NewIdent(errorsPkgAlias) } importGenDecl.Specs = append(importGenDecl.Specs, importSpec) pass.Report(analysis.Diagnostic{ Pos: pos, Message: `adding "github.com/pkg/errors" import`, SuggestedFixes: []analysis.SuggestedFix{ { Message: `adding "github.com/pkg/errors" import`, TextEdits: []analysis.TextEdit{ { Pos: pos, End: end, NewText: []byte(render(importGenDecl, pass.Fset)), }, }, }, }, }) } case *ast.IfStmt: switch action { case analyzers.Visit: if !isErrNeqNull(node) { return true } for _, stmt := range node.Body.List { returnStmt, ok := stmt.(*ast.ReturnStmt) if !ok { continue } if len(returnStmt.Results) == 0 { continue } lastResult := returnStmt.Results[len(returnStmt.Results)-1] resultValue, ok := lastIdent(lastResult) if !ok { continue } if !strings.HasPrefix(resultValue.Name, "Err") { continue } suggested := &ast.ReturnStmt{ Return: returnStmt.Return, Results: make([]ast.Expr, len(returnStmt.Results)), } copy(suggested.Results, returnStmt.Results) errorsSelector := "errors" if errorsNameConflict { errorsSelector = errorsPkgAlias } suggested.Results[len(suggested.Results)-1] = &ast.CallExpr{ Fun: ast.NewIdent(errorsSelector + ".Wrap"), Args: []ast.Expr{ lastResult, &ast.SelectorExpr{ X: ast.NewIdent("err"), Sel: ast.NewIdent("Error()"), }, }, } old := render(returnStmt, pass.Fset) new := render(suggested, pass.Fset) pass.Report(analysis.Diagnostic{ Pos: returnStmt.Pos(), Message: fmt.Sprintf("unwrapped error found '%s'", old), SuggestedFixes: []analysis.SuggestedFix{ { Message: fmt.Sprintf("should replace '%s' with '%s'", old, new), TextEdits: []analysis.TextEdit{ { Pos: returnStmt.Pos(), End: returnStmt.End(), NewText: []byte(new), }, }, }, }, }) modified = true } return true } } return true }) return nil, nil }, }
Functions ¶
This section is empty.
Types ¶
This section is empty.
Click to show internal directories.
Click to hide internal directories.