The question is about static compile time analysis. Let's say there is a file-level const val
:
const val SOME_CONST_VAL = "SOME_CONST_VAL"
How is it possible to get the number of usages for this SOME_CONST_VAL
?
To clarify:
Checked:
What is the preferable direction here?
After a while of trial and error (mostly error 🙂) I can partially answer my question.
I tried to avoid the path with compiler plugins because there are no lots of materials on it. The official Kotlin documentation provides only examples of such plugins - no overview or at least basic conceptual notes, various 3rd-party articles mostly recommend to guide yourself by reviewing existing compiler plugins. At the same time the corresponding APIs can tend to change, so I decided to postpone this way.
My focus was concentrated on attempts to extend detekt for this. Here are some observations:
The scope of visitors used to analyze the code is restricted to separate source KtFile
s. Unsure if can reference some detekt documentation page stating this, but it can be inferred from its APIs (also, could find the direct answer stating it in the corresponding GitHub discussions).
The consequence of #1 is that there is no way to write such rule: there are no methods for rule implementations to do some processing after all files have been visited. And if we do the required check on each file visited, we won't have enough information to state whether some variable is used in the entire codebase or not. Of course there can be attempts to do dirty workarounds - for example by using static collections to accumulate visited references and trigger their whole verification in the end, but it doesn't seem stable.
It can seem possible to write a custom processor instead as it has a callback triggered when all files have been visited. But in this case we are encountering limitations in the way how detekt allows to report for processors - it provides only means of quantitative reporting. Of course it's possible to include everything we want to report into the ProjectMetric::type
string, but I guess it can be restricted one day.
There is no way to operate with something resembling a dependency tree for all variable and various other references. The code analysis is more like a token-based string reading. I tried to play with some heuristics based on the usage of FullQualifiedNameGuesser
, but it doesn't provide stable results on attempts to find a declaration of some usage.
Even if all the points above can be solved with some workaround, it's going to have a huge performance overkill as we essentially collect all declarations and all references throughout the entire codebase and match them eventually.
To sum up: I think that extending detekt by its available APIs doesn't allow to solve the problem described in the question. Going to check something else.
Update (20.4.23) - tried Qodana, the UnusedSymbol
inspection does something similar (a little bit from the opposite side), but it's not very extensible (by the code means) and requires Docker to run. It's also possible to use Structural Search and export its templates to be run with Qodana, but again it seems not quite something I need.
Update (22.5.23) - well, as it was assumed originally, the Kotlin compiler plugin path turns out to be the most suitable for the task described in the initial question. It allows to solve the drawbacks related to linters described above and fits all target conditions stated in the question. A couple of notes:
I think the original question is closed now, see no reasons to add any implementation details as it was formulated as: "What is the preferable direction here?"