diff --git a/python/src/com/jetbrains/python/PyNames.java b/python/src/com/jetbrains/python/PyNames.java index 72f0645d9d4b..f1c93ab8b0c9 100644 --- a/python/src/com/jetbrains/python/PyNames.java +++ b/python/src/com/jetbrains/python/PyNames.java @@ -12,54 +12,59 @@ import java.util.regex.Pattern; /** * @author dcheryasov */ +@NonNls public class PyNames { private PyNames() { } - @NonNls public static final String INIT = "__init__"; - @NonNls public static final String DOT_PY = ".py"; - @NonNls public static final String INIT_DOT_PY = INIT + DOT_PY; + public static final String INIT = "__init__"; + public static final String DOT_PY = ".py"; + public static final String INIT_DOT_PY = INIT + DOT_PY; - @NonNls public static final String NEW = "__new__"; - @NonNls public static final String GETATTR = "__getattr__"; - @NonNls public static final String GETATTRIBUTE = "__getattribute__"; - @NonNls public static final String CLASS = "__class__"; - @NonNls public static final String METACLASS = "__metaclass__"; + public static final String NEW = "__new__"; + public static final String GETATTR = "__getattr__"; + public static final String GETATTRIBUTE = "__getattribute__"; + public static final String CLASS = "__class__"; + public static final String METACLASS = "__metaclass__"; - @NonNls public static final String SUPER = "super"; + public static final String SUPER = "super"; - @NonNls public static final String OBJECT = "object"; - @NonNls public static final String NONE = "None"; - @NonNls public static final String TRUE = "True"; - @NonNls public static final String FALSE = "False"; - @NonNls public static final String FAKE_OLD_BASE = "___Classobj"; + public static final String OBJECT = "object"; + public static final String NONE = "None"; + public static final String TRUE = "True"; + public static final String FALSE = "False"; + public static final String FAKE_OLD_BASE = "___Classobj"; - @NonNls public static final String FUTURE_MODULE = "__future__"; + public static final String FUTURE_MODULE = "__future__"; - @NonNls public static final String CLASSMETHOD = "classmethod"; - @NonNls public static final String STATICMETHOD = "staticmethod"; + public static final String CLASSMETHOD = "classmethod"; + public static final String STATICMETHOD = "staticmethod"; - @NonNls public static final String PROPERTY = "property"; + public static final String PROPERTY = "property"; - @NonNls public static final String ALL = "__all__"; - @NonNls public static final String SLOTS = "__slots__"; - @NonNls public static final String DEBUG = "__debug__"; + public static final String ALL = "__all__"; + public static final String SLOTS = "__slots__"; + public static final String DEBUG = "__debug__"; - @NonNls public static final String ISINSTANCE = "isinstance"; + public static final String ISINSTANCE = "isinstance"; - @NonNls public static final String DOCFORMAT = "__docformat__"; + public static final String DOCFORMAT = "__docformat__"; - @NonNls public static final String DIRNAME = "dirname"; - @NonNls public static final String ABSPATH = "abspath"; - @NonNls public static final String JOIN = "join"; - @NonNls public static final String REPLACE = "replace"; - @NonNls public static final String FILE = "__file__"; + public static final String DIRNAME = "dirname"; + public static final String ABSPATH = "abspath"; + public static final String JOIN = "join"; + public static final String REPLACE = "replace"; + public static final String FILE = "__file__"; + + public static final String WARN = "warn"; + public static final String DEPRECATION_WARNING = "DeprecationWarning"; + public static final String PENDING_DEPRECATION_WARNING = "PendingDeprecationWarning"; /** * Contains all known predefined names of "__foo__" form. */ public static ImmutableSet UnderscoredAttributes = ImmutableSet.of( - "__all__", + "__all__", "__author__", "__bases__", "__dict__", diff --git a/python/src/com/jetbrains/python/inspections/PyDeprecationInspection.java b/python/src/com/jetbrains/python/inspections/PyDeprecationInspection.java new file mode 100644 index 000000000000..6742066b7ee3 --- /dev/null +++ b/python/src/com/jetbrains/python/inspections/PyDeprecationInspection.java @@ -0,0 +1,57 @@ +package com.jetbrains.python.inspections; + +import com.intellij.codeInspection.LocalInspectionToolSession; +import com.intellij.codeInspection.ProblemHighlightType; +import com.intellij.codeInspection.ProblemsHolder; +import com.intellij.lang.ASTNode; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiElementVisitor; +import com.jetbrains.python.psi.PyFile; +import com.jetbrains.python.psi.PyFunction; +import com.jetbrains.python.psi.PyReferenceExpression; +import com.jetbrains.python.psi.resolve.PyResolveContext; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author yole + */ +public class PyDeprecationInspection extends PyInspection { + @Nls + @NotNull + @Override + public String getDisplayName() { + return "Deprecated function, class or module"; + } + + @NotNull + @Override + public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, + boolean isOnTheFly, + @NotNull LocalInspectionToolSession session) { + return new Visitor(holder); + } + + private static class Visitor extends PyInspectionVisitor { + public Visitor(@Nullable final ProblemsHolder holder) { + super(holder); + } + + @Override + public void visitPyReferenceExpression(PyReferenceExpression node) { + PsiElement resolveResult = node.getReference(PyResolveContext.noImplicits()).resolve(); + String deprecationMessage = null; + if (resolveResult instanceof PyFunction) { + deprecationMessage = ((PyFunction) resolveResult).getDeprecationMessage(); + } + else if (resolveResult instanceof PyFile) { + deprecationMessage = ((PyFile)resolveResult).getDeprecationMessage(); + } + if (deprecationMessage != null) { + ASTNode nameElement = node.getNameElement(); + registerProblem(nameElement == null ? node : nameElement.getPsi(), deprecationMessage, ProblemHighlightType.LIKE_DEPRECATED, null); + } + } + } +} diff --git a/python/src/com/jetbrains/python/inspections/PythonInspectionToolProvider.java b/python/src/com/jetbrains/python/inspections/PythonInspectionToolProvider.java index 13fc7166fec9..9cc3c6f00c15 100644 --- a/python/src/com/jetbrains/python/inspections/PythonInspectionToolProvider.java +++ b/python/src/com/jetbrains/python/inspections/PythonInspectionToolProvider.java @@ -61,6 +61,7 @@ public class PythonInspectionToolProvider implements InspectionToolProvider { PySetFunctionToLiteralInspection.class, PyDecoratorInspection.class, PyTypeCheckerInspection.class, + PyDeprecationInspection.class, }; } } diff --git a/python/src/com/jetbrains/python/psi/PyFile.java b/python/src/com/jetbrains/python/psi/PyFile.java index f1926990658a..7bb05a95b08a 100644 --- a/python/src/com/jetbrains/python/psi/PyFile.java +++ b/python/src/com/jetbrains/python/psi/PyFile.java @@ -1,6 +1,5 @@ package com.jetbrains.python.psi; -import com.intellij.openapi.util.Key; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.jetbrains.python.codeInsight.controlflow.ScopeOwner; @@ -62,4 +61,11 @@ public interface PyFile extends PyElement, PsiFile, PyDocStringOwner, ScopeOwner * Return true if the file contains a 'from __future__ import ...' statement with given feature. */ boolean hasImportFromFuture(FutureFeature feature); + + /** + * If the function raises a DeprecationWarning or a PendingDeprecationWarning, returns the explanation text provided for the warning.. + * + * @return the deprecation message or null if the function is not deprecated. + */ + String getDeprecationMessage(); } diff --git a/python/src/com/jetbrains/python/psi/PyFileElementType.java b/python/src/com/jetbrains/python/psi/PyFileElementType.java index f13bd4f1f31c..d09cae9afe8a 100644 --- a/python/src/com/jetbrains/python/psi/PyFileElementType.java +++ b/python/src/com/jetbrains/python/psi/PyFileElementType.java @@ -14,6 +14,7 @@ import com.intellij.psi.stubs.StubElement; import com.intellij.psi.stubs.StubInputStream; import com.intellij.psi.stubs.StubOutputStream; import com.intellij.psi.tree.IStubFileElementType; +import com.intellij.util.io.StringRef; import com.jetbrains.python.lexer.PythonIndentingLexer; import com.jetbrains.python.parsing.PyParser; import com.jetbrains.python.parsing.StatementParsing; @@ -42,7 +43,7 @@ public class PyFileElementType extends IStubFileElementType { @Override public int getStubVersion() { - return 33; + return 34; } @Override @@ -84,13 +85,15 @@ public class PyFileElementType extends IStubFileElementType { public void serialize(PyFileStub stub, StubOutputStream dataStream) throws IOException { writeNullableList(dataStream, stub.getDunderAll()); writeBitSet(dataStream, stub.getFutureFeatures()); + dataStream.writeName(stub.getDeprecationMessage()); } @Override public PyFileStub deserialize(StubInputStream dataStream, StubElement parentStub) throws IOException { List all = readNullableList(dataStream); BitSet future_features = readBitSet(dataStream); - return new PyFileStubImpl(all, future_features); + StringRef deprecationMessage = dataStream.readName(); + return new PyFileStubImpl(all, future_features, deprecationMessage); } private static BitSet readBitSet(StubInputStream dataStream) throws IOException { diff --git a/python/src/com/jetbrains/python/psi/PyFunction.java b/python/src/com/jetbrains/python/psi/PyFunction.java index f5c3963d9be0..3c7ea2b656dd 100644 --- a/python/src/com/jetbrains/python/psi/PyFunction.java +++ b/python/src/com/jetbrains/python/psi/PyFunction.java @@ -53,6 +53,13 @@ extends @Nullable PyType getReturnTypeFromDocString(); + /** + * If the function raises a DeprecationWarning or a PendingDeprecationWarning, returns the explanation text provided for the warning.. + * + * @return the deprecation message or null if the function is not deprecated. + */ + String getDeprecationMessage(); + /** * Flags that mark common alterations of a function: decoration by and wrapping in classmethod() and staticmethod(). */ diff --git a/python/src/com/jetbrains/python/psi/impl/PyCallExpressionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyCallExpressionImpl.java index ac03486ebed3..1de439ae4b72 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyCallExpressionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyCallExpressionImpl.java @@ -51,7 +51,7 @@ public class PyCallExpressionImpl extends PyElementImpl implements PyCallExpress @Override public T getArgument(int index, Class argClass) { PyExpression[] args = getArguments(); - return args.length >= index && argClass.isInstance(args[index]) ? argClass.cast(args[index]) : null; + return args.length > index && argClass.isInstance(args[index]) ? argClass.cast(args[index]) : null; } public void addArgument(PyExpression expression) { diff --git a/python/src/com/jetbrains/python/psi/impl/PyFileImpl.java b/python/src/com/jetbrains/python/psi/impl/PyFileImpl.java index fb1f52e96c81..c1d2b730a5c5 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyFileImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyFileImpl.java @@ -547,6 +547,19 @@ public class PyFileImpl extends PsiFileBase implements PyFile, PyExpression { return enabled; } + @Override + public String getDeprecationMessage() { + final StubElement stub = getStub(); + if (stub instanceof PyFileStub) { + return ((PyFileStub) stub).getDeprecationMessage(); + } + return extractDeprecationMessage(); + } + + public String extractDeprecationMessage() { + return PyFunctionImpl.extractDeprecationMessage(getStatements()); + } + public boolean calculateImportFromFuture(FutureFeature feature) { final List fromImports = getFromImports(); for (PyFromImportStatement fromImport : fromImports) { diff --git a/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java index 2e15206fc058..77b699dc0969 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java @@ -220,6 +220,42 @@ public class PyFunctionImpl extends PyPresentableElementImpl imp return PyTypeParser.getTypeByName(this, typeName); } + @Override + public String getDeprecationMessage() { + PyFunctionStub stub = getStub(); + if (stub != null) { + return stub.getDeprecationMessage(); + } + return extractDeprecationMessage(); + } + + public String extractDeprecationMessage() { + PyStatementList statementList = getStatementList(); + if (statementList == null) { + return null; + } + return extractDeprecationMessage(Arrays.asList(statementList.getStatements())); + } + + public static String extractDeprecationMessage(List statements) { + for (PyStatement statement : statements) { + if (statement instanceof PyExpressionStatement) { + PyExpressionStatement expressionStatement = (PyExpressionStatement)statement; + if (expressionStatement.getExpression() instanceof PyCallExpression) { + PyCallExpression callExpression = (PyCallExpression)expressionStatement.getExpression(); + if (callExpression.isCalleeText(PyNames.WARN)) { + PyReferenceExpression warningClass = callExpression.getArgument(1, PyReferenceExpression.class); + if (warningClass != null && (PyNames.DEPRECATION_WARNING.equals(warningClass.getReferencedName()) || + PyNames.PENDING_DEPRECATION_WARNING.equals(warningClass.getReferencedName()))) { + return PyUtil.strValue(callExpression.getArguments() [0]); + } + } + } + } + } + return null; + } + @Nullable public String extractDocStringReturnType() { final PyStringLiteralExpression docString = getDocStringExpression(); diff --git a/python/src/com/jetbrains/python/psi/impl/stubs/PyFileStubImpl.java b/python/src/com/jetbrains/python/psi/impl/stubs/PyFileStubImpl.java index a45299f43a46..a2d32b6c1083 100644 --- a/python/src/com/jetbrains/python/psi/impl/stubs/PyFileStubImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/stubs/PyFileStubImpl.java @@ -2,6 +2,7 @@ package com.jetbrains.python.psi.impl.stubs; import com.intellij.psi.stubs.PsiFileStubImpl; import com.intellij.psi.tree.IStubFileElementType; +import com.intellij.util.io.StringRef; import com.jetbrains.python.PythonLanguage; import com.jetbrains.python.psi.FutureFeature; import com.jetbrains.python.psi.PyFile; @@ -17,6 +18,7 @@ import java.util.List; public class PyFileStubImpl extends PsiFileStubImpl implements PyFileStub { private final List myDunderAll; private final BitSet myFutureFeatures; // stores IDs of features + private final StringRef myDeprecationMessage; private static final int FUTURE_FEATURE_SET_SIZE = 32; // 32 features is ought to be enough for everybody! all bits fit into an int. @@ -28,12 +30,15 @@ public class PyFileStubImpl extends PsiFileStubImpl implements PyFileStu for (FutureFeature fuf : FutureFeature.ALL) { myFutureFeatures.set(fuf.ordinal(), fileImpl.calculateImportFromFuture(fuf)); } + String message = fileImpl.extractDeprecationMessage(); + myDeprecationMessage = message == null ? null : StringRef.fromString(message); } - public PyFileStubImpl(List dunderAll, final BitSet future_features) { + public PyFileStubImpl(List dunderAll, final BitSet future_features, final StringRef deprecationMessage) { super(null); myDunderAll = dunderAll; myFutureFeatures = future_features; + myDeprecationMessage = deprecationMessage; } @Override @@ -46,6 +51,11 @@ public class PyFileStubImpl extends PsiFileStubImpl implements PyFileStu return myFutureFeatures; } + @Override + public String getDeprecationMessage() { + return myDeprecationMessage == null ? null : myDeprecationMessage.getString(); + } + @Override public IStubFileElementType getType() { return PythonLanguage.getInstance().getFileElementType(); diff --git a/python/src/com/jetbrains/python/psi/impl/stubs/PyFunctionElementType.java b/python/src/com/jetbrains/python/psi/impl/stubs/PyFunctionElementType.java index 2aab9f9d6614..68df1cadd099 100644 --- a/python/src/com/jetbrains/python/psi/impl/stubs/PyFunctionElementType.java +++ b/python/src/com/jetbrains/python/psi/impl/stubs/PyFunctionElementType.java @@ -32,19 +32,24 @@ public class PyFunctionElementType extends PyStubElementType implements PyFunctionStub { private final String myName; private final String myReturnTypeFromDocString; + private final StringRef myDeprecationMessage; - public PyFunctionStubImpl(final String name, final String returnTypeFromDocString, final StubElement parent) { + public PyFunctionStubImpl(final String name, final String returnTypeFromDocString, @Nullable final StringRef deprecationMessage, + final StubElement parent) { super(parent, PyElementTypes.FUNCTION_DECLARATION); myName = name; myReturnTypeFromDocString = returnTypeFromDocString; + myDeprecationMessage = deprecationMessage; } public String getName() { @@ -27,6 +29,11 @@ public class PyFunctionStubImpl extends StubBase implements PyFuncti return myReturnTypeFromDocString; } + @Override + public String getDeprecationMessage() { + return myDeprecationMessage == null ? null : myDeprecationMessage.getString(); + } + @Override public String toString() { return "PyFunctionStub(" + myName + ")"; diff --git a/python/src/com/jetbrains/python/psi/stubs/PyFileStub.java b/python/src/com/jetbrains/python/psi/stubs/PyFileStub.java index 46c4a25ad24e..892a2d92bbc6 100644 --- a/python/src/com/jetbrains/python/psi/stubs/PyFileStub.java +++ b/python/src/com/jetbrains/python/psi/stubs/PyFileStub.java @@ -1,7 +1,6 @@ package com.jetbrains.python.psi.stubs; import com.intellij.psi.stubs.PsiFileStub; -import com.jetbrains.python.psi.FutureFeature; import com.jetbrains.python.psi.PyFile; import java.util.BitSet; @@ -13,4 +12,5 @@ import java.util.List; public interface PyFileStub extends PsiFileStub { List getDunderAll(); BitSet getFutureFeatures(); + String getDeprecationMessage(); } diff --git a/python/src/com/jetbrains/python/psi/stubs/PyFunctionStub.java b/python/src/com/jetbrains/python/psi/stubs/PyFunctionStub.java index d20f59cf712e..9dea887bf278 100644 --- a/python/src/com/jetbrains/python/psi/stubs/PyFunctionStub.java +++ b/python/src/com/jetbrains/python/psi/stubs/PyFunctionStub.java @@ -1,6 +1,3 @@ -/* - * @author max - */ package com.jetbrains.python.psi.stubs; import com.intellij.psi.stubs.NamedStub; @@ -8,4 +5,5 @@ import com.jetbrains.python.psi.PyFunction; public interface PyFunctionStub extends NamedStub { String getReturnTypeFromDocString(); + String getDeprecationMessage(); } \ No newline at end of file diff --git a/python/testData/deprecation/deprecatedImport.py b/python/testData/deprecation/deprecatedImport.py new file mode 100644 index 000000000000..968d4f71dbcb --- /dev/null +++ b/python/testData/deprecation/deprecatedImport.py @@ -0,0 +1 @@ +import deprecatedModule diff --git a/python/testData/deprecation/deprecatedModule.py b/python/testData/deprecation/deprecatedModule.py new file mode 100644 index 000000000000..54d18cc324d0 --- /dev/null +++ b/python/testData/deprecation/deprecatedModule.py @@ -0,0 +1,3 @@ +import warnings +warnings.warn("the deprecated module is deprecated; use a non-deprecated module instead", + DeprecationWarning, 2) diff --git a/python/testData/deprecation/deprecatedProperty.py b/python/testData/deprecation/deprecatedProperty.py new file mode 100644 index 000000000000..da68d9a11bb4 --- /dev/null +++ b/python/testData/deprecation/deprecatedProperty.py @@ -0,0 +1,8 @@ +class Foo: + @property + def bar(self): + import warnings + warnings.warn("this is deprecated", DeprecationWarning, 2) + +foo = Foo() +foo.bar diff --git a/python/testData/deprecation/functionStub.py b/python/testData/deprecation/functionStub.py new file mode 100644 index 000000000000..771f9b06b3ca --- /dev/null +++ b/python/testData/deprecation/functionStub.py @@ -0,0 +1,5 @@ +def getstatus(file): + """Return output of "ls -ld " in a string.""" + import warnings + warnings.warn("commands.getstatus() is deprecated", DeprecationWarning, 2) + return getoutput('ls -ld' + mkarg(file)) diff --git a/python/testSrc/com/jetbrains/python/PyDeprecationTest.java b/python/testSrc/com/jetbrains/python/PyDeprecationTest.java new file mode 100644 index 000000000000..004015a11928 --- /dev/null +++ b/python/testSrc/com/jetbrains/python/PyDeprecationTest.java @@ -0,0 +1,47 @@ +package com.jetbrains.python; + +import com.jetbrains.python.fixtures.PyLightFixtureTestCase; +import com.jetbrains.python.inspections.PyDeprecationInspection; +import com.jetbrains.python.psi.PyFile; +import com.jetbrains.python.psi.PyFunction; + +/** + * @author yole + */ +public class PyDeprecationTest extends PyLightFixtureTestCase { + public void testFunction() { + myFixture.configureByText(PythonFileType.INSTANCE, + "def getstatus(file):\n" + + " \"\"\"Return output of \"ls -ld \" in a string.\"\"\"\n" + + " import warnings\n" + + " warnings.warn(\"commands.getstatus() is deprecated\", DeprecationWarning, 2)\n" + + " return getoutput('ls -ld' + mkarg(file))"); + PyFunction getstatus = ((PyFile) myFixture.getFile()).findTopLevelFunction("getstatus"); + assertEquals("commands.getstatus() is deprecated", getstatus.getDeprecationMessage()); + } + + public void testFunctionStub() { + myFixture.configureByFile("deprecation/functionStub.py"); + PyFunction getstatus = ((PyFile) myFixture.getFile()).findTopLevelFunction("getstatus"); + assertEquals("commands.getstatus() is deprecated", getstatus.getDeprecationMessage()); + assertNotParsed((PyFile) myFixture.getFile()); + } + + public void testDeprecatedProperty() { + myFixture.enableInspections(PyDeprecationInspection.class); + myFixture.configureByFile("deprecation/deprecatedProperty.py"); + myFixture.checkHighlighting(true, false, false); + } + + public void testDeprecatedImport() { + myFixture.enableInspections(PyDeprecationInspection.class); + myFixture.configureByFiles("deprecation/deprecatedImport.py", "deprecation/deprecatedModule.py"); + myFixture.checkHighlighting(true, false, false); + } + + public void testFileStub() { + myFixture.configureByFile("deprecation/deprecatedModule.py"); + assertEquals("the deprecated module is deprecated; use a non-deprecated module instead", ((PyFile) myFixture.getFile()).getDeprecationMessage()); + assertNotParsed((PyFile) myFixture.getFile()); + } +} diff --git a/python/testSrc/com/jetbrains/python/PyStubsTest.java b/python/testSrc/com/jetbrains/python/PyStubsTest.java index 0559b8278ea4..0832f25a2e8e 100644 --- a/python/testSrc/com/jetbrains/python/PyStubsTest.java +++ b/python/testSrc/com/jetbrains/python/PyStubsTest.java @@ -28,17 +28,12 @@ import java.util.List; @TestDataPath("$CONTENT_ROOT/../testData/stubs/") public class PyStubsTest extends PyLightFixtureTestCase { - private static final String PARSED_ERROR_MSG = "Operations should have been performed on stubs but caused file to be parsed"; @Override protected String getTestDataPath() { return PythonTestUtil.getTestDataPath() + "/stubs/"; } - private static void assertNotParsed(PyFile file) { - assertNull(PARSED_ERROR_MSG, ((PyFileImpl)file).getTreeElement()); - } - public void testStubStructure() { final PyFile file = getTestFile(); // vfile is problematic, but we need an SDK to check builtins diff --git a/python/testSrc/com/jetbrains/python/PythonAllTestsSuite.java b/python/testSrc/com/jetbrains/python/PythonAllTestsSuite.java index 493985153e02..0fd915c19322 100644 --- a/python/testSrc/com/jetbrains/python/PythonAllTestsSuite.java +++ b/python/testSrc/com/jetbrains/python/PythonAllTestsSuite.java @@ -80,7 +80,8 @@ public class PythonAllTestsSuite { PyCompletionPatternsTest.class, PyCompatibilityInspectionTest.class, PyUnresolvedReferencesInspectionTest.class, - PyUnusedImportTest.class + PyUnusedImportTest.class, + PyDeprecationTest.class }; public static TestSuite suite() { diff --git a/python/testSrc/com/jetbrains/python/fixtures/PyLightFixtureTestCase.java b/python/testSrc/com/jetbrains/python/fixtures/PyLightFixtureTestCase.java index a69b2b0e66f3..e52fd134c8b4 100644 --- a/python/testSrc/com/jetbrains/python/fixtures/PyLightFixtureTestCase.java +++ b/python/testSrc/com/jetbrains/python/fixtures/PyLightFixtureTestCase.java @@ -24,6 +24,8 @@ import com.intellij.testFramework.fixtures.impl.LightTempDirTestFixtureImpl; import com.jetbrains.python.PythonMockSdk; import com.jetbrains.python.PythonTestUtil; import com.jetbrains.python.psi.LanguageLevel; +import com.jetbrains.python.psi.PyFile; +import com.jetbrains.python.psi.impl.PyFileImpl; import com.jetbrains.python.psi.impl.PythonLanguageLevelPusher; import org.jetbrains.annotations.Nullable; @@ -34,6 +36,7 @@ import org.jetbrains.annotations.Nullable; public abstract class PyLightFixtureTestCase extends UsefulTestCase { private static final PyLightProjectDescriptor ourPyDescriptor = new PyLightProjectDescriptor("2.5"); protected static final PyLightProjectDescriptor ourPy3Descriptor = new PyLightProjectDescriptor("3.1"); + private static final String PARSED_ERROR_MSG = "Operations should have been performed on stubs but caused file to be parsed"; protected CodeInsightTestFixture myFixture; private static boolean ourPlatformPrefixInitialized; @@ -86,6 +89,10 @@ public abstract class PyLightFixtureTestCase extends UsefulTestCase { PythonLanguageLevelPusher.setForcedLanguageLevel(myFixture.getProject(), languageLevel); } + protected static void assertNotParsed(PyFile file) { + assertNull(PARSED_ERROR_MSG, ((PyFileImpl)file).getTreeElement()); + } + protected static class PyLightProjectDescriptor implements LightProjectDescriptor { private final String myPythonVersion;