wrap_error

package
v0.0.0-...-928c1b2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 29, 2021 License: MIT Imports: 10 Imported by: 0

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL