dnscrypt-proxy/vendor/github.com/polyfloyd/go-errorlint/errorlint/allowed.go

138 lines
4.1 KiB
Go

package errorlint
import (
"fmt"
"go/ast"
"go/types"
)
var allowedErrors = []struct {
err string
fun string
}{
// pkg/archive/tar
{err: "io.EOF", fun: "(*tar.Reader).Next"},
{err: "io.EOF", fun: "(*tar.Reader).Read"},
// pkg/bufio
{err: "io.EOF", fun: "(*bufio.Reader).Read"},
{err: "io.EOF", fun: "(*bufio.Reader).ReadByte"},
{err: "io.EOF", fun: "(*bufio.Reader).ReadBytes"},
{err: "io.EOF", fun: "(*bufio.Reader).ReadSlice"},
{err: "io.EOF", fun: "(*bufio.Reader).ReadString"},
{err: "io.EOF", fun: "(*bufio.Scanner).Scan"},
// pkg/bytes
{err: "io.EOF", fun: "(*bytes.Buffer).Read"},
{err: "io.EOF", fun: "(*bytes.Buffer).ReadByte"},
{err: "io.EOF", fun: "(*bytes.Buffer).ReadBytes"},
{err: "io.EOF", fun: "(*bytes.Buffer).ReadRune"},
{err: "io.EOF", fun: "(*bytes.Buffer).ReadString"},
// pkg/database/sql
{err: "sql.ErrNoRows", fun: "(*database/sql.Row).Scan"},
// pkg/io
{err: "io.EOF", fun: "(io.Reader).Read"},
{err: "io.ErrClosedPipe", fun: "(*io.PipeWriter).Write"},
{err: "io.ErrShortBuffer", fun: "io.ReadAtLeast"},
{err: "io.ErrUnexpectedEOF", fun: "io.ReadAtLeast"},
{err: "io.ErrUnexpectedEOF", fun: "io.ReadFull"},
// pkg/os
{err: "io.EOF", fun: "(*os.File).Read"},
{err: "io.EOF", fun: "(*os.File).ReadAt"},
{err: "io.EOF", fun: "(*os.File).ReadDir"},
{err: "io.EOF", fun: "(*os.File).Readdir"},
{err: "io.EOF", fun: "(*os.File).Readdirnames"},
// pkg/strings
{err: "io.EOF", fun: "(*strings.Reader).Read"},
{err: "io.EOF", fun: "(*strings.Reader).ReadAt"},
{err: "io.EOF", fun: "(*strings.Reader).ReadByte"},
{err: "io.EOF", fun: "(*strings.Reader).ReadRune"},
}
func isAllowedErrorComparison(info types.Info, binExpr *ast.BinaryExpr) bool {
var errName string // `<package>.<name>`, e.g. `io.EOF`
var callExpr *ast.CallExpr
// Figure out which half of the expression is the returned error and which
// half is the presumed error declaration.
for _, expr := range []ast.Expr{binExpr.X, binExpr.Y} {
switch t := expr.(type) {
case *ast.SelectorExpr:
// A selector which we assume refers to a staticaly declared error
// in a package.
errName = selectorToString(t)
case *ast.Ident:
// Identifier, most likely to be the `err` variable or whatever
// produces it.
callExpr = assigningCallExpr(info, t)
case *ast.CallExpr:
callExpr = t
}
}
// Unimplemented or not sure, disallow the expression.
if errName == "" || callExpr == nil {
return false
}
// Find the expression that last assigned the subject identifier.
functionSelector, ok := callExpr.Fun.(*ast.SelectorExpr)
if !ok {
// If the function is not a selector it is not an Std function that is
// allowed.
return false
}
var functionName string
if sel, ok := info.Selections[functionSelector]; ok {
functionName = fmt.Sprintf("(%s).%s", sel.Recv(), sel.Obj().Name())
} else {
// If there is no selection, assume it is a package.
functionName = selectorToString(callExpr.Fun.(*ast.SelectorExpr))
}
for _, w := range allowedErrors {
if w.fun == functionName && w.err == errName {
return true
}
}
return false
}
func assigningCallExpr(info types.Info, subject *ast.Ident) *ast.CallExpr {
if subject.Obj == nil {
return nil
}
switch declT := subject.Obj.Decl.(type) {
case *ast.AssignStmt:
// The identifier is LHS of an assignment.
assignment := declT
assigningExpr := assignment.Rhs[0]
// If the assignment is comprised of multiple expressions, find out
// which LHS expression we should use by finding its index in the LHS.
if len(assignment.Rhs) > 1 {
for i, lhs := range assignment.Lhs {
if subject.Name == lhs.(*ast.Ident).Name {
assigningExpr = assignment.Rhs[i]
break
}
}
}
switch assignT := assigningExpr.(type) {
case *ast.CallExpr:
// Found the function call.
return assignT
case *ast.Ident:
// The subject was the result of assigning from another identifier.
return assigningCallExpr(info, assignT)
}
}
return nil
}
func selectorToString(selExpr *ast.SelectorExpr) string {
if ident, ok := selExpr.X.(*ast.Ident); ok {
return ident.Name + "." + selExpr.Sel.Name
}
return ""
}