From f3b2f3ca06c69a62e8b2c1f690a791eb69f4f2c4 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Fri, 14 Nov 2014 13:34:27 +0100 Subject: [PATCH 1/8] Cleanup (formatting) --- .../intellij/notification/Notification.java | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/platform/platform-api/src/com/intellij/notification/Notification.java b/platform/platform-api/src/com/intellij/notification/Notification.java index 7b3ecd67ca47..fc818e4e5239 100644 --- a/platform/platform-api/src/com/intellij/notification/Notification.java +++ b/platform/platform-api/src/com/intellij/notification/Notification.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -44,19 +44,23 @@ public class Notification { private Boolean myImportant; private WeakReference myBalloonRef; - public Notification(@NotNull final String groupDisplayId, @NotNull final String title, @NotNull final String content, @NotNull final NotificationType type) { + public Notification(@NotNull String groupDisplayId, @NotNull String title, @NotNull String content, @NotNull NotificationType type) { this(groupDisplayId, title, content, type, null); } /** * @param groupDisplayId this should be a human-readable, capitalized string like "Facet Detector". * It will appear in "Notifications" configurable. - * @param title notification title - * @param content notification content - * @param type notification type - * @param listener notification lifecycle listener + * @param title notification title + * @param content notification content + * @param type notification type + * @param listener notification lifecycle listener */ - public Notification(@NotNull final String groupDisplayId, @NotNull final String title, @NotNull final String content, @NotNull final NotificationType type, @Nullable NotificationListener listener) { + public Notification(@NotNull String groupDisplayId, + @NotNull String title, + @NotNull String content, + @NotNull NotificationType type, + @Nullable NotificationListener listener) { myGroupId = groupDisplayId; myTitle = title; myContent = content; From 4895ebd7247ca08a3ad8a2535daf047f9ff1d078 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Fri, 14 Nov 2014 13:42:39 +0100 Subject: [PATCH 2/8] project: reference to missing module dropped --- .idea/modules.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/.idea/modules.xml b/.idea/modules.xml index dc3b64547d50..66832a09f40a 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -79,7 +79,6 @@ - From b091a61841bdfa262880c09c261880edb8c1744a Mon Sep 17 00:00:00 2001 From: Konstantin Bulenkov Date: Fri, 14 Nov 2014 14:11:35 +0100 Subject: [PATCH 3/8] ability to define tooltips for OptionActions --- .../ui/components/JBOptionButton.java | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/platform/platform-api/src/com/intellij/ui/components/JBOptionButton.java b/platform/platform-api/src/com/intellij/ui/components/JBOptionButton.java index b4b88ee2f5a9..debc508cd91e 100644 --- a/platform/platform-api/src/com/intellij/ui/components/JBOptionButton.java +++ b/platform/platform-api/src/com/intellij/ui/components/JBOptionButton.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 JetBrains s.r.o. + * Copyright 2000-2014 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. @@ -284,6 +284,9 @@ public class JBOptionButton extends JButton implements MouseMotionListener, Weig eachItem.setMnemonic(info.myMnemonic); eachItem.setDisplayedMnemonicIndex(info.myMnemonicIndex); } + if (info.getToolTipText() != null) { + eachItem.setToolTipText(info.getToolTipText()); + } myOptionInfos.add(info); } @@ -299,6 +302,7 @@ public class JBOptionButton extends JButton implements MouseMotionListener, Weig int myMnemonicIndex; JBOptionButton myButton; Action myAction; + private String myToolTipText; OptionInfo(String plainText, int mnemonic, int mnemonicIndex, JBOptionButton button, Action action) { myPlainText = plainText; @@ -327,10 +331,20 @@ public class JBOptionButton extends JButton implements MouseMotionListener, Weig public Action getAction() { return myAction; } + + public String getToolTipText() { + return myToolTipText; + } + + public void setToolTipText(String toolTipText) { + myToolTipText = toolTipText; + } } private OptionInfo getMenuInfo(Action each) { final String text = (String)each.getValue(Action.NAME); + final String description = (String)each.getValue(Action.SHORT_DESCRIPTION); + final String longDescription = (String)each.getValue(Action.LONG_DESCRIPTION); int mnemonic = -1; int mnemonicIndex = -1; StringBuilder plainText = new StringBuilder(); @@ -346,8 +360,14 @@ public class JBOptionButton extends JButton implements MouseMotionListener, Weig } plainText.append(ch); } - - return new OptionInfo(plainText.toString(), mnemonic, mnemonicIndex, this, each); + + final OptionInfo info = new OptionInfo(plainText.toString(), mnemonic, mnemonicIndex, this, each); + if (longDescription != null) { + info.setToolTipText(longDescription); + } else if (description != null) { + info.setToolTipText(description); + } + return info; } From 96828f7ecd5489995a4038d3d541221ea8ae64ad Mon Sep 17 00:00:00 2001 From: Konstantin Bulenkov Date: Fri, 14 Nov 2014 14:23:15 +0100 Subject: [PATCH 4/8] rollback useless change --- .../ui/components/JBOptionButton.java | 24 ++----------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/platform/platform-api/src/com/intellij/ui/components/JBOptionButton.java b/platform/platform-api/src/com/intellij/ui/components/JBOptionButton.java index debc508cd91e..9034294e8740 100644 --- a/platform/platform-api/src/com/intellij/ui/components/JBOptionButton.java +++ b/platform/platform-api/src/com/intellij/ui/components/JBOptionButton.java @@ -284,9 +284,6 @@ public class JBOptionButton extends JButton implements MouseMotionListener, Weig eachItem.setMnemonic(info.myMnemonic); eachItem.setDisplayedMnemonicIndex(info.myMnemonicIndex); } - if (info.getToolTipText() != null) { - eachItem.setToolTipText(info.getToolTipText()); - } myOptionInfos.add(info); } @@ -302,7 +299,6 @@ public class JBOptionButton extends JButton implements MouseMotionListener, Weig int myMnemonicIndex; JBOptionButton myButton; Action myAction; - private String myToolTipText; OptionInfo(String plainText, int mnemonic, int mnemonicIndex, JBOptionButton button, Action action) { myPlainText = plainText; @@ -331,20 +327,10 @@ public class JBOptionButton extends JButton implements MouseMotionListener, Weig public Action getAction() { return myAction; } - - public String getToolTipText() { - return myToolTipText; - } - - public void setToolTipText(String toolTipText) { - myToolTipText = toolTipText; - } } private OptionInfo getMenuInfo(Action each) { final String text = (String)each.getValue(Action.NAME); - final String description = (String)each.getValue(Action.SHORT_DESCRIPTION); - final String longDescription = (String)each.getValue(Action.LONG_DESCRIPTION); int mnemonic = -1; int mnemonicIndex = -1; StringBuilder plainText = new StringBuilder(); @@ -360,14 +346,8 @@ public class JBOptionButton extends JButton implements MouseMotionListener, Weig } plainText.append(ch); } - - final OptionInfo info = new OptionInfo(plainText.toString(), mnemonic, mnemonicIndex, this, each); - if (longDescription != null) { - info.setToolTipText(longDescription); - } else if (description != null) { - info.setToolTipText(description); - } - return info; + + return new OptionInfo(plainText.toString(), mnemonic, mnemonicIndex, this, each); } From 8a7504345801cbd4c043cf74edfa0e7b2f69921e Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Thu, 13 Nov 2014 17:29:08 +0300 Subject: [PATCH 5/8] Improve javadoc for PointInImport and clean up a bit in ResolveImportUtil --- .../com/jetbrains/python/psi/resolve/PointInImport.java | 6 ++++-- .../jetbrains/python/psi/resolve/ResolveImportUtil.java | 9 ++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/python/psi-api/src/com/jetbrains/python/psi/resolve/PointInImport.java b/python/psi-api/src/com/jetbrains/python/psi/resolve/PointInImport.java index bdddb3cf129a..d89f1cba7024 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/resolve/PointInImport.java +++ b/python/psi-api/src/com/jetbrains/python/psi/resolve/PointInImport.java @@ -22,12 +22,14 @@ public enum PointInImport { NONE, /** - * The reference is inside import and refers to a module + * The reference is inside import and it's either source qualifier in {@code from REFERENCE import ...} + * or imported name itself in {@code import REFERENCE}. */ AS_MODULE, /** - * The reference is inside import and refers to a name imported from a module + * The reference is inside import and refers to a name imported from a module, i.e. it's concrete imported name in + * import statement of form {@code from somewhere import REFERENCE}, not the optional alias part. */ AS_NAME } diff --git a/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java b/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java index e2ea4a797317..4c6535cacd5a 100644 --- a/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java +++ b/python/src/com/jetbrains/python/psi/resolve/ResolveImportUtil.java @@ -392,17 +392,16 @@ public class ResolveImportUtil { /** * @param element what we test (identifier, reference, import element, etc) * @return the how the element relates to an enclosing import statement, if any + * @see com.jetbrains.python.psi.resolve.PointInImport */ + @NotNull public static PointInImport getPointInImport(@NotNull PsiElement element) { - PsiElement parent = PsiTreeUtil.getNonStrictParentOfType( - element, - PyImportElement.class, PyFromImportStatement.class - ); + final PsiElement parent = PsiTreeUtil.getNonStrictParentOfType(element, PyImportElement.class, PyFromImportStatement.class); if (parent instanceof PyFromImportStatement) { return PointInImport.AS_MODULE; // from foo ... } if (parent instanceof PyImportElement) { - PsiElement statement = parent.getParent(); + final PsiElement statement = parent.getParent(); if (statement instanceof PyImportStatement) { return PointInImport.AS_MODULE; // import foo,... } From 3b04af366a51bb26ebdeaaa2c5063c6c10a776cb Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Fri, 14 Nov 2014 14:55:25 +0300 Subject: [PATCH 6/8] Improve resolve/completion for package attributes (PY-14385, PY-14387, PY-14388, PY-14335) * Outside of import statement do not suggest names of not imported submodules for namespace packages (PY-14385). It has been done so for normal packages (with __init__.py) already, now the same policy applies to namespace packages. * While searching for imported submodules, use only immediate children of package, not arbitrary descendants (PY-14387). * Names defined in __init__.py for indirectly imported (intermediate) packages are included in completion list (PY-14388). * Removed custom collecting of submodule names from PyModuleType, use only PyModuleType#collectImportedSubmodules for this purpose. As result duplicate undecorated entries are no longer included in completion list for namespace packages (PY-14335). --- .../src/com/jetbrains/python/psi/PyUtil.java | 4 +- .../psi/types/PyImportedModuleType.java | 54 +++++++------ .../python/psi/types/PyModuleType.java | 79 ++++++++++++++----- .../a.after.py | 3 + .../attributeOfIndirectlyImportedPackage/a.py | 3 + .../pkg1/__init__.py | 1 + .../pkg1/m1.py | 0 .../a.after.py | 3 + .../a.py | 3 + .../nspkg1/m2.py | 0 .../nspkg1/m3.py | 0 .../submoduleOfIndirectlyImportedPackage/a.py | 4 + .../pkg1/__init__.py | 1 + .../pkg1/subpkg1/__init__.py | 0 .../pkg1/subpkg1/m1.py | 0 .../main.py | 5 ++ .../nspkg1/m2.py | 0 .../nspkg1/m3.py | 0 .../nspkg1/nssubpkg1/m1.py | 0 .../jetbrains/python/Py3CompletionTest.java | 5 ++ .../python/PythonCompletionTest.java | 15 ++++ ...Py3UnresolvedReferencesInspectionTest.java | 5 ++ 22 files changed, 140 insertions(+), 45 deletions(-) create mode 100644 python/testData/completion/attributeOfIndirectlyImportedPackage/a.after.py create mode 100644 python/testData/completion/attributeOfIndirectlyImportedPackage/a.py create mode 100644 python/testData/completion/attributeOfIndirectlyImportedPackage/pkg1/__init__.py create mode 100644 python/testData/completion/attributeOfIndirectlyImportedPackage/pkg1/m1.py create mode 100644 python/testData/completion/notImportedSubmodulesOfNamespacePackage/a.after.py create mode 100644 python/testData/completion/notImportedSubmodulesOfNamespacePackage/a.py create mode 100644 python/testData/completion/notImportedSubmodulesOfNamespacePackage/nspkg1/m2.py create mode 100644 python/testData/completion/notImportedSubmodulesOfNamespacePackage/nspkg1/m3.py create mode 100644 python/testData/completion/submoduleOfIndirectlyImportedPackage/a.py create mode 100644 python/testData/completion/submoduleOfIndirectlyImportedPackage/pkg1/__init__.py create mode 100644 python/testData/completion/submoduleOfIndirectlyImportedPackage/pkg1/subpkg1/__init__.py create mode 100644 python/testData/completion/submoduleOfIndirectlyImportedPackage/pkg1/subpkg1/m1.py create mode 100644 python/testData/inspections/PyUnresolvedReferencesInspection3K/NotImportedSubmodulesOfNamespacePackage/main.py create mode 100644 python/testData/inspections/PyUnresolvedReferencesInspection3K/NotImportedSubmodulesOfNamespacePackage/nspkg1/m2.py create mode 100644 python/testData/inspections/PyUnresolvedReferencesInspection3K/NotImportedSubmodulesOfNamespacePackage/nspkg1/m3.py create mode 100644 python/testData/inspections/PyUnresolvedReferencesInspection3K/NotImportedSubmodulesOfNamespacePackage/nspkg1/nssubpkg1/m1.py diff --git a/python/src/com/jetbrains/python/psi/PyUtil.java b/python/src/com/jetbrains/python/psi/PyUtil.java index 47e66d75db5a..6232b90b18a2 100644 --- a/python/src/com/jetbrains/python/psi/PyUtil.java +++ b/python/src/com/jetbrains/python/psi/PyUtil.java @@ -77,6 +77,7 @@ import com.jetbrains.python.refactoring.classes.PyDependenciesComparator; import com.jetbrains.python.refactoring.classes.extractSuperclass.PyExtractSuperclassHelper; import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo; import com.jetbrains.python.sdk.PythonSdkType; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -992,8 +993,9 @@ public class PyUtil { } // don't touch non-dirs } + @Contract("null -> null; !null -> !null") @Nullable - public static PsiElement turnInitIntoDir(PsiElement target) { + public static PsiElement turnInitIntoDir(@Nullable PsiElement target) { if (target instanceof PyFile && isPackage((PsiFile)target)) { return ((PsiFile)target).getContainingDirectory(); } diff --git a/python/src/com/jetbrains/python/psi/types/PyImportedModuleType.java b/python/src/com/jetbrains/python/psi/types/PyImportedModuleType.java index bc26a1a87ba2..01bcf032bfa2 100644 --- a/python/src/com/jetbrains/python/psi/types/PyImportedModuleType.java +++ b/python/src/com/jetbrains/python/psi/types/PyImportedModuleType.java @@ -15,17 +15,20 @@ */ package com.jetbrains.python.psi.types; +import com.google.common.collect.Sets; import com.intellij.codeInsight.lookup.LookupElement; -import com.intellij.codeInsight.lookup.LookupElementBuilder; +import com.intellij.openapi.util.Condition; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiElement; -import com.intellij.psi.util.QualifiedName; import com.intellij.util.ArrayUtil; import com.intellij.util.ProcessingContext; -import com.jetbrains.python.codeInsight.controlflow.ScopeOwner; -import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil; -import com.jetbrains.python.psi.*; +import com.intellij.util.containers.ContainerUtil; +import com.jetbrains.python.psi.AccessDirection; +import com.jetbrains.python.psi.PyExpression; +import com.jetbrains.python.psi.PyFile; +import com.jetbrains.python.psi.PyUtil; import com.jetbrains.python.psi.impl.PyImportedModule; +import com.jetbrains.python.psi.resolve.PointInImport; import com.jetbrains.python.psi.resolve.PyResolveContext; import com.jetbrains.python.psi.resolve.RatedResolveResult; import com.jetbrains.python.psi.resolve.ResolveImportUtil; @@ -35,6 +38,7 @@ import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Set; /** * @author yole @@ -58,33 +62,37 @@ public class PyImportedModuleType implements PyType { return new PyModuleType(file, myImportedModule).resolveMember(name, location, direction, resolveContext); } else if (resolved instanceof PsiDirectory) { - final List elements = Collections.singletonList(ResolveImportUtil.resolveChild(resolved, name, null, true, true)); + List elements = Collections.singletonList(ResolveImportUtil.resolveChild(resolved, name, null, true, true)); + if (location != null && ResolveImportUtil.getPointInImport(location) == PointInImport.NONE) { + final Set imported = Sets.newHashSet(PyModuleType.collectImportedSubmodules((PsiDirectory)resolved, location)); + elements = ContainerUtil.filter(elements, new Condition() { + @Override + public boolean value(PsiElement element) { + return imported.contains(element); + } + }); + } return ResolveImportUtil.rateResults(elements); } return null; } public Object[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) { - List result = new ArrayList(); - ScopeOwner scopeOwner = ScopeUtil.getScopeOwner(location); - assert scopeOwner != null; - final List importTargets = PyModuleType.getVisibleImports(scopeOwner); - final int imported = myImportedModule.getImportedPrefix().getComponentCount(); - for (PyImportElement importTarget : importTargets) { - final QualifiedName qName = importTarget.getImportedQName(); - if (qName != null && qName.matchesPrefix(myImportedModule.getImportedPrefix())) { - final List components = qName.getComponents(); - if (components.size() > imported) { - String module = components.get(imported); - result.add(LookupElementBuilder.create(module)); - } - } - } + final List result = new ArrayList(); final PsiElement resolved = myImportedModule.resolve(); - if (resolved instanceof PsiDirectory) { + if (resolved instanceof PyFile) { + final PyModuleType moduleType = new PyModuleType((PyFile)resolved, myImportedModule); + result.addAll(moduleType.getCompletionVariantsAsLookupElements(location, context, false, false)); + } + else if (resolved instanceof PsiDirectory) { final PsiDirectory dir = (PsiDirectory)resolved; if (PyUtil.isPackage(dir, location)) { - result.addAll(PyModuleType.getSubModuleVariants(dir, location, null)); + if (ResolveImportUtil.getPointInImport(location) != PointInImport.NONE) { + result.addAll(PyModuleType.getSubModuleVariants(dir, location, null)); + } + else { + result.addAll(PyModuleType.collectImportedSubmodulesAsLookupElements(dir, location, context.get(CTX_NAMES))); + } } } return ArrayUtil.toObjectArray(result); diff --git a/python/src/com/jetbrains/python/psi/types/PyModuleType.java b/python/src/com/jetbrains/python/psi/types/PyModuleType.java index de3be514462f..3fedadfd29c4 100644 --- a/python/src/com/jetbrains/python/psi/types/PyModuleType.java +++ b/python/src/com/jetbrains/python/psi/types/PyModuleType.java @@ -29,7 +29,9 @@ import com.intellij.psi.*; import com.intellij.psi.scope.PsiScopeProcessor; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.QualifiedName; +import com.intellij.util.Function; import com.intellij.util.ProcessingContext; +import com.intellij.util.containers.ContainerUtil; import com.jetbrains.python.PyNames; import com.jetbrains.python.codeInsight.PyCustomMember; import com.jetbrains.python.codeInsight.controlflow.ScopeOwner; @@ -331,7 +333,7 @@ public class PyModuleType implements PyType { // Modules don't descend from obje result.addAll(getSubModuleVariants(myModule.getContainingDirectory(), location, namesAlready)); } else { - addImportedSubmodules(location, namesAlready, result); + result.addAll(collectImportedSubmodulesAsLookupElements(myModule, location, namesAlready)); } } return result; @@ -355,27 +357,62 @@ public class PyModuleType implements PyType { // Modules don't descend from obje return processor; } - private void addImportedSubmodules(PsiElement location, Set existingNames, List result) { - PsiFile file = location.getContainingFile(); - if (file instanceof PyFile) { - PyFile pyFile = (PyFile)file; - PsiElement moduleBase = PyUtil.isPackage(myModule) ? myModule.getContainingDirectory() : myModule; - for (PyImportElement importElement : pyFile.getImportTargets()) { - PsiElement target = PyUtil.turnInitIntoDir(importElement.resolve()); - if (target != null && PsiTreeUtil.isAncestor(moduleBase, target, true)) { - LookupElement element = null; - if (target instanceof PsiFileSystemItem) { - element = buildFileLookupElement((PsiFileSystemItem) target, existingNames); - } - else if (target instanceof PsiNamedElement) { - element = LookupElementBuilder.createWithIcon((PsiNamedElement)target); - } - if (element != null) { - result.add(element); - } - } - } + @NotNull + public static List collectImportedSubmodulesAsLookupElements(@NotNull PsiFileSystemItem pyPackage, + @NotNull PsiElement location, + @Nullable final Set existingNames) { + + return ContainerUtil.mapNotNull(collectImportedSubmodules(pyPackage, location), + new Function() { + @Override + public LookupElement fun(PsiElement element) { + if (element instanceof PsiFileSystemItem) { + return buildFileLookupElement((PsiFileSystemItem)element, existingNames); + } + else if (element instanceof PsiNamedElement) { + return LookupElementBuilder.createWithIcon((PsiNamedElement)element); + } + return null; + } + }); + } + + @NotNull + public static List collectImportedSubmodules(@NotNull PsiFileSystemItem pyPackage, @NotNull PsiElement location) { + final PsiElement parentAnchor; + if (pyPackage instanceof PyFile && PyUtil.isPackage(((PyFile)pyPackage))) { + parentAnchor = ((PyFile)pyPackage).getContainingDirectory(); } + else if (pyPackage instanceof PsiDirectory && PyUtil.isPackage(((PsiDirectory)pyPackage), location)) { + parentAnchor = pyPackage; + } + else { + throw new IllegalArgumentException("Package argument should be either standard Python package or namespace package"); + } + + final ScopeOwner scopeOwner = ScopeUtil.getScopeOwner(location); + if (scopeOwner == null) { + return Collections.emptyList(); + } + final List result = new ArrayList(); + nextImportElement: + for (PyImportElement importElement : getVisibleImports(scopeOwner)) { + PsiElement resolvedChild = PyUtil.turnInitIntoDir(importElement.resolve()); + if (resolvedChild == null || !PsiTreeUtil.isAncestor(parentAnchor, resolvedChild, true)) { + continue; + } + QualifiedName importedQName = importElement.getImportedQName(); + // Looking for strict child of parentAncestor + while (resolvedChild != null && resolvedChild.getParent() != parentAnchor) { + if (importedQName == null || importedQName.getComponentCount() <= 1) { + continue nextImportElement; + } + importedQName = importedQName.removeTail(1); + resolvedChild = PyUtil.turnInitIntoDir(ResolveImportUtil.resolveImportElement(importElement, importedQName)); + } + ContainerUtil.addIfNotNull(result, resolvedChild); + } + return result; } public static List getSubModuleVariants(final PsiDirectory directory, diff --git a/python/testData/completion/attributeOfIndirectlyImportedPackage/a.after.py b/python/testData/completion/attributeOfIndirectlyImportedPackage/a.after.py new file mode 100644 index 000000000000..f217a2117a34 --- /dev/null +++ b/python/testData/completion/attributeOfIndirectlyImportedPackage/a.after.py @@ -0,0 +1,3 @@ +import pkg1.m1 + +pkg1.foo \ No newline at end of file diff --git a/python/testData/completion/attributeOfIndirectlyImportedPackage/a.py b/python/testData/completion/attributeOfIndirectlyImportedPackage/a.py new file mode 100644 index 000000000000..03e922263bd6 --- /dev/null +++ b/python/testData/completion/attributeOfIndirectlyImportedPackage/a.py @@ -0,0 +1,3 @@ +import pkg1.m1 + +pkg1.f \ No newline at end of file diff --git a/python/testData/completion/attributeOfIndirectlyImportedPackage/pkg1/__init__.py b/python/testData/completion/attributeOfIndirectlyImportedPackage/pkg1/__init__.py new file mode 100644 index 000000000000..79bf605b3de4 --- /dev/null +++ b/python/testData/completion/attributeOfIndirectlyImportedPackage/pkg1/__init__.py @@ -0,0 +1 @@ +foo = 42 \ No newline at end of file diff --git a/python/testData/completion/attributeOfIndirectlyImportedPackage/pkg1/m1.py b/python/testData/completion/attributeOfIndirectlyImportedPackage/pkg1/m1.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/testData/completion/notImportedSubmodulesOfNamespacePackage/a.after.py b/python/testData/completion/notImportedSubmodulesOfNamespacePackage/a.after.py new file mode 100644 index 000000000000..8d04a72755a1 --- /dev/null +++ b/python/testData/completion/notImportedSubmodulesOfNamespacePackage/a.after.py @@ -0,0 +1,3 @@ +import nspkg1.m2 + +nspkg1.m2 diff --git a/python/testData/completion/notImportedSubmodulesOfNamespacePackage/a.py b/python/testData/completion/notImportedSubmodulesOfNamespacePackage/a.py new file mode 100644 index 000000000000..1685a9106112 --- /dev/null +++ b/python/testData/completion/notImportedSubmodulesOfNamespacePackage/a.py @@ -0,0 +1,3 @@ +import nspkg1.m2 + +nspkg1.m diff --git a/python/testData/completion/notImportedSubmodulesOfNamespacePackage/nspkg1/m2.py b/python/testData/completion/notImportedSubmodulesOfNamespacePackage/nspkg1/m2.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/testData/completion/notImportedSubmodulesOfNamespacePackage/nspkg1/m3.py b/python/testData/completion/notImportedSubmodulesOfNamespacePackage/nspkg1/m3.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/testData/completion/submoduleOfIndirectlyImportedPackage/a.py b/python/testData/completion/submoduleOfIndirectlyImportedPackage/a.py new file mode 100644 index 000000000000..496be75feba7 --- /dev/null +++ b/python/testData/completion/submoduleOfIndirectlyImportedPackage/a.py @@ -0,0 +1,4 @@ +import pkg1.subpkg1.m1 +import pkg1 + +pkg1. \ No newline at end of file diff --git a/python/testData/completion/submoduleOfIndirectlyImportedPackage/pkg1/__init__.py b/python/testData/completion/submoduleOfIndirectlyImportedPackage/pkg1/__init__.py new file mode 100644 index 000000000000..fe5433bef744 --- /dev/null +++ b/python/testData/completion/submoduleOfIndirectlyImportedPackage/pkg1/__init__.py @@ -0,0 +1 @@ +VAR = 42 \ No newline at end of file diff --git a/python/testData/completion/submoduleOfIndirectlyImportedPackage/pkg1/subpkg1/__init__.py b/python/testData/completion/submoduleOfIndirectlyImportedPackage/pkg1/subpkg1/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/testData/completion/submoduleOfIndirectlyImportedPackage/pkg1/subpkg1/m1.py b/python/testData/completion/submoduleOfIndirectlyImportedPackage/pkg1/subpkg1/m1.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection3K/NotImportedSubmodulesOfNamespacePackage/main.py b/python/testData/inspections/PyUnresolvedReferencesInspection3K/NotImportedSubmodulesOfNamespacePackage/main.py new file mode 100644 index 000000000000..54e2273f4550 --- /dev/null +++ b/python/testData/inspections/PyUnresolvedReferencesInspection3K/NotImportedSubmodulesOfNamespacePackage/main.py @@ -0,0 +1,5 @@ +import nspkg1.m2 + +print(nspkg1.m2) +print(nspkg1.m3) +print(nspkg1.nssubpkg1) diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection3K/NotImportedSubmodulesOfNamespacePackage/nspkg1/m2.py b/python/testData/inspections/PyUnresolvedReferencesInspection3K/NotImportedSubmodulesOfNamespacePackage/nspkg1/m2.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection3K/NotImportedSubmodulesOfNamespacePackage/nspkg1/m3.py b/python/testData/inspections/PyUnresolvedReferencesInspection3K/NotImportedSubmodulesOfNamespacePackage/nspkg1/m3.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection3K/NotImportedSubmodulesOfNamespacePackage/nspkg1/nssubpkg1/m1.py b/python/testData/inspections/PyUnresolvedReferencesInspection3K/NotImportedSubmodulesOfNamespacePackage/nspkg1/nssubpkg1/m1.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/testSrc/com/jetbrains/python/Py3CompletionTest.java b/python/testSrc/com/jetbrains/python/Py3CompletionTest.java index 7e085a397a2e..51b463948b03 100644 --- a/python/testSrc/com/jetbrains/python/Py3CompletionTest.java +++ b/python/testSrc/com/jetbrains/python/Py3CompletionTest.java @@ -136,4 +136,9 @@ public class Py3CompletionTest extends PyTestCase { myFixture.completeBasic(); myFixture.checkResultByFile("completion/" + getTestName(true) + "/nspkg1/a.after.py"); } + + // PY-14385 + public void testNotImportedSubmodulesOfNamespacePackage() { + doMultiFileTest(); + } } diff --git a/python/testSrc/com/jetbrains/python/PythonCompletionTest.java b/python/testSrc/com/jetbrains/python/PythonCompletionTest.java index a1366ac2e17a..c6a66f4f9fb0 100644 --- a/python/testSrc/com/jetbrains/python/PythonCompletionTest.java +++ b/python/testSrc/com/jetbrains/python/PythonCompletionTest.java @@ -707,4 +707,19 @@ public class PythonCompletionTest extends PyTestCase { assertContainsElements(suggested, PyNames.FUNCTION_SPECIAL_ATTRIBUTES); assertDoesntContain(suggested, PyNames.METHOD_SPECIAL_ATTRIBUTES); } + + // PY-14388 + public void testAttributeOfIndirectlyImportedPackage() { + doMultiFileTest(); + } + + // PY-14387 + public void testSubmoduleOfIndirectlyImportedPackage() { + myFixture.copyDirectoryToProject("completion/" + getTestName(true), ""); + myFixture.configureByFile("a.py"); + myFixture.completeBasic(); + final List suggested = myFixture.getLookupElementStrings(); + assertNotNull(suggested); + assertSameElements(suggested, "VAR", "subpkg1"); + } } diff --git a/python/testSrc/com/jetbrains/python/inspections/Py3UnresolvedReferencesInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/Py3UnresolvedReferencesInspectionTest.java index 159a7218ff6f..32505db617a1 100644 --- a/python/testSrc/com/jetbrains/python/inspections/Py3UnresolvedReferencesInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/Py3UnresolvedReferencesInspectionTest.java @@ -111,4 +111,9 @@ public class Py3UnresolvedReferencesInspectionTest extends PyTestCase { public void testAttributesOfUnresolvedTypeFile() { doTest(); } + + // PY-14385 + public void testNotImportedSubmodulesOfNamespacePackage() { + doMultiFileTest("main.py"); + } } From c2bd7f6bd2fcb534b1bef3e749fb93c8e4578a73 Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Fri, 14 Nov 2014 15:53:20 +0300 Subject: [PATCH 7/8] Remove unused declarations from PyUtil --- .../src/com/jetbrains/python/psi/PyUtil.java | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/python/src/com/jetbrains/python/psi/PyUtil.java b/python/src/com/jetbrains/python/psi/PyUtil.java index 6232b90b18a2..b7aa91031d5e 100644 --- a/python/src/com/jetbrains/python/psi/PyUtil.java +++ b/python/src/com/jetbrains/python/psi/PyUtil.java @@ -38,7 +38,6 @@ import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.ui.MessageType; import com.intellij.openapi.ui.popup.Balloon; import com.intellij.openapi.ui.popup.JBPopupFactory; -import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.io.FileUtilRt; import com.intellij.openapi.util.text.StringUtil; @@ -95,8 +94,6 @@ import static com.jetbrains.python.psi.PyFunction.Modifier.STATICMETHOD; public class PyUtil { - private static final Object[] EMPTY_OBJECTS = new Object[0]; - private PyUtil() { } @@ -1232,23 +1229,6 @@ public class PyUtil { return false; } - public static class UnderscoreFilter implements Condition { - private int myAllowed; // how many starting underscores is allowed: 0 is none, 1 is only one, 2 is two and more. - - public UnderscoreFilter(int allowed) { - myAllowed = allowed; - } - - public boolean value(String name) { - if (name == null) return false; - if (name.length() < 1) return false; // empty strings make no sense - int have_underscores = 0; - if (name.charAt(0) == '_') have_underscores = 1; - if (have_underscores != 0 && name.length() > 1 && name.charAt(1) == '_') have_underscores = 2; - return myAllowed >= have_underscores; - } - } - @Nullable public static String getKeywordArgumentString(PyCallExpression expr, String keyword) { return PyPsiUtils.strValue(expr.getKeywordArgument(keyword)); From e3bd7521d304c3c82d76694707035a532777e595 Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Fri, 14 Nov 2014 16:02:00 +0300 Subject: [PATCH 8/8] Fix issues from review IDEA-COMMUNITY-CR-901 --- .../python/inspections/PyPep8NamingInspection.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/python/src/com/jetbrains/python/inspections/PyPep8NamingInspection.java b/python/src/com/jetbrains/python/inspections/PyPep8NamingInspection.java index b7323c87e6e5..9a584e0ffff4 100644 --- a/python/src/com/jetbrains/python/inspections/PyPep8NamingInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyPep8NamingInspection.java @@ -31,7 +31,6 @@ import com.intellij.psi.util.QualifiedName; import com.intellij.ui.components.JBList; import com.intellij.util.Consumer; import com.intellij.util.Function; -import com.intellij.util.SmartList; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.ui.CheckBox; import com.jetbrains.python.codeInsight.controlflow.ControlFlowCache; @@ -39,6 +38,7 @@ import com.jetbrains.python.codeInsight.dataflow.scope.Scope; import com.jetbrains.python.inspections.quickfix.PyRenameElementQuickFix; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.search.PySuperMethodsSearch; +import com.jetbrains.python.psi.types.PyClassLikeType; import com.jetbrains.python.psi.types.TypeEvalContext; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -117,7 +117,7 @@ public class PyPep8NamingInspection extends PyInspection { if (!LOWERCASE_REGEX.matcher(name).matches()) { final ASTNode nameNode = function.getNameNode(); if (nameNode != null) { - final List quickFixes = new SmartList(new PyRenameElementQuickFix()); + final List quickFixes = Lists.newArrayList(new PyRenameElementQuickFix()); if (containingClass != null) { quickFixes.add(new IgnoreBaseClassQuickFix(containingClass, myTypeEvalContext)); } @@ -135,8 +135,8 @@ public class PyPep8NamingInspection extends PyInspection { if (blackList.contains(pyClass.getQualifiedName())) { return true; } - for (PyClass ancestor : pyClass.getAncestorClasses(myTypeEvalContext)) { - if (blackList.contains(ancestor.getQualifiedName())) { + for (PyClassLikeType ancestor : pyClass.getAncestorTypes(myTypeEvalContext)) { + if (blackList.contains(ancestor.getClassQName())) { return true; } }