Fortitude 0.7.0 release: a Fortran linter

We’ve just released a new version of Fortitude, our Fortran linter based on Ruff. This release has been all about configurability, featuring improvements to fortitude.toml /fpm.toml configuration files and adding new options such as file filtering and per-line/per-file rule ignores. It also adds 25 new rules – the full list is available here.

One significant change is that we’ve re-categorised a large number of our rules, so a lot of previous rule codes have been remapped (e.g. T001C001 ). We’ve automated the redirection of old codes to minimise breakages to existing workflows, but there are likely some edge-cases we didn’t consider, so please let us know if you run into any trouble migrating to the new version. We’ve turned some rules off by default, and several new rules are in preview mode, so if you want to turn everything on, feel free to try --select=ALL --preview to get all 50+ rules!

The roadmap for 0.8.0 includes editor integration with LSP, improved preprocessor support, and a formatting mode. Please let us know if you’re particularly keen to see any of these, or if there’s any other features or new rules you’d love to see! Pull requests are always welcome! :smiley:

18 Likes

If by formatting mode you mean the ability to just apply all given rules to a file, then that would be very nice. We currently use flint, but that only gives us a list of issues.

1 Like

Sorry, I meant a formatter like fprettify, or black for Python.

We do have auto-fixes for a bunch of rules (but not all), you can apply with them --fix. Some fixes also require setting --unsafe-fixes as it’s possible they will change the meaning of your code. See the list of rules for which ones have auto-fixes available.

Can I ask which rules in flint you find most useful and would like auto-fixes for?

2 Likes

This thread contains a potential new rule: Compiler option to warn about argument declarations of x(1) instead of x(*)

The idea would be to mark x(1) (a legacy convention for arrays of unspecified length) variables as candidates for upgrade to x(*), when x appears as a procedure dummy argument.

2 Likes

This is our rc file for flint

The main issue is actually various corner cases that we kept catching as we introduced the linting.

1 Like

this is fantastic! I’ve added it to my pre-commit pipeline :slight_smile: thanks so much

1 Like

We’ve just released 0.7.1 with some bug-fixes and a new rule, as well as a pre-commit hook, as several people have asked for that!

1 Like

We’ve just released 0.7.3 with a couple of bugfixes and six new rules:

6 Likes

Not sure I agree with your single quote rule. In strict Fortran 77, only the apostrophe was allowed to delimit strings. In some compilers the quote character was used in other ways. (E.g., CDC FTN5 something like "HELLO" was equivalent to Hollerith 5HHELLO, and not of character data type.) F90 expanded the Fortran character set to allow quote characters, and use them like apostrophes.

@wspector I think the idea is that you enable such rules that you agree with for your projects.

Thanks for pushing this @zedthree. Time will tell which of these rules will end up being liked and not liked. Then the most liked rules we can implement in compilers directly.

2 Likes

Yeah it’s pretty simple to remove rules you don’t agree with using the toml file (a section in your fpm.toml). It’s very handy

i.e. I ignore a handful

[extra.fortitude.check]
ignore = [
       "MOD011",  # allows (/ /)                                                       
       "MOD021",  # allows .gt.                                                        
       #"C132",    # allows default public accessiblility                              
       "E001",    # ignores syntax errors - useful as pre-processor support is tough   
       "C003",    # implicit none(external) not well supported by AMD                  
       "S241",    # makes all strings double quoted. Let's not do that           
       ]

1 Like

As @certik and @RJaBi have said, we don’t expect all rules to be useful for everyone and the configuration is designed to make it easy to ignore those you don’t want, or to only enable a few you care about. Hopefully you find enough rules useful enough to make use of them though!

On top of that, that particular rule is in our style category – these are explicitly opinionated rules that don’t affect correctness of the code, but that we think push code towards a more modern, uniform style (think PEP8 for Python). We do expect some of these to be more contentious than rules in the correctness category, but in our opinion they are generally well reasoned and justified.

I would be open to modifying the rule to enforce consistency rather than requiring double-quotes in particular. I recently learned that a lot of JavaScript devs insist on single-quotes, so my assertion that double-quotes are considered the default in other languages may have been a bit too strong. Ruff has this rule that can toggled one way or the other, though clashes with the formatter which always enforces double-quotes. We could do something similar with double-quotes as the default, and single-quotes optional via a toml file setting.

