예제 #1
0
    def examine_fortran(self, subject: FortranSource) -> List[Issue]:
        issues = []

        for exit in subject.find_all(Fortran2003.Exit_Stmt):
            if exit.items[1] is None:
                issues.append(
                    Issue(
                        'Usage of "exit" without label indicating '
                        'which "do" construct is being exited '
                        'from.',
                        line=exit.item.span[0]))

        # Above doesn't catch exits in inline if statements
        for statement in subject.find_all(Fortran2003.If_Stmt):
            action = statement.items[1]
            if type(action
                    ) == Fortran2003.Exit_Stmt and action.items[1] is None:
                issues.append(
                    Issue(
                        'Usage of "exit" without label indicating '
                        'which "do" construct is being exited '
                        'from.',
                        line=statement.item.span[0]))

        return issues
예제 #2
0
    def examine_fortran(self, subject: FortranSource) -> List[Issue]:
        issues = []

        scope_units = subject.path('Program_Unit')
        scope_units.extend(
            subject.path([
                'Main_Program', 'Internal_Subprogram_Part',
                'Internal_Subprogram'
            ]))
        scope_units.extend(
            subject.path(
                ['Module', 'Module_Subprogram_Part', 'Module_Subprogram']))
        for scope in scope_units:
            scope_statement = subject.get_first_statement(root=scope)

            implication = subject.path(
                ['Specification_Part', 'Implicit_Part', 'Implicit_Stmt'],
                root=scope.content[1:])
            if not implication:
                nature = MissingImplicit._NATURE_MAP[scope_statement.__class__]
                name = scope_statement.items[1]
                description = "{thing} '{name}' is missing " \
                              + "an implicit statement"
                description = description.format(thing=nature, name=name)
                issues.append(
                    Issue(description, line=scope_statement.item.span[0]))
        return issues
예제 #3
0
    def examine_fortran(self, subject: FortranSource):
        issues: List[Issue] = []

        candidates: List[Fortran2003.StmtBase] = []
        # Component variables
        candidates.extend(subject.find_all(
            Fortran2003.Data_Component_Def_Stmt))
        # Variable declaration
        candidates.extend(subject.find_all(Fortran2003.Type_Declaration_Stmt))

        for candidate in candidates:

            problem = 'Use of dimension attribute for {name}.'

            attributes = candidate.items[1]
            if attributes is None:
                continue
            cannon_attr = list(
                str(item).lower().replace(' ', '')
                for item in attributes.items)
            argument_def = 'intent(in)' in cannon_attr \
                           or 'intent(out)' in cannon_attr \
                           or 'intent(inout)' in cannon_attr
            if any(x.startswith('dimension') for x in cannon_attr):
                for variable in candidate.items[2].items:
                    name = str(variable.items[0])
                    message = problem.format(name=name)
                    line_number = candidate.item.span[0]
                    issues.append(Issue(message, line=line_number))

        issues.sort(key=lambda x: (x.filename, x.line, x.description))
        return issues
예제 #4
0
    def examine(self, subject: SourceText) -> List[Issue]:
        issues = []
        text = subject.get_text()
        line_tally = 0
        for line in text.splitlines():
            line_tally += 1
            match = self._TRAILING_SPACE_PATTERN.search(line)
            if match:
                description = 'Found trailing white space'
                issues.append(Issue(description, line=line_tally))

        return issues
예제 #5
0
    def examine_fortran(self, subject: FortranSource) -> List[Issue]:
        issues = []

        for statement in subject.find_all(Fortran2003.Use_Stmt):
            module = statement.items[2]
            onlies = statement.items[4]
            if str(module).lower() not in self._ignore:
                if onlies is None:
                    description = 'Usage of "{module}" without "only" clause.'
                    issues.append(Issue(description.format(module=module),
                                        line=statement.item.span[0]))

        return issues
