Skip to content
This repository was archived by the owner on Apr 14, 2022. It is now read-only.

Allow navigation to stubs #1960

Merged
merged 42 commits into from
May 1, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
c6a9c22
Remove stale reference
Sep 30, 2019
1360827
Merge branch 'master' of https://github.com/microsoft/python-language…
Oct 1, 2019
ccaaa02
Merge branch 'master' of https://github.com/microsoft/python-language…
Oct 4, 2019
da40dcc
Merge branch 'master' of https://github.com/microsoft/python-language…
Oct 4, 2019
c348ac3
Merge branch 'master' of https://github.com/microsoft/python-language…
Oct 5, 2019
53bc044
Merge branch 'master' of https://github.com/microsoft/python-language…
Oct 7, 2019
484a92a
Merge branch 'master' of https://github.com/MikhailArkhipov/python-la…
Oct 7, 2019
e6df3aa
Merge branch 'master' of https://github.com/microsoft/python-language…
Oct 8, 2019
1d289d8
Merge branch 'master' of https://github.com/microsoft/python-language…
Oct 8, 2019
126f355
Merge branch 'master' of https://github.com/microsoft/python-language…
Oct 12, 2019
7e715f3
Merge branch 'master' of https://github.com/microsoft/python-language…
Oct 25, 2019
32923a5
Merge branch 'master' of https://github.com/microsoft/python-language…
Oct 31, 2019
1b72f4b
Merge branch 'master' of https://github.com/microsoft/python-language…
Nov 2, 2019
f74d5b6
Merge branch 'master' of https://github.com/microsoft/python-language…
Nov 7, 2019
0b28fa4
Merge branch 'master' of https://github.com/microsoft/python-language…
Nov 12, 2019
6109ac7
Don't suppress LHS diagnostics on augmented assign
Nov 12, 2019
bcfc3b7
Revert "Don't suppress LHS diagnostics on augmented assign"
Nov 12, 2019
dc286b8
Merge branch 'master' of https://github.com/microsoft/python-language…
Nov 13, 2019
0aab4f5
Merge branch 'master' of https://github.com/microsoft/python-language…
Nov 16, 2019
b953ce7
Merge branch 'master' of https://github.com/microsoft/python-language…
Nov 18, 2019
aef887d
Merge branch 'master' of https://github.com/microsoft/python-language…
Dec 10, 2019
b97b641
Merge branch 'master' of https://github.com/microsoft/python-language…
Dec 11, 2019
c16646d
Escape [ and ]
Dec 13, 2019
f3e08d5
Merge branch 'master' of https://github.com/MikhailArkhipov/python-la…
Dec 13, 2019
5700642
PR feedback
Dec 13, 2019
1d091db
Merge branch 'master' of https://github.com/microsoft/python-language…
Dec 13, 2019
b8c3615
Merge branch 'master' of https://github.com/microsoft/python-language…
Dec 16, 2019
6a88069
Merge branch 'master' of https://github.com/microsoft/python-language…
Jan 28, 2020
078c975
Merge branch 'master' of https://github.com/microsoft/python-language…
Mar 12, 2020
ac07704
Merge master
Mar 18, 2020
40be62b
Merge branch 'master' of https://github.com/microsoft/python-language…
Mar 19, 2020
b384853
Merge branch 'master' of https://github.com/microsoft/python-language…
Mar 20, 2020
e2d4146
Allow navigation to stubs
Mar 20, 2020
fd64620
Merge branch 'master' of https://github.com/microsoft/python-language…
Mar 20, 2020
481e7f5
Better handle stubs for compiled modules
Mar 23, 2020
c3efd37
Merge branch 'master' of https://github.com/microsoft/python-language…
Mar 26, 2020
1f6af48
Merge branch 'master' of https://github.com/microsoft/python-language…
Apr 17, 2020
39690f8
Merge branch 'master' of https://github.com/microsoft/python-language…
Apr 25, 2020
31fe80d
Fix typeshed path
Apr 26, 2020
9b7c4ee
Partial undo
Apr 26, 2020
443c1d8
Partial undo
Apr 26, 2020
695d715
Undo accidental change
May 1, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,8 @@ public IDisposable OpenScope(IPythonModule module, ScopeStatement node, out Scop
return new ScopeTracker(this);
}

internal void ReplaceVariable(IVariable v) => CurrentScope.ReplaceVariable(v);

