def detect_unused_vars(model): """ Warns about unused variables -- they may indicate typos. """ # First of all, iterate over all variables and mark their usage of other # variables. Notice that it's possible that some code explicitly marked # variables as used with mark_variables_as_used() before this step. for var in model.all_variables(): usage_tracker.visit(var.value) # Not emit warnings for unused variables. import re regex_vs_option = re.compile(r"vs[0-9]+\.option\.") for var in model.all_variables(): if ( not var.is_property and not usage_tracker.is_used(var) and # FIXME: Handle these cases properly. Have a properties group # declaration similar to Property, with type checking and # automated docs and all. Then test for it here as other # properties are tested for. not regex_vs_option.match(var.name) and # FIXME: Handle this case properly. var.name != "configurations" ): warning('variable "%s" is never used', var.name, pos=var.value.pos)
def normalize_and_validate_bool_subexpressions(model): """ Normalizes bool expressions, i.e. ensures the conditions are valid bools. variables' values with respect to their types. """ logger.debug("checking boolean expressions") for var in model.all_variables(): bkl.vartypes.normalize_and_validate_bool_subexpressions(var.value)
def simplify_exprs(model): """ Simplify expressions in the model. This does "cheap" simplifications such as merging concatenated literals, recognizing always-false conditions, eliminating unnecessary variable references (turn ``foo=$(x);bar=$(foo)`` into ``bar=$(x)``) etc. """ logger.debug("simplifying expressions") simplifier = simplify.BasicSimplifier() for var in model.all_variables(): var.value = simplifier.visit(var.value)
def normalize_vars(model): """ Normalizes variables' values with respect to their types. For example, changes non-list value expressions for lists into single-item lists. """ logger.debug("normalizing variables") for var in model.all_variables(): # if the type of the variable wasn't determined yet, guess it if var.type is bkl.vartypes.TheAnyType: var.type = bkl.vartypes.guess_expr_type(var.value) # normalize the value for the type var.value = var.type.normalize(var.value)
def detect_self_references(model): """ Verifies that recursive self-referencing loops (e.g. "foo = $(foo)") don't exist. """ logger.debug("checking for self-references") class SelfRefChecker(Visitor): def __init__(self): super(SelfRefChecker, self).__init__() self.stack = [] self.checked = set() literal = Visitor.noop bool_value = Visitor.noop null = Visitor.noop concat = Visitor.visit_children list = Visitor.visit_children path = Visitor.visit_children bool = Visitor.visit_children if_ = Visitor.visit_children placeholder = Visitor.noop def reference(self, e): var = e.get_variable() if var is None: # reference to default value of a property return if var in self.stack: # TODO: include complete stack of messages+positions raise Error( 'variable "%s" is defined recursively, references itself' % var.name, pos=e.pos) else: self.check(var) def check(self, var): if var in self.checked: return self.stack.append(var) try: self.visit(var.value) finally: self.stack.pop() self.checked.add(var) visitor = SelfRefChecker() for var in model.all_variables(): visitor.check(var)
def validate_vars(model): """ Validates variables' values with respect to their types, i.e. check the correctness of the values. It is assumed that normalize_vars() was executed beforehand. """ logger.debug("checking types of variables") for var in model.all_variables(): try: var.type.validate(var.value) except TypeError as err: # TODO: add this as a remark to the error object err.msg = "variable \"%s\" (%s): %s" % (var.name, var.type, err.msg) raise
def detect_self_references(model): """ Verifies that recursive self-referencing loops (e.g. "foo = $(foo)") don't exist. """ logger.debug("checking for self-references") class SelfRefChecker(Visitor): def __init__(self): super(SelfRefChecker, self).__init__() self.stack = [] self.checked = set() literal = Visitor.noop bool_value = Visitor.noop null = Visitor.noop concat = Visitor.visit_children list = Visitor.visit_children path = Visitor.visit_children bool = Visitor.visit_children if_ = Visitor.visit_children placeholder = Visitor.noop def reference(self, e): var = e.get_variable() if var is None: # reference to default value of a property return if var in self.stack: # TODO: include complete stack of messages+positions raise Error('variable "%s" is defined recursively, references itself' % var.name, pos=e.pos) else: self.check(var) def check(self, var): if var in self.checked: return self.stack.append(var) try: self.visit(var.value) finally: self.stack.pop() self.checked.add(var) visitor = SelfRefChecker() for var in model.all_variables(): visitor.check(var)
def detect_unused_vars(model): """ Warns about unused variables -- they may indicate typos. """ # First of all, iterate over all variables and mark their usage of other # variables. Notice that it's possible that some code explicitly marked # variables as used with mark_variables_in_expr_as_used() before this step. for var in model.all_variables(): usage_tracker.visit(var.value) # Not emit warnings for unused variables. import re regex_vs_option = re.compile(r'vs[0-9]+\.option\.') for var in model.all_variables(): if (not var.is_property and not usage_tracker.is_used(var) and # FIXME: Handle these cases properly. Have a properties group # declaration similar to Property, with type checking and # automated docs and all. Then test for it here as other # properties are tested for. not regex_vs_option.match(var.name) and # FIXME: Handle this case properly. var.name != "configurations"): warning('variable "%s" is never used', var.name, pos=var.value.pos)
def eliminate_superfluous_conditionals(model): """ Removes as much of conditional content as possible. This involves doing as many optimizations as possible, even if the calculation is relatively expensive (compared to simplify_exprs()). """ iteration = 1 simplifier = simplify.ConditionalsSimplifier() while True: logger.debug("removing superfluous conditional expressions: pass %i", iteration) modified = False for var in model.all_variables(): old = var.value var.value = simplifier.visit(var.value) if old is not var.value: logger.debug("new pass triggered because of this change: {%s} -> {%s}", old, var.value) modified = True if modified: iteration += 1 else: break