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

Division by zero exception in lines_description of sourceform.py #623

Closed mscfd closed 5 months ago

mscfd commented 5 months ago

For very simple test projects I hit a division by zero exception in "def lines_description" in sourceform.py, because total=0. As a hack, I changed the routine using a max(1,total) and max(1,total_all), but this is probably not the right thing to do.

    def lines_description(self, total, total_all=0, obj=None):
        if not obj:
            obj = self.obj
        description = f"{float(self.num_lines) / max(1,total) * 100:4.1f}% of total for {self.pretty_obj[obj]}."
        if total_all:
            description = (
                f"<p>{description}</p>Including implementation: {self.num_lines_all} statements, "
                f"{float(self.num_lines_all) / max(1,total_all) * 100:4.1f}% of total for {self.pretty_obj[obj]}."
            )
        return description
ZedThree commented 5 months ago

Thanks for finding this, @mscfd, but I'm a bit unclear on how you trigger it exactly? That implies we're trying to render something that has no lines?

mscfd commented 5 months ago

I wrongly assumed that for really simple test project lines=0 happens naturally. But indeed, it is a bit more intriguing. It looks like that for 'display: private' (possibly in conjunction with submodules) lines are counted wrongly. In the example below, I have self.num_lines=4 but total=0. Whether display:public is present or not, does not change the exception. The description line with my max(1,*)-safeguard is

"400.0% of total for procedures."

ford_project_file.md:

---
project: test
src_dir: ./src
output_dir: ./doc
display: protected
display: private
display: public
---

test.f90:

module test

implicit none
private

public create

type, abstract, public :: base
end type base

end module test

test_smod.f90:

submodule (test) test_smod

implicit none

type, extends(base) :: impl
end type impl

contains

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

end submodule test_smod
ZedThree commented 5 months ago

Note that your example isn't valid Fortran, and you'd need at least an interface for create. Adding that:

module test

implicit none
private

public :: create

type, abstract, public :: base
end type base

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

end module test

and ford runs fine.

I'll add a warning and a safe-guard for lines_description

mscfd commented 5 months ago

Thanks. As this was not real code, I never compiled it. it was just for testing ford.