From e80d2486b27167d3f8050e427900f013ffc1dff2 Mon Sep 17 00:00:00 2001 From: "Anna.Kozlova" Date: Mon, 17 Oct 2016 15:23:02 +0200 Subject: [PATCH 1/6] disable introduce functional parameter outside method: EA-87518 - NPE: IntroduceParameterHandler$MyExtractMethodProcessor$MyAbstractExtractDialog.getChosenParameters --- .../IntroduceParameterHandler.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/java/java-impl/src/com/intellij/refactoring/introduceParameter/IntroduceParameterHandler.java b/java/java-impl/src/com/intellij/refactoring/introduceParameter/IntroduceParameterHandler.java index 8cfe95213ebd..6bfb821afcc0 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceParameter/IntroduceParameterHandler.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceParameter/IntroduceParameterHandler.java @@ -333,7 +333,7 @@ public class IntroduceParameterHandler extends IntroduceHandlerBase { /* do nothing */ } - public static List getEnclosingMethods(PsiMethod nearest) { + public static List getEnclosingMethods(@NotNull PsiMethod nearest) { List enclosingMethods = new ArrayList<>(); enclosingMethods.add(nearest); PsiMethod method = nearest; @@ -531,7 +531,11 @@ public class IntroduceParameterHandler extends IntroduceHandlerBase { if (inplaceIntroducer instanceof InplaceIntroduceParameterPopup) { return false; } - final List enclosingMethods = getEnclosingMethods(Util.getContainingMethod(elements[0])); + final PsiMethod containingMethod = Util.getContainingMethod(elements[0]); + if (containingMethod == null) { + return false; + } + final List enclosingMethods = getEnclosingMethods(containingMethod); if (enclosingMethods.isEmpty()) { return false; } @@ -550,7 +554,9 @@ public class IntroduceParameterHandler extends IntroduceHandlerBase { ? new PsiElement[]{exprInRange} : CodeInsightUtil.findStatementsInRange(copy, range.getStartOffset(), range.getEndOffset()); } - final List enclosingMethodsInCopy = getEnclosingMethods(Util.getContainingMethod(elementsCopy[0])); + final PsiMethod containingMethodCopy = Util.getContainingMethod(elementsCopy[0]); + LOG.assertTrue(containingMethodCopy != null); + final List enclosingMethodsInCopy = getEnclosingMethods(containingMethodCopy); final MyExtractMethodProcessor processor = new MyExtractMethodProcessor(project, editor, elementsCopy, enclosingMethodsInCopy.get(enclosingMethodsInCopy.size() - 1)); try { @@ -702,7 +708,7 @@ public class IntroduceParameterHandler extends IntroduceHandlerBase { private static class MyExtractMethodProcessor extends ExtractMethodProcessor { private final PsiMethod myTopEnclosingMethod; - public MyExtractMethodProcessor(Project project, Editor editor, PsiElement[] elements, PsiMethod topEnclosing) { + public MyExtractMethodProcessor(Project project, Editor editor, PsiElement[] elements, @NotNull PsiMethod topEnclosing) { super(project, editor, elements, null, REFACTORING_NAME, null, null); myTopEnclosingMethod = topEnclosing; } From 2a8fc8cbc6641102aa156256c3dede08cce49d22 Mon Sep 17 00:00:00 2001 From: "Anna.Kozlova" Date: Mon, 17 Oct 2016 17:50:39 +0200 Subject: [PATCH 2/6] extract method object: process parameters as output variables (IDEA-162642) --- .../ExtractMethodObjectProcessor.java | 40 ++++++++++++- .../AssignReturnValueToForeachParameter.java | 34 +++++++++++ ...gnReturnValueToForeachParameter.java.after | 59 +++++++++++++++++++ ...ethodObjectWithMultipleExitPointsTest.java | 4 ++ 4 files changed, 135 insertions(+), 2 deletions(-) create mode 100644 java/java-tests/testData/refactoring/extractMethodObject/multipleExitPoints/AssignReturnValueToForeachParameter.java create mode 100644 java/java-tests/testData/refactoring/extractMethodObject/multipleExitPoints/AssignReturnValueToForeachParameter.java.after diff --git a/java/java-impl/src/com/intellij/refactoring/extractMethodObject/ExtractMethodObjectProcessor.java b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/ExtractMethodObjectProcessor.java index 1967e444c74a..e801402502c3 100644 --- a/java/java-impl/src/com/intellij/refactoring/extractMethodObject/ExtractMethodObjectProcessor.java +++ b/java/java-impl/src/com/intellij/refactoring/extractMethodObject/ExtractMethodObjectProcessor.java @@ -345,6 +345,17 @@ public class ExtractMethodObjectProcessor extends BaseRefactoringProcessor { } } + @Override + public void visitParameter(PsiParameter parameter) { + super.visitParameter(parameter); + final PsiElement declarationScope = parameter.getDeclarationScope(); + for (PsiVariable variable : outputVariables) { + if (Comparing.strEqual(variable.getName(), parameter.getName())) { + replacementMap.put(parameter, myElementFactory.createStatementFromText(myInnerClassName + ".this." + var2FieldNames.get(variable.getName()) + " = " + parameter.getName() + ";", declarationScope)); + } + } + } + @Override public void visitReferenceExpression(final PsiReferenceExpression expression) { super.visitReferenceExpression(expression); @@ -368,16 +379,37 @@ public class ExtractMethodObjectProcessor extends BaseRefactoringProcessor { } } + Map blocksToReplace = new LinkedHashMap<>(); for (PsiElement statement : replacementMap.keySet()) { final PsiElement replacement = replacementMap.get(statement); if (replacement != null) { - if (statement instanceof PsiLocalVariable) { + if (statement instanceof PsiParameter) { + PsiCodeBlock codeBlock = null; + final PsiElement declarationScope = ((PsiParameter)statement).getDeclarationScope(); + if (declarationScope instanceof PsiForeachStatement) { + final PsiStatement loopBody = ((PsiForeachStatement)declarationScope).getBody(); + if (loopBody instanceof PsiBlockStatement) { + codeBlock = ((PsiBlockStatement)loopBody).getCodeBlock(); + } + else { + blocksToReplace.put((PsiStatement)replacement, (PsiForeachStatement)declarationScope); + } + } + else if (declarationScope instanceof PsiCatchSection){ + codeBlock = ((PsiCatchSection)declarationScope).getCatchBlock(); + } + if (codeBlock != null) { + codeBlock.addBefore(replacement, codeBlock.getFirstBodyElement()); + } + } + else if (statement instanceof PsiLocalVariable) { PsiLocalVariable variable = (PsiLocalVariable)statement; variable.normalizeDeclaration(); PsiDeclarationStatement declaration = PsiTreeUtil.getParentOfType(statement, PsiDeclarationStatement.class); LOG.assertTrue(declaration != null); declaration.replace(replacement); - } else { + } + else { if (statement instanceof PsiReturnStatement) { final PsiExpression returnValue = ((PsiReturnStatement)statement).getReturnValue(); if (!(returnValue instanceof PsiReferenceExpression || returnValue == null || returnValue instanceof PsiLiteralExpression)) { @@ -392,6 +424,10 @@ public class ExtractMethodObjectProcessor extends BaseRefactoringProcessor { } } + for (PsiStatement statement : blocksToReplace.keySet()) { + RefactoringUtil.putStatementInLoopBody(statement, blocksToReplace.get(statement), null); + } + myChangeReturnType = true; } diff --git a/java/java-tests/testData/refactoring/extractMethodObject/multipleExitPoints/AssignReturnValueToForeachParameter.java b/java/java-tests/testData/refactoring/extractMethodObject/multipleExitPoints/AssignReturnValueToForeachParameter.java new file mode 100644 index 000000000000..7a7fdb71711e --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethodObject/multipleExitPoints/AssignReturnValueToForeachParameter.java @@ -0,0 +1,34 @@ + +import java.util.Arrays; +import java.util.List; + +class Foo { + private List foos = Arrays.asList(new Foo("one"), new Foo("two")); + + private String name; + + public Foo(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public List getFoos() { + return foos; + } + + public Foo getFoo(String name) { + if (name != null) + for (Foo foo : getFoos()) + if (foo.getName().equals(name)) { + return foo; + } + + + + return null; + } + +} diff --git a/java/java-tests/testData/refactoring/extractMethodObject/multipleExitPoints/AssignReturnValueToForeachParameter.java.after b/java/java-tests/testData/refactoring/extractMethodObject/multipleExitPoints/AssignReturnValueToForeachParameter.java.after new file mode 100644 index 000000000000..87aaa35c604d --- /dev/null +++ b/java/java-tests/testData/refactoring/extractMethodObject/multipleExitPoints/AssignReturnValueToForeachParameter.java.after @@ -0,0 +1,59 @@ + +import java.util.Arrays; +import java.util.List; + +class Foo { + private List foos = Arrays.asList(new Foo("one"), new Foo("two")); + + private String name; + + public Foo(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public List getFoos() { + return foos; + } + + public Foo getFoo(String name) { + if (name != null) + Inner inner = new Inner(name).invoke();if (inner.is()) return inner.getFoo(); + + + return null; + } + + private class Inner { + private boolean myResult; + private String name; + private Foo foo; + + public Inner(String name) { + this.name = name; + } + + boolean is() { + return myResult; + } + + public Foo getFoo() { + return foo; + } + + public Inner invoke() { + for (Foo foo : getFoos()) { + Inner.this.foo = foo; + if (foo.getName().equals(name)) { + myResult = true; + return this; + } + } + myResult = false; + return this; + } + } +} diff --git a/java/java-tests/testSrc/com/intellij/refactoring/ExtractMethodObjectWithMultipleExitPointsTest.java b/java/java-tests/testSrc/com/intellij/refactoring/ExtractMethodObjectWithMultipleExitPointsTest.java index 0dcfe73574ab..20f78f6e9b33 100644 --- a/java/java-tests/testSrc/com/intellij/refactoring/ExtractMethodObjectWithMultipleExitPointsTest.java +++ b/java/java-tests/testSrc/com/intellij/refactoring/ExtractMethodObjectWithMultipleExitPointsTest.java @@ -146,4 +146,8 @@ public class ExtractMethodObjectWithMultipleExitPointsTest extends LightRefactor public void testFormattingInside() throws Exception { doTest(); } + + public void testAssignReturnValueToForeachParameter() throws Exception { + doTest(); + } } From 75ac3842ff4ae74610e0ff709eefcb6db6b4569d Mon Sep 17 00:00:00 2001 From: "Anna.Kozlova" Date: Mon, 17 Oct 2016 18:09:57 +0200 Subject: [PATCH 3/6] convert lambda to method reference: expand the acceptance range when not visible --- .../LambdaCanBeMethodReferenceInspection.java | 8 ++++++-- .../InspectionProjectProfileManager.java | 11 +++++++++++ .../ig/style/ControlFlowStatementVisitorBase.java | 2 +- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/LambdaCanBeMethodReferenceInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/LambdaCanBeMethodReferenceInspection.java index 17bd630a1528..c78731269a6b 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/LambdaCanBeMethodReferenceInspection.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/LambdaCanBeMethodReferenceInspection.java @@ -23,6 +23,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Conditions; import com.intellij.pom.java.LanguageLevel; +import com.intellij.profile.codeInspection.InspectionProjectProfileManager; import com.intellij.psi.*; import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.impl.source.resolve.graphInference.FunctionalInterfaceParameterizationUtil; @@ -102,7 +103,7 @@ public class LambdaCanBeMethodReferenceInspection extends BaseJavaBatchLocalInsp final PsiExpression candidate = canBeMethodReferenceProblem(body, expression.getParameterList().getParameters(), functionalInterfaceType, null); if (candidate != null) { - holder.registerProblem(candidate, + holder.registerProblem(InspectionProjectProfileManager.isInformationLevel(getShortName(), expression) ? expression : candidate, "Can be replaced with method reference", ProblemHighlightType.GENERIC_ERROR_OR_WARNING, new ReplaceWithMethodRefFix()); } @@ -586,8 +587,11 @@ public class LambdaCanBeMethodReferenceInspection extends BaseJavaBatchLocalInsp @Override public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { - final PsiElement element = descriptor.getPsiElement(); + PsiElement element = descriptor.getPsiElement(); if (!FileModificationService.getInstance().preparePsiElementForWrite(element)) return; + if (element instanceof PsiLambdaExpression) { + element = LambdaUtil.extractSingleExpressionFromBody(((PsiLambdaExpression)element).getBody()); + } final PsiLambdaExpression lambdaExpression = PsiTreeUtil.getParentOfType(element, PsiLambdaExpression.class); if (lambdaExpression == null) return; tryConvertToMethodReference(lambdaExpression, element); diff --git a/platform/analysis-impl/src/com/intellij/profile/codeInspection/InspectionProjectProfileManager.java b/platform/analysis-impl/src/com/intellij/profile/codeInspection/InspectionProjectProfileManager.java index f6b157b074b8..319801f46a08 100644 --- a/platform/analysis-impl/src/com/intellij/profile/codeInspection/InspectionProjectProfileManager.java +++ b/platform/analysis-impl/src/com/intellij/profile/codeInspection/InspectionProjectProfileManager.java @@ -15,6 +15,8 @@ */ package com.intellij.profile.codeInspection; +import com.intellij.codeHighlighting.HighlightDisplayLevel; +import com.intellij.codeInsight.daemon.HighlightDisplayKey; import com.intellij.codeInspection.InspectionProfile; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiElement; @@ -40,4 +42,13 @@ public abstract class InspectionProjectProfileManager implements InspectionProfi public InspectionProfile getInspectionProfile(PsiElement element){ return getCurrentProfile(); } + + public static boolean isInformationLevel(String shortName, @NotNull PsiElement element) { + final HighlightDisplayKey key = HighlightDisplayKey.find(shortName); + if (key != null) { + final HighlightDisplayLevel errorLevel = getInstance(element.getProject()).getCurrentProfile().getErrorLevel(key, element); + return HighlightDisplayLevel.DO_NOT_SHOW.equals(errorLevel); + } + return false; + } } diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/ControlFlowStatementVisitorBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/ControlFlowStatementVisitorBase.java index 1efc91fce5b6..88d0ffe1ff54 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/ControlFlowStatementVisitorBase.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/style/ControlFlowStatementVisitorBase.java @@ -146,7 +146,7 @@ public abstract class ControlFlowStatementVisitorBase extends BaseInspectionVisi } if (myKey != null) { final Project project = element.getProject(); - final InspectionProfile profile = InspectionProjectProfileManager.getInstance(project).getInspectionProfile(); + final InspectionProfile profile = InspectionProjectProfileManager.getInstance(project).getCurrentProfile(); final HighlightDisplayLevel errorLevel = profile.getErrorLevel(myKey, element); return !HighlightDisplayLevel.DO_NOT_SHOW.equals(errorLevel); } From e5ea93c403da317d31ff039773c567d4834f22db Mon Sep 17 00:00:00 2001 From: "Anna.Kozlova" Date: Mon, 17 Oct 2016 18:48:48 +0200 Subject: [PATCH 4/6] create test: try to find module with test roots depending on current module (IDEA-162421) --- .../testIntegration/createTest/CreateTestAction.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/java/java-impl/src/com/intellij/testIntegration/createTest/CreateTestAction.java b/java/java-impl/src/com/intellij/testIntegration/createTest/CreateTestAction.java index a056da9e0db4..2f46c53f99d8 100644 --- a/java/java-impl/src/com/intellij/testIntegration/createTest/CreateTestAction.java +++ b/java/java-impl/src/com/intellij/testIntegration/createTest/CreateTestAction.java @@ -142,6 +142,16 @@ public class CreateTestAction extends PsiElementBaseIntentionAction { return module; } } + + if (computeSuitableTestRootUrls(productionModule).isEmpty()) { + final HashSet modules = new HashSet<>(); + ModuleUtilCore.collectModulesDependsOn(productionModule, modules); + modules.remove(productionModule); + for (Module module : modules) { + if (!computeSuitableTestRootUrls(module).isEmpty()) return module; + } + } + return productionModule; } From 043f21193d5f2443ddbd10163e80e229b832f993 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Mon, 17 Oct 2016 19:11:10 +0200 Subject: [PATCH 5/6] Cleanup (warning) --- .../configurations/JavaParameters.java | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/java/execution/openapi/src/com/intellij/execution/configurations/JavaParameters.java b/java/execution/openapi/src/com/intellij/execution/configurations/JavaParameters.java index 6cf8a6e40c9b..f1dcd32a378a 100644 --- a/java/execution/openapi/src/com/intellij/execution/configurations/JavaParameters.java +++ b/java/execution/openapi/src/com/intellij/execution/configurations/JavaParameters.java @@ -25,7 +25,6 @@ import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.roots.*; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.encoding.EncodingProjectManager; -import com.intellij.util.NotNullFunction; import com.intellij.util.PathsList; import com.intellij.util.text.VersionComparatorUtil; import org.intellij.lang.annotations.MagicConstant; @@ -94,16 +93,6 @@ public class JavaParameters extends SimpleJavaParameters { } } - @Nullable - private static NotNullFunction computeRootProvider(@MagicConstant(valuesFromClass = JavaParameters.class) int classPathType, final Sdk jdk) { - return (classPathType & JDK_ONLY) == 0 ? null : (NotNullFunction)orderEntry -> { - if (orderEntry instanceof JdkOrderEntry) { - return jdk.getRootProvider().getFiles(OrderRootType.CLASSES); - } - return orderEntry.getFiles(OrderRootType.CLASSES); - }; - } - public void setDefaultCharset(final Project project) { Charset encoding = EncodingProjectManager.getInstance(project).getDefaultCharset(); setCharset(encoding); @@ -189,9 +178,9 @@ public class JavaParameters extends SimpleJavaParameters { enumerator = enumerator.productionOnly(); } OrderRootsEnumerator rootsEnumerator = enumerator.classes(); - final NotNullFunction provider = computeRootProvider(classPathType, jdk); - if (provider != null) { - rootsEnumerator = rootsEnumerator.usingCustomRootProvider(provider); + if ((classPathType & JDK_ONLY) != 0) { + rootsEnumerator = rootsEnumerator.usingCustomRootProvider( + e -> e instanceof JdkOrderEntry ? jdk.getRootProvider().getFiles(OrderRootType.CLASSES) : e.getFiles(OrderRootType.CLASSES)); } return rootsEnumerator; } From 8f911b5934d19e4b4f10c239d8e2186aa32be602 Mon Sep 17 00:00:00 2001 From: Konstantin Bulenkov Date: Mon, 17 Oct 2016 19:29:24 +0200 Subject: [PATCH 6/6] roll back due to problems with navigation inside Nav Bar --- .../src/com/intellij/ide/navigationToolbar/NavBarPopup.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/ide/navigationToolbar/NavBarPopup.java b/platform/lang-impl/src/com/intellij/ide/navigationToolbar/NavBarPopup.java index d9de8b72e706..0148038ff6d8 100644 --- a/platform/lang-impl/src/com/intellij/ide/navigationToolbar/NavBarPopup.java +++ b/platform/lang-impl/src/com/intellij/ide/navigationToolbar/NavBarPopup.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -89,7 +89,6 @@ public class NavBarPopup extends LightweightHint implements Disposable{ @Override protected void onPopupCancel() { - myPanel.resetSelection(); // select last item if popup cancelled final JComponent component = getComponent(); if (component != null) { Object o = component.getClientProperty(JBLIST_KEY);