...

Source file src/golang.org/x/tools/go/analysis/passes/pkgfact/pkgfact.go

Documentation: golang.org/x/tools/go/analysis/passes/pkgfact

     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  // The pkgfact package is a demonstration and test of the package fact
     6  // mechanism.
     7  //
     8  // The output of the pkgfact analysis is a set of key/values pairs
     9  // gathered from the analyzed package and its imported dependencies.
    10  // Each key/value pair comes from a top-level constant declaration
    11  // whose name starts and ends with "_".  For example:
    12  //
    13  //	package p
    14  //
    15  //	const _greeting_  = "hello"
    16  //	const _audience_  = "world"
    17  //
    18  // the pkgfact analysis output for package p would be:
    19  //
    20  //	{"greeting": "hello", "audience": "world"}.
    21  //
    22  // In addition, the analysis reports a diagnostic at each import
    23  // showing which key/value pairs it contributes.
    24  package pkgfact
    25  
    26  import (
    27  	"fmt"
    28  	"go/ast"
    29  	"go/token"
    30  	"go/types"
    31  	"reflect"
    32  	"sort"
    33  	"strings"
    34  
    35  	"golang.org/x/tools/go/analysis"
    36  )
    37  
    38  var Analyzer = &analysis.Analyzer{
    39  	Name:       "pkgfact",
    40  	Doc:        "gather name/value pairs from constant declarations",
    41  	Run:        run,
    42  	FactTypes:  []analysis.Fact{new(pairsFact)},
    43  	ResultType: reflect.TypeOf(map[string]string{}),
    44  }
    45  
    46  // A pairsFact is a package-level fact that records
    47  // an set of key=value strings accumulated from constant
    48  // declarations in this package and its dependencies.
    49  // Elements are ordered by keys, which are unique.
    50  type pairsFact []string
    51  
    52  func (f *pairsFact) AFact()         {}
    53  func (f *pairsFact) String() string { return "pairs(" + strings.Join(*f, ", ") + ")" }
    54  
    55  func run(pass *analysis.Pass) (interface{}, error) {
    56  	result := make(map[string]string)
    57  
    58  	// At each import, print the fact from the imported
    59  	// package and accumulate its information into the result.
    60  	// (Warning: accumulation leads to quadratic growth of work.)
    61  	doImport := func(spec *ast.ImportSpec) {
    62  		pkg := imported(pass.TypesInfo, spec)
    63  		var fact pairsFact
    64  		if pass.ImportPackageFact(pkg, &fact) {
    65  			for _, pair := range fact {
    66  				eq := strings.IndexByte(pair, '=')
    67  				result[pair[:eq]] = pair[1+eq:]
    68  			}
    69  			pass.ReportRangef(spec, "%s", strings.Join(fact, " "))
    70  		}
    71  	}
    72  
    73  	// At each "const _name_ = value", add a fact into env.
    74  	doConst := func(spec *ast.ValueSpec) {
    75  		if len(spec.Names) == len(spec.Values) {
    76  			for i := range spec.Names {
    77  				name := spec.Names[i].Name
    78  				if strings.HasPrefix(name, "_") && strings.HasSuffix(name, "_") {
    79  
    80  					if key := strings.Trim(name, "_"); key != "" {
    81  						value := pass.TypesInfo.Types[spec.Values[i]].Value.String()
    82  						result[key] = value
    83  					}
    84  				}
    85  			}
    86  		}
    87  	}
    88  
    89  	for _, f := range pass.Files {
    90  		for _, decl := range f.Decls {
    91  			if decl, ok := decl.(*ast.GenDecl); ok {
    92  				for _, spec := range decl.Specs {
    93  					switch decl.Tok {
    94  					case token.IMPORT:
    95  						doImport(spec.(*ast.ImportSpec))
    96  					case token.CONST:
    97  						doConst(spec.(*ast.ValueSpec))
    98  					}
    99  				}
   100  			}
   101  		}
   102  	}
   103  
   104  	// Sort/deduplicate the result and save it as a package fact.
   105  	keys := make([]string, 0, len(result))
   106  	for key := range result {
   107  		keys = append(keys, key)
   108  	}
   109  	sort.Strings(keys)
   110  	var fact pairsFact
   111  	for _, key := range keys {
   112  		fact = append(fact, fmt.Sprintf("%s=%s", key, result[key]))
   113  	}
   114  	if len(fact) > 0 {
   115  		pass.ExportPackageFact(&fact)
   116  	}
   117  
   118  	return result, nil
   119  }
   120  
   121  func imported(info *types.Info, spec *ast.ImportSpec) *types.Package {
   122  	obj, ok := info.Implicits[spec]
   123  	if !ok {
   124  		obj = info.Defs[spec.Name] // renaming import
   125  	}
   126  	return obj.(*types.PkgName).Imported()
   127  }
   128  

View as plain text