Merge "Add type hints to mk2rbc"
This commit is contained in:
@@ -407,6 +407,8 @@ type parseContext struct {
|
|||||||
dependentModules map[string]*moduleInfo
|
dependentModules map[string]*moduleInfo
|
||||||
soongNamespaces map[string]map[string]bool
|
soongNamespaces map[string]map[string]bool
|
||||||
includeTops []string
|
includeTops []string
|
||||||
|
typeHints map[string]starlarkType
|
||||||
|
atTopOfMakefile bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func newParseContext(ss *StarlarkScript, nodes []mkparser.Node) *parseContext {
|
func newParseContext(ss *StarlarkScript, nodes []mkparser.Node) *parseContext {
|
||||||
@@ -450,6 +452,8 @@ func newParseContext(ss *StarlarkScript, nodes []mkparser.Node) *parseContext {
|
|||||||
dependentModules: make(map[string]*moduleInfo),
|
dependentModules: make(map[string]*moduleInfo),
|
||||||
soongNamespaces: make(map[string]map[string]bool),
|
soongNamespaces: make(map[string]map[string]bool),
|
||||||
includeTops: []string{},
|
includeTops: []string{},
|
||||||
|
typeHints: make(map[string]starlarkType),
|
||||||
|
atTopOfMakefile: true,
|
||||||
}
|
}
|
||||||
ctx.pushVarAssignments()
|
ctx.pushVarAssignments()
|
||||||
for _, item := range predefined {
|
for _, item := range predefined {
|
||||||
@@ -1680,7 +1684,8 @@ func (ctx *parseContext) handleSimpleStatement(node mkparser.Node) []starlarkNod
|
|||||||
// Clear the includeTops after each non-comment statement
|
// Clear the includeTops after each non-comment statement
|
||||||
// so that include annotations placed on certain statements don't apply
|
// so that include annotations placed on certain statements don't apply
|
||||||
// globally for the rest of the makefile was well.
|
// globally for the rest of the makefile was well.
|
||||||
if _, wasComment := node.(*mkparser.Comment); !wasComment && len(ctx.includeTops) > 0 {
|
if _, wasComment := node.(*mkparser.Comment); !wasComment {
|
||||||
|
ctx.atTopOfMakefile = false
|
||||||
ctx.includeTops = []string{}
|
ctx.includeTops = []string{}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1690,6 +1695,12 @@ func (ctx *parseContext) handleSimpleStatement(node mkparser.Node) []starlarkNod
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The types allowed in a type_hint
|
||||||
|
var typeHintMap = map[string]starlarkType{
|
||||||
|
"string": starlarkTypeString,
|
||||||
|
"list": starlarkTypeList,
|
||||||
|
}
|
||||||
|
|
||||||
// Processes annotation. An annotation is a comment that starts with #RBC# and provides
|
// Processes annotation. An annotation is a comment that starts with #RBC# and provides
|
||||||
// a conversion hint -- say, where to look for the dynamically calculated inherit/include
|
// a conversion hint -- say, where to look for the dynamically calculated inherit/include
|
||||||
// paths. Returns true if the comment was a successfully-handled annotation.
|
// paths. Returns true if the comment was a successfully-handled annotation.
|
||||||
@@ -1714,6 +1725,35 @@ func (ctx *parseContext) maybeHandleAnnotation(cnode *mkparser.Comment) (starlar
|
|||||||
}
|
}
|
||||||
ctx.includeTops = append(ctx.includeTops, p)
|
ctx.includeTops = append(ctx.includeTops, p)
|
||||||
return nil, true
|
return nil, true
|
||||||
|
} else if p, ok := maybeTrim(annotation, "type_hint"); ok {
|
||||||
|
// Type hints must come at the beginning the file, to avoid confusion
|
||||||
|
// if a type hint was specified later and thus only takes effect for half
|
||||||
|
// of the file.
|
||||||
|
if !ctx.atTopOfMakefile {
|
||||||
|
return ctx.newBadNode(cnode, "type_hint annotations must come before the first Makefile statement"), true
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := strings.Fields(p)
|
||||||
|
if len(parts) <= 1 {
|
||||||
|
return ctx.newBadNode(cnode, "Invalid type_hint annotation: %s. Must be a variable type followed by a list of variables of that type", p), true
|
||||||
|
}
|
||||||
|
|
||||||
|
var varType starlarkType
|
||||||
|
if varType, ok = typeHintMap[parts[0]]; !ok {
|
||||||
|
varType = starlarkTypeUnknown
|
||||||
|
}
|
||||||
|
if varType == starlarkTypeUnknown {
|
||||||
|
return ctx.newBadNode(cnode, "Invalid type_hint annotation. Only list/string types are accepted, found %s", parts[0]), true
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, name := range parts[1:] {
|
||||||
|
// Don't allow duplicate type hints
|
||||||
|
if _, ok := ctx.typeHints[name]; ok {
|
||||||
|
return ctx.newBadNode(cnode, "Duplicate type hint for variable %s", name), true
|
||||||
|
}
|
||||||
|
ctx.typeHints[name] = varType
|
||||||
|
}
|
||||||
|
return nil, true
|
||||||
}
|
}
|
||||||
return ctx.newBadNode(cnode, "unsupported annotation %s", cnode.Comment), true
|
return ctx.newBadNode(cnode, "unsupported annotation %s", cnode.Comment), true
|
||||||
}
|
}
|
||||||
|
@@ -1404,6 +1404,53 @@ def init(g, handle):
|
|||||||
pass
|
pass
|
||||||
if int("100%s" % g.get("MY_VAR", "")) >= 10:
|
if int("100%s" % g.get("MY_VAR", "")) >= 10:
|
||||||
pass
|
pass
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Type hints",
|
||||||
|
mkname: "product.mk",
|
||||||
|
in: `
|
||||||
|
# Test type hints
|
||||||
|
#RBC# type_hint list MY_VAR MY_VAR_2
|
||||||
|
# Unsupported type
|
||||||
|
#RBC# type_hint bool MY_VAR_3
|
||||||
|
# Invalid syntax
|
||||||
|
#RBC# type_hint list
|
||||||
|
# Duplicated variable
|
||||||
|
#RBC# type_hint list MY_VAR_2
|
||||||
|
#RBC# type_hint list my-local-var-with-dashes
|
||||||
|
|
||||||
|
MY_VAR := foo
|
||||||
|
MY_VAR_UNHINTED := foo
|
||||||
|
|
||||||
|
# Vars set after other statements still get the hint
|
||||||
|
MY_VAR_2 := foo
|
||||||
|
|
||||||
|
# You can't specify a type hint after the first statement
|
||||||
|
#RBC# type_hint list MY_VAR_4
|
||||||
|
MY_VAR_4 := foo
|
||||||
|
|
||||||
|
my-local-var-with-dashes := foo
|
||||||
|
`,
|
||||||
|
expected: `# Test type hints
|
||||||
|
# Unsupported type
|
||||||
|
load("//build/make/core:product_config.rbc", "rblf")
|
||||||
|
|
||||||
|
def init(g, handle):
|
||||||
|
cfg = rblf.cfg(handle)
|
||||||
|
rblf.mk2rbc_error("product.mk:5", "Invalid type_hint annotation. Only list/string types are accepted, found bool")
|
||||||
|
# Invalid syntax
|
||||||
|
rblf.mk2rbc_error("product.mk:7", "Invalid type_hint annotation: list. Must be a variable type followed by a list of variables of that type")
|
||||||
|
# Duplicated variable
|
||||||
|
rblf.mk2rbc_error("product.mk:9", "Duplicate type hint for variable MY_VAR_2")
|
||||||
|
g["MY_VAR"] = ["foo"]
|
||||||
|
g["MY_VAR_UNHINTED"] = "foo"
|
||||||
|
# Vars set after other statements still get the hint
|
||||||
|
g["MY_VAR_2"] = ["foo"]
|
||||||
|
# You can't specify a type hint after the first statement
|
||||||
|
rblf.mk2rbc_error("product.mk:19", "type_hint annotations must come before the first Makefile statement")
|
||||||
|
g["MY_VAR_4"] = "foo"
|
||||||
|
_my_local_var_with_dashes = ["foo"]
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@@ -291,6 +291,12 @@ var presetVariables = map[string]bool{
|
|||||||
// addVariable returns a variable with a given name. A variable is
|
// addVariable returns a variable with a given name. A variable is
|
||||||
// added if it does not exist yet.
|
// added if it does not exist yet.
|
||||||
func (ctx *parseContext) addVariable(name string) variable {
|
func (ctx *parseContext) addVariable(name string) variable {
|
||||||
|
// Get the hintType before potentially changing the variable name
|
||||||
|
var hintType starlarkType
|
||||||
|
var ok bool
|
||||||
|
if hintType, ok = ctx.typeHints[name]; !ok {
|
||||||
|
hintType = starlarkTypeUnknown
|
||||||
|
}
|
||||||
// Heuristics: if variable's name is all lowercase, consider it local
|
// Heuristics: if variable's name is all lowercase, consider it local
|
||||||
// string variable.
|
// string variable.
|
||||||
isLocalVariable := name == strings.ToLower(name)
|
isLocalVariable := name == strings.ToLower(name)
|
||||||
@@ -301,8 +307,8 @@ func (ctx *parseContext) addVariable(name string) variable {
|
|||||||
}
|
}
|
||||||
v, found := ctx.variables[name]
|
v, found := ctx.variables[name]
|
||||||
if !found {
|
if !found {
|
||||||
_, preset := presetVariables[name]
|
|
||||||
if vi, found := KnownVariables[name]; found {
|
if vi, found := KnownVariables[name]; found {
|
||||||
|
_, preset := presetVariables[name]
|
||||||
switch vi.class {
|
switch vi.class {
|
||||||
case VarClassConfig:
|
case VarClassConfig:
|
||||||
v = &productConfigVariable{baseVariable{nam: name, typ: vi.valueType, preset: preset}}
|
v = &productConfigVariable{baseVariable{nam: name, typ: vi.valueType, preset: preset}}
|
||||||
@@ -310,10 +316,10 @@ func (ctx *parseContext) addVariable(name string) variable {
|
|||||||
v = &otherGlobalVariable{baseVariable{nam: name, typ: vi.valueType, preset: preset}}
|
v = &otherGlobalVariable{baseVariable{nam: name, typ: vi.valueType, preset: preset}}
|
||||||
}
|
}
|
||||||
} else if isLocalVariable {
|
} else if isLocalVariable {
|
||||||
v = &localVariable{baseVariable{nam: name, typ: starlarkTypeUnknown}}
|
v = &localVariable{baseVariable{nam: name, typ: hintType}}
|
||||||
} else {
|
} else {
|
||||||
vt := starlarkTypeUnknown
|
vt := hintType
|
||||||
if strings.HasPrefix(name, "LOCAL_") {
|
if strings.HasPrefix(name, "LOCAL_") && vt == starlarkTypeUnknown {
|
||||||
// Heuristics: local variables that contribute to corresponding config variables
|
// Heuristics: local variables that contribute to corresponding config variables
|
||||||
if cfgVarName, found := localProductConfigVariables[name]; found {
|
if cfgVarName, found := localProductConfigVariables[name]; found {
|
||||||
vi, found2 := KnownVariables[cfgVarName]
|
vi, found2 := KnownVariables[cfgVarName]
|
||||||
|
Reference in New Issue
Block a user