Skip to content

Commit

Permalink
Don't list contained subroutines or subroutines in the current module…
Browse files Browse the repository at this point in the history
… as external dependencies.
  • Loading branch information
hiker committed Feb 20, 2025
1 parent be0e919 commit 5cb4fca
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 6 deletions.
35 changes: 33 additions & 2 deletions source/fab/parse/fortran.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from fparser.two.Fortran2003 import ( # type: ignore
Entity_Decl_List, Use_Stmt, Module_Stmt, Program_Stmt, Subroutine_Stmt, Function_Stmt, Language_Binding_Spec,
Char_Literal_Constant, Interface_Block, Name, Comment, Module, Call_Stmt, Derived_Type_Def, Derived_Type_Stmt,
Type_Attr_Spec_List, Type_Attr_Spec, Type_Name)
Type_Attr_Spec_List, Type_Attr_Spec, Type_Name, Subroutine_Subprogram, Function_Subprogram)
from fparser.two.utils import walk # type: ignore

# todo: what else should we be importing from 2008 instead of 2003? This seems fragile.
Expand Down Expand Up @@ -220,7 +220,38 @@ def walk_nodes(self, fpath, file_hash, node_tree) -> AnalysedFortran:
# called_name will be None for calls like thing%method(),
# which is fine as it doesn't reveal a dependency on an external function.
if called_name:
analysed_fortran.add_symbol_dep(called_name.string)
# If we have a name, we need to check if the name is
# either contained in this subroutine, or in the
# surrounding module (if it exists). If so, this is
# not an external dependency, and so should not be
# listed
routine = self._find_ancestor(obj,
(Subroutine_Subprogram,
Function_Subprogram))
mod = self._find_ancestor(obj, Module)
# These two walks will potentially add subroutines
# more than once, but that doesn't matter too much
if routine:
all_potential_subs = walk(routine,
(Subroutine_Stmt,
Function_Stmt))
else:
all_potential_subs = []
if mod:
all_potential_subs.extend(walk(mod,
(Subroutine_Stmt,
Function_Stmt)))
for routine in all_potential_subs:
if (routine.get_name().string.lower()
== called_name.string.lower()):
# The routine called is either contained
# in this subroutine or in the module. Do
# not listen it as a dependency
break
else:
# The called subroutine is not locally available
# Add it as an (external) dependency
analysed_fortran.add_symbol_dep(called_name.string)

elif obj_type == Program_Stmt:
analysed_fortran.add_program_def(str(obj.get_name()))
Expand Down
14 changes: 10 additions & 4 deletions tests/unit_tests/parse/fortran/test_contained_subroutine.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
# which you should have received as part of this distribution
# ##############################################################################

'''This module tests if the Fortran analyser handles contained subroutines and
subroutines in the same module - none of which should be listed as external
dependency.
'''

from pathlib import Path
import pytest

Expand All @@ -21,7 +26,7 @@


@pytest.mark.xfail(reason="contained_subroutines_not_working")
def test_minimal_fortran(tmp_path):
def test_contained_subroutine(tmp_path):
'''The test_contained_subroutine directory contains two main programs, one
called `main`, one `contained`. The first one uses `mod_with_contain`,
which calls a `contained` subroutine `contained`. This test makes sure
Expand Down Expand Up @@ -57,11 +62,12 @@ def test_minimal_fortran(tmp_path):

# Test that the main program is not added as a dependency - a main
# program should never be used when trying to resolve dependencies.
assert af_contained is None
# assert af_contained is None

# The module should not contain any dependencies, the dependency to
# `contained` is resolved from the subroutine included.
assert af_mod_with_contain.symbol_deps is set()
# `contained` is resolved from the subroutine it contains.
assert af_mod_with_contain.symbol_deps == set()

# Just in case, also compile and link
compile_fortran(config)
link_exe(config)

0 comments on commit 5cb4fca

Please sign in to comment.