From fcefe43db929ef4c172410b2de140779f1e1dd2e Mon Sep 17 00:00:00 2001 From: Semyon Proshev Date: Thu, 30 Nov 2017 17:46:59 +0300 Subject: [PATCH] Improve ignoring access to namedtuple's protected members (PY-27148) --- .../PyProtectedMemberInspection.java | 1 - .../stdlib/PyStdlibInspectionExtension.kt | 26 +++++++++++++++++++ .../PyProtectedMemberInspection/namedTuple.py | 18 +++++++++++-- .../typingNamedTuple.py | 21 +++++++++++++++ .../PyProtectedMemberInspectionTest.java | 5 ++++ 5 files changed, 68 insertions(+), 3 deletions(-) create mode 100644 python/testData/inspections/PyProtectedMemberInspection/typingNamedTuple.py diff --git a/python/src/com/jetbrains/python/inspections/PyProtectedMemberInspection.java b/python/src/com/jetbrains/python/inspections/PyProtectedMemberInspection.java index f37b3b060618..777cc117bf4d 100644 --- a/python/src/com/jetbrains/python/inspections/PyProtectedMemberInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyProtectedMemberInspection.java @@ -91,7 +91,6 @@ public class PyProtectedMemberInspection extends PyInspection { } private void checkReference(@NotNull final PyReferenceExpression node, @NotNull final PyExpression qualifier) { - if (myTypeEvalContext.getType(qualifier) instanceof PyNamedTupleType) return; final String name = node.getName(); final List quickFixes = new ArrayList<>(); quickFixes.add(new PyRenameElementQuickFix()); diff --git a/python/src/com/jetbrains/python/inspections/stdlib/PyStdlibInspectionExtension.kt b/python/src/com/jetbrains/python/inspections/stdlib/PyStdlibInspectionExtension.kt index f9d4b3a9c2e1..a310283474ac 100644 --- a/python/src/com/jetbrains/python/inspections/stdlib/PyStdlibInspectionExtension.kt +++ b/python/src/com/jetbrains/python/inspections/stdlib/PyStdlibInspectionExtension.kt @@ -5,6 +5,7 @@ import com.intellij.psi.PsiReference import com.jetbrains.python.PyNames import com.jetbrains.python.codeInsight.stdlib.PyNamedTupleType import com.jetbrains.python.codeInsight.stdlib.PyStdlibClassMembersProvider +import com.jetbrains.python.codeInsight.typing.PyTypingTypeProvider import com.jetbrains.python.inspections.PyInspectionExtension import com.jetbrains.python.psi.PyElement import com.jetbrains.python.psi.PyFunction @@ -15,6 +16,10 @@ import com.jetbrains.python.psi.types.TypeEvalContext class PyStdlibInspectionExtension : PyInspectionExtension() { + companion object { + private val NAMEDTUPLE_SPECIAL_ATTRIBUTES = setOf("_make", "_asdict", "_replace", "_source", "_fields") + } + override fun ignoreInitNewSignatures(original: PyFunction, complementary: PyFunction): Boolean { return PyNames.TYPE_ENUM == complementary.containingClass?.qualifiedName } @@ -39,4 +44,25 @@ class PyStdlibInspectionExtension : PyInspectionExtension() { return false } + + override fun ignoreProtectedSymbol(expression: PyReferenceExpression, context: TypeEvalContext): Boolean { + val qualifier = expression.qualifier + + if (qualifier != null && expression.referencedName in NAMEDTUPLE_SPECIAL_ATTRIBUTES) { + val qualifierType = context.getType(qualifier) + + if (qualifierType is PyNamedTupleType) { + return true + } + + val isTypingNT: (PyClassLikeType?) -> Boolean = + { it is PyNamedTupleType || it != null && PyTypingTypeProvider.NAMEDTUPLE == it.classQName } + + if (qualifierType is PyClassLikeType && qualifierType.getAncestorTypes(context).find(isTypingNT) != null) { + return true + } + } + + return false + } } \ No newline at end of file diff --git a/python/testData/inspections/PyProtectedMemberInspection/namedTuple.py b/python/testData/inspections/PyProtectedMemberInspection/namedTuple.py index 905088a1ba69..7dd33d20eb3a 100644 --- a/python/testData/inspections/PyProtectedMemberInspection/namedTuple.py +++ b/python/testData/inspections/PyProtectedMemberInspection/namedTuple.py @@ -1,6 +1,20 @@ from collections import namedtuple -i = namedtuple('Point', ['x', 'y'], verbose=True) -i._replace( **{"a":"a"}) + +class Cat1(namedtuple("Cat", "name age")): + pass +c1 = Cat1("name", 5) +print(c1._make) +print(c1._asdict) +print(c1._replace) +print(c1._source) +print(c1._fields) +Cat2 = namedtuple("Cat2", "name age") +c2 = Cat2("name", 5) +print(c2._make) +print(c2._asdict) +print(c2._replace) +print(c2._source) +print(c2._fields) \ No newline at end of file diff --git a/python/testData/inspections/PyProtectedMemberInspection/typingNamedTuple.py b/python/testData/inspections/PyProtectedMemberInspection/typingNamedTuple.py new file mode 100644 index 000000000000..a57c8a33a7ef --- /dev/null +++ b/python/testData/inspections/PyProtectedMemberInspection/typingNamedTuple.py @@ -0,0 +1,21 @@ +from typing import NamedTuple + + +class Cat1(NamedTuple): + name: str + age: int +c1 = Cat1("name", 5) +print(c1._make) +print(c1._asdict) +print(c1._replace) +print(c1._source) +print(c1._fields) + + +Cat2 = NamedTuple("Cat2", name=str, age=int) +c2 = Cat2("name", 5) +print(c2._make) +print(c2._asdict) +print(c2._replace) +print(c2._source) +print(c2._fields) \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/inspections/PyProtectedMemberInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyProtectedMemberInspectionTest.java index 1a693d18402b..5292e09b2118 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyProtectedMemberInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyProtectedMemberInspectionTest.java @@ -79,6 +79,11 @@ public class PyProtectedMemberInspectionTest extends PyInspectionTestCase { runWithLanguageLevel(LanguageLevel.PYTHON35, this::doMultiFileTest); } + // PY-27148 + public void testTypingNamedTuple() { + runWithLanguageLevel(LanguageLevel.PYTHON36, this::doTest); + } + @NotNull @Override protected Class getInspectionClass() {