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 to
plugin.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 removing
Inspection suffix. (
Some.html for inspection class
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 be
ProblemHighlightType.GENERIC_ERROR_OR_WARNING or empty (there’s a correspondent overload for
ProblemsHolder.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 (
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.
resolveToCall() functions can be used if the single descriptor or resolved call is needed. It’s better to call
analyze for obtaining
BindingTrace and use it for fetching other resolution results when inspection requires multiple resolve calls in a row.
resolveToDesciptor() 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 in
SmartPsiElementPointer 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 (check
SmartPsiElementPointer class and
createSmartPointer() function) can be used when such storage is absolutely necessary.
It’s possible to obtain
LanguageVersionSettings directly from
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).