From e6770b63b2be1bb1aa1f59f59778dc2ac8f3cf51 Mon Sep 17 00:00:00 2001 From: gnikit Date: Thu, 19 May 2022 23:43:59 +0100 Subject: [PATCH 1/2] Fixes Keyword modifiers in module procedures/functions cause incorrect parsing #119 --- .coveragerc | 4 ++- fortls/parse_fortran.py | 13 +++++++-- test/test_server_diagnostics.py | 13 +++++++++ test/test_server_hover.py | 21 ++++++++++++++ .../test_source/diag/test_scope_overreach.f90 | 28 +++++++++++++++++++ 5 files changed, 76 insertions(+), 3 deletions(-) create mode 100644 test/test_source/diag/test_scope_overreach.f90 diff --git a/.coveragerc b/.coveragerc index 4109acf6..26deb54b 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,5 +1,4 @@ [run] -dynamic_context = test_function omit = fortls/__init__.py fortls/version.py @@ -10,3 +9,6 @@ exclude_lines = log.debug except: if not PY3K: + +[html] +show_contexts = True diff --git a/fortls/parse_fortran.py b/fortls/parse_fortran.py index 641fb40f..545edbb2 100644 --- a/fortls/parse_fortran.py +++ b/fortls/parse_fortran.py @@ -532,7 +532,12 @@ def read_generic_def(line: str): def read_mod_def(line: str): - """Attempt to read MODULE and MODULE PROCEDURE definition lines""" + """Attempt to read MODULE and MODULE PROCEDURE, MODULE FUNCTION definition lines""" + # Get all the keyword modifier mathces + keywords = re.findall(FRegex.SUB_MOD, line) + # remove modifiers from line + line = re.sub(FRegex.SUB_MOD, "", line) + mod_match = FRegex.MOD.match(line) if mod_match is None: return None @@ -547,15 +552,19 @@ def read_mod_def(line: str): return "int_pro", pro_names # Check for submodule definition trailing_line = line[mod_match.start(1) :] + # module procedure sub_res = read_sub_def(trailing_line, mod_flag=True) if sub_res is not None: return sub_res + # module function fun_res = read_var_def(trailing_line, fun_only=True) if fun_res is not None: fun_res[1].mod_flag = True - return fun_res[0], fun_res[1] + fun_res[1].keywords = keywords + return fun_res fun_res = read_fun_def(trailing_line, mod_flag=True) if fun_res is not None: + fun_res[1].keywords = keywords return fun_res return "mod", name diff --git a/test/test_server_diagnostics.py b/test/test_server_diagnostics.py index fbf3ceb1..d6da6da4 100644 --- a/test/test_server_diagnostics.py +++ b/test/test_server_diagnostics.py @@ -391,3 +391,16 @@ def test_function(): "severity": 1, } ] + + +def test_submodule_scopes(): + """Test that submodule procedures and functions with modifier keywords are correctly + parsed and their scopes correctly closed.""" + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir / "diag")}) + file_path = str(test_dir / "diag" / "test_scope_overreach.f90") + string += write_rpc_notification( + "textDocument/didOpen", {"textDocument": {"uri": file_path}} + ) + errcode, results = run_request(string, ["-n", "1"]) + assert errcode == 0 + assert results[1]["diagnostics"] == [] diff --git a/test/test_server_hover.py b/test/test_server_hover.py index 7c3ffc09..7fd3f523 100644 --- a/test/test_server_hover.py +++ b/test/test_server_hover.py @@ -327,3 +327,24 @@ def test_hover_block(): assert errcode == 0 ref_results = ["REAL, DIMENSION(5)", "REAL"] validate_hover(results, ref_results) + + +def test_hover_submodule_procedure(): + """Test that submodule procedures and functions with modifier keywords + are correctly displayed when hovering. + """ + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir / "diag")}) + file_path = test_dir / "diag" / "test_scope_overreach.f90" + string += hover_req(file_path, 18, 37) + string += hover_req(file_path, 23, 37) + errcode, results = run_request(string, fortls_args=["-n", "1"]) + assert errcode == 0 + ref_results = [ + """PURE RECURSIVE FUNCTION foo_sp(x) RESULT(fi) + REAL(sp), INTENT(IN) :: x + REAL(sp) :: fi""", + """PURE RECURSIVE FUNCTION foo_dp(x) RESULT(fi) + REAL(dp), INTENT(IN) :: x + REAL(dp) :: fi""", + ] + validate_hover(results, ref_results) diff --git a/test/test_source/diag/test_scope_overreach.f90 b/test/test_source/diag/test_scope_overreach.f90 new file mode 100644 index 00000000..3604e6b5 --- /dev/null +++ b/test/test_source/diag/test_scope_overreach.f90 @@ -0,0 +1,28 @@ +module m + interface + module subroutine sub(arg) + integer :: arg + end subroutine + end interface +end module m + +submodule (m) n + + use, intrinsic :: iso_fortran_env, only: int8, int16, int32, int64 + implicit none + + integer, parameter :: sp = selected_real_kind(6) + integer, parameter :: dp = selected_real_kind(15) + +contains + + pure recursive module function foo_sp(x) result(fi) + real(sp), intent(in) :: x + real(sp) :: fi + end function foo_sp + + pure recursive module function foo_dp(x) result(fi) + real(dp), intent(in) :: x + real(dp) :: fi + end function foo_dp +end submodule n From caaf856496e12bbf736a866c4de9cb9a745546a1 Mon Sep 17 00:00:00 2001 From: gnikit Date: Thu, 19 May 2022 23:45:33 +0100 Subject: [PATCH 2/2] Update CHANGELOG.md --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9eb2f7d3..815433e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,11 @@ - Redesigned the `fortls` website to be more aesthetically pleasing and user-friendly ([#112](https://github.com/gnikit/fortls/issues/112)) +### Fixed + +- Fixed bug where submodule procedure scopes would terminate early if keyword modifiers were used + ([#119](https://github.com/gnikit/fortls/issues/119)) + ## 2.5.0 ### Added