diff --git a/plugins/devkit/resources/META-INF/plugin.xml b/plugins/devkit/resources/META-INF/plugin.xml index 37ea1f01e544..915706f0cbca 100644 --- a/plugins/devkit/resources/META-INF/plugin.xml +++ b/plugins/devkit/resources/META-INF/plugin.xml @@ -147,6 +147,12 @@ groupKey="inspections.group.name" enabledByDefault="false" isInternal="true" level="ERROR" implementationClass="org.jetbrains.idea.devkit.inspections.internal.HighlightVisitorInternalInspection"/> + diff --git a/plugins/devkit/resources/inspectionDescriptions/QuickFixGetFamilyNameViolation.html b/plugins/devkit/resources/inspectionDescriptions/QuickFixGetFamilyNameViolation.html new file mode 100644 index 000000000000..8c9c1911784c --- /dev/null +++ b/plugins/devkit/resources/inspectionDescriptions/QuickFixGetFamilyNameViolation.html @@ -0,0 +1,6 @@ + + +Inspection reports cases when QuickFix#getFamilyName() +implementation uses non-static fields, methods, containing method local variables variables etc. + + \ No newline at end of file diff --git a/plugins/devkit/src/inspections/QuickFixGetFamilyNameViolationInspection.java b/plugins/devkit/src/inspections/QuickFixGetFamilyNameViolationInspection.java new file mode 100644 index 000000000000..d46062c1fce0 --- /dev/null +++ b/plugins/devkit/src/inspections/QuickFixGetFamilyNameViolationInspection.java @@ -0,0 +1,79 @@ +/* + * Copyright 2000-2016 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 org.jetbrains.idea.devkit.inspections; + +import com.intellij.codeInspection.*; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.psi.*; +import com.intellij.psi.util.InheritanceUtil; +import com.intellij.psi.util.PsiTreeUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; + +/** + * @author Dmitry Batkovich + */ +public class QuickFixGetFamilyNameViolationInspection extends DevKitInspectionBase { + private final static Logger LOG = Logger.getInstance(QuickFixGetFamilyNameViolationInspection.class); + + @Nullable + @Override + public ProblemDescriptor[] checkMethod(@NotNull PsiMethod method, @NotNull InspectionManager manager, boolean isOnTheFly) { + if ("getFamilyName".equals(method.getName()) && !method.hasModifierProperty(PsiModifier.ABSTRACT)) { + final PsiClass aClass = method.getContainingClass(); + if (InheritanceUtil.isInheritor(aClass, QuickFix.class.getName()) && !isPureMethod(method)) { + final PsiIdentifier identifier = method.getNameIdentifier(); + LOG.assertTrue(identifier != null); + //noinspection DialogTitleCapitalization + return new ProblemDescriptor[]{ + manager.createProblemDescriptor(identifier, "QuickFix's getFamilyName() implementation must not depend on a specific context", (LocalQuickFix) null, ProblemHighlightType.GENERIC_ERROR_OR_WARNING, true)}; + } + } + return null; + } + + private static boolean isPureMethod(final PsiMethod method) { + if (method.hasModifierProperty(PsiModifier.STATIC)) return true; + final PsiCodeBlock body = method.getBody(); + if (body == null) return true; + final Collection referenceIterator = + PsiTreeUtil.findChildrenOfType(body, PsiJavaCodeReferenceElement.class); + for (PsiJavaCodeReferenceElement reference : referenceIterator) { + + final PsiElement resolved = reference.resolve(); + if (resolved instanceof PsiVariable) { + if (resolved instanceof PsiParameter || (resolved instanceof PsiLocalVariable && !PsiTreeUtil.isAncestor(body, resolved, false))) { + return false; + } + if (resolved instanceof PsiField && !((PsiField)resolved).hasModifierProperty(PsiModifier.STATIC)) { + return false; + } + } + + if (resolved instanceof PsiMethod && !((PsiMethod)resolved).hasModifierProperty(PsiModifier.STATIC)) { + final PsiClass resolvedContainingClass = ((PsiMethod)resolved).getContainingClass(); + if (resolvedContainingClass == method.getContainingClass()) { + if (!isPureMethod((PsiMethod)resolved)) { + return false; + } + } + } + } + return true; + } +} diff --git a/plugins/devkit/testData/inspections/getFamilyNameViolation/NotViolatedGetNameMethod.java b/plugins/devkit/testData/inspections/getFamilyNameViolation/NotViolatedGetNameMethod.java new file mode 100644 index 000000000000..1e64ea58b989 --- /dev/null +++ b/plugins/devkit/testData/inspections/getFamilyNameViolation/NotViolatedGetNameMethod.java @@ -0,0 +1,14 @@ +import com.intellij.codeInspection.QuickFix; + +class MyQuickFix implements QuickFix { + + public String getName() { + return "that fix do some fix"; + }; + + public String getFamilyName() { + return getName(); + }; + + +} \ No newline at end of file diff --git a/plugins/devkit/testData/inspections/getFamilyNameViolation/NotViolatedStaticField.java b/plugins/devkit/testData/inspections/getFamilyNameViolation/NotViolatedStaticField.java new file mode 100644 index 000000000000..62ceb6aeb255 --- /dev/null +++ b/plugins/devkit/testData/inspections/getFamilyNameViolation/NotViolatedStaticField.java @@ -0,0 +1,16 @@ +import com.intellij.codeInspection.QuickFix; + +class MyQuickFix implements QuickFix { + + static String someField = "Hello"; + + public String getName() { + return "some name"; + }; + + public String getFamilyName() { + return someField + getName() + "123"; + }; + + +} \ No newline at end of file diff --git a/plugins/devkit/testData/inspections/getFamilyNameViolation/NotViolatedStaticMethod.java b/plugins/devkit/testData/inspections/getFamilyNameViolation/NotViolatedStaticMethod.java new file mode 100644 index 000000000000..13aecff4781d --- /dev/null +++ b/plugins/devkit/testData/inspections/getFamilyNameViolation/NotViolatedStaticMethod.java @@ -0,0 +1,20 @@ +import com.intellij.codeInspection.QuickFix; + +class MyQuickFix implements QuickFix { + + String someField; + + public String getName() { + return someField; + }; + + public String getFamilyName() { + return getNameStatic() + "123"; + }; + + static String getNameStatic() { + return "Static"; + } + + +} \ No newline at end of file diff --git a/plugins/devkit/testData/inspections/getFamilyNameViolation/ViolationByExternalParameter.java b/plugins/devkit/testData/inspections/getFamilyNameViolation/ViolationByExternalParameter.java new file mode 100644 index 000000000000..dc4014552931 --- /dev/null +++ b/plugins/devkit/testData/inspections/getFamilyNameViolation/ViolationByExternalParameter.java @@ -0,0 +1,17 @@ +import com.intellij.codeInspection.QuickFix; + +class A { + + QuickFix getFix(final String someParameter) { + return new QuickFix() { + public String getName() { + return "some name"; + }; + + public String getFamilyName() { + return someParameter + "123"; + }; + }; + } + +} diff --git a/plugins/devkit/testData/inspections/getFamilyNameViolation/ViolationByField.java b/plugins/devkit/testData/inspections/getFamilyNameViolation/ViolationByField.java new file mode 100644 index 000000000000..c948ae538462 --- /dev/null +++ b/plugins/devkit/testData/inspections/getFamilyNameViolation/ViolationByField.java @@ -0,0 +1,16 @@ +import com.intellij.codeInspection.QuickFix; + +class MyQuickFix implements QuickFix { + + String someField; + + public String getName() { + return "some name"; + }; + + public String getFamilyName() { + return someField + getName() + "123"; + }; + + +} \ No newline at end of file diff --git a/plugins/devkit/testData/inspections/getFamilyNameViolation/ViolationByGetName.java b/plugins/devkit/testData/inspections/getFamilyNameViolation/ViolationByGetName.java new file mode 100644 index 000000000000..35a461b33a20 --- /dev/null +++ b/plugins/devkit/testData/inspections/getFamilyNameViolation/ViolationByGetName.java @@ -0,0 +1,16 @@ +import com.intellij.codeInspection.QuickFix; + +class MyQuickFix implements QuickFix { + + String someField; + + public String getName() { + return someField; + }; + + public String getFamilyName() { + return getName() + "123"; + }; + + +} \ No newline at end of file diff --git a/plugins/devkit/testSources/inspections/QuickFixGetFamilyNameViolationInspectionTest.java b/plugins/devkit/testSources/inspections/QuickFixGetFamilyNameViolationInspectionTest.java new file mode 100644 index 000000000000..10d4d984aa20 --- /dev/null +++ b/plugins/devkit/testSources/inspections/QuickFixGetFamilyNameViolationInspectionTest.java @@ -0,0 +1,67 @@ +/* + * Copyright 2000-2016 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 org.jetbrains.idea.devkit.inspections; + +import com.intellij.openapi.application.PluginPathManager; +import com.intellij.testFramework.TestDataPath; +import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase; + +/** + * @author Dmitry Batkovich + */ +@TestDataPath("$CONTENT_ROOT/testData/inspections/getFamilyNameViolation") +public class QuickFixGetFamilyNameViolationInspectionTest extends JavaCodeInsightFixtureTestCase { + + @Override + protected String getBasePath() { + return PluginPathManager.getPluginHomePathRelative("devkit") + "/testData/inspections/getFamilyNameViolation"; + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + myFixture.enableInspections(QuickFixGetFamilyNameViolationInspection.class); + myFixture.addClass("package com.intellij.codeInspection;" + + "public interface QuickFix {" + + " String getName();" + + " String getFamilyName();" + + "}"); + } + + public void testViolationByField() { + myFixture.testHighlighting(getTestName(false) + ".java"); + } + + public void testViolationByGetName() { + myFixture.testHighlighting(getTestName(false) + ".java"); + } + + public void testViolationByExternalParameter() { + myFixture.testHighlighting(getTestName(false) + ".java"); + } + + public void testNotViolatedStaticField() { + myFixture.testHighlighting(getTestName(false) + ".java"); + } + + public void testNotViolatedStaticMethod() { + myFixture.testHighlighting(getTestName(false) + ".java"); + } + + public void testNotViolatedGetNameMethod() { + myFixture.testHighlighting(getTestName(false) + ".java"); + } +}