diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/META-INF/InspectionGadgets.xml b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/META-INF/InspectionGadgets.xml index 211d4cf0a413..eff2b8e1932a 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/META-INF/InspectionGadgets.xml +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/META-INF/InspectionGadgets.xml @@ -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"/> + {0} #loc return.of.inner.class.problem.descriptor=Return of instance of non-static inner class {0} #loc +parameter.type.prevents.overriding.display.name=Parameter type prevents overriding +parameter.type.prevents.overriding.problem.descriptor=Parameter type #ref 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 diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/inheritance/ParameterTypePreventsOverridingInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/inheritance/ParameterTypePreventsOverridingInspection.java new file mode 100644 index 000000000000..ae397a8cacb9 --- /dev/null +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/inheritance/ParameterTypePreventsOverridingInspection.java @@ -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 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 entry : problemTypeElements.entrySet()) { + registerError(entry.getKey(), entry.getKey(), entry.getValue()); + } + } + } + } +} diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/ParameterTypePreventsOverriding.html b/plugins/InspectionGadgets/src/inspectionDescriptions/ParameterTypePreventsOverriding.html new file mode 100644 index 000000000000..d4858bbd1b4e --- /dev/null +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/ParameterTypePreventsOverriding.html @@ -0,0 +1,22 @@ + + +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. +

Example: +


+public class A {
+  public void method(Object o) {}
+}
+
+public class B extends A {
+  public void method(Object o) {} // warning on parameter type here
+  class Object {}
+}
+
+ +

+New in 14 + + \ No newline at end of file diff --git a/plugins/InspectionGadgets/testsrc/com/siyeh/ig/inheritance/ParameterTypePreventsOverridingInspectionTest.java b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/inheritance/ParameterTypePreventsOverridingInspectionTest.java new file mode 100644 index 000000000000..1fd306ce865b --- /dev/null +++ b/plugins/InspectionGadgets/testsrc/com/siyeh/ig/inheritance/ParameterTypePreventsOverridingInspectionTest.java @@ -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(); + } +} \ No newline at end of file