fixed PY-9253 Broken "add field to class" adds the field to the instance instead.

moved tests to the separate testCase
This commit is contained in:
Ekaterina Tuzova
2013-03-22 18:13:13 +04:00
parent 00eafcb011
commit 9750faf20d
20 changed files with 84 additions and 44 deletions

View File

@@ -700,7 +700,7 @@ public class PyUnresolvedReferencesInspection extends PyInspection {
actions.add(new AddMethodQuickFix(refText, (PyClassType)qtype, true));
}
else if (!(reference instanceof PyOperatorReference)) {
actions.add(new AddFieldQuickFix(refText, cls, "None"));
actions.add(new AddFieldQuickFix(refText, (PyClassType)qtype, "None"));
}
}
}

View File

@@ -27,6 +27,7 @@ import com.jetbrains.python.psi.impl.PyImportStatementNavigator;
import com.jetbrains.python.psi.resolve.PyResolveContext;
import com.jetbrains.python.psi.search.PyOverridingMethodsSearch;
import com.jetbrains.python.psi.search.PySuperMethodsSearch;
import com.jetbrains.python.psi.types.PyClassTypeImpl;
import org.jetbrains.annotations.NotNull;
import java.util.*;
@@ -280,7 +281,7 @@ public class PyUnusedLocalInspectionVisitor extends PyInspectionVisitor {
}
}
final LocalQuickFix[] fixes = mayBeField
? new LocalQuickFix[] { new AddFieldQuickFix(name, containingClass, name) }
? new LocalQuickFix[] { new AddFieldQuickFix(name, new PyClassTypeImpl(containingClass, false), name) }
: LocalQuickFix.EMPTY_ARRAY;
registerWarning(element, PyBundle.message("INSP.unused.locals.parameter.isnot.used", name), fixes);
}

View File

