1
16
17 package warn
18
19 import (
20 "regexp"
21
22 "github.com/bazelbuild/buildtools/build"
23 "github.com/bazelbuild/buildtools/bzlenv"
24 )
25
26
27 type Type int
28
29
30 const (
31 Unknown Type = iota
32 Bool
33 Ctx
34 CtxActions
35 CtxActionsArgs
36 Depset
37 Dict
38 Int
39 None
40 String
41 List
42 Float
43 )
44
45 func (t Type) String() string {
46 return [...]string{
47 "unknown",
48 "bool",
49 "ctx",
50 "ctx.actions",
51 "ctx.actions.args",
52 "depset",
53 "dict",
54 "int",
55 "none",
56 "string",
57 "list",
58 "float",
59 }[t]
60 }
61
62 var intRegexp = regexp.MustCompile(`^([0-9]+|0[Xx][0-9A-Fa-f]+|0[Oo][0-7]+)$`)
63
64
65
66
67
68 func DetectTypes(f *build.File) map[build.Expr]Type {
69 variables := make(map[int]Type)
70 result := make(map[build.Expr]Type)
71
72 var walk func(e *build.Expr, env *bzlenv.Environment)
73 walk = func(e *build.Expr, env *bzlenv.Environment) {
74
75 walkOnce(*e, env, walk)
76
77 nodeType := Unknown
78 defer func() {
79 if nodeType != Unknown {
80 result[*e] = nodeType
81 }
82 }()
83
84 switch node := (*e).(type) {
85 case *build.StringExpr:
86 nodeType = String
87 case *build.DictExpr:
88 nodeType = Dict
89 case *build.ListExpr:
90 nodeType = List
91 case *build.LiteralExpr:
92 if intRegexp.MatchString(node.Token) {
93 nodeType = Int
94 } else {
95 nodeType = Float
96 }
97 case *build.Comprehension:
98 if node.Curly {
99 nodeType = Dict
100 } else {
101 nodeType = List
102 }
103 case *build.CallExpr:
104 if ident, ok := (node.X).(*build.Ident); ok {
105 switch ident.Name {
106 case "bool":
107 nodeType = Bool
108 case "int":
109 nodeType = Int
110 case "float":
111 nodeType = Float
112 case "str":
113 nodeType = String
114 case "depset":
115 nodeType = Depset
116 case "dict":
117 nodeType = Dict
118 case "list":
119 nodeType = List
120 }
121 } else if dot, ok := (node.X).(*build.DotExpr); ok {
122 if result[dot.X] == CtxActions && dot.Name == "args" {
123 nodeType = CtxActionsArgs
124 }
125 }
126 case *build.ParenExpr:
127 nodeType = result[node.X]
128 case *build.Ident:
129 switch node.Name {
130 case "True", "False":
131 nodeType = Bool
132 return
133 case "None":
134 nodeType = None
135 return
136 case "ctx":
137 binding := env.Get(node.Name)
138 if binding != nil && binding.Kind == bzlenv.Parameter {
139 nodeType = Ctx
140 return
141 }
142 }
143 binding := env.Get(node.Name)
144 if binding != nil {
145 if t, ok := variables[binding.ID]; ok {
146 nodeType = t
147 }
148 }
149 case *build.DotExpr:
150 if result[node.X] == Ctx && node.Name == "actions" {
151 nodeType = CtxActions
152 }
153 case *build.BinaryExpr:
154 switch node.Op {
155 case ">", ">=", "<", "<=", "==", "!=", "in", "not in":
156
157 nodeType = Bool
158
159 case "+", "-", "*", "/", "//", "%", "|":
160
161
162 if t, ok := result[node.X]; ok {
163 nodeType = t
164 } else if t, ok := result[node.Y]; ok {
165 if node.Op != "%" || t == String {
166
167
168
169 nodeType = t
170 }
171 }
172 }
173 case *build.AssignExpr:
174 t, ok := result[node.RHS]
175 if !ok {
176 return
177 }
178 if node.Op == "%=" && t != String {
179
180 return
181 }
182 ident, ok := (node.LHS).(*build.Ident)
183 if !ok {
184 return
185 }
186 binding := env.Get(ident.Name)
187 if binding == nil {
188 return
189 }
190 variables[binding.ID] = t
191 }
192 }
193 var expr build.Expr = f
194 walk(&expr, bzlenv.NewEnvironment())
195
196 return result
197 }
198
199
200
201
202
203
204
205
206 func walkOnce(node build.Expr, env *bzlenv.Environment, fct func(e *build.Expr, env *bzlenv.Environment)) {
207 switch expr := node.(type) {
208 case *build.CallExpr:
209 fct(&expr.X, env)
210 for _, param := range expr.List {
211 if as, ok := param.(*build.AssignExpr); ok {
212 fct(&as.RHS, env)
213 } else {
214 fct(¶m, env)
215 }
216 }
217 default:
218 bzlenv.WalkOnceWithEnvironment(expr, env, fct)
219 }
220 }
221
View as plain text