recognize new-style classes based on __metaclass__ declaration

This commit is contained in:
Dmitry Jemerov
2010-07-01 21:53:36 +04:00
parent 9735f9facd
commit 172a3bbcba
6 changed files with 130 additions and 72 deletions
@@ -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<PyClassStub> implement
public static final PyClass[] EMPTY_ARRAY = new PyClassImpl[0];
private PyTargetExpression[] myInstanceAttributes;
private final CachedValue<Boolean> myNewStyle;
private class NewStyleCachedValueProvider implements CachedValueProvider<Boolean> {
@Override
public Result<Boolean> compute() {
return new Result<Boolean>(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<PyClassStub> 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;
}
+2 -1
View File
@@ -1,3 +1,4 @@
class Test:
pass
print <ref>Test()
print Test()
# <ref>
+12
View File
@@ -0,0 +1,12 @@
__metaclass__ = type
class A:
def foo(self):
print "foo"
class B(A):
def foo(self):
super(B, self).foo()
# <ref>
B().foo()
@@ -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<document.getLineCount(); i++) {
int lineStart = document.getLineStartOffset(i);
int lineEnd = document.getLineEndOffset(i);
final int index=document.getCharsSequence().subSequence(lineStart, lineEnd).toString().indexOf("<ref>");
if (index>0) {
offset = document.getLineStartOffset(i-1) + index;
}
}
assert offset != -1;
return offset;
}
}
@@ -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");
}
}
@@ -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 = "<ref>";
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 <T extends PsiElement> void assertResolvesTo(final Class<T> 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<document.getLineCount(); i++) {
int lineStart = document.getLineStartOffset(i);
int lineEnd = document.getLineEndOffset(i);
final int index=document.getCharsSequence().subSequence(lineStart, lineEnd).toString().indexOf("<ref>");
if (index>0) {
offset = document.getLineStartOffset(i-1) + index;
}
}
assert offset != -1;
return offset;
}
}