Skip to content

The Fortitude Linter

The Fortitude linter is a very fast Fortran linter heavily inspired by and built on the Ruff linter for Python. If you've ever used Ruff, Fortitude should feel very familiar.

fortitude check

fortitude check is the main feature of Fortitude, taking a list of files and/or directories, and checking all discovered Fortran files for common (and less common!) errors and style violations, optionally fixing any that are fixable. Fortitude searches directories recursively for any Fortran files:

$ fortitude check                   # Lint Fortran files in current directory
$ fortitude check --fix             # Lint and fix files in current directory
$ fortitude check path/to/source/   # Lint Fortran files in `path/to/source/`

You can see the list of all available options with fortitude check --help.

Rule selection

The set of enabled rules is controlled via the check.select, check.extend-select, and check.ignore settings.

Like Ruff and Flake8, Fortitude gives each rule a short code (for example, C003) consisting of a one-to-three letter prefix (for the category) followed by three digits. Fortitude also gives each rule a more human-readable English name, such as implicit-external-procedures, which can always be used instead of the short code in places like check.select and check.ignore.

Rule selectors like check.select and check.ignore accept any combination of the following:

  • category prefixes like C,
  • category names like correctness,
  • rule short codes like C003,
  • rule names like implicit-external-procedures.

For example, in the following configuration file:

[extra.fortitude.check]
select = ["C", "style"]
ignore = ["implict-external-procedures", "S001"]
[check]
select = ["C", "style"]
ignore = ["implict-external-procedures", "S001"]
[tool.fortitude.check]
select = ["C", "style"]
ignore = ["implict-external-procedures", "S001"]

Fortitude would enable all rules in the correctness (C) and style (S) categories, except for implicit-external-procedures (C003) and line-too-long (S001).

Like Ruff, Fortitude also has the special ALL code, which enables all rules. This should be used with discretion as it will implicitly enable any new rules whenever you upgrade Fortitude.

As a guideline, prefer using check.select over check.extend-select to make your rule set explicit in your configuration file.

Setting --select on the command line will override check.select in the configuration file. For example, given the configuration file above, running fortitude check --select S001 will select only S001 (line-too-long).

If instead you want to select additional rules from the command line, use --extend-select. Running fortitude check --extend-select obsolescent in combination with the settings file above will result in Fortitude enforcing all rules in the correctness, style, and obsolescent categories, except for C003 and S001.

Preview rules

New rules and other features may be in 'preview' mode while they undergo further review and testing. To activate them, use the --preview flag:

fortitude check --preview

For more details on how preview works, see Preview.

Fixes

For some lint errors, Fortitude supports automatic fixes, such as rewriting some deprecated syntax, remove/add whitespace, add missing construct names, and so on.

To apply these fixes, pass --fix to fortitude check:

$ fortitude check --fix

To see which rules Fortitude can automatically fix, see Rules.

You can use the --diff flag to see exactly what Fortitude would change:

$ fortitude check --diff
--- test.f90
+++ test.f90
@@ -1,5 +1,6 @@
 subroutine test()
     use, intrinsic :: iso_fortran_env, dp => real64
-    real(kind=dp) x, y
+    real(kind=dp) :: x, y

     y = ASIN(x)

Would fix 1 error.

Indentation

For rules such as superfluous-else-return, where the fix may move code out of the else branch of an if statement for example, Fortitude will also attempt to fix the indentation of the moved code. This can be hampered by tab characters (but see invalid-tab), or "under-indented" code, comments, or continuation characters. We will give a "best effort" formatting of the code in these cases, but Fortitude will always make sure that the code remains syntactically and semantically valid.

Fix safety

Another concept Fortitude borrows from Ruff is that of fix safety: fixes are labelled as either "safe" or "unsafe". Safe fixes will not change the meaning or intent of your code, while unsafe fixes may change the meaning.

For example, implicit-typing (C001) checks for missing implicit none statements, common in older code, and a frequent source of bugs. However, for codes that are relying on implicit typing, this is not a sufficient fix (as variables will need to be explicitly declared as well), so it is not always safe to apply.

