Some general information about writing inspection, intention and quick fixes available in Code Inspections document.
It's important to know about PSI.
Some PSI viewer will be extremely useful. It might be built-in Psi Viewer that is
going to be enabled when testing inspections with IDEA
run configuration (idea.is.internal=true must be set to active it in Kotlin
project). Alternately, external plugin PsiViewer can be installed.
-
Each inspection should be registered in
plugin-common.xml
. This file is included toplugin.xml
for all plugins obtained from Kotlin source code. -
Each inspection should be added with the description. The description file should be named after correspondent inspection class with adding
*.html
extension and removingInspection
suffix. (Some.html
for inspection classSomeInspection
). -
All inspections should have automatic tests. A default place for such tests is
inspectionsLocal
group (inspections
group can be used if there are no quick fixes available for the inspection). -
ProblemHighlightType
should always beProblemHighlightType.GENERIC_ERROR_OR_WARNING
or empty (there's a correspondent overload forProblemsHolder.registerProblem()
), otherwise, it won't be possible to individually change the desired level in the inspection settings. -
Inspection highlighting range shouldn't be too wide. For example, to show some problem in a class it's much better to highlight its name than to highlight the whole class (the latter just looks very nasty).
-
Resolve operations (
analyze
,resolveToCall
,resolveToDescriptors
) are considered to be expensive and shouldn't be triggered more often than it's absolutely needed. All possible checks should be applied on PSI or file text before actual resolve. -
resolveToDescriptorIfAny()
/resolveToCall()
functions can be used if the single descriptor or resolved call is needed. It's better to callanalyze
for obtainingBindingTrace
and use it for fetching other resolution results when inspection requires multiple resolve calls in a row. -
Prefer
resolveToDescriptorIfAny()
overresolveToDesciptor()
because of exceptions that are thrown from latter function when descriptor is absent. -
Any checks about code state and resolve that were made during reporting an inspection problem an registering a quick fix might be invalidated by the moment of the actual quick fix execution. Avoid the code that can throw exceptions because of that. Re-checks with early exit from the quick fix can be used to workaround it.
-
Intentions and quick fixes execution happens in the UI thread so do not call long operations such as usages search or resolve to avoid freezes.
PSI
elements obtained from resolve during the applicability check can be stored inSmartPsiElementPointer
for the postponed modification in quick-fixes. All complex searches should be executed in a background thread under a progress indicator. Some tests already assert that resolve operations are not called from UI thread while applying fixes. -
There shouldn't be PSI elements stored in QuickFix classes (
val psi: PsiElement
) as such elements might be invalidated and can lead to memory leaks. Smart pointer (checkSmartPsiElementPointer
class andcreateSmartPointer()
function) can be used when such storage is absolutely necessary. -
It's possible to obtain
LanguageVersionSettings
directly frompsiElement
withlanguageVersionSettings
extension. -
Kotlin project itself can be used for new inspection testing. Build a test plugin with the new inspection included, open another instance of Kotlin project and run inspection on the whole project (
Run Inspection By Name
action).- Check the execution time and consider performance issues?
- Check the memory consumption.
- Check the found problems for false positive.
- Try to apply a quick fix and check result.