I prefer double quotes, but ChatGPT often produces Fortran code with single quotes. In general, ChatGPT Fortran code mostly follows the conventions of Fortran code it has seen online (free format thankfully!), sometimes mistakenly following the conventions of other languages (declarations interspersed with executable code, without using BLOCK). So there is no clear convention in Fortran to use single or double quotes.

1 Like

Most people would agree with reaching a more uniform style, but everybody has its own idea about what should be the style. And this is especially true for an old language like Fortran.

Python PEP8 has been defined relatively early in the history of the language, and by the team who is developing the language. There’s nothing like that for Fortran

Yes, Fortran unfortunately has several cases where two different syntaxes have identical meanings, such as:

  • Uppercase and lowercase for keywords (END IF and end if)
  • single / double quotes
  • end if and endif (the same for all other end)
  • < and .lt.
  • [] and (/ /)

And consequently people use both, and each such thing slightly adds to the cognitive barrier what one has to think about when coding. For example it’s something I notice when looking at code, that it uses (/ /) or .gt. and I should not be spending any time on that, and rather focus on the actual algorithm. LFortran does warn about a few things above by default, but it can get annoying as well, so configurability is key. I am big fan of this.

I propose the following as the “recommendation”:

  • lowercase for keywords
  • double quotes
  • end if
  • <
  • []

And warn about the other alternative. But as said above, different projects will make different choices, so as long as it is easily configurable, there should be no problem.

2 Likes

Certainly, and that’s one of the motivations for projects like this: to have an opinion on both best practices and style. You may not agree with our opinion, that’s fine, you can turn those rules off. Our default rules are those that we think are best for most projects. We have a bunch of rules turned off by default, because although we think they’re useful and sensible, they’re more pedantic than the default ones. You can see our list of rules for which ones are turn on :play_button: or off :pause_button: by default. As I said above, hopefully enough people find them useful enough to use! :slight_smile:

Our style choices are partly our opinions about what looks good or makes sense, but also partly by existing practice. I have over 200 open source projects downloaded, with more than 40 million lines of Fortran, and we can very quickly search for things like how do people spell intent(inout) (overwhelmingly without whitespace) or end if (2:1 as two words vs one word), or use single vs double quotes with print*, (about 50/50).

Indeed, this matches our recommendations :slight_smile:

I doubt anyone would question #4, #5, and perhaps #3. I generally use lower case for most things. But sometimes one wants to emphasize certain things. For example in the ESMF library, the ESMF prefixes in procedure and type names are always capitalized, and camel case is used. Also things like preprocessor macros are generally capitalized - just as they are generally capitalized in C/C++. But almost all other coding is done in lower case.

When it comes to apostrophes vs quotes, I’m lazy. Quote requires holding down the shift key, and apostrophie does not… :smiley:

and shows why having options is important. We have found .ge. is far less error prone and is more consistent with user-defined dot operators and that not using the space works far more reliably with external Linux commands like grep and so forth. Lowercase is strongly preferred but parameters are allowed to be all caps in deference to some old C programmers I think, as well as macro names for cpp/fpp processing. is basically required over \( and \) . No rule about quotes at all, but leaning towards preferring double quotes recently. So as long as the default on stylistic options is to each his own good enough. Some of this can waste a lot of resources, like arguing over what a good indentation is (virtually everyone here uses three, but some insist on four, two, and one. No indenting is not allowed). Oh yeah, now that line lengths are much longer some use tab stops, which I personally do not like but others have no problems with.

I find it interesting that in my circle most everyone prefers .ge. that had Fortran as a first language, but everyone who started with C or most other languages seems to like >. It used to be some terminals did not have < and > keys but that reason is gone, but far easier to type and to read for those used to it by many peoples’ standards. Wondering if that is a general rule that if your first language was Fortran you prefer the dot operators(?)

Uppercase for parameters and macros is a very good point. Hopefully one day we’ll get fortitude to the point of being able to enforce such a convention!

This is one of the reasons why I’m strongly in favour of automated tools (such as ours, natch, but also black, ruff, rustfmt, clang-format) for enforcing style. We can argue till the cows come home about indentation, tabs vs space, etc etc, but most people at least agree consistency is good, so we can just sidestep the argument entirely by saying “well this is what the tool says, good enough”. Some customisation per project is fine, but hopefully the tool has sensible enough defaults that most people are happy enough out with it out of the box.

For me, < vs <= is much quicker to recognise than .gt. vs .ge.

2 Likes