mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-12-13 03:56:39 +00:00
82fd65665f
bidichk checks for dangerous unicode character sequences (https://github.com/golangci/golangci-lint/pull/2330)
148 lines
3 KiB
Go
148 lines
3 KiB
Go
package analyzer
|
|
|
|
import (
|
|
"go/ast"
|
|
|
|
"golang.org/x/tools/go/analysis"
|
|
"golang.org/x/tools/go/analysis/passes/inspect"
|
|
"golang.org/x/tools/go/ast/inspector"
|
|
)
|
|
|
|
const (
|
|
name = "nilnil"
|
|
doc = "Checks that there is no simultaneous return of `nil` error and an invalid value."
|
|
|
|
reportMsg = "return both the `nil` error and invalid value: use a sentinel error instead"
|
|
)
|
|
|
|
// New returns new nilnil analyzer.
|
|
func New() *analysis.Analyzer {
|
|
n := newNilNil()
|
|
|
|
a := &analysis.Analyzer{
|
|
Name: name,
|
|
Doc: doc,
|
|
Run: n.run,
|
|
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
|
}
|
|
a.Flags.Var(&n.checkedTypes, "checked-types", "coma separated list")
|
|
|
|
return a
|
|
}
|
|
|
|
type nilNil struct {
|
|
checkedTypes checkedTypes
|
|
}
|
|
|
|
func newNilNil() *nilNil {
|
|
return &nilNil{
|
|
checkedTypes: newDefaultCheckedTypes(),
|
|
}
|
|
}
|
|
|
|
var (
|
|
types = []ast.Node{(*ast.TypeSpec)(nil)}
|
|
|
|
funcAndReturns = []ast.Node{
|
|
(*ast.FuncDecl)(nil),
|
|
(*ast.FuncLit)(nil),
|
|
(*ast.ReturnStmt)(nil),
|
|
}
|
|
)
|
|
|
|
type typeSpecByName map[string]*ast.TypeSpec
|
|
|
|
func (n *nilNil) run(pass *analysis.Pass) (interface{}, error) {
|
|
insp := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
|
|
|
|
typeSpecs := typeSpecByName{}
|
|
insp.Preorder(types, func(node ast.Node) {
|
|
t := node.(*ast.TypeSpec)
|
|
typeSpecs[t.Name.Name] = t
|
|
})
|
|
|
|
var fs funcTypeStack
|
|
insp.Nodes(funcAndReturns, func(node ast.Node, push bool) (proceed bool) {
|
|
switch v := node.(type) {
|
|
case *ast.FuncLit:
|
|
if push {
|
|
fs.Push(v.Type)
|
|
} else {
|
|
fs.Pop()
|
|
}
|
|
|
|
case *ast.FuncDecl:
|
|
if push {
|
|
fs.Push(v.Type)
|
|
} else {
|
|
fs.Pop()
|
|
}
|
|
|
|
case *ast.ReturnStmt:
|
|
ft := fs.Top() // Current function.
|
|
|
|
if !push || len(v.Results) != 2 || ft == nil || ft.Results == nil || len(ft.Results.List) != 2 {
|
|
return false
|
|
}
|
|
|
|
fRes1, fRes2 := ft.Results.List[0], ft.Results.List[1]
|
|
if !(n.isDangerNilField(fRes1, typeSpecs) && n.isErrorField(fRes2)) {
|
|
return
|
|
}
|
|
|
|
rRes1, rRes2 := v.Results[0], v.Results[1]
|
|
if isNil(rRes1) && isNil(rRes2) {
|
|
pass.Reportf(v.Pos(), reportMsg)
|
|
}
|
|
}
|
|
|
|
return true
|
|
})
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
func (n *nilNil) isDangerNilField(f *ast.Field, typeSpecs typeSpecByName) bool {
|
|
return n.isDangerNilType(f.Type, typeSpecs)
|
|
}
|
|
|
|
func (n *nilNil) isDangerNilType(t ast.Expr, typeSpecs typeSpecByName) bool {
|
|
switch v := t.(type) {
|
|
case *ast.StarExpr:
|
|
return n.checkedTypes.Contains(ptrType)
|
|
|
|
case *ast.FuncType:
|
|
return n.checkedTypes.Contains(funcType)
|
|
|
|
case *ast.InterfaceType:
|
|
return n.checkedTypes.Contains(ifaceType)
|
|
|
|
case *ast.MapType:
|
|
return n.checkedTypes.Contains(mapType)
|
|
|
|
case *ast.ChanType:
|
|
return n.checkedTypes.Contains(chanType)
|
|
|
|
case *ast.Ident:
|
|
if t, ok := typeSpecs[v.Name]; ok {
|
|
return n.isDangerNilType(t.Type, nil)
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (n *nilNil) isErrorField(f *ast.Field) bool {
|
|
return isIdent(f.Type, "error")
|
|
}
|
|
|
|
func isNil(e ast.Expr) bool {
|
|
return isIdent(e, "nil")
|
|
}
|
|
|
|
func isIdent(n ast.Node, name string) bool {
|
|
i, ok := n.(*ast.Ident)
|
|
if !ok {
|
|
return false
|
|
}
|
|
return i.Name == name
|
|
}
|