예제 #6
0
    def examine_fortran(self, subject: FortranSource) -> List[Issue]:
        issues = []
        for statement in subject.find_all(Fortran2003.Use_Stmt):
            module = statement.items[2]
            if str(module).lower() in self._INTRINSICS:
                nature = statement.items[0]
                if nature is None or nature.string.lower() != 'intrinsic':
                    description = 'Usage of intrinsic module "{module}" ' \
                                  'without "intrinsic" clause.'
                    issues.append(
                        Issue(description.format(module=module),
                              line=statement.item.span[0]))

        return issues
예제 #7
0
    def examine(self, subject: FortranSource) -> List[Issue]:
        issues = super(FortranRule, self).examine(subject)

        if not isinstance(subject, FortranSource):
            description = 'Non-Fortran source passed to a Fortran rule'
            raise Exception(description)

        if not subject.get_tree():
            description = "Unable to perform {} as source didn't parse: {}"
            issues.append(Issue(description.format(self.__class__.__name__,
                                                   subject.get_tree_error())))
            return issues

        issues.extend(self.examine_fortran(subject))
        return issues
예제 #8
0
    def examine(self, subject: FortranSource) -> List[Issue]:
        # pylint: disable=too-many-branches
        """
        Examines the source code for none Fortran characters.

        This is complicated by the fact that the source must consist of only
        certain characters except comments and strings. These may contain
        anything.
        """
        issues = super(FortranCharacterset, self).examine(subject)

        text = subject.get_text()
        index = 0
        line = 1
        state = 'code'
        while index < len(text):
            character = text[index]
            if state == 'code':
                if character == self._NEWLINE:
                    line += 1
                elif character == self._EXCLAMATION:
                    state = 'comment'
                elif character == self._APOSTROPHY:
                    state = 'apostraphystring'
                elif character == self._QUOTE:
                    state = 'quotestring'
                elif character in self._FORTRAN_CHARACTERSET:
                    pass
                else:
                    description = "Found character {char} " \
                                  + "not in Fortran character set"
                    description = description.format(char=repr(character))
                    issues.append(Issue(description, line=line))
            elif state == 'comment':
                if character == self._NEWLINE:
                    line += 1
                    state = 'code'
            elif state == 'apostraphystring':
                if character == self._APOSTROPHY:
                    state = 'code'
            elif state == 'quotestring':
                if character == self._QUOTE:
                    state = 'code'
            else:
                raise Exception('Parser in unknown state: ' + state)
            index += 1

        return issues
예제 #9
0
    def examine_fortran(self, subject: FortranSource) -> List[Issue]:
        issues = []

        # Collect all variable declarations
        declarations: List[Fortran2003.Type_Declaration_Stmt] = list(
            subject.find_all(Fortran2003.Type_Declaration_Stmt))

        # keep only the ones in subroutines
        declarations = [
            declaration for declaration in declarations if isinstance(
                declaration.parent.parent, Fortran2003.Subroutine_Subprogram)
        ]

        for declaration in declarations:
            type_spec = declaration.items[0]
            # If not a character, no concern
            if not type_spec.items[0] == "CHARACTER":
                continue
            param_value = type_spec.items[1]
            # This might be a length selector, if so get the param value
            if isinstance(param_value, Fortran2003.Length_Selector):
                param_value = param_value.items[1]
            # If not an auto length, no concern
            if not param_value.string == "*":
                continue
            attr_spec_list = declaration.items[1]
            # If no attributes specified, no concern
            if attr_spec_list is None:
                continue
            # Get intent attr and not other attributes
            intent_attr = None
            for item in attr_spec_list.items:
                if isinstance(item, Fortran2003.Intent_Attr_Spec):
                    intent_attr = item
                    break
            # If no intent, no conecern
            # Ensuring arguments specify intent should be enforced elsewhere
            if intent_attr is None:
                continue
            # Intent in, no conern
            if intent_attr.items[1].string == "IN":
                continue
            issues.append(
                Issue(self._message(declaration.items[2].string,
                                    intent_attr.items[1]),
                      line=declaration.item.span[0]))

        return issues
