mirror of
https://github.com/JetBrains/intellij-sdk-code-samples.git
synced 2025-07-28 01:07:49 +08:00
New topic: Inspection options (#988)
This commit is contained in:
parent
2744611193
commit
f2906872cf
2
.github/dependabot.yml
vendored
2
.github/dependabot.yml
vendored
@ -12,7 +12,7 @@ updates:
|
||||
schedule:
|
||||
interval: "daily"
|
||||
- package-ecosystem: "gradle"
|
||||
directory: "/code_samples/comparing_references_inspection/"
|
||||
directory: "/code_samples/comparing_string_references_inspection/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
- package-ecosystem: "gradle"
|
||||
|
2
.github/workflows/code-samples.yml
vendored
2
.github/workflows/code-samples.yml
vendored
@ -29,7 +29,7 @@ jobs:
|
||||
matrix:
|
||||
plugin:
|
||||
- action_basics
|
||||
- comparing_references_inspection
|
||||
- comparing_string_references_inspection
|
||||
- conditional_operator_intention
|
||||
- editor_basics
|
||||
- facet_basics
|
||||
|
9
.idea/gradle.xml
generated
9
.idea/gradle.xml
generated
@ -12,9 +12,9 @@
|
||||
<project path="$PROJECT_DIR$/code_samples/action_basics" />
|
||||
</projects>
|
||||
</build>
|
||||
<build path="$PROJECT_DIR$/code_samples/comparing_references_inspection" name="comparing_references_inspection">
|
||||
<build path="$PROJECT_DIR$/code_samples/comparing_string_references_inspection" name="comparing_string_references_inspection">
|
||||
<projects>
|
||||
<project path="$PROJECT_DIR$/code_samples/comparing_references_inspection" />
|
||||
<project path="$PROJECT_DIR$/code_samples/comparing_string_references_inspection" />
|
||||
</projects>
|
||||
</build>
|
||||
<build path="$PROJECT_DIR$/code_samples/conditional_operator_intention" name="conditional_operator_intention">
|
||||
@ -105,16 +105,13 @@
|
||||
</builds>
|
||||
</compositeBuild>
|
||||
</compositeConfiguration>
|
||||
<option name="delegatedBuild" value="true" />
|
||||
<option name="testRunner" value="GRADLE" />
|
||||
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$/code_samples/_gradleCompositeBuild" />
|
||||
<option name="gradleJvm" value="11" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$/code_samples/_gradleCompositeBuild" />
|
||||
<option value="$PROJECT_DIR$/code_samples/action_basics" />
|
||||
<option value="$PROJECT_DIR$/code_samples/comparing_references_inspection" />
|
||||
<option value="$PROJECT_DIR$/code_samples/comparing_string_references_inspection" />
|
||||
<option value="$PROJECT_DIR$/code_samples/conditional_operator_intention" />
|
||||
<option value="$PROJECT_DIR$/code_samples/editor_basics" />
|
||||
<option value="$PROJECT_DIR$/code_samples/facet_basics" />
|
||||
|
@ -35,9 +35,9 @@ Please see [Code Samples][docs:code-samples] topic on how to import and run code
|
||||
In the following table, you may find all available samples provided in the separated directories as stand-alone projects available for running with the Gradle [`runIde`](tools_gradle_intellij_plugin.md#tasks-runide) task.
|
||||
|
||||
| Code Sample | Description |
|
||||
|------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
|-----------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| [Action Basics](./action_basics) | Action and Action Group patterns implementation, adds entries to the Tools menu. |
|
||||
| [Comparing References Inspection](./comparing_references_inspection) | Local Inspection Tool, adds entries to **Settings | Editor | Inspections | Java | Probable Bugs**. |
|
||||
| [Comparing References Inspection](./comparing_string_references_inspection) | Local Inspection Tool, adds entries to **Settings | Editor | Inspections | Java | Probable Bugs**. |
|
||||
| [Conditional Operator Intention](./conditional_operator_intention) | Intention action, suggests converting a ternary operator into an `if` block and adds entry to **Settings | Editor | Intentions | SDK Intentions**. |
|
||||
| [Editor Basics](./editor_basics) | Basic Editor APIs example with editor popup menu with extra actions. |
|
||||
| [Framework Basics](./framework_basics) | Basic *SDK Demo Framework* support added to the **File | New | Project | Java** wizard. |
|
||||
|
@ -5,7 +5,7 @@
|
||||
rootProject.name = "SDK Code Samples"
|
||||
|
||||
includeBuild("../action_basics")
|
||||
includeBuild("../comparing_references_inspection")
|
||||
includeBuild("../comparing_string_references_inspection")
|
||||
includeBuild("../conditional_operator_intention")
|
||||
includeBuild("../editor_basics")
|
||||
includeBuild("../facet_basics")
|
||||
|
@ -1,203 +0,0 @@
|
||||
// Copyright 2000-2022 JetBrains s.r.o. and other contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
|
||||
package org.intellij.sdk.codeInspection;
|
||||
|
||||
import com.intellij.codeInspection.*;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.ui.DocumentAdapter;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import java.awt.*;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import static com.siyeh.ig.psiutils.ExpressionUtils.isNullLiteral;
|
||||
|
||||
/**
|
||||
* Implements an inspection to detect when object references are compared using 'a==b' or 'a!=b'.
|
||||
* The quick fix converts these comparisons to 'a.equals(b) or '!a.equals(b)' respectively.
|
||||
*/
|
||||
public class ComparingReferencesInspection extends AbstractBaseJavaLocalInspectionTool {
|
||||
|
||||
// Defines the text of the quick fix intention
|
||||
public static final String QUICK_FIX_NAME = "SDK: " +
|
||||
InspectionsBundle.message("inspection.comparing.references.use.quickfix");
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.ComparingReferencesInspection");
|
||||
private final CriQuickFix myQuickFix = new CriQuickFix();
|
||||
// This string holds a list of classes relevant to this inspection.
|
||||
@SuppressWarnings({"WeakerAccess"})
|
||||
@NonNls
|
||||
public String CHECKED_CLASSES = "java.lang.String;java.util.Date";
|
||||
|
||||
/**
|
||||
* This method is called to get the panel describing the inspection.
|
||||
* It is called every time the user selects the inspection in preferences.
|
||||
* The user has the option to edit the list of {@link #CHECKED_CLASSES}.
|
||||
*
|
||||
* @return panel to display inspection information.
|
||||
*/
|
||||
@Override
|
||||
public JComponent createOptionsPanel() {
|
||||
JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT));
|
||||
final JTextField checkedClasses = new JTextField(CHECKED_CLASSES);
|
||||
checkedClasses.getDocument().addDocumentListener(new DocumentAdapter() {
|
||||
public void textChanged(@NotNull DocumentEvent event) {
|
||||
CHECKED_CLASSES = checkedClasses.getText();
|
||||
}
|
||||
});
|
||||
panel.add(checkedClasses);
|
||||
return panel;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is overridden to provide a custom visitor.
|
||||
* that inspects expressions with relational operators '==' and '!='.
|
||||
* The visitor must not be recursive and must be thread-safe.
|
||||
*
|
||||
* @param holder object for visitor to register problems found.
|
||||
* @param isOnTheFly true if inspection was run in non-batch mode
|
||||
* @return non-null visitor for this inspection.
|
||||
* @see JavaElementVisitor
|
||||
*/
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
|
||||
return new JavaElementVisitor() {
|
||||
|
||||
/**
|
||||
* This string defines the short message shown to a user signaling the inspection found a problem.
|
||||
* It reuses a string from the inspections bundle.
|
||||
*/
|
||||
@NonNls
|
||||
private final String DESCRIPTION_TEMPLATE = "SDK " +
|
||||
InspectionsBundle.message("inspection.comparing.references.problem.descriptor");
|
||||
|
||||
/**
|
||||
* Avoid defining visitors for both Reference and Binary expressions.
|
||||
*
|
||||
* @param psiReferenceExpression The expression to be evaluated.
|
||||
*/
|
||||
@Override
|
||||
public void visitReferenceExpression(PsiReferenceExpression psiReferenceExpression) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate binary psi expressions to see if they contain relational operators '==' and '!=', AND they contain
|
||||
* classes contained in CHECKED_CLASSES. The evaluation ignores expressions comparing an object to null.
|
||||
* IF this criteria is met, add the expression to the problems list.
|
||||
*
|
||||
* @param expression The binary expression to be evaluated.
|
||||
*/
|
||||
@Override
|
||||
public void visitBinaryExpression(PsiBinaryExpression expression) {
|
||||
super.visitBinaryExpression(expression);
|
||||
IElementType opSign = expression.getOperationTokenType();
|
||||
if (opSign == JavaTokenType.EQEQ || opSign == JavaTokenType.NE) {
|
||||
// The binary expression is the correct type for this inspection
|
||||
PsiExpression lOperand = expression.getLOperand();
|
||||
PsiExpression rOperand = expression.getROperand();
|
||||
if (rOperand == null || isNullLiteral(lOperand) || isNullLiteral(rOperand)) {
|
||||
return;
|
||||
}
|
||||
// Nothing is compared to null, now check the types being compared
|
||||
PsiType lType = lOperand.getType();
|
||||
PsiType rType = rOperand.getType();
|
||||
if (isCheckedType(lType) || isCheckedType(rType)) {
|
||||
// Identified an expression with potential problems, add to list with fix object.
|
||||
holder.registerProblem(expression,
|
||||
DESCRIPTION_TEMPLATE, myQuickFix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies the input is the correct {@code PsiType} for this inspection.
|
||||
*
|
||||
* @param type The {@code PsiType} to be examined for a match
|
||||
* @return {@code true} if input is {@code PsiClassType} and matches one of the classes
|
||||
* in the {@link ComparingReferencesInspection#CHECKED_CLASSES} list.
|
||||
*/
|
||||
private boolean isCheckedType(PsiType type) {
|
||||
if (!(type instanceof PsiClassType)) {
|
||||
return false;
|
||||
}
|
||||
StringTokenizer tokenizer = new StringTokenizer(CHECKED_CLASSES, ";");
|
||||
while (tokenizer.hasMoreTokens()) {
|
||||
String className = tokenizer.nextToken();
|
||||
if (type.equalsToText(className)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* This class provides a solution to inspection problem expressions by manipulating the PSI tree to use 'a.equals(b)'
|
||||
* instead of '==' or '!='.
|
||||
*/
|
||||
private static class CriQuickFix implements LocalQuickFix {
|
||||
|
||||
/**
|
||||
* Returns a partially localized string for the quick fix intention.
|
||||
* Used by the test code for this plugin.
|
||||
*
|
||||
* @return Quick fix short name.
|
||||
*/
|
||||
@NotNull
|
||||
@Override
|
||||
public String getName() {
|
||||
return QUICK_FIX_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method manipulates the PSI tree to replace 'a==b' with 'a.equals(b)' or 'a!=b' with '!a.equals(b)'.
|
||||
*
|
||||
* @param project The project that contains the file being edited.
|
||||
* @param descriptor A problem found by this inspection.
|
||||
*/
|
||||
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
|
||||
try {
|
||||
PsiBinaryExpression binaryExpression = (PsiBinaryExpression) descriptor.getPsiElement();
|
||||
IElementType opSign = binaryExpression.getOperationTokenType();
|
||||
PsiExpression lExpr = binaryExpression.getLOperand();
|
||||
PsiExpression rExpr = binaryExpression.getROperand();
|
||||
if (rExpr == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory();
|
||||
PsiMethodCallExpression equalsCall =
|
||||
(PsiMethodCallExpression) factory.createExpressionFromText("a.equals(b)", null);
|
||||
|
||||
equalsCall.getMethodExpression().getQualifierExpression().replace(lExpr);
|
||||
equalsCall.getArgumentList().getExpressions()[0].replace(rExpr);
|
||||
|
||||
PsiExpression result = (PsiExpression) binaryExpression.replace(equalsCall);
|
||||
|
||||
if (opSign == JavaTokenType.NE) {
|
||||
PsiPrefixExpression negation = (PsiPrefixExpression) factory.createExpressionFromText("!a", null);
|
||||
negation.getOperand().replace(result);
|
||||
result.replace(negation);
|
||||
}
|
||||
} catch (IncorrectOperationException e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getFamilyName() {
|
||||
return getName();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
<!-- Copyright 2000-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. -->
|
||||
<html>
|
||||
<body>
|
||||
Reports usages of <code>==</code> and <code>!=</code> when comparing instances of specified types.
|
||||
<p>
|
||||
Quick-fix replaces operator with <code>equals()</code> call.
|
||||
</p>
|
||||
<!-- tooltip end -->
|
||||
<p>Configure the inspection:</p>
|
||||
List classes to be inspected separated by semi-colon.
|
||||
</body>
|
||||
</html>
|
@ -1,6 +0,0 @@
|
||||
public class Eq {
|
||||
public boolean compare2Strings(java.lang.String s1, java.lang.String s2) {
|
||||
return (s1.equals(s2));
|
||||
}
|
||||
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
public class Eq {
|
||||
public boolean compare2Strings(java.lang.String s1, java.lang.String s2) {
|
||||
return (<caret>s1 == s2);
|
||||
}
|
||||
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
public class Neq {
|
||||
public boolean compare2Dates(java.util.Date dt1, java.util.Date dt2){
|
||||
return (!dt1.equals(dt2));
|
||||
}
|
||||
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
public class Neq {
|
||||
public boolean compare2Dates(java.util.Date dt1, java.util.Date dt2){
|
||||
return (dt1 <caret>!= dt2);
|
||||
}
|
||||
|
||||
}
|
@ -5,14 +5,14 @@
|
||||
|
||||
Comparing References Inspection Sample demonstrates the implementation of the [Code Inspections][docs:code_inspections] feature for Java classes.
|
||||
|
||||
The plugin inspects your Java code and highlights any fragments containing the comparison of two `String` or `Date` variables.
|
||||
The plugin inspects your Java code and highlights any fragments containing the comparison of two `String` variables.
|
||||
If such a check finds a comparison using the `==` or !`=` operators instead of the `.equals()` method, the plugin proposes a *quick-fix* action.
|
||||
|
||||
### Extension Points
|
||||
|
||||
| Name | Implementation | Extension Point Class |
|
||||
|--------------------------------|---------------------------------------------------------------------|---------------------------------------|
|
||||
| `com.intellij.localInspection` | [ComparingReferencesInspection][file:ComparingReferencesInspection] | `AbstractBaseJavaLocalInspectionTool` |
|
||||
|--------------------------------|---------------------------------------------------------------------------------|---------------------------------------|
|
||||
| `com.intellij.localInspection` | [ComparingStringReferencesInspection][file:ComparingStringReferencesInspection] | `AbstractBaseJavaLocalInspectionTool` |
|
||||
|
||||
*Reference: [Plugin Extension Points in IntelliJ SDK Docs][docs:ep]*
|
||||
|
||||
@ -21,4 +21,4 @@ If such a check finds a comparison using the `==` or !`=` operators instead of t
|
||||
[docs:code_inspections]: https://plugins.jetbrains.com/docs/intellij/code-inspections.html
|
||||
[docs:ep]: https://plugins.jetbrains.com/docs/intellij/plugin-extensions.html
|
||||
|
||||
[file:ComparingReferencesInspection]: ./src/main/java/org/intellij/sdk/codeInspection/ComparingReferencesInspection.java
|
||||
[file:ComparingStringReferencesInspection]: ./src/main/java/org/intellij/sdk/codeInspection/ComparingStringReferencesInspection.java
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
|
||||
plugins {
|
||||
id("java")
|
||||
@ -36,10 +36,4 @@ tasks {
|
||||
sinceBuild.set("221")
|
||||
untilBuild.set("223.*")
|
||||
}
|
||||
|
||||
test {
|
||||
// Set idea.home.path to the absolute path to the intellij-community source
|
||||
// on your local machine.
|
||||
systemProperty("idea.home.path", "/Users/jhake/Documents/source/comm")
|
||||
}
|
||||
}
|
@ -1,3 +1,3 @@
|
||||
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
|
||||
rootProject.name = "comparing_references_inspection"
|
||||
rootProject.name = "comparing_string_references_inspection"
|
@ -0,0 +1,139 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
|
||||
package org.intellij.sdk.codeInspection;
|
||||
|
||||
import com.intellij.codeInspection.AbstractBaseJavaLocalInspectionTool;
|
||||
import com.intellij.codeInspection.LocalQuickFix;
|
||||
import com.intellij.codeInspection.ProblemDescriptor;
|
||||
import com.intellij.codeInspection.ProblemsHolder;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static com.siyeh.ig.psiutils.ExpressionUtils.isNullLiteral;
|
||||
|
||||
/**
|
||||
* Implements an inspection to detect when String references are compared using 'a==b' or 'a!=b'.
|
||||
* The quick fix converts these comparisons to 'a.equals(b) or '!a.equals(b)' respectively.
|
||||
*/
|
||||
public class ComparingStringReferencesInspection extends AbstractBaseJavaLocalInspectionTool {
|
||||
|
||||
private final ReplaceWithEqualsQuickFix myQuickFix = new ReplaceWithEqualsQuickFix();
|
||||
|
||||
/**
|
||||
* This method is overridden to provide a custom visitor
|
||||
* that inspects expressions with relational operators '==' and '!='.
|
||||
* The visitor must not be recursive and must be thread-safe.
|
||||
*
|
||||
* @param holder object for the visitor to register problems found
|
||||
* @param isOnTheFly true if inspection was run in non-batch mode
|
||||
* @return non-null visitor for this inspection
|
||||
* @see JavaElementVisitor
|
||||
*/
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
|
||||
return new JavaElementVisitor() {
|
||||
|
||||
/**
|
||||
* Evaluate binary psi expressions to see if they contain relational operators '==' and '!=',
|
||||
* AND they are of String type.
|
||||
* The evaluation ignores expressions comparing an object to null.
|
||||
* IF these criteria are met, register the problem in the ProblemsHolder.
|
||||
*
|
||||
* @param expression The binary expression to be evaluated.
|
||||
*/
|
||||
@Override
|
||||
public void visitBinaryExpression(PsiBinaryExpression expression) {
|
||||
super.visitBinaryExpression(expression);
|
||||
IElementType opSign = expression.getOperationTokenType();
|
||||
if (opSign == JavaTokenType.EQEQ || opSign == JavaTokenType.NE) {
|
||||
// The binary expression is the correct type for this inspection
|
||||
PsiExpression lOperand = expression.getLOperand();
|
||||
PsiExpression rOperand = expression.getROperand();
|
||||
if (rOperand == null || isNullLiteral(lOperand) || isNullLiteral(rOperand)) {
|
||||
return;
|
||||
}
|
||||
// Nothing is compared to null, now check the types being compared
|
||||
if (isStringType(lOperand) || isStringType(rOperand)) {
|
||||
// Identified an expression with potential problems, register problem with the quick fix object
|
||||
holder.registerProblem(expression,
|
||||
InspectionBundle.message("inspection.comparing.string.references.problem.descriptor"),
|
||||
myQuickFix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isStringType(PsiExpression operand) {
|
||||
PsiType type = operand.getType();
|
||||
if (!(type instanceof PsiClassType)) {
|
||||
return false;
|
||||
}
|
||||
PsiClass resolvedType = ((PsiClassType) type).resolve();
|
||||
if (resolvedType == null) {
|
||||
return false;
|
||||
}
|
||||
return "java.lang.String".equals(resolvedType.getQualifiedName());
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* This class provides a solution to inspection problem expressions by manipulating the PSI tree to use 'a.equals(b)'
|
||||
* instead of '==' or '!='.
|
||||
*/
|
||||
private static class ReplaceWithEqualsQuickFix implements LocalQuickFix {
|
||||
|
||||
/**
|
||||
* Returns a partially localized string for the quick fix intention.
|
||||
* Used by the test code for this plugin.
|
||||
*
|
||||
* @return Quick fix short name.
|
||||
*/
|
||||
@NotNull
|
||||
@Override
|
||||
public String getName() {
|
||||
return InspectionBundle.message("inspection.comparing.string.references.use.quickfix");
|
||||
}
|
||||
|
||||
/**
|
||||
* This method manipulates the PSI tree to replace 'a==b' with 'a.equals(b)' or 'a!=b' with '!a.equals(b)'.
|
||||
*
|
||||
* @param project The project that contains the file being edited.
|
||||
* @param descriptor A problem found by this inspection.
|
||||
*/
|
||||
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
|
||||
PsiBinaryExpression binaryExpression = (PsiBinaryExpression) descriptor.getPsiElement();
|
||||
IElementType opSign = binaryExpression.getOperationTokenType();
|
||||
PsiExpression lExpr = binaryExpression.getLOperand();
|
||||
PsiExpression rExpr = binaryExpression.getROperand();
|
||||
if (rExpr == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory();
|
||||
PsiMethodCallExpression equalsCall =
|
||||
(PsiMethodCallExpression) factory.createExpressionFromText("a.equals(b)", null);
|
||||
|
||||
equalsCall.getMethodExpression().getQualifierExpression().replace(lExpr);
|
||||
equalsCall.getArgumentList().getExpressions()[0].replace(rExpr);
|
||||
|
||||
PsiExpression result = (PsiExpression) binaryExpression.replace(equalsCall);
|
||||
|
||||
if (opSign == JavaTokenType.NE) {
|
||||
PsiPrefixExpression negation = (PsiPrefixExpression) factory.createExpressionFromText("!a", null);
|
||||
negation.getOperand().replace(result);
|
||||
result.replace(negation);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getFamilyName() {
|
||||
return getName();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
|
||||
package org.intellij.sdk.codeInspection;
|
||||
|
||||
import com.intellij.DynamicBundle;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.PropertyKey;
|
||||
|
||||
public final class InspectionBundle extends DynamicBundle {
|
||||
|
||||
private static final InspectionBundle ourInstance = new InspectionBundle();
|
||||
|
||||
@NonNls
|
||||
public static final String BUNDLE = "messages.InspectionBundle";
|
||||
|
||||
private InspectionBundle() {
|
||||
super(BUNDLE);
|
||||
}
|
||||
|
||||
public static @Nls String message(@NotNull @PropertyKey(resourceBundle = BUNDLE) String key,
|
||||
Object @NotNull ... params) {
|
||||
return ourInstance.getMessage(key, params);
|
||||
}
|
||||
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
<!-- Copyright 2000-2022 JetBrains s.r.o. and other contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. -->
|
||||
<!-- Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -->
|
||||
<!-- Plugin Configuration File. Read more: https://plugins.jetbrains.com/docs/intellij/plugin-configuration-file.html -->
|
||||
|
||||
<idea-plugin>
|
||||
@ -35,37 +35,37 @@
|
||||
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<!--
|
||||
Extend the IntelliJ Platform local inspection type, and connect it to the implementation class in this plugin.
|
||||
Extend the IntelliJ Platform local inspection type and connect it to the implementation class in this plugin.
|
||||
<localInspection> type element is applied within the scope of a file under edit.
|
||||
It is preferred over <inspectionToolProvider>
|
||||
@see intellij.platform.resources.LangExtensionPoints
|
||||
@see com.intellij.codeInspection.InspectionProfileEntry
|
||||
|
||||
Attributes:
|
||||
language= Language ID
|
||||
shortName= Not specified, will be computed by the underlying implementation classes.
|
||||
displayName= The string to be shown in the Preferences | Editor | Inspections panel
|
||||
The displayName gets registered to identify this inspection.
|
||||
Can be localized using key= and bundle= attributes instead.
|
||||
groupPath= Defines the outermost grouping for this inspection in
|
||||
- language - inspection language ID
|
||||
- shortName - not specified, will be computed by the underlying implementation classes
|
||||
- bundle - name of the message bundle for the "key" attribute
|
||||
- key - the key of the message to be shown in the Preferences | Editor | Inspections panel
|
||||
- groupPath - defines the outermost grouping for this inspection in
|
||||
the Preferences | Editor | Inspections panel. Not localized.
|
||||
groupBundle= Name of *.bundle file to translate groupKey.
|
||||
In this case reuse an IntelliJ Platform bundle file from intellij.platform.resources.en
|
||||
groupKey= Key to use for translation subgroup name using groupBundle file.
|
||||
In this case reuse the IntelliJ Platform subcategory "Probable bugs"
|
||||
enabledByDefault= Inspection state when Inspections panel is created.
|
||||
level= The default level of error found by this inspection, e.g. INFO, ERROR, etc.
|
||||
- groupBundle - the name of a message bundle file to translate groupKey
|
||||
In this case, reuse an IntelliJ Platform bundle file from intellij.platform.resources.en
|
||||
- groupKey - the key to use for translation subgroup name using groupBundle file.
|
||||
In this case, reuse the IntelliJ Platform subcategory "Probable bugs"
|
||||
- enabledByDefault - inspection state when the Inspections panel is created.
|
||||
- level - the default level of error found by this inspection, e.g. INFO, ERROR, etc.
|
||||
@see com.intellij.codeHighlighting.HighlightDisplayLevel
|
||||
implementationClass= FQN of inspection implementation
|
||||
- implementationClass= the fully-qualified name of the inspection implementation class
|
||||
-->
|
||||
<localInspection language="JAVA"
|
||||
displayName="SDK: '==' or '!=' used instead of 'equals()'"
|
||||
bundle="messages.InspectionBundle"
|
||||
key="inspection.comparing.string.references.display.name"
|
||||
groupPath="Java"
|
||||
groupBundle="messages.InspectionsBundle"
|
||||
groupKey="group.names.probable.bugs"
|
||||
enabledByDefault="true"
|
||||
level="WARNING"
|
||||
implementationClass="org.intellij.sdk.codeInspection.ComparingReferencesInspection"/>
|
||||
implementationClass="org.intellij.sdk.codeInspection.ComparingStringReferencesInspection"/>
|
||||
</extensions>
|
||||
|
||||
</idea-plugin>
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
@ -0,0 +1,9 @@
|
||||
<!-- Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -->
|
||||
<html>
|
||||
<body>
|
||||
Reports usages of <code>==</code> and <code>!=</code> when comparing instances of String.
|
||||
<p>
|
||||
Quick-fix replaces operator with <code>equals()</code> call.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,5 @@
|
||||
# Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
|
||||
inspection.comparing.string.references.display.name=SDK: '==' or '!=' used instead of 'equals()'
|
||||
inspection.comparing.string.references.problem.descriptor=SDK: String objects compared with equality operation
|
||||
inspection.comparing.string.references.use.quickfix=SDK: Use equals()
|
@ -1,23 +1,31 @@
|
||||
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
|
||||
package org.intellij.sdk.codeInspection;
|
||||
|
||||
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
|
||||
import com.intellij.codeInsight.intention.IntentionAction;
|
||||
import com.intellij.testFramework.TestDataPath;
|
||||
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Class for testing ComparingReferencesInspection.
|
||||
* Requires {@code idea.home.path} to be set in build.gradle.kts.
|
||||
* doTest() does the work for individual test cases.
|
||||
*/
|
||||
public class ComparingReferencesInspectionTest extends LightJavaCodeInsightFixtureTestCase {
|
||||
@TestDataPath("$CONTENT_ROOT/testData")
|
||||
public class ComparingStringReferencesInspectionTest extends LightJavaCodeInsightFixtureTestCase {
|
||||
|
||||
private static final String QUICK_FIX_NAME =
|
||||
InspectionBundle.message("inspection.comparing.string.references.use.quickfix");
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
myFixture.enableInspections(new ComparingStringReferencesInspection());
|
||||
// optimization: add a fake java.lang.String class to avoid loading all JDK classes for test
|
||||
myFixture.addClass("package java.lang; public final class String {}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines path to files used for running tests.
|
||||
* Defines the path to files used for running tests.
|
||||
*
|
||||
* @return The path from this module's root directory ($MODULE_WORKING_DIR$) to the
|
||||
* directory containing files for these tests.
|
||||
@ -27,28 +35,6 @@ public class ComparingReferencesInspectionTest extends LightJavaCodeInsightFixtu
|
||||
return "src/test/testData";
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the name of a test file, runs comparing references inspection quick fix and tests
|
||||
* the results against a reference outcome file. File name pattern 'foo.java' and 'foo.after.java'
|
||||
* are matching before and after files in the testData directory.
|
||||
*
|
||||
* @param testName The name of the test file before comparing references inspection.
|
||||
*/
|
||||
protected void doTest(@NotNull String testName) {
|
||||
// Initialize the test based on the testData file
|
||||
myFixture.configureByFile(testName + ".java");
|
||||
// Initialize the inspection and get a list of highlighted
|
||||
myFixture.enableInspections(new ComparingReferencesInspection());
|
||||
List<HighlightInfo> highlightInfos = myFixture.doHighlighting();
|
||||
assertFalse(highlightInfos.isEmpty());
|
||||
// Get the quick fix action for comparing references inspection and apply it to the file
|
||||
final IntentionAction action = myFixture.findSingleIntention(ComparingReferencesInspection.QUICK_FIX_NAME);
|
||||
assertNotNull(action);
|
||||
myFixture.launchAction(action);
|
||||
// Verify the results
|
||||
myFixture.checkResultByFile(testName + ".after.java");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the '==' case.
|
||||
*/
|
||||
@ -63,4 +49,26 @@ public class ComparingReferencesInspectionTest extends LightJavaCodeInsightFixtu
|
||||
doTest("Neq");
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the name of a test file, runs comparing references inspection quick fix and tests
|
||||
* the results against a reference outcome file.
|
||||
* File name pattern 'foo.java' and 'foo.after.java' are matching before and after files
|
||||
* in the testData directory.
|
||||
*
|
||||
* @param testName test file name base
|
||||
*/
|
||||
protected void doTest(@NotNull String testName) {
|
||||
// Initialize the test based on the testData file
|
||||
myFixture.configureByFile(testName + ".java");
|
||||
// Initialize the inspection and get a list of highlighted
|
||||
List<HighlightInfo> highlightInfos = myFixture.doHighlighting();
|
||||
assertFalse(highlightInfos.isEmpty());
|
||||
// Get the quick fix action for comparing references inspection and apply it to the file
|
||||
final IntentionAction action = myFixture.findSingleIntention(QUICK_FIX_NAME);
|
||||
assertNotNull(action);
|
||||
myFixture.launchAction(action);
|
||||
// Verify the results
|
||||
myFixture.checkResultByFile(testName + ".after.java");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
public class Eq {
|
||||
public boolean compareStrings(String s1, String s2) {
|
||||
return (s1.equals(s2));
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
public class Eq {
|
||||
public boolean compareStrings(String s1, String s2) {
|
||||
return (<caret>s1 == s2);
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
public class Neq {
|
||||
public boolean compareStrings(String s1, String s2) {
|
||||
return (!s1.equals(s2));
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
public class Neq {
|
||||
public boolean compareStrings(String s1, String s2) {
|
||||
return (s1 <caret>!= s2);
|
||||
}
|
||||
}
|
1
ijs.tree
1
ijs.tree
@ -191,6 +191,7 @@
|
||||
<toc-element topic="syntax_errors.md"/>
|
||||
<toc-element toc-title="Annotators"/>
|
||||
<toc-element topic="code_inspections.md" toc-title="Inspections">
|
||||
<toc-element topic="inspection_options.md"/>
|
||||
<toc-element toc-title="Profiles"/>
|
||||
<toc-element toc-title="Scopes"/>
|
||||
<toc-element toc-title="Suppressing Highlights"/>
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 2.4 KiB |
@ -1,6 +1,7 @@
|
||||
<!-- Copyright 2000-2023 JetBrains s.r.o. and other contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. -->
|
||||
|
||||
# Notable Changes in IntelliJ Platform and Plugins API 2023.*
|
||||
|
||||
<!-- Copyright 2000-2023 JetBrains s.r.o. and other contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. -->
|
||||
|
||||
<link-summary>List of known Notable API Changes in 2023.*</link-summary>
|
||||
|
||||
@ -17,6 +18,9 @@ _Early Access Program_ (EAP) releases of upcoming versions are available [here](
|
||||
|
||||
### IntelliJ Platform 2023.1
|
||||
|
||||
Declarative Inspection Options
|
||||
: [Code inspections](code_inspections.md) can provide additional options in a [declarative](inspection_options.md#declarative-inspection-options) way which has several benefits over the [UI-based](inspection_options.md#ui-based-inspection-options) approach.
|
||||
|
||||
Nested Index Access
|
||||
: Accessing index data in [nested calls](file_based_indexes.md#nested-index-access) is now possible.
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
[//]: # (title: Modifying the PSI)
|
||||
|
||||
<!-- Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -->
|
||||
|
||||
# Modifying the PSI
|
||||
|
||||
The PSI is a read/write representation of the source code as a tree of elements corresponding to a source file's structure.
|
||||
You can modify the PSI by *adding*, *replacing*, and *deleting* PSI elements.
|
||||
|
||||
@ -35,7 +35,7 @@ For larger code fragments, it's best to perform the modification in several step
|
||||
This ensures that the user code's formatting is preserved and that the modification does not introduce any unwanted whitespace changes.
|
||||
Just as everywhere else in the IntelliJ Platform API, the text passed to `createFileFromText()` and other `createFromText()` methods must use only `\n` as line separators.
|
||||
|
||||
As an example of this approach, see the quickfix in the `ComparingReferencesInspection` [example](code_inspections.md):
|
||||
As an example of this approach, see the quickfix in the `ComparingStringReferencesInspection` [example](code_inspections.md):
|
||||
|
||||
```java
|
||||
// binaryExpression holds a PSI expression of the form "x == y", which needs to be replaced with "x.equals(y)"
|
||||
|
@ -34,7 +34,7 @@ Please do not use <path>platform/testGuiFramework</path>, as it is reserved for
|
||||
|
||||
> Check out [this step-by-step tutorial](writing_tests_for_plugins.md) teaching how to write and run automated tests for your custom language plugin.
|
||||
> Also, code samples
|
||||
> [comparing_references_inspection](https://github.com/JetBrains/intellij-sdk-docs/tree/main/code_samples/comparing_references_inspection)
|
||||
> [comparing_string_references_inspection](https://github.com/JetBrains/intellij-sdk-docs/tree/main/code_samples/comparing_string_references_inspection)
|
||||
> and [conditional_operator_intention](https://github.com/JetBrains/intellij-sdk-docs/tree/main/code_samples/conditional_operator_intention) demonstrate using tests.
|
||||
>
|
||||
{style="note"}
|
||||
|
@ -1,7 +1,7 @@
|
||||
# Content Updates
|
||||
|
||||
<!-- Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -->
|
||||
|
||||
# Content Updates
|
||||
|
||||
This page lists notable additions and updates to the SDK documentation and [](code_samples.md).
|
||||
|
||||
See [GitHub Changelog](https://github.com/JetBrains/intellij-sdk-docs/commits/main) ([RSS](https://github.com/JetBrains/intellij-sdk-docs/commits/main.atom)) for a detailed changelog.
|
||||
@ -12,6 +12,9 @@ See [GitHub Changelog](https://github.com/JetBrains/intellij-sdk-docs/commits/ma
|
||||
|
||||
### February-23
|
||||
|
||||
Inspection Options
|
||||
: Add a section on [](inspection_options.md), which allows extending inspection behavior based on the input provided by user at runtime.
|
||||
|
||||
Minor Changes and Additions
|
||||
:
|
||||
- Add section on [](work_with_icons_and_images.md#mapping-new-ui-icons).
|
||||
|
160
topics/reference_guide/inspection_options.md
Normal file
160
topics/reference_guide/inspection_options.md
Normal file
@ -0,0 +1,160 @@
|
||||
<!-- Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -->
|
||||
|
||||
# Inspection Options
|
||||
|
||||
<link-summary>Implementing inspection options allowing to alter inspection behavior depending on user configuration.</link-summary>
|
||||
|
||||
Some code inspections provide configuration options that affect their behavior.
|
||||
For example, <ui-path>Java | Code style issues | 'size() == 0' can be replaced with isEmpty()</ui-path>, allows ignoring classes from the defined list or expressions, which would be replaced with `!isEmpty()`.
|
||||
|
||||
Currently, there are two ways of providing the inspection options:
|
||||
* [Declarative](#declarative-inspection-options)
|
||||
* [UI-based](#ui-based-inspection-options)
|
||||
|
||||
## Declarative Inspection Options
|
||||
|
||||
> Declarative inspection options API is available since version 2023.1.
|
||||
>
|
||||
{style="note"}
|
||||
|
||||
Declarative API allows to:
|
||||
* delegate component rendering to the platform and make all the inspection options UI consistent and compliant with the [IntelliJ Platform UI Guideline](https://jetbrains.design/intellij/)
|
||||
* optimize checking whether the inspection contains any options
|
||||
* manipulate options in places other than inspection panels (e.g., in quick fixes)
|
||||
* render options in contexts other than IntelliJ Platform-based IDEs
|
||||
|
||||
Providing the inspection options is achieved by implementing
|
||||
[`InspectionProfileEntry.getOptionsPane()`](%gh-ic%/platform/analysis-api/src/com/intellij/codeInspection/InspectionProfileEntry.java),
|
||||
which returns an
|
||||
[`OptPane`](%gh-ic-master%/platform/analysis-api/src/com/intellij/codeInspection/options/OptPane.java)
|
||||
object describing available configuration possibilities.
|
||||
Note that `InspectionProfileEntry` is a parent of inspection base classes like
|
||||
[`LocalInspectionTool`](%gh-ic%/platform/analysis-api/src/com/intellij/codeInspection/LocalInspectionTool.java)
|
||||
and
|
||||
[`GlobalInspectionTool`](%gh-ic%/platform/analysis-api/src/com/intellij/codeInspection/GlobalInspectionTool.java).
|
||||
|
||||
Building the inspection options is achieved by using a DSL-like facade, which contains methods for creating option controls and binding them to the fields declared in an inspection class.
|
||||
|
||||
Building the options for <ui-path>Java | Code style issues | 'size() == 0' can be replaced with 'isEmpty()'</ui-path> is implemented as follows:
|
||||
|
||||
```java
|
||||
public OrderedSet<String> ignoredTypes = new OrderedSet<>();
|
||||
public boolean ignoreNegations = false;
|
||||
|
||||
@Override
|
||||
public @NotNull OptPane getOptionsPane() {
|
||||
return pane(
|
||||
stringList(
|
||||
"ignoredTypes",
|
||||
message("options.label.ignored.classes"),
|
||||
new JavaClassValidator()),
|
||||
checkbox(
|
||||
"ignoreNegations",
|
||||
message("size.replaceable.by.isempty.negation.ignore"))
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
The above example builds a form with two options (see
|
||||
[`SizeReplaceableByIsEmptyInspection`](%gh-ic-master%/plugins/InspectionGadgets/src/com/siyeh/ig/style/SizeReplaceableByIsEmptyInspection.java)
|
||||
for the full implementation context):
|
||||
* List of strings, which are validated for being Java classes. The provided list is bound to the `ignoredTypes` field in the inspection class.
|
||||
* Checkbox, which value is bound to the boolean `ignoreNegations` field in the inspection class.
|
||||
|
||||
The `OptPane` class exposes methods for building fields of other types, e.g., number or dropdown fields.
|
||||
|
||||
Note that the bind identifiers passed as a first string argument of methods creating form controls contain injected references that resolve to the bound fields.
|
||||
It enables resolving and other resolve-related features available, making it easy to rename fields and minimizing the risk of introducing typos resulting in bugs, as unresolved references will be highlighted as errors.
|
||||
|
||||
### Custom Options Binding Protocol
|
||||
|
||||
The default way of binding option form values to fields may be insufficient in more advanced cases.
|
||||
It is possible to customize the way of binding options by providing a custom
|
||||
[`OptionController`](%gh-ic-master%/platform/analysis-api/src/com/intellij/codeInspection/options/OptionController.java)
|
||||
from `InspectionProfileEntry.getOptionController()` method.
|
||||
|
||||
Consider the <ui-path>Properties files | Inconsistent resource bundle</ui-path> global inspection, from bundled **Properties** plugin in IntelliJ IDEA, which reports several types of inconsistencies in <path>.properties</path> files.
|
||||
The inspection allows enabling or disabling reporting specific issue types, which are reported by providers implementing a dedicated interface.
|
||||
Information about enabled providers is stored in a map where the key is a provider ID.
|
||||
The options panel and value binding are implemented in the following way (see
|
||||
[`InconsistentResourceBundleInspection`](%gh-ic-master%/plugins/java-i18n/src/com/intellij/codeInspection/i18n/inconsistentResourceBundle/InconsistentResourceBundleInspection.java)
|
||||
for the full implementation context):
|
||||
|
||||
```java
|
||||
private NotNullLazyValue<InconsistentResourceBundleInspectionProvider[]> myProviders = ...;
|
||||
private Map<String, Boolean> mySettings = new LinkedHashMap<>();
|
||||
|
||||
@Override
|
||||
public @NotNull OptPane getOptionsPane() {
|
||||
return new OptPane(ContainerUtil.map(
|
||||
myProviders.getValue(),
|
||||
provider -> checkbox(provider.getName(), provider.getPresentableName())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull OptionController getOptionController() {
|
||||
return OptionController.of(
|
||||
(bindId) -> ContainerUtil.getOrElse(mySettings, bindId, true),
|
||||
(bindId, value) -> {
|
||||
boolean boolValue = (Boolean)value;
|
||||
if (boolValue) {
|
||||
mySettings.remove(bindId);
|
||||
} else {
|
||||
mySettings.put(bindId, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
Option controls panel is built based on providers’ IDs and presentable names.
|
||||
This implementation doesn't need to be changed regardless of removing or adding new providers in the future.
|
||||
|
||||
Reading and writing options in the map is achieved by registering a custom controller with getter and setter logic provided to the `OptionController.of()` method.
|
||||
|
||||
It's possible to compose several option controllers into the hierarchy based on the `bindId` prefix.
|
||||
It may be useful when some inspections have common configuration options and store the configuration in dedicated objects.
|
||||
See
|
||||
[`OptComponent.prefix()`](%gh-ic-master%/platform/analysis-api/src/com/intellij/codeInspection/options/OptComponent.java)
|
||||
and `OptionController.onPrefix()` methods for more details and example implementation:
|
||||
[`MissingJavadocInspection`](%gh-ic-master%/java/java-impl/src/com/intellij/codeInspection/javaDoc/MissingJavadocInspection.java).
|
||||
|
||||
### Non-Profile Inspection Options
|
||||
|
||||
Sometimes, inspections use options that are rendered in a non-standard way, or are shared with other inspections or other IDE features.
|
||||
Such a shared configuration can be implemented as a [persistent component](persisting_state_of_components.md) and not have a single owner.
|
||||
It is still convenient to be able to configure these options from the inspection panel.
|
||||
|
||||
An example of such a case is the <ui-path>Java | Probable bugs | Nullability problems | @NotNull/@Nullable problems</ui-path> inspection, which contains the <control>Configure Annotations…</control> button that opens the <control>Nullable/NotNull Configuration</control> dialog.
|
||||
|
||||
Custom Swing controls can be provided by implementing
|
||||
[`CustomComponentExtensionWithSwingRenderer`](%gh-ic-master%/platform/lang-api/src/com/intellij/codeInspection/ui/CustomComponentExtensionWithSwingRenderer.java)
|
||||
and registering the implementation in the `com.intellij.inspectionCustomComponent` extension point.
|
||||
Please note that this API is still in experimental state and may be changed without preserving backward compatibility.
|
||||
|
||||
**Example**:
|
||||
[`JavaInspectionButtons`](%gh-ic-master%/java/java-impl/src/com/intellij/codeInsight/options/JavaInspectionButtons.java)
|
||||
providing buttons for configuring options in custom dialogs
|
||||
|
||||
## UI-Based Inspection Options
|
||||
|
||||
> If you target versions 2023.1+ only, it is highly recommended to implement [](#declarative-inspection-options).
|
||||
|
||||
UI-based inspection options are provided by implementing a configuration panel using Swing components and returning it from [`InspectionProfileEntry.createOptionsPanel()`](%gh-ic%/platform/analysis-api/src/com/intellij/codeInspection/InspectionProfileEntry.java) method.
|
||||
It returns the panel with option components that bind the provided values to the inspection class fields or other properties, similarly as in the [declarative](#declarative-inspection-options) approach.
|
||||
Note that since version 2023.1, this method is ignored if `InspectionProfileEntry.getOptionPane()` returns a non-empty panel.
|
||||
|
||||
**Example**:
|
||||
[`SizeReplaceableByIsEmptyInspection`](%gh-ic-223%/plugins/InspectionGadgets/src/com/siyeh/ig/style/SizeReplaceableByIsEmptyInspection.java)
|
||||
in version 2022.3, implemented using the UI-approach
|
||||
|
||||
For simple customization requirements, see also:
|
||||
* [`SingleCheckboxOptionsPanel`](%gh-ic%/platform/lang-api/src/com/intellij/codeInspection/ui/SingleCheckboxOptionsPanel.java) for single checkbox
|
||||
* [`MultipleCheckboxOptionsPanel`](%gh-ic%/platform/lang-api/src/com/intellij/codeInspection/ui/MultipleCheckboxOptionsPanel.java) for multiple checkboxes
|
||||
* [`SingleIntegerFieldOptionsPanel`](%gh-ic%/platform/lang-api/src/com/intellij/codeInspection/ui/SingleIntegerFieldOptionsPanel.java) for single Integer (text field)
|
||||
* [`ConventionOptionsPanel`](%gh-ic%/platform/lang-api/src/com/intellij/codeInspection/ui/ConventionOptionsPanel.java) for validation using regular expression
|
||||
|
||||
> Be careful when you have a hierarchy of inspection classes.
|
||||
> For example, if inspection superclass is converted to the declarative approach, any `createOptionsPanel()` methods in subclasses will be ignored.
|
||||
> If you can't convert all of them at once, you may temporarily add `getOptionsPane()` returning `OptPane.EMPTY` to subclasses, where `createOptionsPanel()` is still used.
|
||||
>
|
||||
{style="warning"}
|
@ -12,14 +12,14 @@
|
||||
|
||||
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_references_inspection](%gh-sdk-samples%/comparing_references_inspection) code sample.
|
||||
An example of the plugin approach is the [comparing_string_references_inspection](%gh-sdk-samples%/comparing_string_references_inspection) code sample.
|
||||
|
||||
See the [Inspections](https://jetbrains.design/intellij/text/inspections/) topic in the IntelliJ Platform UI Guidelines on naming, writing description, and message texts for inspections.
|
||||
|
||||
## Creating an Inspection Plugin
|
||||
|
||||
The [comparing_references_inspection](%gh-sdk-samples%/comparing_references_inspection) code sample adds a new inspection to the <control>Java | Probable Bugs</control> group in the [Inspections list](https://www.jetbrains.com/help/idea/inspections-settings.html).
|
||||
The inspection reports when the `==` or `!=` operator is used between Java expressions of reference types.
|
||||
The [comparing_string_references_inspection](%gh-sdk-samples%/comparing_string_references_inspection) code sample adds a new inspection to the <control>Java | Probable Bugs</control> group in the [Inspections list](https://www.jetbrains.com/help/idea/inspections-settings.html).
|
||||
The inspection reports when the `==` or `!=` operator is used between String expressions.
|
||||
|
||||
It illustrates the components for a custom inspection plugin:
|
||||
* Describing an [inspection](#plugin-configuration-file) in the plugin configuration file.
|
||||
@ -27,9 +27,8 @@ It illustrates the components for a custom inspection plugin:
|
||||
* Creating a [visitor](#visitor-implementation-class) to traverse the PSI tree of the Java file being edited, inspecting for problematic syntax.
|
||||
* Implementing a [quick fix](#quick-fix-implementation) class to correct syntax problems by altering the PSI tree as needed.
|
||||
Quick fixes are displayed to the user like [intentions](code_intentions.md).
|
||||
* Implementing an [inspection preferences panel](#inspection-preferences-panel) to display information about the inspection.
|
||||
* Writing an HTML [description](#inspection-description) of the inspection for display in the inspection preferences panel.
|
||||
* Optionally, create a [unit test](#inspection-unit-test) for the plugin.
|
||||
* Creating a [test](#inspection-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](https://github.com/JetBrains/intellij-community) code base.
|
||||
To identify a given inspection's implementation classes, try to find an inspection [by name](explore_api.md#24-search-for-symbol-names) or [by UI texts](explore_api.md#25-search-by-ui-text).
|
||||
@ -37,29 +36,28 @@ Consider also searching for existing implementations in [IntelliJ Platform Explo
|
||||
|
||||
## Creating an Inspection
|
||||
|
||||
The [comparing_references_inspection](%gh-sdk-samples%/comparing_references_inspection) code sample reports when the `==` or `!=` operators are used between Java expressions of reference types.
|
||||
The [comparing_string_references_inspection](%gh-sdk-samples%/comparing_string_references_inspection) code sample reports when the `==` or `!=` operators are used between String expressions.
|
||||
The user can apply a quick fix to change `a==b` to `a.equals(b)`, or `a!=b` to `!a.equals(b)`.
|
||||
|
||||
The details of the `comparing_references_inspection` implementation illustrate the components of an inspection plugin.
|
||||
The details of the `comparing_string_references_inspection` implementation illustrate the components of an inspection plugin.
|
||||
|
||||
### Plugin Configuration File
|
||||
|
||||
The `comparing_references_inspection` is described as a `com.intellij.localInspection` extension point in the `comparing_references_inspection` plugin configuration ([`plugin.xml`](%gh-sdk-samples%/comparing_references_inspection/src/main/resources/META-INF/plugin.xml)) file.
|
||||
The `comparing_string_references_inspection` is described as a `com.intellij.localInspection` extension point in the `comparing_string_references_inspection` plugin configuration ([`plugin.xml`](%gh-sdk-samples%/comparing_string_references_inspection/src/main/resources/META-INF/plugin.xml)) file.
|
||||
|
||||
There exist two types of inspection extensions:
|
||||
* The `com.intellij.localInspection` extension 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.
|
||||
* The `com.intellij.globalInspection` extension 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 `implementationClass` and `language` attribute (unless the inspection works on any supported language).
|
||||
As shown in the `comparing_references_inspection` plugin configuration file, other attributes can be defined in the `localInspection` element, either with or without localization.
|
||||
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.
|
||||
Note that some attributes are not displayed to the user, so they are never localized.
|
||||
|
||||
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 [`ComparingReferencesInspection`](%gh-sdk-samples%/comparing_references_inspection/src/main/java/org/intellij/sdk/codeInspection/ComparingReferencesInspection.java), are often based on the Java class [`AbstractBaseJavaLocalInspectionTool`](%gh-ic%/java/java-analysis-api/src/com/intellij/codeInspection/AbstractBaseJavaLocalInspectionTool.java).
|
||||
Inspection implementations for Java files, like [`ComparingStringReferencesInspection`](%gh-sdk-samples%/comparing_string_references_inspection/src/main/java/org/intellij/sdk/codeInspection/ComparingStringReferencesInspection.java), are often based on the Java class [`AbstractBaseJavaLocalInspectionTool`](%gh-ic%/java/java-analysis-api/src/com/intellij/codeInspection/AbstractBaseJavaLocalInspectionTool.java).
|
||||
The [`AbstractBaseJavaLocalInspectionTool`](%gh-ic%/java/java-analysis-api/src/com/intellij/codeInspection/AbstractBaseJavaLocalInspectionTool.java) base class offers methods to inspect Java classes, fields, and methods.
|
||||
|
||||
More generally, `localInspection` types are based on the class [`LocalInspectionTool`](%gh-ic%/platform/analysis-api/src/com/intellij/codeInspection/LocalInspectionTool.java).
|
||||
@ -68,55 +66,32 @@ One of these classes is a good basis for a new inspection implementation, but a
|
||||
|
||||
The primary responsibilities of the inspection implementation class are to provide:
|
||||
* A `PsiElementVisitor` object to traverse the PSI tree of the file being inspected.
|
||||
* A `LocalQuickFix` class to fix an identified problem.
|
||||
* A `JPanel` to be displayed in the <control>Inspections</control> settings dialog.
|
||||
* A `LocalQuickFix` class to fix an identified problem (optional).
|
||||
* An options panel to be displayed in the <control>Inspections</control> settings dialog (optional). See [](inspection_options.md) for more details.
|
||||
|
||||
The `ComparingReferencesInspection` class defines two `String` fields:
|
||||
* `QUICK_FIX_NAME` defines the string users see when prompted to apply the quick fix.
|
||||
* `CHECKED_CLASSES` holds a list of class names of interest to the inspection.
|
||||
|
||||
The overridden `ComparingReferencesInspection` methods are discussed in the sections below.
|
||||
The overridden `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.
|
||||
|
||||
The `ComparingReferencesInspection.buildVisitor()` method creates an anonymous visitor class based on [`JavaElementVisitor`](%gh-ic%/java/java-psi-api/src/com/intellij/psi/JavaElementVisitor.java) to traverse the PSI tree of the Java file being edited, inspecting for suspect syntax.
|
||||
The anonymous class overrides three methods in particular.
|
||||
* `visitReferenceExpression()` to prevent any duplicate visitation of reference-type expressions.
|
||||
* `visitBinaryExpression()`, which does all the heavy lifting.
|
||||
It is called to evaluate a `PsiBinaryExpression`, and it checks to see if the operands are `==` or `!=`, and if the operands are classes relevant to this inspection.
|
||||
* `isCheckedType()` evaluates the `PsiType` of the operands to determine if they are of interest to this inspection.
|
||||
The `ComparingStringReferencesInspection.buildVisitor()` method creates an anonymous visitor class based on [`JavaElementVisitor`](%gh-ic%/java/java-psi-api/src/com/intellij/psi/JavaElementVisitor.java) 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 `==` or `!=`, and if both operand types are `String`.
|
||||
|
||||
### Quick Fix Implementation
|
||||
|
||||
The quick fix class acts much like an intention, allowing the user to invoke it on the `PsiElement` (or `TextRange`) highlighted by the inspection.
|
||||
|
||||
The `ComparingReferencesInspection` implementation uses the nested class `CriQuickFix` to implement a quick fix based on [`LocalQuickFix`](%gh-ic%/platform/analysis-api/src/com/intellij/codeInspection/LocalQuickFix.java).
|
||||
The `CriQuickFix` class gives a user the option to change the use of `a == b` and `a != b` expression to `a.equals(b)` and `!a.equals(b)` respectively.
|
||||
The `ComparingStringReferencesInspection` implementation uses the nested class `ReplaceWithEqualsQuickFix` to implement a quick fix based on [`LocalQuickFix`](%gh-ic%/platform/analysis-api/src/com/intellij/codeInspection/LocalQuickFix.java).
|
||||
The `ReplaceWithEqualsQuickFix` class allows the user to change the use of `a == b` and `a != b` expression to `a.equals(b)` and `!a.equals(b)` respectively.
|
||||
|
||||
The heavy lifting is done in `CriQuickFix.applyFix()`, which manipulates the PSI tree to convert the expressions.
|
||||
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:
|
||||
* Getting a `PsiElementFactory`.
|
||||
* Creating a new `PsiMethodCallExpression`.
|
||||
* Substituting the original left and right operands into the new `PsiMethodCallExpression`.
|
||||
* Replacing the original binary expression with the `PsiMethodCallExpression`.
|
||||
|
||||
### Inspection Preferences Panel
|
||||
|
||||
The inspection preferences panel is used to display information and provide additional options for the inspection.
|
||||
|
||||
The panel created by `ComparingReferencesInspection.createOptionsPanel()` just defines a single `JTextField` to display in a `JPanel`.
|
||||
This `JPanel` gets added to the <control>Inspections</control> settings dialog when the inspection is selected.
|
||||
The `JTextField` allows editing of the `CHECKED_CLASSES` field while displayed in the panel.
|
||||
|
||||
For simple customization requirements, see also:
|
||||
|
||||
- [`SingleCheckboxOptionsPanel`](%gh-ic%/platform/lang-api/src/com/intellij/codeInspection/ui/SingleCheckboxOptionsPanel.java) for single checkbox
|
||||
- [`MultipleCheckboxOptionsPanel`](%gh-ic%/platform/lang-api/src/com/intellij/codeInspection/ui/MultipleCheckboxOptionsPanel.java) for multiple checkboxes
|
||||
- [`SingleIntegerFieldOptionsPanel`](%gh-ic%/platform/lang-api/src/com/intellij/codeInspection/ui/SingleIntegerFieldOptionsPanel.java) for single Integer (text field)
|
||||
- [`ConventionOptionsPanel`](%gh-ic%/platform/lang-api/src/com/intellij/codeInspection/ui/ConventionOptionsPanel.java) for validation using regular expression
|
||||
|
||||
### Inspection Description
|
||||
|
||||
The inspection description is an HTML file.
|
||||
@ -126,59 +101,42 @@ Implicit in using [`LocalInspectionTool`](%gh-ic%/platform/analysis-api/src/com/
|
||||
* The inspection description file is expected to be located under <path>$RESOURCES_ROOT_DIRECTORY$/inspectionDescriptions/</path>.
|
||||
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 <path>$SHORT_NAME$.html</path> as provided by the inspection description, or the inspection implementation class.
|
||||
If a short name is not provided by the plugin, the IntelliJ Platform computes one by removing `Inspection` suffix from the implementation class name.
|
||||
If a short name is not provided, the IntelliJ Platform computes one by removing `Inspection` suffix from the implementation class name.
|
||||
|
||||
> To open related [settings](settings.md) directly from the inspection description, add a link with `settings://$CONFIGURABLE_ID$`, optionally followed by `?$SEARCH_STRING$` to pre-select UI element:
|
||||
>
|
||||
> `See <em>Includes</em> tab in <a href="settings://fileTemplates">Settings | Editor | File and Code Templates</a> to configure.`
|
||||
>
|
||||
|
||||
### Inspection Unit Test
|
||||
### Inspection Test
|
||||
|
||||
> Please note that running the test requires setting system property `idea.home.path` in the `test` task configuration of the Gradle build script.
|
||||
>
|
||||
{style="note"}
|
||||
|
||||
The `comparing_references_inspection` code sample provides a unit test for the inspection.
|
||||
The `comparing_string_references_inspection` code sample provides a test for the inspection.
|
||||
See the [](testing_plugins.md) section for general information about plugin testing.
|
||||
|
||||
The `comparing_references_inspection` test is based on the [`UsefulTestCase`](%gh-ic%/platform/testFramework/src/com/intellij/testFramework/UsefulTestCase.java) class, part of the JUnit framework APIs.
|
||||
The `comparing_string_references_inspection` test is based on the [`UsefulTestCase`](%gh-ic%/platform/testFramework/src/com/intellij/testFramework/UsefulTestCase.java) class, part of the JUnit framework APIs.
|
||||
This class handles much of the underlying boilerplate for tests.
|
||||
|
||||
By convention, the folder <path>test/testData/</path> contains the test files.
|
||||
The folder contains pairs of files for each test using the name convention <path>∗.java</path> and <path>∗.after.java</path>.
|
||||
The folder contains pairs of files for each test using the name convention <path>∗.java</path> and <path>∗.after.java</path>, e.g., <path>Eq.java</path> / <path>Eq.after.java</path>.
|
||||
|
||||
In the case of `comparing_references_inspection` the test files are <path>Eq.java</path> / <path>Eq.after.java</path>, and <path>Neq.java</path> / <path>Neq.after.java</path>.
|
||||
The `comparing_string_references_inspection` tests run the inspection on the <path>∗.java</path> files, apply the quick fix, and compare the results with the respective <path>∗.after.java</path> files containing expected results.
|
||||
|
||||
The `comparing_references_inspection` tests run the inspection on the <path>∗.java</path> files, implement the quick fix, and compare the results with the respective <path>∗.after.java</path> files containing expected result.
|
||||
## Running the Comparing String References Inspection Code Sample
|
||||
|
||||
## Running the Comparing References Inspection Code Sample
|
||||
|
||||
The [comparing_references_inspection](%gh-sdk-samples%/comparing_references_inspection) code sample adds a new inspection to the <control>Java | Probable Bugs</control> group in the [Inspections List](https://www.jetbrains.com/help/idea/inspections-settings.html).
|
||||
The [comparing_string_references_inspection](%gh-sdk-samples%/comparing_string_references_inspection) code sample adds a new inspection to the <control>Java | Probable Bugs</control> group in the [Inspections](https://www.jetbrains.com/help/idea/inspections-settings.html) configuration.
|
||||
|
||||
See [](code_samples.md) on how to set up and run the plugin.
|
||||
|
||||
### Configuring the Plugin
|
||||
|
||||
Once the plugin is launched, you can set the plugin options.
|
||||
You can specify the Java classes to participate in the code inspection and the severity level of the found probable bugs.
|
||||
|
||||
On the main menu, open the <ui-path>Settings | Editor | Inspections</ui-path> dialog.
|
||||
In the list of the IntelliJ IDEA <control>Java</control> inspections, expand the <control>Probable bugs</control> node, and then click <control>SDK: '==' or '!=' instead of 'equals()'</control>.
|
||||
|
||||

|
||||
|
||||
Under <control>Options</control>, you can specify the following plugin settings:
|
||||
* From the <control>Severity</control> list, select the severity level of probable bugs the plugin finds such as <control>Warning</control>, <control>Error</control>, etc.
|
||||
* In the text box under <control>Severity</control>, specify the semicolon separated list of Java classes to participate in this code inspection.
|
||||
* When finished, click <control>OK</control>.
|
||||
|
||||
### How does it work?
|
||||
|
||||
The plugin inspects your code opened in the IntelliJ IDEA editor.
|
||||
The plugin highlights the code fragments where two variables of the reference type are separated by `==` or `!=` and proposes to replace this code fragment with `.equals()`:
|
||||
The plugin highlights the code fragments where two `String` expressions are compared by `==` or `!=` and proposes to replace this code fragment with `.equals()`:
|
||||
|
||||

|
||||

|
||||
|
||||
In this example, the `str1` and `str2` are variables of the String type.
|
||||
Invoking <control>SDK: Use equals()</control> replaces:
|
||||
|
1
v.list
1
v.list
@ -10,6 +10,7 @@
|
||||
|
||||
<var name="gh-ic" value="https://github.com/JetBrains/intellij-community/tree/idea/%ijPlatformBuild%"/>
|
||||
<var name="gh-ic-master" value="https://github.com/JetBrains/intellij-community/tree/master"/>
|
||||
<var name="gh-ic-223" value="https://github.com/JetBrains/intellij-community/blob/223"/>
|
||||
|
||||
<var name="gh-ij-android" value="https://github.com/JetBrains/android/tree/idea/%ijPlatformBuild%"/>
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user