Check for unused usage
Make sure that everything listed in the "only" list of a "use" statement is, in fact, used. Obviously this can only work where there is an "only" list.
This is quite a knotty problem.
Firstly, anything which can be made "public" may appear in the "only" list. According to the Fortran standard this means: "Each use-name shall be the name of a named variable, procedure, derived type, named constant or namelist group."
The first problem is that this definition includes operator(...) definitions. Detecting those would require considerable logic to interrogate the source module and understand the type of all operations. Probably best to just acknowledge that as a known shortcoming and move on.
The next thing to do is confirm my suspicion that local definitions can override something which has been brought in by a "use". This makes sense as usage is really just a scope trick. It's like importing a bunch of declarations at the point where the use appears. They are slightly complicated by the fact that things can be renamed by a usage.
Talking of scope, a definition can appear at module scope, type scope, procedure scope or block scope. "Use" statements may appear at module scope and procedure scope.
Potential solutions include a depth-first tree walk which bubbles unused names to the level above.
At the lowest level in the scope tree all used names are noted in a set. This set is pruned by the definitions at that level, then passed up. The next level up adds usages then resolves everything declared at that level. This process iterates over the whole tree. When a "use" is encountered the "only" list can be compared against the set of outstanding names. Anything appearing in the "only" list but not the outstanding names list is unused. Everything which appears in both is removed from the outstanding list.
A top-down approach would also work. Descend the scope tree noting every declaration (including usage) in a list of sets, one list for each level in the scope tree. For each reference to a name, find it in the list of sets starting at the "bottom" and working up, mark the first occurrence found as having been "hit". It needs to remain so additional references do not go further up the list.
After the whole scope tree has been traversed any usage declarations which remain un-"hit" have not been used.
Here is a little example code which might form the basis of testing. uses.zip
I just had a little go with your example code and found fparser doesn't understand block statements. A minimal example results in a syntax error and no tree is created. Probably something to bring up with the fparser team.
Is it worth simplifying the requirement?
Really, we care most if an unused use statement causes a wasteful dependency resulting in code being unnecessarily compiled.
Therefore, we can parse once to collect all things that are used and then parse again to make sure they are all called at least once. If something is declared twice in two scoping units, but only referenced in one, it's not terribly important.
We would miss issues where two scoping units use something with the same name but in different modules, but that feels like a rare scenario that a sensible coder ought to resolve by locally renaming at least one of them. Perhaps a separate rule could require that identical names are locally renamed so all names are unique.
Other issues (an item used but not called in one scoping unit and called but not used in another) would be identified by a compile failure.