new "Parameter type prevents overriding" inspection

This commit is contained in:
Bas Leijdekkers
2014-09-24 12:35:07 +02:00
parent 8db7406803
commit 340f5914fc
5 changed files with 275 additions and 0 deletions
@@ -1018,6 +1018,10 @@
bundle="com.siyeh.InspectionGadgetsBundle" key="non.protected.constructor.in.abstract.class.display.name"
groupBundle="messages.InspectionsBundle" groupKey="group.names.inheritance.issues" enabledByDefault="false"
level="WARNING" implementationClass="com.siyeh.ig.inheritance.NonProtectedConstructorInAbstractClassInspection"/>
<localInspection language="JAVA" shortName="ParameterTypePreventsOverriding" bundle="com.siyeh.InspectionGadgetsBundle"
key="parameter.type.prevents.overriding.display.name" groupBundle="messages.InspectionsBundle"
groupKey="group.names.inheritance.issues" enabledByDefault="false" level="WARNING"
implementationClass="com.siyeh.ig.inheritance.ParameterTypePreventsOverridingInspection"/>
<localInspection language="JAVA" shortName="ProblematicVarargsMethodOverride" bundle="com.siyeh.InspectionGadgetsBundle"
key="problematic.varargs.method.display.name" groupBundle="messages.InspectionsBundle"
groupKey="group.names.inheritance.issues" enabledByDefault="false" level="WARNING"
@@ -2121,3 +2121,7 @@ return.of.inner.class.display.name=Return of instance of anonymous, local or inn
return.of.anonymous.class.problem.descriptor=Return of instance of anonymous class #loc
return.of.local.class.problem.descriptor=Return of instance of local class <code>{0}</code> #loc
return.of.inner.class.problem.descriptor=Return of instance of non-static inner class <code>{0}</code> #loc
parameter.type.prevents.overriding.display.name=Parameter type prevents overriding
parameter.type.prevents.overriding.problem.descriptor=Parameter type <code>#ref</code> is from package ''{0}'' while super method parameter type is from package ''{1}'' preventing overriding
parameter.type.prevents.overriding.quickfix=Change type of parameter to ''{0}''
parameter.type.prevents.overriding.family.quickfix=Change type of parameter
@@ -0,0 +1,165 @@
/*
* 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.inheritance;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.util.containers.ContainerUtilRt;
import com.siyeh.InspectionGadgetsBundle;
import com.siyeh.ig.BaseInspection;
import com.siyeh.ig.BaseInspectionVisitor;
import com.siyeh.ig.InspectionGadgetsFix;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
/**
* @author Bas Leijdekkers
*/
public class ParameterTypePreventsOverridingInspection extends BaseInspection {
@Nls
@NotNull
@Override
public String getDisplayName() {
return InspectionGadgetsBundle.message("parameter.type.prevents.overriding.display.name");
}
@NotNull
@Override
protected String buildErrorString(Object... infos) {
final PsiTypeElement typeElement = (PsiTypeElement)infos[0];
final PsiType type = typeElement.getType();
final String packageName = StringUtil.getPackageName(type.getCanonicalText());
final PsiTypeElement superTypeElement = (PsiTypeElement)infos[1];
final PsiType superType = superTypeElement.getType();
final String superPackageName = StringUtil.getPackageName(superType.getCanonicalText());
return InspectionGadgetsBundle.message("parameter.type.prevents.overriding.problem.descriptor", packageName, superPackageName);
}
@Nullable
@Override
protected InspectionGadgetsFix buildFix(Object... infos) {
final PsiTypeElement typeElement = (PsiTypeElement)infos[1];
final PsiType type = typeElement.getType();
return new ParameterTypePreventsOverridingFix(type.getCanonicalText());
}
private static class ParameterTypePreventsOverridingFix extends InspectionGadgetsFix {
private final String myNewTypeText;
public ParameterTypePreventsOverridingFix(String newTypeText) {
myNewTypeText = newTypeText;
}
@NotNull
@Override
public String getName() {
return InspectionGadgetsBundle.message("parameter.type.prevents.overriding.quickfix", myNewTypeText);
}
@NotNull
@Override
public String getFamilyName() {
return InspectionGadgetsBundle.message("parameter.type.prevents.overriding.family.quickfix");
}
@Override
protected void doFix(Project project, ProblemDescriptor descriptor) {
final PsiElement element = descriptor.getPsiElement();
if (!(element instanceof PsiTypeElement)) {
return;
}
final PsiTypeElement typeElement = (PsiTypeElement)element;
final PsiElementFactory factory = JavaPsiFacade.getElementFactory(typeElement.getProject());
final PsiTypeElement newTypeElement = factory.createTypeElementFromText(myNewTypeText, typeElement);
typeElement.replace(newTypeElement);
}
}
@Override
public BaseInspectionVisitor buildVisitor() {
return new ParameterTypePreventsOverridingVisitor();
}
private static class ParameterTypePreventsOverridingVisitor extends BaseInspectionVisitor {
@Override
public void visitMethod(PsiMethod method) {
super.visitMethod(method);
final PsiParameterList parameterList = method.getParameterList();
final int parameterCount = parameterList.getParametersCount();
if (parameterCount == 0) {
return;
}
final PsiType returnType = method.getReturnType();
if (returnType == null) {
return;
}
final PsiClass containingClass = method.getContainingClass();
if (containingClass == null) {
return;
}
final PsiClass superClass = containingClass.getSuperClass();
if (superClass == null) {
return;
}
final PsiParameter[] parameters = parameterList.getParameters();
final String name = method.getName();
final PsiMethod[] superMethods = superClass.findMethodsByName(name, true);
outer: for (PsiMethod superMethod : superMethods) {
final PsiType superReturnType = superMethod.getReturnType();
if (superReturnType == null || !superReturnType.isAssignableFrom(returnType)) {
continue;
}
final PsiParameterList superParameterList = superMethod.getParameterList();
if (superParameterList.getParametersCount() != parameterCount) {
continue;
}
final PsiParameter[] superParameters = superParameterList.getParameters();
final Map<PsiTypeElement, PsiTypeElement> problemTypeElements = ContainerUtilRt.newHashMap(2);
for (int i = 0; i < parameters.length; i++) {
final PsiParameter parameter = parameters[i];
final PsiParameter superParameter = superParameters[i];
final PsiType type = parameter.getType();
final PsiType superType = superParameter.getType();
if (type.equals(superType)) {
continue;
}
if (!type.getPresentableText().equals(superType.getPresentableText())) {
return;
}
final PsiTypeElement typeElement = parameter.getTypeElement();
if (typeElement == null) {
return;
}
final PsiTypeElement superParameterTypeElement = superParameter.getTypeElement();
if (superParameterTypeElement == null) {
continue outer;
}
problemTypeElements.put(typeElement, superParameterTypeElement);
}
for (Map.Entry<PsiTypeElement, PsiTypeElement> entry : problemTypeElements.entrySet()) {
registerError(entry.getKey(), entry.getKey(), entry.getValue());
}
}
}
}
}
@@ -0,0 +1,22 @@
<html>
<body>
Reports method parameter types in methods that look like they are overriding a super method,
but do not because of the parameter type coming from a different package than the super method type,
although the class name matches.
This prevents the parameter's method from overriding the super method and can be very confusing.
<p>Example:
<pre><code>
<b>public class</b> A {
<b>public void</b> method(Object o) {}
}
<b>public class</b> B <b>extends</b> A {
<b>public void</b> method(Object o) {} // warning on parameter type here
<b>class</b> Object {}
}
</code></pre>
<!-- tooltip end -->
<p>
<small>New in 14</small>
</body>
</html>
@@ -0,0 +1,80 @@
/*
* 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.inheritance;
import com.intellij.codeInspection.InspectionProfileEntry;
import com.siyeh.ig.LightInspectionTestCase;
import org.jetbrains.annotations.Nullable;
/**
* @author Bas Leijdekkers
*/
public class ParameterTypePreventsOverridingInspectionTest extends LightInspectionTestCase {
public void testSimple() {
doTest("package c;" +
"import a.*;" +
"import b.String;" +
"class Sub extends Super {" +
" void m(/*Parameter type 'String' is from package 'b' while super method parameter type is from package 'java.lang' preventing overriding*/String/**/ s) {}" +
"}");
}
public void testNonMatchingReturnType() {
doTest("package c;" +
"import a.*;" +
"import b.String;" +
"class Sub extends Super {" +
" int m(String s) {" +
" return -1;" +
" }" +
" class String {}" +
"}");
}
public void testMatchingReturnType() {
doTest("package c;" +
"import a.*;" +
"import b.String;" +
"class Sub extends Super {" +
" Integer n(/*Parameter type 'String' is from package 'b' while super method parameter type is from package 'java.lang' preventing overriding*/String/**/ s, " +
" /*Parameter type 'String' is from package 'b' while super method parameter type is from package 'java.lang' preventing overriding*/String/**/ t) {" +
" return null;" +
" }" +
"}");
}
@Override
protected String[] getEnvironmentClasses() {
return new String[]{
"package a;" +
"public class Super {" +
" void m(String s) {}" +
" Object n(String s, String t) {" +
" return null;" +
" }" +
"}",
"package b;" +
"public class String {}"
};
}
@Nullable
@Override
protected InspectionProfileEntry getInspection() {
return new ParameterTypePreventsOverridingInspection();
}
}