Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 149 additions & 0 deletions Checks/PWR088/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
# PWR088: Add missing arguments to procedure calls

### Issue

Calling a procedure while omitting one or more required arguments (i.e., dummy
arguments that are not `optional`) can lead to compilation failures or, if not
caught, undefined behavior at runtime.

### Actions

Ensure all arguments required by the procedure (i.e., not marked `optional`)
are supplied at the call site. If a call with fewer arguments is actually
needed, adapt the procedure accordingly; for example, by converting arguments
to `optional` and handling the missing scenarios in the implementation.

### Relevance

When calling a Fortran procedure, the caller is expected to supply all
mandatory arguments. If an explicit procedure interface is available at the
call site, compilers can typically detect and report a mismatch in the number
of supplied arguments during compilation.

However, Fortran also allows calling procedures without information about the
expected arguments; in such cases, an _implicit interface_ is used. The caller
effectively passes a list of memory addresses, which the called procedure
interprets according to its dummy argument declarations. If the actual
arguments are fewer than the dummy arguments, the result is undefined behavior
and may manifest as incorrect results, "random" behavior, or even crashes.

> [!TIP]
> To enhance code safety and reliability, ensure procedures provide
> explicit interfaces to their callers. Check the [PWR068 entry](../PWR068/)
> for more details!

### Code examples

The following example advances a simulation time by `numberSteps * dt`. The
procedure expects four arguments, but the caller accidentally passes only
three:

```fortran {7,11} showLineNumbers
! simulation.f90
pure subroutine updateSimulationTime(numberSteps, dt, prevTime, newTime)
use iso_fortran_env, only: real32
implicit none

integer, intent(in) :: numberSteps
real(kind=real32), intent(in) :: dt
real(kind=real32), intent(in) :: prevTime
real(kind=real32), intent(out) :: newTime

newTime = prevTime + numberSteps * dt
end subroutine updateSimulationTime
```

```fortran {6,13} showLineNumbers
! example.f90
program call_with_missing_arguments
use iso_fortran_env, only: real32
implicit none

external :: updateSimulationTime
integer :: numberSteps
real(kind=real32) :: prevTime, newTime

numberSteps = 10
prevTime = 0.0

call updateSimulationTime(numberSteps, prevTime, newTime)
print *, "New time = ", newTime
end program call_with_missing_arguments
```

Because `updateSimulationTime()` is an `external` procedure, the call uses an
implicit interface. Compilers will typically allow this program to compile
despite the argument count mismatch, producing an incorrect result at runtime:

```txt
$ gfortran --version
GNU Fortran (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0
$ gfortran simulation.f90 example.f90
$ ./a.out
New time = -2.23126931E+24
```

A simple solution is to pass the missing `dt` in the procedure call:

```fortran {8,11,14} showLineNumbers
! solution.f90
program correct_call
use iso_fortran_env, only: real32
implicit none

external :: updateSimulationTime
integer :: numberSteps
real(kind=real32) :: dt, prevTime, newTime

numberSteps = 10
dt = 0.1
prevTime = 0.0

call updateSimulationTime(numberSteps, dt, prevTime, newTime)
print *, "New time = ", newTime
end program correct_call
```

Now, the program will produce the correct result:

```txt
$ gfortran simulation.f90 solution.f90
$ ./a.out
New time = 1.00000000
```

Ultimately, it is recommended to encapsulate all procedures within modules so
that callers have explicit interfaces. This enables the compiler to verify the
provided arguments against the actual dummy arguments, preventing
difficult-to-diagnose runtime bugs.

> [!TIP]
> Check the [PWR068 entry](../PWR068/) for more details on implicit and
> explicit interfaces!

### Related resources

