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

Commit b35bfe8

Browse files
author
Mikhail Arkhipov
authored
Drop AST for libraries after final analysis (#1134)
* Remove old qualified name * Node storage * Class and scope to use AST map * Library analysis * Fix SO * Keep small AST with imports * AST reduction * Final field * Reload * Ignore post-final requests * Drop AST * Remove local variables * Test fixes * Fix overload match * Tests * Add locks * Remove local variables * Drop file content to save memory * Cache PEP hints * Recreate AST * Fix specialization * Fix locations * usings * Test fixes * Add options to keep data in memory * Fix test * Fix lambda parameters * Fix argument set Fix global scope node * Fix overload doc * Fix stub merge errors * Fix async issues * Undo some changes * Fix test * Fix race condition * Restore log null checks * Fix merge conflict * Fix merge issue * Null check * Fix test
1 parent 4f81ba2 commit b35bfe8

File tree

74 files changed

+793
-688
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+793
-688
lines changed

src/Analysis/Ast/Impl/Analyzer/AnalysisWalker.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public override bool Walk(ExpressionStatement node) {
6969
return true;
7070
case CallExpression callex when callex.Target is MemberExpression mex && !string.IsNullOrEmpty(mex.Name):
7171
var t = Eval.GetValueFromExpression(mex.Target)?.GetPythonType();
72-
t?.GetMember(mex.Name).AddReference(Eval.GetLocationOfName(mex));
72+
t?.GetMember(mex.Name)?.AddReference(Eval.GetLocationOfName(mex));
7373
return true;
7474
default:
7575
return base.Walk(node);

src/Analysis/Ast/Impl/Analyzer/Definitions/IAnalyzable.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ internal interface IAnalyzable {
2626
/// <summary>
2727
/// Notifies document that its analysis is now complete.
2828
/// </summary>
29-
/// <param name="analysis">Document analysis</param>
30-
void NotifyAnalysisComplete(IDocumentAnalysis analysis);
29+
void NotifyAnalysisComplete(int version, ModuleWalker walker, bool isFinalPass);
3130
}
3231
}

src/Analysis/Ast/Impl/Analyzer/Definitions/IPythonAnalyzer.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,14 @@
2020
using Microsoft.Python.Analysis.Diagnostics;
2121
using Microsoft.Python.Analysis.Types;
2222
using Microsoft.Python.Core.Collections;
23-
using Microsoft.Python.Parsing.Ast;
2423

2524
namespace Microsoft.Python.Analysis.Analyzer {
2625
public interface IPythonAnalyzer {
2726
Task WaitForCompleteAnalysisAsync(CancellationToken cancellationToken = default);
2827
/// <summary>
2928
/// Schedules module for analysis. Module will be scheduled if version of AST is greater than the one used to get previous analysis
3029
/// </summary>
31-
void EnqueueDocumentForAnalysis(IPythonModule module, PythonAst ast, int version);
30+
void EnqueueDocumentForAnalysis(IPythonModule module, int version);
3231

3332
/// <summary>
3433
/// Schedules module for analysis for its existing AST, but with new dependencies.

src/Analysis/Ast/Impl/Analyzer/DocumentAnalysis.cs

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,11 @@
1313
// See the Apache Version 2.0 License for specific language governing
1414
// permissions and limitations under the License.
1515

16-
using System;
1716
using System.Collections.Generic;
18-
using System.IO;
19-
using System.Linq;
20-
using Microsoft.Python.Analysis.Analyzer.Evaluation;
2117
using Microsoft.Python.Analysis.Diagnostics;
2218
using Microsoft.Python.Analysis.Documents;
2319
using Microsoft.Python.Analysis.Values;
24-
using Microsoft.Python.Core;
2520
using Microsoft.Python.Core.Diagnostics;
26-
using Microsoft.Python.Parsing;
2721
using Microsoft.Python.Parsing.Ast;
2822

2923
namespace Microsoft.Python.Analysis.Analyzer {
@@ -77,24 +71,4 @@ public DocumentAnalysis(IDocument document, int version, IGlobalScope globalScop
7771
public IEnumerable<DiagnosticsEntry> Diagnostics => ExpressionEvaluator.Diagnostics;
7872
#endregion
7973
}
80-
81-
public sealed class EmptyAnalysis : IDocumentAnalysis {
82-
private static PythonAst _emptyAst;
83-
84-
public EmptyAnalysis(IServiceContainer services, IDocument document) {
85-
Document = document ?? throw new ArgumentNullException(nameof(document));
86-
GlobalScope = new EmptyGlobalScope(document);
87-
88-
_emptyAst = _emptyAst ?? (_emptyAst = Parser.CreateParser(new StringReader(string.Empty), PythonLanguageVersion.None).ParseFile(document.Uri));
89-
ExpressionEvaluator = new ExpressionEval(services, document, Ast);
90-
}
91-
92-
public IDocument Document { get; }
93-
public int Version { get; } = -1;
94-
public IGlobalScope GlobalScope { get; }
95-
public PythonAst Ast => _emptyAst;
96-
public IExpressionEvaluator ExpressionEvaluator { get; }
97-
public IReadOnlyList<string> StarImportMemberNames => null;
98-
public IEnumerable<DiagnosticsEntry> Diagnostics => Enumerable.Empty<DiagnosticsEntry>();
99-
}
10074
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Copyright(c) Microsoft Corporation
2+
// All rights reserved.
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the License); you may not use
5+
// this file except in compliance with the License. You may obtain a copy of the
6+
// License at http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
9+
// OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY
10+
// IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
11+
// MERCHANTABILITY OR NON-INFRINGEMENT.
12+
//
13+
// See the Apache Version 2.0 License for specific language governing
14+
// permissions and limitations under the License.
15+
16+
using System;
17+
using System.Collections.Generic;
18+
using System.Linq;
19+
using Microsoft.Python.Analysis.Analyzer.Evaluation;
20+
using Microsoft.Python.Analysis.Diagnostics;
21+
using Microsoft.Python.Analysis.Documents;
22+
using Microsoft.Python.Analysis.Utilities;
23+
using Microsoft.Python.Analysis.Values;
24+
using Microsoft.Python.Core;
25+
using Microsoft.Python.Parsing.Ast;
26+
27+
namespace Microsoft.Python.Analysis.Analyzer {
28+
public sealed class EmptyAnalysis : IDocumentAnalysis {
29+
public EmptyAnalysis(IServiceContainer services, IDocument document) {
30+
Document = document ?? throw new ArgumentNullException(nameof(document));
31+
GlobalScope = new EmptyGlobalScope(document);
32+
Ast = AstUtilities.MakeEmptyAst(document.Uri);
33+
ExpressionEvaluator = new ExpressionEval(services, document);
34+
}
35+
36+
public IDocument Document { get; }
37+
public int Version { get; } = -1;
38+
public IGlobalScope GlobalScope { get; }
39+
public PythonAst Ast { get; }
40+
public IExpressionEvaluator ExpressionEvaluator { get; }
41+
public IReadOnlyList<string> StarImportMemberNames => null;
42+
public IEnumerable<DiagnosticsEntry> Diagnostics => Enumerable.Empty<DiagnosticsEntry>();
43+
}
44+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Copyright(c) Microsoft Corporation
2+
// All rights reserved.
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the License); you may not use
5+
// this file except in compliance with the License. You may obtain a copy of the
6+
// License at http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
9+
// OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY
10+
// IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
11+
// MERCHANTABILITY OR NON-INFRINGEMENT.
12+
//
13+
// See the Apache Version 2.0 License for specific language governing
14+
// permissions and limitations under the License.
15+
16+
using Microsoft.Python.Analysis.Types;
17+
using Microsoft.Python.Parsing.Ast;
18+
19+
namespace Microsoft.Python.Analysis.Analyzer.Evaluation {
20+
internal sealed partial class ExpressionEval {
21+
public IPythonType GetTypeFromAnnotation(Expression expr, LookupOptions options = LookupOptions.Global | LookupOptions.Builtins)
22+
=> GetTypeFromAnnotation(expr, out _, options);
23+
24+
public IPythonType GetTypeFromAnnotation(Expression expr, out bool isGeneric, LookupOptions options = LookupOptions.Global | LookupOptions.Builtins) {
25+
isGeneric = false;
26+
switch (expr) {
27+
case null:
28+
return null;
29+
case CallExpression callExpr:
30+
// x: NamedTuple(...)
31+
return GetValueFromCallable(callExpr)?.GetPythonType() ?? UnknownType;
32+
case IndexExpression indexExpr:
33+
// Try generics
34+
var target = GetValueFromExpression(indexExpr.Target);
35+
var result = GetValueFromGeneric(target, indexExpr);
36+
if (result != null) {
37+
isGeneric = true;
38+
return result.GetPythonType();
39+
}
40+
break;
41+
}
42+
43+
// Look at specialization and typing first
44+
var ann = new TypeAnnotation(Ast.LanguageVersion, expr);
45+
return ann.GetValue(new TypeAnnotationConverter(this, options));
46+
}
47+
}
48+
}

src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.Callables.cs

Lines changed: 75 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
using System.Diagnostics;
1818
using System.Linq;
1919
using Microsoft.Python.Analysis.Documents;
20+
using Microsoft.Python.Analysis.Extensions;
2021
using Microsoft.Python.Analysis.Modules;
2122
using Microsoft.Python.Analysis.Types;
2223
using Microsoft.Python.Analysis.Values;
@@ -77,9 +78,11 @@ public IMember GetValueFromLambda(LambdaExpression expr) {
7778
return null;
7879
}
7980

80-
var location = GetLocationOfName(expr.Function);
81-
var ft = new PythonFunctionType(expr.Function, null, location);
82-
var overload = new PythonFunctionOverload(expr.Function, ft, location);
81+
var fd = expr.Function;
82+
var location = GetLocationOfName(fd);
83+
var ft = new PythonFunctionType(fd, null, location);
84+
var overload = new PythonFunctionOverload(ft, fd, location, expr.Function.ReturnAnnotation?.ToCodeString(Ast));
85+
overload.SetParameters(CreateFunctionParameters(null, ft, fd, false));
8386
ft.AddOverload(overload);
8487
return ft;
8588
}
@@ -101,13 +104,13 @@ public IMember GetValueFromClassCtor(IPythonClassType cls, CallExpression expr)
101104
return cls.CreateInstance(cls.Name, args);
102105
}
103106

104-
public IMember GetValueFromBound(IPythonBoundType t, CallExpression expr) {
107+
private IMember GetValueFromBound(IPythonBoundType t, CallExpression expr) {
105108
switch (t.Type) {
106109
case IPythonFunctionType fn:
107110
return GetValueFromFunctionType(fn, t.Self, expr);
108111
case IPythonPropertyType p:
109112
return GetValueFromProperty(p, t.Self);
110-
case IPythonIteratorType it when t.Self is IPythonCollection seq:
113+
case IPythonIteratorType _ when t.Self is IPythonCollection seq:
111114
return seq.GetIterator();
112115
}
113116
return UnknownType;
@@ -173,7 +176,7 @@ public IMember GetValueFromFunctionType(IPythonFunctionType fn, IPythonInstance
173176
if (instanceType == null || fn.DeclaringType == null || fn.IsSpecialized ||
174177
instanceType.IsSpecialized || fn.DeclaringType.IsSpecialized ||
175178
instanceType.Equals(fn.DeclaringType) ||
176-
fn.IsStub || !string.IsNullOrEmpty(fn.Overloads[args.OverloadIndex].GetReturnDocumentation(null))) {
179+
fn.IsStub || !string.IsNullOrEmpty(fn.Overloads[args.OverloadIndex].GetReturnDocumentation())) {
177180

178181
LoadFunctionDependencyModules(fn);
179182

@@ -192,14 +195,14 @@ public IMember GetValueFromFunctionType(IPythonFunctionType fn, IPythonInstance
192195
// def func(a, b): return a + b
193196
// from working in libraries, but this is small sacrifice for significant performance
194197
// increase in library analysis.
195-
if (fn.DeclaringModule is IDocument doc && fd?.Ast == doc.GetAnyAst() && EvaluateFunctionBody(fn)) {
198+
if (fn.DeclaringModule is IDocument && EvaluateFunctionBody(fn)) {
196199
// Stubs are coming from another module.
197200
return TryEvaluateWithArguments(fn, args);
198201
}
199202
return UnknownType;
200203
}
201204

202-
public IMember GetValueFromProperty(IPythonPropertyType p, IPythonInstance instance) {
205+
private IMember GetValueFromProperty(IPythonPropertyType p, IPythonInstance instance) {
203206
// Function may not have been walked yet. Do it now.
204207
SymbolTable.Evaluate(p.FunctionDefinition);
205208
return instance.Call(p.Name, ArgumentSet.Empty);
@@ -311,5 +314,69 @@ private void LoadFunctionDependencyModules(IPythonFunctionType fn) {
311314
Services.GetService<IPythonAnalyzer>().EnqueueDocumentForAnalysis(Module, dependencies);
312315
}
313316
}
317+
318+
public IReadOnlyList<IParameterInfo> CreateFunctionParameters(IPythonClassType self, IPythonClassMember function, FunctionDefinition fd, bool declareVariables) {
319+
// For class method no need to add extra parameters, but first parameter type should be the class.
320+
// For static and unbound methods do not add or set anything.
321+
// For regular bound methods add first parameter and set it to the class.
322+
323+
var parameters = new List<ParameterInfo>();
324+
var skip = 0;
325+
if (self != null && function.HasClassFirstArgument()) {
326+
var p0 = fd.Parameters.FirstOrDefault();
327+
if (p0 != null && !string.IsNullOrEmpty(p0.Name)) {
328+
// Actual parameter type will be determined when method is invoked.
329+
// The reason is that if method might be called on a derived class.
330+
// Declare self or cls in this scope.
331+
if (declareVariables) {
332+
DeclareVariable(p0.Name, new PythonInstance(self), VariableSource.Declaration, p0.NameExpression);
333+
}
334+
// Set parameter info.
335+
var pi = new ParameterInfo(Ast, p0, self, null, false);
336+
parameters.Add(pi);
337+
skip++;
338+
}
339+
}
340+
341+
// Declare parameters in scope
342+
for (var i = skip; i < fd.Parameters.Length; i++) {
343+
var p = fd.Parameters[i];
344+
if (!string.IsNullOrEmpty(p.Name)) {
345+
var defaultValue = GetValueFromExpression(p.DefaultValue);
346+
var paramType = GetTypeFromAnnotation(p.Annotation, out var isGeneric) ?? UnknownType;
347+
if (paramType.IsUnknown()) {
348+
// If parameter has default value, look for the annotation locally first
349+
// since outer type may be getting redefined. Consider 's = None; def f(s: s = 123): ...
350+
paramType = GetTypeFromAnnotation(p.Annotation, out isGeneric, LookupOptions.Local | LookupOptions.Builtins);
351+
// Default value of None does not mean the parameter is None, just says it can be missing.
352+
defaultValue = defaultValue.IsUnknown() || defaultValue.IsOfType(BuiltinTypeId.NoneType) ? null : defaultValue;
353+
if (paramType == null && defaultValue != null) {
354+
paramType = defaultValue.GetPythonType();
355+
}
356+
}
357+
// If all else fails, look up globally.
358+
var pi = new ParameterInfo(Ast, p, paramType, defaultValue, isGeneric | paramType.IsGeneric());
359+
if (declareVariables) {
360+
DeclareParameter(p, pi);
361+
}
362+
parameters.Add(pi);
363+
} else if (p.IsList || p.IsDictionary) {
364+
parameters.Add(new ParameterInfo(Ast, p, null, null, false));
365+
}
366+
}
367+
return parameters;
368+
}
369+
370+
private void DeclareParameter(Parameter p, ParameterInfo pi) {
371+
IPythonType paramType;
372+
// If type is known from annotation, use it.
373+
if (pi != null && !pi.Type.IsUnknown() && !pi.Type.IsGenericParameter()) {
374+
// TODO: technically generics may have constraints. Should we consider them?
375+
paramType = pi.Type;
376+
} else {
377+
paramType = pi?.DefaultValue?.GetPythonType() ?? UnknownType;
378+
}
379+
DeclareVariable(p.Name, new PythonInstance(paramType), VariableSource.Declaration, p.NameExpression);
380+
}
314381
}
315382
}

src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.Scopes.cs

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -117,33 +117,6 @@ public IMember LookupNameInScopes(string name, out IScope scope, out IVariable v
117117
return value;
118118
}
119119

120-
public IPythonType GetTypeFromAnnotation(Expression expr, LookupOptions options = LookupOptions.Global | LookupOptions.Builtins)
121-
=> GetTypeFromAnnotation(expr, out _, options);
122-
123-
public IPythonType GetTypeFromAnnotation(Expression expr, out bool isGeneric, LookupOptions options = LookupOptions.Global | LookupOptions.Builtins) {
124-
isGeneric = false;
125-
switch (expr) {
126-
case null:
127-
return null;
128-
case CallExpression callExpr:
129-
// x: NamedTuple(...)
130-
return GetValueFromCallable(callExpr)?.GetPythonType() ?? UnknownType;
131-
case IndexExpression indexExpr:
132-
// Try generics
133-
var target = GetValueFromExpression(indexExpr.Target);
134-
var result = GetValueFromGeneric(target, indexExpr);
135-
if (result != null) {
136-
isGeneric = true;
137-
return result.GetPythonType();
138-
}
139-
break;
140-
}
141-
142-
// Look at specialization and typing first
143-
var ann = new TypeAnnotation(Ast.LanguageVersion, expr);
144-
return ann.GetValue(new TypeAnnotationConverter(this, options));
145-
}
146-
147120
/// <summary>
148121
/// Locates and opens existing scope for a node or creates a new scope
149122
/// as a child of the specified scope. Scope is pushed on the stack

src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.cs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
using Microsoft.Python.Analysis.Diagnostics;
2121
using Microsoft.Python.Analysis.Modules;
2222
using Microsoft.Python.Analysis.Types;
23+
using Microsoft.Python.Analysis.Utilities;
2324
using Microsoft.Python.Analysis.Values;
2425
using Microsoft.Python.Core;
2526
using Microsoft.Python.Core.Disposables;
@@ -37,12 +38,15 @@ internal sealed partial class ExpressionEval : IExpressionEvaluator {
3738
private readonly object _lock = new object();
3839
private readonly List<DiagnosticsEntry> _diagnostics = new List<DiagnosticsEntry>();
3940

40-
public ExpressionEval(IServiceContainer services, IPythonModule module, PythonAst ast) {
41+
public ExpressionEval(IServiceContainer services, IPythonModule module)
42+
: this(services, module, new GlobalScope(module)) { }
43+
44+
public ExpressionEval(IServiceContainer services, IPythonModule module, GlobalScope gs) {
4145
Services = services ?? throw new ArgumentNullException(nameof(services));
4246
Module = module ?? throw new ArgumentNullException(nameof(module));
43-
Ast = ast ?? throw new ArgumentNullException(nameof(ast));
47+
Ast = module.GetAst();
4448

45-
GlobalScope = new GlobalScope(module);
49+
GlobalScope = gs;
4650
CurrentScope = GlobalScope;
4751
DefaultLocation = new Location(module);
4852
//Log = services.GetService<ILogger>();
@@ -59,14 +63,15 @@ public ExpressionEval(IServiceContainer services, IPythonModule module, PythonAs
5963
public LocationInfo GetLocationInfo(Node node) => node?.GetLocation(Module) ?? LocationInfo.Empty;
6064

6165
public Location GetLocationOfName(Node node) {
62-
if (node == null || (Module.ModuleType != ModuleType.User && Module.ModuleType != ModuleType.Library)) {
66+
node = node ?? throw new ArgumentNullException(nameof(node));
67+
if (Module.ModuleType != ModuleType.User && Module.ModuleType != ModuleType.Library) {
6368
return DefaultLocation;
6469
}
6570

6671
IndexSpan indexSpan;
6772
switch (node) {
6873
case MemberExpression mex:
69-
indexSpan = mex.GetNameSpan().ToIndexSpan(mex.Ast);
74+
indexSpan = mex.GetNameSpan(Ast).ToIndexSpan(Ast);
7075
break;
7176
case ClassDefinition cd:
7277
indexSpan = cd.NameExpression.IndexSpan;
@@ -86,7 +91,7 @@ public Location GetLocationOfName(Node node) {
8691
// turns into span at line 1 and very large column. This DOES can
8792
// produce false positives occasionally.
8893
#if DEBUG
89-
var sourceSpan = indexSpan.ToSourceSpan(node.Ast);
94+
var sourceSpan = indexSpan.ToSourceSpan(Ast);
9095
Debug.Assert(sourceSpan.Start.Line > 1 || sourceSpan.Start.Column < 1000);
9196
#endif
9297
return new Location(Module, indexSpan);

0 commit comments

Comments
 (0)