@@ -14,6 +14,7 @@ import com.jetbrains.python.PyNames;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.PyBuiltinCache;
import com.jetbrains.python.psi.impl.PyPsiUtils;
import com.jetbrains.python.psi.types.PyClassType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -24,19 +25,19 @@ import org.jetbrains.annotations.Nullable;
*/
public class AddFieldQuickFix implements LocalQuickFix {
private PyClass myQualifierClass;
private PyClassType myQualifierType;
private final String myInitializer;
private String myIdentifier;
public AddFieldQuickFix(String identifier, PyClass qualifierClass, String initializer) {
public AddFieldQuickFix(String identifier, PyClassType qualifierType, String initializer) {
myIdentifier = identifier;
myQualifierClass = qualifierClass;
myQualifierType = qualifierType;
myInitializer = initializer;
}
@NotNull
public String getName() {
return PyBundle.message("QFIX.NAME.add.field.$0.to.class.$1", myIdentifier, myQualifierClass.getName());
return PyBundle.message("QFIX.NAME.add.field.$0.to.class.$1", myIdentifier, myQualifierType.getName());
}
@NotNull
@@ -66,14 +67,19 @@ public class AddFieldQuickFix implements LocalQuickFix {
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
// expect the descriptor to point to the unresolved identifier.
PyClass cls = myQualifierClass;
String item_name = myIdentifier;
if (cls != null) {
PsiElement initStatement = addFieldToInit(project, cls, item_name, new CreateFieldCallback(project, item_name, myInitializer));
if (initStatement != null) {
showTemplateBuilder(initStatement);
return;
}
PyClass cls = myQualifierType.getPyClass();
PsiElement initStatement;
if (!myQualifierType.isDefinition()) {
initStatement = addFieldToInit(project, cls, myIdentifier, new CreateFieldCallback(project, myIdentifier, myInitializer));
}
else {
PyStatement field = PyElementGenerator.getInstance(project)
.createFromText(LanguageLevel.getDefault(), PyStatement.class, myIdentifier + " = " + myInitializer);
initStatement = PyUtil.addElementToStatementList(field, cls.getStatementList(), true);
}
if (initStatement != null) {
showTemplateBuilder(initStatement);
return;
}
// somehow we failed. tell about this
PyUtil.showBalloon(project, PyBundle.message("QFIX.failed.to.add.field"), MessageType.ERROR);
@@ -83,8 +89,11 @@ public class AddFieldQuickFix implements LocalQuickFix {
initStatement = CodeInsightUtilBase.forcePsiPostprocessAndRestoreElement(initStatement);
if (initStatement instanceof PyAssignmentStatement) {
final TemplateBuilder builder = TemplateBuilderFactory.getInstance().createTemplateBuilder(initStatement);
builder.replaceElement(((PyAssignmentStatement) initStatement).getAssignedValue(), myInitializer);
builder.run();
final PyExpression assignedValue = ((PyAssignmentStatement)initStatement).getAssignedValue();
if (assignedValue != null) {
builder.replaceElement(assignedValue, myInitializer);
builder.run();
}
}
}

View File

@@ -113,7 +113,7 @@ public class AddMethodQuickFix implements LocalQuickFix {
meth.addBefore(deco_list, meth.getFirstChild()); // in the very beginning
}
meth = (PyFunction)PyUtil.addElementToStatementList(meth, cls_stmt_list);
meth = (PyFunction)PyUtil.addElementToStatementList(meth, cls_stmt_list, PyNames.INIT.equals(meth.getName()));
if (myReplaceUsage)
showTemplateBuilder(meth);
}

View File

@@ -54,13 +54,13 @@ public class PyMoveAttributeToInitQuickFix implements LocalQuickFix {
PyFunction.class,
"def __init__(self):\n\t" +
copy.getText());
PyUtil.addElementToStatementList(init, classStatementList);
PyUtil.addElementToStatementList(init, classStatementList, true);
return true;
}
final PyStatementList statementList = init.getStatementList();
if (statementList == null) return false;
PyUtil.addElementToStatementList(copy, statementList);
PyUtil.addElementToStatementList(copy, statementList, true);
return true;
}

View File

@@ -1203,15 +1203,16 @@ public class PyUtil {
}
public static PsiElement addElementToStatementList(@NotNull PsiElement element, @NotNull PyStatementList statementList) {
public static PsiElement addElementToStatementList(@NotNull PsiElement element,
@NotNull PyStatementList statementList,
boolean toTheBeginning) {
final PsiElement firstChild = statementList.getFirstChild();
if (firstChild == statementList.getLastChild() && firstChild instanceof PyPassStatement) {
element = firstChild.replace(element);
}
else {
final PyStatement[] statements = statementList.getStatements();
String name = element instanceof PsiNamedElement ? ((PsiNamedElement)element).getName() : "";
if (PyNames.INIT.equals(name) && statements.length > 0) {
if (toTheBeginning && statements.length > 0) {
final PyDocStringOwner docStringOwner = PsiTreeUtil.getParentOfType(statementList, PyDocStringOwner.class);
final PyStatement firstStatement = statements[0];
if (docStringOwner != null && firstStatement instanceof PyExpressionStatement &&

View File

@@ -0,0 +1,8 @@
__author__ = 'ktisha'
class A(object):
def method(self):
var = A.<warning descr="Unresolved attribute reference 'FIELD' for class 'A'"><caret>FIELD</warning>
var = self.<warning descr="Unresolved attribute reference 'test' for class 'A'">test</warning>

View File

@@ -0,0 +1,10 @@
__author__ = 'ktisha'
class A(object):
FIELD = None
def method(self):
var = A.FIELD
var = self.test

View File

@@ -3,8 +3,8 @@ __author__ = 'ktisha'
class A:
def __init__(self):
self._a = 1
self.b = 1
self._a = 1
def foo(self):
pass

View File

@@ -3,8 +3,8 @@ __author__ = 'ktisha'
class A:
def __init__(self):
self._a = 1
self.b = 1
self._a = 1
def foo(self):
c = 1

View File

@@ -95,26 +95,6 @@ public class PyQuickFixTest extends PyTestCase {
true);
}
public void testAddFieldFromMethod() {
doInspectionTest("AddFieldFromMethod.py", PyUnresolvedReferencesInspection.class, PyBundle.message("QFIX.NAME.add.field.$0.to.class.$1", "y", "A"),
true, true);
}
public void testAddFieldFromInstance() {
doInspectionTest("AddFieldFromInstance.py", PyUnresolvedReferencesInspection.class, PyBundle.message("QFIX.NAME.add.field.$0.to.class.$1", "y", "A"),
true, true);
}
public void testAddFieldAddConstructor() {
doInspectionTest("AddFieldAddConstructor.py", PyUnresolvedReferencesInspection.class, PyBundle.message("QFIX.NAME.add.field.$0.to.class.$1", "x", "B"),
true, true);
}
public void testAddFieldNewConstructor() {
doInspectionTest("AddFieldNewConstructor.py", PyUnresolvedReferencesInspection.class, PyBundle.message("QFIX.NAME.add.field.$0.to.class.$1", "x", "B"),
true, true);
}
public void testRemoveTrailingSemicolon() {
doInspectionTest("RemoveTrailingSemicolon.py", PyTrailingSemicolonInspection.class, PyBundle.message("QFIX.remove.trailing.semicolon"),
true, true);

View File

@@ -0,0 +1,31 @@
package com.jetbrains.python.quickFixes;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.inspections.PyUnresolvedReferencesInspection;
/**
* User: ktisha
*/
public class AddFieldQuickFixTest extends PyQuickFixTestCase {
public void testAddClassField() {
doInspectionTest(PyUnresolvedReferencesInspection.class, PyBundle.message("QFIX.NAME.add.field.$0.to.class.$1", "FIELD", "A"));
}
public void testAddFieldFromMethod() {
doInspectionTest(PyUnresolvedReferencesInspection.class, PyBundle.message("QFIX.NAME.add.field.$0.to.class.$1", "y", "A"));
}
public void testAddFieldFromInstance() {
doInspectionTest(PyUnresolvedReferencesInspection.class, PyBundle.message("QFIX.NAME.add.field.$0.to.class.$1", "y", "A"));
}
public void testAddFieldAddConstructor() {
doInspectionTest(PyUnresolvedReferencesInspection.class, PyBundle.message("QFIX.NAME.add.field.$0.to.class.$1", "x", "B"));
}
public void testAddFieldNewConstructor() {
doInspectionTest(PyUnresolvedReferencesInspection.class, PyBundle.message("QFIX.NAME.add.field.$0.to.class.$1", "x", "B"));
}
}