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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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