- [PWR088
examples](https://github.com/codee-com/open-catalog/tree/main/Checks/PWR088/)

### References

- ["Implicit
Interfaces"](https://people.cs.vt.edu/~asandu/Courses/MTU/CS2911/fortran_notes/node44.html),
Adrian Sandu. [last checked February 2026]

- ["More on Implicit
Interfaces"](https://people.cs.vt.edu/~asandu/Courses/MTU/CS2911/fortran_notes/node181.html),
Adrian Sandu. [last checked February 2026]

- ["Explicit
Interfaces"](https://people.cs.vt.edu/~asandu/Courses/MTU/CS2911/fortran_notes/node182.html),
Adrian Sandu. [last checked February 2026]

- ["Doctor Fortran Gets
Explicit!"](https://web.archive.org/web/20130803094211/http://software.intel.com/en-us/forums/topic/275071),
Steve Lionel. [last checked February 2026]

- ["Doctor Fortran Gets Explicit - Again!
"](https://web.archive.org/web/20130113070703/http://software.intel.com/en-us/blogs/2012/01/05/doctor-fortran-gets-explicit-again),
Steve Lionel. [last checked February 2026]
3 changes: 3 additions & 0 deletions Checks/PWR088/benchmark/README.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Omitting required arguments leads to undefined behavior. Thus, performance
benchmarking isn't meaningful since there isn't a correct baseline to measure
against.
18 changes: 18 additions & 0 deletions Checks/PWR088/example.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
! PWR088: Add missing arguments to procedure calls

! NOT-PWR068: External procedure used to demonstrate how compilers can't catch
! the argument count mismatch
program call_with_missing_arguments
use iso_fortran_env, only: real32
implicit none

external :: updateSimulationTime
integer :: numberSteps
real(kind=real32) :: prevTime, newTime

numberSteps = 10
prevTime = 0.0

call updateSimulationTime(numberSteps, prevTime, newTime)
print *, "New time = ", newTime
end program call_with_missing_arguments
13 changes: 13 additions & 0 deletions Checks/PWR088/simulation.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
! PWR088: Add missing arguments to procedure calls

pure subroutine updateSimulationTime(numberSteps, dt, prevTime, newTime)
use iso_fortran_env, only: real32
implicit none

integer, intent(in) :: numberSteps
real(kind=real32), intent(in) :: dt
real(kind=real32), intent(in) :: prevTime
real(kind=real32), intent(out) :: newTime

newTime = prevTime + numberSteps * dt
end subroutine updateSimulationTime
19 changes: 19 additions & 0 deletions Checks/PWR088/solution.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
! PWR088: Add missing arguments to procedure calls

! NOT-PWR068: External procedure used to demonstrate how compilers can't catch
! the argument count mismatch
program correct_call
use iso_fortran_env, only: real32
implicit none

external :: updateSimulationTime
integer :: numberSteps
real(kind=real32) :: dt, prevTime, newTime

numberSteps = 10
dt = 0.1
prevTime = 0.0

call updateSimulationTime(numberSteps, dt, prevTime, newTime)
print *, "New time = ", newTime
end program correct_call
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ designed to demonstrate:
| [PWR080](Checks/PWR080/) | Conditionally initialized variables can lead to undefined behavior | correctness, portability, security | [CWE-758](https://cwe.mitre.org/data/definitions/758.html), [CWE-908](https://cwe.mitre.org/data/definitions/908.html), [CWE-909](https://cwe.mitre.org/data/definitions/909.html) | [6.13](https://j3-fortran.org/doc/year/23/23-241.pdf), [6.22](https://j3-fortran.org/doc/year/23/23-241.pdf), [6.56](https://j3-fortran.org/doc/year/23/23-241.pdf) | [EXP33-C](https://wiki.sei.cmu.edu/confluence/display/c/EXP33-C.+Do+not+read+uninitialized+memory), [EXP34-C](https://wiki.sei.cmu.edu/confluence/display/c/EXP34-C.+Do+not+dereference+null+pointers), [MSC15-C](https://wiki.sei.cmu.edu/confluence/display/c/MSC15-C.+Do+not+depend+on+undefined+behavior) | [EXP53-CPP](https://wiki.sei.cmu.edu/confluence/spaces/cplusplus/pages/88046609/EXP53-CPP.+Do+not+read+uninitialized+memory) | ✓ | ✓ | ✓ | |
| [PWR081](Checks/PWR081/) | Uninitialized output arguments can lead to undefined behavior | correctness, portability, security | [CWE-758](https://cwe.mitre.org/data/definitions/758.html), [CWE-908](https://cwe.mitre.org/data/definitions/908.html), [CWE-909](https://cwe.mitre.org/data/definitions/909.html) | [6.13](https://j3-fortran.org/doc/year/23/23-241.pdf), [6.22](https://j3-fortran.org/doc/year/23/23-241.pdf), [6.56](https://j3-fortran.org/doc/year/23/23-241.pdf) | [EXP33-C](https://wiki.sei.cmu.edu/confluence/display/c/EXP33-C.+Do+not+read+uninitialized+memory), [EXP34-C](https://wiki.sei.cmu.edu/confluence/display/c/EXP34-C.+Do+not+dereference+null+pointers), [MSC15-C](https://wiki.sei.cmu.edu/confluence/display/c/MSC15-C.+Do+not+depend+on+undefined+behavior) | | | ✓ | | |
| [PWR083](Checks/PWR083/) | Match the types of dummy and actual arguments in procedure calls | correctness, security | [CWE-628](https://cwe.mitre.org/data/definitions/628.html) | [6.11](https://j3-fortran.org/doc/year/23/23-241.pdf), [6.32](https://j3-fortran.org/doc/year/23/23-241.pdf), [6.53](https://j3-fortran.org/doc/year/23/23-241.pdf) | [EXP37-C](https://wiki.sei.cmu.edu/confluence/display/c/EXP37-C.+Call+functions+with+the+correct+number+and+type+of+arguments) | | | ✓ | | |
| [PWR088](Checks/PWR088/) | Add missing arguments to procedure calls | correctness, security | [CWE-628](https://cwe.mitre.org/data/definitions/628.html) | [6.32](https://j3-fortran.org/doc/year/23/23-241.pdf) | [EXP37-C](https://wiki.sei.cmu.edu/confluence/display/c/EXP37-C.+Call+functions+with+the+correct+number+and+type+of+arguments) | | | ✓ | | |
| [PWD002](Checks/PWD002/) | Unprotected multithreading reduction operation | correctness, security | [CWE-366](https://cwe.mitre.org/data/definitions/366.html), [CWE-820](https://cwe.mitre.org/data/definitions/820.html) | [6.61](https://j3-fortran.org/doc/year/23/23-241.pdf) | [CON07-C](https://wiki.sei.cmu.edu/confluence/display/c/CON07-C.+Ensure+that+compound+operations+on+shared+variables+are+atomic), [CON43-C](https://wiki.sei.cmu.edu/confluence/display/c/CON43-C.+Do+not+allow+data+races+in+multithreaded+code) | | ✓ | ✓ | ✓ | |
| [PWD003](Checks/PWD003/) | Missing array range in data copy to the GPU | correctness, security | [CWE-131](https://cwe.mitre.org/data/definitions/131.html), [CWE-758](https://cwe.mitre.org/data/definitions/758.html) | [6.56](https://j3-fortran.org/doc/year/23/23-241.pdf) | [MSC15-C](https://wiki.sei.cmu.edu/confluence/display/c/MSC15-C.+Do+not+depend+on+undefined+behavior) | | ✓ | ✓ | ✓ | |
| [PWD004](Checks/PWD004/) | Out-of-memory-bounds array access | correctness, security | [CWE-125](https://cwe.mitre.org/data/definitions/125.html), [CWE-787](https://cwe.mitre.org/data/definitions/787.html) | [6.8](https://j3-fortran.org/doc/year/23/23-241.pdf), [6.9](https://j3-fortran.org/doc/year/23/23-241.pdf), [6.10](https://j3-fortran.org/doc/year/23/23-241.pdf) | [ARR30-C](https://wiki.sei.cmu.edu/confluence/display/c/ARR30-C.+Do+not+form+or+use+out-of-bounds+pointers+or+array+subscripts) | | ✓ | ✓ | ✓ | |
Expand Down