예제 #10
0
    def examine(self, subject: SourceText) -> List[Issue]:
        """
        Examines the text for white space at the end of lines.
        This includes lines which consist entirely of white space.
        """
        issues = []
        text = subject.get_text()
        line_tally = 0
        for line in text.splitlines():
            line_tally += 1
            match = self._TRAILING_SPACE_PATTERN.search(line)
            if match:
                description = 'Found trailing white space'
                issues.append(Issue(description, line=line_tally))

        return issues
예제 #11
0
    def examine(self, subject: FortranSource) -> List[Issue]:
        # pylint: disable=too-many-branches
        """
        Examines the source code for none Fortran characters.

        This is complicated by the fact that the source must consist of only
        certain characters except comments and strings. These may contain
        anything.
        """
        issues = super(FortranOldComparisons, self).examine(subject)

        text = subject.get_text()
        index = 0
        line = 1
        state = 'code'
        while index < len(text) - 4:
            characters = text[index:index + 4]
            if state == 'code':
                if characters[0] == self._NEWLINE:
                    line += 1
                elif characters[0] == self._EXCLAMATION:
                    state = 'comment'
                elif characters[0] == self._APOSTROPHY:
                    state = 'apostraphystring'
                elif characters[0] == self._QUOTE:
                    state = 'quotestring'
                elif characters == ".eq." or characters == ".ne." or characters == ".gt." or characters == ".lt." or characters == ".ge." or characters == ".le.":
                    description = "Found " + characters
                    issues.append(Issue(description, line=line))
                elif characters[0] in self._FORTRAN_CHARACTERSET:
                    pass
            elif state == 'comment':
                if characters[0] == self._NEWLINE:
                    line += 1
                    state = 'code'
            elif state == 'apostraphystring':
                if characters[0] == self._APOSTROPHY:
                    state = 'code'
            elif state == 'quotestring':
                if characters[0] == self._QUOTE:
                    state = 'code'
            else:
                raise Exception('Parser in unknown state: ' + state)
            index += 1

        return issues
예제 #12
0
    def examine_fortran(self, subject: FortranSource) -> List[Issue]:
        issues = []

        text = subject.get_text()
        index = 0
        line = 1
        state = 'code'
        while index < len(text):
            character = text[index]
            if character == self._NEWLINE:
                line += 1
            elif character in self._FORTRAN_CHARACTERSET:
                pass
            else:
                description = "Found tab"
                issues.append(Issue(description, line=line))
            index += 1
        return issues
예제 #13
0
    def examine(self, subject: FortranSource) -> List[Issue]:
        issues = []

        text = subject.get_text()
        index = 0
        line = 1
        state = 'code'
        while index < len(text):
            character = text[index]
            if state == 'code':
                if character == self._NEWLINE:
                    line += 1
                elif character == self._EXCLAMATION:
                    state = 'comment'
                elif character == self._APOSTROPHY:
                    state = 'apostraphystring'
                elif character == self._QUOTE:
                    state = 'quotestring'
                elif character in self._FORTRAN_CHARACTERSET:
                    pass
                else:
                    description = "Found character {char} " \
                                  + "not in Fortran character set"
                    description = description.format(char=repr(character))
                    issues.append(Issue(description, line=line))
            elif state == 'comment':
                if character == self._NEWLINE:
                    line += 1
                    state = 'code'
            elif state == 'apostraphystring':
                if character == self._APOSTROPHY:
                    state = 'code'
            elif state == 'quotestring':
                if character == self._QUOTE:
                    state = 'code'
            else:
                raise Exception('Parser in unknown state: ' + state)
            index += 1

        return issues
