github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/go/analysis/passes/internal/analysisutil/util.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 analysisutil defines various helper functions
     6  // used by two or more packages beneath go/analysis.
     7  package analysisutil
     8  
     9  import (
    10  	"bytes"
    11  	"go/ast"
    12  	"go/printer"
    13  	"go/token"
    14  	"go/types"
    15  	"io/ioutil"
    16  )
    17  
    18  // Format returns a string representation of the expression.
    19  func Format(fset *token.FileSet, x ast.Expr) string {
    20  	var b bytes.Buffer
    21  	printer.Fprint(&b, fset, x)
    22  	return b.String()
    23  }
    24  
    25  // HasSideEffects reports whether evaluation of e has side effects.
    26  func HasSideEffects(info *types.Info, e ast.Expr) bool {
    27  	safe := true
    28  	ast.Inspect(e, func(node ast.Node) bool {
    29  		switch n := node.(type) {
    30  		case *ast.CallExpr:
    31  			typVal := info.Types[n.Fun]
    32  			switch {
    33  			case typVal.IsType():
    34  				// Type conversion, which is safe.
    35  			case typVal.IsBuiltin():
    36  				// Builtin func, conservatively assumed to not
    37  				// be safe for now.
    38  				safe = false
    39  				return false
    40  			default:
    41  				// A non-builtin func or method call.
    42  				// Conservatively assume that all of them have
    43  				// side effects for now.
    44  				safe = false
    45  				return false
    46  			}
    47  		case *ast.UnaryExpr:
    48  			if n.Op == token.ARROW {
    49  				safe = false
    50  				return false
    51  			}
    52  		}
    53  		return true
    54  	})
    55  	return !safe
    56  }
    57  
    58  // Unparen returns e with any enclosing parentheses stripped.
    59  func Unparen(e ast.Expr) ast.Expr {
    60  	for {
    61  		p, ok := e.(*ast.ParenExpr)
    62  		if !ok {
    63  			return e
    64  		}
    65  		e = p.X
    66  	}
    67  }
    68  
    69  // ReadFile reads a file and adds it to the FileSet
    70  // so that we can report errors against it using lineStart.
    71  func ReadFile(fset *token.FileSet, filename string) ([]byte, *token.File, error) {
    72  	content, err := ioutil.ReadFile(filename)
    73  	if err != nil {
    74  		return nil, nil, err
    75  	}
    76  	tf := fset.AddFile(filename, -1, len(content))
    77  	tf.SetLinesForContent(content)
    78  	return content, tf, nil
    79  }
    80  
    81  // LineStart returns the position of the start of the specified line
    82  // within file f, or NoPos if there is no line of that number.
    83  func LineStart(f *token.File, line int) token.Pos {
    84  	// Use binary search to find the start offset of this line.
    85  	//
    86  	// TODO(adonovan): eventually replace this function with the
    87  	// simpler and more efficient (*go/token.File).LineStart, added
    88  	// in go1.12.
    89  
    90  	min := 0        // inclusive
    91  	max := f.Size() // exclusive
    92  	for {
    93  		offset := (min + max) / 2
    94  		pos := f.Pos(offset)
    95  		posn := f.Position(pos)
    96  		if posn.Line == line {
    97  			return pos - (token.Pos(posn.Column) - 1)
    98  		}
    99  
   100  		if min+1 >= max {
   101  			return token.NoPos
   102  		}
   103  
   104  		if posn.Line < line {
   105  			min = offset
   106  		} else {
   107  			max = offset
   108  		}
   109  	}
   110  }
   111  
   112  // Imports returns true if path is imported by pkg.
   113  func Imports(pkg *types.Package, path string) bool {
   114  	for _, imp := range pkg.Imports() {
   115  		if imp.Path() == path {
   116  			return true
   117  		}
   118  	}
   119  	return false
   120  }