diff --git a/build/dependencies/gradle.properties b/build/dependencies/gradle.properties index 1a1041f0368a..4da6c636deb9 100644 --- a/build/dependencies/gradle.properties +++ b/build/dependencies/gradle.properties @@ -1,4 +1,4 @@ # The file might be automatically updated. Comments and empty lines will be removed. kotlinPluginBuild=1.1.2-release-IJ2017.2-1 jetSignBuild=42.30 -jdkBuild=u152b884.5 +jdkBuild=u152b915.1 diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/JavaValue.java b/java/debugger/impl/src/com/intellij/debugger/engine/JavaValue.java index 40cb2be97daa..b5fa6b017b1a 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/JavaValue.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/JavaValue.java @@ -92,7 +92,7 @@ public class JavaValue extends XNamedValue implements NodeDescriptorProvider, XV myContextSet = contextSet; } - static JavaValue create(JavaValue parent, + public static JavaValue create(JavaValue parent, @NotNull ValueDescriptorImpl valueDescriptor, @NotNull EvaluationContextImpl evaluationContext, NodeManagerImpl nodeManager, @@ -364,11 +364,6 @@ public class JavaValue extends XNamedValue implements NodeDescriptorProvider, XV return myValueDescriptor; } - @Override - public void setRemaining(int remaining) { - node.tooManyChildren(remaining); - } - @Override public void initChildrenArrayRenderer(ArrayRenderer renderer) { renderer.START_INDEX = myCurrentChildrenStart; @@ -408,6 +403,36 @@ public class JavaValue extends XNamedValue implements NodeDescriptorProvider, XV @Nullable XDebuggerTreeNodeHyperlink link) { node.setMessage(message, icon, attributes, link); } + + @Override + public void addChildren(@NotNull XValueChildrenList children, boolean last) { + node.addChildren(children, last); + } + + @Override + public void tooManyChildren(int remaining) { + node.tooManyChildren(remaining); + } + + @Override + public void setAlreadySorted(boolean alreadySorted) { + node.setAlreadySorted(alreadySorted); + } + + @Override + public void setErrorMessage(@NotNull String errorMessage) { + node.setErrorMessage(errorMessage); + } + + @Override + public void setErrorMessage(@NotNull String errorMessage, @Nullable XDebuggerTreeNodeHyperlink link) { + node.setErrorMessage(errorMessage, link); + } + + @Override + public boolean isObsolete() { + return node.isObsolete(); + } }, myEvaluationContext); } }); diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DebuggerTree.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DebuggerTree.java index 4114fc9b8ffe..2ae707d0f12c 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DebuggerTree.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/watch/DebuggerTree.java @@ -56,13 +56,17 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.ui.SimpleTextAttributes; import com.intellij.ui.SpeedSearchComparator; import com.intellij.ui.TreeSpeedSearch; +import com.intellij.xdebugger.frame.XDebuggerTreeNodeHyperlink; +import com.intellij.xdebugger.frame.XValueChildrenList; import com.intellij.xdebugger.settings.XDebuggerSettingsManager; import com.sun.jdi.*; import com.sun.jdi.event.Event; import com.sun.jdi.event.ExceptionEvent; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.event.TreeModelEvent; @@ -576,9 +580,6 @@ public abstract class DebuggerTree extends DebuggerTreeBase implements DataProvi return (ValueDescriptorImpl)getNode().getDescriptor(); } - @Override - public void setRemaining(int remaining) {} - @Override public void initChildrenArrayRenderer(ArrayRenderer renderer) {} @@ -591,6 +592,38 @@ public abstract class DebuggerTree extends DebuggerTreeBase implements DataProvi } updateUI(false); } + + @Override + public void addChildren(@NotNull XValueChildrenList children, boolean last) { + } + + @Override + public void tooManyChildren(int remaining) { + } + + @Override + public void setAlreadySorted(boolean alreadySorted) { + } + + @Override + public void setErrorMessage(@NotNull String errorMessage) { + } + + @Override + public void setErrorMessage(@NotNull String errorMessage, @Nullable XDebuggerTreeNodeHyperlink link) { + } + + @Override + public void setMessage(@NotNull String message, + @Nullable Icon icon, + @NotNull SimpleTextAttributes attributes, + @Nullable XDebuggerTreeNodeHyperlink link) { + } + + @Override + public boolean isObsolete() { + return false; + } } private class BuildStaticNodeCommand extends BuildNodeCommand { diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ArrayRenderer.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ArrayRenderer.java index f246c3785e38..7f01e744dec8 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ArrayRenderer.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ArrayRenderer.java @@ -21,21 +21,25 @@ import com.intellij.debugger.DebuggerManagerEx; import com.intellij.debugger.actions.ArrayAction; import com.intellij.debugger.engine.ContextUtil; import com.intellij.debugger.engine.DebuggerManagerThreadImpl; +import com.intellij.debugger.engine.JavaValue; import com.intellij.debugger.engine.evaluation.EvaluateException; import com.intellij.debugger.engine.evaluation.EvaluationContext; import com.intellij.debugger.engine.evaluation.EvaluationContextImpl; import com.intellij.debugger.engine.evaluation.TextWithImportsImpl; import com.intellij.debugger.impl.DebuggerUtilsEx; +import com.intellij.debugger.memory.utils.ErrorsValueGroup; import com.intellij.debugger.settings.NodeRendererSettings; import com.intellij.debugger.settings.ViewsGeneralSettings; import com.intellij.debugger.ui.impl.watch.ArrayElementDescriptorImpl; import com.intellij.debugger.ui.impl.watch.NodeManagerImpl; +import com.intellij.debugger.ui.impl.watch.ValueDescriptorImpl; import com.intellij.debugger.ui.tree.DebuggerTreeNode; import com.intellij.debugger.ui.tree.NodeDescriptor; import com.intellij.debugger.ui.tree.NodeDescriptorFactory; import com.intellij.debugger.ui.tree.ValueDescriptor; import com.intellij.icons.AllIcons; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.LanguageLevelProjectExtension; import com.intellij.openapi.util.DefaultJDOMExternalizer; import com.intellij.openapi.util.InvalidDataException; @@ -49,6 +53,7 @@ import com.intellij.ui.SimpleTextAttributes; import com.intellij.util.IncorrectOperationException; import com.intellij.xdebugger.XExpression; import com.intellij.xdebugger.frame.XDebuggerTreeNodeHyperlink; +import com.intellij.xdebugger.frame.XValueChildrenList; import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree; import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; import com.sun.jdi.ArrayReference; @@ -159,7 +164,7 @@ public class ArrayRenderer extends NodeRendererImpl{ builder.setMessage(DebuggerBundle.message("message.node.elements.null.hidden"), null, SimpleTextAttributes.REGULAR_ATTRIBUTES, null); } if (!myForced && END_INDEX < arrayLength - 1) { - builder.setRemaining(arrayLength - 1 - END_INDEX); + builder.tooManyChildren(arrayLength - 1 - END_INDEX); } } } @@ -252,6 +257,7 @@ public class ArrayRenderer extends NodeRendererImpl{ int added = 0; if (arrayLength - 1 >= START_INDEX) { + ErrorsValueGroup errorsGroup = null; for (int idx = START_INDEX; idx < arrayLength; idx++) { try { if (DebuggerUtilsEx.evaluateBoolean(cachedEvaluator.getEvaluator(evaluationContext.getProject()), @@ -269,7 +275,17 @@ public class ArrayRenderer extends NodeRendererImpl{ } } catch (EvaluateException e) { - builder.addChildren(Collections.singletonList(nodeManager.createMessageNode(e.getMessage())), false); + if (errorsGroup == null) { + errorsGroup = new ErrorsValueGroup(); + builder.addChildren(XValueChildrenList.bottomGroup(errorsGroup), false); + } + JavaValue childValue = JavaValue + .create(null, + (ValueDescriptorImpl)descriptorFactory.getArrayItemDescriptor(builder.getParentDescriptor(), array, idx), + ((EvaluationContextImpl)evaluationContext), + nodeManager, + false); + errorsGroup.addErrorValue(e.getMessage(), childValue); } } } diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ChildrenBuilder.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ChildrenBuilder.java index 416089ed0de7..1e4b406add12 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ChildrenBuilder.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ChildrenBuilder.java @@ -15,16 +15,15 @@ */ package com.intellij.debugger.ui.tree.render; -import com.intellij.debugger.ui.tree.*; -import com.intellij.ui.SimpleTextAttributes; -import com.intellij.xdebugger.frame.XDebuggerTreeNodeHyperlink; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +import com.intellij.debugger.ui.tree.DebuggerTreeNode; +import com.intellij.debugger.ui.tree.NodeDescriptorFactory; +import com.intellij.debugger.ui.tree.NodeManager; +import com.intellij.debugger.ui.tree.ValueDescriptor; +import com.intellij.xdebugger.frame.XCompositeNode; -import javax.swing.*; import java.util.List; -public interface ChildrenBuilder { +public interface ChildrenBuilder extends XCompositeNode { NodeDescriptorFactory getDescriptorManager(); NodeManager getNodeManager(); @@ -37,13 +36,10 @@ public interface ChildrenBuilder { setChildren(children); } - default void setMessage(@NotNull String message, - @Nullable Icon icon, - @NotNull SimpleTextAttributes attributes, - @Nullable XDebuggerTreeNodeHyperlink link) { + @Deprecated + default void setRemaining(int remaining) { + tooManyChildren(remaining); } - void setRemaining(int remaining); - void initChildrenArrayRenderer(ArrayRenderer renderer); } diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java index af791a1cf50a..d58fe33d0cfd 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java @@ -1579,11 +1579,13 @@ public class HighlightUtil extends HighlightUtilBase { final PsiElement parent = expr.getParent(); final PsiElement resolved = parent instanceof PsiReferenceExpression ? ((PsiReferenceExpression)parent).resolve() : null; + PsiClass containingClass = + ObjectUtils.notNull(resolved instanceof PsiMethod ? ((PsiMethod)resolved).getContainingClass() : null, aClass); for (PsiClass superClass : classT.getSupers()) { - if (superClass.isInheritor(aClass, true)) { + if (superClass.isInheritor(containingClass, true)) { String cause = null; - if (superClass.isInterface()) { - cause = "redundant interface " + format(aClass) + " is extended by "; + if (superClass.isInheritor(aClass, true) && superClass.isInterface()) { + cause = "redundant interface " + format(containingClass) + " is extended by "; } else if (resolved instanceof PsiMethod && MethodSignatureUtil.findMethodBySuperMethod(superClass, (PsiMethod)resolved, true) != resolved) { diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/QualifySuperArgumentFix.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/QualifySuperArgumentFix.java index 6cfc3fc07868..b7ff8c8954f5 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/QualifySuperArgumentFix.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/QualifySuperArgumentFix.java @@ -20,6 +20,7 @@ import com.intellij.codeInsight.daemon.impl.HighlightInfo; import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.refactoring.util.RefactoringChangeUtil; +import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; public class QualifySuperArgumentFix extends QualifyThisOrSuperArgumentFix { @@ -50,7 +51,14 @@ public class QualifySuperArgumentFix extends QualifyThisOrSuperArgumentFix { final PsiExpression superQualifierCopy = copy.getMethodExpression().getQualifierExpression(); LOG.assertTrue(superQualifierCopy != null); superQualifierCopy.delete(); - PsiMethod method = ((PsiMethodCallExpression)elementFactory.createExpressionFromText(copy.getText(), superClass)).resolveMethod(); + PsiMethod method; + try { + method = ((PsiMethodCallExpression)elementFactory.createExpressionFromText(copy.getText(), superClass)).resolveMethod(); + } + catch (IncorrectOperationException e) { + LOG.info(e); + return; + } if (method != null && !method.hasModifierProperty(PsiModifier.ABSTRACT)) { QuickFixAction.registerQuickFixAction(highlightInfo, new QualifySuperArgumentFix(expr, superClass)); } diff --git a/java/java-impl/src/com/intellij/codeInspection/MakeVoidQuickFix.java b/java/java-impl/src/com/intellij/codeInspection/MakeVoidQuickFix.java index 95c2f9ab09b0..3d867c9174dd 100644 --- a/java/java-impl/src/com/intellij/codeInspection/MakeVoidQuickFix.java +++ b/java/java-impl/src/com/intellij/codeInspection/MakeVoidQuickFix.java @@ -83,18 +83,12 @@ public class MakeVoidQuickFix implements LocalQuickFix { for (final PsiMethod oMethod : OverridingMethodsSearch.search(psiMethod)) { replaceReturnStatements(oMethod); } - final PsiParameter[] params = psiMethod.getParameterList().getParameters(); - final ParameterInfoImpl[] infos = new ParameterInfoImpl[params.length]; - for (int i = 0; i < params.length; i++) { - PsiParameter param = params[i]; - infos[i] = new ParameterInfoImpl(i, param.getName(), param.getType()); - } - + final ChangeSignatureProcessor csp = new ChangeSignatureProcessor(project, psiMethod, false, null, psiMethod.getName(), PsiType.VOID, - infos); + ParameterInfoImpl.fromMethod(psiMethod)); csp.run(); } diff --git a/java/java-impl/src/com/intellij/lang/java/JavaRefactoringSupportProvider.java b/java/java-impl/src/com/intellij/lang/java/JavaRefactoringSupportProvider.java index 7c62f3d89c92..685744a8c4f1 100644 --- a/java/java-impl/src/com/intellij/lang/java/JavaRefactoringSupportProvider.java +++ b/java/java-impl/src/com/intellij/lang/java/JavaRefactoringSupportProvider.java @@ -33,6 +33,7 @@ import com.intellij.refactoring.extractclass.ExtractClassHandler; import com.intellij.refactoring.introduceField.IntroduceConstantHandler; import com.intellij.refactoring.introduceField.IntroduceFieldHandler; import com.intellij.refactoring.introduceParameter.IntroduceParameterHandler; +import com.intellij.refactoring.introduceVariable.IntroduceFunctionalVariableHandler; import com.intellij.refactoring.introduceVariable.IntroduceVariableHandler; import com.intellij.refactoring.memberPullUp.JavaPullUpHandler; import com.intellij.refactoring.memberPushDown.JavaPushDownHandler; @@ -92,6 +93,11 @@ public class JavaRefactoringSupportProvider extends RefactoringSupportProvider { return new IntroduceFunctionalParameterHandler(); } + @Override + public RefactoringActionHandler getIntroduceFunctionalVariableHandler() { + return new IntroduceFunctionalVariableHandler(); + } + @Override public RefactoringActionHandler getPullUpHandler() { return new JavaPullUpHandler(); diff --git a/java/java-impl/src/com/intellij/refactoring/extractMethod/ExtractMethodDialog.java b/java/java-impl/src/com/intellij/refactoring/extractMethod/ExtractMethodDialog.java index e69233b24e3e..083df697eb10 100644 --- a/java/java-impl/src/com/intellij/refactoring/extractMethod/ExtractMethodDialog.java +++ b/java/java-impl/src/com/intellij/refactoring/extractMethod/ExtractMethodDialog.java @@ -124,6 +124,7 @@ public class ExtractMethodDialog extends DialogWrapper implements AbstractExtrac if (canBeChainedConstructor) { myCbChainedConstructor = new NonFocusableCheckBox(RefactoringBundle.message("extract.chained.constructor.checkbox")); } + myInputVariables = myVariableData.getInputVariables().toArray(new VariableData[myVariableData.getInputVariables().size()]); init(); } @@ -308,28 +309,7 @@ public class ExtractMethodDialog extends DialogWrapper implements AbstractExtrac //optionsPanel.add(new JLabel("Options: ")); - if (myStaticFlag || myCanBeStatic) { - myMakeStatic.setEnabled(!myStaticFlag); - myMakeStatic.setSelected(myStaticFlag); - if (myVariableData.hasInstanceFields()) { - myMakeStatic.setText(RefactoringBundle.message("declare.static.pass.fields.checkbox")); - } - myMakeStatic.addItemListener(e -> { - if (myVariableData.hasInstanceFields()) { - myVariableData.setPassFields(myMakeStatic.isSelected()); - myInputVariables = myVariableData.getInputVariables().toArray(new VariableData[myVariableData.getInputVariables().size()]); - updateVarargsEnabled(); - createParametersPanel(); - } - updateSignature(); - }); - optionsPanel.add(myMakeStatic); - } else { - myMakeStatic.setSelected(false); - myMakeStatic.setEnabled(false); - } - final Border emptyBorder = IdeBorderFactory.createEmptyBorder(5, 0, 5, 4); - myMakeStatic.setBorder(emptyBorder); + createStaticOptions(optionsPanel, RefactoringBundle.message("declare.static.pass.fields.checkbox")); myFoldParameters.setSelected(myVariableData.isFoldingSelectedByDefault()); myFoldParameters.setVisible(myVariableData.isFoldable()); @@ -344,6 +324,7 @@ public class ExtractMethodDialog extends DialogWrapper implements AbstractExtrac updateSignature(); }); optionsPanel.add(myFoldParameters); + final Border emptyBorder = IdeBorderFactory.createEmptyBorder(5, 0, 5, 4); myFoldParameters.setBorder(emptyBorder); boolean canBeVarargs = false; @@ -394,6 +375,30 @@ public class ExtractMethodDialog extends DialogWrapper implements AbstractExtrac return optionsPanel; } + protected void createStaticOptions(JPanel optionsPanel, String passFieldsAsParamsLabel) { + if (myStaticFlag || myCanBeStatic) { + myMakeStatic.setEnabled(!myStaticFlag); + myMakeStatic.setSelected(myStaticFlag); + if (myVariableData.hasInstanceFields()) { + myMakeStatic.setText(passFieldsAsParamsLabel); + } + myMakeStatic.addItemListener(e -> { + if (myVariableData.hasInstanceFields()) { + myVariableData.setPassFields(myMakeStatic.isSelected()); + myInputVariables = myVariableData.getInputVariables().toArray(new VariableData[myVariableData.getInputVariables().size()]); + updateVarargsEnabled(); + createParametersPanel(); + } + updateSignature(); + }); + optionsPanel.add(myMakeStatic); + } else { + myMakeStatic.setSelected(false); + myMakeStatic.setEnabled(false); + } + myMakeStatic.setBorder(IdeBorderFactory.createEmptyBorder(5, 0, 5, 4)); + } + private ComboBoxVisibilityPanel createVisibilityPanel() { final JavaComboBoxVisibilityPanel panel = new JavaComboBoxVisibilityPanel(); final PsiMethod containingMethod = getContainingMethod(); @@ -432,7 +437,8 @@ public class ExtractMethodDialog extends DialogWrapper implements AbstractExtrac @Override @NotNull public String getVisibility() { - return myTargetClass.isInterface() ? PsiModifier.PUBLIC : ObjectUtils.notNull(myVisibilityPanel.getVisibility(), PsiModifier.PUBLIC); + return myTargetClass.isInterface() || myVisibilityPanel == null + ? PsiModifier.PUBLIC : ObjectUtils.notNull(myVisibilityPanel.getVisibility(), PsiModifier.PUBLIC); } @Override diff --git a/java/java-impl/src/com/intellij/refactoring/extractMethod/ExtractMethodProcessor.java b/java/java-impl/src/com/intellij/refactoring/extractMethod/ExtractMethodProcessor.java index 302bd6c5f96b..79c79d81e084 100644 --- a/java/java-impl/src/com/intellij/refactoring/extractMethod/ExtractMethodProcessor.java +++ b/java/java-impl/src/com/intellij/refactoring/extractMethod/ExtractMethodProcessor.java @@ -549,8 +549,7 @@ public class ExtractMethodProcessor implements MatchProvider { } protected AbstractExtractDialog createExtractMethodDialog(final boolean direct) { - final List variables = myInputVariables.getInputVariables(); - myVariableDatum = variables.toArray(new VariableData[variables.size()]); + setDataFromInputVariables(); myNullness = initNullness(); myArtificialOutputVariable = PsiType.VOID.equals(myReturnType) ? getArtificialOutputVariable() : null; final PsiType returnType = myArtificialOutputVariable != null ? myArtificialOutputVariable.getType() : myReturnType; @@ -608,6 +607,11 @@ public class ExtractMethodProcessor implements MatchProvider { }; } + public void setDataFromInputVariables() { + final List variables = myInputVariables.getInputVariables(); + myVariableDatum = variables.toArray(new VariableData[variables.size()]); + } + public PsiExpression[] findOccurrences() { if (myExpression != null) { return new PsiExpression[] {myExpression}; @@ -1350,7 +1354,7 @@ public class ExtractMethodProcessor implements MatchProvider { } list.add(parm); } - else { + else if (defineVariablesForUnselectedParameters()){ @NonNls StringBuilder buffer = new StringBuilder(); if (isFinal) { buffer.append("final "); @@ -1382,6 +1386,10 @@ public class ExtractMethodProcessor implements MatchProvider { return (PsiMethod)myStyleManager.reformat(newMethod); } + protected boolean defineVariablesForUnselectedParameters() { + return true; + } + private void copyParamAnnotations(PsiParameter parm) { final PsiVariable variable = PsiResolveHelper.SERVICE.getInstance(myProject).resolveReferencedVariable(parm.getName(), myElements[0]); if (variable instanceof PsiParameter) { @@ -1735,6 +1743,10 @@ public class ExtractMethodProcessor implements MatchProvider { return myExtractedMethod; } + public void setMethodName(String methodName) { + myMethodName = methodName; + } + public Boolean hasDuplicates() { List duplicates = getDuplicates(); if (duplicates != null && !duplicates.isEmpty()) { @@ -1831,4 +1843,8 @@ public class ExtractMethodProcessor implements MatchProvider { public PsiVariable[] getOutputVariables() { return myOutputVariables; } + + public void setMethodVisibility(String methodVisibility) { + myMethodVisibility = methodVisibility; + } } 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 1cbc47bc510c..e8521d613eac 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceParameter/IntroduceParameterHandler.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceParameter/IntroduceParameterHandler.java @@ -548,20 +548,7 @@ public class IntroduceParameterHandler extends IntroduceHandlerBase { return false; } - final PsiElement[] elementsCopy; - if (!elements[0].isPhysical()) { - elementsCopy = elements; - } - else { - final PsiFile copy = PsiFileFactory.getInstance(project) - .createFileFromText(file.getName(), file.getFileType(), file.getText(), file.getModificationStamp(), false); - final TextRange range = new TextRange(elements[0].getTextRange().getStartOffset(), - elements[elements.length - 1].getTextRange().getEndOffset()); - final PsiExpression exprInRange = CodeInsightUtil.findExpressionInRange(copy, range.getStartOffset(), range.getEndOffset()); - elementsCopy = exprInRange != null - ? new PsiElement[]{exprInRange} - : CodeInsightUtil.findStatementsInRange(copy, range.getStartOffset(), range.getEndOffset()); - } + final PsiElement[] elementsCopy = getElementsInCopy(project, file, elements); final PsiMethod containingMethodCopy = Util.getContainingMethod(elementsCopy[0]); LOG.assertTrue(containingMethodCopy != null); final List enclosingMethodsInCopy = getEnclosingMethods(containingMethodCopy); @@ -613,6 +600,28 @@ public class IntroduceParameterHandler extends IntroduceHandlerBase { return false; } + public static PsiElement[] getElementsInCopy(Project project, PsiFile file, PsiElement[] elements) { + final PsiElement[] elementsCopy; + if (!elements[0].isPhysical()) { + elementsCopy = elements; + } + else { + final PsiFile copy = PsiFileFactory.getInstance(project) + .createFileFromText(file.getName(), file.getFileType(), file.getText(), file.getModificationStamp(), false); + final TextRange range = new TextRange(elements[0].getTextRange().getStartOffset(), + elements[elements.length - 1].getTextRange().getEndOffset()); + final PsiExpression exprInRange = CodeInsightUtil.findExpressionInRange(copy, range.getStartOffset(), range.getEndOffset()); + elementsCopy = exprInRange != null + ? new PsiElement[]{exprInRange} + : CodeInsightUtil.findStatementsInRange(copy, range.getStartOffset(), range.getEndOffset()); + } + if (elementsCopy.length == 1 && elementsCopy[0].getUserData(ElementToWorkOn.PARENT) == null) { + elementsCopy[0].putUserData(ElementToWorkOn.REPLACE_NON_PHYSICAL, true); + } + + return elementsCopy; + } + private void functionalInterfaceSelected(final PsiType selectedType, final List enclosingMethods, final Project project, @@ -628,7 +637,7 @@ public class IntroduceParameterHandler extends IntroduceHandlerBase { PsiMethod methodToSearchFor, Editor editor, final Project project, final PsiType selectedType, - final MyExtractMethodProcessor processor, + final ExtractMethodProcessor processor, final PsiElement[] elements) { final PsiElement commonParent = findCommonParent(elements); if (commonParent == null) { @@ -657,11 +666,6 @@ public class IntroduceParameterHandler extends IntroduceHandlerBase { LOG.assertTrue(method != null); final String interfaceMethodName = method.getName(); processor.setMethodName(interfaceMethodName); - - if (copyElements.length == 1 && copyElements[0].getUserData(ElementToWorkOn.PARENT) == null) { - copyElements[0].putUserData(ElementToWorkOn.REPLACE_NON_PHYSICAL, true); - } - processor.doExtract(); final PsiMethod extractedMethod = processor.getExtractedMethod(); @@ -730,10 +734,6 @@ public class IntroduceParameterHandler extends IntroduceHandlerBase { return false; } - public void setMethodName(String methodName) { - myMethodName = methodName; - } - @Override public Boolean hasDuplicates() { return false; diff --git a/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceFunctionalVariableHandler.java b/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceFunctionalVariableHandler.java new file mode 100644 index 000000000000..c47ddc0ef168 --- /dev/null +++ b/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceFunctionalVariableHandler.java @@ -0,0 +1,320 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.refactoring.introduceVariable; + +import com.intellij.codeInsight.FunctionalInterfaceSuggester; +import com.intellij.codeInsight.navigation.NavigationUtil; +import com.intellij.ide.util.PsiClassListCellRenderer; +import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Pass; +import com.intellij.psi.*; +import com.intellij.psi.codeStyle.JavaCodeStyleManager; +import com.intellij.psi.codeStyle.SuggestedNameInfo; +import com.intellij.psi.search.PsiElementProcessor; +import com.intellij.psi.util.PsiFormatUtil; +import com.intellij.psi.util.PsiFormatUtilBase; +import com.intellij.psi.util.PsiUtil; +import com.intellij.refactoring.HelpID; +import com.intellij.refactoring.RefactoringBundle; +import com.intellij.refactoring.actions.IntroduceFunctionalVariableAction; +import com.intellij.refactoring.extractMethod.*; +import com.intellij.refactoring.introduceParameter.IntroduceParameterHandler; +import com.intellij.refactoring.rename.inplace.VariableInplaceRenamer; +import com.intellij.refactoring.util.CommonRefactoringUtil; +import com.intellij.refactoring.util.RefactoringUtil; +import com.intellij.util.ObjectUtils; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; +import java.awt.*; +import java.util.Arrays; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.Map; + +public class IntroduceFunctionalVariableHandler extends IntroduceVariableHandler { + + @Override + public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file, DataContext dataContext) { + ExtractMethodHandler.selectAndPass(project, editor, file, new Pass() { + @Override + public void pass(PsiElement[] elements) { + PsiElement anchorStatement = RefactoringUtil.getParentStatement(elements[0], false); + PsiElement tempContainer = checkAnchorStatement(project, editor, anchorStatement); + if (tempContainer == null) return; + + PsiElement[] elementsInCopy = IntroduceParameterHandler.getElementsInCopy(project, file, elements); + MyExtractMethodProcessor processor = + new MyExtractMethodProcessor(project, editor, elementsInCopy, null, IntroduceFunctionalVariableAction.REFACTORING_NAME, null, + HelpID.INTRODUCE_VARIABLE); + try { + processor.prepare(); + } + catch (PrepareFailedException e) { + showErrorMessage(project, editor); + } + + if (!processor.showDialog()) return; + + final PsiMethod emptyMethod = JavaPsiFacade.getElementFactory(project) + .createMethodFromText(processor.generateEmptyMethod("name").getText(), elements[0]); + final Collection types = FunctionalInterfaceSuggester.suggestFunctionalInterfaces(emptyMethod); + if (types.isEmpty()) { + showErrorMessage(project, editor, "No applicable functional interfaces found"); + return; + } + if (types.size() == 1 || ApplicationManager.getApplication().isUnitTestMode()) { + functionalInterfaceSelected(ContainerUtil.getFirstItem(types), project, editor, processor, elements, anchorStatement); + } + else { + final Map classes = new LinkedHashMap<>(); + for (PsiType type : types) { + classes.put(PsiUtil.resolveClassInType(type), type); + } + final PsiClass[] psiClasses = classes.keySet().toArray(new PsiClass[classes.size()]); + final String methodSignature = + PsiFormatUtil.formatMethod(emptyMethod, PsiSubstitutor.EMPTY, PsiFormatUtilBase.SHOW_PARAMETERS, PsiFormatUtilBase.SHOW_TYPE); + final PsiType returnType = emptyMethod.getReturnType(); + assert returnType != null; + final String title = "Choose Applicable Functional Interface: " + methodSignature + " -> " + returnType.getPresentableText(); + NavigationUtil.getPsiElementPopup(psiClasses, new PsiClassListCellRenderer(), title, + new PsiElementProcessor() { + @Override + public boolean execute(@NotNull PsiClass psiClass) { + functionalInterfaceSelected(classes.get(psiClass), project, editor, processor, elements, + anchorStatement); + return true; + } + }).showInBestPositionFor(editor); + } + } + }); + } + + private static void functionalInterfaceSelected(PsiType type, + Project project, + Editor editor, + MyExtractMethodProcessor processor, + PsiElement[] elements, + PsiElement anchorStatement) { + if (!CommonRefactoringUtil.checkReadOnlyStatus(project, elements[0])) return; + PsiMethodCallExpression functionalExpression = createReplacement(project, type, processor, elements); + + PsiExpression qualifier = functionalExpression.getMethodExpression().getQualifierExpression(); + + assert qualifier != null; + + SuggestedNameInfo uniqueNames = getSuggestedName(type, qualifier, anchorStatement); + + WriteCommandAction.runWriteCommandAction(project, () -> { + PsiDeclarationStatement declaration = + replaceSelectionWithFunctionalCall(type, elements, anchorStatement, functionalExpression, qualifier, uniqueNames.names[0]); + + PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(editor.getDocument()); + + PsiLocalVariable localVariable = (PsiLocalVariable)declaration.getDeclaredElements()[0]; + PsiIdentifier nameIdentifier = localVariable.getNameIdentifier(); + final int textOffset = ObjectUtils.notNull(nameIdentifier, localVariable).getTextOffset(); + editor.getCaretModel().moveToOffset(textOffset); + new VariableInplaceRenamer(localVariable, editor) { + @Override + protected boolean shouldSelectAll() { + return true; + } + + @Override + protected void moveOffsetAfter(boolean success) { + super.moveOffsetAfter(success); + if (success) { + final PsiNamedElement renamedVariable = getVariable(); + if (renamedVariable != null) { + editor.getCaretModel().moveToOffset(renamedVariable.getTextRange().getEndOffset()); + } + } + } + }.performInplaceRename(); + }); + } + + private static PsiDeclarationStatement replaceSelectionWithFunctionalCall(PsiType type, + PsiElement[] elements, + PsiElement anchorStatement, + PsiMethodCallExpression functionalExpression, + PsiExpression qualifier, + String variableName) { + PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(functionalExpression.getProject()); + PsiElement tempContainer = anchorStatement.getParent(); + + boolean singleExpression = elements.length == 1 && elements[0] instanceof PsiExpression; + PsiDeclarationStatement declaration = elementFactory + .createVariableDeclarationStatement(variableName, type, qualifier, anchorStatement); + String callExpressionText = variableName + "." + + functionalExpression.getMethodExpression().getReferenceName() + + functionalExpression.getArgumentList().getText(); + if (singleExpression) { + elements[0].replace(elementFactory.createExpressionFromText(callExpressionText, declaration)); + } + if (RefactoringUtil.isLoopOrIf(tempContainer)) { + declaration = (PsiDeclarationStatement)RefactoringUtil.putStatementInLoopBody(declaration, tempContainer, anchorStatement, !singleExpression); + tempContainer = declaration.getParent(); + } + else { + declaration = (PsiDeclarationStatement)tempContainer.addBefore(declaration, anchorStatement); + if (!singleExpression) { + tempContainer.deleteChildRange(elements[0], elements[elements.length - 1]); + } + } + + if (!singleExpression) { + tempContainer.addAfter(elementFactory.createStatementFromText(callExpressionText + ";", declaration), declaration); + } + return (PsiDeclarationStatement)JavaCodeStyleManager.getInstance(declaration.getProject()).shortenClassReferences(declaration); + } + + private static PsiMethodCallExpression createReplacement(Project project, + PsiType selectedType, + ExtractMethodProcessor processor, + PsiElement[] elements) { + final PsiClassType.ClassResolveResult resolveResult = PsiUtil.resolveGenericsClassInType(selectedType); + final PsiClass wrapperClass = resolveResult.getElement(); + + final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project); + final PsiMethod method = LambdaUtil.getFunctionalInterfaceMethod(wrapperClass); + assert method != null : "not functional class"; + final String interfaceMethodName = method.getName(); + processor.setMethodName(interfaceMethodName); + processor.doExtract(); + + final PsiMethod extractedMethod = processor.getExtractedMethod(); + final PsiParameter[] parameters = extractedMethod.getParameterList().getParameters(); + final PsiParameter[] interfaceParameters = method.getParameterList().getParameters(); + final PsiSubstitutor substitutor = resolveResult.getSubstitutor(); + for (int i = 0; i < interfaceParameters.length; i++) { + final PsiTypeElement typeAfterInterface = factory.createTypeElement(substitutor.substitute(interfaceParameters[i].getType())); + final PsiTypeElement typeElement = parameters[i].getTypeElement(); + if (typeElement != null) { + typeElement.replace(typeAfterInterface); + } + } + final PsiMethodCallExpression methodCall = processor.getMethodCall(); + PsiExpression psiExpression = factory + .createExpressionFromText("new " + selectedType.getCanonicalText() + "() {" + extractedMethod.getText() + "}." + methodCall.getText(), + elements[0]); + return (PsiMethodCallExpression)JavaCodeStyleManager.getInstance(project).shortenClassReferences(psiExpression); + } + + + @Override + protected void showErrorMessage(Project project, Editor editor, String message) { + CommonRefactoringUtil + .showErrorHint(project, editor, message, IntroduceFunctionalVariableAction.REFACTORING_NAME, HelpID.INTRODUCE_VARIABLE); + } + + private void showErrorMessage(@NotNull Project project, Editor editor) { + final String message = RefactoringBundle + .getCannotRefactorMessage( + RefactoringBundle.message("is.not.supported.in.the.current.context", IntroduceFunctionalVariableAction.REFACTORING_NAME)); + showErrorMessage(project, editor, message); + } + + protected void setupProcessorWithoutDialog(ExtractMethodProcessor processor, InputVariables inputVariables) { + processor.setDataFromInputVariables(); + processor.setMethodVisibility(PsiModifier.PUBLIC); + } + + private class MyExtractMethodProcessor extends ExtractMethodProcessor { + + public MyExtractMethodProcessor(Project project, + Editor editor, + PsiElement[] elements, + PsiType forcedReturnType, + String refactoringName, String initialMethodName, String helpId) { + super(project, editor, elements, forcedReturnType, refactoringName, initialMethodName, helpId); + } + + @Override + public boolean isStatic() { + return false; + } + + @Override + protected boolean isFoldingApplicable() { + return false; + } + + @Override + protected AbstractExtractDialog createExtractMethodDialog(boolean direct) { + setDataFromInputVariables(); + return new ExtractMethodDialog(myProject, myTargetClass, myInputVariables, null, getTypeParameterList(), + getThrownExceptions(), isStatic(), isCanBeStatic(), false, + IntroduceFunctionalVariableAction.REFACTORING_NAME, HelpID.INTRODUCE_VARIABLE, null, myElements) { + @Override + protected JComponent createNorthPanel() { + if (!myInputVariables.hasInstanceFields()) { + return null; + } + JPanel optionsPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 5)); + createStaticOptions(optionsPanel, "Pass fields as params"); + return optionsPanel; + } + + @Override + public JComponent getPreferredFocusedComponent() { + return myParamTable; + } + + @Override + protected String getSignature() { + String parametersList = + Arrays.stream(getChosenParameters()) + .filter(data -> data.passAsParameter) + .map(data -> data.type.getPresentableText()) + .reduce((result, item) -> result + ", " + item) + .orElse(""); + String returnTypeString = myReturnType == null || PsiType.VOID.equals(myReturnType) + ? "{}" : myReturnType.getPresentableText(); + return "(" + parametersList + ") -> " + returnTypeString; + } + + @NotNull + @Override + public String getVisibility() { + return PsiModifier.PUBLIC; + } + }; + } + + @Override + public boolean showDialog() { + if (!myInputVariables.hasInstanceFields() && myInputVariables.getInputVariables().isEmpty() || + ApplicationManager.getApplication().isUnitTestMode()) { + setupProcessorWithoutDialog(this, myInputVariables); + return true; + } + return super.showDialog(); + } + + @Override + protected boolean defineVariablesForUnselectedParameters() { + return false; + } + } +} diff --git a/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java b/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java index 32effde01998..4a64cd4bdaeb 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceVariable/IntroduceVariableBase.java @@ -613,18 +613,8 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase { final PsiElement anchorStatement = RefactoringUtil.getParentStatement(physicalElement != null ? physicalElement : expr, false); - if (anchorStatement == null) { - return parentStatementNotFound(project, editor); - } - if (checkAnchorBeforeThisOrSuper(project, editor, anchorStatement, REFACTORING_NAME, HelpID.INTRODUCE_VARIABLE)) return false; - - final PsiElement tempContainer = anchorStatement.getParent(); - - if (!(tempContainer instanceof PsiCodeBlock) && !RefactoringUtil.isLoopOrIf(tempContainer) && !(tempContainer instanceof PsiLambdaExpression) && (tempContainer.getParent() instanceof PsiLambdaExpression)) { - String message = RefactoringBundle.message("refactoring.is.not.supported.in.the.current.context", REFACTORING_NAME); - showErrorMessage(project, editor, message); - return false; - } + PsiElement tempContainer = checkAnchorStatement(project, editor, anchorStatement); + if (tempContainer == null) return false; final PsiFile file = anchorStatement.getContainingFile(); LOG.assertTrue(file != null, "expr.getContainingFile() == null"); @@ -766,6 +756,24 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase { return wasSucceed[0]; } + protected PsiElement checkAnchorStatement(Project project, Editor editor, PsiElement anchorStatement) { + if (anchorStatement == null) { + String message = RefactoringBundle.message("refactoring.is.not.supported.in.the.current.context", REFACTORING_NAME); + showErrorMessage(project, editor, message); + return null; + } + if (checkAnchorBeforeThisOrSuper(project, editor, anchorStatement, REFACTORING_NAME, HelpID.INTRODUCE_VARIABLE)) return null; + + final PsiElement tempContainer = anchorStatement.getParent(); + + if (!(tempContainer instanceof PsiCodeBlock) && !RefactoringUtil.isLoopOrIf(tempContainer) && !(tempContainer instanceof PsiLambdaExpression) && (tempContainer.getParent() instanceof PsiLambdaExpression)) { + String message = RefactoringBundle.message("refactoring.is.not.supported.in.the.current.context", REFACTORING_NAME); + showErrorMessage(project, editor, message); + return null; + } + return tempContainer; + } + protected JavaReplaceChoice getOccurrencesChoice() { return null; } @@ -1110,12 +1118,6 @@ public abstract class IntroduceVariableBase extends IntroduceHandlerBase { : factory.createExpressionFromText(text, parent); } - private boolean parentStatementNotFound(final Project project, Editor editor) { - String message = RefactoringBundle.message("refactoring.is.not.supported.in.the.current.context", REFACTORING_NAME); - showErrorMessage(project, editor, message); - return false; - } - protected boolean invokeImpl(Project project, PsiLocalVariable localVariable, Editor editor) { throw new UnsupportedOperationException(); } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java index ba58f4e48a3d..1addc14dfc38 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/graphInference/InferenceSession.java @@ -1984,8 +1984,8 @@ public class InferenceSession { final PsiSubstitutor sSubstitutor = TypeConversionUtil.getSuperClassSubstitutor(gClass, (PsiClassType)sBound); final PsiSubstitutor tSubstitutor = TypeConversionUtil.getSuperClassSubstitutor(gClass, (PsiClassType)tBound); for (PsiTypeParameter typeParameter : gClass.getTypeParameters()) { - final PsiType sType = sSubstitutor.substitute(typeParameter); - final PsiType tType = tSubstitutor.substitute(typeParameter); + final PsiType sType = sSubstitutor.substituteWithBoundsPromotion(typeParameter); + final PsiType tType = tSubstitutor.substituteWithBoundsPromotion(typeParameter); final Pair typePair = Pair.create(sType, tType); if (!processor.process(typePair)) { return gClass; diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/graphInference/ValidIntersectionTypeWithCapturedBounds.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/graphInference/ValidIntersectionTypeWithCapturedBounds.java new file mode 100644 index 000000000000..d66c2f810a19 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/graphInference/ValidIntersectionTypeWithCapturedBounds.java @@ -0,0 +1,11 @@ + +abstract class Bug { + { + D _m = m(); + } + + abstract > J m(); +} + +abstract class C { } +abstract class D extends C { } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newMethodRef/TypeNameInterfaceSuperMethodReferenceApplicability.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newMethodRef/TypeNameInterfaceSuperMethodReferenceApplicability.java index 5fc439fd75e8..0a030b150e1e 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newMethodRef/TypeNameInterfaceSuperMethodReferenceApplicability.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/newMethodRef/TypeNameInterfaceSuperMethodReferenceApplicability.java @@ -81,3 +81,27 @@ class InsideThisRxpression { public interface Bar extends Foo {} } +class SameDefaultMethodDifferentInheritors { + interface A { default void a() {} } + interface B extends A { default void a() {} } + interface B1 extends A { } + interface C extends A {} + + class Clazz implements B, C { + { + C.super.a(); + } + } + + class Clazz1 implements B1, C { + { + C.super.a(); + } + } + + class Clazz2 implements C { + { + C.super.a(); + } + } +} diff --git a/java/java-tests/testData/refactoring/introduceFunctionalVariable/afterExpressionInLoopNoVars.java b/java/java-tests/testData/refactoring/introduceFunctionalVariable/afterExpressionInLoopNoVars.java new file mode 100644 index 000000000000..623367381340 --- /dev/null +++ b/java/java-tests/testData/refactoring/introduceFunctionalVariable/afterExpressionInLoopNoVars.java @@ -0,0 +1,14 @@ +import java.util.function.Supplier; + +class Test { + void foo() { + if (true) { + Supplier stringSupplier = new Supplier() { + public String get() { + return "Hello, world"; + } + }; + System.out.println(stringSupplier.get()); + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/introduceFunctionalVariable/afterExpressionNoVarsSelected.java b/java/java-tests/testData/refactoring/introduceFunctionalVariable/afterExpressionNoVarsSelected.java new file mode 100644 index 000000000000..6b0cf66e8d5e --- /dev/null +++ b/java/java-tests/testData/refactoring/introduceFunctionalVariable/afterExpressionNoVarsSelected.java @@ -0,0 +1,12 @@ +import java.util.function.Supplier; + +class Test { + void foo() { + Supplier stringSupplier = new Supplier() { + public String get() { + return "Hello, world"; + } + }; + System.out.println(stringSupplier.get()); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/introduceFunctionalVariable/afterPassFieldsAsParameters.java b/java/java-tests/testData/refactoring/introduceFunctionalVariable/afterPassFieldsAsParameters.java new file mode 100644 index 000000000000..efc2cc7829b6 --- /dev/null +++ b/java/java-tests/testData/refactoring/introduceFunctionalVariable/afterPassFieldsAsParameters.java @@ -0,0 +1,15 @@ +import java.util.function.Consumer; + +class Test { + String myName; + void foo() { + if (true) { + Consumer stringConsumer = new Consumer() { + public void accept(String myName) { + System.out.println("Hello, world " + myName); + } + }; + stringConsumer.accept(myName); + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/introduceFunctionalVariable/afterSkipUsedLocals.java b/java/java-tests/testData/refactoring/introduceFunctionalVariable/afterSkipUsedLocals.java new file mode 100644 index 000000000000..bbf61ac0ffb9 --- /dev/null +++ b/java/java-tests/testData/refactoring/introduceFunctionalVariable/afterSkipUsedLocals.java @@ -0,0 +1,11 @@ +class Test { + void foo(String name) { + System.out.println("Hello, "); + Runnable runnable = new Runnable() { + public void run() { + System.out.println(name); + } + }; + runnable.run(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/introduceFunctionalVariable/afterStatementInLoop.java b/java/java-tests/testData/refactoring/introduceFunctionalVariable/afterStatementInLoop.java new file mode 100644 index 000000000000..d37ee07557f9 --- /dev/null +++ b/java/java-tests/testData/refactoring/introduceFunctionalVariable/afterStatementInLoop.java @@ -0,0 +1,14 @@ +import java.util.function.Consumer; + +class Test { + void foo(String s) { + if (true) { + Consumer stringConsumer = new Consumer() { + public void accept(String s) { + System.out.println("Hello, world " + s); + } + }; + stringConsumer.accept(s); + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/introduceFunctionalVariable/afterStatements.java b/java/java-tests/testData/refactoring/introduceFunctionalVariable/afterStatements.java new file mode 100644 index 000000000000..a92ac6ec3dfe --- /dev/null +++ b/java/java-tests/testData/refactoring/introduceFunctionalVariable/afterStatements.java @@ -0,0 +1,17 @@ +import java.util.function.Consumer; + +class Test { + void foo(String s) { + if (true) { + Consumer stringConsumer = new Consumer() { + public void accept(String s) { + System.out.println("Hello, world " + s); + System.out.println(); + } + }; + stringConsumer.accept(s); + + System.out.println(); + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/introduceFunctionalVariable/beforeExpressionInLoopNoVars.java b/java/java-tests/testData/refactoring/introduceFunctionalVariable/beforeExpressionInLoopNoVars.java new file mode 100644 index 000000000000..74675b60043b --- /dev/null +++ b/java/java-tests/testData/refactoring/introduceFunctionalVariable/beforeExpressionInLoopNoVars.java @@ -0,0 +1,5 @@ +class Test { + void foo() { + if (true) System.out.println("Hello, world"); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/introduceFunctionalVariable/beforeExpressionNoVarsSelected.java b/java/java-tests/testData/refactoring/introduceFunctionalVariable/beforeExpressionNoVarsSelected.java new file mode 100644 index 000000000000..c755dfd23d3d --- /dev/null +++ b/java/java-tests/testData/refactoring/introduceFunctionalVariable/beforeExpressionNoVarsSelected.java @@ -0,0 +1,5 @@ +class Test { + void foo() { + System.out.println("Hello, world"); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/introduceFunctionalVariable/beforePassFieldsAsParameters.java b/java/java-tests/testData/refactoring/introduceFunctionalVariable/beforePassFieldsAsParameters.java new file mode 100644 index 000000000000..1f18528ac212 --- /dev/null +++ b/java/java-tests/testData/refactoring/introduceFunctionalVariable/beforePassFieldsAsParameters.java @@ -0,0 +1,6 @@ +class Test { + String myName; + void foo() { + if (true) System.out.println("Hello, world " + myName); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/introduceFunctionalVariable/beforeSkipUsedLocals.java b/java/java-tests/testData/refactoring/introduceFunctionalVariable/beforeSkipUsedLocals.java new file mode 100644 index 000000000000..a6c4549b0e0e --- /dev/null +++ b/java/java-tests/testData/refactoring/introduceFunctionalVariable/beforeSkipUsedLocals.java @@ -0,0 +1,6 @@ +class Test { + void foo(String name) { + System.out.println("Hello, "); + System.out.println(name); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/introduceFunctionalVariable/beforeStatementInLoop.java b/java/java-tests/testData/refactoring/introduceFunctionalVariable/beforeStatementInLoop.java new file mode 100644 index 000000000000..46fdef821b81 --- /dev/null +++ b/java/java-tests/testData/refactoring/introduceFunctionalVariable/beforeStatementInLoop.java @@ -0,0 +1,5 @@ +class Test { + void foo(String s) { + if (true) System.out.println("Hello, world " + s); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/introduceFunctionalVariable/beforeStatements.java b/java/java-tests/testData/refactoring/introduceFunctionalVariable/beforeStatements.java new file mode 100644 index 000000000000..238ba4690a0e --- /dev/null +++ b/java/java-tests/testData/refactoring/introduceFunctionalVariable/beforeStatements.java @@ -0,0 +1,10 @@ +class Test { + void foo(String s) { + if (true) { + System.out.println("Hello, world " + s); + System.out.println(); + + System.out.println(); + } + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/lambda/GraphInferenceHighlightingTest.java b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/lambda/GraphInferenceHighlightingTest.java index 2c09c1102be5..645a9442f600 100644 --- a/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/lambda/GraphInferenceHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/java/codeInsight/daemon/lambda/GraphInferenceHighlightingTest.java @@ -115,6 +115,7 @@ public class GraphInferenceHighlightingTest extends LightDaemonAnalyzerTestCase public void testPullUncheckedWarningNotionThroughNestedCalls() { doTest(); } public void testIDEA149774() { doTest(); } public void testDisjunctionTypes() { doTest(); } + public void testValidIntersectionTypeWithCapturedBounds() { doTest(); } public void testPushErasedStateToArguments() { doTest(); } public void testStopAtStandaloneConditional() { doTest(); } public void testTransitiveInferenceVariableDependencies() { doTest(); } diff --git a/java/java-tests/testSrc/com/intellij/java/refactoring/IntroduceFunctionalVariableTest.java b/java/java-tests/testSrc/com/intellij/java/refactoring/IntroduceFunctionalVariableTest.java new file mode 100644 index 000000000000..21aaaa4c4269 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/java/refactoring/IntroduceFunctionalVariableTest.java @@ -0,0 +1,86 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.java.refactoring; + +import com.intellij.JavaTestUtil; +import com.intellij.pom.java.LanguageLevel; +import com.intellij.refactoring.extractMethod.ExtractMethodProcessor; +import com.intellij.refactoring.extractMethod.InputVariables; +import com.intellij.refactoring.introduceVariable.IntroduceFunctionalVariableHandler; +import com.intellij.testFramework.MapDataContext; +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.annotations.NotNull; + +@TestDataPath("$CONTENT_ROOT/testData") +public class IntroduceFunctionalVariableTest extends LightRefactoringTestCase { + + @NotNull + @Override + protected String getTestDataPath() { + return JavaTestUtil.getJavaTestDataPath(); + } + + @Override + protected LanguageLevel getLanguageLevel() { + return LanguageLevel.JDK_1_8; + } + + public void testExpressionNoVarsSelected() throws Exception { + doTest(); + } + + public void testExpressionInLoopNoVars() throws Exception { + doTest(); + } + + public void testStatementInLoop() throws Exception { + doTest(); + } + + public void testStatements() throws Exception { + doTest(); + } + + public void testPassFieldsAsParameters() throws Exception { + doTest(); + } + + public void testSkipUsedLocals() throws Exception { + doTest(0); + } + + private void doTest(int... disableParams) { + boolean enabled = true; + try { + configureByFile("/refactoring/introduceFunctionalVariable/before" + getTestName(false) + ".java"); + enabled = getEditor().getSettings().isVariableInplaceRenameEnabled(); + getEditor().getSettings().setVariableInplaceRenameEnabled(false); + new IntroduceFunctionalVariableHandler() { + @Override + protected void setupProcessorWithoutDialog(ExtractMethodProcessor processor, InputVariables inputVariables) { + inputVariables.setPassFields(true); + super.setupProcessorWithoutDialog(processor, inputVariables); + for (int i : disableParams) { + processor.doNotPassParameter(i); + } + } + }.invoke(getProject(), getEditor(), getFile(), new MapDataContext()); + checkResultByFile("/refactoring/introduceFunctionalVariable/after" + getTestName(false) + ".java"); + } finally { + getEditor().getSettings().setVariableInplaceRenameEnabled(enabled); + } + } +} diff --git a/java/typeMigration/src/com/intellij/refactoring/typeMigration/intentions/ConvertFieldToAtomicIntention.java b/java/typeMigration/src/com/intellij/refactoring/typeMigration/intentions/ConvertFieldToAtomicIntention.java index 87bb1fbd3164..c47383832fae 100644 --- a/java/typeMigration/src/com/intellij/refactoring/typeMigration/intentions/ConvertFieldToAtomicIntention.java +++ b/java/typeMigration/src/com/intellij/refactoring/typeMigration/intentions/ConvertFieldToAtomicIntention.java @@ -4,6 +4,7 @@ import com.intellij.codeInsight.FileModificationService; import com.intellij.codeInsight.intention.LowPriorityAction; import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction; import com.intellij.lang.java.JavaLanguage; +import com.intellij.openapi.application.WriteAction; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; @@ -43,6 +44,7 @@ public class ConvertFieldToAtomicIntention extends PsiElementBaseIntentionAction private static final Logger LOG = Logger.getInstance(ConvertFieldToAtomicIntention.class); private final Map myFromToMap = ContainerUtil.newHashMap(); + { myFromToMap.put(PsiType.INT, AtomicInteger.class.getName()); myFromToMap.put(PsiType.LONG, AtomicLong.class.getName()); @@ -112,119 +114,123 @@ public class ConvertFieldToAtomicIntention extends PsiElementBaseIntentionAction } if (!FileModificationService.getInstance().preparePsiElementsForWrite(elements)) return; - psiVariable.normalizeDeclaration(); + WriteAction.run(() -> { + psiVariable.normalizeDeclaration(); - final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(project); - final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project); - final PsiType fromType = psiVariable.getType(); - PsiClassType toType; - final String atomicQualifiedName = myFromToMap.get(fromType); - if (atomicQualifiedName != null) { - final PsiClass atomicClass = psiFacade.findClass(atomicQualifiedName, GlobalSearchScope.allScope(project)); - if (atomicClass == null) {//show warning - return; + final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(project); + final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project); + final PsiType fromType = psiVariable.getType(); + PsiClassType toType; + final String atomicQualifiedName = myFromToMap.get(fromType); + if (atomicQualifiedName != null) { + final PsiClass atomicClass = psiFacade.findClass(atomicQualifiedName, GlobalSearchScope.allScope(project)); + if (atomicClass == null) {//show warning + return; + } + toType = factory.createType(atomicClass); } - toType = factory.createType(atomicClass); - } - else if (fromType instanceof PsiArrayType) { - final PsiClass atomicReferenceArrayClass = - psiFacade.findClass(AtomicReferenceArray.class.getName(), GlobalSearchScope.allScope(project)); - if (atomicReferenceArrayClass == null) {//show warning - return; + else if (fromType instanceof PsiArrayType) { + final PsiClass atomicReferenceArrayClass = + psiFacade.findClass(AtomicReferenceArray.class.getName(), GlobalSearchScope.allScope(project)); + if (atomicReferenceArrayClass == null) {//show warning + return; + } + final Map substitutor = ContainerUtil.newHashMap(); + final PsiTypeParameter[] typeParameters = atomicReferenceArrayClass.getTypeParameters(); + if (typeParameters.length == 1) { + PsiType componentType = ((PsiArrayType)fromType).getComponentType(); + if (componentType instanceof PsiPrimitiveType) componentType = ((PsiPrimitiveType)componentType).getBoxedType(element); + substitutor.put(typeParameters[0], componentType); + } + toType = factory.createType(atomicReferenceArrayClass, factory.createSubstitutor(substitutor)); } - final Map substitutor = ContainerUtil.newHashMap(); - final PsiTypeParameter[] typeParameters = atomicReferenceArrayClass.getTypeParameters(); - if (typeParameters.length == 1) { - PsiType componentType = ((PsiArrayType)fromType).getComponentType(); - if (componentType instanceof PsiPrimitiveType) componentType = ((PsiPrimitiveType)componentType).getBoxedType(element); - substitutor.put(typeParameters[0], componentType); + else { + final PsiClass atomicReferenceClass = psiFacade.findClass(AtomicReference.class.getName(), GlobalSearchScope.allScope(project)); + if (atomicReferenceClass == null) {//show warning + return; + } + final Map substitutor = ContainerUtil.newHashMap(); + final PsiTypeParameter[] typeParameters = atomicReferenceClass.getTypeParameters(); + if (typeParameters.length == 1) { + PsiType type = fromType; + if (type instanceof PsiPrimitiveType) type = ((PsiPrimitiveType)fromType).getBoxedType(element); + substitutor.put(typeParameters[0], type); + } + toType = factory.createType(atomicReferenceClass, factory.createSubstitutor(substitutor)); } - toType = factory.createType(atomicReferenceArrayClass, factory.createSubstitutor(substitutor)); - } - else { - final PsiClass atomicReferenceClass = psiFacade.findClass(AtomicReference.class.getName(), GlobalSearchScope.allScope(project)); - if (atomicReferenceClass == null) {//show warning - return; - } - final Map substitutor = ContainerUtil.newHashMap(); - final PsiTypeParameter[] typeParameters = atomicReferenceClass.getTypeParameters(); - if (typeParameters.length == 1) { - PsiType type = fromType; - if (type instanceof PsiPrimitiveType) type = ((PsiPrimitiveType)fromType).getBoxedType(element); - substitutor.put(typeParameters[0], type); - } - toType = factory.createType(atomicReferenceClass, factory.createSubstitutor(substitutor)); - } - try { - for (PsiReference reference : refs) { - PsiElement refElement = reference.getElement(); - PsiElement psiElement = refElement; - if (psiElement instanceof PsiExpression) { - final PsiElement parent = psiElement.getParent(); - if (parent instanceof PsiExpression && !(parent instanceof PsiReferenceExpression || parent instanceof PsiPolyadicExpression)) { - psiElement = parent; - } - if (psiElement instanceof PsiBinaryExpression) { - PsiBinaryExpression binary = (PsiBinaryExpression)psiElement; - if (isBinaryOpApplicable(binary.getOperationTokenType(), binary.getLOperand(), binary.getROperand(), refElement, toType)) { - continue; + try { + for (PsiReference reference : refs) { + PsiElement refElement = reference.getElement(); + PsiElement psiElement = refElement; + if (psiElement instanceof PsiExpression) { + final PsiElement parent = psiElement.getParent(); + if (parent instanceof PsiExpression && !(parent instanceof PsiReferenceExpression || parent instanceof PsiPolyadicExpression)) { + psiElement = parent; + } + if (psiElement instanceof PsiBinaryExpression) { + PsiBinaryExpression binary = (PsiBinaryExpression)psiElement; + if (isBinaryOpApplicable(binary.getOperationTokenType(), binary.getLOperand(), binary.getROperand(), refElement, toType)) { + continue; + } + } + else if (psiElement instanceof PsiAssignmentExpression) { + final PsiAssignmentExpression assignment = (PsiAssignmentExpression)psiElement; + final IElementType opSign = TypeConversionUtil.convertEQtoOperation(assignment.getOperationTokenType()); + if (isBinaryOpApplicable(opSign, assignment.getLExpression(), assignment.getRExpression(), refElement, toType)) { + continue; + } + } + final TypeConversionDescriptor directConversion = AtomicConversionRule.findDirectConversion(psiElement, toType, fromType); + if (directConversion != null) { + TypeMigrationReplacementUtil + .replaceExpression((PsiExpression)psiElement, project, directConversion, new TypeEvaluator(null, null)); } } - else if (psiElement instanceof PsiAssignmentExpression) { - final PsiAssignmentExpression assignment = (PsiAssignmentExpression)psiElement; - final IElementType opSign = TypeConversionUtil.convertEQtoOperation(assignment.getOperationTokenType()); - if (isBinaryOpApplicable(opSign, assignment.getLExpression(), assignment.getRExpression(), refElement, toType)) { - continue; - } + } + + PsiExpression initializer = psiVariable.getInitializer(); + if (initializer != null) { + if (initializer instanceof PsiArrayInitializerExpression) { + PsiExpression normalizedExpr = + RefactoringUtil.createNewExpressionFromArrayInitializer((PsiArrayInitializerExpression)initializer, psiVariable.getType()); + initializer = (PsiExpression)initializer.replace(normalizedExpr); } - final TypeConversionDescriptor directConversion = AtomicConversionRule.findDirectConversion(psiElement, toType, fromType); + final TypeConversionDescriptor directConversion = + AtomicConversionRule.wrapWithNewExpression(toType, fromType, initializer, element); if (directConversion != null) { - TypeMigrationReplacementUtil.replaceExpression((PsiExpression)psiElement, project, directConversion, new TypeEvaluator(null, null)); + TypeMigrationReplacementUtil.replaceExpression(initializer, project, directConversion, new TypeEvaluator(null, null)); } } - } + else if (!assertNotNull(psiVariable.getModifierList()).hasModifierProperty(PsiModifier.FINAL)) { + final PsiExpression newInitializer = factory.createExpressionFromText("new " + toType.getCanonicalText() + "()", psiVariable); + if (psiVariable instanceof PsiLocalVariable) { + ((PsiLocalVariable)psiVariable).setInitializer(newInitializer); + } + else if (psiVariable instanceof PsiField) { + ((PsiField)psiVariable).setInitializer(newInitializer); + } + JavaCodeStyleManager.getInstance(project).shortenClassReferences(psiVariable.getInitializer()); + } - PsiExpression initializer = psiVariable.getInitializer(); - if (initializer != null) { - if (initializer instanceof PsiArrayInitializerExpression) { - PsiExpression normalizedExpr = - RefactoringUtil.createNewExpressionFromArrayInitializer((PsiArrayInitializerExpression)initializer, psiVariable.getType()); - initializer = (PsiExpression)initializer.replace(normalizedExpr); - } - final TypeConversionDescriptor directConversion = AtomicConversionRule.wrapWithNewExpression(toType, fromType, initializer, element); - if (directConversion != null) { - TypeMigrationReplacementUtil.replaceExpression(initializer, project, directConversion, new TypeEvaluator(null, null)); - } - } - else if (!assertNotNull(psiVariable.getModifierList()).hasModifierProperty(PsiModifier.FINAL)) { - final PsiExpression newInitializer = factory.createExpressionFromText("new " + toType.getCanonicalText() + "()", psiVariable); - if (psiVariable instanceof PsiLocalVariable) { - ((PsiLocalVariable)psiVariable).setInitializer(newInitializer); - } - else if (psiVariable instanceof PsiField) { - ((PsiField)psiVariable).setInitializer(newInitializer); - } - JavaCodeStyleManager.getInstance(project).shortenClassReferences(psiVariable.getInitializer()); - } + PsiElement replaced = assertNotNull(psiVariable.getTypeElement()).replace(factory.createTypeElement(toType)); + JavaCodeStyleManager.getInstance(project).shortenClassReferences(replaced); - PsiElement replaced = assertNotNull(psiVariable.getTypeElement()).replace(factory.createTypeElement(toType)); - JavaCodeStyleManager.getInstance(project).shortenClassReferences(replaced); - - if (psiVariable instanceof PsiField || CodeStyleSettingsManager.getSettings(project).GENERATE_FINAL_LOCALS) { - final PsiModifierList modifierList = assertNotNull(psiVariable.getModifierList()); - modifierList.setModifierProperty(PsiModifier.FINAL, true); - modifierList.setModifierProperty(PsiModifier.VOLATILE, false); + if (psiVariable instanceof PsiField || CodeStyleSettingsManager.getSettings(project).GENERATE_FINAL_LOCALS) { + final PsiModifierList modifierList = assertNotNull(psiVariable.getModifierList()); + modifierList.setModifierProperty(PsiModifier.FINAL, true); + modifierList.setModifierProperty(PsiModifier.VOLATILE, false); + } } - } - catch (IncorrectOperationException e) { - LOG.error(e); - } + catch (IncorrectOperationException e) { + LOG.error(e); + } + }); } @Override public boolean startInWriteAction() { - return true; + return false; } private static boolean isBinaryOpApplicable(@Nullable IElementType opSign, diff --git a/java/typeMigration/src/com/intellij/refactoring/typeMigration/intentions/ConvertFieldToThreadLocalIntention.java b/java/typeMigration/src/com/intellij/refactoring/typeMigration/intentions/ConvertFieldToThreadLocalIntention.java index 0f9bc886c1bf..6161a42af635 100644 --- a/java/typeMigration/src/com/intellij/refactoring/typeMigration/intentions/ConvertFieldToThreadLocalIntention.java +++ b/java/typeMigration/src/com/intellij/refactoring/typeMigration/intentions/ConvertFieldToThreadLocalIntention.java @@ -4,6 +4,7 @@ import com.intellij.codeInsight.FileModificationService; import com.intellij.codeInsight.intention.LowPriorityAction; import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction; import com.intellij.lang.java.JavaLanguage; +import com.intellij.openapi.application.WriteAction; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; @@ -73,88 +74,91 @@ public class ConvertFieldToThreadLocalIntention extends PsiElementBaseIntentionA elements.add(reference.getElement()); } if (!FileModificationService.getInstance().preparePsiElementsForWrite(elements)) return; - psiField.normalizeDeclaration(); + WriteAction.run(() -> { + psiField.normalizeDeclaration(); - final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(project); - final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project); - final PsiType fromType = psiField.getType(); + final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(project); + final PsiElementFactory factory = JavaPsiFacade.getElementFactory(project); + final PsiType fromType = psiField.getType(); - final PsiClass threadLocalClass = psiFacade.findClass(ThreadLocal.class.getName(), GlobalSearchScope.allScope(project)); - if (threadLocalClass == null) {//show warning - return; - } - final Map substitutor = ContainerUtil.newHashMap(); - final PsiTypeParameter[] typeParameters = threadLocalClass.getTypeParameters(); - if (typeParameters.length == 1) { - PsiType type = fromType; - if (fromType instanceof PsiPrimitiveType) type = ((PsiPrimitiveType)fromType).getBoxedType(element); - substitutor.put(typeParameters[0], type); - } - final PsiClassType toType = factory.createType(threadLocalClass, factory.createSubstitutor(substitutor)); + final PsiClass threadLocalClass = psiFacade.findClass(ThreadLocal.class.getName(), GlobalSearchScope.allScope(project)); + if (threadLocalClass == null) {//show warning + return; + } + final Map substitutor = ContainerUtil.newHashMap(); + final PsiTypeParameter[] typeParameters = threadLocalClass.getTypeParameters(); + if (typeParameters.length == 1) { + PsiType type = fromType; + if (fromType instanceof PsiPrimitiveType) type = ((PsiPrimitiveType)fromType).getBoxedType(element); + substitutor.put(typeParameters[0], type); + } + final PsiClassType toType = factory.createType(threadLocalClass, factory.createSubstitutor(substitutor)); - try { - final TypeMigrationRules rules = new TypeMigrationRules(); - rules.setBoundScope(GlobalSearchScope.fileScope(element.getContainingFile())); - final TypeMigrationLabeler labeler = new TypeMigrationLabeler(rules, toType); - labeler.getMigratedUsages(false, psiField); - for (PsiReference reference : refs) { - PsiElement psiElement = reference.getElement(); - if (psiElement instanceof PsiExpression) { - final PsiElement parent = psiElement.getParent(); - if (parent instanceof PsiExpression && !(parent instanceof PsiReferenceExpression || parent instanceof PsiPolyadicExpression)) { - psiElement = parent; - } - final TypeConversionDescriptor conversion = ThreadLocalConversionRule.findDirectConversion(psiElement, toType, fromType, labeler); - if (conversion != null) { - TypeMigrationReplacementUtil.replaceExpression((PsiExpression)psiElement, project, conversion, new TypeEvaluator(null, null)); + try { + final TypeMigrationRules rules = new TypeMigrationRules(); + rules.setBoundScope(GlobalSearchScope.fileScope(element.getContainingFile())); + final TypeMigrationLabeler labeler = new TypeMigrationLabeler(rules, toType); + labeler.getMigratedUsages(false, psiField); + for (PsiReference reference : refs) { + PsiElement psiElement = reference.getElement(); + if (psiElement instanceof PsiExpression) { + final PsiElement parent = psiElement.getParent(); + if (parent instanceof PsiExpression && !(parent instanceof PsiReferenceExpression || parent instanceof PsiPolyadicExpression)) { + psiElement = parent; + } + final TypeConversionDescriptor conversion = + ThreadLocalConversionRule.findDirectConversion(psiElement, toType, fromType, labeler); + if (conversion != null) { + TypeMigrationReplacementUtil.replaceExpression((PsiExpression)psiElement, project, conversion, new TypeEvaluator(null, null)); + } } } - } - PsiExpression initializer = psiField.getInitializer(); - if (initializer == null) { - final PsiType type = psiField.getType(); - String initializerText = null; - if (PsiType.BOOLEAN.equals(type)) { - initializerText = "false"; + PsiExpression initializer = psiField.getInitializer(); + if (initializer == null) { + final PsiType type = psiField.getType(); + String initializerText = null; + if (PsiType.BOOLEAN.equals(type)) { + initializerText = "false"; + } + else if (type instanceof PsiPrimitiveType) { + initializerText = "0"; + } + if (initializerText != null) { + psiField.setInitializer(factory.createExpressionFromText(initializerText, psiField)); + initializer = psiField.getInitializer(); + } } - else if (type instanceof PsiPrimitiveType) { - initializerText = "0"; + if (initializer != null) { + if (initializer instanceof PsiArrayInitializerExpression) { + PsiExpression normalizedExpr = + RefactoringUtil.createNewExpressionFromArrayInitializer((PsiArrayInitializerExpression)initializer, psiField.getType()); + initializer = (PsiExpression)initializer.replace(normalizedExpr); + } + final TypeConversionDescriptor conversion = ThreadLocalConversionRule.wrapWithNewExpression(toType, fromType, initializer); + TypeMigrationReplacementUtil.replaceExpression(initializer, project, conversion, new TypeEvaluator(null, null)); + CodeStyleManager.getInstance(project).reformat(psiField); } - if (initializerText != null) { - psiField.setInitializer(factory.createExpressionFromText(initializerText, psiField)); - initializer = psiField.getInitializer(); + else if (!assertNotNull(psiField.getModifierList()).hasModifierProperty(PsiModifier.FINAL)) { + final String text = "new " + PsiDiamondTypeUtil.getCollapsedType(toType, psiField) + "()"; + final PsiExpression newInitializer = factory.createExpressionFromText(text, psiField); + psiField.setInitializer(newInitializer); } - } - if (initializer != null) { - if (initializer instanceof PsiArrayInitializerExpression) { - PsiExpression normalizedExpr = - RefactoringUtil.createNewExpressionFromArrayInitializer((PsiArrayInitializerExpression)initializer, psiField.getType()); - initializer = (PsiExpression)initializer.replace(normalizedExpr); - } - final TypeConversionDescriptor conversion = ThreadLocalConversionRule.wrapWithNewExpression(toType, fromType, initializer); - TypeMigrationReplacementUtil.replaceExpression(initializer, project, conversion, new TypeEvaluator(null, null)); - CodeStyleManager.getInstance(project).reformat(psiField); - } - else if (!assertNotNull(psiField.getModifierList()).hasModifierProperty(PsiModifier.FINAL)) { - final String text = "new " + PsiDiamondTypeUtil.getCollapsedType(toType, psiField) + "()"; - final PsiExpression newInitializer = factory.createExpressionFromText(text, psiField); - psiField.setInitializer(newInitializer); - } - assertNotNull(psiField.getTypeElement()).replace(factory.createTypeElement(toType)); + assertNotNull(psiField.getTypeElement()).replace(factory.createTypeElement(toType)); - final PsiModifierList modifierList = assertNotNull(psiField.getModifierList()); - modifierList.setModifierProperty(PsiModifier.FINAL, true); - modifierList.setModifierProperty(PsiModifier.VOLATILE, false); - } - catch (IncorrectOperationException e) { - LOG.error(e); - } + final PsiModifierList modifierList = assertNotNull(psiField.getModifierList()); + modifierList.setModifierProperty(PsiModifier.FINAL, true); + modifierList.setModifierProperty(PsiModifier.VOLATILE, false); + } + catch (IncorrectOperationException e) { + LOG.error(e); + } + }); } @Override public boolean startInWriteAction() { - return true; + return false; } } diff --git a/platform/lang-api/src/com/intellij/lang/refactoring/RefactoringSupportProvider.java b/platform/lang-api/src/com/intellij/lang/refactoring/RefactoringSupportProvider.java index 449a62555746..6531a85ddfa9 100644 --- a/platform/lang-api/src/com/intellij/lang/refactoring/RefactoringSupportProvider.java +++ b/platform/lang-api/src/com/intellij/lang/refactoring/RefactoringSupportProvider.java @@ -101,6 +101,15 @@ public abstract class RefactoringSupportProvider { return null; } + /** + * @return handler for introducing functional locals in this language + * @see ContextAwareActionHandler + * @see RefactoringActionHandler + */ + public RefactoringActionHandler getIntroduceFunctionalVariableHandler() { + return null; + } + /** * @return handler for pulling up members in this language * @see com.intellij.refactoring.RefactoringActionHandler diff --git a/platform/lang-impl/src/com/intellij/refactoring/actions/IntroduceFunctionalVariableAction.java b/platform/lang-impl/src/com/intellij/refactoring/actions/IntroduceFunctionalVariableAction.java new file mode 100644 index 000000000000..e2f5cef8b860 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/refactoring/actions/IntroduceFunctionalVariableAction.java @@ -0,0 +1,42 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.intellij.refactoring.actions; + +import com.intellij.lang.refactoring.RefactoringSupportProvider; +import com.intellij.psi.PsiElement; +import com.intellij.refactoring.RefactoringActionHandler; +import com.intellij.refactoring.RefactoringBundle; +import org.jetbrains.annotations.NotNull; + +public class IntroduceFunctionalVariableAction extends BasePlatformRefactoringAction { + public static final String REFACTORING_NAME = RefactoringBundle.message("introduce.functional.variable.title"); + + @Override + protected boolean isAvailableInEditorOnly() { + return true; + } + + @Override + protected boolean isEnabledOnElements(@NotNull PsiElement[] elements) { + return false; + } + + @Override + protected RefactoringActionHandler getRefactoringHandler(@NotNull RefactoringSupportProvider provider) { + return provider.getIntroduceFunctionalVariableHandler(); + } +} diff --git a/platform/lang-impl/src/com/intellij/refactoring/safeDelete/UnsafeUsagesDialog.java b/platform/lang-impl/src/com/intellij/refactoring/safeDelete/UnsafeUsagesDialog.java index 709ad66242e6..c1472c420881 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/safeDelete/UnsafeUsagesDialog.java +++ b/platform/lang-impl/src/com/intellij/refactoring/safeDelete/UnsafeUsagesDialog.java @@ -58,6 +58,7 @@ public class UnsafeUsagesDialog extends DialogWrapper { JPanel panel = new JPanel(new BorderLayout()); myMessagePane = new JEditorPane(UIUtil.HTML_MIME, ""); myMessagePane.setEditable(false); + myMessagePane.setEditorKit(UIUtil.getHTMLEditorKit()); JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(myMessagePane); scrollPane.setPreferredSize(JBUI.size(500, 400)); panel.add(new JLabel(RefactoringBundle.message("the.following.problems.were.found")), BorderLayout.NORTH); diff --git a/platform/platform-resources-en/src/messages/ActionsBundle.properties b/platform/platform-resources-en/src/messages/ActionsBundle.properties index f03581f0c9ca..30302cda656e 100644 --- a/platform/platform-resources-en/src/messages/ActionsBundle.properties +++ b/platform/platform-resources-en/src/messages/ActionsBundle.properties @@ -674,7 +674,9 @@ action.IntroduceConstant.description=Replace selected expression with a constant action.IntroduceParameter.text=_Parameter... action.IntroduceParameter.description=Turn the selected expression into method parameter action.IntroduceFunctionalParameter.text=Functiona_l Parameter... -action.IntroduceFunctionalParameter.description=Replace selected statements with a call to new functional method parameter +action.IntroduceFunctionalParameter.description=Replace selected statements with a call to a new functional method parameter +action.IntroduceFunctionalVariable.text=Functional Variable... +action.IntroduceFunctionalVariable.description=Replace selected statements with a call to a new functional variable action.ExtractInterface.text=_Interface... action.ExtractInterface.description=Extract interface from the selected class action.ExtractModule.text=_Module... diff --git a/platform/platform-resources-en/src/messages/RefactoringBundle.properties b/platform/platform-resources-en/src/messages/RefactoringBundle.properties index f43667a758ba..d8951a15ad03 100644 --- a/platform/platform-resources-en/src/messages/RefactoringBundle.properties +++ b/platform/platform-resources-en/src/messages/RefactoringBundle.properties @@ -805,6 +805,7 @@ enter.new.project.name=Enter new project name: rename.project=Rename Project renames.project=Renames project introduce.functional.parameter.title=Extract Functional Parameter +introduce.functional.variable.title=Extract Functional Variable introduce.parameter.convert.lambda=&Convert to functional expression expand.method.reference.warning=Method is used in method reference. Proceeding would result in conversion to lambda expression diff --git a/platform/util/src/com/intellij/util/text/SemVer.java b/platform/util/src/com/intellij/util/text/SemVer.java index 664aa224ab7d..347a1b07b11a 100644 --- a/platform/util/src/com/intellij/util/text/SemVer.java +++ b/platform/util/src/com/intellij/util/text/SemVer.java @@ -23,6 +23,7 @@ import org.jetbrains.annotations.Nullable; * Holds Semantic Version. */ public final class SemVer implements Comparable { + /** @deprecated */ public static final SemVer UNKNOWN = new SemVer("?", 0, 0, 0); private final String myRawVersion; diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangesViewManager.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangesViewManager.java index a43c2189bb97..103acc7c0821 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangesViewManager.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ChangesViewManager.java @@ -73,7 +73,6 @@ import java.awt.event.KeyEvent; import java.util.Collection; import java.util.List; -import static com.intellij.util.ObjectUtils.assertNotNull; import static java.util.stream.Collectors.toList; @State( @@ -83,7 +82,7 @@ import static java.util.stream.Collectors.toList; public class ChangesViewManager implements ChangesViewI, ProjectComponent, PersistentStateComponent { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vcs.changes.ChangesViewManager"); - public static final String CHANGES_VIEW_PREVIEW_SPLITTER_PROPORTION = "ChangesViewManager.DETAILS_SPLITTER_PROPORTION"; + private static final String CHANGES_VIEW_PREVIEW_SPLITTER_PROPORTION = "ChangesViewManager.DETAILS_SPLITTER_PROPORTION"; @NotNull private final ChangesListView myView; private JPanel myProgressLabel; @@ -478,10 +477,14 @@ public class ChangesViewManager implements ChangesViewI, ProjectComponent, Persi private class ToggleDetailsAction extends ShowDiffPreviewAction { @Override public void setSelected(AnActionEvent e, boolean state) { - super.setSelected(e, state); - assertNotNull(mySplitterComponent).setDetailsOn(state); + mySplitterComponent.setDetailsOn(state); VcsConfiguration.getInstance(myProject).LOCAL_CHANGES_DETAILS_PREVIEW_SHOWN = state; } + + @Override + public boolean isSelected(AnActionEvent e) { + return VcsConfiguration.getInstance(myProject).LOCAL_CHANGES_DETAILS_PREVIEW_SHOWN; + } } private class MyChangeProcessor extends ChangeViewDiffRequestProcessor { diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/ShowDiffPreviewAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/ShowDiffPreviewAction.java index 068a97b8ea16..56528d7d599b 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/ShowDiffPreviewAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/ShowDiffPreviewAction.java @@ -16,24 +16,11 @@ package com.intellij.openapi.vcs.changes.actions; import com.intellij.icons.AllIcons; -import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.ToggleAction; import com.intellij.openapi.project.DumbAware; public abstract class ShowDiffPreviewAction extends ToggleAction implements DumbAware { - private boolean myState; - public ShowDiffPreviewAction() { super("Preview Diff", null, AllIcons.Actions.DiffPreview); } - - @Override - public boolean isSelected(AnActionEvent e) { - return myState; - } - - @Override - public void setSelected(AnActionEvent e, boolean state) { - myState = state; - } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/DiffShelvedChangesActionProvider.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/DiffShelvedChangesActionProvider.java index bad6e597a4d3..772fc2514639 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/DiffShelvedChangesActionProvider.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/DiffShelvedChangesActionProvider.java @@ -245,7 +245,7 @@ public class DiffShelvedChangesActionProvider implements AnActionExtensionProvid DiffContentFactory contentFactory = DiffContentFactory.getInstance(); DiffContent leftContent = withLocal ? contentFactory.create(project, file) - : contentFactory.create(project, patch.getSingleHunkPatchText()); + : contentFactory.create(project, patch.getSingleHunkPatchText(), file); return new SimpleDiffRequest(getName(), leftContent, contentFactory.createEmpty(), withLocal ? CURRENT_VERSION : SHELVED_VERSION, null); @@ -269,8 +269,8 @@ public class DiffShelvedChangesActionProvider implements AnActionExtensionProvid DiffContentFactory contentFactory = DiffContentFactory.getInstance(); DiffContent leftContent = withLocal ? contentFactory.create(project, file) - : contentFactory.create(project, assertNotNull(texts.getBase())); - return new SimpleDiffRequest(getName(), leftContent, contentFactory.create(project, texts.getPatched()), + : contentFactory.create(project, assertNotNull(texts.getBase()), file); + return new SimpleDiffRequest(getName(), leftContent, contentFactory.create(project, texts.getPatched(), file), withLocal ? CURRENT_VERSION : BASE_VERSION, SHELVED_VERSION); } else { diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelvedChangesViewManager.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelvedChangesViewManager.java index a811a1a7c1ab..76c6bc03fd89 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelvedChangesViewManager.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/shelf/ShelvedChangesViewManager.java @@ -108,6 +108,7 @@ public class ShelvedChangesViewManager implements ProjectComponent { private static final Logger LOG = Logger.getInstance(ShelvedChangesViewManager.class); @NonNls static final String SHELF_CONTEXT_MENU = "Vcs.Shelf.ContextMenu"; + private static final String SHELVE_PREVIEW_SPLITTER_PROPORTION = "ShelvedChangesViewManager.DETAILS_SPLITTER_PROPORTION"; private final ChangesViewContentManager myContentManager; private final ShelveChangesManager myShelveChangesManager; @@ -247,21 +248,11 @@ public class ShelvedChangesViewManager implements ProjectComponent { DefaultActionGroup actionGroup = new DefaultActionGroup(); actionGroup.addAll((ActionGroup)ActionManager.getInstance().getAction("ShelvedChangesToolbar")); - ShowDiffPreviewAction diffPreviewAction = new ShowDiffPreviewAction() { - @Override - public void setSelected(AnActionEvent e, boolean state) { - super.setSelected(e, state); - assertNotNull(mySplitterComponent).setDetailsOn(state); - VcsConfiguration.getInstance(myProject).SHELVE_DETAILS_PREVIEW_SHOWN = state; - } - }; - actionGroup.add(diffPreviewAction, new Constraints(AFTER, "ShelvedChanges.ShowHideDeleted")); + actionGroup.add(new MyToggleDetailsAction(), new Constraints(AFTER, "ShelvedChanges.ShowHideDeleted")); MyShelvedPreviewProcessor changeProcessor = new MyShelvedPreviewProcessor(myProject); - mySplitterComponent = - new PreviewDiffSplitterComponent(pane, changeProcessor, "ShelvedChangesViewManager.DETAILS_SPLITTER_PROPORTION", - VcsConfiguration.getInstance(myProject).SHELVE_DETAILS_PREVIEW_SHOWN); - diffPreviewAction.setSelected(null, mySplitterComponent.isDetailsOn()); + mySplitterComponent = new PreviewDiffSplitterComponent(pane, changeProcessor, SHELVE_PREVIEW_SPLITTER_PROPORTION, + VcsConfiguration.getInstance(myProject).SHELVE_DETAILS_PREVIEW_SHOWN); ActionToolbar toolbar = ActionManager.getInstance().createActionToolbar("ShelvedChanges", actionGroup, false); JPanel rootPanel = new JPanel(new BorderLayout()); @@ -742,6 +733,19 @@ public class ShelvedChangesViewManager implements ProjectComponent { return new DnDImage(image, new Point(-image.getWidth(null), -image.getHeight(null))); } + private class MyToggleDetailsAction extends ShowDiffPreviewAction { + @Override + public void setSelected(AnActionEvent e, boolean state) { + mySplitterComponent.setDetailsOn(state); + VcsConfiguration.getInstance(myProject).SHELVE_DETAILS_PREVIEW_SHOWN = state; + } + + @Override + public boolean isSelected(AnActionEvent e) { + return VcsConfiguration.getInstance(myProject).SHELVE_DETAILS_PREVIEW_SHOWN; + } + } + private class MyShelvedPreviewProcessor extends CacheDiffRequestProcessor implements DiffPreviewUpdateProcessor { @NotNull private final DiffShelvedChangesActionProvider.PatchesPreloader myPreloader; diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties index 3828297a94dd..d067787d0e95 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties @@ -1448,7 +1448,7 @@ integer.multiplication.implicit.cast.to.long.option=Ignore compile time co wait.or.await.without.timeout.display.name='wait()' or 'await()' without timeout wait.or.await.without.timeout.problem.descriptor=#ref without timeout #loc method.return.always.constant.display.name=Method returns per-class constant -method.return.always.constant.problem.descriptor=Method #ref() returns a per-class constant +method.return.always.constant.problem.descriptor=Method #ref() and all it's derivables always return constants class.with.too.many.dependencies.display.name=Class with too many dependencies class.with.too.many.dependencies.problem.descriptor=Class ''{0}'' has too many dependencies ({1} > {2}) class.with.too.many.transitive.dependencies.display.name=Class with too many transitive dependencies diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/MethodReturnAlwaysConstantInspection.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/MethodReturnAlwaysConstantInspection.java index fbced3b0f8c0..b475ee5d422f 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/MethodReturnAlwaysConstantInspection.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/classlayout/MethodReturnAlwaysConstantInspection.java @@ -17,9 +17,7 @@ package com.siyeh.ig.classlayout; import com.intellij.analysis.AnalysisScope; import com.intellij.codeInspection.*; -import com.intellij.codeInspection.reference.RefEntity; -import com.intellij.codeInspection.reference.RefMethod; -import com.intellij.openapi.util.Key; +import com.intellij.codeInspection.reference.*; import com.intellij.psi.*; import com.intellij.psi.util.PsiUtil; import com.siyeh.InspectionGadgetsBundle; @@ -34,14 +32,10 @@ import java.util.Set; public class MethodReturnAlwaysConstantInspection extends BaseGlobalInspection { - private static final Key ALWAYS_CONSTANT = - Key.create("ALWAYS_CONSTANT"); - @NotNull @Override public String getDisplayName() { - return InspectionGadgetsBundle.message( - "method.return.always.constant.display.name"); + return InspectionGadgetsBundle.message("method.return.always.constant.display.name"); } @Override @@ -51,35 +45,24 @@ public class MethodReturnAlwaysConstantInspection extends BaseGlobalInspection { if (!(refEntity instanceof RefMethod)) { return null; } - final RefMethod refMethod = (RefMethod)refEntity; - final Boolean alreadyProcessed = refMethod.getUserData(ALWAYS_CONSTANT); - if (alreadyProcessed != null && alreadyProcessed.booleanValue()) { + //don't warn on overriders + if (((RefMethod)refEntity).hasSuperMethods()) { return null; } + final RefMethod refMethod = (RefMethod)refEntity; if (!(refMethod.getElement() instanceof PsiMethod)) { return null; } - final PsiMethod method = (PsiMethod)refMethod.getElement(); - if (method.getBody() == null) { - return null; //we'll catch it on another method - } - if (!alwaysReturnsConstant(method)) { - return null; - } - final Set siblingMethods = - MethodInheritanceUtils.calculateSiblingMethods(refMethod); - for (RefMethod siblingMethod : siblingMethods) { - final PsiMethod siblingPsiMethod = - (PsiMethod)siblingMethod.getElement(); - if (method.getBody() != null && - !alwaysReturnsConstant(siblingPsiMethod)) { + final Set allScopeInheritors = MethodInheritanceUtils.calculateSiblingMethods(refMethod); + for (RefMethod siblingMethod : allScopeInheritors) { + final PsiMethod siblingPsiMethod = (PsiMethod)siblingMethod.getElement(); + if (siblingPsiMethod.getBody() != null && !alwaysReturnsConstant(siblingPsiMethod)) { return null; } } final List out = new ArrayList<>(); - for (RefMethod siblingRefMethod : siblingMethods) { - final PsiMethod siblingMethod = - (PsiMethod)siblingRefMethod.getElement(); + for (RefMethod siblingRefMethod : allScopeInheritors) { + final PsiMethod siblingMethod = (PsiMethod)siblingRefMethod.getElement(); final PsiIdentifier identifier = siblingMethod.getNameIdentifier(); if (identifier == null) { continue; @@ -88,8 +71,6 @@ public class MethodReturnAlwaysConstantInspection extends BaseGlobalInspection { InspectionGadgetsBundle.message( "method.return.always.constant.problem.descriptor"), false, null, ProblemHighlightType.GENERIC_ERROR_OR_WARNING)); - siblingRefMethod.putUserData(ALWAYS_CONSTANT, - Boolean.valueOf(true)); } return out.toArray(new ProblemDescriptor[out.size()]); } @@ -100,9 +81,32 @@ public class MethodReturnAlwaysConstantInspection extends BaseGlobalInspection { if (!(statement instanceof PsiReturnStatement)) { return false; } - final PsiReturnStatement returnStatement = - (PsiReturnStatement)statement; + final PsiReturnStatement returnStatement = (PsiReturnStatement)statement; final PsiExpression value = returnStatement.getReturnValue(); return value != null && PsiUtil.isConstantExpression(value); } + + @Override + protected boolean queryExternalUsagesRequests(@NotNull final RefManager manager, @NotNull final GlobalJavaInspectionContext globalContext, + @NotNull final ProblemDescriptionsProcessor processor) { + manager.iterate(new RefJavaVisitor() { + @Override public void visitElement(@NotNull RefEntity refEntity) { + if (refEntity instanceof RefElement && processor.getDescriptions(refEntity) != null) { + refEntity.accept(new RefJavaVisitor() { + @Override public void visitMethod(@NotNull final RefMethod refMethod) { + globalContext.enqueueDerivedMethodsProcessor(refMethod, new GlobalJavaInspectionContext.DerivedMethodsProcessor() { + @Override + public boolean process(PsiMethod derivedMethod) { + processor.ignoreElement(refMethod); + return false; + } + }); + } + }); + } + } + }); + + return false; + } } diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/MethodReturnAlwaysConstant.html b/plugins/InspectionGadgets/src/inspectionDescriptions/MethodReturnAlwaysConstant.html index 05c7d7cbdbcc..a48b19716916 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/MethodReturnAlwaysConstant.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/MethodReturnAlwaysConstant.html @@ -1,6 +1,6 @@ -Reports methods which only ever return a constant. +Reports methods which only ever return a constant, probably different for different inheritors. Because this inspection requires global code analysis it is only available for Analyze|Inspect Code or Analyze|Run Inspection by Name and it will not report in the editor. diff --git a/plugins/IntentionPowerPak/src/com/siyeh/ipp/types/MakeMethodDefaultIntention.java b/plugins/IntentionPowerPak/src/com/siyeh/ipp/types/MakeMethodDefaultIntention.java index a37ff7a31970..634b2e62e242 100644 --- a/plugins/IntentionPowerPak/src/com/siyeh/ipp/types/MakeMethodDefaultIntention.java +++ b/plugins/IntentionPowerPak/src/com/siyeh/ipp/types/MakeMethodDefaultIntention.java @@ -47,7 +47,7 @@ public class MakeMethodDefaultIntention extends BaseElementAtCaretIntentionActio if (psiMethod != null && PsiUtil.isLanguageLevel8OrHigher(psiMethod)) { if (psiMethod.getBody() == null && !psiMethod.hasModifierProperty(PsiModifier.DEFAULT)) { final PsiClass containingClass = psiMethod.getContainingClass(); - if (containingClass != null && containingClass.isInterface()) { + if (containingClass != null && containingClass.isInterface() && !containingClass.isAnnotationType()) { text = "Make '" + psiMethod.getName() + "()' default"; return true; } diff --git a/plugins/IntentionPowerPak/test/com/siyeh/ipp/types/makeDefault/AnnotationType.java b/plugins/IntentionPowerPak/test/com/siyeh/ipp/types/makeDefault/AnnotationType.java new file mode 100644 index 000000000000..4aa6e097cfbd --- /dev/null +++ b/plugins/IntentionPowerPak/test/com/siyeh/ipp/types/makeDefault/AnnotationType.java @@ -0,0 +1,3 @@ +@interface I { + String foo() default "foo"; +} \ No newline at end of file diff --git a/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/types/MakeMethodDefaultTest.java b/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/types/MakeMethodDefaultTest.java index e2939b50abe1..b3dfb59cdb06 100644 --- a/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/types/MakeMethodDefaultTest.java +++ b/plugins/IntentionPowerPak/testSrc/com/siyeh/ipp/types/MakeMethodDefaultTest.java @@ -15,7 +15,6 @@ */ package com.siyeh.ipp.types; -import com.siyeh.IntentionPowerPackBundle; import com.siyeh.ipp.IPPTestCase; public class MakeMethodDefaultTest extends IPPTestCase { @@ -41,4 +40,9 @@ public class MakeMethodDefaultTest extends IPPTestCase { public void testAlreadyHasBody() throws Exception { assertIntentionNotAvailable(); } + + public void testAnnotationType() throws Exception { + assertIntentionNotAvailable(); + } + } diff --git a/python/educational-core/testSrc/com/jetbrains/edu/coursecreator/CCTestCase.java b/python/educational-core/testSrc/com/jetbrains/edu/coursecreator/CCTestCase.java index b9eed80db896..95be2c1581d0 100644 --- a/python/educational-core/testSrc/com/jetbrains/edu/coursecreator/CCTestCase.java +++ b/python/educational-core/testSrc/com/jetbrains/edu/coursecreator/CCTestCase.java @@ -12,6 +12,7 @@ import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.testFramework.EditorTestUtil; import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixtureTestCase; @@ -187,7 +188,7 @@ public abstract class CCTestCase extends LightPlatformCodeInsightFixtureTestCase public Pair> getPlaceholders(String name, boolean useLength, boolean removeMarkers) { try { - String text = FileUtil.loadFile(new File(getBasePath(), name)); + String text = StringUtil.convertLineSeparators(FileUtil.loadFile(new File(getBasePath(), name))); Document tempDocument = EditorFactory.getInstance().createDocument(text); if (removeMarkers) { EditorTestUtil.extractCaretAndSelectionMarkers(tempDocument); diff --git a/resources/src/idea/JavaActions.xml b/resources/src/idea/JavaActions.xml index b5d9b81f1566..676ef3dbc23f 100644 --- a/resources/src/idea/JavaActions.xml +++ b/resources/src/idea/JavaActions.xml @@ -79,6 +79,10 @@ + + + +