github.com/jujuyuki/gospal@v1.0.1-0.20210215170718-af79fae13b20/ssa/build/config.go (about)

     1  package build
     2  
     3  import (
     4  	"fmt"
     5  	"go/build"
     6  	"io"
     7  	"io/ioutil"
     8  	"log"
     9  	"os"
    10  
    11  	"github.com/jujuyuki/gospal/ssa"
    12  	"golang.org/x/tools/go/loader"
    13  	gossa "golang.org/x/tools/go/ssa"
    14  	"golang.org/x/tools/go/ssa/ssautil"
    15  )
    16  
    17  // srcReader is a wrapper for source code which can be read through a NewReader.
    18  type srcReader interface {
    19  	NewReader() io.Reader
    20  }
    21  
    22  type Configurer interface {
    23  	Builder
    24  	Default() Configurer
    25  	AddBadPkg(pkg, reason string) Configurer
    26  	WithBuildLog(l io.Writer, flags int) Configurer
    27  	WithPtaLog(l io.Writer, flags int) Configurer
    28  }
    29  
    30  // Config represents a build configuration.
    31  type Config struct {
    32  	badPkgs map[string]string
    33  
    34  	bldLog    io.Writer // Build log.
    35  	bldLFlags int       // Build log flags.
    36  	ptaLog    io.Writer // Pointer analysis log.
    37  	ptaLFlags int       // Pointer analysis log flags.
    38  
    39  	src srcReader // src points to the program source.
    40  }
    41  
    42  func newConfig(src srcReader) *Config {
    43  	return &Config{
    44  		badPkgs:   make(map[string]string),
    45  		bldLog:    ioutil.Discard,
    46  		bldLFlags: log.LstdFlags,
    47  		ptaLog:    ioutil.Discard,
    48  		ptaLFlags: log.LstdFlags,
    49  		src:       src,
    50  	}
    51  }
    52  
    53  // WithBuildLog adds build log to config.
    54  func (c *Config) WithBuildLog(l io.Writer, flags int) Configurer {
    55  	c.bldLog = l
    56  	c.bldLFlags = flags
    57  	return c
    58  }
    59  
    60  // WithPtaLog adds pointer analysis log to config.
    61  func (c *Config) WithPtaLog(l io.Writer, flags int) Configurer {
    62  	//c := b.(*Config)
    63  	c.ptaLog = l
    64  	c.ptaLFlags = flags
    65  	return c
    66  }
    67  
    68  // AddBadPkg marks a package 'bad' to avoid loading.
    69  func (c *Config) AddBadPkg(pkg, reason string) Configurer {
    70  	//c := b.(*Config)
    71  	c.badPkgs[pkg] = reason
    72  	return c
    73  }
    74  
    75  func (c *Config) Build() (*ssa.Info, error) {
    76  	var lconf = loader.Config{Build: &build.Default}
    77  	bldLog := log.New(c.bldLog, "ssabuild: ", c.bldLFlags)
    78  
    79  	switch src := c.src.(type) {
    80  	case *FileSrc:
    81  		args, err := lconf.FromArgs(src.Files, false /* No tests */)
    82  		if err != nil {
    83  			return nil, err
    84  		}
    85  		if len(args) > 0 {
    86  			return nil, fmt.Errorf("surplus arguments: %q", args)
    87  		}
    88  	default:
    89  		os.Chdir(os.TempDir())
    90  		parsed, err := lconf.ParseFile("tmp", src.NewReader())
    91  		if err != nil {
    92  			return nil, err
    93  		}
    94  		lconf.CreateFromFiles("", parsed)
    95  	}
    96  
    97  	// Load, parse and type-check program
    98  	lprog, err := lconf.Load()
    99  	if err != nil {
   100  		return nil, err
   101  	}
   102  	bldLog.Print("Program loaded and type checked")
   103  
   104  	prog := ssautil.CreateProgram(lprog, gossa.GlobalDebug|gossa.BareInits)
   105  
   106  	var ignoredPkgs []string
   107  	if len(c.badPkgs) == 0 {
   108  		prog.Build()
   109  	} else {
   110  		for _, info := range lprog.AllPackages {
   111  			if reason, badPkg := c.badPkgs[info.Pkg.Path()]; badPkg {
   112  				bldLog.Printf("Skip package: %s (%s)", info.Pkg.Name(), reason)
   113  				ignoredPkgs = append(ignoredPkgs, info.Pkg.Name())
   114  			} else {
   115  				prog.Package(info.Pkg).Build()
   116  			}
   117  		}
   118  	}
   119  
   120  	return &ssa.Info{
   121  		IgnoredPkgs: ignoredPkgs,
   122  		FSet:        lprog.Fset,
   123  		Prog:        prog,
   124  		LProg:       lprog,
   125  		BldLog:      c.bldLog,
   126  		PtaLog:      c.ptaLog,
   127  	}, nil
   128  }
   129  
   130  // Default returns a default configuration for static analysis.
   131  func (c *Config) Default() Configurer {
   132  	return c.
   133  		AddBadPkg("reflect", "Reflection is not supported").
   134  		AddBadPkg("runtime", "Runtime is ignored for static analysis").
   135  		AddBadPkg("internal/singleflight", "Singleflight uses unsupported []chan").
   136  		AddBadPkg("fmt", "Fmt is known to cause unwanted recursive loops")
   137  }