def space_before_call(contents, abstract_syntax_tree):
    """Check that each function call is preceded by a single space."""
    errors = []

    def _call_handler(name, node):
        """Handle function calls."""
        assert name == "FunctionCall"

        line_index = node.line - 1
        col_index = node.col - 1
        end_of_name_index = col_index + len(node.name)
        num_spaces_until_openparen = 0
        index = end_of_name_index
        while contents[line_index][index] != "(":
            index += 1

        # AST checks will assert if the open paren is not found here

        num_spaces_until_openparen = index - end_of_name_index

        # Must be one space only
        if num_spaces_until_openparen != 1:
            extra_spaces = num_spaces_until_openparen - 1
            msg = "{0} extra spaces between call of {1}".format(extra_spaces,
                                                                node.name)
            replacement = util.replace_word(contents[line_index],
                                            end_of_name_index,
                                            " " * (extra_spaces + 1),
                                            " ")
            errors.append(LinterFailure(msg, node.line, replacement))

    ast_visitor.recurse(abstract_syntax_tree,
                        function_call=ignore.visitor_depth(_call_handler))

    return errors
Exemplo n.º 2
0
def uppercase_arguments(contents, abstract_syntax_tree):
    """Check that arguments to definitions are all uppercase."""
    errors = []

    def _definition_visitor(name, node):
        """Visit all definitions."""
        assert name == "FunctionDefinition" or name == "MacroDefinition"

        if len(node.header.arguments) < 2:
            return

        for arg in node.header.arguments[1:]:
            if arg.contents.upper() != arg.contents:
                msg = "{0} must be uppercase".format(arg.contents)
                line_index = arg.line - 1
                col_index = arg.col - 1
                replacement = util.replace_word(contents[line_index],
                                                col_index, arg.contents,
                                                arg.contents.upper())
                errors.append(LinterFailure(msg, arg.line, replacement))

    ast_visitor.recurse(abstract_syntax_tree,
                        function_def=ignore.visitor_depth(_definition_visitor),
                        macro_def=ignore.visitor_depth(_definition_visitor))

    return errors
def uppercase_arguments(contents, abstract_syntax_tree):
    """Check that arguments to definitions are all uppercase."""
    errors = []

    def _definition_visitor(name, node):
        """Visit all definitions."""
        assert name == "FunctionDefinition" or name == "MacroDefinition"

        if len(node.header.arguments) < 2:
            return

        for arg in node.header.arguments[1:]:
            if arg.contents.upper() != arg.contents:
                msg = "{0} must be uppercase".format(arg.contents)
                line_index = arg.line - 1
                col_index = arg.col - 1
                replacement = util.replace_word(contents[line_index],
                                                col_index,
                                                arg.contents,
                                                arg.contents.upper())
                errors.append(LinterFailure(msg, arg.line, replacement))

    ast_visitor.recurse(abstract_syntax_tree,
                        function_def=ignore.visitor_depth(_definition_visitor),
                        macro_def=ignore.visitor_depth(_definition_visitor))

    return errors
Exemplo n.º 4
0
def space_before_call(contents, abstract_syntax_tree):
    """Check that each function call is preceded by a single space."""
    errors = []

    def _call_handler(name, node):
        """Handle function calls."""
        assert name == "FunctionCall"

        line_index = node.line - 1
        col_index = node.col - 1
        end_of_name_index = col_index + len(node.name)
        num_spaces_until_openparen = 0
        index = end_of_name_index
        while contents[line_index][index] != "(":
            index += 1

        # AST checks will assert if the open paren is not found here

        num_spaces_until_openparen = index - end_of_name_index

        # Must be one space only
        if num_spaces_until_openparen != 1:
            extra_spaces = num_spaces_until_openparen - 1
            msg = "{0} extra spaces between call of {1}".format(
                extra_spaces, node.name)
            replacement = util.replace_word(contents[line_index],
                                            end_of_name_index,
                                            " " * (extra_spaces + 1), " ")
            errors.append(LinterFailure(msg, node.line, replacement))

    ast_visitor.recurse(abstract_syntax_tree,
                        function_call=ignore.visitor_depth(_call_handler))

    return errors
