mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-12-03 23:26:29 +00:00
c28f7cb29f
Initial part of #435
116 lines
2.9 KiB
Go
116 lines
2.9 KiB
Go
package ruleguard
|
|
|
|
import (
|
|
"fmt"
|
|
"go/ast"
|
|
"go/importer"
|
|
"go/parser"
|
|
"go/token"
|
|
"go/types"
|
|
"path/filepath"
|
|
"runtime"
|
|
|
|
"github.com/quasilyte/go-ruleguard/internal/golist"
|
|
)
|
|
|
|
// goImporter is a `types.Importer` that tries to load a package no matter what.
|
|
// It iterates through multiple import strategies and accepts whatever succeeds first.
|
|
type goImporter struct {
|
|
// TODO(quasilyte): share importers with gogrep?
|
|
|
|
state *engineState
|
|
|
|
defaultImporter types.Importer
|
|
srcImporter types.Importer
|
|
|
|
fset *token.FileSet
|
|
|
|
debugImports bool
|
|
debugPrint func(string)
|
|
}
|
|
|
|
type goImporterConfig struct {
|
|
fset *token.FileSet
|
|
debugImports bool
|
|
debugPrint func(string)
|
|
}
|
|
|
|
func newGoImporter(state *engineState, config goImporterConfig) *goImporter {
|
|
return &goImporter{
|
|
state: state,
|
|
fset: config.fset,
|
|
debugImports: config.debugImports,
|
|
debugPrint: config.debugPrint,
|
|
defaultImporter: importer.Default(),
|
|
srcImporter: importer.ForCompiler(config.fset, "source", nil),
|
|
}
|
|
}
|
|
|
|
func (imp *goImporter) Import(path string) (*types.Package, error) {
|
|
if pkg := imp.state.GetCachedPackage(path); pkg != nil {
|
|
if imp.debugImports {
|
|
imp.debugPrint(fmt.Sprintf(`imported "%s" from importer cache`, path))
|
|
}
|
|
return pkg, nil
|
|
}
|
|
|
|
pkg, err1 := imp.srcImporter.Import(path)
|
|
if err1 == nil {
|
|
imp.state.AddCachedPackage(path, pkg)
|
|
if imp.debugImports {
|
|
imp.debugPrint(fmt.Sprintf(`imported "%s" from source importer`, path))
|
|
}
|
|
return pkg, nil
|
|
}
|
|
|
|
pkg, err2 := imp.defaultImporter.Import(path)
|
|
if err2 == nil {
|
|
imp.state.AddCachedPackage(path, pkg)
|
|
if imp.debugImports {
|
|
imp.debugPrint(fmt.Sprintf(`imported "%s" from %s importer`, path, runtime.Compiler))
|
|
}
|
|
return pkg, nil
|
|
}
|
|
|
|
// Fallback to `go list` as a last resort.
|
|
pkg, err3 := imp.golistImport(path)
|
|
if err3 == nil {
|
|
imp.state.AddCachedPackage(path, pkg)
|
|
if imp.debugImports {
|
|
imp.debugPrint(fmt.Sprintf(`imported "%s" from golist importer`, path))
|
|
}
|
|
return pkg, nil
|
|
}
|
|
|
|
if imp.debugImports {
|
|
imp.debugPrint(fmt.Sprintf(`failed to import "%s":`, path))
|
|
imp.debugPrint(fmt.Sprintf(" source importer: %v", err1))
|
|
imp.debugPrint(fmt.Sprintf(" %s importer: %v", runtime.Compiler, err2))
|
|
imp.debugPrint(fmt.Sprintf(" golist importer: %v", err3))
|
|
}
|
|
|
|
return nil, err2
|
|
}
|
|
|
|
func (imp *goImporter) golistImport(path string) (*types.Package, error) {
|
|
golistPkg, err := golist.JSON(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
files := make([]*ast.File, 0, len(golistPkg.GoFiles))
|
|
for _, filename := range golistPkg.GoFiles {
|
|
fullname := filepath.Join(golistPkg.Dir, filename)
|
|
f, err := parser.ParseFile(imp.fset, fullname, nil, 0)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
files = append(files, f)
|
|
}
|
|
|
|
// TODO: do we want to assign imp as importer for this nested typecherker?
|
|
// Otherwise it won't be able to resolve imports.
|
|
var typecheker types.Config
|
|
var info types.Info
|
|
return typecheker.Check(path, imp.fset, files, &info)
|
|
}
|