introduce field: resolve conflicts with constructor params (IDEA-91633)

This commit is contained in:
Anna Kozlova
2018-06-22 16:02:47 +03:00
parent 12698d6683
commit 14e65418b5
4 changed files with 72 additions and 39 deletions
@@ -175,7 +175,7 @@ public abstract class LocalToFieldHandler {
private static PsiExpressionStatement createAssignment(PsiLocalVariable local, String fieldname, PsiElementFactory factory) {
try {
String pattern = fieldname + "=0;";
PsiExpressionStatement statement = (PsiExpressionStatement)factory.createStatementFromText(pattern, null);
PsiExpressionStatement statement = (PsiExpressionStatement)factory.createStatementFromText(pattern, local);
statement = (PsiExpressionStatement)CodeStyleManager.getInstance(local.getProject()).reformat(statement);
PsiAssignmentExpression expr = (PsiAssignmentExpression)statement.getExpression();
@@ -212,6 +212,7 @@ public abstract class LocalToFieldHandler {
PsiClass aClass = field.getContainingClass();
PsiMethod[] constructors = aClass.getConstructors();
PsiStatement assignment = createAssignment(local, field.getName(), factory);
PsiExpression thisAccessExpr = factory.createExpressionFromText("this", null);
boolean added = false;
for (PsiMethod constructor : constructors) {
if (constructor == enclosingConstructor) continue;
@@ -228,7 +229,9 @@ public abstract class LocalToFieldHandler {
continue;
}
if ("super".equals(text) && enclosingConstructor == null && PsiTreeUtil.isAncestor(constructor, local, false)) {
ChangeContextUtil.encodeContextInfo(assignment, false);
final PsiStatement statement = (PsiStatement)body.addAfter(assignment, first);
ChangeContextUtil.decodeContextInfo(statement, field.getContainingClass(), thisAccessExpr);
appendComments(local, statement);
local.delete();
return statement;
@@ -236,14 +239,18 @@ public abstract class LocalToFieldHandler {
}
}
if (enclosingConstructor == null && PsiTreeUtil.isAncestor(constructor, local, false)) {
ChangeContextUtil.encodeContextInfo(assignment, false);
final PsiStatement statement = (PsiStatement)body.addBefore(assignment, first);
ChangeContextUtil.decodeContextInfo(statement, field.getContainingClass(), thisAccessExpr);
appendComments(local, statement);
local.delete();
return statement;
}
}
ChangeContextUtil.encodeContextInfo(assignment, false);
assignment = (PsiStatement)body.add(assignment);
ChangeContextUtil.decodeContextInfo(assignment, field.getContainingClass(), thisAccessExpr);
added = true;
}
if (!added && enclosingConstructor == null) {
@@ -0,0 +1,11 @@
class Foo {
String foo = "foo";
public final String fooBar;
public Foo(int foo) {
fooBar = this.foo.subString(1);
}
void bar() {
}
}
@@ -0,0 +1,9 @@
class Foo {
String foo = "foo";
public Foo(int foo) {
}
void bar() {
String fo<caret>oBar = foo.subString(1);
}
}
@@ -34,28 +34,28 @@ public class IntroduceFieldInSameClassTest extends LightCodeInsightTestCase {
@NotNull
@Override
protected String getTestDataPath() {
return JavaTestUtil.getJavaTestDataPath();
return JavaTestUtil.getJavaTestDataPath() + "/refactoring/introduceField/";
}
public void testInClassInitializer() {
configureByFile("/refactoring/introduceField/before1.java");
configureByFile("before1.java");
performRefactoring(BaseExpressionToFieldHandler.InitializationPlace.IN_FIELD_DECLARATION, true);
checkResultByFile("/refactoring/introduceField/after1.java");
checkResultByFile("after1.java");
}
public void testConflictingFieldInContainingClass() {
configureByFile("/refactoring/introduceField/beforeConflictingFieldInContainingClass.java");
configureByFile("beforeConflictingFieldInContainingClass.java");
new MockIntroduceFieldHandler(BaseExpressionToFieldHandler.InitializationPlace.IN_FIELD_DECLARATION, false) {
@Override
protected String getNewName(Project project, PsiExpression expr, PsiType type) {
return "aField";
}
}.invoke(getProject(), myEditor, myFile, null);
checkResultByFile("/refactoring/introduceField/afterConflictingFieldInContainingClass.java");
checkResultByFile("afterConflictingFieldInContainingClass.java");
}
public void testConflictingFieldInContainingClassLocal() {
configureByFile("/refactoring/introduceField/beforeConflictingFieldInContainingClassLocal.java");
configureByFile("beforeConflictingFieldInContainingClassLocal.java");
new MockIntroduceFieldHandler(BaseExpressionToFieldHandler.InitializationPlace.IN_FIELD_DECLARATION, false) {
@Override
protected String getNewName(Project project, PsiExpression expr, PsiType type) {
@@ -67,78 +67,84 @@ public class IntroduceFieldInSameClassTest extends LightCodeInsightTestCase {
return 0;
}
}.invoke(getProject(), myEditor, myFile, null);
checkResultByFile("/refactoring/introduceField/afterConflictingFieldInContainingClassLocal.java");
checkResultByFile("afterConflictingFieldInContainingClassLocal.java");
}
public void testInElseClause() {
configureByFile("/refactoring/introduceField/beforeElseClause.java");
configureByFile("beforeElseClause.java");
performRefactoring(BaseExpressionToFieldHandler.InitializationPlace.IN_CURRENT_METHOD, true);
checkResultByFile("/refactoring/introduceField/afterElseClause.java");
checkResultByFile("afterElseClause.java");
}
public void testOuterClass() {
configureByFile("/refactoring/introduceField/beforeOuterClass.java");
configureByFile("beforeOuterClass.java");
performRefactoring(BaseExpressionToFieldHandler.InitializationPlace.IN_CONSTRUCTOR, false);
checkResultByFile("/refactoring/introduceField/afterOuterClass.java");
checkResultByFile("afterOuterClass.java");
}
public void testConflictingConstructorParameter() {
configureByFile("beforeConflictingConstructorParameter.java");
performRefactoring(BaseExpressionToFieldHandler.InitializationPlace.IN_CONSTRUCTOR, false);
checkResultByFile("afterConflictingConstructorParameter.java");
}
public void testOnClassLevelNoDuplicates() {
configureByFile("/refactoring/introduceField/beforeOnClassLevelNoDuplicates.java");
configureByFile("beforeOnClassLevelNoDuplicates.java");
performRefactoring(BaseExpressionToFieldHandler.InitializationPlace.IN_FIELD_DECLARATION, false);
checkResultByFile("/refactoring/introduceField/afterOnClassLevelNoDuplicates.java");
checkResultByFile("afterOnClassLevelNoDuplicates.java");
}
public void testOnClassLevelDuplicates() {
configureByFile("/refactoring/introduceField/beforeOnClassLevelDuplicates.java");
configureByFile("beforeOnClassLevelDuplicates.java");
performRefactoring(BaseExpressionToFieldHandler.InitializationPlace.IN_FIELD_DECLARATION, false);
checkResultByFile("/refactoring/introduceField/afterOnClassLevelDuplicates.java");
checkResultByFile("afterOnClassLevelDuplicates.java");
}
public void testOnClassLevelDuplicates1() {
configureByFile("/refactoring/introduceField/beforeOnClassLevelDuplicates1.java");
configureByFile("beforeOnClassLevelDuplicates1.java");
performRefactoring(BaseExpressionToFieldHandler.InitializationPlace.IN_FIELD_DECLARATION, false);
checkResultByFile("/refactoring/introduceField/afterOnClassLevelDuplicates1.java");
checkResultByFile("afterOnClassLevelDuplicates1.java");
}
public void testOnClassLevelBinary() {
configureByFile("/refactoring/introduceField/beforeOnClassLevelBinary.java");
configureByFile("beforeOnClassLevelBinary.java");
performRefactoring(BaseExpressionToFieldHandler.InitializationPlace.IN_FIELD_DECLARATION, false);
checkResultByFile("/refactoring/introduceField/afterOnClassLevelBinary.java");
checkResultByFile("afterOnClassLevelBinary.java");
}
//multiple error elements on class level corresponding to the extracted fragment ------------------
public void testOnClassLevelNewExpression() {
configureByFile("/refactoring/introduceField/beforeOnClassLevelNewExpression.java");
configureByFile("beforeOnClassLevelNewExpression.java");
performRefactoring(BaseExpressionToFieldHandler.InitializationPlace.IN_FIELD_DECLARATION, false);
checkResultByFile("/refactoring/introduceField/afterOnClassLevelNewExpression.java");
checkResultByFile("afterOnClassLevelNewExpression.java");
}
public void testOnClassLevelClassForName() {
configureByFile("/refactoring/introduceField/beforeOnClassLevelClassForName.java");
configureByFile("beforeOnClassLevelClassForName.java");
performRefactoring(BaseExpressionToFieldHandler.InitializationPlace.IN_FIELD_DECLARATION, false);
checkResultByFile("/refactoring/introduceField/afterOnClassLevelClassForName.java");
checkResultByFile("afterOnClassLevelClassForName.java");
}
//-------------------------------------------------------------------------------------------------
public void testUnresolvedReferenceToLocalVar() {
configureByFile("/refactoring/introduceField/beforeUnresolvedReferenceToLocalVar.java");
configureByFile("beforeUnresolvedReferenceToLocalVar.java");
performRefactoring(BaseExpressionToFieldHandler.InitializationPlace.IN_CURRENT_METHOD, false);
checkResultByFile("/refactoring/introduceField/afterUnresolvedReferenceToLocalVar.java");
checkResultByFile("afterUnresolvedReferenceToLocalVar.java");
}
public void testForcedFieldType() {
configureByFile("/refactoring/introduceField/beforeForcedFieldType.java");
configureByFile("beforeForcedFieldType.java");
new MockIntroduceFieldHandler(BaseExpressionToFieldHandler.InitializationPlace.IN_CURRENT_METHOD, false) {
@Override
protected PsiType getFieldType(PsiType type) {
return PsiType.INT;
}
}.invoke(getProject(), myEditor, myFile, null);
checkResultByFile("/refactoring/introduceField/afterForcedFieldType.java");
checkResultByFile("afterForcedFieldType.java");
}
public void testRejectIntroduceFieldFromExprInThisCall() {
configureByFile("/refactoring/introduceField/beforeRejectIntroduceFieldFromExprInThisCall.java");
configureByFile("beforeRejectIntroduceFieldFromExprInThisCall.java");
try {
performRefactoring(BaseExpressionToFieldHandler.InitializationPlace.IN_FIELD_DECLARATION, false);
fail("Should not proceed");
@@ -149,38 +155,38 @@ public class IntroduceFieldInSameClassTest extends LightCodeInsightTestCase {
}
public void testInConstructorEnclosingAnonymous() {
configureByFile("/refactoring/introduceField/beforeEnclosingAnonymous.java");
configureByFile("beforeEnclosingAnonymous.java");
performRefactoring(BaseExpressionToFieldHandler.InitializationPlace.IN_CONSTRUCTOR, false);
checkResultByFile("/refactoring/introduceField/afterEnclosingAnonymous.java");
checkResultByFile("afterEnclosingAnonymous.java");
}
public void testLocalVarAnnotations() {
configureByFile("/refactoring/introduceField/beforeLocalVarAnnotations.java");
configureByFile("beforeLocalVarAnnotations.java");
performRefactoring(BaseExpressionToFieldHandler.InitializationPlace.IN_FIELD_DECLARATION, false);
checkResultByFile("/refactoring/introduceField/afterLocalVarAnnotations.java");
checkResultByFile("afterLocalVarAnnotations.java");
}
public void testFromLambdaExpr() {
configureByFile("/refactoring/introduceField/beforeFromLambdaExpr.java");
configureByFile("beforeFromLambdaExpr.java");
performRefactoring(BaseExpressionToFieldHandler.InitializationPlace.IN_FIELD_DECLARATION, false);
checkResultByFile("/refactoring/introduceField/afterFromLambdaExpr.java");
checkResultByFile("afterFromLambdaExpr.java");
}
public void testSimplifyDiamond() {
configureByFile("/refactoring/introduceField/beforeSimplifiedDiamond.java");
configureByFile("beforeSimplifiedDiamond.java");
performRefactoring(BaseExpressionToFieldHandler.InitializationPlace.IN_FIELD_DECLARATION, false);
checkResultByFile("/refactoring/introduceField/afterSimplifiedDiamond.java");
checkResultByFile("afterSimplifiedDiamond.java");
}
public void testStaticFieldInInnerClass() {
configureByFile("/refactoring/introduceField/beforeStaticFieldInInnerClass.java");
configureByFile("beforeStaticFieldInInnerClass.java");
new MockIntroduceFieldHandler(BaseExpressionToFieldHandler.InitializationPlace.IN_CURRENT_METHOD, false) {
@Override
protected int getChosenClassIndex(List<PsiClass> classes) {
return 0;
}
}.invoke(getProject(), myEditor, myFile, null);
checkResultByFile("/refactoring/introduceField/afterStaticFieldInInnerClass.java");
checkResultByFile("afterStaticFieldInInnerClass.java");
}
private static void performRefactoring(BaseExpressionToFieldHandler.InitializationPlace initializationPlace, boolean declareStatic) {