Exemplo n.º 5
0
 def test_body_depth(self):
     """Test node body has depth."""
     tree = ast.parse("function_call (ARGUMENT)")
     listener = MagicMock()
     wrapper = _ast_args_to_kwargs_wrapper(listener)
     ast_visitor.recurse(tree, function_call=wrapper)
     self.assertThat(listener.call_args_list[-1][1].items(),
                     Contains(("depth", 1)))
Exemplo n.º 6
0
 def test_arguments_depth(self):
     """Test arguments to a function call have depth."""
     tree = ast.parse("function_call (ARGUMENT)")
     listener = MagicMock()
     wrapper = _ast_args_to_kwargs_wrapper(listener)
     ast_visitor.recurse(tree, word=wrapper)
     self.assertThat(listener.call_args_list[-1][1].items(),
                     Contains(("depth", 2)))
Exemplo n.º 7
0
 def test_visit(self, node_type, keyword, script):
     """Visit a node."""
     tree = ast.parse(script)
     listener = MagicMock()
     wrapper = _ast_args_to_kwargs_wrapper(listener)
     keywords = {keyword: wrapper}
     ast_visitor.recurse(tree, **keywords)
     self.assertThat(listener.call_args_list[-1][1].items(),
                     Contains(("name", node_type)))
def func_args_aligned(contents, abstract_syntax_tree):
    """Check that function arguments are aligned.

    Function arguments must be aligned either to the same line or
    must fall on the same column as the last argument on the line of
    the function call. The only special case is for definitions, where we
    align after the second argument, (eg the first argument to the defined
    function or macro)
    """
    errors = []

    def _call_visitor(name, node):
        """Visit all function calls."""
        assert name == "FunctionCall"

        def _align_violations(node):
            """All alignment violations in node."""
            arguments_len = len(node.arguments)

            if arguments_len == 0:
                return

            # Check if this was a definition. If so, then we align after the
            # last argument and not after the first.
            is_definition = (_RE_IS_DEFINITION.match(node.name) is not None)

            if is_definition and arguments_len > 1:
                baseline_col = node.arguments[1].col
            else:
                baseline_col = node.arguments[0].col

            align = AlignmentInfo(col=node.arguments[0].col,
                                  line=node.arguments[0].line,
                                  line_has_kw=False)

            for index in range(0, len(node.arguments)):
                arg = node.arguments[index]
                # If the argument is on the same line as the function call,
                # then update align.col and make sure that the argument is one
                # space away from the last argument
                if align.line == arg.line:
                    yield _check_horizontal_space(node, index, contents)

                align, error = _check_alignment(arg,
                                                align,
                                                baseline_col,
                                                node.arguments[0].line,
                                                contents)

                yield error

        errors.extend([e for e in _align_violations(node) if e is not None])

    ast_visitor.recurse(abstract_syntax_tree,
                        function_call=ignore.visitor_depth(_call_visitor))
    return errors
Exemplo n.º 9
0
def func_args_aligned(contents, abstract_syntax_tree):
    """Check that function arguments are aligned.

    Function arguments must be aligned either to the same line or
    must fall on the same column as the last argument on the line of
    the function call. The only special case is for definitions, where we
    align after the second argument, (eg the first argument to the defined
    function or macro)
    """
    errors = []

    def _call_visitor(name, node):
        """Visit all function calls."""
        assert name == "FunctionCall"

        def _align_violations(node):
            """All alignment violations in node."""
            arguments_len = len(node.arguments)

            if arguments_len == 0:
                return

            # Check if this was a definition. If so, then we align after the
            # last argument and not after the first.
            is_definition = (_RE_IS_DEFINITION.match(node.name) is not None)

            if is_definition and arguments_len > 1:
                baseline_col = node.arguments[1].col
            else:
                baseline_col = node.arguments[0].col

            align = AlignmentInfo(col=node.arguments[0].col,
                                  line=node.arguments[0].line,
                                  line_has_kw=False)

            for index in range(0, len(node.arguments)):
                arg = node.arguments[index]
                # If the argument is on the same line as the function call,
                # then update align.col and make sure that the argument is one
                # space away from the last argument
                if align.line == arg.line:
                    yield _check_horizontal_space(node, index, contents)

                align, error = _check_alignment(arg, align, baseline_col,
                                                node.arguments[0].line,
                                                contents)

                yield error

        errors.extend([e for e in _align_violations(node) if e is not None])

    ast_visitor.recurse(abstract_syntax_tree,
                        function_call=ignore.visitor_depth(_call_visitor))
    return errors
