extract field: allow to extract static field from super/this calls (IDEA-197438)

This commit is contained in:
Anna.Kozlova
2018-08-16 17:58:58 +02:00
parent b4ef5da317
commit afea7732d8
7 changed files with 111 additions and 88 deletions
@@ -18,6 +18,7 @@ package com.intellij.refactoring.introduceField;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInsight.ChangeContextUtil;
import com.intellij.codeInsight.ExceptionUtil;
import com.intellij.codeInsight.TestFrameworks;
import com.intellij.codeInsight.daemon.impl.quickfix.AnonymousTargetClassPreselectionUtil;
import com.intellij.codeInsight.highlighting.HighlightManager;
@@ -57,6 +58,7 @@ import com.intellij.refactoring.util.CommonRefactoringUtil;
import com.intellij.refactoring.util.EnumConstantsUtil;
import com.intellij.refactoring.util.RefactoringChangeUtil;
import com.intellij.refactoring.util.RefactoringUtil;
import com.intellij.refactoring.util.classMembers.ClassMemberReferencesVisitor;
import com.intellij.refactoring.util.occurrences.OccurrenceManager;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.VisibilityUtil;
@@ -181,8 +183,12 @@ public abstract class BaseExpressionToFieldHandler extends IntroduceHandlerBase
PsiElement tempAnchorElement = RefactoringUtil.getParentExpressionAnchorElement(selectedExpr);
if (!Comparing.strEqual(IntroduceConstantHandler.REFACTORING_NAME, getRefactoringName()) &&
IntroduceVariableBase.checkAnchorBeforeThisOrSuper(project, editor, tempAnchorElement, getRefactoringName(), getHelpID()))
IntroduceFieldHandler.isInSuperOrThis(selectedExpr) &&
isStaticFinalInitializer(selectedExpr) != null) {
String message = RefactoringBundle.getCannotRefactorMessage(RefactoringBundle.message("invalid.expression.context"));
CommonRefactoringUtil.showErrorHint(project, editor, message, getRefactoringName(), getHelpID());
return true;
}
final Settings settings =
showRefactoringDialog(project, editor, myParentClass, selectedExpr, tempType,
@@ -245,6 +251,15 @@ public abstract class BaseExpressionToFieldHandler extends IntroduceHandlerBase
);
}
@Nullable
protected PsiElement isStaticFinalInitializer(PsiExpression expr) {
PsiClass parentClass = expr != null ? getParentClass(expr) : null;
if (parentClass == null) return null;
IsStaticFinalInitializerExpression visitor = new IsStaticFinalInitializerExpression(parentClass, expr);
expr.accept(visitor);
return visitor.getElementReference();
}
protected abstract OccurrenceManager createOccurrenceManager(PsiExpression selectedExpr, PsiClass parentClass);
protected final PsiClass getParentClass() {
@@ -925,4 +940,74 @@ public abstract class BaseExpressionToFieldHandler extends IntroduceHandlerBase
return myField;
}
}
private static class IsStaticFinalInitializerExpression extends ClassMemberReferencesVisitor {
private PsiElement myElementReference;
private final PsiExpression myInitializer;
private boolean myCheckThrowables = true;
public IsStaticFinalInitializerExpression(PsiClass aClass, PsiExpression initializer) {
super(aClass);
myInitializer = initializer;
}
@Override
public void visitReferenceExpression(PsiReferenceExpression expression) {
final PsiElement psiElement = expression.resolve();
if ((psiElement instanceof PsiLocalVariable || psiElement instanceof PsiParameter) &&
!PsiTreeUtil.isAncestor(myInitializer, psiElement, false)) {
myElementReference = expression;
}
else {
super.visitReferenceExpression(expression);
}
}
@Override
public void visitMethodReferenceExpression(PsiMethodReferenceExpression expression) {
if (!PsiMethodReferenceUtil.isResolvedBySecondSearch(expression)) {
super.visitMethodReferenceExpression(expression);
}
}
@Override
public void visitCallExpression(PsiCallExpression callExpression) {
super.visitCallExpression(callExpression);
if (!myCheckThrowables) return;
final List<PsiClassType> checkedExceptions = ExceptionUtil.getThrownCheckedExceptions(callExpression);
if (!checkedExceptions.isEmpty()) {
myElementReference = callExpression;
}
}
@Override
public void visitClass(PsiClass aClass) {
myCheckThrowables = false;
super.visitClass(aClass);
}
@Override
public void visitLambdaExpression(PsiLambdaExpression expression) {
myCheckThrowables = false;
super.visitLambdaExpression(expression);
}
@Override
protected void visitClassMemberReferenceElement(PsiMember classMember, PsiJavaCodeReferenceElement classMemberReference) {
if (!classMember.hasModifierProperty(PsiModifier.STATIC)) {
myElementReference = classMemberReference;
}
}
@Override
public void visitElement(PsiElement element) {
if (myElementReference != null) return;
super.visitElement(element);
}
@Nullable
public PsiElement getElementReference() {
return myElementReference;
}
}
}
@@ -15,7 +15,6 @@
*/
package com.intellij.refactoring.introduceField;
import com.intellij.codeInsight.ExceptionUtil;
import com.intellij.codeInsight.highlighting.HighlightManager;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.Editor;
@@ -34,14 +33,11 @@ import com.intellij.refactoring.introduce.inplace.AbstractInplaceIntroducer;
import com.intellij.refactoring.ui.TypeSelectorManagerImpl;
import com.intellij.refactoring.util.CommonRefactoringUtil;
import com.intellij.refactoring.util.RefactoringUtil;
import com.intellij.refactoring.util.classMembers.ClassMemberReferencesVisitor;
import com.intellij.refactoring.util.occurrences.ExpressionOccurrenceManager;
import com.intellij.refactoring.util.occurrences.OccurrenceManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
public class IntroduceConstantHandler extends BaseExpressionToFieldHandler {
public static final String REFACTORING_NAME = RefactoringBundle.message("introduce.constant.title");
@@ -212,90 +208,11 @@ public class IntroduceConstantHandler extends BaseExpressionToFieldHandler {
return myInplaceIntroduceConstantPopup;
}
@Nullable
private PsiElement isStaticFinalInitializer(PsiExpression expr) {
PsiClass parentClass = expr != null ? getParentClass(expr) : null;
if (parentClass == null) return null;
IsStaticFinalInitializerExpression visitor = new IsStaticFinalInitializerExpression(parentClass, expr);
expr.accept(visitor);
return visitor.getElementReference();
}
@Override
protected OccurrenceManager createOccurrenceManager(final PsiExpression selectedExpr, final PsiClass parentClass) {
return new ExpressionOccurrenceManager(selectedExpr, parentClass, null);
}
private static class IsStaticFinalInitializerExpression extends ClassMemberReferencesVisitor {
private PsiElement myElementReference;
private final PsiExpression myInitializer;
private boolean myCheckThrowables = true;
public IsStaticFinalInitializerExpression(PsiClass aClass, PsiExpression initializer) {
super(aClass);
myInitializer = initializer;
}
@Override
public void visitReferenceExpression(PsiReferenceExpression expression) {
final PsiElement psiElement = expression.resolve();
if ((psiElement instanceof PsiLocalVariable || psiElement instanceof PsiParameter) &&
!PsiTreeUtil.isAncestor(myInitializer, psiElement, false)) {
myElementReference = expression;
}
else {
super.visitReferenceExpression(expression);
}
}
@Override
public void visitMethodReferenceExpression(PsiMethodReferenceExpression expression) {
if (!PsiMethodReferenceUtil.isResolvedBySecondSearch(expression)) {
super.visitMethodReferenceExpression(expression);
}
}
@Override
public void visitCallExpression(PsiCallExpression callExpression) {
super.visitCallExpression(callExpression);
if (!myCheckThrowables) return;
final List<PsiClassType> checkedExceptions = ExceptionUtil.getThrownCheckedExceptions(callExpression);
if (!checkedExceptions.isEmpty()) {
myElementReference = callExpression;
}
}
@Override
public void visitClass(PsiClass aClass) {
myCheckThrowables = false;
super.visitClass(aClass);
}
@Override
public void visitLambdaExpression(PsiLambdaExpression expression) {
myCheckThrowables = false;
super.visitLambdaExpression(expression);
}
@Override
protected void visitClassMemberReferenceElement(PsiMember classMember, PsiJavaCodeReferenceElement classMemberReference) {
if (!classMember.hasModifierProperty(PsiModifier.STATIC)) {
myElementReference = classMemberReference;
}
}
@Override
public void visitElement(PsiElement element) {
if (myElementReference != null) return;
super.visitElement(element);
}
@Nullable
public PsiElement getElementReference() {
return myElementReference;
}
}
@Override
public PsiClass getParentClass(@NotNull PsiExpression initializerExpression) {
final PsiType type = initializerExpression.getType();
@@ -87,7 +87,7 @@ public abstract class IntroduceFieldCentralPanel {
myTypeSelectorManager = typeSelectorManager;
}
protected boolean setEnabledInitializationPlaces(@NotNull final PsiElement initializer) {
protected boolean setEnabledInitializationPlaces(@NotNull final PsiExpression initializer) {
final Set<PsiField> fields = new HashSet<>();
final Ref<Boolean> refsLocal = new Ref<>(false);
initializer.accept(new JavaRecursiveElementWalkingVisitor() {
@@ -113,11 +113,12 @@ public abstract class IntroduceFieldCentralPanel {
});
final boolean locals = refsLocal.get();
if (!locals && fields.isEmpty()) {
boolean superOrThis = IntroduceFieldHandler.isInSuperOrThis(initializer);
if (!locals && fields.isEmpty() && !superOrThis) {
return true;
}
return updateInitializationPlaceModel(!locals && initializedInSetUp(fields),
!locals && initializedInConstructor(fields));
!locals && !superOrThis && initializedInConstructor(fields));
}
private static boolean initializedInConstructor(Set<PsiField> fields) {
@@ -174,7 +174,7 @@ public class IntroduceFieldHandler extends BaseExpressionToFieldHandler {
return myInplaceIntroduceFieldPopup;
}
private static boolean isInSuperOrThis(PsiExpression occurrence) {
static boolean isInSuperOrThis(PsiExpression occurrence) {
return !NotInSuperCallOccurrenceFilter.INSTANCE.isOK(occurrence) || !NotInThisCallFilter.INSTANCE.isOK(occurrence);
}
@@ -0,0 +1,8 @@
class Test {
public static final String foo = "foo";
Test(String s) {}
Test() {
this(foo);
}
}
@@ -0,0 +1,6 @@
class Test {
Test(String s) {}
Test() {
this(<selection>"foo"</selection>);
}
}
@@ -154,6 +154,12 @@ public class IntroduceFieldInSameClassTest extends LightCodeInsightTestCase {
}
}
public void testAcceptIntroduceFieldFromExprInThisCall() {
configureByFile("beforeAcceptIntroduceFieldFromExprInThisCall.java");
performRefactoring(BaseExpressionToFieldHandler.InitializationPlace.IN_FIELD_DECLARATION, true);
checkResultByFile("afterAcceptIntroduceFieldFromExprInThisCall.java");
}
public void testInConstructorEnclosingAnonymous() {
configureByFile("beforeEnclosingAnonymous.java");
performRefactoring(BaseExpressionToFieldHandler.InitializationPlace.IN_CONSTRUCTOR, false);