The IntelliJ Platform provides tools designed for static code analysis called code inspections, which help the user maintain and clean up code without actually executing it. Custom code inspections can be implemented as IntelliJ Platform plugins. An example of the plugin approach is the comparing_string_references_inspection code sample.
See the Inspections topic in the IntelliJ Platform UI Guidelines on naming, writing description, and message texts for inspections.
Creating an Inspection Plugin
The comparing_string_references_inspection code sample adds a new inspection to the Java | Probable Bugs group in the Inspections list. The inspection reports when the
!= operator is used between String expressions.
It illustrates the components for a custom inspection plugin:
Describing an inspection in the plugin configuration file.
Implementing a local inspection class to inspect Java code in the editor.
Creating a visitor to traverse the PSI tree of the Java file being edited, inspecting for problematic syntax.
Implementing a quick fix class to correct syntax problems by altering the PSI tree as needed. Quick fixes are displayed to the user like intentions.
Writing an HTML description of the inspection for display in the inspection preferences panel.
Creating a test for the implemented inspection and quick fix.
Although the code sample illustrates implementations of these components, it is often useful to see examples of inspections implemented in the IntelliJ Community code base. To identify a given inspection's implementation classes, try to find an inspection by name or by UI texts. Consider also searching for existing implementations in IntelliJ Platform Explorer.
Creating an Inspection
The comparing_string_references_inspection code sample reports when the
!= operators are used between String expressions. The user can apply a quick fix to change
The details of the
comparing_string_references_inspection implementation illustrate the components of an inspection plugin.
Plugin Configuration File
comparing_string_references_inspection is described as a
com.intellij.localInspection extension point in the
comparing_string_references_inspection plugin configuration (
There exist two types of inspection extensions:
com.intellij.localInspectionextension point is used for inspections that operate on one file at a time, and also operate "on-the-fly" as the user edits the file.
com.intellij.globalInspectionextension point is used for inspections that operate across multiple files, and the associated fix might, for example, refactor code between files.
The minimum inspection setup must declare the
language attribute (unless the inspection works on any supported language). As shown in the
comparing_string_references_inspection plugin configuration file, other attributes can be defined in the
localInspection element, either with or without localization. In most cases, it is simplest to define the attributes in the plugin configuration file because the underlying parent classes handle most of the class responsibilities based on the configuration file description.
If required, inspections can define all the attribute information (except
implementationClass) by overriding methods in the inspection implementation class (not recommended in general).
Inspection Implementation Java Class
Inspection implementations for Java files, like
ComparingStringReferencesInspection, are often based on the Java class
AbstractBaseJavaLocalInspectionTool base class offers methods to inspect Java classes, fields, and methods.
localInspection types are based on the class
LocalInspectionTool. Examining the class hierarchy for
LocalInspectionTool shows that the IntelliJ Platform provides many child inspection classes for a variety of languages and frameworks. One of these classes is a good basis for a new inspection implementation, but a bespoke implementation can also be based directly on
The primary responsibilities of the inspection implementation class are to provide:
PsiElementVisitorobject to traverse the PSI tree of the file being inspected.
LocalQuickFixclass to fix an identified problem (optional).
An options panel to be displayed in the Inspections settings dialog (optional). See Inspection Options for more details.
ComparingStringReferencesInspection methods are discussed in the sections below.
Visitor Implementation Class
The visitor class evaluates whether elements of the file's PSI tree are of interest to an inspection.
ComparingStringReferencesInspection.buildVisitor() method creates an anonymous visitor class based on
JavaElementVisitor to traverse the PSI tree of the Java file being edited, inspecting for suspect syntax. The anonymous class overrides
visitBinaryExpression(), which checks if a
PsiBinaryExpression's operator is
!=, and if both operand types are
Quick Fix Implementation
The quick fix class acts much like an intention, allowing the user to invoke it on the
TextRange) highlighted by the inspection.
ComparingStringReferencesInspection implementation uses the nested class
ReplaceWithEqualsQuickFix to implement a quick fix based on
ReplaceWithEqualsQuickFix class allows the user to change the use of
a == b and
a != b expression to
The heavy lifting is done in
ReplaceWithEqualsQuickFix.applyFix(), which manipulates the PSI tree to convert the expressions. The change to the PSI tree is accomplished by the usual approach to modification:
Creating a new
Substituting the original left and right operands into the new
Replacing the original binary expression with the
The inspection description is an HTML file. The description is displayed in the upper right panel of the Inspections settings dialog when an inspection is selected from the list.
See the Inspections topic in the IntelliJ Platform UI Guidelines on important guidelines for writing the description.
Implicit in using
LocalInspectionTool in the class hierarchy of the inspection implementation means following some conventions.
The inspection description file is expected to be located under $RESOURCES_ROOT_DIRECTORY$/inspectionDescriptions/. If the inspection description file is to be located elsewhere, override
getDescriptionUrl()in the inspection implementation class.
The name of the description file is expected to be the inspection $SHORT_NAME$.html as provided by the inspection description, or the inspection implementation class. If a short name is not provided, the IntelliJ Platform computes one by removing
Inspectionsuffix from the implementation class name.
Using the following HTML structure, the description can embed code snippets that will be displayed with syntax highlighting:
The language will be set according to the inspection's registration
language attribute. If required (e.g., when targeting UAST), it can be specified explicitly via
To open related settings directly from the inspection description, add a link with
settings://$CONFIGURABLE_ID$, optionally followed by
?$SEARCH_STRING$ to pre-select UI element:
comparing_string_references_inspection code sample provides a test for the inspection. See the Testing Overview section for general information about plugin testing.
comparing_string_references_inspection test is based on the
UsefulTestCase class, part of the JUnit framework APIs. This class handles much of the underlying boilerplate for tests.
By convention, the folder test/testData/ contains the test files. The folder contains pairs of files for each test using the name convention ∗.java and ∗.after.java, e.g., Eq.java/Eq.after.java.
comparing_string_references_inspection tests run the inspection on the ∗.java files, apply the quick fix, and compare the results with the respective ∗.after.java files containing expected results.
Running the Comparing String References Inspection Code Sample
The comparing_string_references_inspection code sample adds a new inspection to the Java | Probable Bugs group in the Inspections configuration.
See Code Samples on how to set up and run the plugin.
How does it work?
The plugin inspects your code opened in the IntelliJ IDEA editor. The plugin highlights the code fragments where two
String expressions are compared by
!= and proposes to replace this code fragment with
In this example, the
str2 are variables of the String type. Invoking SDK: Use equals() replaces: