mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
added quick fix for the PyAttributeOutsideInitInspection
This commit is contained in:
@@ -88,4 +88,5 @@ public abstract class PyElementGenerator {
|
||||
public abstract PsiFile createDummyFile(LanguageLevel langLevel, String contents);
|
||||
|
||||
public abstract PyExpressionStatement createDocstring(String content);
|
||||
public abstract PyPassStatement createPassStatement();
|
||||
}
|
||||
|
||||
@@ -102,6 +102,9 @@ QFIX.remove.decorator=Remove decorator
|
||||
#PyRenameUnresolvedRefQuickFix
|
||||
QFIX.rename.unresolved.reference=Rename reference
|
||||
|
||||
#PyMoveAttributeToInitQuickFix
|
||||
QFIX.move.attribute=Move attribute to __init__ method
|
||||
|
||||
# Intentions: INTN
|
||||
INTN.Family.convert.import.unqualify=Convert 'import module' to 'from module import'
|
||||
INTN.Family.convert.import.qualify=Convert 'from module import' to 'import module'
|
||||
@@ -432,8 +435,8 @@ INSP.NAME.augment.assignment=Assignment can be replaced with augmented assignmen
|
||||
INSP.NAME.chained.comparisons=Chained comparisons can be simplified
|
||||
|
||||
# PyAttributeOutsideInitInspection
|
||||
INSP.NAME.attribute.outside.init=Instance attribute defined outside _init_
|
||||
INSP.attribute.$0.outside.init=Instance attribute {0} defined outside _init_
|
||||
INSP.NAME.attribute.outside.init=Instance attribute defined outside __init__
|
||||
INSP.attribute.$0.outside.init=Instance attribute {0} defined outside __init__
|
||||
|
||||
# PyProtectedMemberInspection
|
||||
INSP.NAME.protected.member.access=Access to a protected member of a class
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.intellij.codeInspection.LocalInspectionToolSession;
|
||||
import com.intellij.codeInspection.ProblemsHolder;
|
||||
import com.intellij.psi.PsiElementVisitor;
|
||||
import com.jetbrains.python.PyBundle;
|
||||
import com.jetbrains.python.inspections.quickfix.PyMoveAttributeToInitQuickFix;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyFunction;
|
||||
import com.jetbrains.python.psi.PyTargetExpression;
|
||||
@@ -58,7 +59,8 @@ public class PyAttributeOutsideInitInspection extends PyInspection {
|
||||
|
||||
for (Map.Entry<String, PyTargetExpression> attribute : attributes.entrySet()) {
|
||||
if (!attributesInInit.containsKey(attribute.getKey())) {
|
||||
registerProblem(attribute.getValue(), PyBundle.message("INSP.attribute.$0.outside.init", attribute.getKey()));
|
||||
registerProblem(attribute.getValue(), PyBundle.message("INSP.attribute.$0.outside.init", attribute.getKey()),
|
||||
new PyMoveAttributeToInitQuickFix());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
package com.jetbrains.python.inspections.quickfix;
|
||||
|
||||
import com.intellij.codeInspection.LocalQuickFix;
|
||||
import com.intellij.codeInspection.ProblemDescriptor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.jetbrains.python.PyBundle;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* User: ktisha
|
||||
*/
|
||||
public class PyMoveAttributeToInitQuickFix implements LocalQuickFix {
|
||||
|
||||
public PyMoveAttributeToInitQuickFix() {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getName() {
|
||||
return PyBundle.message("QFIX.move.attribute");
|
||||
}
|
||||
|
||||
@NonNls
|
||||
@NotNull
|
||||
public String getFamilyName() {
|
||||
return getName();
|
||||
}
|
||||
|
||||
public void applyFix(@NotNull final Project project, @NotNull final ProblemDescriptor descriptor) {
|
||||
final PsiElement element = descriptor.getPsiElement();
|
||||
if (!(element instanceof PyTargetExpression)) return;
|
||||
final PyTargetExpression targetExpression = (PyTargetExpression)element;
|
||||
|
||||
final PyClass containingClass = targetExpression.getContainingClass();
|
||||
final PyAssignmentStatement assignment = PsiTreeUtil.getParentOfType(element, PyAssignmentStatement.class);
|
||||
if (containingClass == null || assignment == null) return;
|
||||
|
||||
final PsiElement copy = assignment.copy();
|
||||
if (!addDefinition(copy, containingClass)) return;
|
||||
|
||||
removeDefinition(assignment);
|
||||
}
|
||||
|
||||
private static boolean addDefinition(PsiElement copy, PyClass containingClass) {
|
||||
PyFunction init = containingClass.findInitOrNew(false);
|
||||
|
||||
if (init == null) {
|
||||
final PyStatementList classStatementList = containingClass.getStatementList();
|
||||
final PyStatement[] statements = classStatementList.getStatements();
|
||||
init = PyElementGenerator.getInstance(containingClass.getProject()).createFromText(LanguageLevel.forElement(containingClass),
|
||||
PyFunction.class,
|
||||
"def __init__(self):\n\t" +
|
||||
copy.getText());
|
||||
if (statements.length > 0) {
|
||||
final PyStatement statement = statements[0];
|
||||
if (statement instanceof PyExpressionStatement &&
|
||||
((PyExpressionStatement)statement).getExpression() == containingClass.getDocStringExpression())
|
||||
classStatementList.addAfter(init, statement);
|
||||
else
|
||||
classStatementList.addBefore(init, statement);
|
||||
}
|
||||
else {
|
||||
classStatementList.add(init);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
final PyStatementList statementList = init.getStatementList();
|
||||
if (statementList == null) return false;
|
||||
|
||||
final PyStatement[] statements = statementList.getStatements();
|
||||
if (statements.length == 1) {
|
||||
final PyStatement firstStatement = statements[0];
|
||||
if (firstStatement instanceof PyPassStatement) {
|
||||
firstStatement.replace(copy);
|
||||
}
|
||||
else
|
||||
statementList.addAfter(copy, statements[statements.length - 1]);
|
||||
}
|
||||
else
|
||||
statementList.addAfter(copy, statements[statements.length - 1]);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean removeDefinition(PyAssignmentStatement assignment) {
|
||||
final PyStatementList statementList = PsiTreeUtil.getParentOfType(assignment, PyStatementList.class);
|
||||
if (statementList == null) return false;
|
||||
|
||||
if (statementList.getStatements().length == 1) {
|
||||
final PyPassStatement passStatement = PyElementGenerator.getInstance(assignment.getProject()).createPassStatement();
|
||||
statementList.addBefore(passStatement, assignment);
|
||||
}
|
||||
assignment.delete();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -304,6 +304,17 @@ public class PyElementGeneratorImpl extends PyElementGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PyPassStatement createPassStatement() {
|
||||
final PyStatementList statementList = createPassStatementList();
|
||||
return (PyPassStatement)statementList.getStatements()[0];
|
||||
}
|
||||
|
||||
private PyStatementList createPassStatementList() {
|
||||
final PyFunction function = createFromText(LanguageLevel.getDefault(), PyFunction.class, "def foo():\n\tpass");
|
||||
return function.getStatementList();
|
||||
}
|
||||
|
||||
public PyExpressionStatement createDocstring(String content) {
|
||||
return createFromText(LanguageLevel.getDefault(),
|
||||
PyExpressionStatement.class, content + "\n");
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
__author__ = 'ktisha'
|
||||
|
||||
|
||||
class A:
|
||||
def __init__(self):
|
||||
self._a = 1
|
||||
|
||||
def foo(self):
|
||||
self.<caret>b = 1
|
||||
@@ -0,0 +1,10 @@
|
||||
__author__ = 'ktisha'
|
||||
|
||||
|
||||
class A:
|
||||
def __init__(self):
|
||||
self._a = 1
|
||||
self.b = 1
|
||||
|
||||
def foo(self):
|
||||
pass
|
||||
@@ -0,0 +1,7 @@
|
||||
__author__ = 'ktisha'
|
||||
|
||||
|
||||
class A:
|
||||
def foo(self):
|
||||
self.<caret>b = 1
|
||||
c = 1
|
||||
@@ -0,0 +1,9 @@
|
||||
__author__ = 'ktisha'
|
||||
|
||||
|
||||
class A:
|
||||
def __init__(self):
|
||||
self.b = 1
|
||||
|
||||
def foo(self):
|
||||
c = 1
|
||||
@@ -0,0 +1,10 @@
|
||||
__author__ = 'ktisha'
|
||||
|
||||
|
||||
class A:
|
||||
def __init__(self):
|
||||
self._a = 1
|
||||
|
||||
def foo(self):
|
||||
self.<caret>b = 1
|
||||
c = 1
|
||||
@@ -0,0 +1,10 @@
|
||||
__author__ = 'ktisha'
|
||||
|
||||
|
||||
class A:
|
||||
def __init__(self):
|
||||
self._a = 1
|
||||
self.b = 1
|
||||
|
||||
def foo(self):
|
||||
c = 1
|
||||
@@ -0,0 +1,9 @@
|
||||
__author__ = 'ktisha'
|
||||
|
||||
|
||||
class A:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def foo(self):
|
||||
self.<caret>b = 1
|
||||
@@ -0,0 +1,9 @@
|
||||
__author__ = 'ktisha'
|
||||
|
||||
|
||||
class A:
|
||||
def __init__(self):
|
||||
self.b = 1
|
||||
|
||||
def foo(self):
|
||||
pass
|
||||
@@ -0,0 +1,10 @@
|
||||
__author__ = 'ktisha'
|
||||
|
||||
|
||||
class A:
|
||||
"""
|
||||
class docstring
|
||||
"""
|
||||
|
||||
def foo(self):
|
||||
self.<caret>b = 1
|
||||
@@ -0,0 +1,13 @@
|
||||
__author__ = 'ktisha'
|
||||
|
||||
|
||||
class A:
|
||||
"""
|
||||
class docstring
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.b = 1
|
||||
|
||||
def foo(self):
|
||||
pass
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.jetbrains.python.quickFixes;
|
||||
|
||||
import com.intellij.codeInsight.intention.IntentionAction;
|
||||
import com.intellij.testFramework.TestDataPath;
|
||||
import com.jetbrains.python.PyBundle;
|
||||
import com.jetbrains.python.PythonTestUtil;
|
||||
import com.jetbrains.python.fixtures.PyTestCase;
|
||||
import com.jetbrains.python.inspections.PyAttributeOutsideInitInspection;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
|
||||
/**
|
||||
* User: ktisha
|
||||
*/
|
||||
@TestDataPath("$CONTENT_ROOT/../testData/quickFixes/PyMoveAttributeToInitQuickFixTest")
|
||||
public class PyMoveAttributeToInitQuickFixTest extends PyTestCase {
|
||||
|
||||
public void testMoveToInit() {
|
||||
doInspectionTest(PyAttributeOutsideInitInspection.class, true);
|
||||
}
|
||||
|
||||
public void testCreateInit() {
|
||||
doInspectionTest(PyAttributeOutsideInitInspection.class, true);
|
||||
}
|
||||
|
||||
public void testAddPass() {
|
||||
doInspectionTest(PyAttributeOutsideInitInspection.class, true);
|
||||
}
|
||||
|
||||
public void testRemovePass() {
|
||||
doInspectionTest(PyAttributeOutsideInitInspection.class, true);
|
||||
}
|
||||
|
||||
public void testSkipDocstring() {
|
||||
doInspectionTest(PyAttributeOutsideInitInspection.class, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNls
|
||||
protected String getTestDataPath() {
|
||||
return PythonTestUtil.getTestDataPath() + "/quickFixes/PyMoveAttributeToInitQuickFixTest";
|
||||
}
|
||||
|
||||
protected void doInspectionTest(final Class inspectionClass,
|
||||
boolean applyFix) {
|
||||
final String testFileName = getTestName(true);
|
||||
myFixture.enableInspections(inspectionClass);
|
||||
myFixture.configureByFile(testFileName + ".py");
|
||||
myFixture.checkHighlighting(true, false, false);
|
||||
final IntentionAction intentionAction = myFixture.findSingleIntention(PyBundle.message("QFIX.move.attribute"));
|
||||
assertNotNull(intentionAction);
|
||||
if (applyFix) {
|
||||
myFixture.launchAction(intentionAction);
|
||||
myFixture.checkResultByFile(testFileName + "_after.py", true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user