github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/internal/passes/buildir/buildir.go (about)

     1  // Copyright 2018 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package buildir defines an Analyzer that constructs the IR
     6  // of an error-free package and returns the set of all
     7  // functions within it. It does not report any diagnostics itself but
     8  // may be used as an input to other analyzers.
     9  //
    10  // THIS INTERFACE IS EXPERIMENTAL AND MAY BE SUBJECT TO INCOMPATIBLE CHANGE.
    11  package buildir
    12  
    13  import (
    14  	"go/ast"
    15  	"go/types"
    16  	"reflect"
    17  
    18  	"github.com/amarpal/go-tools/go/ir"
    19  
    20  	"golang.org/x/tools/go/analysis"
    21  )
    22  
    23  type noReturn struct {
    24  	Kind ir.NoReturn
    25  }
    26  
    27  func (*noReturn) AFact() {}
    28  
    29  var Analyzer = &analysis.Analyzer{
    30  	Name:       "buildir",
    31  	Doc:        "build IR for later passes",
    32  	Run:        run,
    33  	ResultType: reflect.TypeOf(new(IR)),
    34  	FactTypes:  []analysis.Fact{new(noReturn)},
    35  }
    36  
    37  // IR provides intermediate representation for all the
    38  // non-blank source functions in the current package.
    39  type IR struct {
    40  	Pkg      *ir.Package
    41  	SrcFuncs []*ir.Function
    42  }
    43  
    44  func run(pass *analysis.Pass) (interface{}, error) {
    45  	// Plundered from ssautil.BuildPackage.
    46  
    47  	// We must create a new Program for each Package because the
    48  	// analysis API provides no place to hang a Program shared by
    49  	// all Packages. Consequently, IR Packages and Functions do not
    50  	// have a canonical representation across an analysis session of
    51  	// multiple packages. This is unlikely to be a problem in
    52  	// practice because the analysis API essentially forces all
    53  	// packages to be analysed independently, so any given call to
    54  	// Analysis.Run on a package will see only IR objects belonging
    55  	// to a single Program.
    56  
    57  	mode := ir.GlobalDebug
    58  
    59  	prog := ir.NewProgram(pass.Fset, mode)
    60  
    61  	// Create IR packages for all imports.
    62  	// Order is not significant.
    63  	created := make(map[*types.Package]bool)
    64  	var createAll func(pkgs []*types.Package)
    65  	createAll = func(pkgs []*types.Package) {
    66  		for _, p := range pkgs {
    67  			if !created[p] {
    68  				created[p] = true
    69  				irpkg := prog.CreatePackage(p, nil, nil, true)
    70  				for _, fn := range irpkg.Functions {
    71  					if ast.IsExported(fn.Name()) {
    72  						var noRet noReturn
    73  						if pass.ImportObjectFact(fn.Object(), &noRet) {
    74  							fn.NoReturn = noRet.Kind
    75  						}
    76  					}
    77  				}
    78  				createAll(p.Imports())
    79  			}
    80  		}
    81  	}
    82  	createAll(pass.Pkg.Imports())
    83  
    84  	// Create and build the primary package.
    85  	irpkg := prog.CreatePackage(pass.Pkg, pass.Files, pass.TypesInfo, false)
    86  	irpkg.Build()
    87  
    88  	// Compute list of source functions, including literals,
    89  	// in source order.
    90  	var addAnons func(f *ir.Function)
    91  	funcs := make([]*ir.Function, len(irpkg.Functions))
    92  	copy(funcs, irpkg.Functions)
    93  	addAnons = func(f *ir.Function) {
    94  		for _, anon := range f.AnonFuncs {
    95  			funcs = append(funcs, anon)
    96  			addAnons(anon)
    97  		}
    98  	}
    99  	for _, fn := range irpkg.Functions {
   100  		addAnons(fn)
   101  		if fn.NoReturn > 0 {
   102  			pass.ExportObjectFact(fn.Object(), &noReturn{fn.NoReturn})
   103  		}
   104  	}
   105  
   106  	return &IR{Pkg: irpkg, SrcFuncs: funcs}, nil
   107  }