From 2c5810b984c2a0c188c304ff37c55396ea85056e Mon Sep 17 00:00:00 2001 From: gnikit Date: Sun, 23 Jul 2023 14:26:20 +0100 Subject: [PATCH] fix: Go To Implementation works for submodules It is now possible for interfaces with implementations in submodules i.e. a Function or a Subroutine Fixes #74 --- CHANGELOG.md | 2 ++ fortls/langserver.py | 4 ++++ fortls/objects.py | 3 +++ test/test_server_implementation.py | 15 +++++++++++++++ test/test_source/imp/submodule.f90 | 24 ++++++++++++++++++++++++ 5 files changed, 48 insertions(+) create mode 100644 test/test_source/imp/submodule.f90 diff --git a/CHANGELOG.md b/CHANGELOG.md index 68b380ae..e5b65d70 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,8 @@ ### Fixed +- Fixed bug where Go To Implementation would not work for submodules + ([#74](https://github.com/fortran-lang/fortls/issues/74)) - Fixed bug where `associate` blocks for variables pointing to function results where not properly resolved ([#269](https://github.com/fortran-lang/fortls/issues/269)) diff --git a/fortls/langserver.py b/fortls/langserver.py index 33dc6c35..5555d2dd 100644 --- a/fortls/langserver.py +++ b/fortls/langserver.py @@ -1165,6 +1165,10 @@ def serve_implementation(self, request: dict): impl_obj = var_obj.link_obj if (impl_obj is not None) and (impl_obj.file_ast.file is not None): return self._create_ref_link(impl_obj) + elif var_obj.parent.get_type() == INTERFACE_TYPE_ID: + # Find the first implementation of the interface + if var_obj.link_obj is not None: + return self._create_ref_link(var_obj.link_obj) return None def serve_rename(self, request: dict): diff --git a/fortls/objects.py b/fortls/objects.py index 4daf7b91..72186305 100644 --- a/fortls/objects.py +++ b/fortls/objects.py @@ -897,6 +897,8 @@ def resolve_link(self, obj_tree): if file_scope is child_old: child.file_ast.scope_list[j] = child if child.get_type() == prototype.get_type(): + # Link the interface with the implementation + prototype.link_obj = child prototype.resolve_link(obj_tree) child.copy_interface(prototype) break @@ -922,6 +924,7 @@ def __init__( self.in_children: list = [] self.missing_args: list = [] self.mod_scope: bool = mod_flag + self.link_obj: Subroutine | Function | None = None def is_mod_scope(self): return self.mod_scope diff --git a/test/test_server_implementation.py b/test/test_server_implementation.py index 65a3ddec..874b4a86 100644 --- a/test/test_server_implementation.py +++ b/test/test_server_implementation.py @@ -75,3 +75,18 @@ def test_implementation_no_file(): errcode, results = run_request(string, ["-n", "1"]) assert errcode == 0 assert results[1] is None + + +def test_implementation_submodule(): + """Go to implementation for submodule""" + root = test_dir / "imp" + string = write_rpc_request(1, "initialize", {"rootPath": str(root)}) + file_path = root / "submodule.f90" + string += imp_request(file_path, 5, 30) + string += imp_request(file_path, 8, 30) + string += imp_request(file_path, 9, 30) + errcode, results = run_request(string, ["-n", "1"]) + assert errcode == 0 + assert results[1] == create(str(root / "submodule.f90"), 19, 20, 34) + assert results[2] == create(str(root / "submodule.f90"), 19, 20, 34) + assert results[3] is None diff --git a/test/test_source/imp/submodule.f90 b/test/test_source/imp/submodule.f90 new file mode 100644 index 00000000..639b038d --- /dev/null +++ b/test/test_source/imp/submodule.f90 @@ -0,0 +1,24 @@ +module parent_mod + implicit none + type :: typ + real(kind=8) :: value + contains + procedure :: method1 => submod_method1 + end type typ + interface + module subroutine submod_method1(this) + class(typ), intent(inout) :: this + end subroutine submod_method1 + module subroutine submod_method2(this, value) + class(typ), intent(inout) :: this + real, intent(in) :: value + end subroutine submod_method2 + end interface +end module parent_mod +submodule(parent_mod) submod +contains + module subroutine submod_method1(this) + class(typ), intent(inout) :: this + this%value = 0 + end subroutine submod_method1 +end submodule submod