181 lines
3.6 KiB
Go
181 lines
3.6 KiB
Go
|
package reqwithoutctx
|
||
|
|
||
|
import (
|
||
|
"go/types"
|
||
|
|
||
|
"github.com/gostaticanalysis/analysisutil"
|
||
|
|
||
|
"golang.org/x/tools/go/analysis"
|
||
|
"golang.org/x/tools/go/analysis/passes/buildssa"
|
||
|
"golang.org/x/tools/go/ssa"
|
||
|
)
|
||
|
|
||
|
type Analyzer struct {
|
||
|
Funcs []*ssa.Function
|
||
|
newRequestType types.Type
|
||
|
requestType types.Type
|
||
|
}
|
||
|
|
||
|
func NewAnalyzer(pass *analysis.Pass) *Analyzer {
|
||
|
newRequestType := analysisutil.TypeOf(pass, "net/http", "NewRequest")
|
||
|
requestType := analysisutil.TypeOf(pass, "net/http", "*Request")
|
||
|
|
||
|
srcFuncs := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA).SrcFuncs
|
||
|
|
||
|
return &Analyzer{
|
||
|
Funcs: srcFuncs,
|
||
|
newRequestType: newRequestType,
|
||
|
requestType: requestType,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (a *Analyzer) Exec() []*Report {
|
||
|
if a.newRequestType == nil || a.requestType == nil {
|
||
|
return []*Report{}
|
||
|
}
|
||
|
|
||
|
usedReqs := a.usedReqs()
|
||
|
newReqs := a.requestsByNewRequest()
|
||
|
|
||
|
return a.report(usedReqs, newReqs)
|
||
|
}
|
||
|
|
||
|
func (a *Analyzer) report(usedReqs map[string]*ssa.Extract, newReqs map[*ssa.Call]*ssa.Extract) []*Report {
|
||
|
var reports []*Report
|
||
|
|
||
|
for _, fReq := range usedReqs {
|
||
|
for newRequest, req := range newReqs {
|
||
|
if fReq == req {
|
||
|
reports = append(reports, &Report{Instruction: newRequest})
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return reports
|
||
|
}
|
||
|
|
||
|
func (a *Analyzer) usedReqs() map[string]*ssa.Extract {
|
||
|
reqExts := make(map[string]*ssa.Extract)
|
||
|
|
||
|
for _, f := range a.Funcs {
|
||
|
for _, b := range f.Blocks {
|
||
|
for _, instr := range b.Instrs {
|
||
|
switch i := instr.(type) {
|
||
|
case *ssa.Call:
|
||
|
exts := a.usedReqByCall(i)
|
||
|
for _, ext := range exts {
|
||
|
key := i.String() + ext.String()
|
||
|
reqExts[key] = ext
|
||
|
}
|
||
|
case *ssa.UnOp:
|
||
|
ext := a.usedReqByUnOp(i)
|
||
|
if ext != nil {
|
||
|
key := i.String() + ext.String()
|
||
|
reqExts[key] = ext
|
||
|
}
|
||
|
case *ssa.Return:
|
||
|
exts := a.usedReqByReturn(i)
|
||
|
for _, ext := range exts {
|
||
|
key := i.String() + ext.String()
|
||
|
reqExts[key] = ext
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return reqExts
|
||
|
}
|
||
|
|
||
|
func (a *Analyzer) usedReqByCall(call *ssa.Call) []*ssa.Extract {
|
||
|
var exts []*ssa.Extract
|
||
|
|
||
|
// skip net/http.Request method call
|
||
|
if call.Common().Signature().Recv() != nil && types.Identical(call.Value().Type(), a.requestType) {
|
||
|
return exts
|
||
|
}
|
||
|
|
||
|
args := call.Common().Args
|
||
|
if len(args) == 0 {
|
||
|
return exts
|
||
|
}
|
||
|
|
||
|
for _, arg := range args {
|
||
|
ext, ok := arg.(*ssa.Extract)
|
||
|
if !ok {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
if !types.Identical(ext.Type(), a.requestType) {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
exts = append(exts, ext)
|
||
|
}
|
||
|
|
||
|
return exts
|
||
|
}
|
||
|
|
||
|
func (a *Analyzer) usedReqByUnOp(op *ssa.UnOp) *ssa.Extract {
|
||
|
if ext, ok := op.X.(*ssa.Extract); ok && types.Identical(ext.Type(), a.requestType) {
|
||
|
return ext
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (a *Analyzer) usedReqByReturn(ret *ssa.Return) []*ssa.Extract {
|
||
|
rets := ret.Results
|
||
|
exts := make([]*ssa.Extract, 0, len(rets))
|
||
|
|
||
|
for _, ret := range rets {
|
||
|
ext, ok := ret.(*ssa.Extract)
|
||
|
if !ok {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
if types.Identical(ext.Type(), a.requestType) {
|
||
|
exts = append(exts, ext)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return exts
|
||
|
}
|
||
|
|
||
|
func (a *Analyzer) requestsByNewRequest() map[*ssa.Call]*ssa.Extract {
|
||
|
reqs := make(map[*ssa.Call]*ssa.Extract)
|
||
|
|
||
|
for _, f := range a.Funcs {
|
||
|
for _, b := range f.Blocks {
|
||
|
for _, instr := range b.Instrs {
|
||
|
ext, ok := instr.(*ssa.Extract)
|
||
|
if !ok {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
if !types.Identical(ext.Type(), a.requestType) {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
operands := ext.Operands([]*ssa.Value{})
|
||
|
if len(operands) != 1 {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
operand := *operands[0]
|
||
|
|
||
|
f, ok := operand.(*ssa.Call)
|
||
|
if !ok {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
if types.Identical(f.Call.Value.Type(), a.newRequestType) {
|
||
|
reqs[f] = ext
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return reqs
|
||
|
}
|