diff --git a/src/Analysis/Ast/Impl/Analyzer/PythonAnalyzer.cs b/src/Analysis/Ast/Impl/Analyzer/PythonAnalyzer.cs index 205e64d01..427a1e0b9 100644 --- a/src/Analysis/Ast/Impl/Analyzer/PythonAnalyzer.cs +++ b/src/Analysis/Ast/Impl/Analyzer/PythonAnalyzer.cs @@ -356,7 +356,15 @@ public Task> FindDependenciesAsync(PythonAnalyzerEntry } break; case FromImportStatement fromImport: - HandleSearchResults(isTypeshed, dependencies, moduleResolution, pathResolver.FindImports(module.FilePath, fromImport)); + var imports = pathResolver.FindImports(module.FilePath, fromImport); + HandleSearchResults(isTypeshed, dependencies, moduleResolution, imports); + if (imports is IImportChildrenSource childrenSource) { + foreach (var name in fromImport.Names) { + if (childrenSource.TryGetChildImport(name.Name, out var childImport)) { + HandleSearchResults(isTypeshed, dependencies, moduleResolution, childImport); + } + } + } break; } } diff --git a/src/LanguageServer/Test/GoToDefinitionTests.cs b/src/LanguageServer/Test/GoToDefinitionTests.cs index 2a8dda4ef..299ad4651 100644 --- a/src/LanguageServer/Test/GoToDefinitionTests.cs +++ b/src/LanguageServer/Test/GoToDefinitionTests.cs @@ -13,8 +13,11 @@ // See the Apache Version 2.0 License for specific language governing // permissions and limitations under the License. +using System; +using System.IO; using System.Threading.Tasks; using FluentAssertions; +using Microsoft.Python.Analysis.Documents; using Microsoft.Python.Core.Text; using Microsoft.Python.LanguageServer.Sources; using Microsoft.Python.LanguageServer.Tests.FluentAssertions; @@ -161,5 +164,27 @@ class A(object): var reference = ds.FindDefinition(analysis, new SourceLocation(2, 12)); reference.Should().BeNull(); } + + [TestMethod, Priority(0)] + public async Task GotoRelativeImportInExplicitPackage() { + var pkgPath = TestData.GetTestSpecificUri("pkg", "__init__.py"); + var modPath = TestData.GetTestSpecificUri("pkg", "mod.py"); + var subpkgPath = TestData.GetTestSpecificUri("pkg", "subpkg", "__init__.py"); + var submodPath = TestData.GetTestSpecificUri("pkg", "submod", "__init__.py"); + + var root = TestData.GetTestSpecificRootUri().AbsolutePath; + await CreateServicesAsync(root, PythonVersions.LatestAvailable3X); + var rdt = Services.GetService(); + + rdt.OpenDocument(pkgPath, string.Empty); + rdt.OpenDocument(modPath, "hello = 'World'"); + rdt.OpenDocument(subpkgPath, string.Empty); + var submod = rdt.OpenDocument(submodPath, "from .. import mod"); + + var analysis = await submod.GetAnalysisAsync(-1); + var ds = new DefinitionSource(); + var reference = ds.FindDefinition(analysis, new SourceLocation(1, 18)); + reference.uri.Should().Be(modPath); + } } } diff --git a/src/UnitTests/Core/Impl/TestData.cs b/src/UnitTests/Core/Impl/TestData.cs index 3b67963b9..4968b179e 100644 --- a/src/UnitTests/Core/Impl/TestData.cs +++ b/src/UnitTests/Core/Impl/TestData.cs @@ -80,9 +80,11 @@ public static Uri[] GetNextModuleUris(int count) { } public static Uri GetTestSpecificUri(string relativePath) => new Uri(GetTestSpecificPath(relativePath)); + public static Uri GetTestSpecificUri(params string[] parts) => new Uri(GetTestSpecificPath(parts)); public static Uri GetTestSpecificRootUri() => TestRunScopeAsyncLocal.Value.RootUri; public static string GetTestSpecificPath(string relativePath) => TestRunScopeAsyncLocal.Value.GetTestSpecificPath(relativePath); + public static string GetTestSpecificPath(params string[] parts) => TestRunScopeAsyncLocal.Value.GetTestSpecificPath(Path.Combine(parts)); public static string GetTestRelativePath(Uri uri) => TestRunScopeAsyncLocal.Value.GetTestRelativePath(uri); public static string GetDefaultModulePath() => TestRunScopeAsyncLocal.Value.GetDefaultModulePath(); public static string GetNextModulePath() => TestRunScopeAsyncLocal.Value.GetNextModulePath();