Ejemplo n.º 1
0
    def visit_ClassDef(self, node):
        position = self.get_metadata(PositionProvider, node)
        length = position.length - 1  # Minus one for the class' header

        if True:
            docstring = node.get_docstring()
            if docstring is not None:
                length -= len(
                    docstring.splitlines()) + 2  # Docstring delimiters

            methods = findall(node, FunctionDef())
            for method in methods:
                docstring = method.get_docstring()
                if docstring is not None:
                    length -= len(
                        docstring.splitlines()) + 2  # Docstring delimiters

        if True:
            comments = findall(node, Comment())
            for comment in comments:
                parent_node = self.get_metadata(ParentNodeProvider, comment)
                if isinstance(parent_node,
                              EmptyLine):  # The comment is not inline
                    length -= 1  # One comment = one line

        if length > 200:
            self.add_error('class-too-long', node=node, args=(length, 200))
Ejemplo n.º 2
0
    def visit_FunctionDef(self, node):
        scope = self.get_metadata(ScopeProvider, node)
        for index, param_node in enumerate(node.params.all_params):
            param_name = param_node.name.value

            # Check the first parameter of non static methods
            if index == 0 and isinstance(scope, ClassScope) and not findall(
                    node, self.MATCHER_STATICMETHOD):
                if findall(node, self.MATCHER_CLASSMETHOD):
                    if param_name != 'cls':
                        self.add_error('bad-parameter-name',
                                       node=param_node,
                                       args=(param_name, "should be 'cls'"))
                elif param_name != 'self':
                    self.add_error('bad-parameter-name',
                                   node=param_node,
                                   args=(param_name, "should be 'self'"))
            elif getattr(builtins, param_name, None):
                self.add_error('builtin-parameter-name',
                               node=param_node,
                               args=(param_name, ))
            elif not self.CRE_FORMATS['snake_case'].match(param_name):
                self.add_error('invalid-parameter-name',
                               node=param_node,
                               args=(param_name, 'snake_case'))
            elif param_name in self.BAD_PARAMETER_NAMES:
                self.add_error('bad-parameter-name',
                               node=param_node,
                               args=(param_name, 'is blacklisted'))
Ejemplo n.º 3
0
    def visit_FunctionDef(self, node):
        position = self.get_metadata(PositionProvider, node)
        length = position.length - 1  # Minus one for the function's signature

        if True:
            docstring = node.get_docstring()
            if docstring is not None:
                length -= len(
                    docstring.splitlines()) + 2  # Docstring delimiters

            # We might have nested functions that are documented...
            nested_functions = findall(node, FunctionDef())
            for nested_function in nested_functions:
                docstring = nested_function.get_docstring()
                if docstring is not None:
                    length -= len(
                        docstring.splitlines()) + 2  # Docstring delimiters

        if True:
            comments = findall(node, Comment())
            for comment in comments:
                parent_node = self.get_metadata(ParentNodeProvider, comment)
                if isinstance(parent_node,
                              EmptyLine):  # The comment is not inline
                    length -= 1  # One comment = one line

        if length > 25:
            self.add_error('function-too-long', node=node, args=(length, 25))
Ejemplo n.º 4
0
 def test_findall_with_sentinels(self) -> None:
     # Verify behavior when provided a sentinel
     nothing = findall(cst.RemovalSentinel.REMOVE,
                       m.Name("True") | m.Name("False"))
     self.assertNodeSequenceEqual(nothing, [])
     nothing = findall(cst.MaybeSentinel.DEFAULT,
                       m.Name("True") | m.Name("False"))
     self.assertNodeSequenceEqual(nothing, [])
Ejemplo n.º 5
0
    def test_findall_with_metadata_wrapper(self) -> None:
        # Find all assignments in a tree
        code = """
            a = 1
            b = True

            def foo(bar: int) -> bool:
                return False
        """

        module = cst.parse_module(dedent(code))
        wrapper = meta.MetadataWrapper(module)

        # Test that when we find over a wrapper, we implicitly use it for
        # metadata as well as traversal.
        booleans = findall(
            wrapper,
            m.MatchMetadata(meta.ExpressionContextProvider,
                            meta.ExpressionContext.STORE),
        )
        self.assertNodeSequenceEqual(
            booleans,
            [
                cst.Name("a"),
                cst.Name("b"),
                cst.Name("foo"),
                cst.Name("bar"),
            ],
        )

        # Test that we can provide an explicit resolver and tree
        booleans = findall(
            wrapper.module,
            m.MatchMetadata(meta.ExpressionContextProvider,
                            meta.ExpressionContext.STORE),
            metadata_resolver=wrapper,
        )
        self.assertNodeSequenceEqual(
            booleans,
            [
                cst.Name("a"),
                cst.Name("b"),
                cst.Name("foo"),
                cst.Name("bar"),
            ],
        )

        # Test that failing to provide metadata leads to no match
        booleans = findall(
            wrapper.module,
            m.MatchMetadata(meta.ExpressionContextProvider,
                            meta.ExpressionContext.STORE),
        )
        self.assertNodeSequenceEqual(booleans, [])
