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);
}
}
}