Java: Convert intention "Join Declaration And Assignment" into inspection - improved highlighting (IDEA-178919)

This commit is contained in:
Pavel Dolgov
2018-10-01 16:44:16 +03:00
parent 3dafef6aee
commit 0623c7b75f
15 changed files with 114 additions and 48 deletions

View File

@@ -912,7 +912,7 @@
<localInspection groupPath="Java" language="JAVA" shortName="JoinDeclarationAndAssignmentJava"
key="inspection.join.declaration.and.assignment.display.name" bundle="messages.InspectionsBundle"
groupKey="group.names.code.style.issues" groupBundle="messages.InspectionsBundle"
enabledByDefault="true" level="INFO"
enabledByDefault="true" level="INFORMATION"
implementationClass="com.intellij.codeInspection.JoinDeclarationAndAssignmentJavaInspection" />
<globalInspection groupPath="Java" language="JAVA" shortName="EmptyMethod" displayName="Empty method" groupKey="group.names.declaration.redundancy" enabledByDefault="true" groupBundle="messages.InspectionsBundle"

View File

@@ -5,7 +5,7 @@ import com.intellij.codeInsight.FileModificationService;
import com.intellij.codeInsight.editorActions.DeclarationJoinLinesHandler;
import com.intellij.openapi.application.WriteAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
import com.intellij.psi.*;
import com.intellij.psi.search.LocalSearchScope;
import com.intellij.psi.search.searches.ReferencesSearch;
@@ -16,6 +16,9 @@ import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static com.intellij.codeInspection.ProblemHighlightType.GENERIC_ERROR_OR_WARNING;
import static com.intellij.codeInspection.ProblemHighlightType.INFORMATION;
/**
* It's called "Java" inspection because the name without "Java" already exists.
*
@@ -30,14 +33,33 @@ public class JoinDeclarationAndAssignmentJavaInspection extends AbstractBaseJava
public void visitAssignmentExpression(PsiAssignmentExpression assignmentExpression) {
super.visitAssignmentExpression(assignmentExpression);
final Pair.NonNull<PsiLocalVariable, PsiAssignmentExpression> pair = getPair(assignmentExpression);
if (pair != null) {
String name = pair.getFirst().getName();
if (name != null) {
String message = InspectionsBundle.message("inspection.join.declaration.and.assignment.message", name);
JoinDeclarationAndAssignmentFix fix = new JoinDeclarationAndAssignmentFix();
holder.registerProblem(pair.getFirst(), message, fix);
holder.registerProblem(pair.getSecond(), message, fix);
visitLocation(assignmentExpression);
}
@Override
public void visitLocalVariable(PsiLocalVariable variable) {
super.visitLocalVariable(variable);
// At the "information" level only bare minimal set of elements are visited when the file is being edited.
// If the caret is at the variable declaration the assignment expression can be not visited so we do the same check here.
if (isOnTheFly) visitLocation(variable);
}
public void visitLocation(@Nullable PsiElement location) {
Context context = getContext(location);
if (context != null) {
PsiLocalVariable variable = context.myVariable;
PsiAssignmentExpression assignment = context.myAssignment;
assert location == variable || location == assignment : "context location";
String message = InspectionsBundle.message("inspection.join.declaration.and.assignment.message", context.myName);
JoinDeclarationAndAssignmentFix fix = new JoinDeclarationAndAssignmentFix();
ProblemHighlightType highlightType = context.myIsUpdate ? INFORMATION : GENERIC_ERROR_OR_WARNING;
if (isOnTheFly && (context.myIsUpdate || InspectionProjectProfileManager.isInformationLevel(getShortName(), location))) {
holder.registerProblem(location, message, highlightType, fix);
}
else if (location == assignment) {
holder.registerProblem(assignment.getLExpression(), message, highlightType, fix);
}
}
}
@@ -45,22 +67,43 @@ public class JoinDeclarationAndAssignmentJavaInspection extends AbstractBaseJava
}
@Nullable
private static Pair.NonNull<PsiLocalVariable, PsiAssignmentExpression> getPair(@Nullable PsiElement element) {
PsiLocalVariable variable = null;
PsiAssignmentExpression assignmentExpression = null;
if (element instanceof PsiAssignmentExpression) {
assignmentExpression = (PsiAssignmentExpression)element;
variable = findVariable(assignmentExpression);
private static Context getContext(@Nullable PsiElement element) {
if (element != null) {
PsiElement parent = element.getParent();
if (parent != null) {
if (element instanceof PsiAssignmentExpression) {
PsiAssignmentExpression assignment = (PsiAssignmentExpression)element;
return getContext(findVariable(assignment), assignment);
}
if (parent instanceof PsiAssignmentExpression) {
PsiAssignmentExpression assignment = (PsiAssignmentExpression)parent;
return getContext(findVariable(assignment), assignment);
}
if (element instanceof PsiLocalVariable) {
PsiLocalVariable variable = (PsiLocalVariable)element;
return getContext(variable, findAssignment(variable));
}
if (parent instanceof PsiLocalVariable) {
PsiLocalVariable variable = (PsiLocalVariable)parent;
return getContext(variable, findAssignment(variable));
}
}
}
else if (element instanceof PsiLocalVariable) {
variable = (PsiLocalVariable)element;
assignmentExpression = findAssignment(variable);
}
if (variable != null && assignmentExpression != null) {
PsiExpression rExpression = assignmentExpression.getRExpression();
if (rExpression != null &&
ReferencesSearch.search(variable, new LocalSearchScope(rExpression), false).findFirst() == null) {
return Pair.createNonNull(variable, assignmentExpression);
return null;
}
@Nullable
private static Context getContext(@Nullable PsiLocalVariable variable, @Nullable PsiAssignmentExpression assignment) {
if (variable != null && assignment != null) {
String variableName = variable.getName();
PsiExpression rExpression = assignment.getRExpression();
if (variableName != null && rExpression != null) {
for (PsiLocalVariable aVar = variable; aVar != null; aVar = PsiTreeUtil.getNextSiblingOfType(aVar, PsiLocalVariable.class)) {
if (ReferencesSearch.search(aVar, new LocalSearchScope(rExpression), false).findFirst() != null) {
return null;
}
}
return new Context(variable, assignment, variableName);
}
}
return null;
@@ -68,13 +111,14 @@ public class JoinDeclarationAndAssignmentJavaInspection extends AbstractBaseJava
@Nullable
private static PsiLocalVariable findVariable(@NotNull PsiAssignmentExpression assignmentExpression) {
PsiElement assignmentParent = assignmentExpression.getParent();
if (assignmentParent instanceof PsiExpressionStatement) {
PsiElement candidate = PsiTreeUtil.skipWhitespacesAndCommentsBackward(assignmentParent);
if (candidate instanceof PsiDeclarationStatement) {
PsiExpression lExpression = assignmentExpression.getLExpression();
if (lExpression instanceof PsiReferenceExpression) {
PsiElement resolved = ((PsiReferenceExpression)lExpression).resolve();
PsiExpression lExpression = assignmentExpression.getLExpression();
if (lExpression instanceof PsiReferenceExpression) {
PsiReferenceExpression reference = (PsiReferenceExpression)lExpression;
if (!reference.isQualified()) { // optimization: locals aren't qualified
PsiExpressionStatement statement = ObjectUtils.tryCast(assignmentExpression.getParent(), PsiExpressionStatement.class);
PsiElement candidate = PsiTreeUtil.skipWhitespacesAndCommentsBackward(statement);
if (candidate instanceof PsiDeclarationStatement) {
PsiElement resolved = reference.resolve();
if (resolved instanceof PsiLocalVariable && resolved.getParent() == candidate) {
return (PsiLocalVariable)resolved;
}
@@ -113,10 +157,10 @@ public class JoinDeclarationAndAssignmentJavaInspection extends AbstractBaseJava
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
Pair.NonNull<PsiLocalVariable, PsiAssignmentExpression> pair = getPair(descriptor.getPsiElement());
if (pair != null) {
PsiLocalVariable variable = pair.getFirst();
PsiAssignmentExpression assignmentExpression = pair.getSecond();
Context context = getContext(descriptor.getPsiElement());
if (context != null) {
PsiLocalVariable variable = context.myVariable;
PsiAssignmentExpression assignmentExpression = context.myAssignment;
PsiExpression initializer = variable.getInitializer();
if (initializer != null && assignmentExpression.getOperationTokenType() == JavaTokenType.EQ) {
RemoveInitializerFix.sideEffectAwareRemove(project, initializer, initializer, variable);
@@ -133,4 +177,18 @@ public class JoinDeclarationAndAssignmentJavaInspection extends AbstractBaseJava
}
}
}
private static class Context {
@NotNull final PsiLocalVariable myVariable;
@NotNull final PsiAssignmentExpression myAssignment;
@NotNull final String myName;
final boolean myIsUpdate;
Context(@NotNull PsiLocalVariable variable, @NotNull PsiAssignmentExpression assignment, @NotNull String name) {
myAssignment = assignment;
myVariable = variable;
myName = name;
myIsUpdate = !JavaTokenType.EQ.equals(myAssignment.getOperationTokenType());
}
}
}

View File

@@ -1,4 +1,4 @@
// "Join declaration and assignment" "true"
// "Join declaration and assignment" "GENERIC_ERROR_OR_WARNING"
class Test {
{
String ss = "hello"; // comment A

View File

@@ -1,4 +1,4 @@
// "Join declaration and assignment" "true"
// "Join declaration and assignment" "GENERIC_ERROR_OR_WARNING"
class Test {
{
String ss = "hello";

View File

@@ -1,4 +1,4 @@
// "Join declaration and assignment" "true"
// "Join declaration and assignment" "GENERIC_ERROR_OR_WARNING"
class Test {
{
String ss = "hello", a = "";

View File

@@ -1,4 +1,4 @@
// "Join declaration and assignment" "true"
// "Join declaration and assignment" "INFORMATION"
class Test {
{
int i = 4 * (2 + 3);

View File

@@ -1,4 +1,4 @@
// "Join declaration and assignment" "true"
// "Join declaration and assignment" "INFORMATION"
class Test {
{
String ss = "hello" + "world";

View File

@@ -1,4 +1,4 @@
// "Join declaration and assignment" "true"
// "Join declaration and assignment" "GENERIC_ERROR_OR_WARNING"
class T {
{
foo(1);

View File

@@ -0,0 +1,8 @@
// "Join declaration and assignment" "false"
class T {
static int foo() {
int x, y;
<caret>x = y = 0;
return x + y;
}
}

View File

@@ -1,4 +1,4 @@
// "Join declaration and assignment" "true"
// "Join declaration and assignment" "GENERIC_ERROR_OR_WARNING"
class Test {
{
String s<caret>s = ""; // comment A

View File

@@ -1,4 +1,4 @@
// "Join declaration and assignment" "true"
// "Join declaration and assignment" "GENERIC_ERROR_OR_WARNING"
class Test {
{
String s<caret>s = "";

View File

@@ -1,4 +1,4 @@
// "Join declaration and assignment" "true"
// "Join declaration and assignment" "GENERIC_ERROR_OR_WARNING"
class Test {
{
String s<caret>s = "", a = "";

View File

@@ -1,4 +1,4 @@
// "Join declaration and assignment" "true"
// "Join declaration and assignment" "INFORMATION"
class Test {
{
int i = 4;

View File

@@ -1,4 +1,4 @@
// "Join declaration and assignment" "true"
// "Join declaration and assignment" "INFORMATION"
class Test {
{
String ss = "hello";

View File

@@ -1,4 +1,4 @@
// "Join declaration and assignment" "true"
// "Join declaration and assignment" "GENERIC_ERROR_OR_WARNING"
class T {
{
int a =<caret> foo(1);