diff --git a/python/psi-api/src/com/jetbrains/python/psi/PyElementGenerator.java b/python/psi-api/src/com/jetbrains/python/psi/PyElementGenerator.java index 8304e3e691aa..b2647c45c3e3 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/PyElementGenerator.java +++ b/python/psi-api/src/com/jetbrains/python/psi/PyElementGenerator.java @@ -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(); } diff --git a/python/src/META-INF/python-plugin-common.xml b/python/src/META-INF/python-plugin-common.xml index 57b8bf8b71c5..2eb8d1ce0151 100644 --- a/python/src/META-INF/python-plugin-common.xml +++ b/python/src/META-INF/python-plugin-common.xml @@ -316,6 +316,11 @@ + + + + + diff --git a/python/src/com/jetbrains/python/PyBundle.properties b/python/src/com/jetbrains/python/PyBundle.properties index 60fda20373fa..c224e40e0673 100644 --- a/python/src/com/jetbrains/python/PyBundle.properties +++ b/python/src/com/jetbrains/python/PyBundle.properties @@ -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' @@ -431,6 +434,14 @@ INSP.NAME.augment.assignment=Assignment can be replaced with augmented assignmen # PyChainedComparsonsInspection 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__ + +# PyProtectedMemberInspection +INSP.NAME.protected.member.access=Access to a protected member of a class +INSP.protected.member.$0.access=Access to a protected member {0} of a class + # PyArgumentEqualDefaultInspection INSP.NAME.argument.equal.default=Argument passed to function is equal to default parameter value INSP.argument.equals.to.default=Argument equals to default parameter value @@ -460,6 +471,18 @@ INSP.NAME.decorator.outside.class=Class specific decorator on method outside cla # PyPackageRequirementsInspection INSP.NAME.requirements=Package requirements +# PyClassHasNoInitInspection +INSP.NAME.class.has.no.init=Class has no __init__ method +INSP.class.has.no.init=Class has no __init__ method +INSP.parent.$0.has.no.init=Parent ''{0}'' has no __init__ method + +#PyNoneFunctionAssignmentInspection +INSP.NAME.none.function.assignment=Assigning function call that doesn't return anything (None) +INSP.none.function.assignment=Function ''{0}'' doesn''t return anything + +#PyGlobalUndefinedInspection +INSP.NAME.global.undefined=Global variable is undefined at the module level +INSP.NAME.global.$0.undefined=Global variable ''{0}'' is undefined at the module level # Refactoring # introduce diff --git a/python/src/com/jetbrains/python/inspections/PyAttributeOutsideInitInspection.java b/python/src/com/jetbrains/python/inspections/PyAttributeOutsideInitInspection.java new file mode 100644 index 000000000000..b6add93f047c --- /dev/null +++ b/python/src/com/jetbrains/python/inspections/PyAttributeOutsideInitInspection.java @@ -0,0 +1,70 @@ +package com.jetbrains.python.inspections; + +import com.intellij.codeInspection.LocalInspectionToolSession; +import com.intellij.codeInspection.ProblemsHolder; +import com.intellij.psi.PsiElementVisitor; +import com.jetbrains.python.PyBundle; +import com.jetbrains.python.PyNames; +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; +import com.jetbrains.python.psi.impl.PyClassImpl; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; +import java.util.Map; + +/** + * User: ktisha + * + * Inspection to detect situations, where instance attribute + * defined outside __init__ function + */ +public class PyAttributeOutsideInitInspection extends PyInspection { + @Nls + @NotNull + @Override + public String getDisplayName() { + return PyBundle.message("INSP.NAME.attribute.outside.init"); + } + + @NotNull + @Override + public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, + boolean isOnTheFly, + @NotNull LocalInspectionToolSession session) { + return new Visitor(holder, session); + } + + + private static class Visitor extends PyInspectionVisitor { + public Visitor(@Nullable ProblemsHolder holder, @NotNull LocalInspectionToolSession session) { + super(holder, session); + } + + @Override + public void visitPyFunction(PyFunction node) { + final PyClass containingClass = node.getContainingClass(); + if (containingClass == null) return; + + Map attributesInInit = new HashMap(); + final PyFunction initMethod = containingClass.findMethodByName(PyNames.INIT, false); + if (initMethod != null) + PyClassImpl.collectInstanceAttributes(initMethod, attributesInInit); + + Map attributes = new HashMap(); + PyClassImpl.collectInstanceAttributes(node, attributes); + + for (Map.Entry attribute : attributes.entrySet()) { + if (!attributesInInit.containsKey(attribute.getKey())) { + registerProblem(attribute.getValue(), PyBundle.message("INSP.attribute.$0.outside.init", attribute.getKey()), + new PyMoveAttributeToInitQuickFix()); + } + } + } + + } +} diff --git a/python/src/com/jetbrains/python/inspections/PyClassHasNoInitInspection.java b/python/src/com/jetbrains/python/inspections/PyClassHasNoInitInspection.java new file mode 100644 index 000000000000..89555fc3ce09 --- /dev/null +++ b/python/src/com/jetbrains/python/inspections/PyClassHasNoInitInspection.java @@ -0,0 +1,57 @@ +package com.jetbrains.python.inspections; + +import com.intellij.codeInspection.LocalInspectionToolSession; +import com.intellij.codeInspection.ProblemsHolder; +import com.intellij.psi.PsiElementVisitor; +import com.jetbrains.python.PyBundle; +import com.jetbrains.python.PyNames; +import com.jetbrains.python.inspections.quickfix.AddMethodQuickFix; +import com.jetbrains.python.psi.PyClass; +import com.jetbrains.python.psi.PyFunction; +import com.jetbrains.python.psi.types.PyClassTypeImpl; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * User: ktisha + * See pylint W0232 + */ +public class PyClassHasNoInitInspection extends PyInspection { + @Nls + @NotNull + @Override + public String getDisplayName() { + return PyBundle.message("INSP.NAME.class.has.no.init"); + } + + @NotNull + @Override + public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, + boolean isOnTheFly, + @NotNull LocalInspectionToolSession session) { + return new Visitor(holder, session); + } + + private static class Visitor extends PyInspectionVisitor { + public Visitor(@Nullable ProblemsHolder holder, @NotNull LocalInspectionToolSession session) { + super(holder, session); + } + + @Override + public void visitPyClass(PyClass node) { + final PyFunction init = node.findMethodByName(PyNames.INIT, false); + if (init == null) { + registerProblem(node.getNameIdentifier(), PyBundle.message("INSP.class.has.no.init"), + new AddMethodQuickFix("__init__", new PyClassTypeImpl(node, false), false)); + } + for (PyClass ancestor : node.iterateAncestorClasses()) { + final PyFunction ancestorInit = ancestor.findMethodByName(PyNames.INIT, false); + if (ancestorInit == null) { + registerProblem(node.getNameIdentifier(), PyBundle.message("INSP.parent.$0.has.no.init", ancestor.getName()), + new AddMethodQuickFix("__init__", new PyClassTypeImpl(ancestor, false), false)); + } + } + } + } +} \ No newline at end of file diff --git a/python/src/com/jetbrains/python/inspections/PyGlobalUndefinedInspection.java b/python/src/com/jetbrains/python/inspections/PyGlobalUndefinedInspection.java new file mode 100644 index 000000000000..132c55ddf7a7 --- /dev/null +++ b/python/src/com/jetbrains/python/inspections/PyGlobalUndefinedInspection.java @@ -0,0 +1,51 @@ +package com.jetbrains.python.inspections; + +import com.intellij.codeInspection.LocalInspectionToolSession; +import com.intellij.codeInspection.ProblemsHolder; +import com.intellij.psi.PsiElementVisitor; +import com.jetbrains.python.PyBundle; +import com.jetbrains.python.psi.PyGlobalStatement; +import com.jetbrains.python.psi.PyTargetExpression; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * User: ktisha + * + * pylint W0601 + */ +public class PyGlobalUndefinedInspection extends PyInspection { + @Nls + @NotNull + @Override + public String getDisplayName() { + return PyBundle.message("INSP.NAME.global.undefined"); + } + + @NotNull + @Override + public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, + boolean isOnTheFly, + @NotNull LocalInspectionToolSession session) { + return new Visitor(holder, session); + } + + + private static class Visitor extends PyInspectionVisitor { + public Visitor(@Nullable ProblemsHolder holder, @NotNull LocalInspectionToolSession session) { + super(holder, session); + } + + @Override + public void visitPyGlobalStatement(PyGlobalStatement node) { + final PyTargetExpression[] globals = node.getGlobals(); + + for (PyTargetExpression global : globals) { + if (global.getReference().resolve() == global) { + registerProblem(global, PyBundle.message("INSP.NAME.global.$0.undefined", global.getName())); + } + } + } + } +} diff --git a/python/src/com/jetbrains/python/inspections/PyNoneFunctionAssignmentInspection.java b/python/src/com/jetbrains/python/inspections/PyNoneFunctionAssignmentInspection.java new file mode 100644 index 000000000000..a2840609827c --- /dev/null +++ b/python/src/com/jetbrains/python/inspections/PyNoneFunctionAssignmentInspection.java @@ -0,0 +1,57 @@ +package com.jetbrains.python.inspections; + +import com.intellij.codeInspection.LocalInspectionToolSession; +import com.intellij.codeInspection.ProblemsHolder; +import com.intellij.psi.PsiElementVisitor; +import com.jetbrains.python.PyBundle; +import com.jetbrains.python.psi.PyAssignmentStatement; +import com.jetbrains.python.psi.PyCallExpression; +import com.jetbrains.python.psi.PyExpression; +import com.jetbrains.python.psi.types.PyNoneType; +import com.jetbrains.python.psi.types.PyType; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * User: ktisha + * + * pylint E1111 + * + * Used when an assignment is done on a function call but the inferred function doesn't return anything. + */ +public class PyNoneFunctionAssignmentInspection extends PyInspection { + @Nls + @NotNull + @Override + public String getDisplayName() { + return PyBundle.message("INSP.NAME.none.function.assignment"); + } + + @NotNull + @Override + public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, + boolean isOnTheFly, + @NotNull LocalInspectionToolSession session) { + return new Visitor(holder, session); + } + + + private static class Visitor extends PyInspectionVisitor { + public Visitor(@Nullable ProblemsHolder holder, @NotNull LocalInspectionToolSession session) { + super(holder, session); + } + + @Override + public void visitPyAssignmentStatement(PyAssignmentStatement node) { + final PyExpression value = node.getAssignedValue(); + if (value instanceof PyCallExpression) { + final PyType type = myTypeEvalContext.getType(value); + final PyExpression callee = ((PyCallExpression)value).getCallee(); + if (type instanceof PyNoneType && callee != null) { + registerProblem(node, PyBundle.message("INSP.none.function.assignment", callee.getName())); + } + } + } + } +} diff --git a/python/src/com/jetbrains/python/inspections/PyProtectedMemberInspection.java b/python/src/com/jetbrains/python/inspections/PyProtectedMemberInspection.java new file mode 100644 index 000000000000..f8cbe3778b22 --- /dev/null +++ b/python/src/com/jetbrains/python/inspections/PyProtectedMemberInspection.java @@ -0,0 +1,65 @@ +package com.jetbrains.python.inspections; + +import com.intellij.codeInspection.LocalInspectionToolSession; +import com.intellij.codeInspection.ProblemsHolder; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiElementVisitor; +import com.intellij.psi.PsiReference; +import com.intellij.psi.util.PsiTreeUtil; +import com.jetbrains.python.PyBundle; +import com.jetbrains.python.psi.PyClass; +import com.jetbrains.python.psi.PyExpression; +import com.jetbrains.python.psi.PyReferenceExpression; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * User: ktisha + * + * Inspection to detect situations, where + * protected member (i.e. class member with a name beginning with an underscore) + * is access outside the class or a descendant of the class where it's defined. + */ +public class PyProtectedMemberInspection extends PyInspection { + @Nls + @NotNull + @Override + public String getDisplayName() { + return PyBundle.message("INSP.NAME.protected.member.access"); + } + + @NotNull + @Override + public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, + boolean isOnTheFly, + @NotNull LocalInspectionToolSession session) { + return new Visitor(holder, session); + } + + + private static class Visitor extends PyInspectionVisitor { + public Visitor(@Nullable ProblemsHolder holder, @NotNull LocalInspectionToolSession session) { + super(holder, session); + } + + @Override + public void visitPyReferenceExpression(PyReferenceExpression node) { + final PyExpression qualifier = node.getQualifier(); + if (qualifier == null) return; + final String name = node.getName(); + if (name != null && name.startsWith("_")) { + final PyClass parentClass = PsiTreeUtil.getParentOfType(node, PyClass.class); + if (parentClass != null) { + final PsiReference reference = node.getReference(); + final PsiElement resolvedExpression = reference.resolve(); + final PyClass resolvedClass = PsiTreeUtil.getParentOfType(resolvedExpression, PyClass.class); + if (parentClass.isSubclass(resolvedClass)) + return; + } + registerProblem(node, PyBundle.message("INSP.protected.member.$0.access", name)); + } + } + + } +} diff --git a/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java b/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java index e9e2b2a42e3c..da29e94bd1f3 100644 --- a/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyUnresolvedReferencesInspection.java @@ -684,7 +684,7 @@ public class PyUnresolvedReferencesInspection extends PyInspection { return true; } final String docString = cls.getDocStringValue(); - if (docString != null && docString.indexOf("@DynamicAttrs") != -1) { + if (docString != null && docString.contains("@DynamicAttrs")) { return true; } } @@ -697,7 +697,7 @@ public class PyUnresolvedReferencesInspection extends PyInspection { PyClass cls = ((PyClassType)qtype).getPyClass(); if (!PyBuiltinCache.getInstance(element).hasInBuiltins(cls)) { if (element.getParent() instanceof PyCallExpression) { - actions.add(new AddMethodQuickFix(refText, (PyClassType)qtype)); + actions.add(new AddMethodQuickFix(refText, (PyClassType)qtype, true)); } else if (!(reference instanceof PyOperatorReference)) { actions.add(new AddFieldQuickFix(refText, cls, "None")); diff --git a/python/src/com/jetbrains/python/inspections/quickfix/AddMethodQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/AddMethodQuickFix.java index c855bd32c94a..0b4e34859e66 100644 --- a/python/src/com/jetbrains/python/inspections/quickfix/AddMethodQuickFix.java +++ b/python/src/com/jetbrains/python/inspections/quickfix/AddMethodQuickFix.java @@ -29,11 +29,14 @@ import static com.jetbrains.python.psi.PyUtil.sure; public class AddMethodQuickFix implements LocalQuickFix { private PyClassType myQualifierType; + private final boolean myReplaceUsage; private String myIdentifier; - public AddMethodQuickFix(String identifier, PyClassType qualifierType) { + public AddMethodQuickFix(String identifier, PyClassType qualifierType, + boolean replaceUsage) { myIdentifier = identifier; myQualifierType = qualifierType; + myReplaceUsage = replaceUsage; } @NotNull @@ -62,10 +65,12 @@ public class AddMethodQuickFix implements LocalQuickFix { PyFunctionBuilder builder = new PyFunctionBuilder(item_name); PsiElement pe = problem_elt.getParent(); String deco_name = null; // set to non-null to add a decorator - sure(pe instanceof PyCallExpression); - PyArgumentList arglist = ((PyCallExpression)pe).getArgumentList(); - sure(arglist); - final PyExpression[] args = arglist.getArguments(); + PyExpression[] args = new PyExpression[0]; + if (pe instanceof PyCallExpression) { + PyArgumentList arglist = ((PyCallExpression)pe).getArgumentList(); + sure(arglist); + args = arglist.getArguments(); + } boolean made_instance = false; if (call_by_class) { if (args.length > 0) { @@ -107,17 +112,10 @@ public class AddMethodQuickFix implements LocalQuickFix { PyDecoratorList deco_list = generator.createFromText(LanguageLevel.getDefault(), PyDecoratorList.class, "@" + deco_name + "\ndef foo(): pass", new int[]{0, 0}); meth.addBefore(deco_list, meth.getFirstChild()); // in the very beginning } - - final PsiElement first_stmt = cls_stmt_list.getFirstChild(); - if (first_stmt == cls_stmt_list.getLastChild() && first_stmt instanceof PyPassStatement) { - // replace the lone 'pass' - meth = (PyFunction) first_stmt.replace(meth); - } - else { - // add ourselves to the bottom - meth = (PyFunction) cls_stmt_list.add(meth); - } - showTemplateBuilder(meth); + + meth = (PyFunction)PyUtil.addElementToStatementList(meth, cls_stmt_list); + if (myReplaceUsage) + showTemplateBuilder(meth); } catch (IncorrectOperationException ignored) { // we failed. tell about this @@ -138,7 +136,9 @@ public class AddMethodQuickFix implements LocalQuickFix { } ); - builder.replaceElement(method.getStatementList(), PyNames.PASS); + final PyStatementList statementList = method.getStatementList(); + if (statementList == null) return; + builder.replaceElement(statementList, PyNames.PASS); builder.run(); } diff --git a/python/src/com/jetbrains/python/inspections/quickfix/PyMoveAttributeToInitQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/PyMoveAttributeToInitQuickFix.java new file mode 100644 index 000000000000..c18e697b7e09 --- /dev/null +++ b/python/src/com/jetbrains/python/inspections/quickfix/PyMoveAttributeToInitQuickFix.java @@ -0,0 +1,78 @@ +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.PyNames; +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.findMethodByName(PyNames.INIT, false); + + if (init == null) { + final PyStatementList classStatementList = containingClass.getStatementList(); + init = PyElementGenerator.getInstance(containingClass.getProject()).createFromText(LanguageLevel.forElement(containingClass), + PyFunction.class, + "def __init__(self):\n\t" + + copy.getText()); + PyUtil.addElementToStatementList(init, classStatementList); + return true; + } + + final PyStatementList statementList = init.getStatementList(); + if (statementList == null) return false; + PyUtil.addElementToStatementList(copy, statementList); + 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; + } +} diff --git a/python/src/com/jetbrains/python/psi/PyUtil.java b/python/src/com/jetbrains/python/psi/PyUtil.java index db4c4677f819..9b72ceb55c05 100644 --- a/python/src/com/jetbrains/python/psi/PyUtil.java +++ b/python/src/com/jetbrains/python/psi/PyUtil.java @@ -1202,5 +1202,30 @@ public class PyUtil { return element instanceof PsiWhiteSpace ? null : element; } + + public static PsiElement addElementToStatementList(@NotNull PsiElement element, @NotNull PyStatementList statementList) { + 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) { + final PyDocStringOwner docStringOwner = PsiTreeUtil.getParentOfType(statementList, PyDocStringOwner.class); + final PyStatement firstStatement = statements[0]; + if (docStringOwner != null && firstStatement instanceof PyExpressionStatement && + ((PyExpressionStatement)firstStatement).getExpression() == docStringOwner.getDocStringExpression()) { + element = statementList.addAfter(element, firstStatement); + } + else + element = statementList.addBefore(element, firstStatement); + } + else { + element = statementList.add(element); + } + } + return element; + } } diff --git a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java index eabaa37e163a..d96a641f3e75 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java @@ -929,7 +929,7 @@ public class PyClassImpl extends PyPresentableElementImpl implement return new ArrayList(expressions); } - private static void collectInstanceAttributes(@NotNull PyFunction method, @NotNull final Map result) { + public static void collectInstanceAttributes(@NotNull PyFunction method, @NotNull final Map result) { final PyParameter[] params = method.getParameterList().getParameters(); if (params.length == 0) { return; diff --git a/python/src/com/jetbrains/python/psi/impl/PyElementGeneratorImpl.java b/python/src/com/jetbrains/python/psi/impl/PyElementGeneratorImpl.java index d7249521648b..29c121fe571a 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyElementGeneratorImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyElementGeneratorImpl.java @@ -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"); diff --git a/python/testData/inspections/PyAttributeOutsideInitInspection/trueNegative.py b/python/testData/inspections/PyAttributeOutsideInitInspection/trueNegative.py new file mode 100644 index 000000000000..e0961ac41062 --- /dev/null +++ b/python/testData/inspections/PyAttributeOutsideInitInspection/trueNegative.py @@ -0,0 +1,9 @@ +__author__ = 'ktisha' + +class A: + def __init__(self): + self.a = 1 + self.b = 1 + + def foo(self): + self.b = 1 diff --git a/python/testData/inspections/PyAttributeOutsideInitInspection/truePositive.py b/python/testData/inspections/PyAttributeOutsideInitInspection/truePositive.py new file mode 100644 index 000000000000..87fc96cfef11 --- /dev/null +++ b/python/testData/inspections/PyAttributeOutsideInitInspection/truePositive.py @@ -0,0 +1,8 @@ +__author__ = 'ktisha' + +class A: + def __init__(self): + self.a = 1 + + def foo(self): + self.b= 1 diff --git a/python/testData/inspections/PyClassHasNoInitInspection/class.py b/python/testData/inspections/PyClassHasNoInitInspection/class.py new file mode 100644 index 000000000000..d3cebf62b1c1 --- /dev/null +++ b/python/testData/inspections/PyClassHasNoInitInspection/class.py @@ -0,0 +1,4 @@ +__author__ = 'ktisha' +class A: + def foo(self): + self.b = 1 \ No newline at end of file diff --git a/python/testData/inspections/PyClassHasNoInitInspection/parentClass.py b/python/testData/inspections/PyClassHasNoInitInspection/parentClass.py new file mode 100644 index 000000000000..b28f8965302b --- /dev/null +++ b/python/testData/inspections/PyClassHasNoInitInspection/parentClass.py @@ -0,0 +1,9 @@ +__author__ = 'ktisha' +class A: + def foo(self): + self.b = 1 + +class B(A): + def __init__(self): + self.b = 2 + diff --git a/python/testData/inspections/PyClassHasNoInitInspection/trueNegative.py b/python/testData/inspections/PyClassHasNoInitInspection/trueNegative.py new file mode 100644 index 000000000000..7b8eacb4df4c --- /dev/null +++ b/python/testData/inspections/PyClassHasNoInitInspection/trueNegative.py @@ -0,0 +1,7 @@ +__author__ = 'ktisha' +class A: + def __init__(self): + self._a = 1 + + def foo(self): + self.b = 1 \ No newline at end of file diff --git a/python/testData/inspections/PyGlobalUndefinedInspection/trueNegative.py b/python/testData/inspections/PyGlobalUndefinedInspection/trueNegative.py new file mode 100644 index 000000000000..928d6c65d41b --- /dev/null +++ b/python/testData/inspections/PyGlobalUndefinedInspection/trueNegative.py @@ -0,0 +1,8 @@ +__author__ = 'ktisha' +bar = 1 + +def foo(): + global bar + print bar + +foo() \ No newline at end of file diff --git a/python/testData/inspections/PyGlobalUndefinedInspection/truePositive.py b/python/testData/inspections/PyGlobalUndefinedInspection/truePositive.py new file mode 100644 index 000000000000..d34a29765a7d --- /dev/null +++ b/python/testData/inspections/PyGlobalUndefinedInspection/truePositive.py @@ -0,0 +1,6 @@ +__author__ = 'ktisha' +def foo(): + global bar + print bar + +foo() \ No newline at end of file diff --git a/python/testData/inspections/PyNoneFunctionAssignmentInspection/noReturn.py b/python/testData/inspections/PyNoneFunctionAssignmentInspection/noReturn.py new file mode 100644 index 000000000000..7b14161bb1fc --- /dev/null +++ b/python/testData/inspections/PyNoneFunctionAssignmentInspection/noReturn.py @@ -0,0 +1,6 @@ +__author__ = 'ktisha' + +def foo(): + a = 1 + +b = foo() \ No newline at end of file diff --git a/python/testData/inspections/PyNoneFunctionAssignmentInspection/noType.py b/python/testData/inspections/PyNoneFunctionAssignmentInspection/noType.py new file mode 100644 index 000000000000..1f540cd70028 --- /dev/null +++ b/python/testData/inspections/PyNoneFunctionAssignmentInspection/noType.py @@ -0,0 +1,6 @@ +__author__ = 'ktisha' + +def foo(x): + return x + +b = foo() \ No newline at end of file diff --git a/python/testData/inspections/PyNoneFunctionAssignmentInspection/pass.py b/python/testData/inspections/PyNoneFunctionAssignmentInspection/pass.py new file mode 100644 index 000000000000..6caff7dada73 --- /dev/null +++ b/python/testData/inspections/PyNoneFunctionAssignmentInspection/pass.py @@ -0,0 +1,6 @@ +__author__ = 'ktisha' + +def foo(): + pass + +b = foo() \ No newline at end of file diff --git a/python/testData/inspections/PyNoneFunctionAssignmentInspection/returnNone.py b/python/testData/inspections/PyNoneFunctionAssignmentInspection/returnNone.py new file mode 100644 index 000000000000..c92aadde89b7 --- /dev/null +++ b/python/testData/inspections/PyNoneFunctionAssignmentInspection/returnNone.py @@ -0,0 +1,6 @@ +__author__ = 'ktisha' + +def foo(): + return None + +b = foo() \ No newline at end of file diff --git a/python/testData/inspections/PyNoneFunctionAssignmentInspection/trueNegative.py b/python/testData/inspections/PyNoneFunctionAssignmentInspection/trueNegative.py new file mode 100644 index 000000000000..af29087226f1 --- /dev/null +++ b/python/testData/inspections/PyNoneFunctionAssignmentInspection/trueNegative.py @@ -0,0 +1,6 @@ +__author__ = 'ktisha' + +def foo(): + return 3 + +b = foo() \ No newline at end of file diff --git a/python/testData/inspections/PyProtectedMemberInspection/trueNegative.py b/python/testData/inspections/PyProtectedMemberInspection/trueNegative.py new file mode 100644 index 000000000000..cef8d0097304 --- /dev/null +++ b/python/testData/inspections/PyProtectedMemberInspection/trueNegative.py @@ -0,0 +1,15 @@ +__author__ = 'ktisha' + +class A: + def __init__(self): + self._a = 1 + + def foo(self): + self.b= 1 + +class B(A): + def __init__(self): + A.__init__(self) + self.b = self._a + + diff --git a/python/testData/inspections/PyProtectedMemberInspection/truePositive.py b/python/testData/inspections/PyProtectedMemberInspection/truePositive.py new file mode 100644 index 000000000000..979cfd9a5ead --- /dev/null +++ b/python/testData/inspections/PyProtectedMemberInspection/truePositive.py @@ -0,0 +1,11 @@ +__author__ = 'ktisha' + +class A: + def __init__(self): + self._a = 1 + + def foo(self): + self.b= 1 + + +print A()._a \ No newline at end of file diff --git a/python/testData/inspections/PyProtectedMemberInspection/truePositiveInClass.py b/python/testData/inspections/PyProtectedMemberInspection/truePositiveInClass.py new file mode 100644 index 000000000000..45474e7b4f58 --- /dev/null +++ b/python/testData/inspections/PyProtectedMemberInspection/truePositiveInClass.py @@ -0,0 +1,12 @@ +__author__ = 'ktisha' + +class A: + def __init__(self): + self._a = 1 + + def foo(self): + self.b= 1 + +class B: + def __init__(self): + A()._a diff --git a/python/testData/quickFixes/AddMethodQuickFixTest/addInit.py b/python/testData/quickFixes/AddMethodQuickFixTest/addInit.py new file mode 100644 index 000000000000..e218d09b8165 --- /dev/null +++ b/python/testData/quickFixes/AddMethodQuickFixTest/addInit.py @@ -0,0 +1,4 @@ +__author__ = 'ktisha' +class A(object): + def foo(self): + self.b = 1 diff --git a/python/testData/quickFixes/AddMethodQuickFixTest/addInitAfterDocstring.py b/python/testData/quickFixes/AddMethodQuickFixTest/addInitAfterDocstring.py new file mode 100644 index 000000000000..fa1256c76f0f --- /dev/null +++ b/python/testData/quickFixes/AddMethodQuickFixTest/addInitAfterDocstring.py @@ -0,0 +1,5 @@ +__author__ = 'ktisha' +class A(object): + """ class docstring""" + def foo(self): + self.b = 1 diff --git a/python/testData/quickFixes/AddMethodQuickFixTest/addInitAfterDocstring_after.py b/python/testData/quickFixes/AddMethodQuickFixTest/addInitAfterDocstring_after.py new file mode 100644 index 000000000000..035e6aabdfd1 --- /dev/null +++ b/python/testData/quickFixes/AddMethodQuickFixTest/addInitAfterDocstring_after.py @@ -0,0 +1,9 @@ +__author__ = 'ktisha' +class A(object): + """ class docstring""" + + def __init__(self): + pass + + def foo(self): + self.b = 1 diff --git a/python/testData/quickFixes/AddMethodQuickFixTest/addInit_after.py b/python/testData/quickFixes/AddMethodQuickFixTest/addInit_after.py new file mode 100644 index 000000000000..a4c47a805bbd --- /dev/null +++ b/python/testData/quickFixes/AddMethodQuickFixTest/addInit_after.py @@ -0,0 +1,7 @@ +__author__ = 'ktisha' +class A(object): + def __init__(self): + pass + + def foo(self): + self.b = 1 diff --git a/python/testData/inspections/AddMethodFromInstance.py b/python/testData/quickFixes/AddMethodQuickFixTest/addMethodFromInstance.py similarity index 100% rename from python/testData/inspections/AddMethodFromInstance.py rename to python/testData/quickFixes/AddMethodQuickFixTest/addMethodFromInstance.py diff --git a/python/testData/inspections/AddMethodFromInstance_after.py b/python/testData/quickFixes/AddMethodQuickFixTest/addMethodFromInstance_after.py similarity index 100% rename from python/testData/inspections/AddMethodFromInstance_after.py rename to python/testData/quickFixes/AddMethodQuickFixTest/addMethodFromInstance_after.py diff --git a/python/testData/inspections/AddMethodFromMethod.py b/python/testData/quickFixes/AddMethodQuickFixTest/addMethodFromMethod.py similarity index 100% rename from python/testData/inspections/AddMethodFromMethod.py rename to python/testData/quickFixes/AddMethodQuickFixTest/addMethodFromMethod.py diff --git a/python/testData/inspections/AddMethodFromMethod_after.py b/python/testData/quickFixes/AddMethodQuickFixTest/addMethodFromMethod_after.py similarity index 100% rename from python/testData/inspections/AddMethodFromMethod_after.py rename to python/testData/quickFixes/AddMethodQuickFixTest/addMethodFromMethod_after.py diff --git a/python/testData/quickFixes/AddMethodQuickFixTest/addMethodReplacePass.py b/python/testData/quickFixes/AddMethodQuickFixTest/addMethodReplacePass.py new file mode 100644 index 000000000000..7e93b2109fca --- /dev/null +++ b/python/testData/quickFixes/AddMethodQuickFixTest/addMethodReplacePass.py @@ -0,0 +1,5 @@ +class A: + pass + +a = A() +a.y() diff --git a/python/testData/quickFixes/AddMethodQuickFixTest/addMethodReplacePass_after.py b/python/testData/quickFixes/AddMethodQuickFixTest/addMethodReplacePass_after.py new file mode 100644 index 000000000000..3082267d4d02 --- /dev/null +++ b/python/testData/quickFixes/AddMethodQuickFixTest/addMethodReplacePass_after.py @@ -0,0 +1,6 @@ +class A: + def y(self): + pass + +a = A() +a.y() diff --git a/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/addPass.py b/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/addPass.py new file mode 100644 index 000000000000..2f8f8b71940a --- /dev/null +++ b/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/addPass.py @@ -0,0 +1,9 @@ +__author__ = 'ktisha' + + +class A: + def __init__(self): + self._a = 1 + + def foo(self): + self.b = 1 diff --git a/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/addPass_after.py b/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/addPass_after.py new file mode 100644 index 000000000000..da65a030ea4d --- /dev/null +++ b/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/addPass_after.py @@ -0,0 +1,10 @@ +__author__ = 'ktisha' + + +class A: + def __init__(self): + self._a = 1 + self.b = 1 + + def foo(self): + pass diff --git a/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/createInit.py b/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/createInit.py new file mode 100644 index 000000000000..a748e483d84c --- /dev/null +++ b/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/createInit.py @@ -0,0 +1,7 @@ +__author__ = 'ktisha' + + +class A: + def foo(self): + self.b = 1 + c = 1 \ No newline at end of file diff --git a/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/createInit_after.py b/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/createInit_after.py new file mode 100644 index 000000000000..f6e6e3f22f53 --- /dev/null +++ b/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/createInit_after.py @@ -0,0 +1,9 @@ +__author__ = 'ktisha' + + +class A: + def __init__(self): + self.b = 1 + + def foo(self): + c = 1 \ No newline at end of file diff --git a/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/moveToInit.py b/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/moveToInit.py new file mode 100644 index 000000000000..767bd48bb19b --- /dev/null +++ b/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/moveToInit.py @@ -0,0 +1,10 @@ +__author__ = 'ktisha' + + +class A: + def __init__(self): + self._a = 1 + + def foo(self): + self.b = 1 + c = 1 \ No newline at end of file diff --git a/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/moveToInit_after.py b/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/moveToInit_after.py new file mode 100644 index 000000000000..2dc80743d949 --- /dev/null +++ b/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/moveToInit_after.py @@ -0,0 +1,10 @@ +__author__ = 'ktisha' + + +class A: + def __init__(self): + self._a = 1 + self.b = 1 + + def foo(self): + c = 1 \ No newline at end of file diff --git a/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/removePass.py b/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/removePass.py new file mode 100644 index 000000000000..6b9381ffa4b7 --- /dev/null +++ b/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/removePass.py @@ -0,0 +1,9 @@ +__author__ = 'ktisha' + + +class A: + def __init__(self): + pass + + def foo(self): + self.b = 1 \ No newline at end of file diff --git a/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/removePass_after.py b/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/removePass_after.py new file mode 100644 index 000000000000..b8da02ff7b17 --- /dev/null +++ b/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/removePass_after.py @@ -0,0 +1,9 @@ +__author__ = 'ktisha' + + +class A: + def __init__(self): + self.b = 1 + + def foo(self): + pass diff --git a/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/skipDocstring.py b/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/skipDocstring.py new file mode 100644 index 000000000000..a8ba90afa2a9 --- /dev/null +++ b/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/skipDocstring.py @@ -0,0 +1,10 @@ +__author__ = 'ktisha' + + +class A: + """ + class docstring + """ + + def foo(self): + self.b = 1 \ No newline at end of file diff --git a/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/skipDocstring_after.py b/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/skipDocstring_after.py new file mode 100644 index 000000000000..d188b5eb7a24 --- /dev/null +++ b/python/testData/quickFixes/PyMoveAttributeToInitQuickFixTest/skipDocstring_after.py @@ -0,0 +1,13 @@ +__author__ = 'ktisha' + + +class A: + """ + class docstring + """ + + def __init__(self): + self.b = 1 + + def foo(self): + pass diff --git a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java index a4a236419ada..70eeef9216ca 100644 --- a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java +++ b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java @@ -115,16 +115,6 @@ public class PyQuickFixTest extends PyTestCase { true, true); } - public void testAddMethodFromInstance() { - doInspectionTest("AddMethodFromInstance.py", PyUnresolvedReferencesInspection.class, PyBundle.message("QFIX.NAME.add.method.$0.to.class.$1", "y", "A"), - true, true); - } - - public void testAddMethodFromMethod() { - doInspectionTest("AddMethodFromMethod.py", PyUnresolvedReferencesInspection.class, PyBundle.message("QFIX.NAME.add.method.$0.to.class.$1", "y", "A"), - true, true); - } - public void testRemoveTrailingSemicolon() { doInspectionTest("RemoveTrailingSemicolon.py", PyTrailingSemicolonInspection.class, PyBundle.message("QFIX.remove.trailing.semicolon"), true, true); diff --git a/python/testSrc/com/jetbrains/python/inspections/PyAttributeOutsideInitInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyAttributeOutsideInitInspectionTest.java new file mode 100644 index 000000000000..45bddc738a5c --- /dev/null +++ b/python/testSrc/com/jetbrains/python/inspections/PyAttributeOutsideInitInspectionTest.java @@ -0,0 +1,23 @@ +package com.jetbrains.python.inspections; + +import com.jetbrains.python.fixtures.PyTestCase; + +/** + * User: ktisha + */ +public class PyAttributeOutsideInitInspectionTest extends PyTestCase { + + public void testTruePositive() { + doTest(); + } + + public void testTrueNegative() { + doTest(); + } + + private void doTest() { + myFixture.configureByFile("inspections/PyAttributeOutsideInitInspection/" + getTestName(true) + ".py"); + myFixture.enableInspections(PyAttributeOutsideInitInspection.class); + myFixture.checkHighlighting(false, false, true); + } +} diff --git a/python/testSrc/com/jetbrains/python/inspections/PyClassHasNoInitInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyClassHasNoInitInspectionTest.java new file mode 100644 index 000000000000..266c13ee8d98 --- /dev/null +++ b/python/testSrc/com/jetbrains/python/inspections/PyClassHasNoInitInspectionTest.java @@ -0,0 +1,27 @@ +package com.jetbrains.python.inspections; + +import com.jetbrains.python.fixtures.PyTestCase; + +/** + * User: ktisha + */ +public class PyClassHasNoInitInspectionTest extends PyTestCase { + + public void testClass() { + doTest(); + } + + public void testTrueNegative() { + doTest(); + } + + public void testParentClass() { + doTest(); + } + + private void doTest() { + myFixture.configureByFile("inspections/PyClassHasNoInitInspection/" + getTestName(true) + ".py"); + myFixture.enableInspections(PyClassHasNoInitInspection.class); + myFixture.checkHighlighting(false, false, true); + } +} diff --git a/python/testSrc/com/jetbrains/python/inspections/PyGlobalUndefinedInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyGlobalUndefinedInspectionTest.java new file mode 100644 index 000000000000..cad5cd00286b --- /dev/null +++ b/python/testSrc/com/jetbrains/python/inspections/PyGlobalUndefinedInspectionTest.java @@ -0,0 +1,23 @@ +package com.jetbrains.python.inspections; + +import com.jetbrains.python.fixtures.PyTestCase; + +/** + * User: ktisha + */ +public class PyGlobalUndefinedInspectionTest extends PyTestCase { + + public void testTruePositive() { + doTest(); + } + + public void testTrueNegative() { + doTest(); + } + + private void doTest() { + myFixture.configureByFile("inspections/PyGlobalUndefinedInspection/" + getTestName(true) + ".py"); + myFixture.enableInspections(PyGlobalUndefinedInspection.class); + myFixture.checkHighlighting(false, false, true); + } +} diff --git a/python/testSrc/com/jetbrains/python/inspections/PyNoneFunctionAssignmentInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyNoneFunctionAssignmentInspectionTest.java new file mode 100644 index 000000000000..7247cc07afe6 --- /dev/null +++ b/python/testSrc/com/jetbrains/python/inspections/PyNoneFunctionAssignmentInspectionTest.java @@ -0,0 +1,35 @@ +package com.jetbrains.python.inspections; + +import com.jetbrains.python.fixtures.PyTestCase; + +/** + * User: ktisha + */ +public class PyNoneFunctionAssignmentInspectionTest extends PyTestCase { + + public void testPass() { + doTest(); + } + + public void testReturnNone() { + doTest(); + } + + public void testNoReturn() { + doTest(); + } + + public void testTrueNegative() { + doTest(); + } + + public void testNoType() { + doTest(); + } + + private void doTest() { + myFixture.configureByFile("inspections/PyNoneFunctionAssignmentInspection/" + getTestName(true) + ".py"); + myFixture.enableInspections(PyNoneFunctionAssignmentInspection.class); + myFixture.checkHighlighting(false, false, true); + } +} diff --git a/python/testSrc/com/jetbrains/python/inspections/PyProtectedMemberInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyProtectedMemberInspectionTest.java new file mode 100644 index 000000000000..5398d14fb120 --- /dev/null +++ b/python/testSrc/com/jetbrains/python/inspections/PyProtectedMemberInspectionTest.java @@ -0,0 +1,27 @@ +package com.jetbrains.python.inspections; + +import com.jetbrains.python.fixtures.PyTestCase; + +/** + * User: ktisha + */ +public class PyProtectedMemberInspectionTest extends PyTestCase { + + public void testTruePositive() { + doTest(); + } + + public void testTruePositiveInClass() { + doTest(); + } + + public void testTrueNegative() { + doTest(); + } + + private void doTest() { + myFixture.configureByFile("inspections/PyProtectedMemberInspection/" + getTestName(true) + ".py"); + myFixture.enableInspections(PyProtectedMemberInspection.class); + myFixture.checkHighlighting(false, false, true); + } +} diff --git a/python/testSrc/com/jetbrains/python/quickFixes/AddMethodQuickFixTest.java b/python/testSrc/com/jetbrains/python/quickFixes/AddMethodQuickFixTest.java new file mode 100644 index 000000000000..01d97615b242 --- /dev/null +++ b/python/testSrc/com/jetbrains/python/quickFixes/AddMethodQuickFixTest.java @@ -0,0 +1,32 @@ +package com.jetbrains.python.quickFixes; + +import com.jetbrains.python.PyBundle; +import com.jetbrains.python.inspections.PyClassHasNoInitInspection; +import com.jetbrains.python.inspections.PyUnresolvedReferencesInspection; + +/** + * User: ktisha + */ +public class AddMethodQuickFixTest extends PyQuickFixTestCase { + + public void testAddInit() { + doInspectionTest(PyClassHasNoInitInspection.class, PyBundle.message("QFIX.NAME.add.method.$0.to.class.$1", "__init__", "A")); + } + + public void testAddInitAfterDocstring() { + doInspectionTest(PyClassHasNoInitInspection.class, PyBundle.message("QFIX.NAME.add.method.$0.to.class.$1", "__init__", "A")); + } + + public void testAddMethodReplacePass() { + doInspectionTest(PyUnresolvedReferencesInspection.class, PyBundle.message("QFIX.NAME.add.method.$0.to.class.$1", "y", "A")); + } + + public void testAddMethodFromInstance() { + doInspectionTest(PyUnresolvedReferencesInspection.class, PyBundle.message("QFIX.NAME.add.method.$0.to.class.$1", "y", "A")); + } + + public void testAddMethodFromMethod() { + doInspectionTest(PyUnresolvedReferencesInspection.class, PyBundle.message("QFIX.NAME.add.method.$0.to.class.$1", "y", "A")); + } + +} diff --git a/python/testSrc/com/jetbrains/python/quickFixes/PyMoveAttributeToInitQuickFixTest.java b/python/testSrc/com/jetbrains/python/quickFixes/PyMoveAttributeToInitQuickFixTest.java new file mode 100644 index 000000000000..b02d5fd4f092 --- /dev/null +++ b/python/testSrc/com/jetbrains/python/quickFixes/PyMoveAttributeToInitQuickFixTest.java @@ -0,0 +1,33 @@ +package com.jetbrains.python.quickFixes; + +import com.intellij.testFramework.TestDataPath; +import com.jetbrains.python.PyBundle; +import com.jetbrains.python.inspections.PyAttributeOutsideInitInspection; + +/** + * User: ktisha + */ +@TestDataPath("$CONTENT_ROOT/../testData/quickFixes/PyMoveAttributeToInitQuickFixTest") +public class PyMoveAttributeToInitQuickFixTest extends PyQuickFixTestCase { + + public void testMoveToInit() { + doInspectionTest(PyAttributeOutsideInitInspection.class, PyBundle.message("QFIX.move.attribute")); + } + + public void testCreateInit() { + doInspectionTest(PyAttributeOutsideInitInspection.class, PyBundle.message("QFIX.move.attribute")); + } + + public void testAddPass() { + doInspectionTest(PyAttributeOutsideInitInspection.class, PyBundle.message("QFIX.move.attribute")); + } + + public void testRemovePass() { + doInspectionTest(PyAttributeOutsideInitInspection.class, PyBundle.message("QFIX.move.attribute")); + } + + public void testSkipDocstring() { + doInspectionTest(PyAttributeOutsideInitInspection.class, PyBundle.message("QFIX.move.attribute")); + } + +} diff --git a/python/testSrc/com/jetbrains/python/quickFixes/PyQuickFixTestCase.java b/python/testSrc/com/jetbrains/python/quickFixes/PyQuickFixTestCase.java new file mode 100644 index 000000000000..660dcdf13959 --- /dev/null +++ b/python/testSrc/com/jetbrains/python/quickFixes/PyQuickFixTestCase.java @@ -0,0 +1,28 @@ +package com.jetbrains.python.quickFixes; + +import com.intellij.codeInsight.intention.IntentionAction; +import com.jetbrains.python.PythonTestUtil; +import com.jetbrains.python.fixtures.PyTestCase; +import org.jetbrains.annotations.NonNls; + +/** + * User: ktisha + */ +public abstract class PyQuickFixTestCase extends PyTestCase { + @Override + @NonNls + protected String getTestDataPath() { + return PythonTestUtil.getTestDataPath() + "/quickFixes/" + getClass().getSimpleName(); + } + + protected void doInspectionTest(final Class inspectionClass, final String hint) { + final String testFileName = getTestName(true); + myFixture.enableInspections(inspectionClass); + myFixture.configureByFile(testFileName + ".py"); + myFixture.checkHighlighting(true, false, false); + final IntentionAction intentionAction = myFixture.findSingleIntention(hint); + assertNotNull(intentionAction); + myFixture.launchAction(intentionAction); + myFixture.checkResultByFile(testFileName + "_after.py", true); + } +} diff --git a/python/testSrc/com/jetbrains/python/refactoring/PyIntroduceTestCase.java b/python/testSrc/com/jetbrains/python/refactoring/PyIntroduceTestCase.java index 6922ec5915e5..c7b20ac1b025 100644 --- a/python/testSrc/com/jetbrains/python/refactoring/PyIntroduceTestCase.java +++ b/python/testSrc/com/jetbrains/python/refactoring/PyIntroduceTestCase.java @@ -1,11 +1,9 @@ package com.jetbrains.python.refactoring; -import com.intellij.codeInsight.template.TemplateManager; import com.intellij.codeInsight.template.impl.TemplateManagerImpl; import com.intellij.codeInsight.template.impl.TemplateState; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.testFramework.LightPlatformTestCase; import com.intellij.util.Consumer; import com.jetbrains.python.fixtures.PyTestCase; import com.jetbrains.python.psi.PyExpression; @@ -63,9 +61,8 @@ public abstract class PyIntroduceTestCase extends PyTestCase { String name = getTestName(true); myFixture.configureByFile(name + ".py"); final boolean enabled = myFixture.getEditor().getSettings().isVariableInplaceRenameEnabled(); - TemplateManagerImpl templateManager = (TemplateManagerImpl)TemplateManager.getInstance(LightPlatformTestCase.getProject()); try { - templateManager.setTemplateTesting(true); + TemplateManagerImpl.setTemplateTesting(myFixture.getProject(), getTestRootDisposable()); myFixture.getEditor().getSettings().setVariableInplaceRenameEnabled(true); IntroduceHandler handler = createHandler(); @@ -83,7 +80,6 @@ public abstract class PyIntroduceTestCase extends PyTestCase { } finally { myFixture.getEditor().getSettings().setVariableInplaceRenameEnabled(enabled); - templateManager.setTemplateTesting(false); } } }