def in_tree(abstract_syntax_tree):
    """Find all assigned variables in this abstract_syntax_tree."""
    variables = []

    def _call_visitor(name, node):
        """Visit all function calls."""
        assert name == "FunctionCall"

        evaluate = by_function_call(node)
        if evaluate:
            variables.append(evaluate)

    ast_visitor.recurse(abstract_syntax_tree,
                        function_call=ignore.visitor_depth(_call_visitor))

    return variables
Exemplo n.º 11
0
def in_tree(abstract_syntax_tree):
    """Find all assigned variables in this abstract_syntax_tree."""
    variables = []

    def _call_visitor(name, node):
        """Visit all function calls."""
        assert name == "FunctionCall"

        evaluate = by_function_call(node)
        if evaluate:
            variables.append(evaluate)

    ast_visitor.recurse(abstract_syntax_tree,
                        function_call=ignore.visitor_depth(_call_visitor))

    return variables
Exemplo n.º 12
0
def calls(abstract_syntax_tree, track_call):
    """Return a dict of calls mapped to where they occurred."""
    call_lines = {}

    def _call_handler(name, node, depth):
        """Visit all calls in this module."""
        assert name == "FunctionCall"

        del depth

        if track_call(node):
            _append_line_occurence(call_lines, node.name, node.line)

    ast_visitor.recurse(abstract_syntax_tree, function_call=_call_handler)

    return call_lines
Exemplo n.º 13
0
def do_print(filename):
    """Print the AST of filename."""
    with open(filename) as cmake_file:
        body = ast.parse(cmake_file.read())

        word_print = _print_details(
            lambda n: "{0} {1}".format(n.type, n.contents))
        ast_visitor.recurse(body,
                            while_stmnt=_print_details(),
                            foreach=_print_details(),
                            function_def=_print_details(),
                            macro_def=_print_details(),
                            if_block=_print_details(),
                            if_stmnt=_print_details(),
                            elseif_stmnt=_print_details(),
                            else_stmnt=_print_details(),
                            function_call=_print_details(lambda n: n.name),
                            word=word_print)
Exemplo n.º 14
0
def do_print(filename):
    """Print the AST of filename."""
    with open(filename) as cmake_file:
        body = ast.parse(cmake_file.read())

        word_print = _print_details(lambda n: "{0} {1}".format(n.type,
                                                               n.contents))
        ast_visitor.recurse(body,
                            while_stmnt=_print_details(),
                            foreach=_print_details(),
                            function_def=_print_details(),
                            macro_def=_print_details(),
                            if_block=_print_details(),
                            if_stmnt=_print_details(),
                            elseif_stmnt=_print_details(),
                            else_stmnt=_print_details(),
                            function_call=_print_details(lambda n: n.name),
                            word=word_print)
