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 }