* Drop the iso_checksum_type & iso_checksum_url fields In favor of simply using iso_checksum that will know what to do. * fix after master merge * Update builder_test.go * Update builder_test.go * Update builder_test.go * Update builder_test.go * Update builder_test.go * remove checksum lowercasing tests * Update builder_test.go * Update builder_test.go * better docs * Update builder_test.go * even better docs * Update config.go * Update builder_test.go * Update step_create_vmx_test.go * make generate * better docs * fix imports * up tests * Update _ISOConfig-required.html.md * Update builder_test.go * don't use sha1.Sum("none") as a caching path * Update builder_test.go * better docs * Update iso_config_test.go remove ISOChecksumType/ISOChecksumURL references * Update step_download_test.go * add iso_checksum_url and iso_checksum_type fixers + tests * add concrete examples of checksum values * add examples of checksumming from local file * update go-getter dep * up deps * use new go-getter version * up ESX5Driver.VerifyChecksum: use go-getter's checksumming * ISOConfig.Prepare: get checksum there in case we need it as a string in ESX5Driver.VerifyChecksum * Update iso_config.go * get go-getter from v2 branch * Update driver_esx5.go add more comments * Update driver_esx5.go * show better error message when the checksum is invalid * Update builder_test.go put in a valid checksum to fix tests, checksum is md5("packer") * Update builder_test.go test invalid and valid checksum * more test updating * fix default md5 string to be a valid md5 * TestChecksumFileNameMixedCaseBug: use 'file:' prefix for file checksumming * Update iso_config_test.go * Update iso_config_test.go * Update builder_test.go * Update builder_test.go * Update builder_test.go * Update CHANGELOG.md * Update CHANGELOG.md * Update go.mod * Update go.mod * Update CHANGELOG.md
178 lines
4.2 KiB
Go
178 lines
4.2 KiB
Go
package facts
|
|
|
|
import (
|
|
"go/types"
|
|
"reflect"
|
|
|
|
"golang.org/x/tools/go/analysis"
|
|
"honnef.co/go/tools/functions"
|
|
"honnef.co/go/tools/internal/passes/buildir"
|
|
"honnef.co/go/tools/ir"
|
|
)
|
|
|
|
type IsPure struct{}
|
|
|
|
func (*IsPure) AFact() {}
|
|
func (d *IsPure) String() string { return "is pure" }
|
|
|
|
type PurityResult map[*types.Func]*IsPure
|
|
|
|
var Purity = &analysis.Analyzer{
|
|
Name: "fact_purity",
|
|
Doc: "Mark pure functions",
|
|
Run: purity,
|
|
Requires: []*analysis.Analyzer{buildir.Analyzer},
|
|
FactTypes: []analysis.Fact{(*IsPure)(nil)},
|
|
ResultType: reflect.TypeOf(PurityResult{}),
|
|
}
|
|
|
|
var pureStdlib = map[string]struct{}{
|
|
"errors.New": {},
|
|
"fmt.Errorf": {},
|
|
"fmt.Sprintf": {},
|
|
"fmt.Sprint": {},
|
|
"sort.Reverse": {},
|
|
"strings.Map": {},
|
|
"strings.Repeat": {},
|
|
"strings.Replace": {},
|
|
"strings.Title": {},
|
|
"strings.ToLower": {},
|
|
"strings.ToLowerSpecial": {},
|
|
"strings.ToTitle": {},
|
|
"strings.ToTitleSpecial": {},
|
|
"strings.ToUpper": {},
|
|
"strings.ToUpperSpecial": {},
|
|
"strings.Trim": {},
|
|
"strings.TrimFunc": {},
|
|
"strings.TrimLeft": {},
|
|
"strings.TrimLeftFunc": {},
|
|
"strings.TrimPrefix": {},
|
|
"strings.TrimRight": {},
|
|
"strings.TrimRightFunc": {},
|
|
"strings.TrimSpace": {},
|
|
"strings.TrimSuffix": {},
|
|
"(*net/http.Request).WithContext": {},
|
|
}
|
|
|
|
func purity(pass *analysis.Pass) (interface{}, error) {
|
|
seen := map[*ir.Function]struct{}{}
|
|
irpkg := pass.ResultOf[buildir.Analyzer].(*buildir.IR).Pkg
|
|
var check func(fn *ir.Function) (ret bool)
|
|
check = func(fn *ir.Function) (ret bool) {
|
|
if fn.Object() == nil {
|
|
// TODO(dh): support closures
|
|
return false
|
|
}
|
|
if pass.ImportObjectFact(fn.Object(), new(IsPure)) {
|
|
return true
|
|
}
|
|
if fn.Pkg != irpkg {
|
|
// Function is in another package but wasn't marked as
|
|
// pure, ergo it isn't pure
|
|
return false
|
|
}
|
|
// Break recursion
|
|
if _, ok := seen[fn]; ok {
|
|
return false
|
|
}
|
|
|
|
seen[fn] = struct{}{}
|
|
defer func() {
|
|
if ret {
|
|
pass.ExportObjectFact(fn.Object(), &IsPure{})
|
|
}
|
|
}()
|
|
|
|
if functions.IsStub(fn) {
|
|
return false
|
|
}
|
|
|
|
if _, ok := pureStdlib[fn.Object().(*types.Func).FullName()]; ok {
|
|
return true
|
|
}
|
|
|
|
if fn.Signature.Results().Len() == 0 {
|
|
// A function with no return values is empty or is doing some
|
|
// work we cannot see (for example because of build tags);
|
|
// don't consider it pure.
|
|
return false
|
|
}
|
|
|
|
for _, param := range fn.Params {
|
|
// TODO(dh): this may not be strictly correct. pure code
|
|
// can, to an extent, operate on non-basic types.
|
|
if _, ok := param.Type().Underlying().(*types.Basic); !ok {
|
|
return false
|
|
}
|
|
}
|
|
|
|
// Don't consider external functions pure.
|
|
if fn.Blocks == nil {
|
|
return false
|
|
}
|
|
checkCall := func(common *ir.CallCommon) bool {
|
|
if common.IsInvoke() {
|
|
return false
|
|
}
|
|
builtin, ok := common.Value.(*ir.Builtin)
|
|
if !ok {
|
|
if common.StaticCallee() != fn {
|
|
if common.StaticCallee() == nil {
|
|
return false
|
|
}
|
|
if !check(common.StaticCallee()) {
|
|
return false
|
|
}
|
|
}
|
|
} else {
|
|
switch builtin.Name() {
|
|
case "len", "cap":
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
for _, b := range fn.Blocks {
|
|
for _, ins := range b.Instrs {
|
|
switch ins := ins.(type) {
|
|
case *ir.Call:
|
|
if !checkCall(ins.Common()) {
|
|
return false
|
|
}
|
|
case *ir.Defer:
|
|
if !checkCall(&ins.Call) {
|
|
return false
|
|
}
|
|
case *ir.Select:
|
|
return false
|
|
case *ir.Send:
|
|
return false
|
|
case *ir.Go:
|
|
return false
|
|
case *ir.Panic:
|
|
return false
|
|
case *ir.Store:
|
|
return false
|
|
case *ir.FieldAddr:
|
|
return false
|
|
case *ir.Alloc:
|
|
return false
|
|
case *ir.Load:
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
|
|
check(fn)
|
|
}
|
|
|
|
out := PurityResult{}
|
|
for _, fact := range pass.AllObjectFacts() {
|
|
out[fact.Object.(*types.Func)] = fact.Fact.(*IsPure)
|
|
}
|
|
return out, nil
|
|
}
|