private class ScopeTracker : IDisposable {
private readonly ExpressionEval _eval;

Expand Down
4 changes: 3 additions & 1 deletion src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@ public ExpressionEval(IServiceContainer services, IPythonModule module, PythonAs
public LocationInfo GetLocationInfo(Node node) => node?.GetLocation(this) ?? LocationInfo.Empty;

public Location GetLocationOfName(Node node) {
if (node == null || Module.ModuleType != ModuleType.User && Module.ModuleType != ModuleType.Library) {
if (node == null ||
Module.ModuleType == ModuleType.Specialized || Module.ModuleType == ModuleType.Compiled ||
Module.ModuleType == ModuleType.CompiledBuiltin || Module.ModuleType == ModuleType.Builtins) {
return DefaultLocation;
}

Expand Down
26 changes: 23 additions & 3 deletions src/Analysis/Ast/Impl/Analyzer/StubMerger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,10 @@ private void TryReplaceMember(IVariable v, IPythonType sourceType, IPythonType s
MergeMembers(v, sourceFunction, stubType, cancellationToken);
break;

case PythonPropertyType sourceProperty:
MergeMembers(v, sourceProperty, stubType, cancellationToken);
break;

case IPythonModule _:
// We do not re-declare modules.
break;
Expand Down Expand Up @@ -181,7 +185,17 @@ private void MergeMembers(IVariable v, IPythonType sourceType, IPythonType stubT
// Replace the class entirely since stub members may use generic types
// and the class definition is important. We transfer missing members
// from the original class to the stub.
_eval.DeclareVariable(v.Name, v.Value, v.Source);
//
// In case module is compiled, it is already a stub and has no locations
// for code navigation.. In this case we replace the entire variable by one
// from the stub rather than just the value since stub variable has location
// and its own root definition/reference chain.
if (sourceType.DeclaringModule.ModuleType == ModuleType.Compiled ||
sourceType.DeclaringModule.ModuleType == ModuleType.CompiledBuiltin) {
_eval.ReplaceVariable(v);
} else {
_eval.DeclareVariable(v.Name, v.Value, v.Source);
}

// First pass: go through source class members and pick those
// that are not present in the stub class.
Expand All @@ -193,7 +207,7 @@ private void MergeMembers(IVariable v, IPythonType sourceType, IPythonType stubT
continue; // Do not add unknowns to the stub.
}
var sourceMemberType = sourceMember?.GetPythonType();
if (sourceMemberType is IPythonClassMember cm && cm.DeclaringType != sourceType) {
if (sourceMemberType is IPythonClassMember cm && !cm.DeclaringModule.Equals(sourceType.DeclaringModule)) {
continue; // Only take members from this class and not from bases.
}
if (!IsFromThisModuleOrSubmodules(sourceMemberType)) {
Expand Down Expand Up @@ -340,7 +354,13 @@ private bool IsFromThisModuleOrSubmodules(IPythonType type) {
var thisModule = _eval.Module;
var typeModule = type.DeclaringModule;
var typeMainModuleName = typeModule.Name.Split('.').FirstOrDefault();
return typeModule.Equals(thisModule) || typeMainModuleName == thisModule.Name;
if (typeModule.Equals(thisModule) || typeMainModuleName == thisModule.Name) {
return true;
}
// Check if module is explicitly imported by the current one. For example, 'os'
// imports 'nt' and os.pyi specifies functions from 'nt' such as mkdir and so on.
var imported = thisModule.GlobalScope.Variables[typeModule.Name];
return imported?.Value != null && imported.Source == VariableSource.Import;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,11 @@ internal sealed class TypeshedResolution : ModuleResolutionBase, IModuleResoluti

public TypeshedResolution(string root, IServiceContainer services) : base(root, services) {
// TODO: merge with user-provided stub paths
var stubs = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Stubs");
_typeStubPaths = GetTypeShedPaths(Root)
var asmLocation = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
var stubs = Path.Combine(asmLocation, "Stubs");
var typeshedRoot = Root ?? Path.Combine(asmLocation, "Typeshed");

_typeStubPaths = GetTypeShedPaths(typeshedRoot)
.Concat(GetTypeShedPaths(stubs))
.Where(services.GetService<IFileSystem>().DirectoryExists)
.ToImmutableArray();
Expand Down
5 changes: 5 additions & 0 deletions src/Analysis/Ast/Impl/Values/Scope.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@ public void DeclareImported(string name, IMember value, Location location = defa

internal void AddChildScope(Scope s) => (_childScopes ?? (_childScopes = new Dictionary<ScopeStatement, Scope>()))[s.Node] = s;

internal void ReplaceVariable(IVariable v) {
VariableCollection.RemoveVariable(v.Name);
VariableCollection.DeclareVariable(v.Name, v.Value, v.Source, v.Location);
}

private VariableCollection VariableCollection => _variables ?? (_variables = new VariableCollection());

private void DeclareBuiltinVariables() {
Expand Down
18 changes: 12 additions & 6 deletions src/LanguageServer/Impl/Implementation/Server.Editor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,17 +89,23 @@ public async Task<Reference[]> GotoDefinition(TextDocumentPositionParams @params
_log?.Log(TraceEventType.Verbose, $"Goto Definition in {uri} at {@params.position}");

var analysis = await Document.GetAnalysisAsync(uri, Services, CompletionAnalysisTimeout, cancellationToken);
var reference = new DefinitionSource(Services).FindDefinition(analysis, @params.position, out _);
return reference != null ? new[] { reference } : Array.Empty<Reference>();
var ds = new DefinitionSource(Services);
var reference = ds.FindDefinition(analysis, @params.position, out _);
return reference != null && ds.CanNavigateToModule(reference.uri)
? new[] { reference }
: Array.Empty<Reference>();
}

public async Task<Location> GotoDeclaration(TextDocumentPositionParams @params, CancellationToken cancellationToken) {
var uri = @params.textDocument.uri;
_log?.Log(TraceEventType.Verbose, $"Goto Declaration in {uri} at {@params.position}");

var analysis = await Document.GetAnalysisAsync(uri, Services, CompletionAnalysisTimeout, cancellationToken);
var reference = new DeclarationSource(Services).FindDefinition(analysis, @params.position, out _);
return reference != null ? new Location { uri = reference.uri, range = reference.range } : null;
var ds = new DeclarationSource(Services);
var reference = ds.FindDefinition(analysis, @params.position, out _);
return reference != null && ds.CanNavigateToModule(reference.uri)
? new Location { uri = reference.uri, range = reference.range }
: null;
}

public Task<Reference[]> FindReferences(ReferencesParams @params, CancellationToken cancellationToken) {
Expand Down Expand Up @@ -137,8 +143,8 @@ public async Task<CodeAction[]> CodeAction(CodeActionParams @params, Cancellatio

return codeActions.ToArray();

static bool AskedFor(CodeActionParams @params, string codeActionKind) {
return @params.context.only == null || @params.context.only.Any(s => s.StartsWith(codeActionKind));
bool AskedFor(CodeActionParams p, string codeActionKind) {
return p.context.only == null || p.context.only.Any(s => s.StartsWith(codeActionKind));
}
}
}
Expand Down
25 changes: 18 additions & 7 deletions src/LanguageServer/Impl/Sources/DefinitionSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
// permissions and limitations under the License.

using System;
using System.IO;
using System.Linq;
using Microsoft.Python.Analysis;
using Microsoft.Python.Analysis.Analyzer;
Expand Down Expand Up @@ -360,22 +361,32 @@ private Reference FromMember(IMember m) {
return null;
}

private bool CanNavigateToModule(Uri uri) {
public bool CanNavigateToModule(Uri uri) {
if (uri == null) {
return false;
}

if (!CanNavigateToPath(uri.LocalPath)) {
return false;
}
var rdt = _services.GetService<IRunningDocumentTable>();
var doc = rdt.GetDocument(uri);
// Allow navigation to modules not in RDT - most probably
// it is a module that was restored from database.
return doc == null || CanNavigateToModule(doc);
}

private static bool CanNavigateToModule(IPythonModule m)
=> m?.ModuleType == ModuleType.User ||
m?.ModuleType == ModuleType.Stub ||
m?.ModuleType == ModuleType.Package ||
m?.ModuleType == ModuleType.Library ||
m?.ModuleType == ModuleType.Specialized;
private static bool CanNavigateToModule(IPythonModule m) {
if(m == null || !CanNavigateToPath(m.FilePath)) {
return false;
}
return m.ModuleType == ModuleType.User ||
m.ModuleType == ModuleType.Stub ||
m.ModuleType == ModuleType.Package ||
m.ModuleType == ModuleType.Library ||
m.ModuleType == ModuleType.Specialized;
}

private static bool CanNavigateToPath(string path) => Path.GetExtension(path) != ".exe";
}
}
14 changes: 14 additions & 0 deletions src/LanguageServer/Test/GoToDefinitionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -722,5 +722,19 @@ def func(a, b):
var reference = ds.FindDefinition(analysis, new SourceLocation(3, 12), out _);
reference.range.Should().Be(1, 9, 1, 10);
}

[TestMethod, Priority(0)]
public async Task CompiledCode() {
const string code = @"
import os
os.mkdir()
";
var analysis = await GetAnalysisAsync(code);
var ds = new DefinitionSource(Services);
var reference = ds.FindDefinition(analysis, new SourceLocation(3, 6), out _);
reference.range.start.line.Should().NotBe(1);
reference.range.end.line.Should().NotBe(1);
reference.uri.AbsolutePath.Should().Contain(".pyi");
}
}
}
12 changes: 12 additions & 0 deletions src/LanguageServer/Test/HoverTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,18 @@ class A:
AssertHover(hs, analysis, new SourceLocation(4, 7), @"x: int", new SourceSpan(4, 7, 4, 8));
}

[TestMethod, Priority(0)]
public async Task CompiledCode() {
const string code = @"
import os
os.mkdir()
";
var analysis = await GetAnalysisAsync(code);
var hs = new HoverSource(new PlainTextDocumentationSource());
var hover = hs.GetHover(analysis, new SourceLocation(3, 6));
hover.contents.value.Should().Contain("Create a directory");
}

private static void AssertNoHover(HoverSource hs, IDocumentAnalysis analysis, SourceLocation position) {
var hover = hs.GetHover(analysis, position);
hover.Should().BeNull();
Expand Down