예제 #14
0
    def examine_fortran(self, subject: FortranSource) -> List[Issue]:
        issues: List[Issue] = []

        candidates: List[Fortran2003.Base] = []
        # Component variables
        candidates.extend(subject.find_all(
            Fortran2003.Data_Component_Def_Stmt))
        # Component Procedure
        candidates.extend(subject.find_all(
            Fortran2003.Proc_Component_Def_Stmt))
        # Procedure declaration
        candidates.extend(
            subject.find_all(Fortran2003.Procedure_Declaration_Stmt))
        # Variable declaration
        candidates.extend(subject.find_all(Fortran2003.Type_Declaration_Stmt))

        for candidate in candidates:
            if isinstance(
                    candidate,  # Is variable
                (Fortran2003.Data_Component_Def_Stmt,
                 Fortran2003.Type_Declaration_Stmt)):
                type_spec: Fortran2003.Intrinsic_Type_Spec = candidate.items[0]
                kind_selector: Fortran2003.Kind_Selector = type_spec.items[1]

                if isinstance(kind_selector, Fortran2003.Kind_Selector):
                    data_type: str = type_spec.items[0].lower()
                    kind: str = kind_selector.string.strip('()')
                    match = self._patterns[data_type].match(kind)
                    if match is None:
                        entity_declaration = candidate.items[2]
                        message = self._ISSUE_TEMPLATE.format(
                            type=data_type,
                            kind=kind,
                            name=entity_declaration,
                            pattern=self._patterns[data_type].pattern)
                        issues.append(
                            Issue(message, line=candidate.item.span[0]))

        issues.sort(key=lambda x: (x.filename, x.line, x.description))
        return issues
예제 #15
0
    def examine(self, subject: FortranSource) -> List[Issue]:
        """
        Base for rules which scruitinise the parse tree of Fortran source.

        :param subject: Source file to examine.
        :return: Issues found with the source.
        """
        issues = []
        if not isinstance(subject, FortranSource):
            description = 'Non-Fortran source passed to a Fortran rule'
            raise Exception(description)

        if not subject.get_tree():
            description = "Unable to perform {} as source didn't parse: {}"
            issues.append(
                Issue(
                    description.format(self.__class__.__name__,
                                       subject.get_tree_error())))
            return issues

        issues.extend(self.examine_fortran(subject))
        return issues
예제 #16
0
    def examine_fortran(self, subject: FortranSource) -> List[Issue]:
        issues: List[Issue] = []
        scope_units: List[Fortran2003.Block] = []

        # get all subprograms (functions and subroutines) within programs
        scope_units.extend(
            subject.path([
                'Main_Program', 'Internal_Subprogram_Part',
                'Internal_Subprogram'
            ]))

        # get all subprograms within modules
        scope_units.extend(
            subject.path(
                ['Module', 'Module_Subprogram_Part', 'Module_Subprogram']))

        # get all naked subprograms
        scope_units.extend(subject.path(['Function_Subprogram']))
        scope_units.extend(subject.path(['Subroutine_Subprogram']))

        for scope in scope_units:
            # get initialisation statement and piece containing all type
            # declarations

            specs = None
            for part in scope.children:
                if type(part) in (Fortran2003.Function_Stmt,
                                  Fortran2003.Subroutine_Stmt):
                    stmt = part
                if type(part) == Fortran2003.Specification_Part:
                    specs = part
                    # we don't need to check the rest of the children
                    break

            # covert tree node into a python list
            dummy_arg_list = stmt.children[2]

            # initialise set in case empty
            dummy_args: List[str] = []
            if dummy_arg_list is not None:
                dummy_args = [
                    arg.string.lower() for arg in dummy_arg_list.children
                ]

            if specs is not None:
                for spec in specs.children:
                    if spec.__class__ == Fortran2008.Type_Declaration_Stmt:

                        # check if type declaration has an intent
                        attributes = spec.children[1]
                        if attributes is not None:
                            for attribute in spec.children[1].children:
                                if attribute.__class__ == \
                                        Fortran2003.Intent_Attr_Spec:

                                    # if so, remove argument names from
                                    # dummy_args
                                    for arg in spec.children[2].children:
                                        arg_name = arg.children[
                                            0].string.lower()
                                        if arg_name in dummy_args:
                                            dummy_args.remove(arg_name)

            # get the type of block
            if type(scope) == Fortran2003.Subroutine_Subprogram:
                unit_type = 'subroutine'
            elif type(scope) == Fortran2003.Function_Subprogram:
                unit_type = 'function'

            # any remaining dummy arguments lack intent
            for arg in dummy_args:
                description = f'Dummy argument "{arg}" of {unit_type} "' \
                              f'{stmt.children[1].string}" is missing an ' \
                              f'"intent" statement'
                issues.append(Issue(description, line=stmt.item.span[0]))

        return issues
