Source file src/cmd/vendor/golang.org/x/tools/go/analysis/passes/internal/analysisutil/util.go

     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  	"os"
    16  
    17  	"golang.org/x/tools/go/analysis"
    18  	"golang.org/x/tools/internal/analysisinternal"
    19  )
    20  
    21  // Format returns a string representation of the expression.
    22  func Format(fset *token.FileSet, x ast.Expr) string {
    23  	var b bytes.Buffer
    24  	printer.Fprint(&b, fset, x)
    25  	return b.String()
    26  }
    27  
    28  // HasSideEffects reports whether evaluation of e has side effects.
    29  func HasSideEffects(info *types.Info, e ast.Expr) bool {
    30  	safe := true
    31  	ast.Inspect(e, func(node ast.Node) bool {
    32  		switch n := node.(type) {
    33  		case *ast.CallExpr:
    34  			typVal := info.Types[n.Fun]
    35  			switch {
    36  			case typVal.IsType():
    37  				// Type conversion, which is safe.
    38  			case typVal.IsBuiltin():
    39  				// Builtin func, conservatively assumed to not
    40  				// be safe for now.
    41  				safe = false
    42  				return false
    43  			default:
    44  				// A non-builtin func or method call.
    45  				// Conservatively assume that all of them have
    46  				// side effects for now.
    47  				safe = false
    48  				return false
    49  			}
    50  		case *ast.UnaryExpr:
    51  			if n.Op == token.ARROW {
    52  				safe = false
    53  				return false
    54  			}
    55  		}
    56  		return true
    57  	})
    58  	return !safe
    59  }
    60  
    61  // ReadFile reads a file and adds it to the FileSet
    62  // so that we can report errors against it using lineStart.
    63  func ReadFile(pass *analysis.Pass, filename string) ([]byte, *token.File, error) {
    64  	readFile := pass.ReadFile
    65  	if readFile == nil {
    66  		readFile = os.ReadFile
    67  	}
    68  	content, err := readFile(filename)
    69  	if err != nil {
    70  		return nil, nil, err
    71  	}
    72  	tf := pass.Fset.AddFile(filename, -1, len(content))
    73  	tf.SetLinesForContent(content)
    74  	return content, tf, nil
    75  }
    76  
    77  // LineStart returns the position of the start of the specified line
    78  // within file f, or NoPos if there is no line of that number.
    79  func LineStart(f *token.File, line int) token.Pos {
    80  	// Use binary search to find the start offset of this line.
    81  	//
    82  	// TODO(adonovan): eventually replace this function with the
    83  	// simpler and more efficient (*go/token.File).LineStart, added
    84  	// in go1.12.
    85  
    86  	min := 0        // inclusive
    87  	max := f.Size() // exclusive
    88  	for {
    89  		offset := (min + max) / 2
    90  		pos := f.Pos(offset)
    91  		posn := f.Position(pos)
    92  		if posn.Line == line {
    93  			return pos - (token.Pos(posn.Column) - 1)
    94  		}
    95  
    96  		if min+1 >= max {
    97  			return token.NoPos
    98  		}
    99  
   100  		if posn.Line < line {
   101  			min = offset
   102  		} else {
   103  			max = offset
   104  		}
   105  	}
   106  }
   107  
   108  // Imports returns true if path is imported by pkg.
   109  func Imports(pkg *types.Package, path string) bool {
   110  	for _, imp := range pkg.Imports() {
   111  		if imp.Path() == path {
   112  			return true
   113  		}
   114  	}
   115  	return false
   116  }
   117  
   118  // IsNamedType reports whether t is the named type with the given package path
   119  // and one of the given names.
   120  // This function avoids allocating the concatenation of "pkg.Name",
   121  // which is important for the performance of syntax matching.
   122  func IsNamedType(t types.Type, pkgPath string, names ...string) bool {
   123  	n, ok := types.Unalias(t).(*types.Named)
   124  	if !ok {
   125  		return false
   126  	}
   127  	obj := n.Obj()
   128  	if obj == nil || obj.Pkg() == nil || obj.Pkg().Path() != pkgPath {
   129  		return false
   130  	}
   131  	name := obj.Name()
   132  	for _, n := range names {
   133  		if name == n {
   134  			return true
   135  		}
   136  	}
   137  	return false
   138  }
   139  
   140  // IsFunctionNamed reports whether f is a top-level function defined in the
   141  // given package and has one of the given names.
   142  // It returns false if f is nil or a method.
   143  func IsFunctionNamed(f *types.Func, pkgPath string, names ...string) bool {
   144  	if f == nil {
   145  		return false
   146  	}
   147  	if f.Pkg() == nil || f.Pkg().Path() != pkgPath {
   148  		return false
   149  	}
   150  	if f.Type().(*types.Signature).Recv() != nil {
   151  		return false
   152  	}
   153  	for _, n := range names {
   154  		if f.Name() == n {
   155  			return true
   156  		}
   157  	}
   158  	return false
   159  }
   160  
   161  var MustExtractDoc = analysisinternal.MustExtractDoc
   162  

View as plain text