diff --git a/python/psi-api/src/com/jetbrains/python/inspections/PyInspectionExtension.java b/python/psi-api/src/com/jetbrains/python/inspections/PyInspectionExtension.java index f11618d72e60..74ee42db48e5 100644 --- a/python/psi-api/src/com/jetbrains/python/inspections/PyInspectionExtension.java +++ b/python/psi-api/src/com/jetbrains/python/inspections/PyInspectionExtension.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -55,10 +55,32 @@ public abstract class PyInspectionExtension { return false; } + /** + * Checks if unresolved member could be ignored. + * + * @param type type whose member will be checked + * @param name member name + * @return true if the unresolved member with the specified name could be ignored + * @deprecated Use {@link PyInspectionExtension#ignoreUnresolvedMember(PyType, String, TypeEvalContext)} instead. + * This method will be removed in 2018.1. + */ + @Deprecated public boolean ignoreUnresolvedMember(@NotNull PyType type, @NotNull String name) { return false; } + /** + * Checks if unresolved member could be ignored. + * + * @param type type whose member will be checked + * @param name member name + * @param context type evaluation context + * @return true if the unresolved member with the specified name could be ignored + */ + public boolean ignoreUnresolvedMember(@NotNull PyType type, @NotNull String name, @NotNull TypeEvalContext context) { + return ignoreUnresolvedMember(type, name); + } + /** * Returns true if access to protected (the one started with "_") symbol should not be treated as violation. * diff --git a/python/src/META-INF/python-core-common.xml b/python/src/META-INF/python-core-common.xml index 8dc049e719f3..7af2fee357a4 100644 --- a/python/src/META-INF/python-core-common.xml +++ b/python/src/META-INF/python-core-common.xml @@ -690,6 +690,7 @@ + diff --git a/python/src/com/jetbrains/python/codeInsight/typing/PyTypingInspectionExtension.kt b/python/src/com/jetbrains/python/codeInsight/typing/PyTypingInspectionExtension.kt new file mode 100644 index 000000000000..f958f584b029 --- /dev/null +++ b/python/src/com/jetbrains/python/codeInsight/typing/PyTypingInspectionExtension.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.jetbrains.python.codeInsight.typing + +import com.jetbrains.python.PyNames +import com.jetbrains.python.inspections.PyInspectionExtension +import com.jetbrains.python.psi.types.PyClassLikeType +import com.jetbrains.python.psi.types.PyType +import com.jetbrains.python.psi.types.TypeEvalContext + +class PyTypingInspectionExtension : PyInspectionExtension() { + + override fun ignoreUnresolvedMember(type: PyType, name: String, context: TypeEvalContext): Boolean { + return name == PyNames.GETITEM && type is PyClassLikeType && type.isDefinition && isGenericItselfOrDescendant(type, context) + } + + private fun isGenericItselfOrDescendant(type: PyClassLikeType, + context: TypeEvalContext): Boolean { + return type.classQName == PyTypingTypeProvider.GENERIC || + type.getSuperClassTypes(context).any { it.classQName == PyTypingTypeProvider.GENERIC } + } +} + diff --git a/python/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.java b/python/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.java index f04a8869286e..6ee353183b47 100644 --- a/python/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.java +++ b/python/src/com/jetbrains/python/codeInsight/typing/PyTypingTypeProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -61,6 +61,7 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { public static final String ASYNC_GENERATOR = "typing.AsyncGenerator"; public static final String COROUTINE = "typing.Coroutine"; public static final String NAMEDTUPLE = "typing.NamedTuple"; + public static final String GENERIC = "typing.Generic"; public static final Pattern TYPE_COMMENT_PATTERN = Pattern.compile("# *type: *(.*)"); @@ -80,7 +81,7 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { .build(); private static final ImmutableSet GENERIC_CLASSES = ImmutableSet.builder() - .add("typing.Generic") + .add(GENERIC) .add("typing.AbstractGeneric") .add("typing.Protocol") .build(); @@ -89,7 +90,7 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { .add("typing.overload") .add("typing.Any") .add("typing.TypeVar") - .add("typing.Generic") + .add(GENERIC) .add("typing.Tuple") .add("typing.Callable") .add("typing.Type") @@ -107,7 +108,7 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { public PyType getReferenceExpressionType(@NotNull PyReferenceExpression referenceExpression, @NotNull TypeEvalContext context) { // Check for the exact name in advance for performance reasons if ("Generic".equals(referenceExpression.getName())) { - if (resolveToQualifiedNames(referenceExpression, context).contains("typing.Generic")) { + if (resolveToQualifiedNames(referenceExpression, context).contains(GENERIC)) { return createTypingGenericType(); } } @@ -219,7 +220,7 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { @NotNull private static PyType createTypingGenericType() { - return new PyCustomType("typing.Generic", null, false); + return new PyCustomType(GENERIC, null, false); } private static boolean omitFirstParamInTypeComment(@NotNull PyFunction func) { @@ -304,7 +305,7 @@ public class PyTypingTypeProvider extends PyTypeProviderBase { if (referenceTarget instanceof PyTargetExpression) { final PyTargetExpression target = (PyTargetExpression)referenceTarget; // Depends on typing.Generic defined as a target expression - if ("typing.Generic".equals(target.getQualifiedName())) { + if (GENERIC.equals(target.getQualifiedName())) { return createTypingGenericType(); } if (context.maySwitchToAST(target)) { diff --git a/python/src/com/jetbrains/python/inspections/unresolvedReference/PyUnresolvedReferencesInspection.java b/python/src/com/jetbrains/python/inspections/unresolvedReference/PyUnresolvedReferencesInspection.java index 5bcec2e4273f..e5e6b0293243 100644 --- a/python/src/com/jetbrains/python/inspections/unresolvedReference/PyUnresolvedReferencesInspection.java +++ b/python/src/com/jetbrains/python/inspections/unresolvedReference/PyUnresolvedReferencesInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -812,7 +812,7 @@ public class PyUnresolvedReferencesInspection extends PyInspection { } } for (PyInspectionExtension extension : Extensions.getExtensions(PyInspectionExtension.EP_NAME)) { - if (extension.ignoreUnresolvedMember(type, name)) { + if (extension.ignoreUnresolvedMember(type, name, myTypeEvalContext)) { return true; } } diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection3K/typingGenericDunderGetItem.py b/python/testData/inspections/PyUnresolvedReferencesInspection3K/typingGenericDunderGetItem.py new file mode 100644 index 000000000000..028a98449049 --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection3K/typingGenericDunderGetItem.py @@ -0,0 +1,4 @@ +import typing + +class MyClass(typing.Generic[str]): + pass \ No newline at end of file diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection3K/typingIterableDunderGetItem.py b/python/testData/inspections/PyUnresolvedReferencesInspection3K/typingIterableDunderGetItem.py new file mode 100644 index 000000000000..561367e4c950 --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection3K/typingIterableDunderGetItem.py @@ -0,0 +1,5 @@ +import typing + + +def myfunc(seq: typing.Iterable[str]): + pass \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/inspections/Py3UnresolvedReferencesInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/Py3UnresolvedReferencesInspectionTest.java index 3b707ae9c67c..034911607a39 100644 --- a/python/testSrc/com/jetbrains/python/inspections/Py3UnresolvedReferencesInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/Py3UnresolvedReferencesInspectionTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2016 JetBrains s.r.o. + * Copyright 2000-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -195,4 +195,14 @@ public class Py3UnresolvedReferencesInspectionTest extends PyTestCase { public void testMockPatchObject() { doMultiFileTest(getTestName(true) + ".py"); } + + // PY-22525 + public void testTypingIterableDunderGetItem() { + doTest(); + } + + // PY-22642 + public void testTypingGenericDunderGetItem() { + doTest(); + } }