diff --git a/python/python-psi-impl/src/com/jetbrains/python/inspections/unresolvedReference/PyUnresolvedReferencesVisitor.java b/python/python-psi-impl/src/com/jetbrains/python/inspections/unresolvedReference/PyUnresolvedReferencesVisitor.java index 5d40e058d03d..b67940ef1123 100644 --- a/python/python-psi-impl/src/com/jetbrains/python/inspections/unresolvedReference/PyUnresolvedReferencesVisitor.java +++ b/python/python-psi-impl/src/com/jetbrains/python/inspections/unresolvedReference/PyUnresolvedReferencesVisitor.java @@ -661,44 +661,43 @@ abstract class PyUnresolvedReferencesVisitor extends PyInspectionVisitor { final String exprName = expr.getName(); if (exprName != null) { if (qualifier != null) { - final PyType type = context.getType(qualifier); - if (type instanceof PyClassType) { - ContainerUtil.addIfNotNull(result, extractAttributeQNameFromClassType(exprName, (PyClassType)type)); - } - else if (type instanceof PyModuleType) { - final PyFile file = ((PyModuleType)type).getModule(); - final QualifiedName name = QualifiedNameFinder.findCanonicalImportPath(file, element); - if (name != null) { - ContainerUtil.addIfNotNull(result, name.append(exprName)); - } - } - else if (type instanceof PyImportedModuleType) { - final PyImportedModule module = ((PyImportedModuleType)type).getImportedModule(); - final PsiElement resolved = module.resolve(); - if (resolved != null) { - final QualifiedName path = QualifiedNameFinder.findCanonicalImportPath(resolved, element); - if (path != null) { - ContainerUtil.addIfNotNull(result, path.append(exprName)); + final PyType qualifierType = context.getType(qualifier); + PyTypeUtil.toStream(qualifierType) + .map(type -> { + if (type instanceof PyClassType) { + return extractAttributeQNameFromClassType(exprName, (PyClassType)type); } - } - } - else if (type instanceof PyFunctionType) { - final PyCallable callable = ((PyFunctionType)type).getCallable(); - final String callableName = callable.getName(); - if (callableName != null) { - final QualifiedName path = QualifiedNameFinder.findCanonicalImportPath(callable, element); - if (path != null) { - result.add(path.append(QualifiedName.fromComponents(callableName, exprName))); + else if (type instanceof PyModuleType) { + final PyFile file = ((PyModuleType)type).getModule(); + final QualifiedName name = QualifiedNameFinder.findCanonicalImportPath(file, element); + if (name != null) { + return name.append(exprName); + } } - } - } - else if (type instanceof PyUnionType) { - for (PyType memberType : ((PyUnionType)type).getMembers()) { - if (memberType instanceof PyClassType) { - ContainerUtil.addIfNotNull(result, extractAttributeQNameFromClassType(exprName, (PyClassType)memberType)); + else if (type instanceof PyImportedModuleType) { + final PyImportedModule module = ((PyImportedModuleType)type).getImportedModule(); + final PsiElement resolved = module.resolve(); + if (resolved != null) { + final QualifiedName path = QualifiedNameFinder.findCanonicalImportPath(resolved, element); + if (path != null) { + return path.append(exprName); + } + } } - } - } + else if (type instanceof PyFunctionType) { + final PyCallable callable = ((PyFunctionType)type).getCallable(); + final String callableName = callable.getName(); + if (callableName != null) { + final QualifiedName path = QualifiedNameFinder.findCanonicalImportPath(callable, element); + if (path != null) { + return path.append(QualifiedName.fromComponents(callableName, exprName)); + } + } + } + return null; + }) + .nonNull() + .into(result); } else { final PsiElement parent = element.getParent(); diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/WildcardIgnorePatternReferenceForNestedBinaryModule/a.py b/python/testData/inspections/PyUnresolvedReferencesInspection/WildcardIgnorePatternReferenceForNestedBinaryModule/a.py new file mode 100644 index 000000000000..5db321720e71 --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection/WildcardIgnorePatternReferenceForNestedBinaryModule/a.py @@ -0,0 +1,3 @@ +import pkg + +pkg.unresolved \ No newline at end of file diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/WildcardIgnorePatternReferenceForNestedBinaryModule/python_stubs/pkg/__init__.py b/python/testData/inspections/PyUnresolvedReferencesInspection/WildcardIgnorePatternReferenceForNestedBinaryModule/python_stubs/pkg/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/WildcardIgnorePatternReferenceForNestedBinaryModule/python_stubs/pkg/mod.py b/python/testData/inspections/PyUnresolvedReferencesInspection/WildcardIgnorePatternReferenceForNestedBinaryModule/python_stubs/pkg/mod.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/WildcardIgnorePatternReferenceForNestedBinaryModule/site-packages/pkg/__init__.py b/python/testData/inspections/PyUnresolvedReferencesInspection/WildcardIgnorePatternReferenceForNestedBinaryModule/site-packages/pkg/__init__.py new file mode 100644 index 000000000000..df09185d0eb7 --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection/WildcardIgnorePatternReferenceForNestedBinaryModule/site-packages/pkg/__init__.py @@ -0,0 +1 @@ +from .mod import * \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java index fbcd8379255f..cd8a88248ea2 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java @@ -828,6 +828,25 @@ public class PyUnresolvedReferencesInspectionTest extends PyInspectionTestCase { "a.append(10)"); } + // PY-39682 + public void testWildcardIgnorePatternReferenceForNestedBinaryModule() { + // TODO simplify runWithAdditionalClassEntryInSdkRoots to accept a relative path directly + final String testDataDir = getTestDataPath() + "/" + getTestDirectoryPath(); + final VirtualFile sitePackagesDir = StandardFileSystems.local().findFileByPath(testDataDir + "/site-packages"); + final VirtualFile skeletonsDir = StandardFileSystems.local().findFileByPath(testDataDir + "/python_stubs"); + runWithAdditionalClassEntryInSdkRoots(sitePackagesDir, () -> { + runWithAdditionalClassEntryInSdkRoots(skeletonsDir, () -> { + myFixture.configureByFile(getTestDirectoryPath() + "/a.py"); + final PyUnresolvedReferencesInspection inspection = new PyUnresolvedReferencesInspection(); + inspection.ignoredIdentifiers.add("pkg.*"); + myFixture.enableInspections(inspection); + myFixture.checkHighlighting(isWarning(), isInfo(), isWeakWarning()); + assertSdkRootsNotParsed(myFixture.getFile()); + assertProjectFilesNotParsed(myFixture.getFile()); + }); + }); + } + @NotNull @Override protected Class getInspectionClass() {