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

Commit a82c356

Browse files
AlexanderSherMikhail Arkhipov
authored andcommitted
- Fix #1455: AF: Library module of type Stub has been analyzed already! (#1470)
- Restore task handling in unit tests - Throw exception from Debug.Fail instead of FailFast
1 parent 86c8426 commit a82c356

File tree

19 files changed

+138
-1148
lines changed

19 files changed

+138
-1148
lines changed

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

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -23,48 +23,41 @@
2323
namespace Microsoft.Python.Analysis.Analyzer {
2424
[DebuggerDisplay("{Name} : {FilePath}")]
2525
internal readonly struct AnalysisModuleKey : IEquatable<AnalysisModuleKey> {
26-
private enum KeyType { Default, Typeshed, LibraryAsDocument }
27-
28-
private readonly KeyType _type;
2926
public string Name { get; }
3027
public string FilePath { get; }
31-
public bool IsTypeshed => _type == KeyType.Typeshed;
32-
public bool IsLibraryAsDocument => _type == KeyType.LibraryAsDocument;
28+
public bool IsTypeshed { get; }
29+
public bool IsNonUserAsDocument { get; }
3330

3431
public AnalysisModuleKey(IPythonModule module) {
3532
Name = module.Name;
3633
FilePath = module.ModuleType == ModuleType.CompiledBuiltin ? null : module.FilePath;
37-
_type = module is StubPythonModule stub && stub.IsTypeshed
38-
? KeyType.Typeshed
39-
: module.ModuleType == ModuleType.Library && module is IDocument document && document.IsOpen
40-
? KeyType.LibraryAsDocument
41-
: KeyType.Default;
34+
IsTypeshed = module is StubPythonModule stub && stub.IsTypeshed;
35+
IsNonUserAsDocument = (module.IsNonUserFile() || module.IsCompiled()) && module is IDocument document && document.IsOpen;
4236
}
4337

44-
public AnalysisModuleKey(string name, string filePath, bool isTypeshed) {
45-
Name = name;
46-
FilePath = filePath;
47-
_type = isTypeshed ? KeyType.Typeshed : KeyType.Default;
48-
}
38+
public AnalysisModuleKey(string name, string filePath, bool isTypeshed)
39+
: this(name, filePath, isTypeshed, false) { }
4940

50-
private AnalysisModuleKey(string name, string filePath, KeyType type) {
41+
private AnalysisModuleKey(string name, string filePath, bool isTypeshed, bool isNonUserAsDocument) {
5142
Name = name;
5243
FilePath = filePath;
53-
_type = type;
44+
IsTypeshed = isTypeshed;
45+
IsNonUserAsDocument = isNonUserAsDocument;
5446
}
5547

56-
public AnalysisModuleKey GetLibraryAsDocumentKey() => new AnalysisModuleKey(Name, FilePath, KeyType.LibraryAsDocument);
48+
public AnalysisModuleKey GetNonUserAsDocumentKey() => new AnalysisModuleKey(Name, FilePath, IsTypeshed, true);
5749

5850
public bool Equals(AnalysisModuleKey other)
59-
=> Name.EqualsOrdinal(other.Name) && FilePath.PathEquals(other.FilePath) && _type == other._type;
51+
=> Name.EqualsOrdinal(other.Name) && FilePath.PathEquals(other.FilePath) && IsTypeshed == other.IsTypeshed && IsNonUserAsDocument == other.IsNonUserAsDocument;
6052

6153
public override bool Equals(object obj) => obj is AnalysisModuleKey other && Equals(other);
6254

6355
public override int GetHashCode() {
6456
unchecked {
65-
var hashCode = (Name != null ? Name.GetHashCode() : 0);
57+
var hashCode = Name != null ? Name.GetHashCode() : 0;
6658
hashCode = (hashCode * 397) ^ (FilePath != null ? FilePath.GetPathHashCode() : 0);
67-
hashCode = (hashCode * 397) ^ _type.GetHashCode();
59+
hashCode = (hashCode * 397) ^ IsTypeshed.GetHashCode();
60+
hashCode = (hashCode * 397) ^ IsNonUserAsDocument.GetHashCode();
6861
return hashCode;
6962
}
7063
}

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

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
using System.Collections.Generic;
1818
using System.Diagnostics;
1919
using System.Linq;
20+
using System.Runtime.ExceptionServices;
2021
using System.Threading;
2122
using System.Threading.Tasks;
2223
using Microsoft.Python.Analysis.Caching;
@@ -166,9 +167,9 @@ public void EnqueueDocumentForAnalysis(IPythonModule module, PythonAst ast, int
166167
// It is possible that parsing request for the library has been started when document is open,
167168
// but it is closed at the moment of analysis and then become open again.
168169
// In this case, we still need to analyze the document, but using correct entry.
169-
var libraryAsDocumentKey = key.GetLibraryAsDocumentKey();
170-
if (entry.PreviousAnalysis is LibraryAnalysis && _analysisEntries.TryGetValue(libraryAsDocumentKey, out var documentEntry)) {
171-
key = libraryAsDocumentKey;
170+
var nonUserAsDocumentKey = key.GetNonUserAsDocumentKey();
171+
if (entry.PreviousAnalysis is LibraryAnalysis && _analysisEntries.TryGetValue(nonUserAsDocumentKey, out var documentEntry)) {
172+
key = nonUserAsDocumentKey;
172173
entry = documentEntry;
173174
}
174175

@@ -239,7 +240,7 @@ private void AnalyzeDocument(in AnalysisModuleKey key, in PythonAnalyzerEntry en
239240
ActivityTracker.StartTracking();
240241
_log?.Log(TraceEventType.Verbose, $"Analysis of {entry.Module.Name}({entry.Module.ModuleType}) queued");
241242

242-
var graphVersion = _dependencyResolver.ChangeValue(key, entry, entry.IsUserOrBuiltin || key.IsLibraryAsDocument, dependencies);
243+
var graphVersion = _dependencyResolver.ChangeValue(key, entry, entry.IsUserOrBuiltin || key.IsNonUserAsDocument, dependencies);
243244

244245
lock (_syncObj) {
245246
if (_version > graphVersion) {
@@ -301,6 +302,11 @@ private bool TryCreateSession(in int graphVersion, in PythonAnalyzerEntry entry,
301302
}
302303

303304
private void StartNextSession(Task task) {
305+
if (task.IsFaulted && task.Exception != null) {
306+
var exception = task.Exception.InnerException;
307+
ExceptionDispatchInfo.Capture(exception).Throw();
308+
}
309+
304310
PythonAnalyzerSession session;
305311
lock (_syncObj) {
306312
if (_nextSession == null) {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ public bool IsValidVersion(int version, out IPythonModule module, out PythonAst
113113
}
114114

115115
if (ast == null) {
116-
Debug.Assert(!(_previousAnalysis is LibraryAnalysis), $"Library module {module.Name} of type {module.ModuleType} has been analyzed already!");
116+
Debug.Assert(!(_analysisVersion <= version && _previousAnalysis is LibraryAnalysis), $"Library module {module.Name} of type {module.ModuleType} has been analyzed already!");
117117
return false;
118118
}
119119

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
using System.Diagnostics;
1818
using System.Linq;
1919
using System.Runtime;
20+
using System.Runtime.ExceptionServices;
2021
using System.Threading;
2122
using System.Threading.Tasks;
2223
using Microsoft.Python.Analysis.Analyzer.Evaluation;
@@ -29,6 +30,7 @@
2930
using Microsoft.Python.Core;
3031
using Microsoft.Python.Core.Logging;
3132
using Microsoft.Python.Core.Services;
33+
using Microsoft.Python.Core.Testing;
3234
using Microsoft.Python.Parsing.Ast;
3335

3436
namespace Microsoft.Python.Analysis.Analyzer {
@@ -210,6 +212,7 @@ private async Task<int> AnalyzeAffectedEntriesAsync(Stopwatch stopWatch) {
210212
if (Interlocked.Increment(ref _runningTasks) >= _maxTaskRunning || _walker.Remaining == 1) {
211213
RunAnalysis(node, stopWatch);
212214
} else {
215+
ace.AddOne();
213216
StartAnalysis(node, ace, stopWatch).DoNotWait();
214217
}
215218
}
@@ -250,7 +253,6 @@ private Task StartAnalysis(IDependencyChainNode<PythonAnalyzerEntry> node, Async
250253
/// </summary>
251254
private void Analyze(IDependencyChainNode<PythonAnalyzerEntry> node, AsyncCountdownEvent ace, Stopwatch stopWatch) {
252255
try {
253-
ace?.AddOne();
254256
var entry = node.Value;
255257

256258
if (!entry.IsValidVersion(_walker.Version, out var module, out var ast)) {
@@ -372,6 +374,10 @@ private void LogException(IPythonModule module, Exception exception) {
372374
if (_log != null) {
373375
_log.Log(TraceEventType.Verbose, $"Analysis of {module.Name}({module.ModuleType}) failed. {exception}");
374376
}
377+
378+
if (TestEnvironment.Current != null) {
379+
ExceptionDispatchInfo.Capture(exception).Throw();
380+
}
375381
}
376382

377383
private IDocumentAnalysis CreateAnalysis(IDependencyChainNode<PythonAnalyzerEntry> node, IDocument document, PythonAst ast, int version, ModuleWalker walker, bool isCanceled) {

src/Analysis/Ast/Impl/Analyzer/Symbols/FunctionEvaluator.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ public override bool Walk(AssignmentStatement node) {
125125
public override bool Walk(ReturnStatement node) {
126126
var value = Eval.GetValueFromExpression(node.Expression);
127127
if (value != null) {
128+
128129
// although technically legal, __init__ in a constructor should not have a not-none return value
129130
if (_function.IsDunderInit() && !value.IsOfType(BuiltinTypeId.NoneType)) {
130131
Eval.ReportDiagnostics(Module.Uri, new DiagnosticsEntry(

src/Analysis/Ast/Impl/Extensions/FunctionDefinitionExtensions.cs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,19 @@
1-
using Microsoft.Python.Analysis.Types;
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;
217

318
namespace Microsoft.Python.Analysis {
419
public static class ClassMemberExtensions {

src/Analysis/Ast/Impl/Extensions/PythonModuleExtensions.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,5 +67,8 @@ internal static string GetComment(this IPythonModule module, int lineNum) {
6767

6868
return line.Substring(commentPos + 1).Trim('\t', ' ');
6969
}
70+
71+
internal static bool IsNonUserFile(this IPythonModule module) => module.ModuleType.IsNonUserFile();
72+
internal static bool IsCompiled(this IPythonModule module) => module.ModuleType.IsCompiled();
7073
}
7174
}

src/Analysis/Ast/Impl/Extensions/PythonTypeExtensions.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,9 @@ public static bool IsGenericParameter(this IPythonType value)
2727

2828
public static bool IsGeneric(this IPythonType value)
2929
=> value is IGenericTypeParameter || (value is IGenericType gt && gt.IsGeneric);
30+
31+
public static string GetQualifiedName(this IPythonType t) => $"{t.DeclaringModule.Name}:{t.Name}";
32+
33+
internal static IPythonType ToBound(this IPythonType t) => t is PythonFunctionType.PythonUnboundMethod m ? m.Function : t;
3034
}
3135
}

src/Analysis/Ast/Impl/Modules/Definitions/ModuleType.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,4 +60,9 @@ public enum ModuleType {
6060
/// </summary>
6161
Specialized
6262
}
63+
64+
public static class ModuleTypeExtensions {
65+
public static bool IsNonUserFile(this ModuleType type) => type == ModuleType.Library || type == ModuleType.Stub;
66+
public static bool IsCompiled(this ModuleType type) => type == ModuleType.Compiled || type == ModuleType.CompiledBuiltin;
67+
}
6368
}

src/Caching/Impl/ModuleUniqueId.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,7 @@ public static string GetUniqueId(string moduleName, string filePath, ModuleType
7878
}
7979

8080
var config = interpreter.Configuration;
81-
if (moduleType == ModuleType.Builtins || moduleType == ModuleType.CompiledBuiltin ||
82-
string.IsNullOrEmpty(filePath) || modulePathType == PythonLibraryPathType.StdLib) {
81+
if (moduleType.IsCompiled() || string.IsNullOrEmpty(filePath) || modulePathType == PythonLibraryPathType.StdLib) {
8382
// If module is a standard library, unique id is its name + interpreter version.
8483
return $"{moduleName}({config.Version.Major}.{config.Version.Minor})";
8584
}

0 commit comments

Comments
 (0)