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

View as plain text