IG: add option to ignore methods which return the type of the enclosing class (IDEA-93333)

for "Chained method calls" inspection
This commit is contained in:
Bas Leijdekkers
2019-04-02 19:55:03 +02:00
parent b6cc976052
commit 4ce5115d22
5 changed files with 86 additions and 35 deletions
@@ -1292,7 +1292,7 @@ literal.as.arg.to.string.equals.problem.descriptor=Literal <code>#ref</code> is
literal.as.arg.to.string.equals.flip.quickfix=Flip ''{0}()''
c.style.array.declaration.replace.quickfix=Replace with Java-style array declaration
chained.method.call.ignore.option=Ignore chained method calls in field initializers
chained.method.call.ignore.this.super.option=Ignore chained method calls in 'this()' and 'super()' calls
chained.method.call.ignore.self.types.option=Ignore calls to methods returning the same type as their enclosing class
introduce.variable.quickfix=Introduce variable
introduce.variable.may.change.semantics.quickfix=Introduce variable (may change semantics)
flip.comparison.quickfix=Flip comparison
@@ -1,5 +1,5 @@
/*
* Copyright 2003-2016 Dave Griffith, Bas Leijdekkers
* Copyright 2003-2019 Dave Griffith, Bas Leijdekkers
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@ package com.siyeh.ig.style;
import com.intellij.codeInspection.ui.MultipleCheckboxOptionsPanel;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.JavaPsiConstructorUtil;
import com.siyeh.InspectionGadgetsBundle;
import com.siyeh.ig.BaseInspection;
@@ -25,6 +26,7 @@ import com.siyeh.ig.BaseInspectionVisitor;
import com.siyeh.ig.InspectionGadgetsFix;
import com.siyeh.ig.fixes.IntroduceVariableFix;
import com.siyeh.ig.psiutils.ParenthesesUtils;
import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
@@ -32,14 +34,24 @@ import javax.swing.*;
public class ChainedMethodCallInspection extends BaseInspection {
@SuppressWarnings("PublicField")
public boolean m_ignoreFieldInitializations = true;
@SuppressWarnings({"PublicField", "unused"})
public boolean m_ignoreThisSuperCalls = true; // keep for compatibility
@SuppressWarnings("PublicField")
public boolean m_ignoreThisSuperCalls = true;
public boolean ignoreSelfTypes = true;
@Override
public void writeSettings(@NotNull Element node) {
defaultWriteSettings(node, "ignoreSelfTypes");
writeBooleanOption(node, "ignoreSelfTypes", true);
}
@Override
public JComponent createOptionsPanel() {
final MultipleCheckboxOptionsPanel panel = new MultipleCheckboxOptionsPanel(this);
panel.addCheckbox(InspectionGadgetsBundle.message("chained.method.call.ignore.option"), "m_ignoreFieldInitializations");
panel.addCheckbox(InspectionGadgetsBundle.message("chained.method.call.ignore.this.super.option"), "m_ignoreThisSuperCalls");
panel.addCheckbox(InspectionGadgetsBundle.message("chained.method.call.ignore.self.types.option"), "ignoreSelfTypes");
return panel;
}
@@ -76,11 +88,11 @@ public class ChainedMethodCallInspection extends BaseInspection {
public void visitMethodCallExpression(@NotNull PsiMethodCallExpression expression) {
super.visitMethodCallExpression(expression);
final PsiReferenceExpression reference = expression.getMethodExpression();
final PsiExpression qualifier = reference.getQualifierExpression();
final PsiExpression qualifier = ParenthesesUtils.stripParentheses(reference.getQualifierExpression());
if (qualifier == null) {
return;
}
if (!isCallExpression(qualifier)) {
if (!(qualifier instanceof PsiCallExpression)) {
return;
}
if (m_ignoreFieldInitializations) {
@@ -89,21 +101,35 @@ public class ChainedMethodCallInspection extends BaseInspection {
return;
}
}
if (m_ignoreThisSuperCalls) {
final PsiExpressionList expressionList = PsiTreeUtil.getParentOfType(expression, PsiExpressionList.class);
if (expressionList != null) {
final PsiElement parent = expressionList.getParent();
if (JavaPsiConstructorUtil.isConstructorCall(parent)) {
final PsiExpressionList expressionList = PsiTreeUtil.getParentOfType(expression, PsiExpressionList.class);
if (expressionList != null) {
final PsiElement parent = expressionList.getParent();
if (JavaPsiConstructorUtil.isConstructorCall(parent)) {
return;
}
}
if (ignoreSelfTypes) {
if (qualifier instanceof PsiMethodCallExpression) {
final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)qualifier;
final PsiMethod qualifierMethod = methodCallExpression.resolveMethod();
if (qualifierMethod == null) {
return;
}
PsiClass containingClass = qualifierMethod.getContainingClass();
PsiClass aClass = PsiUtil.resolveClassInClassTypeOnly(qualifierMethod.getReturnType());
if (containingClass == null || containingClass.equals(aClass)) {
return;
}
}
else {
PsiClass callClass = PsiUtil.resolveClassInClassTypeOnly(expression.getType());
PsiClass qualifierClass = PsiUtil.resolveClassInClassTypeOnly(qualifier.getType());
if (qualifierClass == null || qualifierClass.equals(callClass)) {
return;
}
}
}
registerMethodCallError(expression);
}
private boolean isCallExpression(PsiExpression expression) {
expression = ParenthesesUtils.stripParentheses(expression);
return expression instanceof PsiMethodCallExpression || expression instanceof PsiNewExpression;
}
}
}
@@ -1,19 +0,0 @@
package com.siyeh.igtest.bugs;
public class ChainedMethodInspection{
private ChainedMethodInspection baz = foo().bar();
public void baz(){
foo().bar();
(foo()).bar();
}
public ChainedMethodInspection foo()
{
return this;
}
public ChainedMethodInspection bar()
{
return this;
}
}
@@ -0,0 +1,22 @@
package com.siyeh.igtest.bugs;
public class ChainedMethodCall {
private X baz = new X().foo().bar();
public void baz(){
new X().<warning descr="Chained method call 'foo()'">foo</warning>().<warning descr="Chained method call 'bar()'">bar</warning>();
(new X().<warning descr="Chained method call 'foo()'">foo</warning>()).<warning descr="Chained method call 'bar()'">bar</warning>();
String s = new StringBuilder().append("x: ").append(new X()).append("y: ").append(new Y()).toString();
}
class X {
public Y foo() {
return new Y();
}
}
class Y {
public X bar() {
return new X();
}
}
}
@@ -0,0 +1,22 @@
// Copyright 2000-2019 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.
package com.siyeh.ig.style;
import com.intellij.codeInspection.InspectionProfileEntry;
import com.siyeh.ig.LightInspectionTestCase;
import org.jetbrains.annotations.Nullable;
/**
* @author Bas Leijdekkers
*/
public class ChainedMethodCallInspectionTest extends LightInspectionTestCase {
public void testChainedMethodCall() {
doTest();
}
@Nullable
@Override
protected InspectionProfileEntry getInspection() {
return new ChainedMethodCallInspection();
}
}