From 172a3bbcbada1b3765e49f0d3e57f871358b6346 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Thu, 1 Jul 2010 16:28:49 +0400 Subject: [PATCH] recognize new-style classes based on __metaclass__ declaration --- .../python/psi/impl/PyClassImpl.java | 34 +++++- python/testData/resolve/Class.py | 3 +- python/testData/resolve/SuperMetaClass.py | 12 ++ .../python/PyMultiFileResolveTest.java | 17 --- .../com/jetbrains/python/PyResolveTest.java | 110 ++++++++++-------- .../python/fixtures/PyResolveTestCase.java | 26 ++++- 6 files changed, 130 insertions(+), 72 deletions(-) create mode 100644 python/testData/resolve/SuperMetaClass.py diff --git a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java index 98dca1f1758f..a4eb0fd8198d 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java @@ -9,7 +9,7 @@ import com.intellij.psi.ResolveState; import com.intellij.psi.StubBasedPsiElement; import com.intellij.psi.scope.PsiScopeProcessor; import com.intellij.psi.stubs.StubElement; -import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.*; import com.intellij.reference.SoftReference; import com.intellij.util.Icons; import com.intellij.util.IncorrectOperationException; @@ -44,13 +44,23 @@ public class PyClassImpl extends PyPresentableElementImpl implement public static final PyClass[] EMPTY_ARRAY = new PyClassImpl[0]; private PyTargetExpression[] myInstanceAttributes; + private final CachedValue myNewStyle; + + private class NewStyleCachedValueProvider implements CachedValueProvider { + @Override + public Result compute() { + return new Result(calculateNewStyleClass(), PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT); + } + } public PyClassImpl(ASTNode astNode) { super(astNode); + myNewStyle = CachedValuesManager.getManager(getProject()).createCachedValue(new NewStyleCachedValueProvider(), false); } public PyClassImpl(final PyClassStub stub) { super(stub, PyElementTypes.CLASS_DECLARATION); + myNewStyle = CachedValuesManager.getManager(getProject()).createCachedValue(new NewStyleCachedValueProvider(), false); } public PsiElement setName(@NotNull String name) throws IncorrectOperationException { @@ -656,10 +666,32 @@ public class PyClassImpl extends PyPresentableElementImpl implement } public boolean isNewStyleClass() { + return myNewStyle.getValue(); + } + + private boolean calculateNewStyleClass() { PyClass objclass = PyBuiltinCache.getInstance(this).getClass("object"); if (this == objclass) return true; // a rare but possible case + if (hasNewStyleMetaClass(this)) return true; for (PyClass ancestor : iterateAncestors()) { if (ancestor == objclass) return true; + if (hasNewStyleMetaClass(ancestor)) { + return true; + } + } + return false; + } + + private static boolean hasNewStyleMetaClass(PyClass pyClass) { + final PsiFile containingFile = pyClass.getContainingFile(); + if (containingFile instanceof PyFile) { + final PsiElement element = ((PyFile)containingFile).findExportedName("__metaclass__"); + if (element instanceof PyTargetExpression) { + final PyExpression assignedValue = ((PyTargetExpression)element).findAssignedValue(); + if (assignedValue != null && assignedValue.getText().equals("type")) { + return true; + } + } } return false; } diff --git a/python/testData/resolve/Class.py b/python/testData/resolve/Class.py index b9798dca07ac..0383d158cca1 100644 --- a/python/testData/resolve/Class.py +++ b/python/testData/resolve/Class.py @@ -1,3 +1,4 @@ class Test: pass -print Test() \ No newline at end of file +print Test() +# \ No newline at end of file diff --git a/python/testData/resolve/SuperMetaClass.py b/python/testData/resolve/SuperMetaClass.py new file mode 100644 index 000000000000..cb58231a416f --- /dev/null +++ b/python/testData/resolve/SuperMetaClass.py @@ -0,0 +1,12 @@ +__metaclass__ = type + +class A: + def foo(self): + print "foo" + +class B(A): + def foo(self): + super(B, self).foo() +# + +B().foo() diff --git a/python/testSrc/com/jetbrains/python/PyMultiFileResolveTest.java b/python/testSrc/com/jetbrains/python/PyMultiFileResolveTest.java index 70bc9fc1bf82..b01a5d611813 100644 --- a/python/testSrc/com/jetbrains/python/PyMultiFileResolveTest.java +++ b/python/testSrc/com/jetbrains/python/PyMultiFileResolveTest.java @@ -1,6 +1,5 @@ package com.jetbrains.python; -import com.intellij.openapi.editor.Document; import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.fileTypes.FileTypeManager; import com.intellij.openapi.vfs.VirtualFile; @@ -226,20 +225,4 @@ public class PyMultiFileResolveTest extends PyResolveTestCase { final PsiPolyVariantReference ref = (PsiPolyVariantReference)psiFile.findReferenceAt(offset); return ref.multiResolve(false); } - - private int findMarkerOffset(final PsiFile psiFile) { - Document document = PsiDocumentManager.getInstance(myFixture.getProject()).getDocument(psiFile); - assert document != null; - int offset = -1; - for (int i=1; i"); - if (index>0) { - offset = document.getLineStartOffset(i-1) + index; - } - } - assert offset != -1; - return offset; - } } diff --git a/python/testSrc/com/jetbrains/python/PyResolveTest.java b/python/testSrc/com/jetbrains/python/PyResolveTest.java index c332084c844d..5908cb10ddf6 100644 --- a/python/testSrc/com/jetbrains/python/PyResolveTest.java +++ b/python/testSrc/com/jetbrains/python/PyResolveTest.java @@ -12,7 +12,17 @@ import com.jetbrains.python.psi.impl.PyBuiltinCache; import com.jetbrains.python.psi.resolve.ImportedResolveResult; public class PyResolveTest extends PyResolveTestCase { - protected PsiElement doResolve() { + @Override + protected PsiElement doResolve() throws Exception { + myFixture.configureByFile("resolve/" + getTestName(false) + ".py"); + final Project project = myFixture.getProject(); + project.putUserData(PyBuiltinCache.TEST_SDK, PythonMockSdk.findOrCreate()); + int offset = findMarkerOffset(myFixture.getFile()); + final PsiReference ref = myFixture.getFile().findReferenceAt(offset); + return ref.resolve(); + } + + protected PsiElement resolve() { PsiReference ref = configureByFile("resolve/" + getTestName(false) + ".py"); final Project project = ref.getElement().getContainingFile().getProject(); project.putUserData(PyBuiltinCache.TEST_SDK, PythonMockSdk.findOrCreate()); @@ -31,12 +41,12 @@ public class PyResolveTest extends PyResolveTestCase { } public void testFunc() { - PsiElement targetElement = doResolve(); + PsiElement targetElement = resolve(); assertTrue(targetElement instanceof PyFunction); } public void testToConstructor() { - PsiElement target = doResolve(); + PsiElement target = resolve(); assertTrue(target instanceof PyFunction); assertEquals(((PyFunction)target).getName(), PyNames.INIT); } @@ -60,7 +70,7 @@ public class PyResolveTest extends PyResolveTestCase { } // NOTE: maybe this test does not belong exactly here; still it's the best place currently. public void testComplexCallee() { - PsiElement targetElement = doResolve(); + PsiElement targetElement = resolve(); PyExpression assigned = ((PyAssignmentStatement)targetElement.getContext()).getAssignedValue(); assertTrue(assigned instanceof PyCallExpression); PsiElement callee = ((PyCallExpression)assigned).getCallee(); @@ -68,191 +78,191 @@ public class PyResolveTest extends PyResolveTestCase { } public void testVar() { - PsiElement targetElement = doResolve(); + PsiElement targetElement = resolve(); assertTrue(targetElement instanceof PyTargetExpression); } public void testDefaultInClass() { - PsiElement targetElement = doResolve(); + PsiElement targetElement = resolve(); assertNotNull(targetElement); assertTrue(targetElement instanceof PyTargetExpression); assertEquals(((PyTargetExpression)targetElement).getName(), "FOO"); } public void testQualifiedFunc() { - PsiElement targetElement = doResolve(); + PsiElement targetElement = resolve(); assertTrue(targetElement instanceof PyFunction); } public void testQualifiedVar() { - PsiElement targetElement = doResolve(); + PsiElement targetElement = resolve(); assertTrue(targetElement instanceof PyTargetExpression); } public void testQualifiedTarget() { - PsiElement targetElement = doResolve(); + PsiElement targetElement = resolve(); assertTrue(targetElement instanceof PyTargetExpression); } public void testQualifiedFalseTarget() { - PsiElement targetElement = doResolve(); + PsiElement targetElement = resolve(); assertNull(targetElement); } public void testInnerFuncVar() { - PsiElement targetElement = doResolve(); + PsiElement targetElement = resolve(); assertTrue(targetElement instanceof PyTargetExpression); } public void testTupleInComprh() { - PsiElement targetElement = doResolve(); + PsiElement targetElement = resolve(); assertTrue(targetElement instanceof PyTargetExpression); } public void testForStatement() { - PsiElement targetElement = doResolve(); + PsiElement targetElement = resolve(); assertTrue(targetElement instanceof PyTargetExpression); } public void testExceptClause() { - PsiElement targetElement = doResolve(); + PsiElement targetElement = resolve(); assertTrue(targetElement instanceof PyTargetExpression); } public void testLookAhead() { - PsiElement targetElement = doResolve(); + PsiElement targetElement = resolve(); assertTrue(targetElement instanceof PyTargetExpression); } public void testLookAheadCapped() { - PsiElement targetElement = doResolve(); + PsiElement targetElement = resolve(); assertTrue(targetElement instanceof PyTargetExpression); } public void testTryExceptElse() { - PsiElement targetElement = doResolve(); + PsiElement targetElement = resolve(); assertTrue(targetElement instanceof PyTargetExpression); } public void testGlobal() { - PsiElement targetElement = doResolve(); + PsiElement targetElement = resolve(); assertTrue(targetElement instanceof PyTargetExpression); assertTrue(targetElement.getParent() instanceof PyAssignmentStatement); } public void testLambda() { - PsiElement targetElement = doResolve(); + PsiElement targetElement = resolve(); assertTrue(targetElement instanceof PyNamedParameter); } public void testLambdaParameterOutside() { - PsiElement targetElement = doResolve(); + PsiElement targetElement = resolve(); assertTrue(targetElement instanceof PyTargetExpression); } public void testSuperField() { - PsiElement targetElement = doResolve(); + PsiElement targetElement = resolve(); assertTrue(targetElement instanceof PyTargetExpression); } public void testFieldInCondition() { - PsiElement targetElement = doResolve(); + PsiElement targetElement = resolve(); assertTrue(targetElement instanceof PyTargetExpression); } public void testMultipleFields() { - PsiElement targetElement = doResolve(); + PsiElement targetElement = resolve(); assertTrue(targetElement instanceof PyTargetExpression); } public void testClassPeerMembers() { - PsiElement target = doResolve(); + PsiElement target = resolve(); assertTrue(target instanceof PyFunction); } public void testTuple() { - PsiElement targetElement = doResolve(); + PsiElement targetElement = resolve(); assertTrue(targetElement instanceof PyTargetExpression); assertTrue(targetElement.getParent() instanceof PyAssignmentStatement); } public void testMultiTarget() { - PsiElement targetElement = doResolve(); + PsiElement targetElement = resolve(); assertTrue(targetElement instanceof PyTargetExpression); assertTrue(targetElement.getParent() instanceof PyAssignmentStatement); } public void testMultiTargetTuple() { - PsiElement targetElement = doResolve(); + PsiElement targetElement = resolve(); assertTrue(targetElement instanceof PyTargetExpression); assertNotNull(PsiTreeUtil.getParentOfType(targetElement, PyAssignmentStatement.class)); // it's deep in a tuple } public void testWithStatement() { - PsiElement targetElement = doResolve(); + PsiElement targetElement = resolve(); assertTrue(targetElement instanceof PyTargetExpression); assertTrue(targetElement.getParent() instanceof PyWithItem); } public void testTupleInExcept() { - PsiElement targetElement = doResolve(); + PsiElement targetElement = resolve(); assertTrue(targetElement instanceof PyTargetExpression); assertTrue(PsiTreeUtil.getParentOfType(targetElement, PyExceptPart.class) != null); } public void testDocStringClass() { - PsiElement targetElement = doResolve(); + PsiElement targetElement = resolve(); assertTrue(targetElement instanceof PyStringLiteralExpression); assertEquals("Docstring of class Foo", ((PyStringLiteralExpression)targetElement).getStringValue()); } public void testDocStringInstance() { - PsiElement targetElement = doResolve(); + PsiElement targetElement = resolve(); assertTrue(targetElement instanceof PyStringLiteralExpression); assertEquals("Docstring of class Foo", ((PyStringLiteralExpression)targetElement).getStringValue()); } public void testDocStringFunction() { - PsiElement targetElement = doResolve(); + PsiElement targetElement = resolve(); assertTrue(targetElement instanceof PyStringLiteralExpression); assertEquals("Docstring of function bar", ((PyStringLiteralExpression)targetElement).getStringValue()); } public void testDocStringInvalid() { - PsiElement targetElement = doResolve(); + PsiElement targetElement = resolve(); assertNull(targetElement); } public void testFieldNotInInit() { - PsiElement targetElement = doResolve(); + PsiElement targetElement = resolve(); assertTrue(targetElement instanceof PyTargetExpression); } public void testClassIsNotMemberOfItself() { - PsiElement targetElement = doResolve(); + PsiElement targetElement = resolve(); assertNull(targetElement); } public void testSuper() { - PsiElement targetElement = doResolve(); + PsiElement targetElement = resolve(); assertTrue(targetElement instanceof PyFunction); assertEquals("A", ((PyFunction) targetElement).getContainingClass().getName()); } public void testStackOverflow() { - PsiElement targetElement = doResolve(); + PsiElement targetElement = resolve(); assertNull(targetElement); } public void testProperty() { - PsiElement targetElement = doResolve(); + PsiElement targetElement = resolve(); assertTrue(targetElement instanceof PyFunction); assertEquals("set_full_name", ((PyFunction)targetElement).getName()); } public void testLambdaWithParens() { // PY-882 - PsiElement targetElement = doResolve(); + PsiElement targetElement = resolve(); assertTrue(targetElement instanceof PyParameter); } @@ -263,41 +273,41 @@ public class PyResolveTest extends PyResolveTestCase { } public void testClassPrivateInClass() { - PsiElement targetElement = doResolve(); + PsiElement targetElement = resolve(); assertTrue(targetElement instanceof PyTargetExpression); assertTrue(targetElement.getParent() instanceof PyAssignmentStatement); } public void testClassPrivateInMethod() { - PsiElement targetElement = doResolve(); + PsiElement targetElement = resolve(); assertTrue(targetElement instanceof PyTargetExpression); assertTrue(targetElement.getParent() instanceof PyAssignmentStatement); } public void testClassPrivateInMethodNested() { - PsiElement targetElement = doResolve(); + PsiElement targetElement = resolve(); assertTrue(targetElement instanceof PyTargetExpression); assertTrue(targetElement.getParent() instanceof PyAssignmentStatement); } public void testClassPrivateInherited() { - PsiElement targetElement = doResolve(); + PsiElement targetElement = resolve(); assertTrue(targetElement instanceof PyTargetExpression); assertTrue(targetElement.getParent() instanceof PyAssignmentStatement); } public void testClassPrivateOutsideClass() { - PsiElement targetElement = doResolve(); + PsiElement targetElement = resolve(); assertNull(targetElement); } public void testClassPrivateOutsideInstance() { - PsiElement targetElement = doResolve(); + PsiElement targetElement = resolve(); assertNull(targetElement); } public void testClassNameEqualsMethodName() { - PsiElement targetElement = doResolve(); + PsiElement targetElement = resolve(); assertInstanceOf(targetElement, PyFunction.class); } @@ -310,12 +320,16 @@ public class PyResolveTest extends PyResolveTestCase { } public void testIsInstance() { // PY-1133 - PsiElement targetElement = doResolve(); + PsiElement targetElement = resolve(); assertInstanceOf(targetElement, PyNamedParameter.class); } public void testListComprehension() { // PY-1143 - PsiElement targetElement = doResolve(); + PsiElement targetElement = resolve(); assertInstanceOf(targetElement, PyTargetExpression.class); } + + public void testSuperMetaClass() { + assertResolvesTo(PyFunction.class, "foo"); + } } \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/fixtures/PyResolveTestCase.java b/python/testSrc/com/jetbrains/python/fixtures/PyResolveTestCase.java index 9f1515b947ac..693854b629c5 100644 --- a/python/testSrc/com/jetbrains/python/fixtures/PyResolveTestCase.java +++ b/python/testSrc/com/jetbrains/python/fixtures/PyResolveTestCase.java @@ -1,13 +1,12 @@ package com.jetbrains.python.fixtures; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.editor.Document; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiNamedElement; -import com.intellij.psi.PsiReference; +import com.intellij.psi.*; import com.intellij.testFramework.TestDataFile; import org.jetbrains.annotations.NonNls; @@ -20,6 +19,7 @@ import java.io.IOException; public abstract class PyResolveTestCase extends PyLightFixtureTestCase { @NonNls protected static final String MARKER = ""; + protected PsiReference configureByFile(@TestDataFile final String filePath) { VirtualFile testDataRoot = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(new File(getTestDataPath())); final VirtualFile file = testDataRoot.findFileByRelativePath(filePath); @@ -57,8 +57,8 @@ public abstract class PyResolveTestCase extends PyLightFixtureTestCase { } protected void assertResolvesTo(final Class aClass, - final String name, - String containingFilePath) { + final String name, + String containingFilePath) { final PsiElement element; try { element = doResolve(); @@ -72,4 +72,20 @@ public abstract class PyResolveTestCase extends PyLightFixtureTestCase { assertEquals(containingFilePath, element.getContainingFile().getVirtualFile().getPath()); } } + + protected int findMarkerOffset(final PsiFile psiFile) { + Document document = PsiDocumentManager.getInstance(myFixture.getProject()).getDocument(psiFile); + assert document != null; + int offset = -1; + for (int i=1; i"); + if (index>0) { + offset = document.getLineStartOffset(i-1) + index; + } + } + assert offset != -1; + return offset; + } }