Ejemplo n.º 6
0
 def find_required_modules(all_types):
     req_mod = set()
     for _, a_node in all_types:
         m = match.findall(
             a_node.annotation,
             match.Attribute(value=match.DoNotCare(),
                             attr=match.DoNotCare()))
         if len(m) != 0:
             for i in m:
                 req_mod.add([
                     n.value for n in match.findall(
                         i, match.Name(value=match.DoNotCare()))
                 ][0])
     return req_mod
Ejemplo n.º 7
0
    def __extract_names_multi_assign(self, elements):
        # Add self vars. in tuple assignments, e.g. self.x, self.y = 1, 2
        # Adds variables in tuple(s) in multiple assignments, e.g. a, (b, c) = 1, (2, 3)
        names: List[cst.Name] = []
        i = 0
        while i < len(elements):
            if match.matches(
                    elements[i],
                    match.Element(value=match.Name(value=match.DoNotCare()))):
                names.append(elements[i].value)
            elif match.matches(
                    elements[i],
                    match.Element(value=match.Attribute(attr=match.Name(
                        value=match.DoNotCare())))):
                names.append(elements[i].value)
            elif match.matches(
                    elements[i],
                    match.Element(value=match.Tuple(
                        elements=match.DoNotCare()))):
                elements.extend(
                    match.findall(
                        elements[i].value,
                        match.Element(value=match.OneOf(
                            match.Attribute(attr=match.Name(
                                value=match.DoNotCare())),
                            match.Name(value=match.DoNotCare())))))
            i += 1

        return names
Ejemplo n.º 8
0
 def discard_empty_else_blocks(self, _, updated_node):
     # An `else: pass` block can always simply be discarded, and libcst ensures
     # that an Else node can only ever occur attached to an If, While, For, or Try
     # node; in each case `None` is the valid way to represent "no else block".
     if m.findall(updated_node, m.Comment()):
         return updated_node  # If there are any comments, keep the node
     return cst.RemoveFromParent()
Ejemplo n.º 9
0
    def visit_FunctionDef(self, node):
        scope = self.get_metadata(ScopeProvider, node)
        if isinstance(scope, (FunctionScope, ClassScope)):
            offset = 1
        elif isinstance(scope, GlobalScope):
            offset = 2

        parent_node = self.get_metadata(ParentNodeProvider, node)
        parent_position = self.get_metadata(PositionProvider, parent_node)
        position = self.get_metadata(PositionProvider, node)
        # A module is actually counted as one line longer than it is,
        # so we use offset to take that in account
        # This gives us '+ 0' for functions and classes
        # and '+ 1' for modules
        if position.end.line + (offset - 1) == parent_position.end.line:
            return

        # Finds true empty lines (no comments) following the function
        matcher = EmptyLine(comment=None,
                            metadata=MatchMetadataIfTrue(
                                PositionProvider,
                                lambda p: position.end.line < p.start.line <=
                                position.end.line + offset + 1))
        nodes_count = len(findall(parent_node, matcher,
                                  metadata_resolver=self))

        if nodes_count > offset:
            self.add_error('extra-trailing-function-newline',
                           node=node,
                           args=(node.name.value, ))
        elif nodes_count < offset:
            self.add_error('missing-trailing-function-newline',
                           node=node,
                           args=(node.name.value, ))
Ejemplo n.º 10
0
def extract_names_from_type_annot(type_annot: str):
    """
    Extracts all the names/identifiers from a type annotation
    """

    return [
        n.value for n in match.findall(cst.parse_expression(type_annot),
                                       match.Name(value=match.DoNotCare()))
    ]
Ejemplo n.º 11
0
    def test_simple_findall(self) -> None:
        # Find all booleans in a tree
        code = """
            a = 1
            b = True

            def foo(bar: int) -> bool:
                return False
        """

        module = cst.parse_module(dedent(code))
        booleans = findall(module, m.Name("True") | m.Name("False"))
        self.assertNodeSequenceEqual(booleans, [cst.Name("True"), cst.Name("False")])
