Resolve to project sources first instead of libraries and stubs (PY-7775).

This commit is contained in:
Dmitry Trofimov
2012-10-22 00:07:32 +02:00
parent d210d1228b
commit 606a52f82f
10 changed files with 73 additions and 39 deletions
@@ -533,7 +533,7 @@ class DocumentationBuilder {
myPath = path;
}
public boolean visitRoot(VirtualFile root, Module module, Sdk sdk) {
public boolean visitRoot(VirtualFile root, Module module, Sdk sdk, boolean isModuleSource) {
String vpath = VfsUtil.urlToPath(root.getUrl());
if (myPath.startsWith(vpath)) {
myResult = vpath;
@@ -125,11 +125,13 @@ public class QualifiedNameFinder {
/**
* Tries to find roots that contain given vfile, and among them the root that contains at the smallest depth.
* For equal depth source root is in preference to library.
*/
private static class PathChoosingVisitor implements RootVisitor {
private final VirtualFile myVFile;
private List<String> myResult;
private boolean myIsModuleSource;
private PathChoosingVisitor(VirtualFile file) {
if (!file.isDirectory() && file.getName().equals(PyNames.INIT_DOT_PY)) {
@@ -140,11 +142,11 @@ public class QualifiedNameFinder {
}
}
public boolean visitRoot(VirtualFile root, Module module, Sdk sdk) {
public boolean visitRoot(VirtualFile root, Module module, Sdk sdk, boolean isModuleSource) {
final String relativePath = VfsUtilCore.getRelativePath(myVFile, root, '/');
if (relativePath != null) {
List<String> result = StringUtil.split(relativePath, "/");
if (myResult == null || result.size() < myResult.size()) {
if (myResult == null || result.size() < myResult.size() || (isModuleSource && !myIsModuleSource)) {
if (result.size() > 0) {
result.set(result.size() - 1, FileUtil.getNameWithoutExtension(result.get(result.size() - 1)));
}
@@ -154,6 +156,7 @@ public class QualifiedNameFinder {
}
}
myResult = result;
myIsModuleSource = isModuleSource;
}
}
return myResult == null || myResult.size() > 0;
@@ -36,7 +36,8 @@ public class QualifiedNameResolverImpl implements RootVisitor, QualifiedNameReso
boolean myCheckForPackage = true;
private final QualifiedNameResolveContext myContext = new QualifiedNameResolveContext();
private final @NotNull PyQualifiedName myQualifiedName;
final Set<PsiElement> results = Sets.newLinkedHashSet();
final Set<PsiElement> mySourceResults = Sets.newLinkedHashSet();
final Set<PsiElement> myLibResults = Sets.newLinkedHashSet();
private boolean myVisitAllModules = false;
private int myRelativeLevel = -1;
private boolean myWithoutRoots;
@@ -102,7 +103,7 @@ public class QualifiedNameResolverImpl implements RootVisitor, QualifiedNameReso
/**
* Specifies whether we should attempt to resolve imports relative to the current file.
*
*
* @param relativeLevel if >= 0, we try to resolve at the specified number of levels above the current file.
* @return this
*/
@@ -140,26 +141,35 @@ public class QualifiedNameResolverImpl implements RootVisitor, QualifiedNameReso
myCheckForPackage = false;
return this;
}
public boolean visitRoot(final VirtualFile root, @Nullable Module module, @Nullable Sdk sdk) {
public boolean visitRoot(final VirtualFile root, @Nullable Module module, @Nullable Sdk sdk, boolean isModuleSource) {
if (!root.isValid()) {
return true;
}
PsiFileSystemItem resolveResult = resolveInRoot(root);
if (resolveResult != null) {
results.add(resolveResult);
addRoot(resolveResult, isModuleSource);
}
if (isAcceptRootAsTopLevelPackage() && myQualifiedName.matchesPrefix(PyQualifiedName.fromDottedString(root.getName()))) {
resolveResult = resolveInRoot(root.getParent());
if (resolveResult != null) {
results.add(resolveResult);
addRoot(resolveResult, isModuleSource);
}
}
return true;
}
private void addRoot(PsiFileSystemItem resolveResult, boolean isModuleSource) {
if (isModuleSource) {
mySourceResults.add(resolveResult);
}
else {
myLibResults.add(resolveResult);
}
}
@Override
@NotNull
public List<PsiElement> resultsAsList() {
@@ -177,24 +187,27 @@ public class QualifiedNameResolverImpl implements RootVisitor, QualifiedNameReso
PsiFileSystemItem module = resolveModuleAt(dir);
if (module != null) {
results.add(module);
addRoot(module, true);
}
}
if (!myWithoutRoots) {
results.addAll(resolveInRoots());
mySourceResults.addAll(resolveInRoots());
}
if (!myWithoutForeign) {
for (PyImportResolver resolver : Extensions.getExtensions(PyImportResolver.EP_NAME)) {
PsiElement foreign = resolver.resolveImportReference(myQualifiedName, myContext);
if (foreign != null) {
results.add(foreign);
mySourceResults.add(foreign);
}
}
}
return Lists.newArrayList(results);
mySourceResults.addAll(myLibResults);
myLibResults.clear();
return Lists.newArrayList(mySourceResults);
}
private List<PsiElement> resolveInRoots() {
@@ -228,7 +241,9 @@ public class QualifiedNameResolverImpl implements RootVisitor, QualifiedNameReso
throw new IllegalStateException();
}
final ArrayList<PsiElement> resultList = Lists.newArrayList(results);
mySourceResults.addAll(myLibResults);
myLibResults.clear();
final ArrayList<PsiElement> resultList = Lists.newArrayList(mySourceResults);
if (cache != null) {
cache.put(myQualifiedName, resultList);
}
@@ -249,18 +264,18 @@ public class QualifiedNameResolverImpl implements RootVisitor, QualifiedNameReso
for (PsiElement element : resultsAsList()) {
if (clazz.isInstance(element)) {
//noinspection unchecked
result.add((T) element);
result.add((T)element);
}
}
return result;
}
}
@Override
@Nullable
public <T extends PsiElement> T firstResultOfType(Class<T> clazz) {
final List<T> list = resultsOfType(clazz);
return list.size() > 0 ? list.get(0) : null;
}
}
private boolean withOtherSdk() {
return myContext.getSdk() != null && myContext.getSdk() != PythonSdkType.findPythonSdk(myContext.getModule());
@@ -307,9 +322,8 @@ public class QualifiedNameResolverImpl implements RootVisitor, QualifiedNameReso
if (name == null) {
return null;
}
seeker = (PsiFileSystemItem) ResolveImportUtil.resolveChild(seeker, name, myContext.getFootholdFile(), true, myCheckForPackage);
seeker = (PsiFileSystemItem)ResolveImportUtil.resolveChild(seeker, name, myContext.getFootholdFile(), true, myCheckForPackage);
}
return seeker;
}
}
@@ -18,5 +18,5 @@ public interface RootVisitor {
*
* @return false when visiting must stop.
*/
boolean visitRoot(VirtualFile root, @Nullable Module module, @Nullable Sdk sdk);
boolean visitRoot(VirtualFile root, @Nullable Module module, @Nullable Sdk sdk, boolean isModuleSource);
}
@@ -79,7 +79,7 @@ public class RootVisitorHost {
public static boolean visitSdkRoots(@NotNull Sdk sdk, @NotNull RootVisitor visitor) {
final VirtualFile[] roots = sdk.getRootProvider().getFiles(OrderRootType.CLASSES);
for (VirtualFile root : roots) {
if (!visitor.visitRoot(root, null, sdk)) {
if (!visitor.visitRoot(root, null, sdk, false)) {
return true;
}
}
@@ -92,10 +92,10 @@ public class RootVisitorHost {
for (ContentEntry entry : rootModel.getContentEntries()) {
VirtualFile rootFile = entry.getFile();
if (rootFile != null && !visitor.visitRoot(rootFile, null, null)) return false;
if (rootFile != null && !visitor.visitRoot(rootFile, null, null, true)) return false;
contentRoots.add(rootFile);
for (VirtualFile folder : entry.getSourceFolderFiles()) {
if (!visitor.visitRoot(folder, rootModel.getModule(), null)) return false;
if (!visitor.visitRoot(folder, rootModel.getModule(), null, true)) return false;
}
}
return true;
@@ -108,7 +108,7 @@ public class RootVisitorHost {
Module module = entry instanceof ModuleOrderEntry ? ((ModuleOrderEntry) entry).getModule() : null;
Sdk sdk = entry instanceof JdkOrderEntry ? ((JdkOrderEntry) entry).getJdk() : null;
for (VirtualFile root : allRoots) {
if (!visitor.visitRoot(root, module, sdk)) {
if (!visitor.visitRoot(root, module, sdk, false)) {
return false;
}
}
@@ -0,0 +1,5 @@
import re
re.foobar()
# <ref>
@@ -0,0 +1,4 @@
#re module in project sources
def foobar():
pass
@@ -26,7 +26,7 @@ public class PyMultiFileResolveTest extends PyResolveTestCase {
public void testSimple() {
PsiElement element = doResolve();
assertTrue(element instanceof PyFile);
assertEquals("ImportedFile.py", ((PyFile) element).getName());
assertEquals("ImportedFile.py", ((PyFile)element).getName());
}
public void testFromImport() {
@@ -34,7 +34,7 @@ public class PyMultiFileResolveTest extends PyResolveTestCase {
assertTrue(results.length == 2); // func and import stmt
PsiElement func_elt = results[0].getElement();
assertTrue("is PyFunction?", func_elt instanceof PyFunction);
assertEquals("named 'func'?", "func", ((PyFunction) func_elt).getName());
assertEquals("named 'func'?", "func", ((PyFunction)func_elt).getName());
PsiElement import_elt = results[1].getElement();
assertTrue("is import?", import_elt instanceof PyImportElement);
}
@@ -44,7 +44,7 @@ public class PyMultiFileResolveTest extends PyResolveTestCase {
assertTrue(results.length == 2); // func and import-* stmt
PsiElement func_elt = results[0].getElement();
assertTrue("is PyFunction?", func_elt instanceof PyFunction);
assertEquals("named 'func'?", "func", ((PyFunction) func_elt).getName());
assertEquals("named 'func'?", "func", ((PyFunction)func_elt).getName());
PsiElement import_elt = results[1].getElement();
assertTrue("is import?", import_elt instanceof PyStarImportElement);
}
@@ -57,7 +57,7 @@ public class PyMultiFileResolveTest extends PyResolveTestCase {
public void testFromPackageImportFile() {
PsiElement element = doResolve();
assertTrue(element instanceof PsiFile);
assertEquals("myfile.py", ((PyFile) element).getName());
assertEquals("myfile.py", ((PyFile)element).getName());
}
public void testFromQualifiedPackageImport() {
@@ -68,20 +68,20 @@ public class PyMultiFileResolveTest extends PyResolveTestCase {
public void testFromQualifiedFileImportClass() {
PsiElement element = doResolve();
assertTrue(element instanceof PsiFile);
assertEquals("myfile.py", ((PsiFile) element).getName());
assertEquals("mypackage", ((PsiFile) element).getContainingDirectory().getName());
assertEquals("myfile.py", ((PsiFile)element).getName());
assertEquals("mypackage", ((PsiFile)element).getContainingDirectory().getName());
}
public void testImportAs() {
PsiElement element = doResolve();
assertTrue(element instanceof PyFunction);
assertEquals("func", ((PyFunction) element).getName());
assertEquals("func", ((PyFunction)element).getName());
}
public void testFromQualifiedPackageImportFile() {
PsiElement element = doResolve();
assertTrue(element instanceof PsiFile);
assertEquals("testfile.py", ((PsiFile) element).getName());
assertEquals("testfile.py", ((PsiFile)element).getName());
}
public void testFromInitPyImportFunction() {
@@ -101,7 +101,7 @@ public class PyMultiFileResolveTest extends PyResolveTestCase {
final PsiFile psiFile = myFixture.configureByFile("pack/__init__.py");
final PsiElement result = doResolve(psiFile);
assertInstanceOf(result, PyFile.class);
assertEquals("mod.py", ((PyFile) result).getName());
assertEquals("mod.py", ((PyFile)result).getName());
}
public void testResolveInPkg() {
@@ -109,7 +109,7 @@ public class PyMultiFileResolveTest extends PyResolveTestCase {
assertTrue(results.length == 2); // func and import stmt
PsiElement func_elt = results[0].getElement();
assertTrue("is PyFunction?", func_elt instanceof PyFunction);
assertEquals("named 'token'?", "token", ((PyFunction) func_elt).getName());
assertEquals("named 'token'?", "token", ((PyFunction)func_elt).getName());
PsiElement import_elt = results[1].getElement();
assertTrue("is import?", import_elt instanceof PyImportElement);
}
@@ -186,7 +186,7 @@ public class PyMultiFileResolveTest extends PyResolveTestCase {
PsiElement element = doResolve();
element = element.getNavigationElement();
assertInstanceOf(element, PyFile.class);
assertEquals("__init__.py", ((PyFile) element).getName());
assertEquals("__init__.py", ((PyFile)element).getName());
}
public void testImportOsPath() {
@@ -405,7 +405,8 @@ public class PyMultiFileResolveTest extends PyResolveTestCase {
setLanguageLevel(LanguageLevel.PYTHON33);
try {
assertResolvesTo(PsiDirectory.class, "p1");
} finally {
}
finally {
setLanguageLevel(null);
}
}
@@ -415,11 +416,18 @@ public class PyMultiFileResolveTest extends PyResolveTestCase {
setLanguageLevel(LanguageLevel.PYTHON33);
try {
assertResolvesTo(PyFunction.class, "foo");
} finally {
}
finally {
setLanguageLevel(null);
}
}
// PY-7775
public void testProjectSourcesFirst() {
myTestFileName = "mod/" + getTestName(false) + ".py";
assertResolvesTo(PyFunction.class, "foobar");
}
private void prepareTestDirectory() {
final String testName = getTestName(true);
myFixture.copyDirectoryToProject(testName, "");
@@ -429,7 +437,7 @@ public class PyMultiFileResolveTest extends PyResolveTestCase {
private PsiFile prepareFile() {
prepareTestDirectory();
VirtualFile sourceFile = null;
for (String ext : new String[] {".py", ".pyx"}) {
for (String ext : new String[]{".py", ".pyx"}) {
final String fileName = myTestFileName != null ? myTestFileName : getTestName(false) + ext;
sourceFile = myFixture.findFileInTempDir(fileName);
if (sourceFile != null) {
@@ -460,7 +468,7 @@ public class PyMultiFileResolveTest extends PyResolveTestCase {
if (resolveResults.length == 0) {
return null;
}
return resolveResults[0].isValidResult() ? resolveResults [0].getElement() : null;
return resolveResults[0].isValidResult() ? resolveResults[0].getElement() : null;
}
finally {
psiManager.setAssertOnFileLoadingFilter(VirtualFileFilter.NONE);