Debugging, Testing & Documentation

Compiler warnings (-Wall -Wextra -pedantic -std=f2018), fortitude (fast linter), flint, camfort, fprettify, findent, LFortran's semantic checks, NAG compiler's strict checking.

34.1 Debug builds

-fcheck=bounds catches out-of-bounds indexing; -finit-real=snan exposes uninitialized reals.

-fsanitize=address,undefined (gfortran) for memory errors; Valgrind for leaks.

Debuggers: gdb (break, run, print a(1:5), bt), lldb, Intel gdb, TotalView/DDT for parallel codes; VS Code debugging via the Modern Fortran extension.

gfortran -g -O0 -Wall -Wextra -Wimplicit-interface -fcheck=all -fbacktrace \
         -ffpe-trap=invalid,zero,overflow -finit-real=snan -std=f2018 prog.f90
ifx -g -O0 -check all -traceback -warn all -fpe0 prog.f90

34.2 Unit testing frameworks

test-drive (fpm default):

pFUnit (NASA; supports MPI and OpenMP tests), veggies/garden, Zofu, FRUIT, fortuno.

Regression tests: compare outputs against reference data with tolerances; CI with GitHub Actions (fortran-lang/setup-fortran).

Code coverage: gcov/gcovr/lcov with --coverage.

module test_math
  use testdrive, only: new_unittest, unittest_type, error_type, check
  use mymath, only: square
  implicit none
contains
  subroutine collect_tests(testsuite)
    type(unittest_type), allocatable, intent(out) :: testsuite(:)
    testsuite = [new_unittest("square", test_square)]
  end subroutine
  subroutine test_square(error)
    type(error_type), allocatable, intent(out) :: error
    call check(error, square(3.0) == 9.0, "square(3) should be 9")
  end subroutine
end module test_math

34.3 Static analysis & formatting

Compiler warnings (-Wall -Wextra -pedantic -std=f2018), fortitude (fast linter), flint, camfort, fprettify, findent, LFortran's semantic checks, NAG compiler's strict checking.

34.4 Documentation with FORD

ford project.md generates an HTML site with call graphs, module dependency graphs, and source listings. Doxygen also supports Fortran.

!> Computes the area of a circle.
!>
!> Uses the formula \( A = \pi r^2 \).
pure real(dp) function circle_area(r)
  real(dp), intent(in) :: r   !! radius (must be non-negative)
  circle_area = pi * r**2
end function circle_area