support for incremental linting on existing projects with --git-staged to run just on staged changes, and --git-since to run on changes since a particular commit/branch
14 new rules, bringing us to 101 rules total!
34 rules out of preview mode
As always, we’d love to hear your feedback and ideas for future features and rules, and please feel free to raise an issue if you run into any problems!
I tried it on a few Fortran patterns that I would consider bug-prone. The two cases I could not get it to detect were:
intent(out) dummy arguments assigned only inside a conditional block:
subroutine set_value(flag, x)
implicit none
logical, intent(in) :: flag
integer, intent(out) :: x
if (flag) then
x = 1
end if
end subroutine set_value
Passing an N-D array to a routine that reinterprets it with a different explicit shape:
program demo
implicit none
integer :: x(3, 3, 3, 3) ! 4D array
call fill_as_2d(x)
contains
subroutine fill_as_2d(a)
implicit none
integer, intent(out) :: a(3,3) !!! < redefined as 2D array
a = 1
end subroutine fill_as_2d
end program demo
The second case is especially easy to miss because gfortran catches the mismatch if the dummy is assumed-shape, for example a(:,:), but not when it is explicit-shape.
Thanks @Simo! Those are both great ideas for rules. The first one I think should be pretty easy to achieve, but the second one might be a little harder right now because it needs semantic information which might come from other modules, and currently we only collect it for local variables. However, the fact that it’s a bug and gfortran misses it means it would be really good if we could catch it.
I’ll make issues for both of these, they should definitely be on our radar.
If I understand correctly, the Fortran standard allows the second program (it’s not a bug when the shapes are specified explicitly), although I am pretty sure it’s bug-prone in practice, at least in my experience.
Both programs are standard conforming. That is, it is allowed to declare variables in an undefined state, or to perform some action that leaves a variable in an undefined state. The intent(out) declaration with no subsequent definition of that entity does that. And, as you say, it is also allowed to have array dimension and array rank mismatches with explicit shape declarations. That sometimes causes copy-in/copy-out argument association, but it is standard conforming.
I did not mean to say that the first program is not standard-conforming. As far as I know, that particular pitfall is already well known and has been discussed on this forum and elsewhere. My point was only that the second program is also standard-conforming, even though the first reaction many people might have when seeing it is: “This is definitely wrong.”
This is the way that line of code would have been written in f77, before array syntax. Nowadays, one would probably write that line of code as
call fill_as_2d( x(:,:,1,1) )
I think this does exactly the same thing, the compiler will see those two lines as being equivalent, but now a human reader will see that only a subset of the array elements are being referenced, and he will also see that a rank-2 actual array argument is associated with a rank-2 dummy array, so everything is now clear.
On the other hand, if the line of code were written as
call fill_as_2d( x(1,:,1,:))
then this is still legal fortran, but now the compiler must do something special (e.g. copy-in/copy-out type argument association) to make things work. (actually, only copy-out in this case because of intent(out)). This requires slice notation and array syntax, so this was not possible with f77. The programmer must understand the storage sequence convention to know the difference.