Fortran-FOSS-Programmers / ford

Automatically generates FORtran Documentation from comments within the code.
https://forddocs.readthedocs.io
GNU General Public License v3.0
402 stars 131 forks source link

Derived-types declared in submodules are missing #620

Closed mscfd closed 5 months ago

mscfd commented 6 months ago

Besides links to submodules (there is an issue for that), I'm also missing links (e.g. in inheritance graphs) for derived-types declared solely in submodules.

For example: a module defines an abstract base class base_class_t and exports a factory function create. A non-abstract implementation impl_class_t (extended from base_class_t) is declared and implemented in the submodule. The factory allocates and returns an object of dynamic-type impl_class_t as class(base_class_t). Derived-type impl_class_t is completely hidden in the submodule.

I can see the submodule in the file list, I can see base_class_t in the derived-type list, but I do not see impl_class_t anywhere. The ford documentation does not contain any links or references to impl_class_t. The implementation of function create is also not accessible, just the module interface declaration.

Note: I have not tested this example code. It is derived from a larger project, where this pattern is used.

module mod

implicit none
private

public create

type, abstract, public :: base_class_t
contains
   procedure(foo_ifc), deferred :: foo
end type base_class_t

abstract interface
   subroutine foo_ifc(self)
      import base_class_t
      class(base_class_t), intent(in) :: self
   end subroutine foo_ifc
end interface

interface
   module function create() result(x)
      class(base_class_t), pointer :: x
   end function create
end interface

end module mod
submodule (mod) mod_s

implicit none

type, extends(base_class_t) :: impl_class_t
   integer :: i = 123
contains
   procedure :: foo => impl_foo
end type impl_class_t

contains

module function create() result(x)
   class(base_class_t), pointer :: x
   allocate(impl_class_t :: x)
end function create

subroutine impl_foo(self)
   class(impl_class_t), intent(in) :: self
   print *,'impl: ', self%i
end subroutine impl_foo

end submodule mod_s
ZedThree commented 5 months ago

This is because anything declared in a submodule is private, and so won't be documented by default. You can put display: private in the ford comment on mod_s to include impl_class_t:

submodule (mod) mod_s
  !! display: private

  ...

image

Note that this will also include docs for the implementations of create and impl_foo. I don't think there's a way to annotate a single entity to be included in the rendered docs.

mscfd commented 5 months ago

Thanks a lot for pointing out. As I need this for all submodules and also want to have all private entities documented, I can actually use the "display: private" option in the ford project setup file. I came across that option but did not make the connection to submodules. Maybe it would be helpful to mention submodules there?

mscfd commented 5 months ago

As I wrote display: private helps exposing the submodule types, but ford fails at referencing public derived types. For the test project below with a module and accompanying submodule the "derived-type table" lists type "impl" but not "base". Moreover, it says that "impl" extends "../../base", and links are missing.

Remarks: (1) Note that ford_project_file.md is placed in "./build" and the two source files are placed in "./src".

(2) If I remove "display:private" then impl disappears and base becomes visible.

(3) If I remove "display:private" then base becomes visible, but the abstract attribute is missing from the derived-type description.

ford_project_file.md:

---
project: test
summary: test
src_dir: ../src
output_dir: ./doc
summary: test
author: test
author_description: test
display: private
proc_internals: true
show_proc_parent: true
preprocess: true
predocmark: >
source: true
graph: true
graph_maxdepth: 10
graph_maxnodes: 50
coloured_edges: false
search: true
max_frontpage_items: 4
parallel: 0
---

test.f90

module test

implicit none
private

public create

!> my base class
type, abstract, public :: base
   !> base component
   integer :: i = 5
end type base

interface
   module function create() result(p)
      class(base), pointer :: p
   end function create
end interface

end module test

test_smod.f90

submodule (test) test_smod

implicit none

!> my implementation of base
type, extends(base) :: impl
   !> implementation component
   integer :: k = 6
end type impl

contains

module function create() result(p)
   class(base), pointer :: p
   allocate(impl :: p)
end function create

end submodule test_smod
mscfd commented 5 months ago

Ok re-reading the documentation I surmised that I need to provide public and private and possibly protected as well. I finally succeeded by adding the three lines

display: private
display: public
display: protected

to the project file. Is there any use-case where one wants to see private but not public instances? This is bound to cause confusion.

BTW: for the "abstract" property I will open a new issue.

ZedThree commented 5 months ago

Is there any use-case where one wants to see private but not public instances? This is bound to cause confusion.

Yes, I don't think you're the first to be confused by this. I should write up a FAQ with common questions like this

mscfd commented 5 months ago

But, is there any use-case? Or should it not be changed to "private implies public"?