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

Improve handling of circular dependencies #1686

Merged
merged 55 commits into from
Nov 4, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
c9ff4e1
Refactoring
AlexanderSher Sep 22, 2019
6ab46ad
WIP
AlexanderSher Sep 24, 2019
76887fc
Merge commit '88762ef1a11cfddd51d382c920359c36e95f8d4d' into Numpy
AlexanderSher Sep 24, 2019
1068b18
Bug fixes
AlexanderSher Sep 25, 2019
03f004d
Fix part of the test
AlexanderSher Sep 25, 2019
43c7766
Fully restore tests
AlexanderSher Sep 25, 2019
c6a9c22
Remove stale reference
Sep 30, 2019
cbfd3e2
Initial
Oct 1, 2019
1360827
Merge branch 'master' of https://github.com/microsoft/python-language…
Oct 1, 2019
e5f0110
Merge issues
Oct 3, 2019
87f280a
Merge branch 'master' of https://github.com/microsoft/python-language…
Oct 3, 2019
782ca9a
Merge fixes
Oct 3, 2019
ee0a213
Fix null ref
Oct 3, 2019
ccaaa02
Merge branch 'master' of https://github.com/microsoft/python-language…
Oct 4, 2019
51463f3
Merge branch 'master' of https://github.com/microsoft/python-language…
Oct 4, 2019
a1b238b
Fix creation of entries for missing keys
Oct 4, 2019
2a1821d
Merge branch 'master' of https://github.com/microsoft/python-language…
Oct 5, 2019
a9ae53f
Increase cancellation time in debug
Oct 5, 2019
f329e8f
Merge branch 'mkeys2' into numpy
Oct 5, 2019
263ac4a
Merge branch 'mkeys2' of https://github.com/MikhailArkhipov/python-la…
Oct 7, 2019
1523067
Merge branch 'numpy' of https://github.com/MikhailArkhipov/python-lan…
Oct 7, 2019
0ce2ef6
Cache walker + add failing test
Oct 8, 2019
6bed88a
Merge remote-tracking branch 'Microsoft/master' into Numpy
AlexanderSher Oct 9, 2019
695dd85
Fix GetMemberNames
AlexanderSher Oct 9, 2019
d01cb08
Remove `InvalidateAnalysis` call from constructor
AlexanderSher Oct 10, 2019
152a4a2
Build fix
AlexanderSher Oct 10, 2019
bc80e57
Merge branch 'master' into Numpy
AlexanderSher Oct 10, 2019
50ca658
Merge recent
Oct 13, 2019
83d1c65
Merge issues
Oct 13, 2019
7ac9f14
Fix FindReferences
AlexanderSher Oct 14, 2019
e7832d0
Merge branch 'Numpy' of https://github.com/AlexanderSher/python-langu…
Oct 14, 2019
4f8e2ae
Tests
Oct 14, 2019
bab1a32
Tests
Oct 14, 2019
732f263
Merge master
Oct 14, 2019
f1a2efd
Add restoration from db in loop handling
Oct 14, 2019
a1bf54a
Revert "Add restoration from db in loop handling"
Oct 24, 2019
bcebb91
Merge master and fix loop node markWalked
Oct 24, 2019
11de546
Make numpy worh with library caching
Oct 24, 2019
9a935e7
Fix saving of ALL import statements
Oct 25, 2019
37a1048
Fix stub merge typo (members only in source were not transferred)
Oct 25, 2019
2a9c7a7
Don't re-analyze cached loop
Oct 25, 2019
b06c601
Fix TypeVar bound to Unknown
Oct 28, 2019
be880b9
Fix lambda restore
Oct 28, 2019
a01fb7d
Merge branch 'master' of https://github.com/microsoft/python-language…
Oct 28, 2019
38d0ea0
Walk loops fully
Oct 29, 2019
5db4970
Fix diagnostics on reopen
Oct 29, 2019
9b5c74f
Fix gotodef test
Oct 29, 2019
0f6a07a
Fix key exception
Oct 29, 2019
d461b8a
Add original test
Oct 29, 2019
62d84e3
Handle annotations in loops
Oct 30, 2019
e6bf9b9
Merge master
Oct 30, 2019
fda7234
Null check
Oct 30, 2019
52fe42c
Merge branch 'master' into numpy
jakebailey Nov 1, 2019
2be00a2
Merge master
Nov 2, 2019
67752e7
Call ActivityTracker on modules in loops
jakebailey Nov 4, 2019
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
1 change: 0 additions & 1 deletion src/Analysis/Ast/Impl/Analyzer/ActivityTracker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@

