IDEA-21316 (New inspection: suspicious getter/setter (accessing different field than expected))

This commit is contained in:
Bas Leijdekkers
2014-09-24 12:35:08 +02:00
parent d351608684
commit 02b61cbc03
6 changed files with 223 additions and 0 deletions
@@ -1221,6 +1221,10 @@
key="property.value.set.to.itself.display.name" groupBundle="messages.InspectionsBundle"
groupKey="group.names.javabeans.issues" enabledByDefault="false" level="WARNING"
implementationClass="com.siyeh.ig.javabeans.PropertyValueSetToItselfInspection"/>
<localInspection language="JAVA" shortName="SuspiciousGetterSetter" bundle="com.siyeh.InspectionGadgetsBundle"
key="suspicious.getter.setter.display.name" groupBundle="messages.InspectionsBundle"
groupKey="group.names.javabeans.issues" enabledByDefault="false" level="WARNING"
implementationClass="com.siyeh.ig.javabeans.SuspiciousGetterSetterInspection"/>
<!--group.names.javadoc.issues-->
<localInspection language="JAVA" shortName="HtmlTagCanBeJavadocTag" bundle="com.siyeh.InspectionGadgetsBundle"
@@ -2125,3 +2125,6 @@ parameter.type.prevents.overriding.display.name=Parameter type prevents overridi
parameter.type.prevents.overriding.problem.descriptor=Parameter type <code>#ref</code> is located in ''{0}'' while super method parameter type is located in ''{1}'' preventing overriding
parameter.type.prevents.overriding.quickfix=Change type of parameter to ''{0}''
parameter.type.prevents.overriding.family.quickfix=Change type of parameter
suspicious.getter.setter.display.name=Suspicious getter/setter
suspicious.setter.problem.descriptor=Setter <code>#ref()</code> assigns field ''{0}''
suspicious.getter.problem.descriptor=Getter <code>#ref()</code> returns field ''{0}''
@@ -0,0 +1,143 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.siyeh.ig.javabeans;
import com.intellij.codeInspection.ui.SingleCheckboxOptionsPanel;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiField;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.codeStyle.VariableKind;
import com.intellij.psi.util.PropertyUtil;
import com.siyeh.InspectionGadgetsBundle;
import com.siyeh.ig.BaseInspection;
import com.siyeh.ig.BaseInspectionVisitor;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
/**
* @author Bas Leijdekkers
*/
public class SuspiciousGetterSetterInspection extends BaseInspection {
@SuppressWarnings("PublicField")
public boolean onlyWarnWhenFieldPresent = false;
@Nls
@NotNull
@Override
public String getDisplayName() {
return InspectionGadgetsBundle.message("suspicious.getter.setter.display.name");
}
@NotNull
@Override
protected String buildErrorString(Object... infos) {
return ((Boolean)infos[0]).booleanValue()
? InspectionGadgetsBundle.message("suspicious.setter.problem.descriptor", infos[1])
: InspectionGadgetsBundle.message("suspicious.getter.problem.descriptor", infos[1]);
}
@Nullable
@Override
public JComponent createOptionsPanel() {
return new SingleCheckboxOptionsPanel("Only warn when field matching getter/setter name is present", this, "onlyWarnWhenFieldPresent");
}
@Override
public BaseInspectionVisitor buildVisitor() {
return new SuspiciousGetterSetterVisitor();
}
private class SuspiciousGetterSetterVisitor extends BaseInspectionVisitor {
@Override
public void visitMethod(PsiMethod method) {
super.visitMethod(method);
final String name = method.getName();
final String fieldName;
final boolean setter;
final String extractedFieldName;
if (nameStartsWith(name, "get")) {
final PsiField getterField = PropertyUtil.getFieldOfGetter(method);
if (getterField == null) {
return;
}
fieldName = getterField.getName();
extractedFieldName = name.substring(3);
setter = false;
}
else if (nameStartsWith(name, "is")) {
final PsiField getterField = PropertyUtil.getFieldOfGetter(method);
if (getterField == null) {
return;
}
fieldName = getterField.getName();
extractedFieldName = name.substring(2);
setter = false;
}
else if (nameStartsWith(name, "set")) {
final PsiField setterField = PropertyUtil.getFieldOfSetter(method);
if (setterField == null) {
return;
}
fieldName = setterField.getName();
extractedFieldName = name.substring(3);
setter = true;
}
else {
return;
}
final JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(method.getProject());
final String computedFieldName = codeStyleManager.propertyNameToVariableName(decapitalize(extractedFieldName), VariableKind.FIELD);
if (fieldName.equalsIgnoreCase(computedFieldName)) {
return;
}
if (onlyWarnWhenFieldPresent) {
final PsiClass aClass = method.getContainingClass();
if (aClass == null) {
return;
}
if (aClass.findFieldByName(computedFieldName, true) == null) {
return;
}
}
registerMethodError(method, Boolean.valueOf(setter), fieldName);
}
}
private static boolean nameStartsWith(String name, String prefix) {
return name.startsWith(prefix) && name.length() != prefix.length() && Character.isUpperCase(name.charAt(prefix.length()));
}
private static String decapitalize(String name) {
final StringBuilder result = new StringBuilder();
for (int i = 0, length = name.length(); i < length; i++) {
final char c = name.charAt(i);
if (Character.isUpperCase(c)) {
result.append(Character.toLowerCase(c));
}
else {
result.append(name.substring(i));
return result.toString();
}
}
return result.toString();
}
}
@@ -0,0 +1,13 @@
<html>
<body>
Reports suspicious getter or setter methods.
A getter or setter is suspicious if it accesses a different field than would be expected by its name.
<!-- tooltip end -->
<p>
Use the checkbox below to indicate the inspection should only warn when a field
with a name matching the getter or setter name is present in the class.
<p>
<small>New in 14</small>
</body>
</html>
@@ -0,0 +1,13 @@
public class SuspiciousGetterSetter {
private String myOne;
private String myTwo;
public String <warning descr="Getter 'getTwo()' returns field 'myOne'">getTwo</warning>() {
return myOne;
}
public void <warning descr="Setter 'setTwo()' assigns field 'myOne'">setTwo</warning>(String two) {
myOne = two;
}
}
@@ -0,0 +1,47 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.siyeh.ig.javabeans;
import com.intellij.codeInspection.InspectionProfileEntry;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import com.siyeh.ig.LightInspectionTestCase;
import org.jetbrains.annotations.Nullable;
/**
* @author Bas Leijdekkers
*/
public class SuspiciousGetterSetterInspectionTest extends LightInspectionTestCase {
public void testSuspiciousGetterSetter() {
final CodeStyleSettings settings = CodeStyleSettingsManager.getInstance(getProject()).getCurrentSettings();
final String oldPrefix = settings.FIELD_NAME_PREFIX;
try {
settings.FIELD_NAME_PREFIX = "my";
doTest();
} finally {
settings.FIELD_NAME_PREFIX = oldPrefix;
}
}
@Nullable
@Override
protected InspectionProfileEntry getInspection() {
final SuspiciousGetterSetterInspection inspection = new SuspiciousGetterSetterInspection();
inspection.onlyWarnWhenFieldPresent = true;
return inspection;
}
}