From 3b5e051bfef58e6f32f3713cbed5bc5a0ca2a1d8 Mon Sep 17 00:00:00 2001 From: Semyon Proshev Date: Tue, 2 Aug 2016 20:20:01 +0300 Subject: [PATCH] PY-20279 Fixed: Suggest implicit __class__ inside methods In PyReferenceImpl add implicit __class__ to lookup variants --- .../psi/impl/references/PyReferenceImpl.java | 11 ++++- .../jetbrains/python/Py3CompletionTest.java | 27 ++++++++++++- .../python/PythonCompletionTest.java | 40 ++++++++++++++++--- 3 files changed, 69 insertions(+), 9 deletions(-) diff --git a/python/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java b/python/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java index d1dd18822472..15656299b247 100644 --- a/python/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/references/PyReferenceImpl.java @@ -668,12 +668,19 @@ public class PyReferenceImpl implements PsiReferenceEx, PsiPolyVariantReference if (underscores >= 2) { // if we're a normal module, add module's attrs - PsiFile f = realContext.getContainingFile(); - if (f instanceof PyFile) { + if (realContext.getContainingFile() instanceof PyFile) { for (String name : PyModuleType.getPossibleInstanceMembers()) { ret.add(LookupElementBuilder.create(name).withIcon(PlatformIcons.FIELD_ICON)); } } + + // if we're inside method, add implicit __class__ + if (LanguageLevel.forElement(myElement).isAtLeast(LanguageLevel.PYTHON30)) { + Optional + .ofNullable(PsiTreeUtil.getParentOfType(myElement, PyFunction.class)) + .map(PyFunction::getContainingClass) + .ifPresent(pyClass -> ret.add(LookupElementBuilder.create(PyNames.__CLASS__))); + } } ret.addAll(getOriginalElements(processor)); diff --git a/python/testSrc/com/jetbrains/python/Py3CompletionTest.java b/python/testSrc/com/jetbrains/python/Py3CompletionTest.java index 2bedfc9b4521..3101bd460df0 100644 --- a/python/testSrc/com/jetbrains/python/Py3CompletionTest.java +++ b/python/testSrc/com/jetbrains/python/Py3CompletionTest.java @@ -19,6 +19,8 @@ import com.intellij.codeInsight.completion.impl.CamelHumpMatcher; import com.intellij.testFramework.LightProjectDescriptor; import com.jetbrains.python.fixtures.PyTestCase; import com.jetbrains.python.psi.LanguageLevel; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.List; @@ -76,7 +78,8 @@ public class Py3CompletionTest extends PyTestCase { myFixture.checkResultByFile(getTestName(true) + "/a.after.py"); } - private List doTestByText(String text) { + @Nullable + private List doTestByText(@NotNull String text) { myFixture.configureByText(PythonFileType.INSTANCE, text); myFixture.completeBasic(); return myFixture.getLookupElementStrings(); @@ -165,6 +168,28 @@ public class Py3CompletionTest extends PyTestCase { runWithLanguageLevel(LanguageLevel.PYTHON30, this::doTest); } + // PY-20279 + public void testImplicitDunderClass() { + final List inClassMethod = doTestByText("class First:\n" + + " def foo(self):\n" + + " print(__cl)"); + assertNotNull(inClassMethod); + assertContainsElements(inClassMethod, PyNames.__CLASS__); + + final List inStaticMethod = doTestByText("class First:\n" + + " @staticmethod\n" + + " def foo():\n" + + " print(__cl)"); + assertNotNull(inStaticMethod); + assertContainsElements(inStaticMethod, PyNames.__CLASS__); + + assertNull(doTestByText("class First:\n" + + " print(__cl)")); + + assertNull(doTestByText("def abc():\n" + + " print(__cl)")); + } + // PY-20770 public void testAsyncGenerator() { runWithLanguageLevel( diff --git a/python/testSrc/com/jetbrains/python/PythonCompletionTest.java b/python/testSrc/com/jetbrains/python/PythonCompletionTest.java index f31b36a8a3da..798f90baa45e 100644 --- a/python/testSrc/com/jetbrains/python/PythonCompletionTest.java +++ b/python/testSrc/com/jetbrains/python/PythonCompletionTest.java @@ -27,6 +27,7 @@ import com.jetbrains.python.documentation.PyDocumentationSettings; import com.jetbrains.python.documentation.docstrings.DocStringFormat; import com.jetbrains.python.fixtures.PyTestCase; import com.jetbrains.python.psi.LanguageLevel; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Arrays; @@ -50,7 +51,8 @@ public class PythonCompletionTest extends PyTestCase { myFixture.checkResultByFile(getTestName(true) + "/a.after.py"); } - private List doTestByText(String text) { + @Nullable + private List doTestByText(@NotNull String text) { myFixture.configureByText(PythonFileType.INSTANCE, text); myFixture.completeBasic(); return myFixture.getLookupElementStrings(); @@ -893,7 +895,7 @@ public class PythonCompletionTest extends PyTestCase { public void testFormatStringWithFormatModifier() { doTest(); } - + // PY-3077 public void testPercentStringWithDictLiteralArg() { doTest(); @@ -903,7 +905,7 @@ public class PythonCompletionTest extends PyTestCase { public void testPercentStringWithDictCallArg() { doTest(); } - + // PY-3077 public void testPercentStringWithParenDictCallArg() { doTest(); @@ -918,17 +920,17 @@ public class PythonCompletionTest extends PyTestCase { public void testPercentStringDictLiteralStringKey() { doTest(); } - + // PY-3077 public void testPercentStringDictCallStringKey() { doTest(); } - + // PY-3077 public void testPercentStringDictLiteralArgument() { doTest(); } - + // PY-19839 public void testPercentStringDictRefKeys() { final List variants = doTestByFile(); @@ -1015,6 +1017,32 @@ public class PythonCompletionTest extends PyTestCase { doTest(); } + // PY-20279 + public void testImplicitDunderClass() { + final List inClassMethod = doTestByText("class First:\n" + + " def foo(self):\n" + + " print(__cl)"); + assertNotNull(inClassMethod); + assertDoesntContain(inClassMethod, PyNames.__CLASS__); + + final List inStaticMethod = doTestByText("class First:\n" + + " @staticmethod\n" + + " def foo():\n" + + " print(__cl)"); + assertNotNull(inStaticMethod); + assertDoesntContain(inStaticMethod, PyNames.__CLASS__); + + final List inClass = doTestByText("class First:\n" + + " print(__cl)"); + assertNotNull(inClass); + assertEmpty(inClass); + + final List inFunction = doTestByText("def abc():\n" + + " print(__cl)"); + assertNotNull(inFunction); + assertEmpty(inClass); + } + // PY-20768 public void testInitSubclassBuiltinMethod() { runWithLanguageLevel(LanguageLevel.PYTHON36,