def lowercase_functions(contents, abstract_syntax_tree):
    """Check that function / macro usage is all lowercase."""
    errors = []

    def _call_handler(name, node):
        """Handle function calls."""
        assert name == "FunctionCall"

        if node.name.lower() != node.name:
            msg = "{0} is not lowercase".format(node.name)
            replacement = util.replace_word(contents[node.line - 1],
                                            node.col - 1,
                                            node.name,
                                            node.name.lower())
            errors.append(LinterFailure(msg, node.line, replacement))

    def _definition_handler(name, node):
        """Handle function calls and macro definitions."""
        assert name == "FunctionDefinition" or name == "MacroDefinition"

        definition_name_word = node.header.arguments[0]
        definition_name = definition_name_word.contents

        if definition_name.lower() != definition_name:
            msg = "{0} name {1} is not lowercase".format(name, definition_name)
            line_index = definition_name_word.line - 1
            col_index = definition_name_word.col - 1
            replacement = util.replace_word(contents[line_index],
                                            col_index,
                                            definition_name,
                                            definition_name.lower())
            errors.append(LinterFailure(msg,
                                        definition_name_word.line,
                                        replacement))

    ast_visitor.recurse(abstract_syntax_tree,
                        function_call=ignore.visitor_depth(_call_handler),
                        function_def=ignore.visitor_depth(_definition_handler),
                        macro_def=ignore.visitor_depth(_definition_handler))

    return errors
Exemplo n.º 16
0
def definitions(abstract_syntax_tree, track_definition):
    """Return a dict of definitions mapped to where they occurred."""
    definition_lines = {}

    def _definition_handler(name, node, depth):
        """Visit all definitions."""
        assert name == "FunctionDefinition" or name == "MacroDefinition"
        assert len(node.header.arguments) > 0

        del depth

        if track_definition(node):
            _append_line_occurence(definition_lines,
                                   node.header.arguments[0].contents,
                                   node.line)

    ast_visitor.recurse(abstract_syntax_tree,
                        function_def=_definition_handler,
                        macro_def=_definition_handler)

    return definition_lines
Exemplo n.º 17
0
def double_outer_quotes(contents, abstract_syntax_tree):
    """Check that all outer quotes are double quotes."""
    errors = []

    def _word_visitor(name, node):
        """Visit all arguments."""
        assert name == "Word"

        if node.type == WordType.String:
            if not _RE_DOUBLE_OUTER_QUOTES.match(node.contents):
                msg = "{0} must use double quotes".format(node.contents)
                replacement_word = "\"{0}\"".format(node.contents[1:-1])
                replacement = util.replace_word(contents[node.line - 1],
                                                node.col - 1, node.contents,
                                                replacement_word)
                errors.append(LinterFailure(msg, node.line, replacement))

    ast_visitor.recurse(abstract_syntax_tree,
                        word=ignore.visitor_depth(_word_visitor))

    return errors
def double_outer_quotes(contents, abstract_syntax_tree):
    """Check that all outer quotes are double quotes."""
    errors = []

    def _word_visitor(name, node):
        """Visit all arguments."""
        assert name == "Word"

        if node.type == WordType.String:
            if not _RE_DOUBLE_OUTER_QUOTES.match(node.contents):
                msg = "{0} must use double quotes".format(node.contents)
                replacement_word = "\"{0}\"".format(node.contents[1:-1])
                replacement = util.replace_word(contents[node.line - 1],
                                                node.col - 1,
                                                node.contents,
                                                replacement_word)
                errors.append(LinterFailure(msg, node.line, replacement))

    ast_visitor.recurse(abstract_syntax_tree,
                        word=ignore.visitor_depth(_word_visitor))

    return errors
Exemplo n.º 19
0
def definitions_namespaced(contents, abstract_syntax_tree, **kwargs):
    """Check that function and macro definitions are namespaced."""
    errors = []

    try:
        namespace = kwargs["namespace"]
    except KeyError:
        return errors

    def _definition_handler(name, node, depth):
        """Visit all definitions."""
        assert name == "FunctionDefinition" or name == "MacroDefinition"
        assert len(node.header.arguments) > 0

        del depth

        definition = node.header.arguments[0]
        def_name = definition.contents

        if not def_name.startswith((namespace, "_" + namespace)):
            msg = "Definition {0} does not start with {1}".format(def_name,
                                                                  namespace)
            replacement_name = "{0}_{1}".format(namespace, def_name)
            if def_name.startswith("_"):
                replacement_name = "_{0}".format(replacement_name)

            replacement = util.replace_word(contents[node.line - 1],
                                            definition.col - 1,
                                            def_name,
                                            replacement_name)

            errors.append(LinterFailure(msg, node.line, replacement))

    ast_visitor.recurse(abstract_syntax_tree,
                        function_def=_definition_handler,
                        macro_def=_definition_handler)

    return errors