Fortitude only enables safe fixes by default. Unsafe fixes can be enabled by settings unsafe-fixes in your configuration file or passing the --unsafe-fixes flag to fortitude check:

# Show unsafe fixes
fortitude check --unsafe-fixes

# Apply unsafe fixes
fortitude check --fix --unsafe-fixes

By default, Fortitude will display a hint when unsafe fixes are available but not enabled. The suggestion can be silenced by setting the unsafe-fixes setting to false or using the --no-unsafe-fixes flag.

Error suppression

Fortitude has several ways of suppressing lint errors, whether they're false positives or permissible in context.

To globally ignore a rule, add it to the check.ignore list, either on the command line for a one-off check, or in your configuration file for a more permanent suppression for a project.

For slightly more fine-grained control, rules can be ignored for individual files through the check.per-file-ignores setting in your configuration file.

For the most fine-grained control, rules can be suppressed in the source code too, which is useful for allowing individual exceptions for whatever reason. Fortitude deviates from how Ruff works here, and instead uses something like Rust's approach, by using an allow or "suppression" comment before the statement. This allow comment then applies to the whole of the next statement, which could be an entire module, for example:

! allow(superfluous-implicit-none)
module numbers
  ! allow(use-all)
  use, intrinsic :: iso_fortran_env
  implicit none

contains
  subroutine greater_than_five(x)
    use some_other_module
    implicit none
  ...

Here, the superfluous-implicit-none check is disabled for all procedures in the whole numbers module, including in greater_than_five, while use-all is disabled only for the iso_fortran_env import and will still apply to some_other_module.

Multiple rules can be in an allow comment, separated by commas, and you can use the typical rule selection naming:

! allow(style, M, FORT002, implicit-real-kind)

will allow all style and modernisation rules, as well as FORT002 (unused-allow-comment) and implicit-real-kind (C022).

Unused allow comments

By default, Fortitude will detect "unused" allow comments via unused-allow-comment: that is, if a suppression comment says it allows a given warning, then the statement(s) it applies to should generate those warnings (and then be allowed by the comment).

Fortitude can automatically remove unused allow comments.

You can also temporarily ignore these suppression comments with --ignore-allow-comments on the command line.

Running fortitude on only modified files

Introduced in 0.9.0

Rather than running Fortitude on all the files in a project, you may wish to restrict it to only those files which have been modified, for example when starting to use Fortitude on an existing project. For this, there are two command line flags you can use:

  • --git-staged will only run Fortitude on those lines in files which have been staged in a git repository
  • --git-since <commit> will run Fortitude on lines in files have been modified since the given commit/branch/reference -- for example, main, 01c34f, HEAD~

There is also --line-filter for advanced users, which lets you specify exactly which lines of which files to run on.

These flags work with the passed set of paths, so it's possible to filter to files that have been staged in a given directory, for example:

# Run only on lines in files in `subdir` that have been staged
$ fortitude check --git-staged subdir/
# Run only on those lines in `foo.f90` that have been modified since `main`
$ fortitude check --git-since main foo.f90

Learning more about a rule

The explain command can be used to get extra information about any rules. Without any arguments, it returns information on all rules; otherwise you can pass it any number of rules or categories, following the usual rule selection naming:

# Print extra information for all rules
$ fortitude explain
# Only get information for selected rules, by code or by name
$ fortitude explain C001 trailing-whitespace
# Print information on all style rules
$ fortitude explain style

Exit codes

By default, fortitude check exits with the following status codes:

  • 0 if no violations were found, or if all present violations were fixed automatically.
  • 1 if violations were found.
  • 2 if Fortitude terminates abnormally due to invalid configuration, invalid CLI options, or an internal error.

This convention mirrors that of tools like ESLint, Prettier, and RuboCop.

fortitude check supports two command-line flags that alter its exit code behavior:

  • --exit-zero will cause Fortitude to exit with a status code of 0 even if violations were found. Note that Fortitude will still exit with a status code of 2 if it terminates abnormally.
  • --exit-non-zero-on-fix will cause Fortitude to exit with a status code of 1 if violations were found, even if all such violations were fixed automatically. Note that the use of --exit-non-zero-on-fix can result in a non-zero exit code even if no violations remain after fixing.