using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace Microsoft.Python.Analysis.Analyzer {
internal static class ActivityTracker {
Expand Down
4 changes: 2 additions & 2 deletions src/Analysis/Ast/Impl/Analyzer/AnalysisWalker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ internal abstract class AnalysisWalker : PythonWalker {
public PythonAst Ast => Eval.Ast;
protected ModuleSymbolTable SymbolTable => Eval.SymbolTable;

protected AnalysisWalker(ExpressionEval eval) {
protected AnalysisWalker(ExpressionEval eval, IImportedVariableHandler importedVariableHandler) {
Eval = eval;
ImportHandler = new ImportHandler(this);
ImportHandler = new ImportHandler(this, importedVariableHandler);
AssignmentHandler = new AssignmentHandler(this);
LoopHandler = new LoopHandler(this);
ConditionalHandler = new ConditionalHandler(this);
Expand Down
7 changes: 7 additions & 0 deletions src/Analysis/Ast/Impl/Analyzer/Definitions/IPythonAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,5 +71,12 @@ public interface IPythonAnalyzer {
/// Fires when analysis is complete.
/// </summary>
event EventHandler<AnalysisCompleteEventArgs> AnalysisComplete;

/// <summary>
/// Attempts to restore modules analysis from database.
/// </summary>
/// <param name="module"></param>
/// <returns></returns>
IDocumentAnalysis TryRestoreCachedAnalysis(IPythonModule module);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,6 @@ public T GetInScope<T>(string name, IScope scope) where T : class, IMember
public void DeclareVariable(string name, IMember value, VariableSource source)
=> DeclareVariable(name, value, source, default(Location));

public void DeclareVariable(string name, IMember value, VariableSource source, IPythonModule module)
=> DeclareVariable(name, value, source, new Location(module));

public void DeclareVariable(string name, IMember value, VariableSource source, Node location, bool overwrite = true)
=> DeclareVariable(name, value, source, GetLocationOfName(location), overwrite);

Expand Down
10 changes: 6 additions & 4 deletions src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ 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.User && Module.ModuleType != ModuleType.Library) {
return DefaultLocation;
}

Expand Down Expand Up @@ -233,7 +233,8 @@ private IMember GetValueFromName(NameExpression expr, LookupOptions options = Lo
}

private IMember GetValueFromMember(MemberExpression expr, LookupOptions lookupOptions = LookupOptions.Normal) {
if (expr?.Target == null || string.IsNullOrEmpty(expr.Name)) {
var memberName = expr?.Name;
if (expr?.Target == null || string.IsNullOrEmpty(memberName)) {
return null;
}

Expand All @@ -243,8 +244,9 @@ private IMember GetValueFromMember(MemberExpression expr, LookupOptions lookupOp
}

var type = m.GetPythonType();
var value = type?.GetMember(expr.Name);
type?.AddMemberReference(expr.Name, this, GetLocationOfName(expr));
var value = type?.GetMember(memberName);
var location = GetLocationOfName(expr);
type?.AddMemberReference(memberName, this, location);

if (type is IPythonModule) {
return value;
Expand Down
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 Microsoft.Python.Analysis.Analyzer.Handlers;
using Microsoft.Python.Analysis.Analyzer.Symbols;
using Microsoft.Python.Analysis.Types;
using Microsoft.Python.Parsing.Ast;
Expand All @@ -28,7 +29,7 @@ internal sealed class FunctionCallEvaluator: AnalysisWalker {
private readonly FunctionDefinition _function;
private IMember _result;

public FunctionCallEvaluator(IPythonModule declaringModule, FunctionDefinition fd, ExpressionEval eval): base(eval) {
public FunctionCallEvaluator(IPythonModule declaringModule, FunctionDefinition fd, ExpressionEval eval): base(eval, SimpleImportedVariableHandler.Instance) {
_declaringModule = declaringModule ?? throw new ArgumentNullException(nameof(declaringModule));
_eval = eval ?? throw new ArgumentNullException(nameof(eval));
_function = fd ?? throw new ArgumentNullException(nameof(fd));
Expand Down
59 changes: 41 additions & 18 deletions src/Analysis/Ast/Impl/Analyzer/Handlers/FromImportHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,21 @@ private void AssignVariables(FromImportStatement node, IImportSearchResult impor

for (var i = 0; i < names.Count; i++) {
var memberName = names[i].Name;
if (!string.IsNullOrEmpty(memberName)) {
var nameExpression = asNames[i] ?? names[i];
var variableName = nameExpression?.Name ?? memberName;
if (!string.IsNullOrEmpty(variableName)) {
DeclareVariable(variableModule, memberName, imports, variableName, node.StartIndex, nameExpression);
}
if (string.IsNullOrEmpty(memberName)) {
continue;
}

var nameExpression = asNames[i] ?? names[i];
var variableName = nameExpression?.Name ?? memberName;
if (!string.IsNullOrEmpty(variableName)) {
DeclareVariable(variableModule, memberName, imports, variableName, node.StartIndex, nameExpression);
}

if (imports is IImportChildrenSource cs
&& cs.TryGetChildImport(memberName, out var csr)
&& HandleImportSearchResult(csr, variableModule, null, names[i], out var childModule)) {

_importedVariableHandler.EnsureModule(childModule);
}
}
}
Expand All @@ -74,10 +83,11 @@ private void HandleModuleImportStar(PythonVariableModule variableModule, IImport
// from self import * won't define any new members
return;
}

// If __all__ is present, take it, otherwise declare all members from the module that do not begin with an underscore.
var memberNames = imports is ImplicitPackageImport
? variableModule.GetMemberNames()
: variableModule.Analysis.StarImportMemberNames ?? variableModule.GetMemberNames().Where(s => !s.StartsWithOrdinal("_"));
: _importedVariableHandler.GetMemberNames(variableModule).ToArray();

foreach (var memberName in memberNames) {
DeclareVariable(variableModule, memberName, imports, memberName, importPosition, nameExpression);
Expand All @@ -104,23 +114,36 @@ private void DeclareVariable(PythonVariableModule variableModule, string memberN
value = GetValueFromImports(variableModule, imports as IImportChildrenSource, memberName);

// First try exported or child submodules.
value = value ?? variableModule.GetMember(memberName);
var member = variableModule.GetMember(memberName);

// Value may be variable or submodule. If it is variable, we need it in order to add reference.
var variable = variableModule.Analysis?.GlobalScope?.Variables[memberName];
value = variable?.Value?.Equals(value) == true ? variable : value;

// If nothing is exported, variables are still accessible.
value = value ?? variableModule.Analysis?.GlobalScope?.Variables[memberName]?.Value ?? Eval.UnknownType;
var variable = _importedVariableHandler.GetVariable(variableModule, memberName);

if (member is PythonVariableModule vm && vm.Equals(variable?.Value)) {
// If member is submodule, use actual variable so it can be linked through for goto definition.
value = variable;
} else if (value == null) {
if (member is PythonVariableModule) {
// If member is submodule, use it.
value = member;
} else if (variable?.Value != null) {
// Otherwise use variable, if available so references can be linked.
value = variable;
} else if (member != null) {
value = member;
} else {
value = Eval.UnknownType;
}
}
}

// Do not allow imported variables to override local declarations
var canOverwrite = CanOverwriteVariable(variableName, importPosition, value);

// Do not declare references to '*'
var locationExpression = nameLocation is NameExpression nex && nex.Name == "*" ? null : nameLocation;
Eval.DeclareVariable(variableName, value, VariableSource.Import, locationExpression, canOverwrite);

// Make sure module is loaded and analyzed.
if (value is IPythonModule m) {
ModuleResolution.GetOrLoadModule(m.Name);
Expand All @@ -132,8 +155,8 @@ private bool CanOverwriteVariable(string name, int importPosition, IMember newVa
if (v == null) {
return true; // Variable does not exist
}
if(newValue.IsUnknown()) {

if (newValue.IsUnknown()) {
return false; // Do not overwrite potentially good value with unknowns.
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright(c) Microsoft Corporation
// All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the License); you may not use
// this file except in compliance with the License. You may obtain a copy of the
// License at http://www.apache.org/licenses/LICENSE-2.0
//
// THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
// OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY
// IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
// MERCHANTABILITY OR NON-INFRINGEMENT.
//
// See the Apache Version 2.0 License for specific language governing
// permissions and limitations under the License.

using System.Collections.Generic;
using Microsoft.Python.Analysis.Modules;
using Microsoft.Python.Analysis.Values;

namespace Microsoft.Python.Analysis.Analyzer.Handlers {
internal interface IImportedVariableHandler {
IEnumerable<string> GetMemberNames(PythonVariableModule variableModule);
Copy link
Member

@jakebailey jakebailey Oct 25, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After rereading this PR and checking references/impls, this function actually only ever returns what gets copied in a * import, so GetMemberNames is actually a misnomer and should probably be renamed to better reflect that, like GetStarImportMemberNames or similar.

IVariable GetVariable(in PythonVariableModule module, in string name);
void EnsureModule(in PythonVariableModule module);
}
}
37 changes: 15 additions & 22 deletions src/Analysis/Ast/Impl/Analyzer/Handlers/ImportHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,12 @@

namespace Microsoft.Python.Analysis.Analyzer.Handlers {
internal sealed partial class ImportHandler : StatementHandler {
private readonly IImportedVariableHandler _importedVariableHandler;
private readonly Dictionary<string, PythonVariableModule> _variableModules = new Dictionary<string, PythonVariableModule>();

public ImportHandler(AnalysisWalker walker) : base(walker) { }
public ImportHandler(AnalysisWalker walker, in IImportedVariableHandler importedVariableHandler) : base(walker) {
_importedVariableHandler = importedVariableHandler;
}

public bool HandleImport(ImportStatement node) {
if (Module.ModuleType == ModuleType.Specialized) {
Expand Down Expand Up @@ -65,7 +68,9 @@ private void HandleImport(ModuleName moduleImportExpression, NameExpression asNa
lastModule = default;
break;
}

resolvedModules[i] = (nameExpression.Name, lastModule);
_importedVariableHandler.EnsureModule(lastModule);
}

// "import fob.oar.baz as baz" is handled as baz = import_module('fob.oar.baz')
Expand All @@ -89,15 +94,6 @@ private void HandleImport(ModuleName moduleImportExpression, NameExpression asNa
Eval.DeclareVariable(importNames[0], firstModule, VariableSource.Import, moduleImportExpression.Names[0]);
}
}

// import a.b.c.d => declares a, b in the current module, c in b, d in c.
for (var i = 1; i < resolvedModules.Length - 1; i++) {
var (childName, childModule) = resolvedModules[i + 1];
if (!string.IsNullOrEmpty(childName) && childModule != null) {
var parent = resolvedModules[i].module;
parent?.AddChildModule(childName, childModule);
}
}
}

private bool HandleImportSearchResult(in IImportSearchResult imports, in PythonVariableModule parent, in NameExpression asNameExpression, in Node location, out PythonVariableModule variableModule) {
Expand All @@ -112,8 +108,7 @@ private bool HandleImportSearchResult(in IImportSearchResult imports, in PythonV
return TryGetPackageFromImport(packageImport, parent, out variableModule);
case RelativeImportBeyondTopLevel importBeyondTopLevel:
var message = Resources.ErrorRelativeImportBeyondTopLevel.FormatInvariant(importBeyondTopLevel.RelativeImportName);
Eval.ReportDiagnostics(Eval.Module.Uri,
new DiagnosticsEntry(message, location.GetLocation(Eval).Span, ErrorCodes.UnresolvedImport, Severity.Warning, DiagnosticSource.Analysis));
Eval.ReportDiagnostics(Eval.Module.Uri, new DiagnosticsEntry(message, location.GetLocation(Eval).Span, ErrorCodes.UnresolvedImport, Severity.Warning, DiagnosticSource.Analysis));
variableModule = default;
return false;
case ImportNotFound importNotFound:
Expand Down Expand Up @@ -177,7 +172,7 @@ private bool TryGetModulePossibleImport(PossibleModuleImport possibleModuleImpor
return false;
}
}

return true;
}

Expand All @@ -196,24 +191,22 @@ private void MakeUnresolvedImport(string variableName, string moduleName, Node l
}

private PythonVariableModule GetOrCreateVariableModule(in string fullName, in PythonVariableModule parentModule, in string memberName) {
if (_variableModules.TryGetValue(fullName, out var variableModule)) {
return variableModule;
if (!_variableModules.TryGetValue(fullName, out var variableModule)) {
variableModule = new PythonVariableModule(fullName, Eval.Interpreter);
_variableModules[fullName] = variableModule;
}

variableModule = new PythonVariableModule(fullName, Eval.Interpreter);
_variableModules[fullName] = variableModule;

parentModule?.AddChildModule(memberName, variableModule);
return variableModule;
}

private PythonVariableModule GetOrCreateVariableModule(in IPythonModule module, in PythonVariableModule parentModule, in string memberName) {
var moduleFullName = module.Name;
if (_variableModules.TryGetValue(moduleFullName, out var variableModule)) {
return variableModule;
if (!_variableModules.TryGetValue(moduleFullName, out var variableModule)) {
variableModule = new PythonVariableModule(module);
_variableModules[moduleFullName] = variableModule;
}

variableModule = new PythonVariableModule(module);
_variableModules[moduleFullName] = variableModule;
parentModule?.AddChildModule(memberName, variableModule);
return variableModule;
}
Expand Down
Loading