diff --git a/platform/lang-impl/src/com/intellij/codeEditor/printing/TextPainter.java b/platform/lang-impl/src/com/intellij/codeEditor/printing/TextPainter.java index e36efc870d41..b57def2b36be 100644 --- a/platform/lang-impl/src/com/intellij/codeEditor/printing/TextPainter.java +++ b/platform/lang-impl/src/com/intellij/codeEditor/printing/TextPainter.java @@ -27,7 +27,6 @@ import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; -import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; import com.intellij.psi.codeStyle.CodeStyleSettings; import com.intellij.psi.codeStyle.CodeStyleSettingsManager; @@ -93,8 +92,6 @@ public class TextPainter implements Printable { myFileName = fileName; mySegmentEnd = myDocument.getTextLength(); - PsiDocumentManager.getInstance(project).commitAllDocuments(); - myFileType = psiFile.getFileType(); diff --git a/platform/lang-impl/src/com/intellij/ide/projectView/impl/ProjectViewImpl.java b/platform/lang-impl/src/com/intellij/ide/projectView/impl/ProjectViewImpl.java index 08d9c320118f..9b63a06d17bc 100644 --- a/platform/lang-impl/src/com/intellij/ide/projectView/impl/ProjectViewImpl.java +++ b/platform/lang-impl/src/com/intellij/ide/projectView/impl/ProjectViewImpl.java @@ -253,8 +253,6 @@ public class ProjectViewImpl extends ProjectView implements PersistentStateCompo myViewContentPanel = new JPanel(); myPanel = new SimpleToolWindowPanel(true).setProvideQuickActions(false); myPanel.setContent(myViewContentPanel); - - myPanel.setBorder(new ToolWindow.Border(true, false, false, false)); } public String getName() { diff --git a/platform/platform-api/src/com/intellij/openapi/ui/MasterDetailsComponent.java b/platform/platform-api/src/com/intellij/openapi/ui/MasterDetailsComponent.java index 62c78bbb0f6c..c7d0dcbacf19 100644 --- a/platform/platform-api/src/com/intellij/openapi/ui/MasterDetailsComponent.java +++ b/platform/platform-api/src/com/intellij/openapi/ui/MasterDetailsComponent.java @@ -462,7 +462,7 @@ public abstract class MasterDetailsComponent implements Configurable, DetailsCom boolean hasFocus) { if (value instanceof MyNode) { final MyNode node = ((MyNode)value); - setIcon(node.getConfigurable().getIcon(expanded)); + setIcon(node.getIcon(expanded)); final Font font = UIUtil.getTreeFont(); if (node.isDisplayInBold()) { setFont(font.deriveFont(Font.BOLD)); @@ -699,8 +699,8 @@ public abstract class MasterDetailsComponent implements Configurable, DetailsCom throw new ConfigurationException("Name should contain non-space characters"); } if (names.contains(name)) { - final NamedConfigurable selectedConfugurable = getSelectedConfugurable(); - if (selectedConfugurable == null || !Comparing.strEqual(selectedConfugurable.getDisplayName(), name)) { + final NamedConfigurable selectedConfigurable = getSelectedConfugurable(); + if (selectedConfigurable == null || !Comparing.strEqual(selectedConfigurable.getDisplayName(), name)) { selectNodeInTree(node); } throw new ConfigurationException(CommonBundle.message("smth.already.exist.error.message", prefix, name), title); @@ -836,6 +836,16 @@ public abstract class MasterDetailsComponent implements Configurable, DetailsCom public void setDisplayInBold(boolean displayInBold) { myDisplayInBold = displayInBold; } + + @Nullable + public Icon getIcon(boolean expanded) { + // thanks to invokeLater() in TreeUtil.showAndSelect(), we can get calls to getIcon() after the tree has been disposed + final NamedConfigurable configurable = getConfigurable(); + if (configurable != null) { + return configurable.getIcon(expanded); + } + return null; + } } @SuppressWarnings({"ConstantConditions"}) diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/highlighter/EditorHighlighterFactoryImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/highlighter/EditorHighlighterFactoryImpl.java index e53742e1e2f4..4c32efd347a6 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/highlighter/EditorHighlighterFactoryImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/highlighter/EditorHighlighterFactoryImpl.java @@ -16,6 +16,7 @@ package com.intellij.openapi.editor.highlighter; import com.intellij.lang.Language; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.colors.EditorColorsManager; import com.intellij.openapi.editor.colors.EditorColorsScheme; import com.intellij.openapi.editor.ex.util.LexerEditorHighlighter; @@ -32,6 +33,8 @@ import org.jetbrains.annotations.Nullable; * @author yole */ public class EditorHighlighterFactoryImpl extends EditorHighlighterFactory { + private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.editor.highlighter.EditorHighlighterFactoryImpl"); + @Override public EditorHighlighter createEditorHighlighter(SyntaxHighlighter highlighter, final EditorColorsScheme colors) { if (highlighter == null) highlighter = new PlainSyntaxHighlighter(); @@ -74,8 +77,16 @@ public class EditorHighlighterFactoryImpl extends EditorHighlighterFactory { final ContentBasedFileSubstitutor[] processors = Extensions.getExtensions(ContentBasedFileSubstitutor.EP_NAME); SyntaxHighlighter highlighter = null; for (ContentBasedFileSubstitutor processor : processors) { - if (processor.isApplicable(project, vFile) && processor instanceof ContentBasedClassFileProcessor) { - highlighter = ((ContentBasedClassFileProcessor) processor).createHighlighter(project, vFile); + boolean applicable; + try { + applicable = processor.isApplicable(project, vFile); + } + catch (Exception e) { + LOG.error(e); + continue; + } + if (applicable && processor instanceof ContentBasedClassFileProcessor) { + highlighter = ((ContentBasedClassFileProcessor)processor).createHighlighter(project, vFile); } } if (highlighter == null) { diff --git a/platform/util/src/com/intellij/openapi/util/Comparing.java b/platform/util/src/com/intellij/openapi/util/Comparing.java index 95f5f8ab166a..4860c7ab53f7 100644 --- a/platform/util/src/com/intellij/openapi/util/Comparing.java +++ b/platform/util/src/com/intellij/openapi/util/Comparing.java @@ -140,6 +140,10 @@ public class Comparing { return o1 < o2 ? -1 : o1 == o2 ? 0 : 1; } + public static int compare(boolean o1, boolean o2) { + return o1 == o2 ? 0 : o1 ? 1 : -1; + } + public static int compare(int o1, int o2) { return o1 < o2 ? -1 : o1 == o2 ? 0 : 1; } diff --git a/plugins/android/resources/messages/AndroidBundle.properties b/plugins/android/resources/messages/AndroidBundle.properties index ba66305c6f68..a5203a77da24 100644 --- a/plugins/android/resources/messages/AndroidBundle.properties +++ b/plugins/android/resources/messages/AndroidBundle.properties @@ -36,7 +36,8 @@ activity.class.not.specified.error=Activity class not specified not.activity.subclass.error={0} is not an Activity subclass no.facet.error=No Android facet found for {0} android.logcat.tab.title=Logcat -android.logcat.title=Android Logcat +android.adb.logs.tab.title=ADB logs +android.logcat.title=Android android.logcat.error.dialog.title=Android Logcat Error android.logcat.color.page.name=Android Logcat warning.level.title=Warning diff --git a/plugins/android/src/META-INF/plugin.xml b/plugins/android/src/META-INF/plugin.xml index 1cfc42b1a4fd..008774bdaad1 100644 --- a/plugins/android/src/META-INF/plugin.xml +++ b/plugins/android/src/META-INF/plugin.xml @@ -181,7 +181,7 @@ - { return ApplicationManager.getApplication().runReadAction(new Computable() { @Nullable public PsiClass compute() { - return c.getSuperClass(); + return c.isValid() ? c.getSuperClass() : null; } }); } diff --git a/plugins/android/src/org/jetbrains/android/dom/manifest/ManifestDomFileDescription.java b/plugins/android/src/org/jetbrains/android/dom/manifest/ManifestDomFileDescription.java index acea0d6bb5f5..641838ee575c 100644 --- a/plugins/android/src/org/jetbrains/android/dom/manifest/ManifestDomFileDescription.java +++ b/plugins/android/src/org/jetbrains/android/dom/manifest/ManifestDomFileDescription.java @@ -42,7 +42,7 @@ public class ManifestDomFileDescription extends DomFileDescription { return false; } final Module module = ModuleUtil.findModuleForPsiElement(file); - return module == null || AndroidFacet.getInstance(module) != null; + return module == null || !module.isDisposed() && AndroidFacet.getInstance(module) != null; } protected void initializeFileDescription() { diff --git a/plugins/android/src/org/jetbrains/android/logcat/AdbErrors.java b/plugins/android/src/org/jetbrains/android/logcat/AdbErrors.java new file mode 100644 index 000000000000..4fd6125e134d --- /dev/null +++ b/plugins/android/src/org/jetbrains/android/logcat/AdbErrors.java @@ -0,0 +1,37 @@ +package org.jetbrains.android.logcat; + +import com.intellij.util.ArrayUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Eugene.Kudelevsky + */ +public class AdbErrors { + private AdbErrors() { + } + + private static volatile List ourAdbErrorList; + + @NotNull + public static synchronized String[] getErrors() { + return ourAdbErrorList != null ? ArrayUtil.toStringArray(ourAdbErrorList) : ArrayUtil.EMPTY_STRING_ARRAY; + } + + public static synchronized void clear() { + if (ourAdbErrorList != null) { + ourAdbErrorList.clear(); + } + } + + public static synchronized void reportError(@NotNull String message, @Nullable String tag) { + final String fullMessage = tag != null ? tag + ": " + message : message; + if (ourAdbErrorList == null) { + ourAdbErrorList = new ArrayList(); + } + ourAdbErrorList.add(fullMessage); + } +} diff --git a/plugins/android/src/org/jetbrains/android/logcat/AndroidLogcatToolWindowFactory.java b/plugins/android/src/org/jetbrains/android/logcat/AndroidLogcatToolWindowFactory.java index 82f5a22cdd84..389f0f05b1a6 100644 --- a/plugins/android/src/org/jetbrains/android/logcat/AndroidLogcatToolWindowFactory.java +++ b/plugins/android/src/org/jetbrains/android/logcat/AndroidLogcatToolWindowFactory.java @@ -16,8 +16,10 @@ package org.jetbrains.android.logcat; +import com.android.ddmlib.Log; import com.intellij.ProjectTopics; import com.intellij.execution.filters.HyperlinkInfo; +import com.intellij.execution.impl.ConsoleViewImpl; import com.intellij.execution.ui.ConsoleView; import com.intellij.execution.ui.ConsoleViewContentType; import com.intellij.facet.ProjectFacetManager; @@ -34,6 +36,7 @@ import com.intellij.openapi.wm.ex.ToolWindowManagerAdapter; import com.intellij.openapi.wm.ex.ToolWindowManagerEx; import com.intellij.ui.content.Content; import com.intellij.ui.content.ContentManager; +import com.intellij.ui.content.impl.ContentImpl; import com.intellij.util.messages.MessageBusConnection; import org.jetbrains.android.facet.AndroidFacet; import org.jetbrains.android.maven.AndroidMavenUtil; @@ -90,12 +93,34 @@ public class AndroidLogcatToolWindowFactory implements ToolWindowFactory { JPanel contentPanel = view.getContentPanel(); final ContentManager contentManager = toolWindow.getContentManager(); - final Content content = contentManager.getFactory().createContent(contentPanel, null, false); - content.setDisposer(view); - content.setCloseable(false); - content.setPreferredFocusableComponent(contentPanel); - contentManager.addContent(content); - contentManager.setSelectedContent(content, true); + + final Content logcatContent = + contentManager.getFactory().createContent(contentPanel, AndroidBundle.message("android.logcat.tab.title"), false); + logcatContent.setDisposer(view); + logcatContent.setCloseable(false); + logcatContent.setPreferredFocusableComponent(contentPanel); + contentManager.addContent(logcatContent); + contentManager.setSelectedContent(logcatContent, true); + + final ConsoleView console = new ConsoleViewImpl(project, false); + final Content adbLogsContent = new ContentImpl(console.getComponent(), AndroidBundle.message("android.adb.logs.tab.title"), false); + adbLogsContent.setCloseable(false); + contentManager.addContent(adbLogsContent); + + //noinspection UnnecessaryFullyQualifiedName + com.android.ddmlib.Log.setLogOutput(new Log.ILogOutput() { + @Override + public void printLog(Log.LogLevel logLevel, String tag, String message) { + reportAdbLogMessage(logLevel, tag, message, console); + } + + @Override + public void printAndPromptLog(Log.LogLevel logLevel, String tag, String message) { + // todo: should we show dialog? + reportAdbLogMessage(logLevel, tag, message, console); + } + }); + ApplicationManager.getApplication().invokeLater(new Runnable() { public void run() { view.activate(); @@ -107,6 +132,48 @@ public class AndroidLogcatToolWindowFactory implements ToolWindowFactory { }); } + private static void reportAdbLogMessage(Log.LogLevel logLevel, String tag, String message, @NotNull ConsoleView consoleView) { + if (message == null) { + return; + } + if (logLevel == null) { + logLevel = Log.LogLevel.INFO; + } + + if (logLevel == Log.LogLevel.ERROR || logLevel == Log.LogLevel.ASSERT) { + AdbErrors.reportError(message, tag); + } + + final ConsoleViewContentType contentType = toConsoleViewContentType(logLevel); + if (contentType == null) { + return; + } + + final String fullMessage = tag != null ? tag + ": " + message : message; + consoleView.print(fullMessage + '\n', contentType); + } + + @Nullable + private static ConsoleViewContentType toConsoleViewContentType(@NotNull Log.LogLevel logLevel) { + switch (logLevel) { + case VERBOSE: + return null; + case DEBUG: + return null; + case INFO: + return ConsoleViewContentType.getConsoleViewType(AndroidLogcatConstants.INFO); + case WARN: + return ConsoleViewContentType.getConsoleViewType(AndroidLogcatConstants.WARNING); + case ERROR: + return ConsoleViewContentType.getConsoleViewType(AndroidLogcatConstants.ERROR); + case ASSERT: + return ConsoleViewContentType.getConsoleViewType(AndroidLogcatConstants.ASSERT); + default: + assert false : "Unknown log level " + logLevel; + } + return null; + } + private static void checkFacetAndSdk(Project project, AndroidLogcatToolWindowView view) { final List facets = ProjectFacetManager.getInstance(project).getFacets(AndroidFacet.ID); final ConsoleView console = view.getLogConsole().getConsole(); diff --git a/plugins/android/src/org/jetbrains/android/sdk/AndroidSdkData.java b/plugins/android/src/org/jetbrains/android/sdk/AndroidSdkData.java index c63650b914fb..616a78281a17 100644 --- a/plugins/android/src/org/jetbrains/android/sdk/AndroidSdkData.java +++ b/plugins/android/src/org/jetbrains/android/sdk/AndroidSdkData.java @@ -18,6 +18,7 @@ package org.jetbrains.android.sdk; import com.android.ddmlib.AndroidDebugBridge; import com.android.ddmlib.DdmPreferences; +import com.android.ddmlib.Log; import com.android.sdklib.*; import com.android.sdklib.internal.project.ProjectProperties; import com.intellij.CommonBundle; @@ -33,6 +34,7 @@ import com.intellij.reference.SoftReference; import com.intellij.util.containers.HashMap; import gnu.trove.TIntObjectHashMap; import org.jetbrains.android.actions.AndroidEnableAdbServiceAction; +import org.jetbrains.android.logcat.AdbErrors; import org.jetbrains.android.util.AndroidCommonUtils; import org.jetbrains.android.util.AndroidUtils; import org.jetbrains.android.util.BufferingFileWrapper; @@ -182,12 +184,15 @@ public class AndroidSdkData { return getLocation().hashCode(); } + @SuppressWarnings("AssignmentToStaticFieldFromInstanceMethod") private boolean initializeDdmlib(@NotNull Project project) { ApplicationManager.getApplication().assertIsDispatchThread(); while (true) { final MyInitializeDdmlibTask task = new MyInitializeDdmlibTask(project); + AdbErrors.clear(); + Thread t = new Thread(new Runnable() { @Override public void run() { @@ -205,25 +210,33 @@ public class AndroidSdkData { boolean finished = task.isFinished(); - //noinspection AssignmentToStaticFieldFromInstanceMethod - myAdbCrashed = !finished; - if (task.isCanceled()) { + myAdbCrashed = !finished; forceInterrupt(t); return false; } - if (!finished) { - int result = Messages - .showOkCancelDialog(project, - "ADB not responding. Please, kill \"" + SdkConstants.FN_ADB + "\" process manually and click 'Retry'", - CommonBundle.getErrorTitle(), "&Retry", "&Cancel", Messages.getErrorIcon()); + myAdbCrashed = false; - if (result == 1) { + if (!finished) { + final String adbErrorString = combine(AdbErrors.getErrors()); + final int result = Messages.showDialog(project, "ADB not responding. You can wait more, or kill \"" + + SdkConstants.FN_ADB + + "\" process manually and click 'Restart'" + + (adbErrorString.length() > 0 ? "\nErrors from ADB:\n" + adbErrorString : ""), + CommonBundle.getErrorTitle(), new String[]{"&Wait more", "&Restart", "&Cancel"}, 0, + Messages.getErrorIcon()); + if (result == 2) { + // cancel + myAdbCrashed = true; forceInterrupt(t); return false; } - retryWas = true; + else if (result == 1) { + // restart + myAdbCrashed = true; + retryWas = true; + } } } @@ -236,6 +249,19 @@ public class AndroidSdkData { return true; } + @NotNull + private static String combine(@NotNull String[] strs) { + final StringBuilder builder = new StringBuilder(); + + for (String str : strs) { + if (builder.length() > 0) { + builder.append('\n'); + } + builder.append(str); + } + return builder.toString(); + } + @SuppressWarnings({"BusyWait"}) private static void forceInterrupt(Thread thread) { /* @@ -262,6 +288,7 @@ public class AndroidSdkData { synchronized (myDdmsLock) { if (!myDdmLibInitialized) { myDdmLibInitialized = true; + DdmPreferences.setLogLevel(Log.LogLevel.INFO.getStringValue()); DdmPreferences.setTimeOut(AndroidUtils.TIMEOUT); AndroidDebugBridge.init(AndroidEnableAdbServiceAction.isAdbServiceEnabled()); LOG.info("DDMLib initialized"); diff --git a/plugins/groovy/resources/inspectionDescriptions/GroovyUnusedAssignment.html b/plugins/groovy/resources/inspectionDescriptions/GroovyUnusedAssignment.html index f10ac59f3c47..d093014e3eab 100644 --- a/plugins/groovy/resources/inspectionDescriptions/GroovyUnusedAssignment.html +++ b/plugins/groovy/resources/inspectionDescriptions/GroovyUnusedAssignment.html @@ -2,6 +2,6 @@ This inspection reports on unnecessary Groovy assignment statement -
Powered by InspectorGroovy +
diff --git a/plugins/groovy/resources/inspectionDescriptions/GroovyUnusedIncOrDec.html b/plugins/groovy/resources/inspectionDescriptions/GroovyUnusedIncOrDec.html new file mode 100644 index 000000000000..13ba5a2d65e5 --- /dev/null +++ b/plugins/groovy/resources/inspectionDescriptions/GroovyUnusedIncOrDec.html @@ -0,0 +1,6 @@ + + +This inspection reports on unnecessary Groovy incrementing and decrementing expressions +
+ + diff --git a/plugins/groovy/src/META-INF/plugin.xml b/plugins/groovy/src/META-INF/plugin.xml index f16a34ca5f0c..956a30ddf366 100644 --- a/plugins/groovy/src/META-INF/plugin.xml +++ b/plugins/groovy/src/META-INF/plugin.xml @@ -408,6 +408,9 @@ + diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/actions/DumpGroovyControlFlowAction.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/actions/DumpGroovyControlFlowAction.java index 856b586a76c6..46f6694f41fd 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/actions/DumpGroovyControlFlowAction.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/actions/DumpGroovyControlFlowAction.java @@ -82,6 +82,7 @@ public class DumpGroovyControlFlowAction extends AnAction implements DumbAware { } private static void passInner(GrControlFlowOwner owner) { + System.out.println(owner.getText()); System.out.println(ControlFlowUtils.dumpControlFlow(owner.getControlFlow())); } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java index 4858ab7eae7a..3a81c5db8a1d 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/GroovyAnnotator.java @@ -290,7 +290,8 @@ public class GroovyAnnotator extends GroovyElementVisitor implements Annotator { for (PsiReference reference : ReferencesSearch.search(var, new LocalSearchScope(scope)).findAll()) { if (reference instanceof GrReferenceExpression && (PsiUtil.isLValue((GrReferenceExpression)reference) || - ((GrReferenceExpression)reference).getParent() instanceof GrPostfixExpression)) { + ((GrReferenceExpression)reference).getParent() instanceof GrUnaryExpression && + ((GrUnaryExpression)((GrReferenceExpression)reference).getParent()).isPostfix())) { if (hasAssignment) { return true; } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/GroovyInspectionBundle.properties b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/GroovyInspectionBundle.properties index b650ca7468de..28f439a39a95 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/GroovyInspectionBundle.properties +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/GroovyInspectionBundle.properties @@ -35,6 +35,8 @@ groovy.dfa.issues=Data Flow Issues unused.assignment=Unused Assignment unused.assignment.tooltip=Assignment is not used +unused.inc.dec=Unused Incrementing or Decrementing + unassigned.access=Variable Not Assigned unassigned.access.short.name=VariableNotAssigned unassigned.access.tooltip=Variable ''{0}'' might not be assigned @@ -77,3 +79,7 @@ rtype.cannot.contain.ltype=''{1}'' cannot contain ''{0}'' new.instance.of.singleton=New instance of class annotated with @groovy.lang.Singleton replace.new.expression.with.0.instance=Replace with ''{0}.instance'' getter.0.clashes.with.getter.1={0} clashes with {1} +unused.0=Unused {0} +remove.0=Remove {0} +replace.postfix.0.with.prefix.0=Replace postfix {0} with prefix {0} +replace.0.with.1=Replace {0} with binary {1} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/GroovyLocalInspectionBase.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/GroovyLocalInspectionBase.java index d22a439132a4..c46ea77c0c87 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/GroovyLocalInspectionBase.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/GroovyLocalInspectionBase.java @@ -22,6 +22,7 @@ import org.jetbrains.plugins.groovy.lang.psi.GrControlFlowOwner; import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor; import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementVisitor; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrClassInitializer; import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock; import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrOpenBlock; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; @@ -42,7 +43,6 @@ public abstract class GroovyLocalInspectionBase extends GroovySuppressableInspec return new GroovyPsiElementVisitor(new GroovyElementVisitor() { public void visitClosure(GrClosableBlock closure) { check(closure, problemsHolder); - } public void visitMethod(GrMethod method) { @@ -55,6 +55,11 @@ public abstract class GroovyLocalInspectionBase extends GroovySuppressableInspec public void visitFile(GroovyFileBase file) { check(file, problemsHolder); } + + @Override + public void visitClassInitializer(GrClassInitializer initializer) { + check(initializer.getBlock(), problemsHolder); + } }); } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/confusing/GrUnusedIncDecInspection.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/confusing/GrUnusedIncDecInspection.java new file mode 100644 index 000000000000..fadb994f0ec0 --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/confusing/GrUnusedIncDecInspection.java @@ -0,0 +1,241 @@ +/* + * 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.confusing; + +import com.intellij.codeInspection.LocalQuickFix; +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.codeInspection.ProblemHighlightType; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiElement; +import com.intellij.psi.tree.IElementType; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.groovy.codeInspection.BaseInspection; +import org.jetbrains.plugins.groovy.codeInspection.BaseInspectionVisitor; +import org.jetbrains.plugins.groovy.codeInspection.GroovyInspectionBundle; +import org.jetbrains.plugins.groovy.codeInspection.utils.ControlFlowUtils; +import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes; +import org.jetbrains.plugins.groovy.lang.psi.GrControlFlowOwner; +import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable; +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.api.statements.expressions.GrUnaryExpression; +import org.jetbrains.plugins.groovy.lang.psi.controlFlow.Instruction; +import org.jetbrains.plugins.groovy.lang.psi.controlFlow.ReadWriteVariableInstruction; +import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; + +import java.util.Iterator; +import java.util.List; + +/** + * @author Max Medvedev + */ +public class GrUnusedIncDecInspection extends BaseInspection { + private static final Logger LOG = Logger.getInstance(GrUnusedIncDecInspection.class); + @Override + protected BaseInspectionVisitor buildVisitor() { + return new GrUnusedIncDecInspectionVisitor(); + } + + @Override + public boolean isEnabledByDefault() { + return true; + } + + @Nls + @NotNull + public String getGroupDisplayName() { + return GroovyInspectionBundle.message("groovy.dfa.issues"); + } + + @Nls + @NotNull + public String getDisplayName() { + return GroovyInspectionBundle.message("unused.inc.dec"); + } + + @NonNls + @NotNull + public String getShortName() { + return "GroovyUnusedIncOrDec"; + } + + private static class GrUnusedIncDecInspectionVisitor extends BaseInspectionVisitor { + @Override + public void visitUnaryExpression(GrUnaryExpression expression) { + super.visitUnaryExpression(expression); + + IElementType opType = expression.getOperationTokenType(); + if (opType != GroovyTokenTypes.mINC && opType != GroovyTokenTypes.mDEC) return; + + GrExpression operand = expression.getOperand(); + if (!(operand instanceof GrReferenceExpression)) return; + + PsiElement resolved = ((GrReferenceExpression)operand).resolve(); + if (!(resolved instanceof GrVariable) || resolved instanceof GrField) return; + + final GrControlFlowOwner owner = ControlFlowUtils.findControlFlowOwner(expression); + assert owner != null; + GrControlFlowOwner ownerOfDeclaration = ControlFlowUtils.findControlFlowOwner(resolved); + if (ownerOfDeclaration != owner) return; + + final Instruction cur = ControlFlowUtils.findInstruction(operand, owner.getControlFlow()); + + if (cur == null) { + LOG.error("no instruction found in flow." + "operand: " + operand.getText() + " cfo: " + owner.getText()); + } + + //get write access for inc or dec + Iterable successors = cur.allSuccessors(); + Iterator iterator = successors.iterator(); + LOG.assertTrue(iterator.hasNext()); + Instruction writeAccess = iterator.next(); + LOG.assertTrue(!iterator.hasNext()); + + List accesses = ControlFlowUtils.findAccess((GrVariable)resolved, true, false, writeAccess); + + boolean allAreWrite = true; + for (ReadWriteVariableInstruction access : accesses) { + if (!access.isWrite()) { + allAreWrite = false; + break; + } + } + + + if (allAreWrite) { + if (expression.isPostfix() && PsiUtil.isExpressionUsed(expression)) { + registerError(expression.getOperationToken(), + GroovyInspectionBundle.message("unused.0", expression.getOperationToken().getText()), + new LocalQuickFix[]{new ReplacePostfixIncWithPrefixFix(expression), new RemoveIncOrDecFix(expression)}, + ProblemHighlightType.LIKE_UNUSED_SYMBOL); + } + else if (!PsiUtil.isExpressionUsed(expression)) { + registerError(expression.getOperationToken(), + GroovyInspectionBundle.message("unused.0", expression.getOperationToken().getText()), LocalQuickFix.EMPTY_ARRAY, + ProblemHighlightType.LIKE_UNUSED_SYMBOL); + } + } + } + + private static class RemoveIncOrDecFix implements LocalQuickFix { + private final String myMessage; + + public RemoveIncOrDecFix(GrUnaryExpression expression) { + myMessage = GroovyInspectionBundle.message("remove.0", expression.getOperationToken().getText()); + } + + @NotNull + @Override + public String getName() { + return myMessage; + } + + @NotNull + @Override + public String getFamilyName() { + return myMessage; + } + + @Override + public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { + GrUnaryExpression expr = findUnaryExpression(descriptor); + if (expr == null) return; + + expr.replaceWithExpression(expr.getOperand(), true); + } + } + + private static class ReplacePostfixIncWithPrefixFix implements LocalQuickFix { + private final String myMessage; + + public ReplacePostfixIncWithPrefixFix(GrUnaryExpression expression) { + myMessage = GroovyInspectionBundle.message("replace.postfix.0.with.prefix.0", expression.getOperationToken().getText()); + } + + @NotNull + @Override + public String getName() { + return myMessage; + } + + @NotNull + @Override + public String getFamilyName() { + return myMessage; + } + + @Override + public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { + GrUnaryExpression expr = findUnaryExpression(descriptor); + if (expr == null) return; + + GrExpression prefix = GroovyPsiElementFactory.getInstance(project) + .createExpressionFromText(expr.getOperationToken().getText() + expr.getOperand().getText()); + + expr.replaceWithExpression(prefix, true); + } + } + + private static class ReplaceIncDecWithBinary implements LocalQuickFix { + private final String myMessage; + + public ReplaceIncDecWithBinary(GrUnaryExpression expression) { + String opToken = expression.getOperationToken().getText(); + myMessage = GroovyInspectionBundle.message("replace.0.with.1", opToken, opToken.substring(0, 1)); + } + + @NotNull + @Override + public String getName() { + return myMessage; + } + + @NotNull + @Override + public String getFamilyName() { + return myMessage; + } + + @Override + public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { + GrUnaryExpression expr = findUnaryExpression(descriptor); + GrExpression newExpr = GroovyPsiElementFactory.getInstance(project) + .createExpressionFromText(expr.getOperand().getText() + expr.getOperationToken().getText().substring(0, 1) + "1"); + expr.replaceWithExpression(newExpr, true); + } + } + } + + @Nullable + private static GrUnaryExpression findUnaryExpression(ProblemDescriptor descriptor) { + GrUnaryExpression expr; + PsiElement element = descriptor.getPsiElement(); + if (element == null) return null; + PsiElement parent = element.getParent(); + IElementType opType = element.getNode().getElementType(); + if (opType != GroovyTokenTypes.mINC && opType != GroovyTokenTypes.mDEC) return null; + if (!(parent instanceof GrUnaryExpression)) return null; + expr = (GrUnaryExpression)parent; + return expr; + } +} + diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/threading/GroovyWhileLoopSpinsOnFieldInspection.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/threading/GroovyWhileLoopSpinsOnFieldInspection.java index 01548294a59b..a7429db7e476 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/threading/GroovyWhileLoopSpinsOnFieldInspection.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/threading/GroovyWhileLoopSpinsOnFieldInspection.java @@ -30,7 +30,10 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrBlockStatement; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrWhileStatement; import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrOpenBlock; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.*; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrBinaryExpression; +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.api.statements.expressions.GrUnaryExpression; import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; import javax.swing.*; @@ -94,9 +97,8 @@ public class GroovyWhileLoopSpinsOnFieldInspection extends BaseInspection { return true; } - if (condition instanceof GrPostfixExpression) { - final GrPostfixExpression postfixExpression = - (GrPostfixExpression) condition; + if (condition instanceof GrUnaryExpression && ((GrUnaryExpression)condition).isPostfix()) { + final GrUnaryExpression postfixExpression = (GrUnaryExpression) condition; final GrExpression operand = postfixExpression.getOperand(); return isSimpleFieldComparison(operand); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/unusedDef/UnusedDefInspection.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/unusedDef/UnusedDefInspection.java index fb8e2f0e6c12..da1bd028bac2 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/unusedDef/UnusedDefInspection.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/unusedDef/UnusedDefInspection.java @@ -24,7 +24,7 @@ import com.intellij.psi.PsiReference; import com.intellij.psi.impl.DebugUtil; import com.intellij.psi.search.LocalSearchScope; import com.intellij.psi.search.searches.ReferencesSearch; -import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.tree.IElementType; import com.intellij.util.Processor; import gnu.trove.TIntHashSet; import gnu.trove.TIntObjectHashMap; @@ -33,28 +33,25 @@ import gnu.trove.TObjectProcedure; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.codeInspection.GroovyInspectionBundle; import org.jetbrains.plugins.groovy.codeInspection.GroovyLocalInspectionBase; +import org.jetbrains.plugins.groovy.codeInspection.utils.ControlFlowUtils; +import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes; import org.jetbrains.plugins.groovy.lang.psi.GrControlFlowOwner; -import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrClassInitializer; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrAssignmentExpression; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrPostfixExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrUnaryExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; import org.jetbrains.plugins.groovy.lang.psi.controlFlow.Instruction; import org.jetbrains.plugins.groovy.lang.psi.controlFlow.ReadWriteVariableInstruction; import org.jetbrains.plugins.groovy.lang.psi.dataFlow.DFAEngine; import org.jetbrains.plugins.groovy.lang.psi.dataFlow.reachingDefs.ReachingDefinitionsDfaInstance; import org.jetbrains.plugins.groovy.lang.psi.dataFlow.reachingDefs.ReachingDefinitionsSemilattice; -import java.util.ArrayList; +import java.util.List; /** & @author ven @@ -86,7 +83,7 @@ public class UnusedDefInspection extends GroovyLocalInspectionBase { final ReachingDefinitionsDfaInstance dfaInstance = new ReachingDefinitionsDfaInstance(flow); final ReachingDefinitionsSemilattice lattice = new ReachingDefinitionsSemilattice(); final DFAEngine> engine = new DFAEngine>(flow, dfaInstance, lattice); - final ArrayList> dfaResult = engine.performDFA(); + final List> dfaResult = engine.performDFA(); final TIntHashSet unusedDefs = new TIntHashSet(); for (Instruction instruction : flow) { if (instruction instanceof ReadWriteVariableInstruction && ((ReadWriteVariableInstruction) instruction).isWrite()) { @@ -97,9 +94,9 @@ public class UnusedDefInspection extends GroovyLocalInspectionBase { for (int i = 0; i < dfaResult.size(); i++) { final Instruction instruction = flow[i]; if (instruction instanceof ReadWriteVariableInstruction) { - final ReadWriteVariableInstruction varInsn = (ReadWriteVariableInstruction) instruction; - if (!varInsn.isWrite()) { - final String varName = varInsn.getVariableName(); + final ReadWriteVariableInstruction varInst = (ReadWriteVariableInstruction) instruction; + if (!varInst.isWrite()) { + final String varName = varInst.getVariableName(); TIntObjectHashMap e = dfaResult.get(i); e.forEachValue(new TObjectProcedure() { public boolean execute(TIntHashSet reaching) { @@ -124,21 +121,8 @@ public class UnusedDefInspection extends GroovyLocalInspectionBase { final ReadWriteVariableInstruction instruction = (ReadWriteVariableInstruction)flow[num]; final PsiElement element = instruction.getElement(); if (element == null) return true; - PsiElement toHighlight = null; - if (isLocalAssignment(element) && isUsedInToplevelFlowOnly(element)) { - if (element instanceof GrReferenceExpression) { - PsiElement parent = element.getParent(); - if (parent instanceof GrAssignmentExpression) { - toHighlight = ((GrAssignmentExpression)parent).getLValue(); - } - if (parent instanceof GrPostfixExpression) { - toHighlight = parent; - } - } - else if (element instanceof GrVariable) { - toHighlight = ((GrVariable)element).getNameIdentifierGroovy(); - } - if (toHighlight == null) toHighlight = element; + if (isLocalAssignment(element) && isUsedInTopLevelFlowOnly(element) && !isIncOrDec(element)) { + PsiElement toHighlight = getHighlightElement(element); problemsHolder.registerProblem(toHighlight, GroovyInspectionBundle.message("unused.assignment.tooltip"), ProblemHighlightType.LIKE_UNUSED_SYMBOL); } @@ -147,25 +131,52 @@ public class UnusedDefInspection extends GroovyLocalInspectionBase { }); } - private static boolean isUsedInToplevelFlowOnly(PsiElement element) { + private static PsiElement getHighlightElement(PsiElement element) { + PsiElement toHighlight = null; + if (element instanceof GrReferenceExpression) { + PsiElement parent = element.getParent(); + if (parent instanceof GrAssignmentExpression) { + toHighlight = ((GrAssignmentExpression)parent).getLValue(); + } + if (parent instanceof GrUnaryExpression && ((GrUnaryExpression)parent).isPostfix()) { + toHighlight = parent; + } + } + else if (element instanceof GrVariable) { + toHighlight = ((GrVariable)element).getNameIdentifierGroovy(); + } + if (toHighlight == null) toHighlight = element; + return toHighlight; + } + + private static boolean isIncOrDec(PsiElement element) { + PsiElement parent = element.getParent(); + if (!(parent instanceof GrUnaryExpression)) return false; + + IElementType type = ((GrUnaryExpression)parent).getOperationTokenType(); + return type == GroovyTokenTypes.mINC || type == GroovyTokenTypes.mDEC; + } + + private static boolean isUsedInTopLevelFlowOnly(PsiElement element) { GrVariable var = null; if (element instanceof GrVariable) { - var = (GrVariable) element; - } else if (element instanceof GrReferenceExpression) { - final PsiElement resolved = ((GrReferenceExpression) element).resolve(); - if (resolved instanceof GrVariable) var = (GrVariable) resolved; + var = (GrVariable)element; + } + else if (element instanceof GrReferenceExpression) { + final PsiElement resolved = ((GrReferenceExpression)element).resolve(); + if (resolved instanceof GrVariable) var = (GrVariable)resolved; } if (var != null) { - final GroovyPsiElement scope = getScope(var); + final GroovyPsiElement scope = ControlFlowUtils.findControlFlowOwner(var); if (scope == null) { PsiFile file = var.getContainingFile(); - LOG.error(file == null ? "no file??? var of type"+ var.getClass().getCanonicalName() : DebugUtil.psiToString(file, true, false)); + LOG.error(file == null ? "no file??? var of type" + var.getClass().getCanonicalName() : DebugUtil.psiToString(file, true, false)); } return ReferencesSearch.search(var, new LocalSearchScope(scope)).forEach(new Processor() { public boolean process(PsiReference ref) { - return getScope(ref.getElement()) == scope; + return ControlFlowUtils.findControlFlowOwner(ref.getElement()) == scope; } }); } @@ -173,17 +184,14 @@ public class UnusedDefInspection extends GroovyLocalInspectionBase { return true; } - @Nullable - private static GroovyPsiElement getScope(PsiElement var) { - return PsiTreeUtil.getContextOfType(var, GrClosableBlock.class, GrMethod.class, GrClassInitializer.class, GroovyFileBase.class); - } private static boolean isLocalAssignment(PsiElement element) { if (element instanceof GrVariable) { - return isLocalVariable((GrVariable) element, false); - } else if (element instanceof GrReferenceExpression) { - final PsiElement resolved = ((GrReferenceExpression) element).resolve(); - return resolved instanceof GrVariable && isLocalVariable((GrVariable) resolved, true); + return isLocalVariable((GrVariable)element, false); + } + else if (element instanceof GrReferenceExpression) { + final PsiElement resolved = ((GrReferenceExpression)element).resolve(); + return resolved instanceof GrVariable && isLocalVariable((GrVariable)resolved, true); } return false; diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/utils/ControlFlowUtils.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/utils/ControlFlowUtils.java index 36583cf8bba0..18fb9b913a0c 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/utils/ControlFlowUtils.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/utils/ControlFlowUtils.java @@ -40,7 +40,6 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.branch.GrReturnState import org.jetbrains.plugins.groovy.lang.psi.api.statements.branch.GrThrowStatement; import org.jetbrains.plugins.groovy.lang.psi.api.statements.clauses.GrCaseSection; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrPostfixExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrUnaryExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; @@ -54,7 +53,6 @@ import org.jetbrains.plugins.groovy.lang.psi.dataFlow.DFAEngine; import org.jetbrains.plugins.groovy.lang.psi.dataFlow.DfaInstance; import org.jetbrains.plugins.groovy.lang.psi.dataFlow.Semilattice; import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; -import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringUtil; import java.util.*; @@ -499,7 +497,6 @@ public class ControlFlowUtils { public static boolean isIncOrDecOperand(GrReferenceExpression referenceExpression) { final PsiElement parent = referenceExpression.getParent(); - if (parent instanceof GrPostfixExpression) return true; if (parent instanceof GrUnaryExpression) { final IElementType opType = ((GrUnaryExpression)parent).getOperationTokenType(); return opType == GroovyTokenTypes.mDEC || opType == GroovyTokenTypes.mINC; @@ -650,10 +647,10 @@ public class ControlFlowUtils { @Nullable public static GrControlFlowOwner findControlFlowOwner(PsiElement place) { if (place instanceof GrCodeBlock) { - place = place.getParent(); + place = place.getContext(); } while (true) { - place = place.getParent(); + place = place.getContext(); if (place == null) return null; if (place instanceof GrClosableBlock) return (GrClosableBlock)place; if (place instanceof GrMethod) return ((GrMethod)place).getBlock(); @@ -675,22 +672,28 @@ public class ControlFlowUtils { } public static List findAccess(GrVariable local, final PsiElement place, boolean ahead, boolean writeAccessOnly) { - LOG.assertTrue(GroovyRefactoringUtil.isLocalVariable(local), local.getClass()); + LOG.assertTrue(!(local instanceof GrField), local.getClass()); final GrControlFlowOwner owner = findControlFlowOwner(local); assert owner != null; final Instruction cur = findInstruction(place, owner.getControlFlow()); - if (cur == null) throw new IllegalArgumentException("place is not in the flow"); + if (cur == null) { + throw new IllegalArgumentException("place is not in the flow"); + } + return findAccess(local, ahead, writeAccessOnly, cur); + } + + public static List findAccess(GrVariable local, boolean ahead, boolean writeAccessOnly, Instruction cur) { String name = local.getName(); - + final ArrayList result = new ArrayList(); final HashSet visited = new HashSet(); - + visited.add(cur); - + Queue queue = new ArrayDeque(); for (Instruction i : ahead ? cur.allSuccessors() : cur.allPredecessors()) { @@ -698,11 +701,11 @@ public class ControlFlowUtils { queue.add(i); } } - + while (true) { Instruction instruction = queue.poll(); if (instruction == null) break; - + if (instruction instanceof ReadWriteVariableInstruction) { ReadWriteVariableInstruction rw = (ReadWriteVariableInstruction)instruction; if (name.equals(rw.getVariableName())) { @@ -710,13 +713,13 @@ public class ControlFlowUtils { result.add(rw); continue; } - + if (!writeAccessOnly) { result.add(rw); } } } - + for (Instruction i : ahead ? instruction.allSuccessors() : instruction.allPredecessors()) { if (visited.add(i)) { queue.add(i); @@ -726,9 +729,9 @@ public class ControlFlowUtils { return result; } - + @Nullable - private static Instruction findInstruction(final PsiElement place, Instruction[] controlFlow) { + public static Instruction findInstruction(final PsiElement place, Instruction[] controlFlow) { return ContainerUtil.find(controlFlow, new Condition() { @Override public boolean value(Instruction instruction) { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/utils/EquivalenceChecker.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/utils/EquivalenceChecker.java index fdde5dd99efd..c67351f4dced 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/utils/EquivalenceChecker.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/utils/EquivalenceChecker.java @@ -445,8 +445,8 @@ public class EquivalenceChecker { return prefixExpressionsAreEquivalent((GrUnaryExpression) expToCompare1, (GrUnaryExpression) expToCompare2); case POSTFIX_EXPRESSION: - return postfixExpressionsAreEquivalent((GrPostfixExpression) expToCompare1, - (GrPostfixExpression) expToCompare2); + return postfixExpressionsAreEquivalent((GrUnaryExpression) expToCompare1, + (GrUnaryExpression) expToCompare2); case BINARY_EXPRESSION: return binaryExpressionsAreEquivalent((GrBinaryExpression) expToCompare1, (GrBinaryExpression) expToCompare2); @@ -674,8 +674,8 @@ public class EquivalenceChecker { return expressionsAreEquivalent(operand1, operand2); } - private static boolean postfixExpressionsAreEquivalent(@NotNull GrPostfixExpression postfixExp1, - @NotNull GrPostfixExpression postfixExp2) { + private static boolean postfixExpressionsAreEquivalent(@NotNull GrUnaryExpression postfixExp1, + @NotNull GrUnaryExpression postfixExp2) { final IElementType sign1 = postfixExp1.getOperationTokenType(); final IElementType sign2 = postfixExp2.getOperationTokenType(); if (!sign1.equals(sign2)) { @@ -690,7 +690,7 @@ public class EquivalenceChecker { @NotNull GrBinaryExpression binaryExp2) { final IElementType sign1 = binaryExp1.getOperationTokenType(); final IElementType sign2 = binaryExp2.getOperationTokenType(); - if (sign1 == null || sign2 == null || !sign1.equals(sign2)) { + if (!sign1.equals(sign2)) { return false; } final GrExpression lhs1 = binaryExp1.getLeftOperand(); @@ -834,11 +834,8 @@ public class EquivalenceChecker { if (exp instanceof GrMethodCall) { return CALL_EXPRESSION; } - if (exp instanceof GrPostfixExpression) { - return POSTFIX_EXPRESSION; - } if (exp instanceof GrUnaryExpression) { - return PREFIX_EXPRESSION; + return ((GrUnaryExpression)exp).isPostfix() ? POSTFIX_EXPRESSION : PREFIX_EXPRESSION; } if (exp instanceof GrAssignmentExpression) { return ASSIGNMENT_EXPRESSION; diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/utils/SideEffectChecker.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/utils/SideEffectChecker.java index 13cc60aaf959..6f4946169677 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/utils/SideEffectChecker.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/utils/SideEffectChecker.java @@ -20,7 +20,10 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; import org.jetbrains.plugins.groovy.lang.psi.GroovyRecursiveElementVisitor; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.*; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrAssignmentExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrNewExpression; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrUnaryExpression; import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression; public class SideEffectChecker { @@ -71,19 +74,6 @@ public class SideEffectChecker { mayHaveSideEffects = true; } - public void visitPostfixExpression( - @NotNull GrPostfixExpression expression) { - if (mayHaveSideEffects) { - return; - } - super.visitPostfixExpression(expression); - final IElementType tokenType = expression.getOperationTokenType(); - if (tokenType.equals(GroovyTokenTypes.mINC) || - tokenType.equals(GroovyTokenTypes.mDEC)) { - mayHaveSideEffects = true; - } - } - public void visitUnaryExpression( @NotNull GrUnaryExpression expression) { if (mayHaveSideEffects) { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/formatter/processors/GroovySpacingProcessor.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/formatter/processors/GroovySpacingProcessor.java index 18c08d51d08a..970d869780a1 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/formatter/processors/GroovySpacingProcessor.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/formatter/processors/GroovySpacingProcessor.java @@ -484,17 +484,8 @@ public class GroovySpacingProcessor extends GroovyElementVisitor { @Override public void visitUnaryExpression(GrUnaryExpression expression) { - //prefix only - LOG.assertTrue(!(expression instanceof GrPostfixExpression)); - - if (expression.getOperationToken() == myChild1) { - createSpaceInCode(mySettings.SPACE_AROUND_UNARY_OPERATOR); - } - } - - @Override - public void visitPostfixExpression(GrPostfixExpression expression) { - if (expression.getOperationToken() == myChild2) { + if (!expression.isPostfix() && expression.getOperationToken() == myChild1 || + expression.isPostfix() && expression.getOperationToken() == myChild2) { createSpaceInCode(mySettings.SPACE_AROUND_UNARY_OPERATOR); } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/utils/ParenthesesUtils.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/utils/ParenthesesUtils.java index cf87e09df314..4c354de1c83a 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/utils/ParenthesesUtils.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/utils/ParenthesesUtils.java @@ -104,17 +104,14 @@ public class ParenthesesUtils { expression instanceof GrNewExpression) { return TYPE_CAST_PRECEDENCE; } - if (expression instanceof GrPostfixExpression) { - return POSTFIX_PRECEDENCE; - } if (expression instanceof GrUnaryExpression) { - return PREFIX_PRECEDENCE; + return ((GrUnaryExpression)expression).isPostfix()?POSTFIX_PRECEDENCE:PREFIX_PRECEDENCE; } if (expression instanceof GrBinaryExpression) { final GrBinaryExpression binaryExpression = (GrBinaryExpression) expression; final IElementType sign = binaryExpression.getOperationTokenType(); - if (sign != null) return precedenceForBinaryOperator(sign); + return precedenceForBinaryOperator(sign); } if (expression instanceof GrConditionalExpression) { return CONDITIONAL_PRECEDENCE; diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/lexer/TokenSets.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/lexer/TokenSets.java index b04214d900aa..d09ec7ff336d 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/lexer/TokenSets.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/lexer/TokenSets.java @@ -201,14 +201,4 @@ public abstract class TokenSets { ); public static final TokenSet SHIFT_SIGNS = TokenSet.create(COMPOSITE_LSHIFT_SIGN, COMPOSITE_RSHIFT_SIGN, COMPOSITE_TRIPLE_SHIFT_SIGN); - - public static final TokenSet POSTFIXES = TokenSet.create(mINC,mDEC); - public static final TokenSet PREFIXES = TokenSet.create( - mPLUS, - mMINUS, - mINC, - mDEC, - mBNOT, - mLNOT - ); } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/GroovyElementTypes.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/GroovyElementTypes.java index e1d73e4cc191..0ecc5f9e99f6 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/GroovyElementTypes.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/GroovyElementTypes.java @@ -222,7 +222,6 @@ public interface GroovyElementTypes extends GroovyTokenTypes, GroovyDocElementTy GroovyElementType CAST_EXPRESSION = new GroovyElementType("cast expression"); GroovyElementType SAFE_CAST_EXPRESSION = new GroovyElementType("safe cast expression"); GroovyElementType INSTANCEOF_EXPRESSION = new GroovyElementType("instanceof expression"); - GroovyElementType POSTFIX_EXPRESSION = new GroovyElementType("Postfix expression"); GroovyElementType PATH_PROPERTY_REFERENCE = new GroovyElementType("Property reference"); GroovyElementType PATH_METHOD_CALL = new GroovyElementType("Method call"); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/GroovyPsiCreator.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/GroovyPsiCreator.java index 30c697c579a0..6f5d0be5057f 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/GroovyPsiCreator.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/GroovyPsiCreator.java @@ -28,7 +28,10 @@ import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyASTPsiElementImpl; import org.jetbrains.plugins.groovy.lang.psi.impl.auxiliary.GrLabelImpl; import org.jetbrains.plugins.groovy.lang.psi.impl.auxiliary.GrListOrMapImpl; import org.jetbrains.plugins.groovy.lang.psi.impl.auxiliary.GrThrowsClauseImpl; -import org.jetbrains.plugins.groovy.lang.psi.impl.auxiliary.annotation.*; +import org.jetbrains.plugins.groovy.lang.psi.impl.auxiliary.annotation.GrAnnotationArgumentListImpl; +import org.jetbrains.plugins.groovy.lang.psi.impl.auxiliary.annotation.GrAnnotationArrrayInitializerImpl; +import org.jetbrains.plugins.groovy.lang.psi.impl.auxiliary.annotation.GrAnnotationImpl; +import org.jetbrains.plugins.groovy.lang.psi.impl.auxiliary.annotation.GrAnnotationNameValuePairImpl; import org.jetbrains.plugins.groovy.lang.psi.impl.auxiliary.modifiers.GrModifierListImpl; import org.jetbrains.plugins.groovy.lang.psi.impl.statements.*; import org.jetbrains.plugins.groovy.lang.psi.impl.statements.arguments.GrArgumentLabelImpl; @@ -218,7 +221,6 @@ public class GroovyPsiCreator implements GroovyElementTypes { if (elem == POWER_EXPRESSION) return new GrPowerExpressionImpl(node); if (elem == POWER_EXPRESSION_SIMPLE) return new GrPowerExpressionImpl(node); if (elem == UNARY_EXPRESSION) return new GrUnaryExpressionImpl(node); - if (elem == POSTFIX_EXPRESSION) return new GrPostfixExprImpl(node); if (elem == CAST_EXPRESSION) return new GrTypeCastExpressionImpl(node); if (elem == SAFE_CAST_EXPRESSION) return new GrSafeCastExpressionImpl(node); if (elem == INSTANCEOF_EXPRESSION) return new GrInstanceofExpressionImpl(node); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/parsing/statements/expressions/arithmetic/PostfixExpression.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/parsing/statements/expressions/arithmetic/PostfixExpression.java index e8edbafc9d0b..5ab5c9efd0a9 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/parsing/statements/expressions/arithmetic/PostfixExpression.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/parsing/statements/expressions/arithmetic/PostfixExpression.java @@ -40,9 +40,9 @@ public class PostfixExpression implements GroovyElementTypes { } private static void subParse(PsiBuilder builder, PsiBuilder.Marker marker) { - if (ParserUtils.getToken(builder, TokenSets.POSTFIXES)) { + if (ParserUtils.getToken(builder, TokenSets.POSTFIX_UNARY_OP_SET)) { PsiBuilder.Marker newMarker = marker.precede(); - marker.done(POSTFIX_EXPRESSION); + marker.done(UNARY_EXPRESSION); subParse(builder, newMarker); } else { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/parsing/statements/expressions/arithmetic/UnaryExpression.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/parsing/statements/expressions/arithmetic/UnaryExpression.java index 64d4a3c3c515..d215334597f7 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/parsing/statements/expressions/arithmetic/UnaryExpression.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/parser/parsing/statements/expressions/arithmetic/UnaryExpression.java @@ -30,7 +30,7 @@ public class UnaryExpression implements GroovyElementTypes { public static boolean parse(PsiBuilder builder, GroovyParser parser) { PsiBuilder.Marker marker = builder.mark(); - if (ParserUtils.getToken(builder, TokenSets.PREFIXES)) { + if (ParserUtils.getToken(builder, TokenSets.UNARY_OP_SET)) { ParserUtils.getToken(builder, mNLS); parse(builder, parser); marker.done(UNARY_EXPRESSION); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/GroovyElementVisitor.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/GroovyElementVisitor.java index cdd57b7486ec..872546b7894a 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/GroovyElementVisitor.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/GroovyElementVisitor.java @@ -168,10 +168,6 @@ public abstract class GroovyElementVisitor { visitExpression(expression); } - public void visitPostfixExpression(GrPostfixExpression expression) { - visitUnaryExpression(expression); - } - public void visitRegexExpression(GrRegex expression) { visitGStringExpression(expression); } @@ -443,4 +439,8 @@ public abstract class GroovyElementVisitor { public void visitTypeParameterList(GrTypeParameterList list) { visitElement(list); } + + public void visitClassInitializer(GrClassInitializer initializer) { + visitElement(initializer); + } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrPostfixExpression.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrPostfixExpression.java deleted file mode 100644 index de2bb73e0d4d..000000000000 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrPostfixExpression.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2000-2009 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.lang.psi.api.statements.expressions; - -/** - * @author ilyas - */ -public interface GrPostfixExpression extends GrUnaryExpression { -} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrUnaryExpression.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrUnaryExpression.java index ef4a8b6b5b85..03900c94f287 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrUnaryExpression.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/api/statements/expressions/GrUnaryExpression.java @@ -35,4 +35,6 @@ public interface GrUnaryExpression extends GrExpression, PsiPolyVariantReference @NotNull @Override GroovyResolveResult[] multiResolve(final boolean incompleteCode); + + boolean isPostfix(); } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/controlFlow/ReadWriteVariableInstruction.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/controlFlow/ReadWriteVariableInstruction.java index 247cdd929346..e68eddd8fa56 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/controlFlow/ReadWriteVariableInstruction.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/controlFlow/ReadWriteVariableInstruction.java @@ -23,13 +23,16 @@ import org.jetbrains.plugins.groovy.lang.psi.controlFlow.impl.InstructionImpl; * @author ven */ public class ReadWriteVariableInstruction extends InstructionImpl { + public static final int WRITE = -1; + public static final int READ = 1; + private final boolean myIsWrite; private final String myName; - public ReadWriteVariableInstruction(@NotNull String varName, PsiElement element, int num, boolean isWrite) { + public ReadWriteVariableInstruction(@NotNull String varName, PsiElement element, int num, int accessType) { super(element, num); myName = varName; - myIsWrite = isWrite; + myIsWrite = accessType == WRITE; } @NotNull public String getVariableName() { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/controlFlow/impl/ControlFlowBuilder.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/controlFlow/impl/ControlFlowBuilder.java index 2e4f31e52201..1fea9d257838 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/controlFlow/impl/ControlFlowBuilder.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/controlFlow/impl/ControlFlowBuilder.java @@ -20,6 +20,7 @@ import com.intellij.openapi.util.Pair; import com.intellij.psi.*; import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.containers.hash.HashSet; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.codeInspection.utils.ControlFlowUtils; import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes; @@ -51,6 +52,9 @@ import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; import java.util.*; +import static org.jetbrains.plugins.groovy.lang.psi.controlFlow.ReadWriteVariableInstruction.READ; +import static org.jetbrains.plugins.groovy.lang.psi.controlFlow.ReadWriteVariableInstruction.WRITE; + /** * @author ven */ @@ -107,7 +111,7 @@ public class ControlFlowBuilder extends GroovyRecursiveElementVisitor { final PsiElement lbrace = block.getLBrace(); if (lbrace != null && parent instanceof GrMethod) { for (GrParameter parameter : ((GrMethod)parent).getParameters()) { - addNode(new ReadWriteVariableInstruction(parameter.getName(), parameter, myInstructionNumber++, true)); + addNode(new ReadWriteVariableInstruction(parameter.getName(), parameter, myInstructionNumber++, WRITE)); } } super.visitOpenBlock(block); @@ -185,7 +189,7 @@ public class ControlFlowBuilder extends GroovyRecursiveElementVisitor { private void buildFlowForClosure(final GrClosableBlock closure) { for (GrParameter parameter : closure.getAllParameters()) { - addNode(new ReadWriteVariableInstruction(parameter.getName(), parameter, myInstructionNumber++, true)); + addNode(new ReadWriteVariableInstruction(parameter.getName(), parameter, myInstructionNumber++, WRITE)); } final Set names = new LinkedHashSet(); @@ -207,7 +211,7 @@ public class ControlFlowBuilder extends GroovyRecursiveElementVisitor { names.add("owner"); for (String name : names) { - addNode(new ReadWriteVariableInstruction(name, closure.getLBrace(), myInstructionNumber++, true)); + addNode(new ReadWriteVariableInstruction(name, closure.getLBrace(), myInstructionNumber++, WRITE)); } PsiElement child = closure.getFirstChild(); @@ -241,12 +245,26 @@ public class ControlFlowBuilder extends GroovyRecursiveElementVisitor { //do not go inside closures except gstring injections if (closure.getParent() instanceof GrStringInjection) { super.visitClosure(closure); + return; } - else { - //create instruction for closure to use it in inline local refactoring - final InstructionImpl i = new InstructionImpl(closure, myInstructionNumber++); + + Set names = new HashSet(); + + ReadWriteVariableInstruction[] reads = ControlFlowBuilderUtil.getReadsWithoutPriorWrites(closure.getControlFlow()); + for (ReadWriteVariableInstruction read : reads) { + names.add(read.getVariableName()); + } + + for (String name : names) { + ReadWriteVariableInstruction i = new ReadWriteVariableInstruction(name, closure, myInstructionNumber++, READ); addNode(i); + checkPending(i); } + + + InstructionImpl i = new InstructionImpl(closure, myInstructionNumber++); + addNode(i); + checkPending(i); } public void visitBreakStatement(GrBreakStatement breakStatement) { @@ -364,7 +382,7 @@ public class ControlFlowBuilder extends GroovyRecursiveElementVisitor { if (lValue instanceof GrReferenceExpression) { String referenceName = ((GrReferenceExpression)lValue).getReferenceName(); if (referenceName != null) { - ReadWriteVariableInstruction instruction = new ReadWriteVariableInstruction(referenceName, lValue, myInstructionNumber++, false); + ReadWriteVariableInstruction instruction = new ReadWriteVariableInstruction(referenceName, lValue, myInstructionNumber++, READ); addNode(instruction); checkPending(instruction); } @@ -413,17 +431,14 @@ public class ControlFlowBuilder extends GroovyRecursiveElementVisitor { if (name == null) return; if (ControlFlowUtils.isIncOrDecOperand(refExpr) && !myAssertionsOnly) { - final ReadWriteVariableInstruction i = new ReadWriteVariableInstruction(name, refExpr, myInstructionNumber++, false); + final InstructionImpl i = new ReadWriteVariableInstruction(name, refExpr, myInstructionNumber++, READ); addNode(i); - addNode(new ReadWriteVariableInstruction(name, refExpr, myInstructionNumber++, true)); - if (refExpr.getParent() instanceof GrUnaryExpression) { - addNode(new ReadWriteVariableInstruction(name, refExpr, myInstructionNumber++, false)); - } + addNode(new ReadWriteVariableInstruction(name, refExpr, myInstructionNumber++, WRITE)); checkPending(i); } else { - final ReadWriteVariableInstruction i = - new ReadWriteVariableInstruction(name, refExpr, myInstructionNumber++, !myAssertionsOnly && PsiUtil.isLValue(refExpr)); + boolean isWrite = !myAssertionsOnly && PsiUtil.isLValue(refExpr); + final InstructionImpl i = new ReadWriteVariableInstruction(name, refExpr, myInstructionNumber++, isWrite ? WRITE : READ); addNode(i); checkPending(i); } @@ -563,10 +578,10 @@ public class ControlFlowBuilder extends GroovyRecursiveElementVisitor { } GrVariable variable = clause.getDeclaredVariable(); if (variable != null) { - ReadWriteVariableInstruction writeInsn = - new ReadWriteVariableInstruction(variable.getName(), variable, myInstructionNumber++, true); - checkPending(writeInsn); - addNode(writeInsn); + ReadWriteVariableInstruction writeInst = + new ReadWriteVariableInstruction(variable.getName(), variable, myInstructionNumber++, WRITE); + checkPending(writeInst); + addNode(writeInst); } } @@ -755,6 +770,7 @@ public class ControlFlowBuilder extends GroovyRecursiveElementVisitor { Set> pendingAfterTry = new LinkedHashSet>(myPending); + @SuppressWarnings("unchecked") List[] throwers = new List[catchClauses.length]; for (int i = 0; i < catchClauses.length; i++) { @@ -772,7 +788,7 @@ public class ControlFlowBuilder extends GroovyRecursiveElementVisitor { final GrParameter parameter = catchClauses[i].getParameter(); if (parameter != null) { - addNode(new ReadWriteVariableInstruction(parameter.getName(), parameter, myInstructionNumber++, true)); + addNode(new ReadWriteVariableInstruction(parameter.getName(), parameter, myInstructionNumber++, WRITE)); } catchClauses[i].accept(this); catches[i] = myHead; @@ -867,19 +883,73 @@ public class ControlFlowBuilder extends GroovyRecursiveElementVisitor { public void visitMethod(GrMethod method) { } - public void visitTypeDefinition(GrTypeDefinition typeDefinition) { - if (typeDefinition instanceof GrAnonymousClassDefinition) { - super.visitTypeDefinition(typeDefinition); + @Override + public void visitClassInitializer(GrClassInitializer initializer) { + } + + public void visitTypeDefinition(final GrTypeDefinition typeDefinition) { + if (!(typeDefinition instanceof GrAnonymousClassDefinition)) return; + + final Set vars = new HashSet(); + typeDefinition.acceptChildren(new GroovyRecursiveElementVisitor() { + private void collectVars(Instruction[] flow) { + ReadWriteVariableInstruction[] reads = ControlFlowBuilderUtil.getReadsWithoutPriorWrites(flow); + for (ReadWriteVariableInstruction instruction : reads) { + vars.add(instruction.getVariableName()); + } + } + + @Override + public void visitField(GrField field) { + GrExpression initializer = field.getInitializerGroovy(); + if (initializer != null) { + Instruction[] flow = new ControlFlowBuilder(field.getProject()).buildControlFlow(initializer); + collectVars(flow); + } + } + + @Override + public void visitMethod(GrMethod method) { + GrOpenBlock block = method.getBlock(); + if (block != null) { + collectVars(block.getControlFlow()); + } + } + + @Override + public void visitClassInitializer(GrClassInitializer initializer) { + GrOpenBlock block = initializer.getBlock(); + collectVars(block.getControlFlow()); + } + + @Override + public void visitTypeDefinition(GrTypeDefinition typeDefinition) { + typeDefinition.acceptChildren(this); + } + }); + + PsiField[] fields = typeDefinition.getAllFields(); + for (PsiField field : fields) { + vars.remove(field.getName()); } + + for (String var : vars) { + ReadWriteVariableInstruction i = new ReadWriteVariableInstruction(var, typeDefinition, myInstructionNumber++, READ); + addNode(i); + checkPending(i); + } + InstructionImpl i = new InstructionImpl(typeDefinition, myInstructionNumber++); + addNode(i); + checkPending(i); } public void visitVariable(GrVariable variable) { super.visitVariable(variable); if (variable.getInitializerGroovy() != null || variable.getParent() instanceof GrTupleDeclaration && ((GrTupleDeclaration)variable.getParent()).getInitializerGroovy() != null) { - ReadWriteVariableInstruction writeInsn = new ReadWriteVariableInstruction(variable.getName(), variable, myInstructionNumber++, true); - checkPending(writeInsn); - addNode(writeInsn); + ReadWriteVariableInstruction writeInst = new ReadWriteVariableInstruction(variable.getName(), variable, myInstructionNumber++, WRITE); + checkPending(writeInst); + addNode(writeInst); } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/PsiImplUtil.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/PsiImplUtil.java index f837f3687ec3..22d5d9ad691d 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/PsiImplUtil.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/PsiImplUtil.java @@ -39,7 +39,6 @@ import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.formatter.GeeseUtil; -import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes; import org.jetbrains.plugins.groovy.lang.lexer.TokenSets; import org.jetbrains.plugins.groovy.lang.psi.GrNamedElement; import org.jetbrains.plugins.groovy.lang.psi.GrQualifiedReference; @@ -317,15 +316,13 @@ public class PsiImplUtil { private static int getExprPriorityLevel(GrExpression expr) { int priority = 0; //if (expr instanceof GrNewExpression) priority = 1; - if (expr instanceof GrPostfixExpression) priority = 5; - else if (expr instanceof GrUnaryExpression || - expr instanceof GrTypeCastExpression) priority = 6; + if (expr instanceof GrUnaryExpression) priority = ((GrUnaryExpression)expr).isPostfix() ? 5 : 6; + else if (expr instanceof GrTypeCastExpression) priority = 6; else if (expr instanceof GrRangeExpressionImpl) priority = 11; else if (expr instanceof GrBinaryExpression) { final IElementType opToken = ((GrBinaryExpression)expr).getOperationTokenType(); - LOG.assertTrue(opToken != null, expr.getText()); if (opToken == mSTAR_STAR) priority = 7; else if (opToken == mSTAR || opToken == mDIV) priority = 8; @@ -507,7 +504,7 @@ public class PsiImplUtil { } else { if (oldQualifier == null) { if (refNameElement != null) { - node.addLeaf(GroovyTokenTypes.mDOT, ".", refNameElement.getNode()); + node.addLeaf(mDOT, ".", refNameElement.getNode()); ref.addBefore(newQualifier, refNameElement.getPrevSibling()); } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrClassInitializerImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrClassInitializerImpl.java index ab2d9984d740..623daf3e780f 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrClassInitializerImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrClassInitializerImpl.java @@ -25,6 +25,7 @@ import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes; +import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor; import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.modifiers.GrModifierList; import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrClassInitializer; import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrOpenBlock; @@ -42,6 +43,11 @@ public class GrClassInitializerImpl extends GroovyPsiElementImpl implements GrCl super(node); } + @Override + public void accept(GroovyElementVisitor visitor) { + visitor.visitClassInitializer(this); + } + public String toString() { return "Class initializer"; } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/arithmetic/GrPostfixExprImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/arithmetic/GrPostfixExprImpl.java deleted file mode 100644 index f5f2092ad9be..000000000000 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/arithmetic/GrPostfixExprImpl.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2000-2009 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.lang.psi.impl.statements.expressions.arithmetic; - -import com.intellij.lang.ASTNode; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor; -import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrPostfixExpression; - -/** - * @author ilyas - */ -public class GrPostfixExprImpl extends GrUnaryExpressionImpl implements GrPostfixExpression { - - public GrPostfixExprImpl(@NotNull ASTNode node) { - super(node); - } - - public String toString() { - return "Postfix expression"; - } - - public void accept(GroovyElementVisitor visitor) { - visitor.visitPostfixExpression(this); - } -} \ No newline at end of file diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/arithmetic/GrUnaryExpressionImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/arithmetic/GrUnaryExpressionImpl.java index 2b95343cbaef..3ae033a3a647 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/arithmetic/GrUnaryExpressionImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/arithmetic/GrUnaryExpressionImpl.java @@ -125,6 +125,11 @@ public class GrUnaryExpressionImpl extends GrExpressionImpl implements GrUnaryEx return (GroovyResolveResult[])ResolveCache.getInstance(getProject()).resolveWithCaching(this, OUR_RESOLVER, false, incompleteCode); } + @Override + public boolean isPostfix() { + return getFirstChild() instanceof GrExpression; + } + @Override public PsiElement getElement() { return this; diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/ResolveUtil.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/ResolveUtil.java index 239574da80c1..ce38cea5b7cd 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/ResolveUtil.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/ResolveUtil.java @@ -78,9 +78,9 @@ public class ResolveUtil { } /** - * + * * @param place - place to start tree walk up - * @param processor + * @param processor * @param processNonCodeMethods - this parameter tells us if we need non code members. But non code members are started to process only after we walk up any code block or script * @return */ @@ -95,10 +95,6 @@ public class ResolveUtil { while (run != null) { if (!run.processDeclarations(processor, ResolveState.initial(), lastParent, place)) return false; - if (run instanceof GrClosableBlock) { - PsiClass superClass = getLiteralSuperClass((GrClosableBlock)run); - if (superClass != null && !superClass.processDeclarations(processor, ResolveState.initial(), null, place)) return false; - } if (processNonCodeMethods) { if (!doProcessNonCodeMembers) { if (run instanceof GrCodeBlock) doProcessNonCodeMembers = true; @@ -115,6 +111,9 @@ public class ResolveUtil { } } else if (run instanceof GrClosableBlock) { + PsiClass superClass = getLiteralSuperClass((GrClosableBlock)run); + if (superClass != null && !superClass.processDeclarations(processor, ResolveState.initial(), null, place)) return false; + if (!GdkMethodUtil.categoryIteration((GrClosableBlock)run, processor)) return false; if (!GdkMethodUtil.withIteration((GrClosableBlock)run, processor, place)) return false; } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/convertToJava/ExpressionGenerator.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/convertToJava/ExpressionGenerator.java index b55c62da66d4..2a60c5494208 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/convertToJava/ExpressionGenerator.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/convertToJava/ExpressionGenerator.java @@ -669,7 +669,7 @@ public class ExpressionGenerator extends Generator { @Override public void visitUnaryExpression(GrUnaryExpression expression) { - final boolean postfix = expression instanceof GrPostfixExpression; + final boolean postfix = expression.isPostfix(); final GroovyResolveResult resolveResult = PsiImplUtil.extractUniqueResult(expression.multiResolve(false)); final PsiElement resolved = resolveResult.getElement(); @@ -816,12 +816,12 @@ public class ExpressionGenerator extends Generator { private static void writeSimpleUnary(GrExpression operand, GrUnaryExpression unary, ExpressionGenerator generator) { String opTokenText = unary.getOperationToken().getText(); - boolean isPrefix = !(unary instanceof GrPostfixExpression); - if (isPrefix) { + boolean isPostfix = unary.isPostfix(); + if (!isPostfix) { generator.getBuilder().append(opTokenText); } operand.accept(generator); - if (!isPrefix) { + if (isPostfix) { generator.getBuilder().append(opTokenText); } } 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 d4f6fe10a871..e0ba3cd80dad 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GroovyHighlightingTest.groovy @@ -37,6 +37,7 @@ import org.jetbrains.plugins.groovy.codeInspection.assignment.GroovyAssignabilit import org.jetbrains.plugins.groovy.codeInspection.assignment.GroovyResultOfAssignmentUsedInspection import org.jetbrains.plugins.groovy.codeInspection.assignment.GroovyUncheckedAssignmentOfMemberOfRawTypeInspection import org.jetbrains.plugins.groovy.codeInspection.confusing.ClashingGettersInspection +import org.jetbrains.plugins.groovy.codeInspection.confusing.GrUnusedIncDecInspection import org.jetbrains.plugins.groovy.codeInspection.confusing.GroovyOctalIntegerInspection import org.jetbrains.plugins.groovy.codeInspection.confusing.GroovyResultOfIncrementOrDecrementUsedInspection import org.jetbrains.plugins.groovy.codeInspection.control.GroovyTrivialConditionalInspection @@ -188,10 +189,10 @@ public class GroovyHighlightingTest extends LightCodeInsightFixtureTestCase { public void testUnassigned3() throws Exception { doTest(new UnassignedVariableAccessInspection()); } public void testUnassignedTryFinally() throws Exception { doTest(new UnassignedVariableAccessInspection()); } - public void testUnusedVariable() throws Exception { doTest(new UnusedDefInspection()); } - public void testDefinitionUsedInClosure() throws Exception { doTest(new UnusedDefInspection()); } - public void testDefinitionUsedInClosure2() throws Exception { doTest(new UnusedDefInspection()); } - public void testDefinitionUsedInSwitchCase() throws Exception { doTest(new UnusedDefInspection()); } + public void testUnusedVariable() throws Exception { doTest(new UnusedDefInspection(), new GrUnusedIncDecInspection()); } + public void testDefinitionUsedInClosure() throws Exception { doTest(new UnusedDefInspection(), new GrUnusedIncDecInspection()); } + public void testDefinitionUsedInClosure2() throws Exception { doTest(new UnusedDefInspection(), new GrUnusedIncDecInspection()); } + public void testDefinitionUsedInSwitchCase() throws Exception { doTest(new UnusedDefInspection(), new GrUnusedIncDecInspection()); } public void testDuplicateInnerClass() throws Throwable{doTest();} public void testThisInStaticContext() throws Throwable {doTest();} @@ -248,8 +249,8 @@ class A { public void testByteArrayArgument() throws Exception {doTest(new GroovyAssignabilityCheckInspection());} public void testForLoopWithNestedEndlessLoop() throws Exception {doTest(new UnassignedVariableAccessInspection());} - public void testPrefixIncrementCfa() throws Exception {doTest(new UnusedDefInspection());} - public void testIfIncrementElseReturn() throws Exception {doTest(new UnusedDefInspection()); } + public void testPrefixIncrementCfa() throws Exception {doTest(new UnusedDefInspection(), new GrUnusedIncDecInspection());} + public void testIfIncrementElseReturn() throws Exception {doTest(new UnusedDefInspection(), new GrUnusedIncDecInspection()); } public void testArrayLikeAccess() throws Exception {doTest();} @@ -315,7 +316,7 @@ class A { public void testBuiltInTypeInstantiation() {doTest();} - public void testSwitchControlFlow() {doTest(new UnusedDefInspection(), new GroovyResultOfAssignmentUsedInspection());} + public void testSwitchControlFlow() {doTest(new UnusedDefInspection(), new GroovyResultOfAssignmentUsedInspection(), new GrUnusedIncDecInspection());} public void testRawTypeInAssignment() {doTest(new GroovyAssignabilityCheckInspection());} @@ -325,7 +326,7 @@ class A { IdeaTestUtil.assertTiming("", 10000, 1, new Runnable() { @Override public void run() { - doTest(new GroovyAssignabilityCheckInspection(), new UnusedDefInspection()); + doTest(new GroovyAssignabilityCheckInspection(), new UnusedDefInspection(), new GrUnusedIncDecInspection()); } }); } @@ -397,7 +398,7 @@ class A { doTest(new GroovyUnresolvedAccessInspection(), new GroovyUntypedAccessInspection()); } - public void testUsageInInjection() { doTest(new UnusedDefInspection()); } + public void testUsageInInjection() { doTest(new UnusedDefInspection(), new GrUnusedIncDecInspection()); } public void testDuplicatedNamedArgs() {doTest();} @@ -419,15 +420,19 @@ class A { } public void testUnusedDefsForArgs() { - doTest(new UnusedDefInspection()); + doTest(new UnusedDefInspection(), new GrUnusedIncDecInspection()); } public void testUsedDefBeforeTry1() { - doTest(new UnusedDefInspection()); + doTest(new UnusedDefInspection(), new GrUnusedIncDecInspection()); } public void testUsedDefBeforeTry2() { - doTest(new UnusedDefInspection()); + doTest(new UnusedDefInspection(), new GrUnusedIncDecInspection()); + } + + public void testUnusedInc() { + doTest(new UnusedDefInspection(), new GrUnusedIncDecInspection()) } public void testStringAssignableToChar() { diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/controlFlow/ControlFlowTest.java b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/controlFlow/ControlFlowTest.java index a6790d48e8d5..ff34da2ea32e 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/controlFlow/ControlFlowTest.java +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/controlFlow/ControlFlowTest.java @@ -55,6 +55,8 @@ public class ControlFlowTest extends LightCodeInsightFixtureTestCase { public void testIfInstanceofElse() throws Throwable { doTest(); } public void testReturnMapFromClosure() {doTest();} public void testSwitchInTryWithThrows() {doTest();} + public void testClosure() {doTest();} + public void testAnonymous() {doTest();} public void doTest() { final List input = TestUtils.readInput(getTestDataPath() + getTestName(true) + ".test"); diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/refactoring/extract/method/ExtractMethodTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/refactoring/extract/method/ExtractMethodTest.groovy index 04fee16b1f12..729e0d436827 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/refactoring/extract/method/ExtractMethodTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/refactoring/extract/method/ExtractMethodTest.groovy @@ -114,4 +114,7 @@ public class ExtractMethodTest extends LightGroovyTestCase { public void testParamChangedInsideExtractedMethod() {doTest();} public void testTerribleAppStatement() {doTest()} + + public void testArgsUsedOnlyInClosure() {doTest()} + public void testArgsUsedOnlyInAnonymousClass() {doTest()} } \ No newline at end of file diff --git a/plugins/groovy/testdata/groovy/controlFlow/anonymous.test b/plugins/groovy/testdata/groovy/controlFlow/anonymous.test new file mode 100644 index 000000000000..79b93ac0c3fd --- /dev/null +++ b/plugins/groovy/testdata/groovy/controlFlow/anonymous.test @@ -0,0 +1,17 @@ +int x = 0 +[1, 2, 3].each (new Closure(this, this) { + void call() { + x++ + } +}) + +print x +----- +0(1) element: null +1(2) WRITE x +2(3) READ x +3(4) element: Anonymous class +4(5) READ print +5(6) READ x +6(7) element: Call expression MAYBE_RETURN +7() element: null \ No newline at end of file diff --git a/plugins/groovy/testdata/groovy/controlFlow/closure.test b/plugins/groovy/testdata/groovy/controlFlow/closure.test new file mode 100644 index 000000000000..ed94fc177ee3 --- /dev/null +++ b/plugins/groovy/testdata/groovy/controlFlow/closure.test @@ -0,0 +1,15 @@ +int x = 0 +[1, 2, 3].each { + x++ +} + +print x +----- +0(1) element: null +1(2) WRITE x +2(3) READ x +3(4) element: Closable block +4(5) READ print +5(6) READ x +6(7) element: Call expression MAYBE_RETURN +7() element: null \ No newline at end of file diff --git a/plugins/groovy/testdata/groovy/controlFlow/for1.test b/plugins/groovy/testdata/groovy/controlFlow/for1.test index 0f0ebbd84c66..cefbd1ce77bf 100644 --- a/plugins/groovy/testdata/groovy/controlFlow/for1.test +++ b/plugins/groovy/testdata/groovy/controlFlow/for1.test @@ -3,8 +3,7 @@ for(int i = 0; i < 10; i++); 0(1) element: null 1(2) WRITE i 2(3) element: For statement -3(4,7) READ i +3(4,6) READ i 4(5) READ i -5(6) WRITE i -6(2) READ i -7() element: null \ No newline at end of file +5(2) WRITE i +6() element: null \ No newline at end of file diff --git a/plugins/groovy/testdata/groovy/controlFlow/while1.test b/plugins/groovy/testdata/groovy/controlFlow/while1.test index 3b7578eb1411..493df467f932 100644 --- a/plugins/groovy/testdata/groovy/controlFlow/while1.test +++ b/plugins/groovy/testdata/groovy/controlFlow/while1.test @@ -8,9 +8,8 @@ while (true) { 1(2) WRITE i 2(3) element: WHILE statement 3(4,5) element: IF statement -4(9) READ i +4(8) READ i 5(6) element: IF statement 6(7) READ i -7(8) WRITE i -8(2) READ i -9() element: null \ No newline at end of file +7(2) WRITE i +8() element: null \ No newline at end of file diff --git a/plugins/groovy/testdata/groovy/controlFlow/whileNonConstant.test b/plugins/groovy/testdata/groovy/controlFlow/whileNonConstant.test index a54fd3bee520..f1206ab03412 100644 --- a/plugins/groovy/testdata/groovy/controlFlow/whileNonConstant.test +++ b/plugins/groovy/testdata/groovy/controlFlow/whileNonConstant.test @@ -7,11 +7,10 @@ while (condition()) { 0(1) element: null 1(2) WRITE i 2(3) element: WHILE statement -3(4,10) READ condition +3(4,9) READ condition 4(5,6) element: IF statement -5(10) READ i +5(9) READ i 6(7) element: IF statement 7(8) READ i -8(9) WRITE i -9(2) READ i -10() element: null \ No newline at end of file +8(2) WRITE i +9() element: null \ No newline at end of file diff --git a/plugins/groovy/testdata/groovy/refactoring/extractMethod/argsUsedOnlyInAnonymousClass.test b/plugins/groovy/testdata/groovy/refactoring/extractMethod/argsUsedOnlyInAnonymousClass.test new file mode 100644 index 000000000000..0857daf5c4db --- /dev/null +++ b/plugins/groovy/testdata/groovy/refactoring/extractMethod/argsUsedOnlyInAnonymousClass.test @@ -0,0 +1,19 @@ +def foo(b, c) { + [].each (new Closure(this, this) { + void call() { + b.plus(c) + } + }) +} +----- +def foo(b, c) { + testMethod(b, c) +} + +private testMethod(b, c) { + [].each(new Closure(this, this) { + void call() { + b.plus(c) + } + }) +} \ No newline at end of file diff --git a/plugins/groovy/testdata/groovy/refactoring/extractMethod/argsUsedOnlyInClosure.test b/plugins/groovy/testdata/groovy/refactoring/extractMethod/argsUsedOnlyInClosure.test new file mode 100644 index 000000000000..7ff5598dbdff --- /dev/null +++ b/plugins/groovy/testdata/groovy/refactoring/extractMethod/argsUsedOnlyInClosure.test @@ -0,0 +1,15 @@ +def foo(b, c) { + [].each { + b.plus(c) + } +} +----- +def foo(b, c) { + testMethod(b, c) +} + +private testMethod(b, c) { + [].each { + b.plus(c) + } +} \ No newline at end of file diff --git a/plugins/groovy/testdata/groovy/refactoring/extractMethod/output1.test b/plugins/groovy/testdata/groovy/refactoring/extractMethod/output1.test index d77f380f9410..e1738a81d52c 100644 --- a/plugins/groovy/testdata/groovy/refactoring/extractMethod/output1.test +++ b/plugins/groovy/testdata/groovy/refactoring/extractMethod/output1.test @@ -12,7 +12,7 @@ class S { ----- class S { def r() { - Closure sin = testMethod() + Closure sin = testMethod() 10.times { @@ -21,7 +21,7 @@ class S { } - private Closure testMethod() { + private Closure testMethod() { Closure sin = {x -> Math.sin(x)} return sin } diff --git a/plugins/groovy/testdata/highlighting/PrefixIncrementCfa.groovy b/plugins/groovy/testdata/highlighting/PrefixIncrementCfa.groovy index 711bb7b8c062..e886f8664bdf 100644 --- a/plugins/groovy/testdata/highlighting/PrefixIncrementCfa.groovy +++ b/plugins/groovy/testdata/highlighting/PrefixIncrementCfa.groovy @@ -1,7 +1,7 @@ int idx idx = 2 idx = 3 -if (++idx == 8) { //Assignment is used here +if (++idx == 8) { idx = 33 } print idx \ No newline at end of file diff --git a/plugins/groovy/testdata/highlighting/UnusedInc.groovy b/plugins/groovy/testdata/highlighting/UnusedInc.groovy new file mode 100644 index 000000000000..3483ac2f7b68 --- /dev/null +++ b/plugins/groovy/testdata/highlighting/UnusedInc.groovy @@ -0,0 +1,7 @@ +def a = 4 +print (++a) +print (a++) + +def b = 3 +b++ +b = 3 \ No newline at end of file diff --git a/plugins/groovy/testdata/parsing/groovy/expressions/arithmetic/post1.test b/plugins/groovy/testdata/parsing/groovy/expressions/arithmetic/post1.test index 65d287aed2d6..e51d2abf5bda 100644 --- a/plugins/groovy/testdata/parsing/groovy/expressions/arithmetic/post1.test +++ b/plugins/groovy/testdata/parsing/groovy/expressions/arithmetic/post1.test @@ -3,8 +3,8 @@ Groovy script Unary expression PsiElement(!)('!') - Postfix expression - Postfix expression + Unary expression + Unary expression Literal PsiElement(Integer)('3') PsiElement(++)('++') diff --git a/plugins/groovy/testdata/parsing/groovy/statements/for/for10.test b/plugins/groovy/testdata/parsing/groovy/statements/for/for10.test index 24de169dd13b..bec24b69f0a0 100644 --- a/plugins/groovy/testdata/parsing/groovy/statements/for/for10.test +++ b/plugins/groovy/testdata/parsing/groovy/statements/for/for10.test @@ -31,7 +31,7 @@ Groovy script PsiWhiteSpace(' ') PsiElement(;)(';') PsiWhiteSpace(' ') - Postfix expression + Unary expression Reference expression PsiElement(identifier)('i') PsiElement(++)('++') diff --git a/plugins/groovy/testdata/parsing/groovy/statements/for/for6.test b/plugins/groovy/testdata/parsing/groovy/statements/for/for6.test index 8cea9c0925fb..42f99b3dda73 100644 --- a/plugins/groovy/testdata/parsing/groovy/statements/for/for6.test +++ b/plugins/groovy/testdata/parsing/groovy/statements/for/for6.test @@ -26,7 +26,7 @@ Groovy script PsiElement(Integer)('5') PsiElement(;)(';') PsiWhiteSpace(' ') - Postfix expression + Unary expression Reference expression PsiElement(identifier)('i') PsiElement(++)('++') diff --git a/plugins/groovy/testdata/parsing/groovy/statements/for/for9.test b/plugins/groovy/testdata/parsing/groovy/statements/for/for9.test index 24de169dd13b..bec24b69f0a0 100644 --- a/plugins/groovy/testdata/parsing/groovy/statements/for/for9.test +++ b/plugins/groovy/testdata/parsing/groovy/statements/for/for9.test @@ -31,7 +31,7 @@ Groovy script PsiWhiteSpace(' ') PsiElement(;)(';') PsiWhiteSpace(' ') - Postfix expression + Unary expression Reference expression PsiElement(identifier)('i') PsiElement(++)('++') diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/MavenPluginConfigurationDomExtender.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/MavenPluginConfigurationDomExtender.java index 44cb3dff60bd..eebd3737476a 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/MavenPluginConfigurationDomExtender.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/MavenPluginConfigurationDomExtender.java @@ -17,7 +17,9 @@ package org.jetbrains.idea.maven.dom; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.PsiElement; import com.intellij.psi.codeStyle.NameUtil; +import com.intellij.psi.xml.XmlElement; import com.intellij.psi.xml.XmlTag; import com.intellij.util.xml.DomElement; import com.intellij.util.xml.GenericDomValue; @@ -34,13 +36,15 @@ import org.jetbrains.idea.maven.dom.converters.MavenPluginCustomParameterValueCo import org.jetbrains.idea.maven.dom.model.MavenDomConfiguration; import org.jetbrains.idea.maven.dom.model.MavenDomConfigurationParameter; import org.jetbrains.idea.maven.dom.model.MavenDomPluginExecution; -import org.jetbrains.idea.maven.dom.model.MavenDomPluginManagement; import org.jetbrains.idea.maven.dom.plugin.MavenDomMojo; import org.jetbrains.idea.maven.dom.plugin.MavenDomParameter; import org.jetbrains.idea.maven.dom.plugin.MavenDomPluginModel; import java.lang.annotation.Annotation; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; public class MavenPluginConfigurationDomExtender extends DomExtender { public static final Key PLUGIN_PARAMETER_KEY = Key.create("MavenPluginConfigurationDomExtender.PLUGIN_PARAMETER_KEY"); @@ -53,21 +57,26 @@ public class MavenPluginConfigurationDomExtender extends DomExtender collectParameters(MavenDomPluginModel pluginModel, MavenDomConfiguration config) { diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/MavenPluginDomUtil.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/MavenPluginDomUtil.java index 7cbcfa2e51de..63bcc9862a2b 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/MavenPluginDomUtil.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/dom/MavenPluginDomUtil.java @@ -20,6 +20,7 @@ import com.intellij.openapi.vfs.JarFileSystem; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.xml.DomElement; +import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.maven.dom.model.MavenDomPlugin; import org.jetbrains.idea.maven.dom.plugin.MavenDomPluginModel; import org.jetbrains.idea.maven.project.MavenProjectsManager; @@ -28,6 +29,7 @@ import org.jetbrains.idea.maven.utils.MavenArtifactUtil; import java.io.File; public class MavenPluginDomUtil { + @Nullable public static MavenDomPluginModel getMavenPluginModel(DomElement element) { Project p = element.getXmlElement().getProject(); @@ -40,6 +42,7 @@ public class MavenPluginDomUtil { return MavenDomUtil.getMavenDomModel(p, pluginXmlFile, MavenDomPluginModel.class); } + @Nullable private static VirtualFile getPluginXmlFile(Project p, MavenDomPlugin pluginElement) { String groupId = pluginElement.getGroupId().getStringValue(); String artifactId = pluginElement.getArtifactId().getStringValue();