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 | } |
121 |
Members