From be280d0e044666d4fc679f66a7f444c9e42ea767 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Wed, 14 Aug 2013 17:41:21 +0400 Subject: [PATCH 1/9] Allow stub->AST in TypeEvalContext only if explicitly allowed or in the origin file --- .../src/com/jetbrains/python/psi/types/TypeEvalContext.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/psi-api/src/com/jetbrains/python/psi/types/TypeEvalContext.java b/python/psi-api/src/com/jetbrains/python/psi/types/TypeEvalContext.java index 2a5c2b475f5d..03bb1ac60102 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/types/TypeEvalContext.java +++ b/python/psi-api/src/com/jetbrains/python/psi/types/TypeEvalContext.java @@ -159,7 +159,7 @@ public class TypeEvalContext { } public boolean maySwitchToAST(@NotNull StubBasedPsiElement element) { - return myAllowStubToAST || (element.getStub() == null && (myOrigin == null || myOrigin == element.getContainingFile())); + return myAllowStubToAST || myOrigin == element.getContainingFile(); } @Nullable From b0f6a39540f1e870e90a001b939c3015e3f89fc1 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 14 Aug 2013 16:39:37 +0200 Subject: [PATCH 2/9] introduce PyQualifiedNameOwner interface --- .../com/jetbrains/python/psi/Callable.java | 10 +--------- .../src/com/jetbrains/python/psi/PyClass.java | 5 +---- .../python/psi/PyQualifiedNameOwner.java | 19 +++++++++++++++++++ .../python/PyGotoSymbolContributor.java | 18 +++--------------- 4 files changed, 24 insertions(+), 28 deletions(-) create mode 100644 python/psi-api/src/com/jetbrains/python/psi/PyQualifiedNameOwner.java diff --git a/python/psi-api/src/com/jetbrains/python/psi/Callable.java b/python/psi-api/src/com/jetbrains/python/psi/Callable.java index c156564506a3..fd1b6abddd6b 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/Callable.java +++ b/python/psi-api/src/com/jetbrains/python/psi/Callable.java @@ -10,7 +10,7 @@ import org.jetbrains.annotations.Nullable; * @author dcheryasov */ -public interface Callable extends PyTypedElement { +public interface Callable extends PyTypedElement, PyQualifiedNameOwner { /** * @return a list of parameters passed to this callable, possibly empty. @@ -29,12 +29,4 @@ public interface Callable extends PyTypedElement { */ @Nullable PyFunction asMethod(); - - /** - * Returns the qualified name of the function. - * - * @return the qualified name of the function, or null for a lambda expression. - */ - @Nullable - String getQualifiedName(); } diff --git a/python/psi-api/src/com/jetbrains/python/psi/PyClass.java b/python/psi-api/src/com/jetbrains/python/psi/PyClass.java index 4b34a1635377..bdc69149a8d5 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/PyClass.java +++ b/python/psi-api/src/com/jetbrains/python/psi/PyClass.java @@ -21,7 +21,7 @@ import java.util.List; * Represents a class declaration in source. */ public interface PyClass extends PsiNameIdentifierOwner, PyStatement, NameDefiner, PyDocStringOwner, StubBasedPsiElement, - ScopeOwner, PyDecoratable, PyTypedElement { + ScopeOwner, PyDecoratable, PyTypedElement, PyQualifiedNameOwner { ArrayFactory ARRAY_FACTORY = new ArrayFactory() { @NotNull @Override @@ -149,9 +149,6 @@ public interface PyClass extends PsiNameIdentifierOwner, PyStatement, NameDefine boolean isSubclass(@NotNull String superClassQName); - @Nullable - String getQualifiedName(); - /** * Returns the list of names in the class' __slots__ attribute, or null if the class * does not define such an attribute. diff --git a/python/psi-api/src/com/jetbrains/python/psi/PyQualifiedNameOwner.java b/python/psi-api/src/com/jetbrains/python/psi/PyQualifiedNameOwner.java new file mode 100644 index 000000000000..ad915f78a17f --- /dev/null +++ b/python/psi-api/src/com/jetbrains/python/psi/PyQualifiedNameOwner.java @@ -0,0 +1,19 @@ +package com.jetbrains.python.psi; + +import org.jetbrains.annotations.Nullable; + +/** + * Base class for elements that have a qualified name (classes and functions). + * + * @author yole + */ +public interface PyQualifiedNameOwner extends PyElement { + /** + * Returns the qualified name of the element. + * + * @return the qualified name of the element, or null if the element doesn't have a name (for example, it is a lambda expression) or + * is contained inside an element that doesn't have a qualified name. + */ + @Nullable + String getQualifiedName(); +} diff --git a/python/src/com/jetbrains/python/PyGotoSymbolContributor.java b/python/src/com/jetbrains/python/PyGotoSymbolContributor.java index dac92071202c..3c4c9f5ff51a 100644 --- a/python/src/com/jetbrains/python/PyGotoSymbolContributor.java +++ b/python/src/com/jetbrains/python/PyGotoSymbolContributor.java @@ -3,13 +3,10 @@ package com.jetbrains.python; import com.intellij.navigation.GotoClassContributor; import com.intellij.navigation.NavigationItem; import com.intellij.openapi.project.Project; -import com.intellij.psi.PsiFile; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.stubs.StubIndex; import com.intellij.util.ArrayUtil; -import com.jetbrains.python.psi.PyClass; -import com.jetbrains.python.psi.PyFunction; -import com.jetbrains.python.psi.impl.PyPresentableElementImpl; +import com.jetbrains.python.psi.PyQualifiedNameOwner; import com.jetbrains.python.psi.search.PyProjectScopeBuilder; import com.jetbrains.python.psi.stubs.PyClassNameIndex; import com.jetbrains.python.psi.stubs.PyFunctionNameIndex; @@ -50,17 +47,8 @@ public class PyGotoSymbolContributor implements GotoClassContributor { @Override public String getQualifiedName(NavigationItem item) { - if (item instanceof PyClass) { - return ((PyClass) item).getQualifiedName(); - } - if (item instanceof PyFunction) { - PyFunction function = (PyFunction) item; - final PyClass containingClass = function.getContainingClass(); - if (containingClass != null) { - return containingClass.getQualifiedName() + "." + function.getName(); - } - PsiFile file = function.getContainingFile(); - return PyPresentableElementImpl.getPackageForFile(file) + "." + function.getName(); + if (item instanceof PyQualifiedNameOwner) { + return ((PyQualifiedNameOwner) item).getQualifiedName(); } return null; } From 95f758bb5414c603d9ea967babe5a81237efdcf0 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 14 Aug 2013 17:24:11 +0200 Subject: [PATCH 3/9] class name completion in string literals inserts qualified names after imports; disable class name completion after dot in string literals (PY-10526) --- .../PyClassNameCompletionContributor.java | 38 ++++++++++++++++--- .../stringLiteral/mypackage/__init__.py | 3 ++ .../stringLiteral/stringLiteral.after.py | 1 + .../className/stringLiteral/stringLiteral.py | 1 + .../mypackage/__init__.py | 3 ++ .../stringLiteralAfterDot.py | 1 + .../python/PyClassNameCompletionTest.java | 3 ++ 7 files changed, 44 insertions(+), 6 deletions(-) create mode 100644 python/testData/completion/className/stringLiteral/mypackage/__init__.py create mode 100644 python/testData/completion/className/stringLiteral/stringLiteral.after.py create mode 100644 python/testData/completion/className/stringLiteral/stringLiteral.py create mode 100644 python/testData/completion/className/stringLiteralAfterDot/mypackage/__init__.py create mode 100644 python/testData/completion/className/stringLiteralAfterDot/stringLiteralAfterDot.py diff --git a/python/src/com/jetbrains/python/codeInsight/completion/PyClassNameCompletionContributor.java b/python/src/com/jetbrains/python/codeInsight/completion/PyClassNameCompletionContributor.java index 552aa0025153..c93209b05b12 100644 --- a/python/src/com/jetbrains/python/codeInsight/completion/PyClassNameCompletionContributor.java +++ b/python/src/com/jetbrains/python/codeInsight/completion/PyClassNameCompletionContributor.java @@ -42,20 +42,31 @@ public class PyClassNameCompletionContributor extends CompletionContributor { if (parent instanceof PyReferenceExpression && ((PyReferenceExpression)parent).getQualifier() != null) { return; } + if (parent instanceof PyStringLiteralExpression) { + String prefix = parent.getText().substring(0, parameters.getOffset() - parent.getTextRange().getStartOffset()); + if (prefix.contains(".")) { + return; + } + } final FileViewProvider provider = element.getContainingFile().getViewProvider(); if (provider instanceof MultiplePsiFilesPerDocumentFileViewProvider) return; if (PsiTreeUtil.getParentOfType(element, PyImportStatementBase.class) != null) { return; } final PsiFile originalFile = parameters.getOriginalFile(); - addVariantsFromIndex(result, originalFile, PyClassNameIndex.KEY, IMPORTING_INSERT_HANDLER, Conditions.alwaysTrue()); - addVariantsFromIndex(result, originalFile, PyFunctionNameIndex.KEY, FUNCTION_INSERT_HANDLER, IS_TOPLEVEL); - addVariantsFromIndex(result, originalFile, PyVariableNameIndex.KEY, IMPORTING_INSERT_HANDLER, IS_TOPLEVEL); - addVariantsFromModules(result, originalFile); + addVariantsFromIndex(result, originalFile, PyClassNameIndex.KEY, + parent instanceof PyStringLiteralExpression ? STRING_LITERAL_INSERT_HANDLER : IMPORTING_INSERT_HANDLER, + Conditions.alwaysTrue()); + addVariantsFromIndex(result, originalFile, PyFunctionNameIndex.KEY, + parent instanceof PyStringLiteralExpression ? STRING_LITERAL_INSERT_HANDLER : FUNCTION_INSERT_HANDLER, IS_TOPLEVEL); + addVariantsFromIndex(result, originalFile, PyVariableNameIndex.KEY, + parent instanceof PyStringLiteralExpression ? STRING_LITERAL_INSERT_HANDLER : IMPORTING_INSERT_HANDLER, + IS_TOPLEVEL); + addVariantsFromModules(result, originalFile, parent instanceof PyStringLiteralExpression); } } - private static void addVariantsFromModules(CompletionResultSet result, PsiFile targetFile) { + private static void addVariantsFromModules(CompletionResultSet result, PsiFile targetFile, boolean inStringLiteral) { Collection files = FileTypeIndex.getFiles(PythonFileType.INSTANCE, PyProjectScopeBuilder.excludeSdkTestsScope(targetFile)); for (VirtualFile file : files) { PsiFile pyFile = targetFile.getManager().findFile(file); @@ -64,7 +75,7 @@ public class PyClassNameCompletionContributor extends CompletionContributor { if (PythonReferenceImporter.isImportableModule(targetFile, importable)) { LookupElementBuilder element = PyModuleType.buildFileLookupElement(importable, null); if (element != null) { - result.addElement(element.withInsertHandler(IMPORTING_INSERT_HANDLER)); + result.addElement(element.withInsertHandler(inStringLiteral ? STRING_LITERAL_INSERT_HANDLER : IMPORTING_INSERT_HANDLER)); } } } @@ -113,6 +124,21 @@ public class PyClassNameCompletionContributor extends CompletionContributor { } }; + private static final InsertHandler STRING_LITERAL_INSERT_HANDLER = new InsertHandler() { + @Override + public void handleInsert(InsertionContext context, LookupElement item) { + PsiElement element = item.getPsiElement(); + if (element instanceof PyQualifiedNameOwner) { + String qName = ((PyQualifiedNameOwner) element).getQualifiedName(); + String name = ((PyQualifiedNameOwner) element).getName(); + if (qName != null && name != null) { + String qNamePrefix = qName.substring(0, qName.length()-name.length()); + context.getDocument().insertString(context.getStartOffset(), qNamePrefix); + } + } + } + }; + private static void addImportForLookupElement(final InsertionContext context, final LookupElement item, final int tailOffset) { PsiDocumentManager manager = PsiDocumentManager.getInstance(context.getProject()); Document document = manager.getDocument(context.getFile()); diff --git a/python/testData/completion/className/stringLiteral/mypackage/__init__.py b/python/testData/completion/className/stringLiteral/mypackage/__init__.py new file mode 100644 index 000000000000..8c1d291974c8 --- /dev/null +++ b/python/testData/completion/className/stringLiteral/mypackage/__init__.py @@ -0,0 +1,3 @@ +class Shazam: + pass + diff --git a/python/testData/completion/className/stringLiteral/stringLiteral.after.py b/python/testData/completion/className/stringLiteral/stringLiteral.after.py new file mode 100644 index 000000000000..dc6863342be5 --- /dev/null +++ b/python/testData/completion/className/stringLiteral/stringLiteral.after.py @@ -0,0 +1 @@ +x = 'mypackage.Shazam' diff --git a/python/testData/completion/className/stringLiteral/stringLiteral.py b/python/testData/completion/className/stringLiteral/stringLiteral.py new file mode 100644 index 000000000000..492d73d8024d --- /dev/null +++ b/python/testData/completion/className/stringLiteral/stringLiteral.py @@ -0,0 +1 @@ +x = 'Shaz' diff --git a/python/testData/completion/className/stringLiteralAfterDot/mypackage/__init__.py b/python/testData/completion/className/stringLiteralAfterDot/mypackage/__init__.py new file mode 100644 index 000000000000..8c1d291974c8 --- /dev/null +++ b/python/testData/completion/className/stringLiteralAfterDot/mypackage/__init__.py @@ -0,0 +1,3 @@ +class Shazam: + pass + diff --git a/python/testData/completion/className/stringLiteralAfterDot/stringLiteralAfterDot.py b/python/testData/completion/className/stringLiteralAfterDot/stringLiteralAfterDot.py new file mode 100644 index 000000000000..d3530c48cbbe --- /dev/null +++ b/python/testData/completion/className/stringLiteralAfterDot/stringLiteralAfterDot.py @@ -0,0 +1 @@ +x = 'mypackage.' diff --git a/python/testSrc/com/jetbrains/python/PyClassNameCompletionTest.java b/python/testSrc/com/jetbrains/python/PyClassNameCompletionTest.java index 2052c3fd41a4..6f2f2e1552e5 100644 --- a/python/testSrc/com/jetbrains/python/PyClassNameCompletionTest.java +++ b/python/testSrc/com/jetbrains/python/PyClassNameCompletionTest.java @@ -42,6 +42,9 @@ public class PyClassNameCompletionTest extends PyTestCase { doTestWithoutFromImport(); } + public void testStringLiteral() { // PY-10526 + doTest(); + } private void doTestWithoutFromImport() { final PyCodeInsightSettings settings = PyCodeInsightSettings.getInstance(); boolean oldValue = settings.PREFER_FROM_IMPORT; From 18f98d49e9b5cca6d3f098c364c57f91b583813d Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 14 Aug 2013 18:15:01 +0200 Subject: [PATCH 4/9] better presentation of template variables in usage view (PY-6999) --- .../impl/PyStringLiteralExpressionImpl.java | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/python/src/com/jetbrains/python/psi/impl/PyStringLiteralExpressionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyStringLiteralExpressionImpl.java index e110791a9699..d45df200fccc 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyStringLiteralExpressionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyStringLiteralExpressionImpl.java @@ -1,8 +1,10 @@ package com.jetbrains.python.psi.impl; +import com.intellij.icons.AllIcons; import com.intellij.lang.ASTNode; import com.intellij.lang.Language; import com.intellij.lang.injection.InjectedLanguageManager; +import com.intellij.navigation.ItemPresentation; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.TextRange; @@ -24,6 +26,7 @@ import org.intellij.lang.regexp.psi.RegExpGroup; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import javax.swing.*; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -304,6 +307,29 @@ public class PyStringLiteralExpressionImpl extends PyElementImpl implements PySt return ReferenceProvidersRegistry.getReferencesFromProviders(this, PsiReferenceService.Hints.NO_HINTS); } + @Override + public ItemPresentation getPresentation() { + return new ItemPresentation() { + @Nullable + @Override + public String getPresentableText() { + return getStringValue(); + } + + @Nullable + @Override + public String getLocationString() { + return "(" + PyPresentableElementImpl.getPackageForFile(getContainingFile()) + ")"; + } + + @Nullable + @Override + public Icon getIcon(boolean unused) { + return AllIcons.Nodes.Variable; + } + }; + } + public PsiLanguageInjectionHost updateText(@NotNull String text) { // TODO is this the correct implementation? most likely not ASTNode valueNode = getNode().getFirstChildNode(); From ec685df54b5478d01e6b6c028af338c787e2316b Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 14 Aug 2013 19:05:01 +0200 Subject: [PATCH 5/9] find usages for django template parameters passed from view functions and class-based views (PY-7000) --- python/src/META-INF/python-plugin-common.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/python/src/META-INF/python-plugin-common.xml b/python/src/META-INF/python-plugin-common.xml index 5398458bf1d6..f7b2fb68e9dd 100644 --- a/python/src/META-INF/python-plugin-common.xml +++ b/python/src/META-INF/python-plugin-common.xml @@ -680,6 +680,10 @@ + + + + From 361aa087811b52719a2827f75f4b406134274bdd Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 14 Aug 2013 19:25:47 +0200 Subject: [PATCH 6/9] honor 'resolve collection items' flag for values assigned via subscription expression (PY-10542) --- .../com/jetbrains/python/psi/impl/PyBlockEvaluator.java | 3 ++- .../testSrc/com/jetbrains/python/PyBlockEvaluatorTest.java | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/python/src/com/jetbrains/python/psi/impl/PyBlockEvaluator.java b/python/src/com/jetbrains/python/psi/impl/PyBlockEvaluator.java index d6a8d48fdbd0..19a9277438ed 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyBlockEvaluator.java +++ b/python/src/com/jetbrains/python/psi/impl/PyBlockEvaluator.java @@ -66,7 +66,8 @@ public class PyBlockEvaluator { if (currentValue instanceof Map) { Object mapKey = prepareEvaluator().evaluate(indexExpression); if (mapKey != null) { - ((Map)currentValue).put(mapKey, prepareEvaluator().evaluate(node.getAssignedValue())); + Object value = myEvaluateCollectionItems ? prepareEvaluator().evaluate(node.getAssignedValue()) : node.getAssignedValue(); + ((Map)currentValue).put(mapKey, value); } } } diff --git a/python/testSrc/com/jetbrains/python/PyBlockEvaluatorTest.java b/python/testSrc/com/jetbrains/python/PyBlockEvaluatorTest.java index 960815bf9ad1..dfacd3d62864 100644 --- a/python/testSrc/com/jetbrains/python/PyBlockEvaluatorTest.java +++ b/python/testSrc/com/jetbrains/python/PyBlockEvaluatorTest.java @@ -68,6 +68,13 @@ public class PyBlockEvaluatorTest extends PyTestCase { assertEquals("c", map.get("b")); } + public void testDictAssignNoEvaluate() { + PyBlockEvaluator eval = doEvaluate("a={}\na['b']='c'", true); + Map map = (Map) eval.getValue("a"); + assertEquals(1, map.size()); + assertTrue(map.get("b") instanceof PyStringLiteralExpression); + } + public void testDictUpdate() { PyBlockEvaluator eval = doEvaluate("a={}\na.update({'b': 'c'})"); Map map = (Map) eval.getValue("a"); From 81458c1cc34203cf8a5a1d73397c6b3b35f43d9e Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Wed, 14 Aug 2013 21:33:42 +0400 Subject: [PATCH 7/9] Allow any PsiElement as an anchor in TypeEvalContext.maySwitchToAST() --- .../src/com/jetbrains/python/psi/types/TypeEvalContext.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/python/psi-api/src/com/jetbrains/python/psi/types/TypeEvalContext.java b/python/psi-api/src/com/jetbrains/python/psi/types/TypeEvalContext.java index 03bb1ac60102..ff0d48ad734e 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/types/TypeEvalContext.java +++ b/python/psi-api/src/com/jetbrains/python/psi/types/TypeEvalContext.java @@ -3,7 +3,6 @@ package com.jetbrains.python.psi.types; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; -import com.intellij.psi.StubBasedPsiElement; import com.jetbrains.python.psi.PyTypedElement; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -158,7 +157,7 @@ public class TypeEvalContext { } } - public boolean maySwitchToAST(@NotNull StubBasedPsiElement element) { + public boolean maySwitchToAST(@NotNull PsiElement element) { return myAllowStubToAST || myOrigin == element.getContainingFile(); } From 25e55b1c1b138d4f624970fa6bd9f08a45554211 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Wed, 14 Aug 2013 21:39:28 +0400 Subject: [PATCH 8/9] Property.getGetter() doesn't depend on stub/AST switch, explicit Property.getType() --- .../com/jetbrains/python/psi/Property.java | 7 +++ .../python/psi/impl/PyClassImpl.java | 49 +++++++++++++++++-- .../psi/impl/PyReferenceExpressionImpl.java | 12 +++-- python/testData/property/Classic.py | 2 +- .../python/PyClassicPropertyTest.java | 5 ++ 5 files changed, 66 insertions(+), 9 deletions(-) diff --git a/python/psi-api/src/com/jetbrains/python/psi/Property.java b/python/psi-api/src/com/jetbrains/python/psi/Property.java index aad101b0a575..60373dd0a0db 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/Property.java +++ b/python/psi-api/src/com/jetbrains/python/psi/Property.java @@ -1,5 +1,7 @@ package com.jetbrains.python.psi; +import com.jetbrains.python.psi.types.PyType; +import com.jetbrains.python.psi.types.TypeEvalContext; import com.jetbrains.python.toolbox.Maybe; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -51,4 +53,9 @@ public interface Property { @NotNull Maybe getByDirection(@NotNull AccessDirection direction); + /** + * Get the return type of the property getter. + */ + @Nullable + PyType getType(@NotNull TypeEvalContext context); } diff --git a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java index 1eb2a9b1fcb4..829cde42f98e 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java @@ -691,6 +691,24 @@ public class PyClassImpl extends PyPresentableElementImpl implement mySite = site; } + @NotNull + @Override + public Maybe getGetter() { + return filterNonStubExpression(myGetter); + } + + @NotNull + @Override + public Maybe getSetter() { + return filterNonStubExpression(mySetter); + } + + @NotNull + @Override + public Maybe getDeleter() { + return filterNonStubExpression(myDeleter); + } + public String getName() { return myName; } @@ -704,15 +722,29 @@ public class PyClassImpl extends PyPresentableElementImpl implement public Maybe getByDirection(@NotNull AccessDirection direction) { switch (direction) { case READ: - return myGetter; + return getGetter(); case WRITE: - return mySetter; + return getSetter(); case DELETE: - return myDeleter; + return getDeleter(); } throw new IllegalArgumentException("Unknown direction " + PyUtil.nvl(direction)); } + @Nullable + @Override + public PyType getType(@NotNull TypeEvalContext context) { + final Callable callable = myGetter.valueOrNull(); + if (callable != null) { + // Ignore return types of non stub-based elements if we are not allowed to use AST + if (!(callable instanceof StubBasedPsiElement) && !context.maySwitchToAST(callable)) { + return null; + } + return callable.getReturnType(context, null); + } + return null; + } + @NotNull @Override protected Maybe translate(@Nullable PyExpression expr) { @@ -733,6 +765,17 @@ public class PyClassImpl extends PyPresentableElementImpl implement return NONE; } + @NotNull + private static Maybe filterNonStubExpression(@NotNull Maybe maybeCallable) { + final Callable callable = maybeCallable.valueOrNull(); + if (callable != null) { + if (!(callable instanceof StubBasedPsiElement)) { + return UNKNOWN_CALL; + } + } + return maybeCallable; + } + public String toString() { return "property(" + myGetter + ", " + mySetter + ", " + myDeleter + ", " + myDoc + ")"; } diff --git a/python/src/com/jetbrains/python/psi/impl/PyReferenceExpressionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyReferenceExpressionImpl.java index f77f6501f6f8..3e702bf69ef5 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyReferenceExpressionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyReferenceExpressionImpl.java @@ -27,7 +27,6 @@ import com.jetbrains.python.psi.impl.references.PyReferenceImpl; import com.jetbrains.python.psi.resolve.*; import com.jetbrains.python.psi.types.*; import com.jetbrains.python.refactoring.PyDefUseUtil; -import com.jetbrains.python.toolbox.Maybe; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -269,10 +268,13 @@ public class PyReferenceExpressionImpl extends PyElementImpl implements PyRefere if (classType.isDefinition()) { return Ref.create(PyBuiltinCache.getInstance(pyClass).getObjectType(PyNames.PROPERTY)); } - final Maybe accessor = property.getByDirection(AccessDirection.of(this)); - final Callable callable = accessor.valueOrNull(); - final PyType type = (callable != null) ? callable.getReturnType(context, this) : null; - return Ref.create(type); + if (AccessDirection.of(this) == AccessDirection.READ) { + final PyType type = property.getType(context); + if (type != null) { + return Ref.create(type); + } + } + return Ref.create(); } } else if (qualifierType instanceof PyUnionType) { diff --git a/python/testData/property/Classic.py b/python/testData/property/Classic.py index c7293b63d2e4..c48b4503b513 100644 --- a/python/testData/property/Classic.py +++ b/python/testData/property/Classic.py @@ -19,6 +19,6 @@ class A(object): v1 = property(getter, setter) v2 = property(fset=setter, fdel=deleter, fget=getter, doc="doc of v2") - v3 = property(lambda self: self._v, None, (deleter)) + v3 = property(lambda self: 0, None, (deleter)) v4 = otherworldly # NOTE: not supported yet v5 = property(v5getter, v5setter) diff --git a/python/testSrc/com/jetbrains/python/PyClassicPropertyTest.java b/python/testSrc/com/jetbrains/python/PyClassicPropertyTest.java index 0ba9de3683eb..e72e10aaea5f 100644 --- a/python/testSrc/com/jetbrains/python/PyClassicPropertyTest.java +++ b/python/testSrc/com/jetbrains/python/PyClassicPropertyTest.java @@ -2,6 +2,8 @@ package com.jetbrains.python; import com.jetbrains.python.fixtures.PyTestCase; import com.jetbrains.python.psi.*; +import com.jetbrains.python.psi.types.PyType; +import com.jetbrains.python.psi.types.TypeEvalContext; import com.jetbrains.python.toolbox.Maybe; public class PyClassicPropertyTest extends PyTestCase { @@ -78,6 +80,9 @@ public class PyClassicPropertyTest extends PyTestCase { accessor = p.getGetter(); assertFalse(accessor.isDefined()); + final PyType codeInsightType = p.getType(TypeEvalContext.codeInsightFallback()); + assertNull(codeInsightType); + accessor = p.getSetter(); assertTrue(accessor.isDefined()); assertNull(accessor.value()); From a09bc025a9a6e4cb98fca0b44fc7ddddbdfbd1a2 Mon Sep 17 00:00:00 2001 From: nik Date: Thu, 15 Aug 2013 10:15:36 +0400 Subject: [PATCH 9/9] cleanup: added default (empty) implementation for SettingsEditor#disposeEditor --- .../src/com/jetbrains/rest/run/RestConfigurationEditor.java | 3 --- .../python/testing/pytest/PyTestConfigurationEditor.java | 3 --- 2 files changed, 6 deletions(-) diff --git a/python/python-rest/src/com/jetbrains/rest/run/RestConfigurationEditor.java b/python/python-rest/src/com/jetbrains/rest/run/RestConfigurationEditor.java index e1791aef50b6..24951861dd59 100644 --- a/python/python-rest/src/com/jetbrains/rest/run/RestConfigurationEditor.java +++ b/python/python-rest/src/com/jetbrains/rest/run/RestConfigurationEditor.java @@ -95,9 +95,6 @@ public class RestConfigurationEditor extends SettingsEditor