def path_variables_quoted(contents, abstract_syntax_tree):
    """Check that each variable mutated is capitalized."""
    errors = []

    def _word_visitor(name, node, depth):
        """Visit all words."""
        assert name == "Word"

        del depth

        def _generate_error(node):
            """Generate an error and replacement for node in violation."""
            msg = "Path {0} must be quoted".format(node.contents)
            line_index = node.line - 1
            col_index = node.col - 1
            quoted = "\"{0}\""
            replacement = util.replace_word(contents[line_index],
                                            col_index,
                                            node.contents,
                                            quoted.format(node.contents))
            return LinterFailure(msg, node.line, replacement)

        if util.is_word_maybe_path(node.type):
            # CompoundLiterals definitely cannot have slashes
            if node.type == WordType.CompoundLiteral:
                if _RE_PATH_SLASH.search(node.contents):
                    errors.append(_generate_error(node))
                    return

            for always_quote_word in _ALWAYS_QUOTE_MATCHERS_INT:
                result = always_quote_word.regex.search(node.contents)
                if result is not None:
                    errors.append(_generate_error(node))
                    return

    ast_visitor.recurse(abstract_syntax_tree, word=_word_visitor)

    return errors
Exemplo n.º 21
0
def lowercase_functions(contents, abstract_syntax_tree):
    """Check that function / macro usage is all lowercase."""
    errors = []

    def _call_handler(name, node):
        """Handle function calls."""
        assert name == "FunctionCall"

        if node.name.lower() != node.name:
            msg = "{0} is not lowercase".format(node.name)
            replacement = util.replace_word(contents[node.line - 1],
                                            node.col - 1, node.name,
                                            node.name.lower())
            errors.append(LinterFailure(msg, node.line, replacement))

    def _definition_handler(name, node):
        """Handle function calls and macro definitions."""
        assert name == "FunctionDefinition" or name == "MacroDefinition"

        definition_name_word = node.header.arguments[0]
        definition_name = definition_name_word.contents

        if definition_name.lower() != definition_name:
            msg = "{0} name {1} is not lowercase".format(name, definition_name)
            line_index = definition_name_word.line - 1
            col_index = definition_name_word.col - 1
            replacement = util.replace_word(contents[line_index], col_index,
                                            definition_name,
                                            definition_name.lower())
            errors.append(
                LinterFailure(msg, definition_name_word.line, replacement))

    ast_visitor.recurse(abstract_syntax_tree,
                        function_call=ignore.visitor_depth(_call_handler),
                        function_def=ignore.visitor_depth(_definition_handler),
                        macro_def=ignore.visitor_depth(_definition_handler))

    return errors
def path_variables_quoted(contents, abstract_syntax_tree):
    """Check that each variable mutated is capitalized."""
    errors = []

    def _word_visitor(name, node, depth):
        """Visit all words."""
        assert name == "Word"

        del depth

        def _generate_error(node):
            """Generate an error and replacement for node in violation."""
            msg = "Path {0} must be quoted".format(node.contents)
            line_index = node.line - 1
            col_index = node.col - 1
            quoted = "\"{0}\""
            replacement = util.replace_word(contents[line_index], col_index,
                                            node.contents,
                                            quoted.format(node.contents))
            return LinterFailure(msg, node.line, replacement)

        if util.is_word_maybe_path(node.type):
            # CompoundLiterals definitely cannot have slashes
            if node.type == WordType.CompoundLiteral:
                if _RE_PATH_SLASH.search(node.contents):
                    errors.append(_generate_error(node))
                    return

            for always_quote_word in _ALWAYS_QUOTE_MATCHERS_INT:
                result = always_quote_word.regex.search(node.contents)
                if result is not None:
                    errors.append(_generate_error(node))
                    return

    ast_visitor.recurse(abstract_syntax_tree, word=_word_visitor)

    return errors