mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
recognize new-style classes based on __metaclass__ declaration
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
class Test:
|
||||
pass
|
||||
print <ref>Test()
|
||||
print Test()
|
||||
# <ref>
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user