2 * Copyright 2000-2016 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.refactoring;
18 import com.intellij.JavaTestUtil;
19 import com.intellij.codeInsight.CodeInsightUtil;
20 import com.intellij.lang.java.JavaLanguage;
21 import com.intellij.openapi.application.ApplicationManager;
22 import com.intellij.openapi.editor.Editor;
23 import com.intellij.openapi.project.Project;
24 import com.intellij.psi.*;
25 import com.intellij.psi.codeStyle.CodeStyleSettings;
26 import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
27 import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
28 import com.intellij.psi.search.GlobalSearchScope;
29 import com.intellij.psi.util.PsiTreeUtil;
30 import com.intellij.refactoring.extractMethod.ExtractMethodHandler;
31 import com.intellij.refactoring.extractMethod.ExtractMethodProcessor;
32 import com.intellij.refactoring.extractMethod.PrepareFailedException;
33 import com.intellij.refactoring.introduceVariable.IntroduceVariableBase;
34 import com.intellij.refactoring.util.duplicates.Match;
35 import com.intellij.testFramework.LightCodeInsightTestCase;
36 import com.intellij.util.IncorrectOperationException;
37 import org.jetbrains.annotations.NonNls;
38 import org.jetbrains.annotations.NotNull;
40 import java.util.List;
42 public class ExtractMethodTest extends LightCodeInsightTestCase {
43 @NonNls private static final String BASE_PATH = "/refactoring/extractMethod/";
44 private boolean myCatchOnNewLine = true;
48 protected String getTestDataPath() {
49 return JavaTestUtil.getJavaTestDataPath();
52 public void testExitPoints1() throws Exception {
53 doExitPointsTest(true);
56 public void testExitPoints2() throws Exception {
60 public void testExitPoints3() throws Exception {
61 doExitPointsTest(true);
64 public void testExitPoints4() throws Exception {
68 public void testExitPoints4Nullable() throws Exception {
69 doExitPointsTest(false);
72 public void testExitPointsInsideLoop() throws Exception {
73 doExitPointsTest(true);
76 public void testExitPoints5() throws Exception {
80 public void testExitPoints6() throws Exception {
81 doExitPointsTest(false);
84 public void testExitPoints7() throws Exception {
85 doExitPointsTest(false);
88 public void testExitPoints8() throws Exception {
92 public void testExitPoints9() throws Exception {
96 public void testNotNullCheckNameConflicts() throws Exception {
100 public void testContinueInside() throws Exception {
104 public void testBooleanExpression() throws Exception {
108 public void testScr6241() throws Exception {
112 public void testScr7091() throws Exception {
116 public void testScr10464() throws Exception {
120 public void testScr9852() throws Exception {
124 public void testUseVarAfterTry() throws Exception {
128 public void testOneBranchAssignment() throws Exception {
132 public void testExtractFromCodeBlock() throws Exception {
136 public void testUnusedInitializedVar() throws Exception {
140 public void testTryFinally() throws Exception {
144 public void testFinally() throws Exception {
148 public void testExtractFromAnonymous() throws Exception {
152 public void testSCR12245() throws Exception {
156 public void testLeaveCommentsWhenExpressionExtracted() throws Exception {
160 public void testSCR15815() throws Exception {
164 public void testSCR27887() throws Exception {
168 public void testSCR28427() throws Exception {
172 public void testTryFinallyInsideFor() throws Exception {
176 public void testExtractFromTryFinally() throws Exception {
180 public void testExtractAssignmentExpression() throws Exception {
184 public void testExtractAssignmentExpressionFromStatement() throws Exception {
188 public void _testExtractFromTryFinally2() throws Exception { // IDEADEV-11844
192 public void testLesyaBug() throws Exception {
193 myCatchOnNewLine = false;
197 public void testForEach() throws Exception {
201 public void testAnonInner() throws Exception {
206 public void testConflictingAnonymous() throws Exception {
210 public void testVarDeclAfterExpressionExtraction() throws Exception {
214 public void testFinalParamUsedInsideAnon() throws Exception {
215 CodeStyleSettingsManager.getSettings(getProject()).GENERATE_FINAL_PARAMETERS = false;
219 public void testNonFinalWritableParam() throws Exception {
220 CodeStyleSettingsManager.getSettings(getProject()).GENERATE_FINAL_PARAMETERS = true;
224 public void testCodeDuplicatesWithContinue() throws Exception {
228 public void testDuplicatesFromAnonymous() throws Exception {
232 public void testCodeDuplicatesWithContinueNoReturn() throws Exception {
236 public void testCodeDuplicatesWithStaticInitializer() throws Exception {
240 public void testDuplicateInUnreachableCode() throws Exception {
244 public void testExpressionDuplicates() throws Exception {
248 public void testCodeDuplicates() throws Exception {
252 public void testCodeDuplicates2() throws Exception {
256 public void testCodeDuplicates3() throws Exception {
260 public void testCodeDuplicates4() throws Exception {
264 public void testCodeDuplicates5() throws Exception {
268 public void testCodeDuplicatesWithOutputValue() throws Exception {
272 public void testCodeDuplicatesWithOutputValue1() throws Exception {
276 public void testCodeDuplicatesWithMultExitPoints() throws Exception {
280 public void testCodeDuplicatesWithReturn() throws Exception {
284 public void testCodeDuplicatesWithReturn2() throws Exception {
288 public void testCodeDuplicatesWithReturnInAnonymous() throws Exception {
292 public void testCodeDuplicatesWithComments() throws Exception {
296 public void testSCR32924() throws Exception {
300 public void testFinalOutputVar() throws Exception {
304 public void testIdeaDev2291() throws Exception {
308 public void testOxfordBug() throws Exception {
312 public void testIDEADEV33368() throws Exception {
316 public void testInlineCreated2ReturnLocalVariablesOnly() throws Exception {
320 public void testGuardMethodDuplicates() throws Exception {
324 public void testGuardMethodDuplicates1() throws Exception {
328 public void testInstanceMethodDuplicatesInStaticContext() throws Exception {
333 public void testLValueNotDuplicate() throws Exception {
337 protected void doDuplicatesTest() throws Exception {
341 public void testExtractFromFinally() throws Exception {
345 public void testNoShortCircuit() throws Exception {
349 public void testStopFolding() throws Exception {
353 public void testStopFoldingPostfixInside() throws Exception {
357 public void testFoldingWithFieldInvolved() throws Exception {
361 public void testIDEADEV11748() throws Exception {
365 public void testIDEADEV11848() throws Exception {
369 public void testIDEADEV11036() throws Exception {
373 public void testLocalClass() throws Exception {
374 doPrepareErrorTest("Cannot extract method because the selected code fragment uses local classes defined outside of the fragment");
377 public void testLocalClassUsage() throws Exception {
378 doPrepareErrorTest("Cannot extract method because the selected code fragment defines local classes used outside of the fragment");
381 public void testStaticImport() throws Exception {
385 public void testThisCall() throws Exception {
389 public void testChainedConstructor() throws Exception {
390 doChainedConstructorTest(false);
393 public void testChainedConstructorDuplicates() throws Exception {
394 doChainedConstructorTest(true);
397 public void testChainedConstructorInvalidDuplicates() throws Exception {
398 doChainedConstructorTest(true);
401 public void testReturnFromTry() throws Exception {
405 public void testLocalClassDefinedInMethodWhichIsUsedLater() throws Exception {
406 doPrepareErrorTest("Cannot extract method because the selected code fragment defines variable of local class type used outside of the fragment");
409 public void testForceBraces() throws Exception {
410 final CommonCodeStyleSettings settings = CodeStyleSettingsManager.getSettings(getProject()).getCommonSettings(JavaLanguage.INSTANCE);
411 int old = settings.IF_BRACE_FORCE;
412 settings.IF_BRACE_FORCE = CommonCodeStyleSettings.FORCE_BRACES_ALWAYS;
417 settings.IF_BRACE_FORCE = old;
421 public void testConstantConditionsAffectingControlFlow() throws Exception {
425 public void testConstantConditionsAffectingControlFlow1() throws Exception {
428 public void testNotInitializedInsideFinally() throws Exception {
432 public void testGenericsParameters() throws Exception {
436 public void testUnusedGenerics() throws Exception {
440 public void testParamsUsedInLocalClass() throws Exception {
444 private void doChainedConstructorTest(final boolean replaceAllDuplicates) throws Exception {
445 configureByFile(BASE_PATH + getTestName(false) + ".java");
446 boolean success = performExtractMethod(true, replaceAllDuplicates, getEditor(), getFile(), getProject(), true);
448 checkResultByFile(BASE_PATH + getTestName(false) + "_after.java");
451 public void testReassignedVarAfterCall() throws Exception {
452 final CodeStyleSettings settings = CodeStyleSettingsManager.getSettings(getProject());
453 boolean oldGenerateFinalLocals = settings.GENERATE_FINAL_LOCALS;
455 settings.GENERATE_FINAL_LOCALS = true;
459 settings.GENERATE_FINAL_LOCALS = oldGenerateFinalLocals;
463 public void testNonPhysicalAssumptions() throws Exception {
467 public void testNullableCheck() throws Exception {
471 public void testNullableCheck1() throws Exception {
475 public void testNullableCheckVoid() throws Exception {
479 public void testNullableCheckDontMissFinal() throws Exception {
483 public void testNullableCheckBreak() throws Exception {
487 public void testSimpleArrayAccess() throws Exception {
491 public void testArrayAccess() throws Exception {
495 public void testArrayAccess1() throws Exception {
499 public void testArrayAccessWithLocalIndex() throws Exception {
503 public void testArrayAccessWithTopExpression() throws Exception {
507 public void testArrayAccessWithDuplicates() throws Exception {
511 public void testVerboseArrayAccess() throws Exception {
515 public void testReturnStatementFolding() throws Exception {
519 public void testWriteArrayAccess() throws Exception {
523 public void testShortCircuit() throws Exception {
527 public void testRecursiveCallToExtracted() throws Exception {
531 public void testCodeDuplicatesVarargsShouldNotChangeReturnType() throws Exception {
535 public void testParametersFromAnonymous() throws Exception {
539 public void testCast4ParamGeneration() throws Exception {
543 public void testNearComment() throws Exception {
547 public void testFoldInWhile() throws Exception {
551 public void testFoldedParamNameSuggestion() throws Exception {
555 public void testNonFoldInIfBody() throws Exception {
559 public void testComplexTypeParams() throws Exception {
563 public void testExtractWithLeadingComment() throws Exception {
567 public void testInvalidReference() throws Exception {
571 public void testRedundantCast() throws Exception {
575 public void testDisabledParam() throws Exception {
576 doTestDisabledParam();
579 public void testTypeParamsList() throws Exception {
583 public void testFromLambdaBody() throws Exception {
587 public void testFromLambdaBody1() throws Exception {
591 public void testFromLambdaBodyToAnonymous() throws Exception {
595 public void testFromLambdaBodyToToplevelInsideCodeBlock() throws Exception {
599 public void testFromLambdaBodyWithReturn() throws Exception {
603 public void testOneLineLambda() throws Exception {
607 public void testMethod2Interface() throws Exception {
611 public void testMethod2InterfaceFromStatic() throws Exception {
615 public void testMethod2InterfaceFromConstant() throws Exception {
619 public void testParamDetection() throws Exception {
623 public void testSkipComments() throws Exception {
627 public void testFinalParams4LocalClasses() throws Exception {
631 public void testIncompleteExpression() throws Exception {
635 public void testTwoFromThreeEqStatements() throws Exception {
639 public void testCastWhenDuplicateReplacement() throws Exception {
643 public void testCheckQualifierMapping() throws Exception {
647 public void testArrayReturnType() throws Exception {
651 public void testOverloadedMethods() throws Exception {
655 public void testSuggestChangeSignatureOneParam() throws Exception {
659 public void testSuggestChangeSignatureOneParamMultipleTimesInside() throws Exception {
663 public void testSuggestChangeSignatureLeaveSameExpressionsUntouched() throws Exception {
667 public void testSuggestChangeSignatureSameParamNames() throws Exception {
671 public void testSuggestChangeSignatureCallToSameClassMethod() throws Exception {
675 public void testSuggestChangeSignatureInitialParameterUnused() throws Exception {
679 public void testSuggestChangeSignatureWithFolding() throws Exception {
683 public void testSuggestChangeSignatureWithOutputVariables() throws Exception {
687 public void testSuggestChangeSignatureWithChangedParameterName() throws Exception {
688 configureByFile(BASE_PATH + getTestName(false) + ".java");
689 boolean success = performExtractMethod(true, true, getEditor(), getFile(), getProject(), false, null, false, "p");
691 checkResultByFile(BASE_PATH + getTestName(false) + "_after.java");
694 public void testTargetAnonymous() throws Exception {
698 public void testExtractUnresolvedLambdaParameter() throws Exception {
702 public void testExtractUnresolvedLambdaExpression() throws Exception {
706 public void testTheOnlyParenthesisExpressionWhichIsSkippedInControlFlow() throws Exception {
710 public void testExpression() throws Exception {
714 public void testCopyParamAnnotations() throws Exception {
718 public void testInferredNotNullInReturnStatement() throws Exception {
722 public void testSkipThrowsDeclaredInLambda() throws Exception {
726 public void testChangedReturnType() throws Exception {
727 doTestReturnTypeChanged(PsiType.getJavaLangObject(getPsiManager(), GlobalSearchScope.allScope(getProject())));
730 public void testMakeVoidMethodReturnVariable() throws Exception {
731 doTestReturnTypeChanged(PsiType.INT);
734 public void testNoReturnTypesSuggested() throws Exception {
735 doTestReturnTypeChanged(PsiType.INT);
738 public void testMultipleVarsInMethodNoReturnStatementAndAssignment() throws Exception {
739 //return type should not be suggested but still
740 doTestReturnTypeChanged(PsiType.INT);
743 public void testReassignFinalFieldInside() throws Exception {
744 doTestReturnTypeChanged(PsiType.INT);
747 public void testShortenClassRefsInNewReturnType() throws Exception {
748 doTestReturnTypeChanged(PsiType.getTypeByName(CommonClassNames.JAVA_UTIL_COLLECTION, getProject(), GlobalSearchScope.allScope(getProject())));
751 public void testPassFieldAsParameterAndMakeStatic() throws Exception {
752 doTestPassFieldsAsParams();
755 public void testDefaultNamesConflictResolution() throws Exception {
756 final CodeStyleSettings settings = CodeStyleSettingsManager.getSettings(getProject());
757 final String oldPrefix = settings.LOCAL_VARIABLE_NAME_PREFIX;
759 settings.LOCAL_VARIABLE_NAME_PREFIX = "_";
763 settings.LOCAL_VARIABLE_NAME_PREFIX = oldPrefix;
767 public void testInferredNotNull() throws Exception {
771 public void testCantPassFieldAsParameter() throws Exception {
773 doTestPassFieldsAsParams();
774 fail("Field was modified inside. Make static should be disabled");
776 catch (PrepareFailedException ignore) {
780 public void testConditionalExitCombinedWithNullabilityShouldPreserveVarsUsedInExitStatements() throws Exception {
784 public void testSingleExitPOintWithTryFinally() throws Exception {
788 public void testQualifyWhenConflictingNamePresent() throws Exception {
789 final CodeStyleSettings settings = CodeStyleSettingsManager.getSettings(getProject());
790 settings.ELSE_ON_NEW_LINE = true;
791 settings.CATCH_ON_NEW_LINE = myCatchOnNewLine;
792 configureByFile(BASE_PATH + getTestName(false) + ".java");
793 final PsiClass psiClass = PsiTreeUtil.getParentOfType(getFile().findElementAt(getEditor().getSelectionModel().getLeadSelectionOffset()), PsiClass.class);
794 assertNotNull(psiClass);
795 boolean success = performExtractMethod(true, true, getEditor(), getFile(), getProject(), false, null, false, null, psiClass.getContainingClass());
797 checkResultByFile(BASE_PATH + getTestName(false) + "_after.java");
800 private void doTestDisabledParam() throws PrepareFailedException {
801 final CodeStyleSettings settings = CodeStyleSettingsManager.getSettings(getProject());
802 settings.ELSE_ON_NEW_LINE = true;
803 settings.CATCH_ON_NEW_LINE = myCatchOnNewLine;
804 configureByFile(BASE_PATH + getTestName(false) + ".java");
805 boolean success = performExtractMethod(true, true, getEditor(), getFile(), getProject(), false, 0);
807 checkResultByFile(BASE_PATH + getTestName(false) + "_after.java");
810 private void doTestReturnTypeChanged(PsiType type) throws PrepareFailedException {
811 final CodeStyleSettings settings = CodeStyleSettingsManager.getSettings(getProject());
812 settings.ELSE_ON_NEW_LINE = true;
813 settings.CATCH_ON_NEW_LINE = myCatchOnNewLine;
814 configureByFile(BASE_PATH + getTestName(false) + ".java");
815 boolean success = performExtractMethod(true, true, getEditor(), getFile(), getProject(), false, type, false, null);
817 checkResultByFile(BASE_PATH + getTestName(false) + "_after.java");
820 private void doTestPassFieldsAsParams() throws PrepareFailedException {
821 final CodeStyleSettings settings = CodeStyleSettingsManager.getSettings(getProject());
822 settings.ELSE_ON_NEW_LINE = true;
823 settings.CATCH_ON_NEW_LINE = myCatchOnNewLine;
824 configureByFile(BASE_PATH + getTestName(false) + ".java");
825 boolean success = performExtractMethod(true, true, getEditor(), getFile(), getProject(), false, null, true, null);
827 checkResultByFile(BASE_PATH + getTestName(false) + "_after.java");
830 private void doPrepareErrorTest(final String expectedMessage) throws Exception {
831 String expectedError = null;
833 doExitPointsTest(false);
835 catch(PrepareFailedException ex) {
836 expectedError = ex.getMessage();
838 assertEquals(expectedMessage, expectedError);
841 private void doExitPointsTest(boolean shouldSucceed) throws Exception {
842 String fileName = getTestName(false) + ".java";
843 configureByFile(BASE_PATH + fileName);
844 boolean success = performAction(false, false);
845 assertEquals(shouldSucceed, success);
848 void doTest() throws Exception {
849 final CodeStyleSettings settings = CodeStyleSettingsManager.getSettings(getProject());
850 settings.ELSE_ON_NEW_LINE = true;
851 settings.CATCH_ON_NEW_LINE = myCatchOnNewLine;
855 private void doTest(boolean duplicates) throws Exception {
856 configureByFile(BASE_PATH + getTestName(false) + ".java");
857 boolean success = performAction(true, duplicates);
859 checkResultByFile(BASE_PATH + getTestName(false) + "_after.java");
862 private static boolean performAction(boolean doRefactor, boolean replaceAllDuplicates) throws Exception {
863 return performExtractMethod(doRefactor, replaceAllDuplicates, getEditor(), getFile(), getProject());
866 public static boolean performExtractMethod(boolean doRefactor, boolean replaceAllDuplicates, Editor editor, PsiFile file, Project project)
867 throws PrepareFailedException, IncorrectOperationException {
868 return performExtractMethod(doRefactor, replaceAllDuplicates, editor, file, project, false);
871 public static boolean performExtractMethod(boolean doRefactor, boolean replaceAllDuplicates, Editor editor, PsiFile file, Project project,
872 final boolean extractChainedConstructor)
873 throws PrepareFailedException, IncorrectOperationException {
874 return performExtractMethod(doRefactor, replaceAllDuplicates, editor, file, project, extractChainedConstructor, null);
877 public static boolean performExtractMethod(boolean doRefactor,
878 boolean replaceAllDuplicates,
882 final boolean extractChainedConstructor,
883 int... disabledParams)
884 throws PrepareFailedException, IncorrectOperationException {
885 return performExtractMethod(doRefactor, replaceAllDuplicates, editor, file, project, extractChainedConstructor, null, false, null, disabledParams);
888 public static boolean performExtractMethod(boolean doRefactor,
889 boolean replaceAllDuplicates,
893 final boolean extractChainedConstructor,
896 String newNameOfFirstParam,
897 int... disabledParams)
898 throws PrepareFailedException, IncorrectOperationException {
899 return performExtractMethod(doRefactor, replaceAllDuplicates, editor, file, project, extractChainedConstructor, returnType, makeStatic,
900 newNameOfFirstParam, null, disabledParams);
903 public static boolean performExtractMethod(boolean doRefactor,
904 boolean replaceAllDuplicates,
908 final boolean extractChainedConstructor,
911 String newNameOfFirstParam,
912 PsiClass targetClass,
913 int... disabledParams)
914 throws PrepareFailedException, IncorrectOperationException {
915 int startOffset = editor.getSelectionModel().getSelectionStart();
916 int endOffset = editor.getSelectionModel().getSelectionEnd();
918 PsiElement[] elements;
919 PsiExpression expr = CodeInsightUtil.findExpressionInRange(file, startOffset, endOffset);
921 elements = new PsiElement[]{expr};
924 elements = CodeInsightUtil.findStatementsInRange(file, startOffset, endOffset);
926 if (elements.length == 0) {
927 final PsiExpression expression = IntroduceVariableBase.getSelectedExpression(project, file, startOffset, endOffset);
928 if (expression != null) {
929 elements = new PsiElement[]{expression};
932 assertTrue(elements.length > 0);
934 final ExtractMethodProcessor processor =
935 new ExtractMethodProcessor(project, editor, elements, null, "Extract Method", "newMethod", null);
936 processor.setShowErrorDialogs(false);
937 processor.setChainedConstructor(extractChainedConstructor);
939 if (!processor.prepare()) {
944 processor.testTargetClass(targetClass);
945 processor.testPrepare(returnType, makeStatic);
946 processor.testNullness();
947 if (disabledParams != null) {
948 for (int param : disabledParams) {
949 processor.doNotPassParameter(param);
952 if (newNameOfFirstParam != null) {
953 processor.changeParamName(0, newNameOfFirstParam);
955 ExtractMethodHandler.run(project, editor, processor);
958 if (replaceAllDuplicates) {
959 final Boolean hasDuplicates = processor.hasDuplicates();
960 if (hasDuplicates == null || hasDuplicates.booleanValue()) {
961 final List<Match> duplicates = processor.getDuplicates();
962 for (final Match match : duplicates) {
963 if (!match.getMatchStart().isValid() || !match.getMatchEnd().isValid()) continue;
964 PsiDocumentManager.getInstance(project).commitAllDocuments();
965 ApplicationManager.getApplication().runWriteAction(new Runnable() {
968 processor.processMatch(match);