in warn/types.go [58:175]
func detectTypes(f *build.File) map[build.Expr]Type {
variables := make(map[int]Type)
result := make(map[build.Expr]Type)
var walk func(e *build.Expr, env *bzlenv.Environment)
walk = func(e *build.Expr, env *bzlenv.Environment) {
// Postorder: determining types of subnodes may help with this node's type
walkOnce(*e, env, walk)
nodeType := Unknown
defer func() {
if nodeType != Unknown {
result[*e] = nodeType
}
}()
switch node := (*e).(type) {
case *build.StringExpr:
nodeType = String
case *build.DictExpr:
nodeType = Dict
case *build.ListExpr:
nodeType = List
case *build.LiteralExpr:
nodeType = Int
case *build.Comprehension:
if node.Curly {
nodeType = Dict
} else {
nodeType = List
}
case *build.CallExpr:
if ident, ok := (node.X).(*build.Ident); ok {
switch ident.Name {
case "depset":
nodeType = Depset
case "dict":
nodeType = Dict
case "list":
nodeType = List
}
} else if dot, ok := (node.X).(*build.DotExpr); ok {
if result[dot.X] == CtxActions && dot.Name == "args" {
nodeType = CtxActionsArgs
}
}
case *build.ParenExpr:
nodeType = result[node.X]
case *build.Ident:
switch node.Name {
case "True", "False":
nodeType = Bool
return
case "None":
nodeType = None
return
case "ctx":
binding := env.Get(node.Name)
if binding != nil && binding.Kind == bzlenv.Parameter {
nodeType = Ctx
return
}
}
binding := env.Get(node.Name)
if binding != nil {
if t, ok := variables[binding.ID]; ok {
nodeType = t
}
}
case *build.DotExpr:
if result[node.X] == Ctx && node.Name == "actions" {
nodeType = CtxActions
}
case *build.BinaryExpr:
switch node.Op {
case ">", ">=", "<", "<=", "==", "!=", "in", "not in":
// Boolean
nodeType = Bool
case "+", "-", "*", "/", "//", "%", "|":
// We assume these operators can only applied to expressions of the same type and
// preserve the type
if t, ok := result[node.X]; ok {
nodeType = t
} else if t, ok := result[node.Y]; ok {
if node.Op != "%" || t == String {
// The percent operator is special because it can be applied to to arguments of
// different types (`"%s\n" % foo`), and we can't assume that the expression has
// type X if the right-hand side has the type X.
nodeType = t
}
}
}
case *build.AssignExpr:
t, ok := result[node.RHS]
if !ok {
return
}
if node.Op == "%=" && t != String {
// If the right hand side is not a string, the left hand side can still be a string
return
}
ident, ok := (node.LHS).(*build.Ident)
if !ok {
return
}
binding := env.Get(ident.Name)
if binding == nil {
return
}
variables[binding.ID] = t
}
}
var expr build.Expr = f
walk(&expr, bzlenv.NewEnvironment())
return result
}