Ejemplo n.º 12
0
    def visit_SimpleStatementLine(self, node: cst.SimpleStatementLine):
        smt_names = [
            n.value for n in match.findall(
                node,
                match.Name(
                    value=match.SaveMatchedNode(match.DoNotCare(), 'name')))
        ]

        if len(self.stack) > 0:
            self.fn_may_args_var_use.append(smt_names)

        if len(self.cls_stack) > 0:
            if self.cls_stack[0].name in smt_names:
                self.cls_may_vars_use.append(smt_names)

        self.__find_module_vars_use(smt_names)
Ejemplo n.º 13
0
    def visit_If(self, node: cst.If):
        if_names = [
            n.value for n in match.findall(
                node.test,
                match.Name(
                    value=match.SaveMatchedNode(match.DoNotCare(), 'name')))
        ]

        if len(self.cls_stack) > 0:
            if self.cls_stack[0].name in if_names:
                self.cls_may_vars_use.append(if_names)

        if len(self.stack) > 0:
            self.fn_may_args_var_use.append(if_names)

        self.__find_module_vars_use(if_names)
Ejemplo n.º 14
0
    def visit_TrailingWhitespace(self, node):
        if node.comment is not None:
            return
        if node.whitespace.value == '':
            return

        position = self.get_metadata(PositionProvider, node)
        parent_node = self.get_metadata(ParentNodeProvider, node)

        matcher = RightCurlyBrace(
            metadata=MatchMetadataIfTrue(
                PositionProvider,
                lambda p: position.start.line == p.start.line and position.start.column == p.end.column
            )
        )
        matches = findall(parent_node, matcher, metadata_resolver=self)
        if matches:
            self.add_error('extra-trailing-brace-whitespace', node=matches[0], args=('}',))
Ejemplo n.º 15
0
    def visit_With(self, node: cst.With):
        with_names = [
            n.value for n in match.findall(
                match.extract(
                    node,
                    match.With(items=match.SaveMatchedNode(
                        match.DoNotCare(), 'with_items')))['with_items'][0],
                match.Name(
                    value=match.SaveMatchedNode(match.DoNotCare(), 'name')))
        ]
        if len(self.stack) > 0:
            self.fn_may_args_var_use.append(with_names)

        if len(self.cls_stack) > 0:
            if self.cls_stack[0].name in with_names:
                self.cls_may_vars_use.append(with_names)

        self.__find_module_vars_use(with_names)
Ejemplo n.º 16
0
    def visit_FunctionDef(self, node):
        complexity = 1

        for child_node in findall(node, OneOf(*self.NODES)):
            class_name = child_node.__class__.__name__

            if class_name == 'Try':
                complexity += len(child_node.handlers) + bool(child_node.orelse)
            elif class_name == 'BooleanOperation':
                complexity += 1
            elif class_name in {'If', 'IfExp', 'Assert'}:
                complexity += 1
            elif class_name in {'For', 'While'}:
                complexity += bool(child_node.orelse) + 1
            elif class_name == 'CompFor':
                complexity += len(child_node.ifs) + 1

        if complexity > 10:
            self.add_error('function-too-complex', node=node, args=(complexity, 10))
Ejemplo n.º 17
0
def _line_ranges_spanned_by_format_strings(
    source: str, ) -> Dict[libcst.CSTNode, LineRange]:
    def _code_range_to_line_range(
        code_range: libcst._position.CodeRange, ) -> LineRange:
        return code_range.start.line, code_range.end.line

    try:
        wrapper = libcst.metadata.MetadataWrapper(libcst.parse_module(source))
    except libcst._exceptions.ParserSyntaxError as exception:
        # NOTE: This should not happen. If a file is unparseable for libcst, it
        # would probably have been unparseable for Pyre as well. In that case,
        # we would not have raised a 404 parse error and not reached here in the
        # first place. Still, catch the exception and just skip the special
        # handling of format strings.
        LOG.warning("Not moving out fixmes from f-strings because"
                    f" libcst failed to parse the file: {exception}")
        return {}

    position_map = wrapper.resolve(libcst.metadata.PositionProvider)
    return {
        format_string: _code_range_to_line_range(position_map[format_string])
        for format_string in libcst_matchers.findall(
            wrapper.module, libcst_matchers.FormattedString())
    }
Ejemplo n.º 18
0
def remove_trailing_comma(node):
    # Remove the comma from this node, *unless* it's already a comma node with comments
    if node.comma is cst.MaybeSentinel.DEFAULT or m.findall(node, m.Comment()):
        return node
    return node.with_changes(comma=cst.MaybeSentinel.DEFAULT)
Ejemplo n.º 19
0
 def visit_ImportFrom(self, node: cst.ImportFrom):
     if node.module is not None:
         self.imports.extend([
             n.value for n in match.findall(
                 node.module, match.Name(value=match.DoNotCare()))
         ])