예제 #17
0
    def examine_fortran(self, subject: FortranSource):
        issues: List[Issue] = []

        candidates: List[Fortran2003.StmtBase] = []
        # Component variables
        candidates.extend(subject.find_all(
            Fortran2003.Data_Component_Def_Stmt))
        # Component Procedure
        candidates.extend(subject.find_all(
            Fortran2003.Proc_Component_Def_Stmt))
        # Procedure declaration
        candidates.extend(
            subject.find_all(Fortran2003.Procedure_Declaration_Stmt))
        # Variable declaration
        candidates.extend(subject.find_all(Fortran2003.Type_Declaration_Stmt))

        return_variable = ''
        for candidate in candidates:
            if isinstance(
                    candidate,  # Is variable
                (Fortran2003.Data_Component_Def_Stmt,
                 Fortran2003.Type_Declaration_Stmt)):
                if isinstance(candidate.parent.parent,
                              Fortran2003.Function_Subprogram):
                    # Is contained in a function
                    function_block: Fortran2003.Function_Subprogram \
                        = candidate.parent.parent
                    statement = None
                    for thing in function_block.children:
                        if isinstance(thing, Fortran2003.Function_Stmt):
                            statement = thing
                            break
                    if statement is None:
                        message = "Malformed parse tree: Function subprogram" \
                                  " without Function statement"
                        raise Exception(message)
                    suffix = statement.items[3]
                    if isinstance(suffix, Fortran2003.Suffix):
                        if isinstance(suffix.items[0], Fortran2003.Name):
                            # Is return variable
                            return_variable = str(suffix.items[0])

            problem = 'Declaration of pointer "{name}" without initialisation.'

            attributes = candidate.items[1]
            if attributes is None:
                continue
            cannon_attr = list(
                str(item).lower().replace(' ', '')
                for item in attributes.items)
            argument_def = 'intent(in)' in cannon_attr \
                           or 'intent(out)' in cannon_attr \
                           or 'intent(inout)' in cannon_attr
            if 'pointer' in cannon_attr and not argument_def:
                for variable in candidate.items[2].items:
                    if isinstance(
                            candidate,  # Is variable
                        (Fortran2003.Data_Component_Def_Stmt,
                         Fortran2003.Type_Declaration_Stmt)):
                        name = str(variable.items[0])
                        if name == return_variable:
                            continue  # Return variables cannot be initialised.
                        init = variable.items[3]
                        if init is None:
                            message = problem.format(name=name)
                            line_number = candidate.item.span[0]
                            issues.append(Issue(message, line=line_number))
                    elif isinstance(
                            candidate,  # Is procedure
                        (Fortran2003.Proc_Component_Def_Stmt,
                         Fortran2003.Procedure_Declaration_Stmt)):
                        name = str(variable)
                        if isinstance(variable, Fortran2003.Name):
                            line_number = candidate.item.span[0]
                            message = problem.format(name=name)
                            issues.append(Issue(message, line=line_number))
                    else:
                        message \
                            = f"Unexpected source element: {repr(candidate)}"
                        raise Exception(message)

        issues.sort(key=lambda x: (x.filename, x.line, x.description))
        return issues