From 94fc29cbb60446c480babc1abcc11e5d73adf1c4 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Tue, 31 Jan 2012 18:10:45 +0400 Subject: [PATCH 01/23] rename file: move to java/groovy --- .../intellij/refactoring/actions}/RenameFileAction.java | 8 +++++--- platform/platform-resources/src/idea/LangActions.xml | 4 ---- resources/src/idea/JavaActions.xml | 5 +++++ 3 files changed, 10 insertions(+), 7 deletions(-) rename {platform/lang-impl/src/com/intellij/refactoring/rename => java/java-impl/src/com/intellij/refactoring/actions}/RenameFileAction.java (82%) diff --git a/platform/lang-impl/src/com/intellij/refactoring/rename/RenameFileAction.java b/java/java-impl/src/com/intellij/refactoring/actions/RenameFileAction.java similarity index 82% rename from platform/lang-impl/src/com/intellij/refactoring/rename/RenameFileAction.java rename to java/java-impl/src/com/intellij/refactoring/actions/RenameFileAction.java index a0aa127ec7b1..1e0008cb867d 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/rename/RenameFileAction.java +++ b/java/java-impl/src/com/intellij/refactoring/actions/RenameFileAction.java @@ -13,13 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.intellij.refactoring.rename; +package com.intellij.refactoring.actions; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiClassOwner; import com.intellij.psi.PsiFile; +import com.intellij.refactoring.rename.PsiElementRenameHandler; /** * @author ven @@ -34,13 +36,13 @@ public class RenameFileAction extends AnAction implements DumbAware { assert virtualFile != null; final Project project = e.getData(PlatformDataKeys.PROJECT); assert project != null; - new RenameDialog(project, file, file, null).show(); + PsiElementRenameHandler.invoke(file, project, file, null); } public void update(AnActionEvent e) { PsiFile file = e.getData(LangDataKeys.PSI_FILE); Presentation presentation = e.getPresentation(); - boolean enabled = file != null && e.getPlace() != ActionPlaces.EDITOR_POPUP && e.getData(PlatformDataKeys.PROJECT) != null; + boolean enabled = file instanceof PsiClassOwner && e.getPlace() != ActionPlaces.EDITOR_POPUP && e.getData(PlatformDataKeys.PROJECT) != null; presentation.setEnabled(enabled); presentation.setVisible(enabled); if (enabled) { diff --git a/platform/platform-resources/src/idea/LangActions.xml b/platform/platform-resources/src/idea/LangActions.xml index 4e5cd27f0bfd..55c7d4b531c0 100644 --- a/platform/platform-resources/src/idea/LangActions.xml +++ b/platform/platform-resources/src/idea/LangActions.xml @@ -254,7 +254,6 @@ class = "com.intellij.refactoring.actions.RefactoringQuickListPopupAction" text = "Refactor This..." description="Context aware popup with list of refactoring actions"/> - @@ -572,9 +571,6 @@ - - - diff --git a/resources/src/idea/JavaActions.xml b/resources/src/idea/JavaActions.xml index 9ba1550421ee..675fcf4d2022 100644 --- a/resources/src/idea/JavaActions.xml +++ b/resources/src/idea/JavaActions.xml @@ -73,6 +73,11 @@ + + + + + From 5ce1335b801c3548c0359b20d22b14cbf71db752 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Tue, 31 Jan 2012 19:03:18 +0400 Subject: [PATCH 02/23] extension to forbid suggestion of final modifier (IDEA-77156) --- .../canBeFinal/CanBeFinalHandler.java | 37 +++++++++++++++++++ .../canBeFinal/CanBeFinalInspection.java | 3 ++ .../ig/style/FieldMayBeFinalInspection.java | 2 + resources/src/META-INF/IdeaPlugin.xml | 2 + 4 files changed, 44 insertions(+) create mode 100644 java/java-impl/src/com/intellij/codeInspection/canBeFinal/CanBeFinalHandler.java diff --git a/java/java-impl/src/com/intellij/codeInspection/canBeFinal/CanBeFinalHandler.java b/java/java-impl/src/com/intellij/codeInspection/canBeFinal/CanBeFinalHandler.java new file mode 100644 index 000000000000..f6d5acf594cf --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInspection/canBeFinal/CanBeFinalHandler.java @@ -0,0 +1,37 @@ +/* + * Copyright 2000-2012 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.intellij.codeInspection.canBeFinal; + +import com.intellij.openapi.extensions.ExtensionPointName; +import com.intellij.openapi.extensions.Extensions; +import com.intellij.psi.PsiMember; + +/** + * User: anna + * Date: 1/31/12 + */ +public abstract class CanBeFinalHandler { + public static final ExtensionPointName EP_NAME = ExtensionPointName.create("com.intellij.canBeFinal"); + + public abstract boolean canBeFinal(PsiMember member); + + public static boolean allowToBeFinal(PsiMember member) { + for (CanBeFinalHandler handler : Extensions.getExtensions(EP_NAME)) { + if (!handler.canBeFinal(member)) return false; + } + return true; + } +} diff --git a/java/java-impl/src/com/intellij/codeInspection/canBeFinal/CanBeFinalInspection.java b/java/java-impl/src/com/intellij/codeInspection/canBeFinal/CanBeFinalInspection.java index 85d04e9abe00..d14eb9dc930d 100644 --- a/java/java-impl/src/com/intellij/codeInspection/canBeFinal/CanBeFinalInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/canBeFinal/CanBeFinalInspection.java @@ -139,6 +139,9 @@ public class CanBeFinalInspection extends GlobalJavaInspectionTool { if (refElement.isFinal()) return null; if (!((RefElementImpl)refElement).checkFlag(CanBeFinalAnnotator.CAN_BE_FINAL_MASK)) return null; + final PsiMember psiMember = (PsiMember)refElement.getElement(); + if (!CanBeFinalHandler.allowToBeFinal(psiMember)) return null; + PsiIdentifier psiIdentifier = null; if (refElement instanceof RefClass) { RefClass refClass = (RefClass)refElement; diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/style/FieldMayBeFinalInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/style/FieldMayBeFinalInspection.java index c3fec9f74ba6..02554ab35332 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/style/FieldMayBeFinalInspection.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/style/FieldMayBeFinalInspection.java @@ -15,6 +15,7 @@ */ package com.siyeh.ig.style; +import com.intellij.codeInspection.canBeFinal.CanBeFinalHandler; import com.intellij.psi.PsiField; import com.intellij.psi.PsiModifier; import com.siyeh.InspectionGadgetsBundle; @@ -65,6 +66,7 @@ public class FieldMayBeFinalInspection extends BaseInspection { !field.hasModifierProperty(PsiModifier.PRIVATE)) { return; } + if (!CanBeFinalHandler.allowToBeFinal(field)) return; if (!FinalUtils.canBeFinal(field)) { return; } diff --git a/resources/src/META-INF/IdeaPlugin.xml b/resources/src/META-INF/IdeaPlugin.xml index adb4c5cd8e46..472fdda42908 100644 --- a/resources/src/META-INF/IdeaPlugin.xml +++ b/resources/src/META-INF/IdeaPlugin.xml @@ -65,6 +65,8 @@ + From f5809aaca2b2d6eac977693b5ae7002cc860d3ef Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Tue, 31 Jan 2012 19:54:59 +0400 Subject: [PATCH 03/23] exclude root with all failed tests --- jps/jps-builders/jps-builders.iml | 1 - 1 file changed, 1 deletion(-) diff --git a/jps/jps-builders/jps-builders.iml b/jps/jps-builders/jps-builders.iml index 2a02bef74b73..1a750f0f1847 100644 --- a/jps/jps-builders/jps-builders.iml +++ b/jps/jps-builders/jps-builders.iml @@ -4,7 +4,6 @@ - From 7aca50fece43b7057343f320b72e23696ecd909b Mon Sep 17 00:00:00 2001 From: Maxim Shafirov Date: Tue, 31 Jan 2012 20:00:07 +0400 Subject: [PATCH 04/23] Core's findClass() works with inner classes too --- .../intellij/core/CoreJavaFileManager.java | 93 +++++++++++++++---- 1 file changed, 77 insertions(+), 16 deletions(-) diff --git a/java/java-psi-impl/src/com/intellij/core/CoreJavaFileManager.java b/java/java-psi-impl/src/com/intellij/core/CoreJavaFileManager.java index a3672df32892..845339e66d7a 100644 --- a/java/java-psi-impl/src/com/intellij/core/CoreJavaFileManager.java +++ b/java/java-psi-impl/src/com/intellij/core/CoreJavaFileManager.java @@ -15,6 +15,7 @@ */ package com.intellij.core; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.roots.PackageIndex; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.VirtualFile; @@ -39,6 +40,8 @@ import java.util.List; * @author yole */ public class CoreJavaFileManager extends PackageIndex implements JavaFileManager { + private static final Logger LOG = Logger.getInstance("#com.intellij.core.CoreJavaFileManager"); + private final CoreLocalFileSystem myLocalFileSystem; private final CoreJarFileSystem myJarFileSystem; private final List myClasspath = new ArrayList(); @@ -85,11 +88,17 @@ public class CoreJavaFileManager extends PackageIndex implements JavaFileManager @Nullable private VirtualFile findUnderClasspathEntry(File classpathEntry, String relativeName) { + VirtualFile root = findRootInClassPathEntry(classpathEntry); + return root != null ? root.findFileByRelativePath(relativeName) : null; + } + + @Nullable + private VirtualFile findRootInClassPathEntry(File classpathEntry) { if (classpathEntry.isFile()) { - return myJarFileSystem.findFileByPath(classpathEntry.getPath() + "!/" + relativeName); + return myJarFileSystem.findFileByPath(classpathEntry.getPath() + "!/"); } else { - return myLocalFileSystem.findFileByPath(new File(classpathEntry, relativeName).getPath()); + return myLocalFileSystem.findFileByPath(classpathEntry.getPath()); } } @@ -115,25 +124,77 @@ public class CoreJavaFileManager extends PackageIndex implements JavaFileManager } @Nullable - private PsiClass findClassInClasspathEntry(String qName, File file) { - // TODO handle inner classes correctly - String fileName = qName.replace(".", "/") + ".java"; - VirtualFile classFile = findUnderClasspathEntry(file, fileName); - if (classFile == null) { - fileName = qName.replace(".", "/") + ".class"; - classFile = findUnderClasspathEntry(file, fileName); + private PsiClass findClassInClasspathEntry(String qName, File rootEntry) { + VirtualFile root = findRootInClassPathEntry(rootEntry); + if (root == null) return null; + + return findClassInClasspathRoot(qName, root, myPsiManager); + } + + @Nullable + public static PsiClass findClassInClasspathRoot(String qName, VirtualFile root, PsiManager psiManager) { + String pathRest = qName; + VirtualFile cur = root; + + while (true) { + int dot = pathRest.indexOf('.'); + if (dot < 0) break; + + String pathComponent = pathRest.substring(0, dot); + VirtualFile child = cur.findChild(pathComponent); + + if (child == null) break; + pathRest = pathRest.substring(dot + 1); + cur = child; } - if (classFile != null) { - PsiFile psiFile = myPsiManager.findFile(classFile); - if (!(psiFile instanceof PsiJavaFile)) { - throw new UnsupportedOperationException("no java file for " + fileName); + String className = pathRest.replace('.', '$'); + int bucks = className.indexOf('$'); + + String rootClassName; + if (bucks < 0) { + rootClassName = className; + } + else { + rootClassName = className.substring(0, bucks); + className = className.substring(bucks + 1); + } + + VirtualFile vFile = cur.findChild(rootClassName + ".class"); + if (vFile == null) vFile = cur.findChild(rootClassName + ".java"); + + if (vFile != null) { + if (!vFile.isValid()) { + LOG.error("Invalid child of valid parent: " + vFile.getPath() + "; " + root.isValid() + " path=" + root.getPath()); + return null; } - final PsiClass[] classes = ((PsiJavaFile)psiFile).getClasses(); - if (classes.length == 1) { - return classes[0]; + + final PsiFile file = psiManager.findFile(vFile); + if (file instanceof PsiClassOwner) { + final PsiClass[] classes = ((PsiClassOwner)file).getClasses(); + if (classes.length == 1) { + PsiClass curClass = classes[0]; + + if (bucks > 0) { + while (true) { + int b = className.indexOf("$"); + + String component = b < 0 ? className : className.substring(0, b); + PsiClass inner = curClass.findInnerClassByName(component, false); + + if (inner == null) return null; + curClass = inner; + className = className.substring(b + 1); + if (b < 0) break; + } + } + + + return curClass; + } } } + return null; } From 496bfb7a42421e01a0a1373d80d4c18d4728e309 Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Tue, 31 Jan 2012 17:02:41 +0100 Subject: [PATCH 05/23] Fix "Field repeatedly accessed in method" inspection problem descriptor --- .../src/com/siyeh/InspectionGadgetsBundle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/InspectionGadgets/src/com/siyeh/InspectionGadgetsBundle.properties b/plugins/InspectionGadgets/src/com/siyeh/InspectionGadgetsBundle.properties index 4639e93c509b..2dd9e08e81b1 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/InspectionGadgetsBundle.properties +++ b/plugins/InspectionGadgets/src/com/siyeh/InspectionGadgetsBundle.properties @@ -460,7 +460,7 @@ large.array.allocation.no.outofmemoryerror.problem.descriptor=Large array alloca large.array.allocation.no.outofmemoryerror.maximum.number.of.elements.option=Maximum number of elements: connection.opened.not.safely.closed.display.name=Connection opened but not safely closed field.repeatedly.accessed.in.method.display.name=Field repeatedly accessed in method -field.repeatedly.accessed.in.method.problem.descriptor=Field ''{0}'' accessed repeatedly in method #ref(0 #loc +field.repeatedly.accessed.in.method.problem.descriptor=Field ''{0}'' accessed repeatedly in method #ref() #loc field.repeatedly.accessed.in.method.ignore.option=Ignore final fields interface.one.inheritor.display.name=Interface which has only one direct inheritor interface.one.inheritor.problem.descriptor=Interface #ref has only one direct inheritor #loc From 7638e45ab3c48eaa3f05cdfd980d072c33f1a2f6 Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Tue, 31 Jan 2012 17:03:54 +0100 Subject: [PATCH 06/23] Make it easier to test IPPs MutablyNamedIntentions --- .../testSrc/com/siyeh/ipp/IPPTestCase.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/IPPTestCase.java b/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/IPPTestCase.java index de380229d7e2..1f8c3bf2029b 100644 --- a/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/IPPTestCase.java +++ b/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/IPPTestCase.java @@ -28,8 +28,12 @@ public abstract class IPPTestCase extends LightCodeInsightFixtureTestCase { protected abstract String getRelativePath(); protected void doTest() { + doTest(getIntentionName()); + } + + protected void doTest(String intentionName) { final String testName = getTestName(false); - CodeInsightTestUtil.doIntentionTest(myFixture, getIntentionName(), testName + ".java", testName + "_after.java"); + CodeInsightTestUtil.doIntentionTest(myFixture, intentionName, testName + ".java", testName + "_after.java"); } protected void assertIntentionNotAvailable() { From 55ee5dcb50a60888a45bcee5475a839aead4aacc Mon Sep 17 00:00:00 2001 From: Bas Leijdekkers Date: Tue, 31 Jan 2012 17:06:18 +0100 Subject: [PATCH 07/23] Simplify Demorgans intention, don't keep unnecessary parentheses, add tests --- .../src/com/siyeh/ipp/base/Intention.java | 101 +++++++----------- .../siyeh/ipp/bool/DemorgansIntention.java | 98 ++++++----------- .../src/com/siyeh/ipp/psiutils/BoolUtils.java | 67 ++++++------ .../bool/demorgans/NeedsMoreParentheses.java | 7 ++ .../demorgans/NeedsMoreParentheses_after.java | 7 ++ .../ipp/bool/demorgans/NeedsParentheses.java | 8 ++ .../demorgans/NeedsParentheses_after.java | 8 ++ .../bool/demorgans/NotTooManyParentheses.java | 7 ++ .../NotTooManyParentheses_after.java | 7 ++ .../ipp/bool/DemorgansIntentionTest.java | 35 ++++++ 10 files changed, 182 insertions(+), 163 deletions(-) create mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/bool/demorgans/NeedsMoreParentheses.java create mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/bool/demorgans/NeedsMoreParentheses_after.java create mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/bool/demorgans/NeedsParentheses.java create mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/bool/demorgans/NeedsParentheses_after.java create mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/bool/demorgans/NotTooManyParentheses.java create mode 100644 plugins/IntentionPowerPak/test/com/siyeh/ipp/bool/demorgans/NotTooManyParentheses_after.java create mode 100644 plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/bool/DemorgansIntentionTest.java diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/base/Intention.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/base/Intention.java index a4d69d8cb90b..c056fe0dcb5f 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/base/Intention.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/base/Intention.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2011 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2012 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. @@ -24,7 +24,7 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.codeStyle.JavaCodeStyleManager; -import com.intellij.psi.util.PsiUtil; +import com.intellij.psi.util.PsiUtilCore; import com.intellij.util.IncorrectOperationException; import com.siyeh.IntentionPowerPackBundle; import com.siyeh.ipp.psiutils.BoolUtils; @@ -46,8 +46,7 @@ public abstract class Intention extends PsiElementBaseIntentionAction { } @Override - public void invoke(Project project, Editor editor, PsiElement element) - throws IncorrectOperationException { + public void invoke(Project project, Editor editor, PsiElement element) throws IncorrectOperationException { if (!isWritable(project, element)) { return; } @@ -58,33 +57,25 @@ public abstract class Intention extends PsiElementBaseIntentionAction { processIntention(matchingElement); } - protected abstract void processIntention(@NotNull PsiElement element) - throws IncorrectOperationException; + protected abstract void processIntention(@NotNull PsiElement element) throws IncorrectOperationException; @NotNull protected abstract PsiElementPredicate getElementPredicate(); - protected static void replaceExpression(@NotNull String newExpression, - @NotNull PsiExpression expression) + protected static void replaceExpression(@NotNull String newExpression, @NotNull PsiExpression expression) throws IncorrectOperationException { final Project project = expression.getProject(); - final PsiElementFactory factory = - JavaPsiFacade.getElementFactory(project); - final PsiExpression newCall = - factory.createExpressionFromText(newExpression, expression); + final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project); + final PsiExpression newCall = factory.createExpressionFromText(newExpression, expression); final PsiElement insertedElement = expression.replace(newCall); - final CodeStyleManager codeStyleManager = - CodeStyleManager.getInstance(project); + final CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(project); codeStyleManager.reformat(insertedElement); } - protected static void replaceExpressionWithNegatedExpression( - @NotNull PsiExpression newExpression, - @NotNull PsiExpression expression) + protected static void replaceExpressionWithNegatedExpression(@NotNull PsiExpression newExpression, @NotNull PsiExpression expression) throws IncorrectOperationException { final Project project = expression.getProject(); - final PsiElementFactory factory = - JavaPsiFacade.getElementFactory(project); + final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project); PsiExpression expressionToReplace = expression; final String newExpressionText = newExpression.getText(); final String expString; @@ -93,36 +84,29 @@ public abstract class Intention extends PsiElementBaseIntentionAction { expString = newExpressionText; } else if (ComparisonUtils.isComparison(newExpression)) { - final PsiBinaryExpression binaryExpression = - (PsiBinaryExpression)newExpression; - final String negatedComparison = - ComparisonUtils.getNegatedComparison(binaryExpression.getOperationTokenType()); + final PsiBinaryExpression binaryExpression = (PsiBinaryExpression)newExpression; + final String negatedComparison = ComparisonUtils.getNegatedComparison(binaryExpression.getOperationTokenType()); final PsiExpression lhs = binaryExpression.getLOperand(); final PsiExpression rhs = binaryExpression.getROperand(); assert rhs != null; expString = lhs.getText() + negatedComparison + rhs.getText(); } else { - if (ParenthesesUtils.getPrecedence(newExpression) > - ParenthesesUtils.PREFIX_PRECEDENCE) { + if (ParenthesesUtils.getPrecedence(newExpression) > ParenthesesUtils.PREFIX_PRECEDENCE) { expString = "!(" + newExpressionText + ')'; } else { expString = '!' + newExpressionText; } } - final PsiExpression newCall = - factory.createExpressionFromText(expString, expression); + final PsiExpression newCall = factory.createExpressionFromText(expString, expression); assert expressionToReplace != null; final PsiElement insertedElement = expressionToReplace.replace(newCall); - final CodeStyleManager codeStyleManager = - CodeStyleManager.getInstance(project); + final CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(project); codeStyleManager.reformat(insertedElement); } - protected static void replaceExpressionWithNegatedExpressionString( - @NotNull String newExpression, - @NotNull PsiExpression expression) + protected static void replaceExpressionWithNegatedExpressionString(@NotNull String newExpression, @NotNull PsiExpression expression) throws IncorrectOperationException { final Project project = expression.getProject(); final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(project); @@ -130,52 +114,44 @@ public abstract class Intention extends PsiElementBaseIntentionAction { PsiExpression expressionToReplace = expression; final String expString; if (BoolUtils.isNegated(expression)) { - expressionToReplace = BoolUtils.findNegation(expression); + expressionToReplace = BoolUtils.findNegation(expressionToReplace); expString = newExpression; } else { + PsiElement parent = expressionToReplace.getParent(); + while (parent instanceof PsiParenthesizedExpression) { + expressionToReplace = (PsiExpression)parent; + parent = parent.getParent(); + } expString = "!(" + newExpression + ')'; } - final PsiExpression newCall = - factory.createExpressionFromText(expString, expression); + final PsiExpression newCall = factory.createExpressionFromText(expString, expression); assert expressionToReplace != null; final PsiElement insertedElement = expressionToReplace.replace(newCall); - final CodeStyleManager codeStyleManager = - CodeStyleManager.getInstance(project); + final CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(project); codeStyleManager.reformat(insertedElement); } - protected static void replaceStatement( - @NonNls @NotNull String newStatementText, - @NonNls @NotNull PsiStatement statement) + protected static void replaceStatement(@NonNls @NotNull String newStatementText, @NonNls @NotNull PsiStatement statement) throws IncorrectOperationException { final Project project = statement.getProject(); - final PsiElementFactory factory = - JavaPsiFacade.getElementFactory(project); - final PsiStatement newStatement = - factory.createStatementFromText(newStatementText, statement); + final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project); + final PsiStatement newStatement = factory.createStatementFromText(newStatementText, statement); final PsiElement insertedElement = statement.replace(newStatement); - final CodeStyleManager codeStyleManager = - CodeStyleManager.getInstance(project); + final CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(project); codeStyleManager.reformat(insertedElement); } - protected static void replaceStatementAndShorten( - @NonNls @NotNull String newStatementText, - @NonNls @NotNull PsiStatement statement) + protected static void replaceStatementAndShorten(@NonNls @NotNull String newStatementText, @NonNls @NotNull PsiStatement statement) throws IncorrectOperationException { final Project project = statement.getProject(); final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(project); final PsiElementFactory factory = psiFacade.getElementFactory(); - final PsiStatement newStatement = - factory.createStatementFromText(newStatementText, statement); + final PsiStatement newStatement = factory.createStatementFromText(newStatementText, statement); final PsiElement insertedElement = statement.replace(newStatement); - final JavaCodeStyleManager javaCodeStyleManager = - JavaCodeStyleManager.getInstance(project); - final PsiElement shortenedElement = - javaCodeStyleManager.shortenClassReferences(insertedElement); - final CodeStyleManager codeStyleManager = - CodeStyleManager.getInstance(project); + final JavaCodeStyleManager javaCodeStyleManager = JavaCodeStyleManager.getInstance(project); + final PsiElement shortenedElement = javaCodeStyleManager.shortenClassReferences(insertedElement); + final CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(project); codeStyleManager.reformat(shortenedElement); } @@ -202,8 +178,7 @@ public abstract class Intention extends PsiElementBaseIntentionAction { } @Override - public boolean isAvailable(@NotNull Project project, Editor editor, - @NotNull PsiElement element) { + public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element) { return findMatchingElement(element, editor) != null; } @@ -213,14 +188,12 @@ public abstract class Intention extends PsiElementBaseIntentionAction { } private static boolean isWritable(Project project, PsiElement element) { - final VirtualFile virtualFile = PsiUtil.getVirtualFile(element); + final VirtualFile virtualFile = PsiUtilCore.getVirtualFile(element); if (virtualFile == null) { return true; } - final ReadonlyStatusHandler readonlyStatusHandler = - ReadonlyStatusHandler.getInstance(project); - final ReadonlyStatusHandler.OperationStatus operationStatus = - readonlyStatusHandler.ensureFilesWritable(virtualFile); + final ReadonlyStatusHandler readonlyStatusHandler = ReadonlyStatusHandler.getInstance(project); + final ReadonlyStatusHandler.OperationStatus operationStatus = readonlyStatusHandler.ensureFilesWritable(virtualFile); return !operationStatus.hasReadonlyFiles(); } diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/bool/DemorgansIntention.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/bool/DemorgansIntention.java index b3ef7f4863b6..ce7cb8dd7cd7 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/bool/DemorgansIntention.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/bool/DemorgansIntention.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2006 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2012 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. @@ -29,8 +29,7 @@ import org.jetbrains.annotations.NotNull; public class DemorgansIntention extends MutablyNamedIntention { protected String getTextForElement(PsiElement element) { - final PsiPolyadicExpression binaryExpression = - (PsiPolyadicExpression)element; + final PsiPolyadicExpression binaryExpression = (PsiPolyadicExpression)element; final IElementType tokenType = binaryExpression.getOperationTokenType(); if (tokenType.equals(JavaTokenType.ANDAND)) { return IntentionPowerPackBundle.message("demorgans.intention.name1"); @@ -45,84 +44,55 @@ public class DemorgansIntention extends MutablyNamedIntention { return new ConjunctionPredicate(); } - public void processIntention(@NotNull PsiElement element) - throws IncorrectOperationException { - PsiPolyadicExpression exp = - (PsiPolyadicExpression)element; - final IElementType tokenType = exp.getOperationTokenType(); - PsiElement parent = exp.getParent(); - while (isConjunctionExpression(parent, tokenType)) { - exp = (PsiPolyadicExpression)parent; - assert exp != null; - parent = exp.getParent(); - } - final String newExpression = - convertConjunctionExpression(exp, tokenType); - replaceExpressionWithNegatedExpressionString(newExpression, - exp); + public void processIntention(@NotNull PsiElement element) throws IncorrectOperationException { + final PsiPolyadicExpression polyadicExpression = (PsiPolyadicExpression)element; + final String newExpression = convertConjunctionExpression(polyadicExpression); + replaceExpressionWithNegatedExpressionString(newExpression, polyadicExpression); } - private static String convertConjunctionExpression(PsiPolyadicExpression exp, - IElementType tokenType) { + private static String convertConjunctionExpression(PsiPolyadicExpression polyadicExpression) { + final IElementType tokenType = polyadicExpression.getOperationTokenType(); final String flippedConjunction; - if (tokenType.equals(JavaTokenType.ANDAND)) { - flippedConjunction = "||"; + final boolean tokenTypeAndAnd = tokenType.equals(JavaTokenType.ANDAND); + flippedConjunction = tokenTypeAndAnd ? "||" : "&&"; + final StringBuilder result = new StringBuilder(); + for (PsiExpression operand : polyadicExpression.getOperands()) { + if (result.length() != 0) { + result.append(flippedConjunction); + } + result.append(convertLeafExpression(operand, tokenTypeAndAnd)); } - else { - flippedConjunction = "&&"; - } - String result = null; - for (PsiExpression expression : exp.getOperands()) { - String lhsText = convertLeafExpression(expression); - result = result == null ? lhsText : result + flippedConjunction + lhsText; - } - return result; + return result.toString(); } - private static String convertLeafExpression(PsiExpression condition) { - if (BoolUtils.isNegation(condition)) { - final PsiExpression negated = BoolUtils.getNegated(condition); - if (negated == null) { + private static String convertLeafExpression(PsiExpression expression, boolean tokenTypeAndAnd) { + if (BoolUtils.isNegation(expression)) { + final PsiExpression negatedExpression = BoolUtils.getNegated(expression); + if (negatedExpression == null) { return ""; } - if (ParenthesesUtils.getPrecedence(negated) > - ParenthesesUtils.OR_PRECEDENCE) { - return '(' + negated.getText() + ')'; + if (tokenTypeAndAnd) { + if (ParenthesesUtils.getPrecedence(negatedExpression) > ParenthesesUtils.OR_PRECEDENCE) { + return '(' + negatedExpression.getText() + ')'; + } + } else if (ParenthesesUtils.getPrecedence(negatedExpression) > ParenthesesUtils.AND_PRECEDENCE) { + return '(' + negatedExpression.getText() + ')'; } - final PsiElement conditionParent = condition.getParent(); - if (conditionParent instanceof PsiExpression && - ParenthesesUtils.getPrecedence(negated) > ParenthesesUtils.AND_PRECEDENCE && - ParenthesesUtils.getPrecedence((PsiExpression)conditionParent) > ParenthesesUtils.AND_PRECEDENCE) { - return '(' + negated.getText() + ')'; - } - return negated.getText(); + return negatedExpression.getText(); } - else if (ComparisonUtils.isComparison(condition)) { - final PsiBinaryExpression binaryExpression = - (PsiBinaryExpression)condition; - final String negatedComparison = - ComparisonUtils.getNegatedComparison(binaryExpression.getOperationTokenType()); + else if (ComparisonUtils.isComparison(expression)) { + final PsiBinaryExpression binaryExpression = (PsiBinaryExpression)expression; + final String negatedComparison = ComparisonUtils.getNegatedComparison(binaryExpression.getOperationTokenType()); final PsiExpression lhs = binaryExpression.getLOperand(); final PsiExpression rhs = binaryExpression.getROperand(); assert rhs != null; return lhs.getText() + negatedComparison + rhs.getText(); } - else if (ParenthesesUtils.getPrecedence(condition) > - ParenthesesUtils.PREFIX_PRECEDENCE) { - return "!(" + condition.getText() + ')'; + else if (ParenthesesUtils.getPrecedence(expression) > ParenthesesUtils.PREFIX_PRECEDENCE) { + return "!(" + expression.getText() + ')'; } else { - return '!' + condition.getText(); + return '!' + expression.getText(); } } - - private static boolean isConjunctionExpression(PsiElement exp, - IElementType conjunctionType) { - if (!(exp instanceof PsiPolyadicExpression)) { - return false; - } - final PsiPolyadicExpression binExp = (PsiPolyadicExpression)exp; - final IElementType tokenType = binExp.getOperationTokenType(); - return tokenType.equals(conjunctionType); - } } diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/psiutils/BoolUtils.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/psiutils/BoolUtils.java index 5055617d5484..b809c1631b5a 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/psiutils/BoolUtils.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/psiutils/BoolUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2011 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2012 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. @@ -22,17 +22,17 @@ import org.jetbrains.annotations.Nullable; public class BoolUtils { - private BoolUtils() { - } + private BoolUtils() {} public static boolean isNegated(PsiExpression exp) { PsiExpression ancestor = exp; - while (ancestor.getParent() instanceof PsiParenthesizedExpression) { - ancestor = (PsiExpression)ancestor.getParent(); + PsiElement parent = ancestor.getParent(); + while (parent instanceof PsiParenthesizedExpression) { + ancestor = (PsiExpression)parent; + parent = ancestor.getParent(); } - if (ancestor.getParent() instanceof PsiPrefixExpression) { - final PsiPrefixExpression prefixAncestor = - (PsiPrefixExpression)ancestor.getParent(); + if (parent instanceof PsiPrefixExpression) { + final PsiPrefixExpression prefixAncestor = (PsiPrefixExpression)parent; final IElementType tokenType = prefixAncestor.getOperationTokenType(); if (tokenType.equals(JavaTokenType.EXCL)) { return true; @@ -42,14 +42,15 @@ public class BoolUtils { } @Nullable - public static PsiExpression findNegation(PsiExpression exp) { - PsiExpression ancestor = exp; - while (ancestor.getParent() instanceof PsiParenthesizedExpression) { - ancestor = (PsiExpression)ancestor.getParent(); + public static PsiExpression findNegation(PsiExpression expression) { + PsiExpression ancestor = expression; + PsiElement parent = ancestor.getParent(); + while (parent instanceof PsiParenthesizedExpression) { + ancestor = (PsiExpression)parent; + parent = ancestor.getParent(); } - if (ancestor.getParent() instanceof PsiPrefixExpression) { - final PsiPrefixExpression prefixAncestor = - (PsiPrefixExpression)ancestor.getParent(); + if (parent instanceof PsiPrefixExpression) { + final PsiPrefixExpression prefixAncestor = (PsiPrefixExpression)parent; if (JavaTokenType.EXCL.equals(prefixAncestor.getOperationTokenType())) { return prefixAncestor; } @@ -67,12 +68,16 @@ public class BoolUtils { } @Nullable - public static PsiExpression getNegated(PsiExpression exp) { - final PsiPrefixExpression prefixExp = (PsiPrefixExpression)exp; - final PsiExpression operand = prefixExp.getOperand(); - if (operand == null) { + public static PsiExpression getNegated(PsiExpression expression) { + if (!(expression instanceof PsiPrefixExpression)) { return null; } + final PsiPrefixExpression prefixExpression = (PsiPrefixExpression)expression; + final IElementType tokenType = prefixExpression.getOperationTokenType(); + if (!JavaTokenType.EXCL.equals(tokenType)) { + return null; + } + final PsiExpression operand = prefixExpression.getOperand(); return ParenthesesUtils.stripParentheses(operand); } @@ -80,23 +85,18 @@ public class BoolUtils { if (!(expression instanceof PsiLiteralExpression)) { return false; } - final PsiLiteralExpression literalExpression = - (PsiLiteralExpression)expression; + final PsiLiteralExpression literalExpression = (PsiLiteralExpression)expression; @NonNls final String text = literalExpression.getText(); - return PsiKeyword.TRUE.equals(text) || - PsiKeyword.FALSE.equals(text); + return PsiKeyword.TRUE.equals(text) || PsiKeyword.FALSE.equals(text); } - public static String getNegatedExpressionText( - @Nullable PsiExpression condition) { + public static String getNegatedExpressionText(@Nullable PsiExpression condition) { if (condition == null) { return ""; } if (condition instanceof PsiParenthesizedExpression) { - final PsiParenthesizedExpression parenthesizedExpression = - (PsiParenthesizedExpression)condition; - final PsiExpression expression = - parenthesizedExpression.getExpression(); + final PsiParenthesizedExpression parenthesizedExpression = (PsiParenthesizedExpression)condition; + final PsiExpression expression = parenthesizedExpression.getExpression(); return '(' + getNegatedExpressionText(expression) + ')'; } else if (isNegation(condition)) { @@ -107,10 +107,8 @@ public class BoolUtils { return negated.getText(); } else if (ComparisonUtils.isComparison(condition)) { - final PsiBinaryExpression binaryExpression = - (PsiBinaryExpression)condition; - final String negatedComparison = - ComparisonUtils.getNegatedComparison(binaryExpression.getOperationTokenType()); + final PsiBinaryExpression binaryExpression = (PsiBinaryExpression)condition; + final String negatedComparison = ComparisonUtils.getNegatedComparison(binaryExpression.getOperationTokenType()); final PsiExpression lhs = binaryExpression.getLOperand(); final PsiExpression rhs = binaryExpression.getROperand(); if (rhs == null) { @@ -118,8 +116,7 @@ public class BoolUtils { } return lhs.getText() + negatedComparison + rhs.getText(); } - else if (ParenthesesUtils.getPrecedence(condition) > - ParenthesesUtils.PREFIX_PRECEDENCE) { + else if (ParenthesesUtils.getPrecedence(condition) > ParenthesesUtils.PREFIX_PRECEDENCE) { return "!(" + condition.getText() + ')'; } else { diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/bool/demorgans/NeedsMoreParentheses.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/bool/demorgans/NeedsMoreParentheses.java new file mode 100644 index 000000000000..88cec09a3801 --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/bool/demorgans/NeedsMoreParentheses.java @@ -0,0 +1,7 @@ +package com.siyeh.ipp.bool.demorgans; + +class NeedsMoreParentheses { + void foo(boolean a, boolean b, boolean c, boolean d) { + boolean f = !(!(a || b) || !(c || d)); + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/bool/demorgans/NeedsMoreParentheses_after.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/bool/demorgans/NeedsMoreParentheses_after.java new file mode 100644 index 000000000000..d48dc9019fa3 --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/bool/demorgans/NeedsMoreParentheses_after.java @@ -0,0 +1,7 @@ +package com.siyeh.ipp.bool.demorgans; + +class NeedsMoreParentheses { + void foo(boolean a, boolean b, boolean c, boolean d) { + boolean f = (a || b) && (c || d); + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/bool/demorgans/NeedsParentheses.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/bool/demorgans/NeedsParentheses.java new file mode 100644 index 000000000000..77a460100285 --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/bool/demorgans/NeedsParentheses.java @@ -0,0 +1,8 @@ +package com.siyeh.ipp.bool.demorgans; + +class NeedsParentheses { + + void foo(boolean a, boolean b) { + if (!(!a || !b) || !(a || b)){} + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/bool/demorgans/NeedsParentheses_after.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/bool/demorgans/NeedsParentheses_after.java new file mode 100644 index 000000000000..b01a30ba97ef --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/bool/demorgans/NeedsParentheses_after.java @@ -0,0 +1,8 @@ +package com.siyeh.ipp.bool.demorgans; + +class NeedsParentheses { + + void foo(boolean a, boolean b) { + if (!((!a || !b) && (a || b))){} + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/bool/demorgans/NotTooManyParentheses.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/bool/demorgans/NotTooManyParentheses.java new file mode 100644 index 000000000000..6880190dde7c --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/bool/demorgans/NotTooManyParentheses.java @@ -0,0 +1,7 @@ +package com.siyeh.ipp.bool.demorgans; + +class NotTooManyParentheses { + void foo(boolean a, boolean b, boolean c) { + if (a && (b || c)) {} + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/bool/demorgans/NotTooManyParentheses_after.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/bool/demorgans/NotTooManyParentheses_after.java new file mode 100644 index 000000000000..a8604ee895bf --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/bool/demorgans/NotTooManyParentheses_after.java @@ -0,0 +1,7 @@ +package com.siyeh.ipp.bool.demorgans; + +class NotTooManyParentheses { + void foo(boolean a, boolean b, boolean c) { + if (a && !(!b && !c)) {} + } +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/bool/DemorgansIntentionTest.java b/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/bool/DemorgansIntentionTest.java new file mode 100644 index 000000000000..3f432f412723 --- /dev/null +++ b/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/bool/DemorgansIntentionTest.java @@ -0,0 +1,35 @@ +/* + * Copyright 2000-2012 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.ipp.bool; + +import com.siyeh.IntentionPowerPackBundle; +import com.siyeh.ipp.IPPTestCase; + +public class DemorgansIntentionTest extends IPPTestCase { + public void testNeedsParentheses() { doTest(); } + public void testNeedsMoreParentheses() { doTest(); } + public void testNotTooManyParentheses() { doTest(); } + + @Override + protected String getIntentionName() { + return IntentionPowerPackBundle.message("demorgans.intention.name2"); + } + + @Override + protected String getRelativePath() { + return "bool/demorgans"; + } +} From c2dcc5229dc7c31e4afd60c0f14770a247e2ab7e Mon Sep 17 00:00:00 2001 From: "Denis.Zhdanov" Date: Tue, 31 Jan 2012 20:02:43 +0400 Subject: [PATCH 08/23] IDEA-76142: Gradle support - cannot update IDEA projects once one of build.gradle files changes Added 'navigate to the linked gradle script' action --- .../gradle/action/GradleOpenScriptAction.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/action/GradleOpenScriptAction.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/action/GradleOpenScriptAction.java index c6922d692430..f010e59ebde8 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/action/GradleOpenScriptAction.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/action/GradleOpenScriptAction.java @@ -3,8 +3,13 @@ package org.jetbrains.plugins.gradle.action; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.Presentation; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.fileEditor.FileEditorManager; +import com.intellij.openapi.fileEditor.OpenFileDescriptor; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.gradle.config.GradleSettings; import org.jetbrains.plugins.gradle.util.GradleBundle; @@ -19,6 +24,8 @@ import org.jetbrains.plugins.gradle.util.GradleBundle; */ public class GradleOpenScriptAction extends AbstractGradleLinkedProjectAction implements DumbAware { + private static final Logger LOG = Logger.getInstance("#" + GradleOpenScriptAction.class.getName()); + public GradleOpenScriptAction() { getTemplatePresentation().setText(GradleBundle.message("gradle.action.open.script.text")); getTemplatePresentation().setDescription(GradleBundle.message("gradle.action.open.script.description")); @@ -30,6 +37,12 @@ public class GradleOpenScriptAction extends AbstractGradleLinkedProjectAction im @Override protected void doActionPerformed(@NotNull Project project, @NotNull String linkedProjectPath) { - // TODO den implement + final VirtualFile virtualFile = LocalFileSystem.getInstance().findFileByPath(linkedProjectPath); + if (virtualFile == null) { + LOG.warn(String.format("Can't obtain virtual file for the target file path: '%s'", linkedProjectPath)); + return; + } + OpenFileDescriptor descriptor = new OpenFileDescriptor(project, virtualFile); + FileEditorManager.getInstance(project).openTextEditor(descriptor, true); } } From 80ccdf22bab910c30af8266f18d1e52c9d311b99 Mon Sep 17 00:00:00 2001 From: Konstantin Bulenkov Date: Tue, 31 Jan 2012 17:19:58 +0100 Subject: [PATCH 09/23] anonymous tests --- .../fileStructure/selection/Anonymous.java | 8 ++++++ .../fileStructure/selection/Anonymous.tree | 8 ++++++ .../selection/AnonymousInAnonymous.java | 15 ++++++++++ .../selection/AnonymousInAnonymous.tree | 10 +++++++ java/java-tests/testSrc/Anonymous.java | 10 +++++++ .../JavaFileStructureSelectionTest.java | 10 ++++--- .../JavaFileStructureTestCase.java | 8 ++++++ .../intellij/ide/util/FileStructurePopup.java | 16 +++++++++-- .../testFramework/FileStructureTestBase.java | 11 ++++++-- .../testFramework/TestTreeUpdater.java | 28 +++++++++++++++++++ 10 files changed, 115 insertions(+), 9 deletions(-) create mode 100644 java/java-tests/testData/fileStructure/selection/Anonymous.java create mode 100644 java/java-tests/testData/fileStructure/selection/Anonymous.tree create mode 100644 java/java-tests/testData/fileStructure/selection/AnonymousInAnonymous.java create mode 100644 java/java-tests/testData/fileStructure/selection/AnonymousInAnonymous.tree create mode 100644 java/java-tests/testSrc/Anonymous.java create mode 100644 platform/testFramework/src/com/intellij/testFramework/TestTreeUpdater.java diff --git a/java/java-tests/testData/fileStructure/selection/Anonymous.java b/java/java-tests/testData/fileStructure/selection/Anonymous.java new file mode 100644 index 000000000000..7504bf0c973b --- /dev/null +++ b/java/java-tests/testData/fileStructure/selection/Anonymous.java @@ -0,0 +1,8 @@ +class Anonymous { + int num1; + int num2; + Object o = new Object(){}; + + Anonymous() {} + void foo() {} +} \ No newline at end of file diff --git a/java/java-tests/testData/fileStructure/selection/Anonymous.tree b/java/java-tests/testData/fileStructure/selection/Anonymous.tree new file mode 100644 index 000000000000..94920b4c0554 --- /dev/null +++ b/java/java-tests/testData/fileStructure/selection/Anonymous.tree @@ -0,0 +1,8 @@ +-Anonymous.java + -Anonymous + Anonymous() + foo():void + num1:int + num2:int + -[o:Object = new Object() {...}] + $1 \ No newline at end of file diff --git a/java/java-tests/testData/fileStructure/selection/AnonymousInAnonymous.java b/java/java-tests/testData/fileStructure/selection/AnonymousInAnonymous.java new file mode 100644 index 000000000000..5bfeff7b4eda --- /dev/null +++ b/java/java-tests/testData/fileStructure/selection/AnonymousInAnonymous.java @@ -0,0 +1,15 @@ +class AnonymousInAnonymous { + int num1; + int num2; + + AnonymousInAnonymous() {} + void foo() { + new Object() { + public String toString() { + return new Object(){ + void method() {} + }.toString(); + } + }; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/fileStructure/selection/AnonymousInAnonymous.tree b/java/java-tests/testData/fileStructure/selection/AnonymousInAnonymous.tree new file mode 100644 index 000000000000..1f8848854d70 --- /dev/null +++ b/java/java-tests/testData/fileStructure/selection/AnonymousInAnonymous.tree @@ -0,0 +1,10 @@ +-AnonymousInAnonymous.java + -AnonymousInAnonymous + AnonymousInAnonymous() + -foo():void + -$1 + -toString():String + -Anonymous + [method():void] + num1:int + num2:int \ No newline at end of file diff --git a/java/java-tests/testSrc/Anonymous.java b/java/java-tests/testSrc/Anonymous.java new file mode 100644 index 000000000000..e72d2fb65789 --- /dev/null +++ b/java/java-tests/testSrc/Anonymous.java @@ -0,0 +1,10 @@ +class Anonymous { + int num1; + int num2; + Object o = new Object(){ + int num = 1; + }; + + Anonymous() {} + void foo() {} +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/ide/fileStructure/JavaFileStructureSelectionTest.java b/java/java-tests/testSrc/com/intellij/ide/fileStructure/JavaFileStructureSelectionTest.java index 84e971463a84..fbea9c3943bf 100644 --- a/java/java-tests/testSrc/com/intellij/ide/fileStructure/JavaFileStructureSelectionTest.java +++ b/java/java-tests/testSrc/com/intellij/ide/fileStructure/JavaFileStructureSelectionTest.java @@ -24,8 +24,10 @@ public class JavaFileStructureSelectionTest extends JavaFileStructureTestCase { return "selection"; } - public void testField() throws Exception {checkTree();} - public void testMethod() throws Exception {checkTree();} - public void testConstructor() throws Exception {checkTree();} - public void testInsideClass() throws Exception {checkTree();} + public void testField() throws Exception {checkTree();} + public void testMethod() throws Exception {checkTree();} + public void testConstructor() throws Exception {checkTree();} + public void testInsideClass() throws Exception {checkTree();} + public void testAnonymous() throws Exception {checkTree();} + public void testAnonymousInAnonymous() throws Exception {checkTree();} } diff --git a/java/java-tests/testSrc/com/intellij/ide/fileStructure/JavaFileStructureTestCase.java b/java/java-tests/testSrc/com/intellij/ide/fileStructure/JavaFileStructureTestCase.java index 0753206117da..c3559b0370f3 100644 --- a/java/java-tests/testSrc/com/intellij/ide/fileStructure/JavaFileStructureTestCase.java +++ b/java/java-tests/testSrc/com/intellij/ide/fileStructure/JavaFileStructureTestCase.java @@ -33,6 +33,9 @@ public abstract class JavaFileStructureTestCase extends FileStructureTestBase { public void setUp() throws Exception { super.setUp(); myShowAnonymousByDefault = PropertiesComponent.getInstance().getBoolean(getAnonymousPropertyName(), false); + if (getTestName(false).contains("Anonymous")) { + setShowAnonymous(true); + } } @Override @@ -40,6 +43,11 @@ public abstract class JavaFileStructureTestCase extends FileStructureTestBase { return "java"; } + public void setShowAnonymous(boolean show) throws Exception { + myPopup.setTreeActionState(JavaAnonymousClassesNodeProvider.class, show); + update(); + } + @Override public void tearDown() throws Exception { PropertiesComponent.getInstance().setValue(getAnonymousPropertyName(), Boolean.toString(myShowAnonymousByDefault)); diff --git a/platform/lang-impl/src/com/intellij/ide/util/FileStructurePopup.java b/platform/lang-impl/src/com/intellij/ide/util/FileStructurePopup.java index 39dbe47a25aa..bc270a07174d 100644 --- a/platform/lang-impl/src/com/intellij/ide/util/FileStructurePopup.java +++ b/platform/lang-impl/src/com/intellij/ide/util/FileStructurePopup.java @@ -107,6 +107,7 @@ public class FileStructurePopup implements Disposable { private int myPreferredWidth; private final FilteringTreeStructure myFilteringStructure; private PsiElement myInitialPsiElement; + private Map myCheckBoxes = new HashMap(); public FileStructurePopup(StructureViewModel structureViewModel, @Nullable Editor editor, @@ -128,7 +129,7 @@ public class FileStructurePopup implements Disposable { myTreeStructure = new SmartTreeStructure(project, myTreeModel){ public void rebuildTree() { - if (!myPopup.isDisposed()) { + if (ApplicationManager.getApplication().isUnitTestMode() || !myPopup.isDisposed()) { super.rebuildTree(); } } @@ -503,7 +504,7 @@ public class FileStructurePopup implements Disposable { return null; } - protected JComponent createCenterPanel() { + public JComponent createCenterPanel() { List fileStructureFilters = new ArrayList(); List fileStructureNodeProviders = new ArrayList(); if (myTreeActionsOwner != null) { @@ -704,6 +705,7 @@ public class FileStructurePopup implements Disposable { } chkFilter.setText(text); panel.add(chkFilter); + myCheckBoxes.put(action.getClass(), chkFilter); } private static boolean getDefaultValue(TreeAction action) { @@ -742,6 +744,16 @@ public class FileStructurePopup implements Disposable { return myAbstractTreeBuilder; } + public void setTreeActionState(Class action, boolean state) { + final JCheckBox checkBox = myCheckBoxes.get(action); + if (checkBox != null) { + checkBox.setSelected(state); + for (ActionListener listener : checkBox.getActionListeners()) { + listener.actionPerformed(new ActionEvent(this, 1, "")); + } + } + } + private class FileStructurePopupFilter implements ElementFilter { private String myLastFilter = null; private HashSet myVisibleParents = new HashSet(); diff --git a/platform/testFramework/src/com/intellij/testFramework/FileStructureTestBase.java b/platform/testFramework/src/com/intellij/testFramework/FileStructureTestBase.java index 645d6ae0192e..99c205834e94 100644 --- a/platform/testFramework/src/com/intellij/testFramework/FileStructureTestBase.java +++ b/platform/testFramework/src/com/intellij/testFramework/FileStructureTestBase.java @@ -36,7 +36,7 @@ import java.io.File; * @author Konstantin Bulenkov */ public abstract class FileStructureTestBase extends CodeInsightFixtureTestCase { - FileStructurePopup myPopup; + protected FileStructurePopup myPopup; @Before public void setUp() throws Exception { @@ -46,6 +46,9 @@ public abstract class FileStructureTestBase extends CodeInsightFixtureTestCase { myFixture.getProject(), null, TextEditorProvider.getInstance().getTextEditor(myFixture.getEditor())); + assert myPopup != null; + myPopup.createCenterPanel(); + getBuilder().getUi().getUpdater().setPassThroughMode(true); update(); } @@ -71,15 +74,17 @@ public abstract class FileStructureTestBase extends CodeInsightFixtureTestCase { } - private void update() throws InterruptedException { + public void update() throws InterruptedException { myPopup.getTreeBuilder().refilter().doWhenProcessed(new Runnable() { @Override public void run() { + getStructure().rebuild(); updateTree(); + getBuilder().updateFromRoot(); TreeUtil.expandAll(getTree()); final FilteringTreeStructure.FilteringNode node = myPopup.selectPsiElement(myPopup.getCurrentElement(getFile())); - getTree().getSelectionModel().setSelectionPath(getTree().getPath(node)); + getBuilder().getUi().select(node, null); } }); } diff --git a/platform/testFramework/src/com/intellij/testFramework/TestTreeUpdater.java b/platform/testFramework/src/com/intellij/testFramework/TestTreeUpdater.java new file mode 100644 index 000000000000..3dfec720075b --- /dev/null +++ b/platform/testFramework/src/com/intellij/testFramework/TestTreeUpdater.java @@ -0,0 +1,28 @@ +/* + * Copyright 2000-2012 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.intellij.testFramework; + +import com.intellij.ide.util.treeView.AbstractTreeBuilder; +import com.intellij.ide.util.treeView.AbstractTreeUpdater; + +/** + * @author Konstantin Bulenkov + */ +public class TestTreeUpdater extends AbstractTreeUpdater { + public TestTreeUpdater(AbstractTreeBuilder treeBuilder) { + super(treeBuilder); + } +} From 957718c0b6a8721dd305eedb7d02bbff1fdb44f1 Mon Sep 17 00:00:00 2001 From: Maxim Shafirov Date: Tue, 31 Jan 2012 20:50:14 +0400 Subject: [PATCH 10/23] Cache core jar's vfs children/parent. It'd better be done in normal jar vfs though --- .../openapi/vfs/impl/jar/CoreJarVirtualFile.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/platform/core-impl/src/com/intellij/openapi/vfs/impl/jar/CoreJarVirtualFile.java b/platform/core-impl/src/com/intellij/openapi/vfs/impl/jar/CoreJarVirtualFile.java index c524c6e09926..c2969d9edc09 100644 --- a/platform/core-impl/src/com/intellij/openapi/vfs/impl/jar/CoreJarVirtualFile.java +++ b/platform/core-impl/src/com/intellij/openapi/vfs/impl/jar/CoreJarVirtualFile.java @@ -32,11 +32,14 @@ public class CoreJarVirtualFile extends VirtualFile { private final CoreJarFileSystem myFileSystem; private final CoreJarHandler myHandler; private final String myPathInJar; + private final VirtualFile myParent; + private VirtualFile[] myChildren; public CoreJarVirtualFile(CoreJarFileSystem fileSystem, CoreJarHandler handler, String pathInJar) { myFileSystem = fileSystem; myHandler = handler; myPathInJar = pathInJar; + myParent = calcParent(); } @NotNull @@ -77,6 +80,10 @@ public class CoreJarVirtualFile extends VirtualFile { @Override public VirtualFile getParent() { + return myParent; + } + + private VirtualFile calcParent() { if (myPathInJar.length() == 0) { return null; } @@ -89,6 +96,15 @@ public class CoreJarVirtualFile extends VirtualFile { @Override public VirtualFile[] getChildren() { + VirtualFile[] answer = myChildren; + if (answer == null) { + answer = calcChildren(); + myChildren = answer; + } + return answer; + } + + private VirtualFile[] calcChildren() { List result = new ArrayList(); final String[] children = myHandler.list(this); for (String child : children) { From a1ff4d0fd3210f23f0733d4acf68822fb89f5cb3 Mon Sep 17 00:00:00 2001 From: Eugene Zhuravlev Date: Tue, 31 Jan 2012 18:09:15 +0100 Subject: [PATCH 11/23] do not run automake in alarm's thread --- .../intellij/compiler/CompileServerManager.java | 17 +++++++++++------ .../intellij/compiler/impl/CompilerUtil.java | 3 ++- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/java/compiler/impl/src/com/intellij/compiler/CompileServerManager.java b/java/compiler/impl/src/com/intellij/compiler/CompileServerManager.java index 05aaeb88f46d..d2862e6289ea 100644 --- a/java/compiler/impl/src/com/intellij/compiler/CompileServerManager.java +++ b/java/compiler/impl/src/com/intellij/compiler/CompileServerManager.java @@ -128,12 +128,17 @@ public class CompileServerManager implements ApplicationComponent{ @Override public void run() { if (!myAutoMakeInProgress.getAndSet(true)) { - try { - runAutoMake(); - } - finally { - myAutoMakeInProgress.set(false); - } + ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { + @Override + public void run() { + try { + runAutoMake(); + } + finally { + myAutoMakeInProgress.set(false); + } + } + }); } else { scheduleMake(this); diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/CompilerUtil.java b/java/compiler/impl/src/com/intellij/compiler/impl/CompilerUtil.java index a7637185dc21..066baa7ea538 100644 --- a/java/compiler/impl/src/com/intellij/compiler/impl/CompilerUtil.java +++ b/java/compiler/impl/src/com/intellij/compiler/impl/CompilerUtil.java @@ -26,6 +26,7 @@ import com.intellij.openapi.compiler.CompilerBundle; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.Module; import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.CharsetToolkit; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VfsUtil; @@ -155,7 +156,7 @@ public class CompilerUtil { public static void addSourceCommandLineSwitch(final Sdk jdk, LanguageLevel chunkLanguageLevel, @NonNls final List commandLine) { final String versionString = jdk.getVersionString(); - if (versionString == null || "".equals(versionString)) { + if (StringUtil.isEmpty(versionString)) { throw new IllegalArgumentException(CompilerBundle.message("javac.error.unknown.jdk.version", jdk.getName())); } From 326c4caadabf861aab04493952f79ab105a9b0f1 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Tue, 31 Jan 2012 21:20:52 +0400 Subject: [PATCH 12/23] override/implement: skip parameter annotations if necessary (IDEA-76470) --- .../generation/GenerateMembersUtil.java | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInsight/generation/GenerateMembersUtil.java b/java/java-impl/src/com/intellij/codeInsight/generation/GenerateMembersUtil.java index 15725edd723d..ec4b6f30f89e 100644 --- a/java/java-impl/src/com/intellij/codeInsight/generation/GenerateMembersUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/generation/GenerateMembersUtil.java @@ -21,6 +21,7 @@ import com.intellij.lang.StdLanguages; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.ScrollType; +import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; @@ -40,9 +41,7 @@ import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Collections; -import java.util.List; -import java.util.Map; +import java.util.*; public class GenerateMembersUtil { private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.generation.GenerateMembersUtil"); @@ -279,7 +278,9 @@ public class GenerateMembersUtil { PsiParameter newParameter = factory.createParameter(paramName, substituted); if (parameter.getLanguage() == StdLanguages.JAVA) { - newParameter.getModifierList().replace(parameter.getModifierList()); + PsiModifierList modifierList = newParameter.getModifierList(); + modifierList = (PsiModifierList)modifierList.replace(parameter.getModifierList()); + processAnnotations(project, modifierList); } newMethod.getParameterList().add(newParameter); } @@ -312,6 +313,23 @@ public class GenerateMembersUtil { } } + private static void processAnnotations(Project project, PsiModifierList modifierList) { + final Set toRemove = new HashSet(); + for (PsiAnnotation annotation : modifierList.getAnnotations()) { + final String qualifiedName = annotation.getQualifiedName(); + for (OverrideImplementsAnnotationsHandler handler : Extensions.getExtensions(OverrideImplementsAnnotationsHandler.EP_NAME)) { + final String[] annotations2Remove = handler.annotationsToRemove(project, qualifiedName); + Collections.addAll(toRemove, annotations2Remove); + } + } + for (String fqn : toRemove) { + final PsiAnnotation psiAnnotation = modifierList.findAnnotation(fqn); + if (psiAnnotation != null) { + psiAnnotation.delete(); + } + } + } + private static PsiType substituteType(final PsiSubstitutor substitutor, final PsiType type) { final PsiType psiType = substitutor.substitute(type); if (psiType != null) return psiType; From 3ba0a4231d88aa6201262f53e37181f98d2e1785 Mon Sep 17 00:00:00 2001 From: peter Date: Mon, 30 Jan 2012 18:01:36 +0100 Subject: [PATCH 13/23] don't error-highlight groovy builder members as unresolved --- .../GroovyUnresolvedAccessInspection.java | 71 ++++++++++++++++++- .../groovy/lang/GroovyHighlightingTest.groovy | 4 ++ .../BuilderMembersAreNotUnresolved.groovy | 22 ++++++ 3 files changed, 94 insertions(+), 3 deletions(-) create mode 100644 plugins/groovy/testdata/highlighting/BuilderMembersAreNotUnresolved.groovy diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/untypedUnresolvedAccess/GroovyUnresolvedAccessInspection.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/untypedUnresolvedAccess/GroovyUnresolvedAccessInspection.java index 91260b7fc7ea..9b661072510b 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/untypedUnresolvedAccess/GroovyUnresolvedAccessInspection.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/untypedUnresolvedAccess/GroovyUnresolvedAccessInspection.java @@ -16,19 +16,24 @@ package org.jetbrains.plugins.groovy.codeInspection.untypedUnresolvedAccess; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiPackage; +import com.intellij.psi.*; +import com.intellij.util.containers.CollectionFactory; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.groovy.annotator.GroovyAnnotator; import org.jetbrains.plugins.groovy.codeInspection.BaseInspection; import org.jetbrains.plugins.groovy.codeInspection.BaseInspectionVisitor; -import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; +import org.jetbrains.plugins.groovy.gpp.GppTypeConverter; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrCall; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression; +import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames; +import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil; +import java.util.Iterator; +import java.util.List; + import static org.jetbrains.plugins.groovy.annotator.GroovyAnnotator.isDeclarationAssignment; /** @@ -72,10 +77,70 @@ public class GroovyUnresolvedAccessInspection extends BaseInspection { if (!(parent instanceof GrCall) && ResolveUtil.isKeyOfMap(refExpr)) return; // It's a key of map. if (!GroovyAnnotator.shouldHighlightAsUnresolved(refExpr)) return; + + if (qualifier != null && isBuilderInvocation(refExpr)) return; PsiElement refNameElement = refExpr.getReferenceNameElement(); registerError(refNameElement == null ? refExpr : refNameElement); } } + private static boolean isBuilderInvocation(@NotNull GrReferenceExpression refExpr) { + GrExpression qualifier = refExpr.getQualifier(); + PsiType type = qualifier == null ? null : qualifier.getType(); + if (type instanceof PsiClassType) { + PsiClass target = ((PsiClassType)type).resolve(); + if (target != null) { + for (PsiMethod method : findBuilderMetaMethods(refExpr, target)) { + PsiClass containingClass = method.getContainingClass(); + if (containingClass != null && + method.getParameterList().getParameters()[0].getType().equalsToText(CommonClassNames.JAVA_LANG_STRING)) { + String qname = containingClass.getQualifiedName(); + if (!GroovyCommonClassNames.GROOVY_OBJECT.equals(qname) && !GroovyCommonClassNames.GROOVY_OBJECT_SUPPORT.equals(qname)) { + return true; + } + } + } + } + } + + return false; + } + + private static List findBuilderMetaMethods(GrReferenceExpression refExpr, PsiClass target) { + boolean gpp = GppTypeConverter.hasTypedContext(target) && GppTypeConverter.hasTypedContext(refExpr); + if (refExpr.getParent() instanceof GrCall) { + List toSearch = + CollectionFactory.arrayList(target.findMethodsByName(gpp ? "invokeUnresolvedMethod" : "invokeMethod", true)); + for (Iterator iterator = toSearch.iterator(); iterator.hasNext(); ) { + PsiMethod method = iterator.next(); + if (!gpp && + (method.getParameterList().getParametersCount() != 2 || method.getParameterList().getParameters()[1].getType() + .equalsToText(CommonClassNames.JAVA_LANG_OBJECT + "[]"))) { + iterator.remove(); + } + } + return toSearch; + } + + if (PsiUtil.isLValue(refExpr)) { + List toSearch = CollectionFactory.arrayList(target.findMethodsByName(gpp ? "setUnresolvedProperty" : "setProperty", true)); + for (Iterator iterator = toSearch.iterator(); iterator.hasNext(); ) { + PsiMethod method = iterator.next(); + if (method.getParameterList().getParametersCount() != 2 || (!gpp && !method.getParameterList().getParameters()[1].getType() + .equalsToText(CommonClassNames.JAVA_LANG_OBJECT))) { + iterator.remove(); + } + } + return toSearch; + } + + List toSearch = CollectionFactory.arrayList(target.findMethodsByName(gpp ? "getUnresolvedProperty" : "getProperty", true)); + for (Iterator iterator = toSearch.iterator(); iterator.hasNext(); ) { + if (iterator.next().getParameterList().getParametersCount() != 1) { + iterator.remove(); + } + } + return toSearch; + } } diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.groovy index 76bf926f58e9..e72b91eb44d6 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.groovy @@ -452,6 +452,10 @@ public class GroovyHighlightingTest extends LightCodeInsightFixtureTestCase { doTest(new GroovyAssignabilityCheckInspection(), new GroovyUnresolvedAccessInspection()); } + public void testBuilderMembersAreNotUnresolved() throws Exception { + doTest(new GroovyUnresolvedAccessInspection()); + } + public void testUnknownVarInArgList() { doTest(new GroovyAssignabilityCheckInspection()); } diff --git a/plugins/groovy/testdata/highlighting/BuilderMembersAreNotUnresolved.groovy b/plugins/groovy/testdata/highlighting/BuilderMembersAreNotUnresolved.groovy new file mode 100644 index 000000000000..300b84e61fce --- /dev/null +++ b/plugins/groovy/testdata/highlighting/BuilderMembersAreNotUnresolved.groovy @@ -0,0 +1,22 @@ +class MyBuilder { + @Override + Object getProperty(String property) { + return super.getProperty(property) + } + + @Override + Object invokeMethod(String name, Object args) { + return super.invokeMethod(name, args) + } + + @Override + void setProperty(String property, Object newValue) { + super.setProperty(property, newValue) + } +} + +def b = new MyBuilder() +println b.foo +println new Object().foo +b.foo = 2 +b.bar() \ No newline at end of file From 2db14dd95b674a582a1bf9f8d64728d9de278fe8 Mon Sep 17 00:00:00 2001 From: peter Date: Mon, 30 Jan 2012 19:24:59 +0100 Subject: [PATCH 14/23] highlight globally unused groovy classes (IDEA-75803) --- .../daemon/impl/PostHighlightingPass.java | 45 +++++++------------ plugins/groovy/src/META-INF/plugin.xml | 4 ++ .../GroovyUnusedDeclarationInspection.java | 42 +++++++++++++++++ ...s.java => GroovyPostHighlightingPass.java} | 39 +++++++++++++--- .../local/GroovyUnusedImportsPassFactory.java | 2 +- 5 files changed, 95 insertions(+), 37 deletions(-) create mode 100644 plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/GroovyUnusedDeclarationInspection.java rename plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/local/{GroovyUnusedImportPass.java => GroovyPostHighlightingPass.java} (80%) diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/PostHighlightingPass.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/PostHighlightingPass.java index 5a4292878996..48b7f0ee5b25 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/PostHighlightingPass.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/PostHighlightingPass.java @@ -378,7 +378,7 @@ public class PostHighlightingPass extends TextEditorHighlightingPass { return UnusedSymbolLocalInspection.isInjected(element); } - private static HighlightInfo createUnusedSymbolInfo(PsiElement element, String message, final HighlightInfoType highlightInfoType) { + public static HighlightInfo createUnusedSymbolInfo(PsiElement element, String message, final HighlightInfoType highlightInfoType) { HighlightInfo info = HighlightInfo.createHighlightInfo(highlightInfoType, element, message); UnusedDeclarationFixProvider[] fixProviders = Extensions.getExtensions(UnusedDeclarationFixProvider.EP_NAME); for (UnusedDeclarationFixProvider provider : fixProviders) { @@ -436,6 +436,9 @@ public class PostHighlightingPass extends TextEditorHighlightingPass { return null; } else if (!myRefCountHolder.isReferenced(field) && weAreSureThereAreNoUsages(field, progress)) { + if (field instanceof PsiEnumConstant && isEnumValuesMethodUsed(field, progress)) { + return null; + } return formatUnusedSymbolHighlightInfo("field.is.not.used", field, "fields", myDeadCodeKey, myDeadCodeInfoType); } return null; @@ -580,49 +583,31 @@ public class PostHighlightingPass extends TextEditorHighlightingPass { if (!myDeadCodeEnabled) return false; if (myDeadCodeInspection.isEntryPoint(member)) return false; - String name = member.getName(); + return isGloballyUnused(member, progress, myFile, member.getName()); + } + + public static boolean isGloballyUnused(PsiMember member, ProgressIndicator progress, @Nullable PsiFile fileToIgnoreOccurrencesIn, String name) { if (name == null) return false; SearchScope useScope = member.getUseScope(); if (!(useScope instanceof GlobalSearchScope)) return false; GlobalSearchScope scope = (GlobalSearchScope)useScope; // some classes may have references from within XML outside dependent modules, e.g. our actions - if (member instanceof PsiClass) scope = GlobalSearchScope.projectScope(myProject).uniteWith(scope); + Project project = member.getProject(); + if (member instanceof PsiClass) scope = GlobalSearchScope.projectScope(project).uniteWith(scope); - PsiSearchHelper.SearchCostResult cheapEnough = PsiSearchHelper.SERVICE.getInstance(myFile.getProject()) - .isCheapEnoughToSearch(name, scope, myFile, progress); + PsiSearchHelper.SearchCostResult cheapEnough = PsiSearchHelper.SERVICE.getInstance(project).isCheapEnoughToSearch(name, scope, fileToIgnoreOccurrencesIn, progress); if (cheapEnough == PsiSearchHelper.SearchCostResult.TOO_MANY_OCCURRENCES) return false; //search usages if it cheap //if count is 0 there is no usages since we've called myRefCountHolder.isReferenced() before if (cheapEnough == PsiSearchHelper.SearchCostResult.ZERO_OCCURRENCES) { - if (member instanceof PsiEnumConstant) { - return !isEnumValuesMethodUsed(member, progress); - } if (!canBeReferencedViaWeirdNames(member)) return true; } - FindUsagesManager findUsagesManager = ((FindManagerImpl)FindManager.getInstance(myProject)).getFindUsagesManager(); - FindUsagesOptions findUsagesOptions; - if (member instanceof PsiClass) { - findUsagesOptions = new JavaClassFindUsagesOptions(myProject); - } - else if (member instanceof PsiMethod) { - findUsagesOptions = new JavaMethodFindUsagesOptions(myProject); - } - else if (member instanceof PsiField) { - findUsagesOptions = new JavaVariableFindUsagesOptions(myProject); - } - else { - LOG.error("unknown member: " + member); - return false; - } + FindUsagesManager findUsagesManager = ((FindManagerImpl)FindManager.getInstance(project)).getFindUsagesManager(); + FindUsagesHandler handler = new JavaFindUsagesHandler(member, new JavaFindUsagesHandlerFactory(project)); + FindUsagesOptions findUsagesOptions = handler.getFindUsagesOptions(); findUsagesOptions.searchScope = scope; - - boolean used = findUsagesManager.isUsed(member, findUsagesOptions); - - if (!used && member instanceof PsiEnumConstant) { - return !isEnumValuesMethodUsed(member, progress); - } - return !used; + return !findUsagesManager.isUsed(member, findUsagesOptions); } private boolean isEnumValuesMethodUsed(PsiMember member, ProgressIndicator progress) { diff --git a/plugins/groovy/src/META-INF/plugin.xml b/plugins/groovy/src/META-INF/plugin.xml index 9a1384fa312a..63f176ae2810 100644 --- a/plugins/groovy/src/META-INF/plugin.xml +++ b/plugins/groovy/src/META-INF/plugin.xml @@ -382,6 +382,10 @@ + + diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/GroovyUnusedDeclarationInspection.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/GroovyUnusedDeclarationInspection.java new file mode 100644 index 000000000000..3b953f67d90c --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/GroovyUnusedDeclarationInspection.java @@ -0,0 +1,42 @@ +/* + * Copyright 2000-2012 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.plugins.groovy.codeInspection; + +import com.intellij.analysis.AnalysisScope; +import com.intellij.codeInspection.GlobalInspectionContext; +import com.intellij.codeInspection.InspectionManager; +import com.intellij.codeInspection.ex.DescriptorProviderInspection; +import com.intellij.codeInspection.ex.JobDescriptor; +import com.intellij.codeInspection.ex.UnfairLocalInspectionTool; +import org.jetbrains.annotations.NotNull; + +/** + * @author peter + */ +public class GroovyUnusedDeclarationInspection extends DescriptorProviderInspection implements UnfairLocalInspectionTool { + public static final String SHORT_NAME = "GroovyUnusedDeclaration"; + + @Override + public void runInspection(@NotNull AnalysisScope scope, @NotNull InspectionManager manager) { + } + + @NotNull + @Override + public JobDescriptor[] getJobDescriptors(GlobalInspectionContext globalInspectionContext) { + return JobDescriptor.EMPTY_ARRAY; + } + +} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/local/GroovyUnusedImportPass.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/local/GroovyPostHighlightingPass.java similarity index 80% rename from plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/local/GroovyUnusedImportPass.java rename to plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/local/GroovyPostHighlightingPass.java index 818f4badb3d3..d3f3b5f40330 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/local/GroovyUnusedImportPass.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/local/GroovyPostHighlightingPass.java @@ -19,8 +19,10 @@ package org.jetbrains.plugins.groovy.codeInspection.local; import com.intellij.codeHighlighting.TextEditorHighlightingPass; import com.intellij.codeInsight.CodeInsightSettings; import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer; +import com.intellij.codeInsight.daemon.HighlightDisplayKey; import com.intellij.codeInsight.daemon.impl.*; import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.codeInspection.InspectionProfile; import com.intellij.codeInspection.ProblemHighlightType; import com.intellij.lang.annotation.Annotation; import com.intellij.lang.annotation.AnnotationHolder; @@ -28,24 +30,28 @@ import com.intellij.lang.annotation.AnnotationSession; import com.intellij.lang.annotation.HighlightSeverity; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.CommandProcessor; -import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.ProjectFileIndex; import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.profile.codeInspection.InspectionProjectProfileManager; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiRecursiveElementWalkingVisitor; import com.intellij.util.Processor; import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.groovy.codeInspection.GroovyInspectionBundle; +import org.jetbrains.plugins.groovy.codeInspection.GroovyUnusedDeclarationInspection; import org.jetbrains.plugins.groovy.lang.editor.GroovyImportOptimizer; +import org.jetbrains.plugins.groovy.lang.psi.GrNamedElement; import org.jetbrains.plugins.groovy.lang.psi.GrReferenceElement; import org.jetbrains.plugins.groovy.lang.psi.GroovyFile; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.imports.GrImportStatement; import java.util.ArrayList; @@ -56,20 +62,30 @@ import java.util.Set; /** * @author ilyas */ -public class GroovyUnusedImportPass extends TextEditorHighlightingPass { +public class GroovyPostHighlightingPass extends TextEditorHighlightingPass { private final GroovyFile myFile; private final Editor myEditor; - public static final Logger LOG = Logger.getInstance("org.jetbrains.plugins.groovy.codeInspection.local.GroovyUnusedImportsPass"); private volatile Set myUnusedImports; private volatile Runnable myOptimizeRunnable; + private volatile List myUnusedDeclarations; - public GroovyUnusedImportPass(GroovyFile file, Editor editor) { + public GroovyPostHighlightingPass(GroovyFile file, Editor editor) { super(file.getProject(), editor.getDocument(), true); myFile = file; myEditor = editor; } - public void doCollectInformation(ProgressIndicator progress) { + public void doCollectInformation(final ProgressIndicator progress) { + InspectionProfile profile = InspectionProjectProfileManager.getInstance(myProject).getInspectionProfile(); + final boolean deadCodeEnabled = profile.isToolEnabled(HighlightDisplayKey.find(GroovyUnusedDeclarationInspection.SHORT_NAME), myFile); + ProjectFileIndex fileIndex = ProjectRootManager.getInstance(myProject).getFileIndex(); + VirtualFile virtualFile = myFile.getViewProvider().getVirtualFile(); + if (!fileIndex.isInContent(virtualFile)) { + return; + } + + + final List unusedDeclarations = new ArrayList(); final Set unusedImports = new HashSet(GroovyImportOptimizer.getValidImportStatements(myFile)); myFile.accept(new PsiRecursiveElementWalkingVisitor() { @Override @@ -83,10 +99,21 @@ public class GroovyUnusedImportPass extends TextEditorHighlightingPass { } } } + + if (deadCodeEnabled && element instanceof GrNamedElement) { + PsiElement nameId = ((GrNamedElement)element).getNameIdentifierGroovy(); + String name = ((GrNamedElement)element).getName(); + if (element instanceof GrTypeDefinition && PostHighlightingPass.isGloballyUnused((GrTypeDefinition)element, progress, null, name)) { + unusedDeclarations.add( + PostHighlightingPass.createUnusedSymbolInfo(nameId, "Class " + name + " is unused", HighlightInfoType.UNUSED_SYMBOL)); + } + } + super.visitElement(element); } }); myUnusedImports = unusedImports; + myUnusedDeclarations = unusedDeclarations; if (!unusedImports.isEmpty() && CodeInsightSettings.getInstance().OPTIMIZE_IMPORTS_ON_THE_FLY) { final VirtualFile vfile = myFile.getVirtualFile(); if (vfile != null && ProjectRootManager.getInstance(myFile.getProject()).getFileIndex().isInSource(vfile)) { @@ -143,7 +170,7 @@ public class GroovyUnusedImportPass extends TextEditorHighlightingPass { public void doApplyInformationToEditor() { AnnotationHolder annotationHolder = new AnnotationHolderImpl(new AnnotationSession(myFile)); - List infos = new ArrayList(myUnusedImports.size()); + List infos = new ArrayList(myUnusedDeclarations); for (GrImportStatement unusedImport : myUnusedImports) { Annotation annotation = annotationHolder.createWarningAnnotation(unusedImport, GroovyInspectionBundle.message("unused.import")); annotation.setHighlightType(ProblemHighlightType.LIKE_UNUSED_SYMBOL); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/local/GroovyUnusedImportsPassFactory.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/local/GroovyUnusedImportsPassFactory.java index 2ea820e498b7..593564cf1bd0 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/local/GroovyUnusedImportsPassFactory.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/local/GroovyUnusedImportsPassFactory.java @@ -42,7 +42,7 @@ public class GroovyUnusedImportsPassFactory extends AbstractProjectComponent imp @Nullable public TextEditorHighlightingPass createHighlightingPass(@NotNull PsiFile file, @NotNull Editor editor) { if (!(file instanceof GroovyFile)) return null; - return new GroovyUnusedImportPass((GroovyFile)file, editor); + return new GroovyPostHighlightingPass((GroovyFile)file, editor); } @NonNls From dd4cfde102c3a19788107d275e1cf3f46e73e493 Mon Sep 17 00:00:00 2001 From: peter Date: Mon, 30 Jan 2012 19:36:21 +0100 Subject: [PATCH 15/23] take implicit usages into account (IDEA-75803) --- .../daemon/impl/PostHighlightingPass.java | 16 +++++++--------- .../local/GroovyPostHighlightingPass.java | 2 +- .../plugins/groovy/lang/psi/GrNamedElement.java | 3 ++- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/PostHighlightingPass.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/PostHighlightingPass.java index 48b7f0ee5b25..6672bd5c45bf 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/PostHighlightingPass.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/PostHighlightingPass.java @@ -102,7 +102,7 @@ public class PostHighlightingPass extends TextEditorHighlightingPass { private final JavaCodeStyleManager myStyleManager; private int myCurrentEntryIndex; private boolean myHasMissortedImports; - private final ImplicitUsageProvider[] myImplicitUsageProviders; + private static final ImplicitUsageProvider[] ourImplicitUsageProviders = Extensions.getExtensions(ImplicitUsageProvider.EP_NAME); private UnusedDeclarationInspection myDeadCodeInspection; private UnusedSymbolLocalInspection myUnusedSymbolInspection; private HighlightDisplayKey myUnusedSymbolKey; @@ -124,8 +124,6 @@ public class PostHighlightingPass extends TextEditorHighlightingPass { myStyleManager = JavaCodeStyleManager.getInstance(myProject); myCurrentEntryIndex = -1; - - myImplicitUsageProviders = Extensions.getExtensions(ImplicitUsageProvider.EP_NAME); } @Override @@ -346,9 +344,9 @@ public class PostHighlightingPass extends TextEditorHighlightingPass { } - private boolean isImplicitUsage(final PsiModifierListOwner element, ProgressIndicator progress) { + public static boolean isImplicitUsage(final PsiModifierListOwner element, ProgressIndicator progress) { if (UnusedSymbolLocalInspection.isInjected(element)) return true; - for (ImplicitUsageProvider provider : myImplicitUsageProviders) { + for (ImplicitUsageProvider provider : ourImplicitUsageProviders) { progress.checkCanceled(); if (provider.isImplicitUsage(element)) { return true; @@ -358,8 +356,8 @@ public class PostHighlightingPass extends TextEditorHighlightingPass { return false; } - private boolean isImplicitRead(final PsiVariable element, ProgressIndicator progress) { - for(ImplicitUsageProvider provider: myImplicitUsageProviders) { + private static boolean isImplicitRead(final PsiVariable element, ProgressIndicator progress) { + for(ImplicitUsageProvider provider: ourImplicitUsageProviders) { progress.checkCanceled(); if (provider.isImplicitRead(element)) { return true; @@ -368,8 +366,8 @@ public class PostHighlightingPass extends TextEditorHighlightingPass { return UnusedSymbolLocalInspection.isInjected(element); } - private boolean isImplicitWrite(final PsiVariable element, ProgressIndicator progress) { - for(ImplicitUsageProvider provider: myImplicitUsageProviders) { + private static boolean isImplicitWrite(final PsiVariable element, ProgressIndicator progress) { + for(ImplicitUsageProvider provider: ourImplicitUsageProviders) { progress.checkCanceled(); if (provider.isImplicitWrite(element)) { return true; diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/local/GroovyPostHighlightingPass.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/local/GroovyPostHighlightingPass.java index d3f3b5f40330..ea024e4cc37e 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/local/GroovyPostHighlightingPass.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/local/GroovyPostHighlightingPass.java @@ -100,7 +100,7 @@ public class GroovyPostHighlightingPass extends TextEditorHighlightingPass { } } - if (deadCodeEnabled && element instanceof GrNamedElement) { + if (deadCodeEnabled && element instanceof GrNamedElement && !PostHighlightingPass.isImplicitUsage((GrNamedElement)element, progress)) { PsiElement nameId = ((GrNamedElement)element).getNameIdentifierGroovy(); String name = ((GrNamedElement)element).getName(); if (element instanceof GrTypeDefinition && PostHighlightingPass.isGloballyUnused((GrTypeDefinition)element, progress, null, name)) { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/GrNamedElement.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/GrNamedElement.java index 8159590e1e87..70275dca1303 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/GrNamedElement.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/GrNamedElement.java @@ -16,6 +16,7 @@ package org.jetbrains.plugins.groovy.lang.psi; +import com.intellij.psi.PsiModifierListOwner; import com.intellij.psi.PsiNamedElement; import com.intellij.psi.PsiElement; import org.jetbrains.annotations.NotNull; @@ -23,7 +24,7 @@ import org.jetbrains.annotations.NotNull; /** * @author ven */ -public interface GrNamedElement extends PsiNamedElement, GroovyPsiElement { +public interface GrNamedElement extends PsiNamedElement, GroovyPsiElement, PsiModifierListOwner { @NotNull PsiElement getNameIdentifierGroovy(); } From 6413bfc5875d7e12ac6b7d620bd982e46b386d6e Mon Sep 17 00:00:00 2001 From: peter Date: Mon, 30 Jan 2012 20:15:09 +0100 Subject: [PATCH 16/23] introduce GlobalUsageHelper --- .../daemon/impl/GlobalUsageHelper.java | 40 ++++++++ .../daemon/impl/PostHighlightingPass.java | 97 ++++++++++--------- .../local/GroovyPostHighlightingPass.java | 14 ++- 3 files changed, 104 insertions(+), 47 deletions(-) create mode 100644 java/java-impl/src/com/intellij/codeInsight/daemon/impl/GlobalUsageHelper.java diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/GlobalUsageHelper.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/GlobalUsageHelper.java new file mode 100644 index 000000000000..18a363133c01 --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/GlobalUsageHelper.java @@ -0,0 +1,40 @@ +/* + * Copyright 2000-2012 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.intellij.codeInsight.daemon.impl; + +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiMember; +import com.intellij.psi.PsiNamedElement; +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author peter + */ +public abstract class GlobalUsageHelper { + final Map unusedClassCache = new HashMap(); + + public abstract boolean shouldCheckUsages(@NotNull PsiMember member); + public boolean isLocallyUsed(@NotNull PsiNamedElement member) { + return false; + } + + public boolean shouldIgnoreUsagesInCurrentFile() { + return false; + } +} diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/PostHighlightingPass.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/PostHighlightingPass.java index 6672bd5c45bf..74f7f6c5d949 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/PostHighlightingPass.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/PostHighlightingPass.java @@ -82,7 +82,6 @@ import com.intellij.psi.util.PsiUtilCore; import com.intellij.refactoring.changeSignature.ChangeSignatureGestureDetector; import com.intellij.util.Processor; import gnu.trove.THashSet; -import gnu.trove.TObjectIntHashMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.PropertyKey; @@ -252,13 +251,28 @@ public class PostHighlightingPass extends TextEditorHighlightingPass { myDeadCodeInfoType = myDeadCodeKey == null ? null : new HighlightInfoType.HighlightInfoTypeImpl(profile.getErrorLevel(myDeadCodeKey, myFile).getSeverity(), HighlightInfoType.UNUSED_SYMBOL.getAttributesKey()); + GlobalUsageHelper helper = new GlobalUsageHelper() { + @Override + public boolean shouldCheckUsages(@NotNull PsiMember member) { + if (myInLibrary) return false; + if (!myDeadCodeEnabled) return false; + if (myDeadCodeInspection.isEntryPoint(member)) return false; + return true; + } + + @Override + public boolean isLocallyUsed(@NotNull PsiNamedElement member) { + return myRefCountHolder.isReferenced(myFile); + } + }; + boolean errorFound = false; if (unusedSymbolEnabled) { for (PsiElement element : elements) { progress.checkCanceled(); if (element instanceof PsiIdentifier) { PsiIdentifier identifier = (PsiIdentifier)element; - HighlightInfo info = processIdentifier(identifier, progress); + HighlightInfo info = processIdentifier(identifier, progress, helper); if (info != null) { errorFound |= info.getSeverity() == HighlightSeverity.ERROR; result.add(info); @@ -285,7 +299,7 @@ public class PostHighlightingPass extends TextEditorHighlightingPass { } @Nullable - private HighlightInfo processIdentifier(PsiIdentifier identifier, ProgressIndicator progress) { + private HighlightInfo processIdentifier(PsiIdentifier identifier, ProgressIndicator progress, GlobalUsageHelper helper) { if (InspectionManagerEx.inspectionResultSuppressed(identifier, myUnusedSymbolInspection)) return null; PsiElement parent = identifier.getParent(); if (PsiUtilCore.hasErrorElementChild(parent)) return null; @@ -294,17 +308,17 @@ public class PostHighlightingPass extends TextEditorHighlightingPass { return processLocalVariable((PsiLocalVariable)parent, progress); } if (parent instanceof PsiField && myUnusedSymbolInspection.FIELD) { - return processField((PsiField)parent, identifier, progress); + return processField((PsiField)parent, identifier, progress, helper); } if (parent instanceof PsiParameter && myUnusedSymbolInspection.PARAMETER) { if (InspectionManagerEx.isSuppressed(identifier, UnusedParametersInspection.SHORT_NAME)) return null; return processParameter((PsiParameter)parent, progress); } if (parent instanceof PsiMethod && myUnusedSymbolInspection.METHOD) { - return processMethod((PsiMethod)parent, progress); + return processMethod((PsiMethod)parent, progress, helper); } if (parent instanceof PsiClass && myUnusedSymbolInspection.CLASS) { - return processClass((PsiClass)parent, progress); + return processClass((PsiClass)parent, progress, helper); } return null; } @@ -389,7 +403,7 @@ public class PostHighlightingPass extends TextEditorHighlightingPass { } @Nullable - private HighlightInfo processField(final PsiField field, final PsiIdentifier identifier, ProgressIndicator progress) { + private HighlightInfo processField(final PsiField field, final PsiIdentifier identifier, ProgressIndicator progress, GlobalUsageHelper helper) { if (field.hasModifierProperty(PsiModifier.PRIVATE)) { if (!myRefCountHolder.isReferenced(field) && !isImplicitUsage(field, progress)) { if (HighlightUtil.isSerializationImplicitlyUsedField(field)) { @@ -433,8 +447,8 @@ public class PostHighlightingPass extends TextEditorHighlightingPass { else if (isImplicitUsage(field, progress)) { return null; } - else if (!myRefCountHolder.isReferenced(field) && weAreSureThereAreNoUsages(field, progress)) { - if (field instanceof PsiEnumConstant && isEnumValuesMethodUsed(field, progress)) { + else if (!myRefCountHolder.isReferenced(field) && weAreSureThereAreNoUsages(field, progress, helper)) { + if (field instanceof PsiEnumConstant && isEnumValuesMethodUsed(field, progress, helper)) { return null; } return formatUnusedSymbolHighlightInfo("field.is.not.used", field, "fields", myDeadCodeKey, myDeadCodeInfoType); @@ -509,10 +523,10 @@ public class PostHighlightingPass extends TextEditorHighlightingPass { } @Nullable - private HighlightInfo processMethod(final PsiMethod method, ProgressIndicator progress) { + private HighlightInfo processMethod(final PsiMethod method, ProgressIndicator progress, GlobalUsageHelper helper) { boolean isPrivate = method.hasModifierProperty(PsiModifier.PRIVATE); PsiClass containingClass = method.getContainingClass(); - if (isMethodReferenced(method, progress, isPrivate, containingClass)) return null; + if (isMethodReferenced(method, progress, isPrivate, containingClass, helper)) return null; HighlightInfoType highlightInfoType; HighlightDisplayKey highlightDisplayKey; String key; @@ -545,8 +559,12 @@ public class PostHighlightingPass extends TextEditorHighlightingPass { return highlightInfo; } - private boolean isMethodReferenced(PsiMethod method, ProgressIndicator progress, boolean aPrivate, PsiClass containingClass) { - if (myRefCountHolder.isReferenced(method)) return true; + private static boolean isMethodReferenced(PsiMethod method, + ProgressIndicator progress, + boolean aPrivate, + PsiClass containingClass, + GlobalUsageHelper helper) { + if (helper.isLocallyUsed(method)) return true; if (HighlightMethodUtil.isSerializationRelatedMethod(method, containingClass)) return true; if (aPrivate) { @@ -561,7 +579,7 @@ public class PostHighlightingPass extends TextEditorHighlightingPass { //class maybe used in some weird way, e.g. from XML, therefore the only constructor is used too if (containingClass != null && method.isConstructor() && containingClass.getConstructors().length == 1 - && isClassUnused(containingClass, progress) == USED) { + && isClassUsed(containingClass, progress, helper)) { return true; } if (isImplicitUsage(method, progress)) return true; @@ -569,22 +587,17 @@ public class PostHighlightingPass extends TextEditorHighlightingPass { if (method.findSuperMethods().length != 0) { return true; } - if (!weAreSureThereAreNoUsages(method, progress)) { + if (!weAreSureThereAreNoUsages(method, progress, helper)) { return true; } } return false; } - private boolean weAreSureThereAreNoUsages(PsiMember member, ProgressIndicator progress) { - if (myInLibrary) return false; - if (!myDeadCodeEnabled) return false; - if (myDeadCodeInspection.isEntryPoint(member)) return false; + private static boolean weAreSureThereAreNoUsages(PsiMember member, ProgressIndicator progress, GlobalUsageHelper helper) { + if (!helper.shouldCheckUsages(member)) return false; - return isGloballyUnused(member, progress, myFile, member.getName()); - } - - public static boolean isGloballyUnused(PsiMember member, ProgressIndicator progress, @Nullable PsiFile fileToIgnoreOccurrencesIn, String name) { + String name = member.getName(); if (name == null) return false; SearchScope useScope = member.getUseScope(); if (!(useScope instanceof GlobalSearchScope)) return false; @@ -593,7 +606,9 @@ public class PostHighlightingPass extends TextEditorHighlightingPass { Project project = member.getProject(); if (member instanceof PsiClass) scope = GlobalSearchScope.projectScope(project).uniteWith(scope); - PsiSearchHelper.SearchCostResult cheapEnough = PsiSearchHelper.SERVICE.getInstance(project).isCheapEnoughToSearch(name, scope, fileToIgnoreOccurrencesIn, progress); + PsiSearchHelper.SearchCostResult cheapEnough = PsiSearchHelper.SERVICE.getInstance(project).isCheapEnoughToSearch(name, scope, + helper.shouldIgnoreUsagesInCurrentFile() ? member.getContainingFile() : null, + progress); if (cheapEnough == PsiSearchHelper.SearchCostResult.TOO_MANY_OCCURRENCES) return false; //search usages if it cheap @@ -608,13 +623,13 @@ public class PostHighlightingPass extends TextEditorHighlightingPass { return !findUsagesManager.isUsed(member, findUsagesOptions); } - private boolean isEnumValuesMethodUsed(PsiMember member, ProgressIndicator progress) { + private static boolean isEnumValuesMethodUsed(PsiMember member, ProgressIndicator progress, GlobalUsageHelper helper) { final PsiClassImpl containingClass = (PsiClassImpl)member.getContainingClass(); if (containingClass == null) return true; final PsiMethod valuesMethod = containingClass.getValuesMethod(); if (valuesMethod == null) return true; boolean isPrivate = valuesMethod.hasModifierProperty(PsiModifier.PRIVATE); - return isMethodReferenced(valuesMethod, progress, isPrivate, containingClass); + return isMethodReferenced(valuesMethod, progress, isPrivate, containingClass, helper); } private static boolean canBeReferencedViaWeirdNames(PsiMember member) { @@ -629,9 +644,8 @@ public class PostHighlightingPass extends TextEditorHighlightingPass { } @Nullable - private HighlightInfo processClass(PsiClass aClass, ProgressIndicator progress) { - int usage = isClassUnused(aClass, progress); - if (usage == USED) return null; + private HighlightInfo processClass(PsiClass aClass, ProgressIndicator progress, GlobalUsageHelper helper) { + if (isClassUsed(aClass, progress, helper)) return null; String pattern; HighlightDisplayKey highlightDisplayKey; @@ -661,27 +675,22 @@ public class PostHighlightingPass extends TextEditorHighlightingPass { return formatUnusedSymbolHighlightInfo(pattern, aClass, "classes", highlightDisplayKey, highlightInfoType); } - private static final int USED = 1; - private static final int UNUSED_LOCALLY = 2; - private static final int UNUSED_GLOBALLY = 3; - private final TObjectIntHashMap unusedClassCache = new TObjectIntHashMap(); - private int isClassUnused(PsiClass aClass, ProgressIndicator progress) { - if (aClass == null) return USED; - int result = unusedClassCache.get(aClass); - if (result == 0) { - result = isReallyUnused(aClass, progress); - unusedClassCache.put(aClass, result); + public static boolean isClassUsed(PsiClass aClass, ProgressIndicator progress, GlobalUsageHelper helper) { + if (aClass == null) return true; + Boolean result = helper.unusedClassCache.get(aClass); + if (result == null) { + result = !isReallyUsed(aClass, progress, helper); + helper.unusedClassCache.put(aClass, result); } return result; } - private int isReallyUnused(PsiClass aClass, ProgressIndicator progress) { - if (isImplicitUsage(aClass, progress) || myRefCountHolder.isReferenced(aClass)) return USED; + private static boolean isReallyUsed(PsiClass aClass, ProgressIndicator progress, GlobalUsageHelper helper) { + if (isImplicitUsage(aClass, progress) || helper.isLocallyUsed(aClass)) return true; if (aClass.getContainingClass() != null && aClass.hasModifierProperty(PsiModifier.PRIVATE) || aClass.getParent() instanceof PsiDeclarationStatement || - aClass instanceof PsiTypeParameter) return UNUSED_LOCALLY; - if (weAreSureThereAreNoUsages(aClass, progress)) return UNUSED_GLOBALLY; - return USED; + aClass instanceof PsiTypeParameter) return false; + return !weAreSureThereAreNoUsages(aClass, progress, helper); } private static HighlightInfo formatUnusedSymbolHighlightInfo(@PropertyKey(resourceBundle = JavaErrorMessages.BUNDLE) String pattern, diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/local/GroovyPostHighlightingPass.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/local/GroovyPostHighlightingPass.java index ea024e4cc37e..5f90199da46c 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/local/GroovyPostHighlightingPass.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/local/GroovyPostHighlightingPass.java @@ -24,6 +24,7 @@ import com.intellij.codeInsight.daemon.impl.*; import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.codeInspection.InspectionProfile; import com.intellij.codeInspection.ProblemHighlightType; +import com.intellij.codeInspection.deadCode.UnusedDeclarationInspection; import com.intellij.lang.annotation.Annotation; import com.intellij.lang.annotation.AnnotationHolder; import com.intellij.lang.annotation.AnnotationSession; @@ -40,6 +41,7 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.profile.codeInspection.InspectionProjectProfileManager; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiMember; import com.intellij.psi.PsiRecursiveElementWalkingVisitor; import com.intellij.util.Processor; import org.jetbrains.annotations.NotNull; @@ -76,14 +78,20 @@ public class GroovyPostHighlightingPass extends TextEditorHighlightingPass { } public void doCollectInformation(final ProgressIndicator progress) { - InspectionProfile profile = InspectionProjectProfileManager.getInstance(myProject).getInspectionProfile(); + final InspectionProfile profile = InspectionProjectProfileManager.getInstance(myProject).getInspectionProfile(); final boolean deadCodeEnabled = profile.isToolEnabled(HighlightDisplayKey.find(GroovyUnusedDeclarationInspection.SHORT_NAME), myFile); ProjectFileIndex fileIndex = ProjectRootManager.getInstance(myProject).getFileIndex(); VirtualFile virtualFile = myFile.getViewProvider().getVirtualFile(); if (!fileIndex.isInContent(virtualFile)) { return; } - + final UnusedDeclarationInspection deadCodeInspection = (UnusedDeclarationInspection)profile.getInspectionTool(UnusedDeclarationInspection.SHORT_NAME, myFile); + final GlobalUsageHelper usageHelper = new GlobalUsageHelper() { + @Override + public boolean shouldCheckUsages(@NotNull PsiMember member) { + return deadCodeInspection == null || !deadCodeInspection.isEntryPoint(member); + } + }; final List unusedDeclarations = new ArrayList(); final Set unusedImports = new HashSet(GroovyImportOptimizer.getValidImportStatements(myFile)); @@ -103,7 +111,7 @@ public class GroovyPostHighlightingPass extends TextEditorHighlightingPass { if (deadCodeEnabled && element instanceof GrNamedElement && !PostHighlightingPass.isImplicitUsage((GrNamedElement)element, progress)) { PsiElement nameId = ((GrNamedElement)element).getNameIdentifierGroovy(); String name = ((GrNamedElement)element).getName(); - if (element instanceof GrTypeDefinition && PostHighlightingPass.isGloballyUnused((GrTypeDefinition)element, progress, null, name)) { + if (element instanceof GrTypeDefinition && PostHighlightingPass.isClassUsed((GrTypeDefinition)element, progress, usageHelper)) { unusedDeclarations.add( PostHighlightingPass.createUnusedSymbolInfo(nameId, "Class " + name + " is unused", HighlightInfoType.UNUSED_SYMBOL)); } From 9dfa66531ef2923b6c2d9f2ce1398607f16a8118 Mon Sep 17 00:00:00 2001 From: peter Date: Mon, 30 Jan 2012 20:21:20 +0100 Subject: [PATCH 17/23] highlight globally unused groovy methods (IDEA-75803) --- .../daemon/impl/PostHighlightingPass.java | 16 +++++++--------- .../local/GroovyPostHighlightingPass.java | 19 +++++++++++++++---- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/PostHighlightingPass.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/PostHighlightingPass.java index 74f7f6c5d949..2c40a1aa2c78 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/PostHighlightingPass.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/PostHighlightingPass.java @@ -524,13 +524,11 @@ public class PostHighlightingPass extends TextEditorHighlightingPass { @Nullable private HighlightInfo processMethod(final PsiMethod method, ProgressIndicator progress, GlobalUsageHelper helper) { - boolean isPrivate = method.hasModifierProperty(PsiModifier.PRIVATE); - PsiClass containingClass = method.getContainingClass(); - if (isMethodReferenced(method, progress, isPrivate, containingClass, helper)) return null; + if (isMethodReferenced(method, progress, helper)) return null; HighlightInfoType highlightInfoType; HighlightDisplayKey highlightDisplayKey; String key; - if (isPrivate) { + if (method.hasModifierProperty(PsiModifier.PRIVATE)) { highlightInfoType = HighlightInfoType.UNUSED_SYMBOL; highlightDisplayKey = myUnusedSymbolKey; key = method.isConstructor() ? "private.constructor.is.not.used" : "private.method.is.not.used"; @@ -552,6 +550,7 @@ public class PostHighlightingPass extends TextEditorHighlightingPass { return true; } }); + PsiClass containingClass = method.getContainingClass(); if (method.getReturnType() != null || containingClass != null && Comparing.strEqual(containingClass.getName(), method.getName())) { //ignore methods with deleted return types as they are always marked as unused without any reason ChangeSignatureGestureDetector.getInstance(myProject).dismissForElement(method); @@ -559,13 +558,13 @@ public class PostHighlightingPass extends TextEditorHighlightingPass { return highlightInfo; } - private static boolean isMethodReferenced(PsiMethod method, + public static boolean isMethodReferenced(PsiMethod method, ProgressIndicator progress, - boolean aPrivate, - PsiClass containingClass, GlobalUsageHelper helper) { if (helper.isLocallyUsed(method)) return true; + boolean aPrivate = method.hasModifierProperty(PsiModifier.PRIVATE); + PsiClass containingClass = method.getContainingClass(); if (HighlightMethodUtil.isSerializationRelatedMethod(method, containingClass)) return true; if (aPrivate) { if (isIntentionalPrivateConstructor(method, containingClass)) { @@ -628,8 +627,7 @@ public class PostHighlightingPass extends TextEditorHighlightingPass { if (containingClass == null) return true; final PsiMethod valuesMethod = containingClass.getValuesMethod(); if (valuesMethod == null) return true; - boolean isPrivate = valuesMethod.hasModifierProperty(PsiModifier.PRIVATE); - return isMethodReferenced(valuesMethod, progress, isPrivate, containingClass, helper); + return isMethodReferenced(valuesMethod, progress, helper); } private static boolean canBeReferencedViaWeirdNames(PsiMember member) { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/local/GroovyPostHighlightingPass.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/local/GroovyPostHighlightingPass.java index 5f90199da46c..4dd5726262f3 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/local/GroovyPostHighlightingPass.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/local/GroovyPostHighlightingPass.java @@ -48,12 +48,14 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.groovy.codeInspection.GroovyInspectionBundle; import org.jetbrains.plugins.groovy.codeInspection.GroovyUnusedDeclarationInspection; import org.jetbrains.plugins.groovy.lang.editor.GroovyImportOptimizer; +import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes; import org.jetbrains.plugins.groovy.lang.psi.GrNamedElement; import org.jetbrains.plugins.groovy.lang.psi.GrReferenceElement; import org.jetbrains.plugins.groovy.lang.psi.GroovyFile; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.imports.GrImportStatement; import java.util.ArrayList; @@ -110,10 +112,19 @@ public class GroovyPostHighlightingPass extends TextEditorHighlightingPass { if (deadCodeEnabled && element instanceof GrNamedElement && !PostHighlightingPass.isImplicitUsage((GrNamedElement)element, progress)) { PsiElement nameId = ((GrNamedElement)element).getNameIdentifierGroovy(); - String name = ((GrNamedElement)element).getName(); - if (element instanceof GrTypeDefinition && PostHighlightingPass.isClassUsed((GrTypeDefinition)element, progress, usageHelper)) { - unusedDeclarations.add( - PostHighlightingPass.createUnusedSymbolInfo(nameId, "Class " + name + " is unused", HighlightInfoType.UNUSED_SYMBOL)); + if (nameId.getNode().getElementType() == GroovyTokenTypes.mIDENT) { + String name = ((GrNamedElement)element).getName(); + if (element instanceof GrTypeDefinition && PostHighlightingPass.isClassUsed((GrTypeDefinition)element, progress, usageHelper)) { + unusedDeclarations.add( + PostHighlightingPass.createUnusedSymbolInfo(nameId, "Class " + name + " is unused", HighlightInfoType.UNUSED_SYMBOL)); + } + else if (element instanceof GrMethod) { + GrMethod method = (GrMethod)element; + if (!PostHighlightingPass.isMethodReferenced(method, progress, usageHelper)) { + unusedDeclarations.add( + PostHighlightingPass.createUnusedSymbolInfo(nameId, (method.isConstructor() ? "Constructor" : "Method") +" " + name + " is unused", HighlightInfoType.UNUSED_SYMBOL)); + } + } } } From ea7151df75429924ad3aa78b3a0a9e3f31ccb128 Mon Sep 17 00:00:00 2001 From: peter Date: Mon, 30 Jan 2012 20:33:23 +0100 Subject: [PATCH 18/23] highlight globally unused groovy properties (IDEA-75803) --- .../daemon/impl/PostHighlightingPass.java | 15 +++++++++++---- .../local/GroovyPostHighlightingPass.java | 5 +++++ .../groovy/lang/GroovyHighlightingTest.groovy | 10 ++++++++-- .../highlighting/GloballyUnusedSymbols.groovy | 19 +++++++++++++++++++ 4 files changed, 43 insertions(+), 6 deletions(-) create mode 100644 plugins/groovy/testdata/highlighting/GloballyUnusedSymbols.groovy diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/PostHighlightingPass.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/PostHighlightingPass.java index 2c40a1aa2c78..531bc286acfd 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/PostHighlightingPass.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/PostHighlightingPass.java @@ -447,15 +447,22 @@ public class PostHighlightingPass extends TextEditorHighlightingPass { else if (isImplicitUsage(field, progress)) { return null; } - else if (!myRefCountHolder.isReferenced(field) && weAreSureThereAreNoUsages(field, progress, helper)) { - if (field instanceof PsiEnumConstant && isEnumValuesMethodUsed(field, progress, helper)) { - return null; - } + else if (isFieldUnused(field, progress, helper)) { return formatUnusedSymbolHighlightInfo("field.is.not.used", field, "fields", myDeadCodeKey, myDeadCodeInfoType); } return null; } + public static boolean isFieldUnused(PsiField field, ProgressIndicator progress, GlobalUsageHelper helper) { + if (helper.isLocallyUsed(field) || !weAreSureThereAreNoUsages(field, progress, helper)) { + return false; + } + if (field instanceof PsiEnumConstant && isEnumValuesMethodUsed(field, progress, helper)) { + return false; + } + return true; + } + private HighlightInfo suggestionsToMakeFieldUsed(final PsiField field, final PsiIdentifier identifier, final String message) { HighlightInfo highlightInfo = createUnusedSymbolInfo(identifier, message, HighlightInfoType.UNUSED_SYMBOL); QuickFixAction.registerQuickFixAction(highlightInfo, new RemoveUnusedVariableFix(field), myUnusedSymbolKey); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/local/GroovyPostHighlightingPass.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/local/GroovyPostHighlightingPass.java index 4dd5726262f3..49c4c67fcec4 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/local/GroovyPostHighlightingPass.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/local/GroovyPostHighlightingPass.java @@ -54,6 +54,7 @@ import org.jetbrains.plugins.groovy.lang.psi.GrReferenceElement; import org.jetbrains.plugins.groovy.lang.psi.GroovyFile; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; import org.jetbrains.plugins.groovy.lang.psi.api.GroovyResolveResult; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.imports.GrImportStatement; @@ -125,6 +126,10 @@ public class GroovyPostHighlightingPass extends TextEditorHighlightingPass { PostHighlightingPass.createUnusedSymbolInfo(nameId, (method.isConstructor() ? "Constructor" : "Method") +" " + name + " is unused", HighlightInfoType.UNUSED_SYMBOL)); } } + else if (element instanceof GrField && PostHighlightingPass.isFieldUnused((GrField)element, progress, usageHelper)) { + unusedDeclarations.add( + PostHighlightingPass.createUnusedSymbolInfo(nameId, "Property " + name + " is unused", HighlightInfoType.UNUSED_SYMBOL)); + } } } diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.groovy index e72b91eb44d6..48231e862fe7 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.groovy @@ -16,7 +16,7 @@ package org.jetbrains.plugins.groovy.lang; -import com.intellij.codeInspection.LocalInspectionTool +import com.intellij.codeInspection.InspectionProfileEntry import com.intellij.openapi.module.Module import com.intellij.openapi.roots.ContentEntry import com.intellij.openapi.roots.ModifiableRootModel @@ -31,6 +31,7 @@ import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase import com.siyeh.ig.junit.JUnitAbstractTestClassNamingConventionInspection import com.siyeh.ig.junit.JUnitTestClassNamingConventionInspection import org.jetbrains.annotations.NotNull +import org.jetbrains.plugins.groovy.codeInspection.GroovyUnusedDeclarationInspection import org.jetbrains.plugins.groovy.codeInspection.assignment.GroovyAssignabilityCheckInspection import org.jetbrains.plugins.groovy.codeInspection.assignment.GroovyResultOfAssignmentUsedInspection import org.jetbrains.plugins.groovy.codeInspection.assignment.GroovyUncheckedAssignmentOfMemberOfRawTypeInspection @@ -48,6 +49,7 @@ import org.jetbrains.plugins.groovy.codeInspection.untypedUnresolvedAccess.Groov import org.jetbrains.plugins.groovy.codeInspection.unusedDef.UnusedDefInspection import org.jetbrains.plugins.groovy.util.TestUtils import org.jetbrains.plugins.groovy.codeInspection.bugs.* +import com.intellij.codeInspection.deadCode.UnusedDeclarationInspection /** * @author peter @@ -83,7 +85,7 @@ public class GroovyHighlightingTest extends LightCodeInsightFixtureTestCase { doTest(); } - private void doTest(LocalInspectionTool... tools) { + private void doTest(InspectionProfileEntry... tools) { myFixture.enableInspections(tools); myFixture.testHighlighting(true, false, false, getTestName(false) + ".groovy"); } @@ -659,4 +661,8 @@ List list2 ''') myFixture.testHighlighting(true, false, false) } + + public void testGloballyUnusedSymbols() { + doTest(new GroovyUnusedDeclarationInspection(), new UnusedDeclarationInspection()) + } } \ No newline at end of file diff --git a/plugins/groovy/testdata/highlighting/GloballyUnusedSymbols.groovy b/plugins/groovy/testdata/highlighting/GloballyUnusedSymbols.groovy new file mode 100644 index 000000000000..5ad85a2c8f6b --- /dev/null +++ b/plugins/groovy/testdata/highlighting/GloballyUnusedSymbols.groovy @@ -0,0 +1,19 @@ + +class UnusedClass {} +class Bar { + int unusedProperty = 2 + int usedProperty = 39 + int usedProperty2 = 39 + int usedProperty3 = 39 + def unusedMethod() {} + Bar usedMethod() { this } + + Bar getUsedPropertyGetter() {} + + public static void main(String[] args) {} + +} +println new Bar().usedMethod().usedProperty +new Bar().setUsedProperty2 42 +println new Bar().getUsedProperty3() +println new Bar().usedPropertyGetter From adf3dd08154d8b6faed43d94f468d0af6454e73a Mon Sep 17 00:00:00 2001 From: peter Date: Mon, 30 Jan 2012 20:45:44 +0100 Subject: [PATCH 19/23] gpp meta methods are not unused (IDEA-75803) --- plugins/groovy/src/META-INF/plugin.xml | 2 + .../groovy/gpp/GppImplicitUsageProvider.java | 59 +++++++++++++++++++ .../groovy/lang/GppFunctionalTest.groovy | 18 ++++++ .../GloballyUnusedGppSymbols.groovy | 5 ++ 4 files changed, 84 insertions(+) create mode 100644 plugins/groovy/src/org/jetbrains/plugins/groovy/gpp/GppImplicitUsageProvider.java create mode 100644 plugins/groovy/testdata/highlighting/GloballyUnusedGppSymbols.groovy diff --git a/plugins/groovy/src/META-INF/plugin.xml b/plugins/groovy/src/META-INF/plugin.xml index 63f176ae2810..dfa3cd461c59 100644 --- a/plugins/groovy/src/META-INF/plugin.xml +++ b/plugins/groovy/src/META-INF/plugin.xml @@ -717,6 +717,8 @@ groupName="Annotations verifying" enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.plugins.groovy.annotator.inspections.GroovySingletonAnnotationInspection"/> + + org.jetbrains.plugins.groovy.intentions.GroovyIntentionsBundle diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/gpp/GppImplicitUsageProvider.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/gpp/GppImplicitUsageProvider.java new file mode 100644 index 000000000000..2fd09354ce7b --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/gpp/GppImplicitUsageProvider.java @@ -0,0 +1,59 @@ +/* + * Copyright 2000-2012 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.plugins.groovy.gpp; + +import com.intellij.codeInsight.daemon.ImplicitUsageProvider; +import com.intellij.psi.*; + +/** + * @author peter + */ +public class GppImplicitUsageProvider implements ImplicitUsageProvider { + + private static boolean isGppMetaMethod(PsiMethod method) { + PsiParameter[] parameters = method.getParameterList().getParameters(); + if (parameters.length == 0 || !parameters[0].getType().equalsToText(CommonClassNames.JAVA_LANG_STRING)) { + return false; + } + + if ("invokeUnresolvedMethod".equals(method.getName())) { + return true; + } + if ("getUnresolvedProperty".equals(method.getName())) { + return parameters.length == 1; + } + if ("setUnresolvedProperty".equals(method.getName())) { + return parameters.length == 2; + } + + return false; + } + + @Override + public boolean isImplicitUsage(PsiElement element) { + return element instanceof PsiMethod && isGppMetaMethod((PsiMethod)element); + } + + @Override + public boolean isImplicitRead(PsiElement element) { + return false; + } + + @Override + public boolean isImplicitWrite(PsiElement element) { + return false; + } +} diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GppFunctionalTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GppFunctionalTest.groovy index 92c59b747b03..1d578702c34b 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GppFunctionalTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GppFunctionalTest.groovy @@ -20,6 +20,8 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefini import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod import org.jetbrains.plugins.groovy.util.TestUtils import com.intellij.psi.* +import org.jetbrains.plugins.groovy.codeInspection.GroovyUnusedDeclarationInspection +import com.intellij.codeInspection.deadCode.UnusedDeclarationInspection /** * @author peter @@ -581,6 +583,22 @@ def bar() { myFixture.checkHighlighting(true, false, false) } + public void testUsedInterceptors() { + configureGppScript ''' +class Bar { + Object getUnresolvedProperty(String name) {} + Object getUnresolvedProperty(int name) {} + void setUnresolvedProperty(String name, String value) {} + int invokeUnresolvedMethod(String name, String arg1, boolean arg2, Object... args) {} + int invokeUnresolvedMethod(String name, Object... args) {} + int invokeUnresolvedMethod(Object... args) {} +} +println new Bar().zzz +''' + myFixture.enableInspections(new GroovyUnusedDeclarationInspection(), new UnusedDeclarationInspection()) + myFixture.checkHighlighting(true, false, false) + } + } class GppProjectDescriptor extends DefaultLightProjectDescriptor { diff --git a/plugins/groovy/testdata/highlighting/GloballyUnusedGppSymbols.groovy b/plugins/groovy/testdata/highlighting/GloballyUnusedGppSymbols.groovy new file mode 100644 index 000000000000..115ff12e394f --- /dev/null +++ b/plugins/groovy/testdata/highlighting/GloballyUnusedGppSymbols.groovy @@ -0,0 +1,5 @@ +@Typed package foo; +class Bar { + +} +println new Bar().zzz From bd365c5cd68f5dd8bc96ee1cbf551235af7f5ae8 Mon Sep 17 00:00:00 2001 From: peter Date: Tue, 31 Jan 2012 13:41:04 +0100 Subject: [PATCH 20/23] correctly report private methods' unusedness (IDEA-75803) --- .../daemon/impl/GlobalUsageHelper.java | 9 +--- .../daemon/impl/PostHighlightingPass.java | 41 +++++++++++-------- .../local/GroovyPostHighlightingPass.java | 15 ++++--- .../highlighting/GloballyUnusedSymbols.groovy | 5 ++- 4 files changed, 41 insertions(+), 29 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/GlobalUsageHelper.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/GlobalUsageHelper.java index 18a363133c01..4f4e3e87c356 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/GlobalUsageHelper.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/GlobalUsageHelper.java @@ -30,11 +30,6 @@ public abstract class GlobalUsageHelper { final Map unusedClassCache = new HashMap(); public abstract boolean shouldCheckUsages(@NotNull PsiMember member); - public boolean isLocallyUsed(@NotNull PsiNamedElement member) { - return false; - } - - public boolean shouldIgnoreUsagesInCurrentFile() { - return false; - } + public abstract boolean isLocallyUsed(@NotNull PsiNamedElement member); + public abstract boolean shouldIgnoreUsagesInCurrentFile(); } diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/PostHighlightingPass.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/PostHighlightingPass.java index 531bc286acfd..6ab7b118c92f 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/PostHighlightingPass.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/PostHighlightingPass.java @@ -260,9 +260,14 @@ public class PostHighlightingPass extends TextEditorHighlightingPass { return true; } + @Override + public boolean shouldIgnoreUsagesInCurrentFile() { + return true; + } + @Override public boolean isLocallyUsed(@NotNull PsiNamedElement member) { - return myRefCountHolder.isReferenced(myFile); + return myRefCountHolder.isReferenced(member); } }; @@ -390,7 +395,7 @@ public class PostHighlightingPass extends TextEditorHighlightingPass { return UnusedSymbolLocalInspection.isInjected(element); } - public static HighlightInfo createUnusedSymbolInfo(PsiElement element, String message, final HighlightInfoType highlightInfoType) { + public static HighlightInfo createUnusedSymbolInfo(@NotNull PsiElement element, @Nullable String message, @NotNull final HighlightInfoType highlightInfoType) { HighlightInfo info = HighlightInfo.createHighlightInfo(highlightInfoType, element, message); UnusedDeclarationFixProvider[] fixProviders = Extensions.getExtensions(UnusedDeclarationFixProvider.EP_NAME); for (UnusedDeclarationFixProvider provider : fixProviders) { @@ -580,6 +585,9 @@ public class PostHighlightingPass extends TextEditorHighlightingPass { if (isImplicitUsage(method, progress)) { return true; } + if (!helper.shouldIgnoreUsagesInCurrentFile()) { + return !weAreSureThereAreNoUsages(method, progress, helper); + } } else { //class maybe used in some weird way, e.g. from XML, therefore the only constructor is used too @@ -606,26 +614,27 @@ public class PostHighlightingPass extends TextEditorHighlightingPass { String name = member.getName(); if (name == null) return false; SearchScope useScope = member.getUseScope(); - if (!(useScope instanceof GlobalSearchScope)) return false; - GlobalSearchScope scope = (GlobalSearchScope)useScope; - // some classes may have references from within XML outside dependent modules, e.g. our actions Project project = member.getProject(); - if (member instanceof PsiClass) scope = GlobalSearchScope.projectScope(project).uniteWith(scope); + if (useScope instanceof GlobalSearchScope) { + GlobalSearchScope scope = (GlobalSearchScope)useScope; + // some classes may have references from within XML outside dependent modules, e.g. our actions + if (member instanceof PsiClass) scope = GlobalSearchScope.projectScope(project).uniteWith(scope); - PsiSearchHelper.SearchCostResult cheapEnough = PsiSearchHelper.SERVICE.getInstance(project).isCheapEnoughToSearch(name, scope, - helper.shouldIgnoreUsagesInCurrentFile() ? member.getContainingFile() : null, - progress); - if (cheapEnough == PsiSearchHelper.SearchCostResult.TOO_MANY_OCCURRENCES) return false; + PsiSearchHelper.SearchCostResult cheapEnough = PsiSearchHelper.SERVICE.getInstance(project).isCheapEnoughToSearch(name, scope, + helper.shouldIgnoreUsagesInCurrentFile() ? member.getContainingFile() : null, + progress); + if (cheapEnough == PsiSearchHelper.SearchCostResult.TOO_MANY_OCCURRENCES) return false; - //search usages if it cheap - //if count is 0 there is no usages since we've called myRefCountHolder.isReferenced() before - if (cheapEnough == PsiSearchHelper.SearchCostResult.ZERO_OCCURRENCES) { - if (!canBeReferencedViaWeirdNames(member)) return true; + //search usages if it cheap + //if count is 0 there is no usages since we've called myRefCountHolder.isReferenced() before + if (cheapEnough == PsiSearchHelper.SearchCostResult.ZERO_OCCURRENCES) { + if (!canBeReferencedViaWeirdNames(member)) return true; + } } FindUsagesManager findUsagesManager = ((FindManagerImpl)FindManager.getInstance(project)).getFindUsagesManager(); FindUsagesHandler handler = new JavaFindUsagesHandler(member, new JavaFindUsagesHandlerFactory(project)); FindUsagesOptions findUsagesOptions = handler.getFindUsagesOptions(); - findUsagesOptions.searchScope = scope; + findUsagesOptions.searchScope = useScope; return !findUsagesManager.isUsed(member, findUsagesOptions); } @@ -684,7 +693,7 @@ public class PostHighlightingPass extends TextEditorHighlightingPass { if (aClass == null) return true; Boolean result = helper.unusedClassCache.get(aClass); if (result == null) { - result = !isReallyUsed(aClass, progress, helper); + result = isReallyUsed(aClass, progress, helper); helper.unusedClassCache.put(aClass, result); } return result; diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/local/GroovyPostHighlightingPass.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/local/GroovyPostHighlightingPass.java index 49c4c67fcec4..7476a4ed4648 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/local/GroovyPostHighlightingPass.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/local/GroovyPostHighlightingPass.java @@ -39,10 +39,7 @@ import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.profile.codeInspection.InspectionProjectProfileManager; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiMember; -import com.intellij.psi.PsiRecursiveElementWalkingVisitor; +import com.intellij.psi.*; import com.intellij.util.Processor; import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.groovy.codeInspection.GroovyInspectionBundle; @@ -90,6 +87,14 @@ public class GroovyPostHighlightingPass extends TextEditorHighlightingPass { } final UnusedDeclarationInspection deadCodeInspection = (UnusedDeclarationInspection)profile.getInspectionTool(UnusedDeclarationInspection.SHORT_NAME, myFile); final GlobalUsageHelper usageHelper = new GlobalUsageHelper() { + public boolean shouldIgnoreUsagesInCurrentFile() { + return false; + } + + public boolean isLocallyUsed(@NotNull PsiNamedElement member) { + return false; + } + @Override public boolean shouldCheckUsages(@NotNull PsiMember member) { return deadCodeInspection == null || !deadCodeInspection.isEntryPoint(member); @@ -115,7 +120,7 @@ public class GroovyPostHighlightingPass extends TextEditorHighlightingPass { PsiElement nameId = ((GrNamedElement)element).getNameIdentifierGroovy(); if (nameId.getNode().getElementType() == GroovyTokenTypes.mIDENT) { String name = ((GrNamedElement)element).getName(); - if (element instanceof GrTypeDefinition && PostHighlightingPass.isClassUsed((GrTypeDefinition)element, progress, usageHelper)) { + if (element instanceof GrTypeDefinition && !PostHighlightingPass.isClassUsed((GrTypeDefinition)element, progress, usageHelper)) { unusedDeclarations.add( PostHighlightingPass.createUnusedSymbolInfo(nameId, "Class " + name + " is unused", HighlightInfoType.UNUSED_SYMBOL)); } diff --git a/plugins/groovy/testdata/highlighting/GloballyUnusedSymbols.groovy b/plugins/groovy/testdata/highlighting/GloballyUnusedSymbols.groovy index 5ad85a2c8f6b..af47b7500098 100644 --- a/plugins/groovy/testdata/highlighting/GloballyUnusedSymbols.groovy +++ b/plugins/groovy/testdata/highlighting/GloballyUnusedSymbols.groovy @@ -10,7 +10,10 @@ class Bar { Bar getUsedPropertyGetter() {} - public static void main(String[] args) {} + public static void main(String[] args) { usedPrivately() } + + private static void usedPrivately() {} + private void unusedPrivately() {} } println new Bar().usedMethod().usedProperty From 894ef5292c1854e37cf8356c5cad4912e42fdeac Mon Sep 17 00:00:00 2001 From: peter Date: Tue, 31 Jan 2012 13:41:53 +0100 Subject: [PATCH 21/23] uncommented test (IDEA-69571) --- .../jetbrains/plugins/groovy/lang/GroovyHighlightingTest.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.groovy index 48231e862fe7..7b671247aa91 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.groovy @@ -417,7 +417,7 @@ public class GroovyHighlightingTest extends LightCodeInsightFixtureTestCase { doTest(new GroovyAssignabilityCheckInspection()); } - public void _testInnerClassConstructorThis() { + public void testInnerClassConstructorThis() { myFixture.enableInspections(new GroovyResultOfAssignmentUsedInspection()); myFixture.testHighlighting(true, true, true, getTestName(false) + ".groovy"); } From 57a2a49892a499437517e82acde608274b4d2afd Mon Sep 17 00:00:00 2001 From: peter Date: Tue, 31 Jan 2012 15:10:11 +0100 Subject: [PATCH 22/23] IDEA-80583 Wrong suggestion in qualified field type --- .../completion/JavaCompletionContributor.java | 2 +- .../completion/JavaCompletionData.java | 26 +++++++++---------- .../PsiJavaCodeReferenceElementImpl.java | 3 +++ .../normal/PackageInMemberType.java | 3 +++ .../normal/PackageInMemberType_after.java | 3 +++ .../completion/NormalCompletionTest.groovy | 14 +++------- 6 files changed, 27 insertions(+), 24 deletions(-) create mode 100644 java/java-tests/testData/codeInsight/completion/normal/PackageInMemberType.java create mode 100644 java/java-tests/testData/codeInsight/completion/normal/PackageInMemberType_after.java diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java index 94135a94b346..30f5c20fbef8 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java @@ -113,7 +113,7 @@ public class JavaCompletionContributor extends CompletionContributor { return new AndFilter(ElementClassFilter.CLASS, new NotFilter(new AssignableFromContextFilter())); } - if (JavaCompletionData.DECLARATION_START.isAcceptable(position, position) || + if (JavaCompletionData.DECLARATION_START.accepts(position) || JavaCompletionData.INSIDE_PARAMETER_LIST.accepts(position)) { return new OrFilter(ElementClassFilter.CLASS, ElementClassFilter.PACKAGE_FILTER); } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionData.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionData.java index 86d61df0968d..5d9d2a0155a4 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionData.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionData.java @@ -166,16 +166,18 @@ public class JavaCompletionData extends JavaAwareCompletionData{ defineScopeEquivalence(PsiMethod.class, JavaCodeFragment.class); } - public static final AndFilter DECLARATION_START = new AndFilter( - CLASS_BODY, - new OrFilter( - END_OF_BLOCK, - new LeftNeighbour(new OrFilter( - new SuperParentFilter(new ClassFilter(PsiModifierList.class)), - new AndFilter (new TokenTypeFilter(JavaTokenType.GT), - new SuperParentFilter(new ClassFilter(PsiTypeParameterList.class))))) - ), - new PatternFilter(not(psiElement().afterLeaf("@", ".")))); + public static final ElementPattern DECLARATION_START = psiElement().andNot(psiElement().afterLeaf("@", ".")). + andOr( + psiElement().and(new FilterPattern(CLASS_BODY)). + andOr( + new FilterPattern(END_OF_BLOCK), + psiElement().afterLeaf(or( + psiElement().inside(PsiModifierList.class), + psiElement().withElementType(JavaTokenType.GT).inside(PsiTypeParameterList.class) + ))), + psiElement().withParents(PsiJavaCodeReferenceElement.class, PsiTypeElement.class, PsiMember.class), + psiElement().withParents(PsiJavaCodeReferenceElement.class, PsiTypeElement.class, PsiClassLevelDeclarationStatement.class) + ); private void declareCompletionSpaces() { declareFinalScope(PsiFile.class); @@ -578,9 +580,7 @@ public class JavaCompletionData extends JavaAwareCompletionData{ .afterLeaf(psiElement().withText("(").withParent(psiElement(PsiParenthesizedExpression.class, PsiTypeCastExpression.class))) .accepts(position); - boolean declaration = DECLARATION_START.isAcceptable(position, position) || - psiElement().withParents(PsiJavaCodeReferenceElement.class, PsiTypeElement.class, PsiMember.class).accepts(position) || - psiElement().withParents(PsiJavaCodeReferenceElement.class, PsiTypeElement.class, PsiClassLevelDeclarationStatement.class).accepts(position); + boolean declaration = DECLARATION_START.accepts(position); if (START_FOR.accepts(position) || INSIDE_PARAMETER_LIST.accepts(position) && !AFTER_DOT.accepts(position) || VARIABLE_AFTER_FINAL.accepts(position) || diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiJavaCodeReferenceElementImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiJavaCodeReferenceElementImpl.java index c6923329dd83..87c91da8c509 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiJavaCodeReferenceElementImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiJavaCodeReferenceElementImpl.java @@ -775,6 +775,9 @@ public class PsiJavaCodeReferenceElementImpl extends CompositePsiElement impleme break; case CLASS_NAME_KIND: addClassFilter(filter); + if (isQualified()) { + filter.addFilter(ElementClassFilter.PACKAGE_FILTER); + } break; case PACKAGE_NAME_KIND: filter.addFilter(ElementClassFilter.PACKAGE_FILTER); diff --git a/java/java-tests/testData/codeInsight/completion/normal/PackageInMemberType.java b/java/java-tests/testData/codeInsight/completion/normal/PackageInMemberType.java new file mode 100644 index 000000000000..1080c468d330 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/PackageInMemberType.java @@ -0,0 +1,3 @@ +class Foo { + java.l +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/normal/PackageInMemberType_after.java b/java/java-tests/testData/codeInsight/completion/normal/PackageInMemberType_after.java new file mode 100644 index 000000000000..9db38bb5b106 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/normal/PackageInMemberType_after.java @@ -0,0 +1,3 @@ +class Foo { + java.lang. +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy index 9e74675a1ec2..e5203e7f1069 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/NormalCompletionTest.groovy @@ -1258,16 +1258,10 @@ public class ListUtils { } } - public void testNoGenericsWhenChoosingWithParen() { - configure() - myFixture.type 'Ma(' - checkResult() - } + public void testNoGenericsWhenChoosingWithParen() { doTest('Ma(') } - public void testNoClosingWhenChoosingWithParenBeforeIdentifier() { - configure() - myFixture.type '(' - checkResult() - } + public void testNoClosingWhenChoosingWithParenBeforeIdentifier() { doTest '(' } + + public void testPackageInMemberType() { doTest() } } From a55e821d83196cd4e964992c3ade16d43ead59e4 Mon Sep 17 00:00:00 2001 From: peter Date: Tue, 31 Jan 2012 17:44:31 +0100 Subject: [PATCH 23/23] IDEA-80577 Perforce: Offline Mode: no changes detected warning will be shown when reverting files which are modified without checkout even if there are changes made in the reverted file --- .../intellij/openapi/vcs/changes/actions/RollbackAction.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/RollbackAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/RollbackAction.java index 02b5bf548012..2e402f5e8cca 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/RollbackAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/RollbackAction.java @@ -100,17 +100,20 @@ public class RollbackAction extends AnAction implements DumbAware { FileDocumentManager.getInstance().saveAllDocuments(); List missingFiles = e.getData(ChangesListView.MISSING_FILES_DATA_KEY); + boolean hasChanges = false; if (missingFiles != null && !missingFiles.isEmpty()) { + hasChanges = true; new RollbackDeletionAction().actionPerformed(e); } List modifiedWithoutEditing = getModifiedWithoutEditing(e); if (modifiedWithoutEditing != null && !modifiedWithoutEditing.isEmpty()) { + hasChanges = true; rollbackModifiedWithoutEditing(project, modifiedWithoutEditing); } Change[] changes = getChanges(project, e); - if (changes != null) { + if (changes != null && (changes.length > 0 || !hasChanges)) { RollbackChangesDialog.rollbackChanges(project, Arrays.asList(changes)); } }