/* * 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.inline; import com.intellij.JavaTestUtil; import com.intellij.codeInsight.TargetElementUtil; import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.PsiCall; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiClassType; import com.intellij.psi.PsiElement; import com.intellij.refactoring.LightRefactoringTestCase; import com.intellij.usageView.UsageInfo; import com.intellij.util.containers.MultiMap; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import java.util.Iterator; /** * @author yole */ public class InlineToAnonymousClassTest extends LightRefactoringTestCase { @NotNull @Override protected String getTestDataPath() { return JavaTestUtil.getJavaTestDataPath(); } public void testSimple() throws Exception { doTest(false, false); } public void testChangeToSuperType() throws Exception { doTest(false, false); } public void testImplementsInterface() throws Exception { doTest(false, false); } public void testClassInitializer() throws Exception { doTest(false, false); } public void testConstructor() throws Exception { doTest(false, false); } public void testConstructorWithArguments() throws Exception { doTest(false, false); } public void testUnrelatedParameters() throws Exception { doTest(false, false); } public void testConstructorWithArgumentsInExpression() throws Exception { doTest(false, false); } public void testMultipleConstructors() throws Exception { doTest(false, false); } public void testMethodUsage() throws Exception { doTest(false, false); } public void testConstructorArgumentToField() throws Exception { doTest(false, false); } public void testField() throws Exception { doTest(false, false); } public void testStaticConstantField() throws Exception { doTest(false, false); } public void testWritableInitializedField() throws Exception { doTest(false, false); } public void testNullInitializedField() throws Exception { doTest(false, false); } public void testInnerClass() throws Exception { doTest(false, false); } public void testConstructorToInstanceInitializer() throws Exception { doTest(false, false); } public void testNewExpressionContext() throws Exception { doTest(false, false); } public void testWritableFieldInitializedWithParameter() throws Exception { doTest(false, false); } public void testFieldInitializedWithVar() throws Exception { doTest(false, false); } public void testFieldVsLocalConflict() throws Exception { doTest(false, false); } public void testFieldVsParameterConflict() throws Exception { doTest(false, false); } public void testGenerics() throws Exception { doTest(false, false); } public void testGenericsSubstitute() throws Exception { doTest(false, false); } public void testGenericsFieldDeclaration() throws Exception { doTest(false, false); } public void testGenericsRawType() throws Exception { doTest(false, false); } public void testGenericsInTypeParameter() throws Exception { doTest(false, false); } public void testQualifyInner() throws Exception { doTest(false, false); } public void testQualifiedNew() throws Exception { doTest(false, false); } public void testChainedConstructors() throws Exception { doTest(false, false); } public void testChainedVarargConstructors() throws Exception { doTest(false, false); } public void testInlineThisOnly() throws Exception { doTest(true, false); } public void testArrayType() throws Exception { doTest(false, false); } public void testArrayTypeWithGenerics() throws Exception { doTest(false, false); } public void testArrayInitializer() throws Exception { doTest(false, false); } public void testVarargs() throws Exception { doTest(false, false); } public void testSelfReference() throws Exception { doTest(false, false); } public void testOuterClassFieldAccess() throws Exception { doTest(false, false); } public void testPrivateFieldUsedFromInnerClass() throws Exception { doTest(false, false); } public void testOverwriteInitializer() throws Exception { doTest(false, false); } public void testMultipleInnerClasses() throws Exception { doTest(false, false); } public void testConstructorArgumentInExpression() throws Exception { doTest(false, false); } public void testMethodCallInNewExpression() throws Exception { doTest(false, false); } public void testMethodCallInNewExpressionWithParens() throws Exception { doTest(false, false); } public void testRedundantImplementsInterface() throws Exception { doTest(false, false); } public void testStringInMethodCallFromConstructor() throws Exception { doTest(false, false); } public void testMultipleGeneratedVars() throws Exception { doTest(false, false); } public void testFieldAsConstructorParameter() throws Exception { doTest(false, false); } public void testQualifyParentStaticReferences() throws Exception { doTest(false, false); } public void testLocalClass() throws Exception { doTest(false, true); } public void testMultipleAssignments() throws Exception { doTest(false, true); } public void testParamTypeReplacement() throws Exception { doTest(false, true); } public void testBraces() throws Exception { doTest(false, false); } public void testAvailableInSupers() throws Exception { doTest(false, false); } public void testNoFinalForJava8() throws Exception { setLanguageLevel(LanguageLevel.HIGHEST); doTest(true, false); } public void testNoInlineAbstract() throws Exception { doTestNoInline("Abstract classes cannot be inlined"); } public void testNoInlineInterface() throws Exception { doTestNoInline("Interfaces cannot be inlined"); } public void testNoInlineEnum() throws Exception { doTestNoInline("Enums cannot be inlined"); } public void testNoInlineAnnotationType() throws Exception { doTestNoInline("Annotation types cannot be inlined"); } public void testNoInlineMultipleInterfaces() throws Exception { doTestNoInline("Classes which implement multiple interfaces cannot be inlined"); } public void testNoInlineSuperclassInterface() throws Exception { doTestNoInline("Classes which have a superclass and implement an interface cannot be inlined"); } public void testNoInlineMethodUsage() throws Exception { doTestNoInline("Class cannot be inlined because there are usages of its methods not inherited from its superclass or interface"); } public void testNoInlineFieldUsage() throws Exception { doTestNoInline("Class cannot be inlined because it has usages of fields not inherited from its superclass"); } public void testNoInlineNewWithInner() throws Exception { doTestNoInline("Class cannot be inlined because it has usages of its inner classes"); } public void testNoInlineStaticField() throws Exception { doTestNoInline("Class cannot be inlined because it has static fields with non-constant initializers"); } public void testNoInlineStaticNonFinalField() throws Exception { doTestNoInline("Class cannot be inlined because it has static non-final fields"); } public void testNoInlineStaticMethod() throws Exception { doTestNoInline("Class cannot be inlined because it has static methods"); } public void testNoInlineStaticInitializer() throws Exception { doTestNoInline("Class cannot be inlined because it has static initializers"); } public void testNoInlineClassLiteral() throws Exception { doTestPreprocessUsages("Class cannot be inlined because it has usages of its class literal"); } public void testNoInlineCatchClause() throws Exception { doTestPreprocessUsages("Class cannot be inlined because it is used in a 'catch' clause"); } public void testNoInlineThrowsClause() throws Exception { doTestPreprocessUsages("Class cannot be inlined because it is used in a 'throws' clause"); } public void testNoInlineThisQualifier() throws Exception { doTestPreprocessUsages("Class cannot be inlined because it is used as a 'this' qualifier"); } public void testNoInlineUnresolvedConstructor() throws Exception { doTestPreprocessUsages("Class cannot be inlined because a call to its constructor is unresolved"); } public void testNoInlineUnresolvedConstructor2() throws Exception { doTestPreprocessUsages("Class cannot be inlined because a call to its constructor is unresolved"); } public void testNoInlineStaticInnerClass() throws Exception { doTestNoInline("Class cannot be inlined because it has static inner classes"); } public void testNoInlineReturnInConstructor() throws Exception { doTestNoInline("Class cannot be inlined because its constructor contains 'return' statements"); } public void testNoInlineUnresolvedSuperclass() throws Exception { doTestNoInline("Class cannot be inlined because its superclass cannot be resolved"); } public void testNoInlineUnresolvedInterface() throws Exception { doTestNoInline("Class cannot be inlined because an interface implemented by it cannot be resolved"); } public void testNoInlineLibraryClass() throws Exception { doTestNoInline("Library classes cannot be inlined"); } public void testNoInlineTypeParameter() throws Exception { doTestNoInline("Type parameters cannot be inlined"); } public void testNoInlineNoUsages() throws Exception { doTestPreprocessUsages("Class is never used"); } public void testNoInlineRecursiveAccess() throws Exception { doTestConflict("Class cannot be inlined because a call to its member inside body", "Class cannot be inlined because a call to its member inside body"); } public void testConflictInaccessibleOuterField() throws Exception { doTestConflict( "Field <b><code>C2.a</code></b> that is used in inlined method is not accessible from call site(s) in method <b><code>C2User.test()</code></b>"); } public void testGetClassConflict() throws Exception { doTestConflict("Result of getClass() invocation would be changed", "Result of getClass() invocation would be changed"); } public void doTestConflict(final String... expected) throws Exception { InlineToAnonymousClassProcessor processor = prepareProcessor(); UsageInfo[] usages = processor.findUsages(); MultiMap<PsiElement,String> conflicts = processor.getConflicts(usages); assertEquals(expected.length, conflicts.size()); final Iterator<? extends String> iterator = conflicts.values().iterator(); for (String s : expected) { assertTrue(iterator.hasNext()); assertEquals(s, iterator.next()); } } private void doTestNoInline(final String expectedMessage) throws Exception { String name = getTestName(false); @NonNls String fileName = "/refactoring/inlineToAnonymousClass/" + name + ".java"; configureByFile(fileName); PsiElement element = TargetElementUtil .findTargetElement(myEditor, TargetElementUtil.ELEMENT_NAME_ACCEPTED | TargetElementUtil.REFERENCED_ELEMENT_ACCEPTED); assertInstanceOf(element, PsiClass.class); String message = InlineToAnonymousClassHandler.getCannotInlineMessage((PsiClass) element); assertEquals(expectedMessage, message); } private void doTest(final boolean inlineThisOnly, final boolean searchInNonJavaFiles) throws Exception { String name = getTestName(false); @NonNls String fileName = "/refactoring/inlineToAnonymousClass/" + name + ".java"; configureByFile(fileName); performAction(inlineThisOnly, searchInNonJavaFiles); checkResultByFile(null, fileName + ".after", true); } private void doTestPreprocessUsages(final String expectedMessage) throws Exception { configureByFile("/refactoring/inlineToAnonymousClass/" + getTestName(false) + ".java"); PsiElement element = TargetElementUtil.findTargetElement(myEditor, TargetElementUtil .ELEMENT_NAME_ACCEPTED | TargetElementUtil.REFERENCED_ELEMENT_ACCEPTED); assertInstanceOf(element, PsiClass.class); final PsiClass psiClass = (PsiClass)element; assertEquals(expectedMessage, InlineToAnonymousClassHandler.getCannotInlineMessage(psiClass)); } private InlineToAnonymousClassProcessor prepareProcessor() throws Exception { String name = getTestName(false); @NonNls String fileName = "/refactoring/inlineToAnonymousClass/" + name + ".java"; configureByFile(fileName); PsiElement element = TargetElementUtil.findTargetElement(myEditor, TargetElementUtil .ELEMENT_NAME_ACCEPTED | TargetElementUtil.REFERENCED_ELEMENT_ACCEPTED); assertInstanceOf(element, PsiClass.class); assertEquals(null, InlineToAnonymousClassHandler.getCannotInlineMessage((PsiClass) element)); return new InlineToAnonymousClassProcessor(getProject(), (PsiClass) element, null, false, false, false); } private void performAction(final boolean inlineThisOnly, final boolean searchInNonJavaFiles) { PsiElement element = TargetElementUtil .findTargetElement(myEditor, TargetElementUtil.ELEMENT_NAME_ACCEPTED | TargetElementUtil.REFERENCED_ELEMENT_ACCEPTED); PsiCall callToInline = InlineToAnonymousClassHandler.findCallToInline(myEditor); PsiClass classToInline = (PsiClass) element; assertEquals(null, InlineToAnonymousClassHandler.getCannotInlineMessage(classToInline)); final InlineToAnonymousClassProcessor processor = new InlineToAnonymousClassProcessor(getProject(), classToInline, callToInline, inlineThisOnly, false, searchInNonJavaFiles); UsageInfo[] usages = processor.findUsages(); MultiMap<PsiElement, String> conflicts = processor.getConflicts(usages); assertEquals(0, conflicts.size()); processor.run(); } public void testCanBeInvokedOnReference() throws Exception { doTestCanBeInvokedOnReference(true); } public void testCanBeInvokedOnReference1() throws Exception { doTestCanBeInvokedOnReference(true); } public void testCanBeInvokedOnReferenceSubstitution() throws Exception { doTestCanBeInvokedOnReference(true); } public void testCanBeInvokedOnReferenceSubstitution1() throws Exception { doTestCanBeInvokedOnReference(true); } public void testCanBeInvokedOnReferenceVarargs() throws Exception { doTestCanBeInvokedOnReference(true); } public void testCantBeInvokedOnReference() throws Exception { doTestCanBeInvokedOnReference(false); } public void testCantBeInvokedOnReference1() throws Exception { doTestCanBeInvokedOnReference(false); } public void testCantBeInvokedOnReferenceReturnStatement() throws Exception { doTestCanBeInvokedOnReference(false); } public void testCanBeInvokedOnReferenceSyncStatement() throws Exception { doTestCanBeInvokedOnReference(true); } private void doTestCanBeInvokedOnReference(boolean canBeInvokedOnReference) throws Exception { configureByFile("/refactoring/inlineToAnonymousClass/" + getTestName(false) + ".java"); PsiElement element = TargetElementUtil .findTargetElement(myEditor, TargetElementUtil.ELEMENT_NAME_ACCEPTED | TargetElementUtil.REFERENCED_ELEMENT_ACCEPTED); PsiCall callToInline = InlineToAnonymousClassHandler.findCallToInline(myEditor); PsiClass classToInline = (PsiClass) element; assertEquals(null, InlineToAnonymousClassHandler.getCannotInlineMessage(classToInline)); final PsiClassType superType = InlineToAnonymousClassProcessor.getSuperType(classToInline); assertTrue(superType != null); assertEquals(canBeInvokedOnReference, InlineToAnonymousClassHandler.canBeInvokedOnReference(callToInline, superType)); } @Override protected LanguageLevel getLanguageLevel() { return LanguageLevel.JDK_1_7; } }