1. The linter
quad gets an old neighbour: a Romberg integrator in fixed-form FORTRAN 77, the kind of file every long-lived code has somewhere, with a test that pins its result:
C ROMBERG INTEGRATION: TRAPEZOID ESTIMATES WITH 1, 2, 4, ... STEPS,
C EXTRAPOLATED UNTIL TWO DIAGONAL VALUES AGREE WITHIN TOL.
DOUBLE PRECISION FUNCTION ROMB(F, A, B, TOL)
DOUBLE PRECISION F, A, B, TOL
EXTERNAL F
DOUBLE PRECISION R(10,10), H, S
INTEGER I, J, K, N
H = B - A
R(1,1) = 0.5D0*H*(F(A)+F(B))
N = 1
DO 30 I = 2, 10
H = 0.5D0*H
S = 0.0D0
DO 10 K = 1, N
S = S + F(A+DBLE(2*K-1)*H)
10 CONTINUE
R(I,1) = 0.5D0*R(I-1,1) + H*S
N = 2*N
DO 20 J = 2, I
R(I,J) = R(I,J-1)
& + (R(I,J-1)-R(I-1,J-1))/(4.0D0**(J-1)-1.0D0)
20 CONTINUE
IF (DABS(R(I,I)-R(I-1,I-1)) .LT. TOL) GOTO 40
30 CONTINUE
I = 10
40 ROMB = R(I,I)
RETURN
END!< Romberg's method reaches 1e-10 on sin(x) over [0, pi].
program test_romberg
!< Romberg's method reaches 1e-10 on sin(x) over [0, pi].
use quad_rules, only : R8P
use integrands, only : sine
implicit none
real(R8P), external :: romb !< The legacy integrator.
real(R8P), parameter :: pi = acos(-1._R8P) !< Pi.
real(R8P) :: estimate !< Its result.
estimate = romb(sine, 0._R8P, pi, 1.e-10_R8P)
print '(A, ES10.3)', 'romberg error: ', abs(estimate - 2._R8P)
if (abs(estimate - 2._R8P) > 1.e-9_R8P) error stop 'romberg did not converge'
endprogram test_romberg$ fobis test --filter test_romberg 2>&1 | tail -n 4
PASS test_romberg (0.00 s)
Results: 1 passed, 0 failed, 0 skipped
Total: 0.00 sFrom this track on quad is a git repository (git init, one commit): the review of chapter 3 needs one.
Linting
fobis lint checks the sources of a mode (the default one here), with its -D defines: an #ifdef branch the build does not compile is not checked either.
$ fobis lint
src/integrate_mpi.F90:4: COR040 [warning] 'use mpi' has no 'only:' list
src/quad_rules.F90:10: COR002 [warning] interface body function 'integrand' has no 'implicit none'
src/romberg.f:3: COR001 [warning] function 'romb' has no 'implicit none'
src/romberg.f:3: COR031 [warning] function 'romb' is not in a module
src/romberg.f:4: COR010 [warning] dummy argument 'a' of function 'romb' has no intent
src/romberg.f:4: COR010 [warning] dummy argument 'b' of function 'romb' has no intent
src/romberg.f:4: COR010 [warning] dummy argument 'tol' of function 'romb' has no intent
src/romberg.f:4: COR030 [warning] 'f' is declared external in function 'romb'
src/romberg.f:11: OBS011 [warning] labelled do loop (obsolescent): use do ... end do
src/romberg.f:14: OBS011 [warning] labelled do loop (obsolescent): use do ... end do
src/romberg.f:19: OBS011 [warning] labelled do loop (obsolescent): use do ... end do
src/romberg.f:23: MDN021 [warning] old relational operator .lt.: use symbolic operators
src/romberg.f:23: OBS005 [warning] specific intrinsic name 'dabs' (obsolescent): use the generic name
test/test_romberg.F90:7: COR030 [warning] 'romb' is declared external in program 'test_romberg'
14 finding(s) in 10 file(s): COR001 x1, COR002 x1, COR010 x3, COR030 x2, COR031 x1, COR040 x1, MDN021 x1, OBS005 x1, OBS011 x3
Explain a rule with: fobis lint --explain <ID>
[exit status 1]Every finding names a rule. The first two are about the code of the previous tracks: use mpi imports every name of the MPI module, and the abstract interface of quad_rules has no implicit none. The rest is the legacy file. --explain says why a rule exists, and what to write instead:
$ fobis lint --explain COR002
COR002 interface-implicit-typing [warning]
An interface body has no `implicit none`.
An interface body is a separate scoping unit: it does not inherit the `implicit none` of the module
or procedure that contains the interface block. Dummy arguments left undeclared in the body are
typed implicitly, and the interface then disagrees with the procedure it describes.
Reported:
interface
subroutine callback(x)
real :: x
end subroutine callback
end interface
Preferred:
interface
subroutine callback(x)
implicit none
real, intent(inout) :: x
end subroutine callback
end interface
Equivalent Fortitude rule: C002
References (read with: fobis kb show <id>):
fortran-std:ch19-scope-association#anti-patterns
Suppress one occurrence with: ! fobis: ignore[COR002] <reason>The references point to the knowledge packs, searchable with fobis kb.
Choosing the rules
--select and --ignore take rule ids or prefixes (OBS, obsolescent; MDN, modernisation; COR, correctness; OMP, ACC, MPI, parallel code; ...). Selecting a family explicitly also turns on its opt-in rules, the ones whose findings are matters of style or project policy:
$ fobis lint --select OBS,MDN
src/integrate_mpi.F90:4: MDN201 [warning] use mpi: prefer use mpi_f08
src/romberg.f:4: MDN001 [warning] double precision declaration: use real with a named kind parameter
src/romberg.f:6: MDN001 [warning] double precision declaration: use real with a named kind parameter
src/romberg.f:9: MDN002 [warning] double precision literal: use a kind suffix (1.0_rk)
src/romberg.f:11: OBS011 [warning] labelled do loop (obsolescent): use do ... end do
src/romberg.f:12: MDN002 [warning] double precision literal: use a kind suffix (1.0_rk)
src/romberg.f:13: MDN002 [warning] double precision literal: use a kind suffix (1.0_rk)
src/romberg.f:14: OBS011 [warning] labelled do loop (obsolescent): use do ... end do
src/romberg.f:17: MDN002 [warning] double precision literal: use a kind suffix (1.0_rk)
src/romberg.f:19: OBS011 [warning] labelled do loop (obsolescent): use do ... end do
src/romberg.f:20: MDN002 [warning] double precision literal: use a kind suffix (1.0_rk)
src/romberg.f:23: MDN021 [warning] old relational operator .lt.: use symbolic operators
src/romberg.f:23: OBS005 [warning] specific intrinsic name 'dabs' (obsolescent): use the generic name
13 finding(s) in 10 file(s): MDN001 x2, MDN002 x5, MDN021 x1, MDN201 x1, OBS005 x1, OBS011 x3
Explain a rule with: fobis lint --explain <ID>
[exit status 1]The rule reference lists them all; a fobos [lint] section sets a project's selection once.
For CI
--format github writes GitHub Actions annotations (shown on the pull request's lines), sarif the format of GitHub code scanning, json a document for scripts:
$ fobis lint --format github --select COR040
::warning file=src/integrate_mpi.F90,line=4,endLine=4,title=COR040 use-all::'use mpi' has no 'only:' list
[exit status 1]The exit status is 1 when anything is found (--exit-zero keeps it 0).