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

Commit 870361c

Browse files
AlexanderSherMikhailArkhipov
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 67932c6 commit 870361c

File tree

18 files changed

+134
-1158
lines changed

18 files changed

+134
-1158
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;
@@ -164,9 +165,9 @@ public void EnqueueDocumentForAnalysis(IPythonModule module, PythonAst ast, int
164165
// It is possible that parsing request for the library has been started when document is open,
165166
// but it is closed at the moment of analysis and then become open again.
166167
// In this case, we still need to analyze the document, but using correct entry.
167-
var libraryAsDocumentKey = key.GetLibraryAsDocumentKey();
168-
if (entry.PreviousAnalysis is LibraryAnalysis && _analysisEntries.TryGetValue(libraryAsDocumentKey, out var documentEntry)) {
169-
key = libraryAsDocumentKey;
168+
var nonUserAsDocumentKey = key.GetNonUserAsDocumentKey();
169+
if (entry.PreviousAnalysis is LibraryAnalysis && _analysisEntries.TryGetValue(nonUserAsDocumentKey, out var documentEntry)) {
170+
key = nonUserAsDocumentKey;
170171
entry = documentEntry;
171172
}
172173

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

240-
var graphVersion = _dependencyResolver.ChangeValue(key, entry, entry.IsUserOrBuiltin || key.IsLibraryAsDocument, dependencies);
241+
var graphVersion = _dependencyResolver.ChangeValue(key, entry, entry.IsUserOrBuiltin || key.IsNonUserAsDocument, dependencies);
241242

242243
lock (_syncObj) {
243244
if (_version > graphVersion) {
@@ -299,6 +300,11 @@ private bool TryCreateSession(in int graphVersion, in PythonAnalyzerEntry entry,
299300
}
300301

301302
private void StartNextSession(Task task) {
303+
if (task.IsFaulted && task.Exception != null) {
304+
var exception = task.Exception.InnerException;
305+
ExceptionDispatchInfo.Capture(exception).Throw();
306+
}
307+
302308
PythonAnalyzerSession session;
303309
lock (_syncObj) {
304310
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;
@@ -28,6 +29,7 @@
2829
using Microsoft.Python.Core;
2930
using Microsoft.Python.Core.Logging;
3031
using Microsoft.Python.Core.Services;
32+
using Microsoft.Python.Core.Testing;
3133
using Microsoft.Python.Parsing.Ast;
3234

3335
namespace Microsoft.Python.Analysis.Analyzer {
@@ -207,6 +209,7 @@ private async Task<int> AnalyzeAffectedEntriesAsync(Stopwatch stopWatch) {
207209
if (Interlocked.Increment(ref _runningTasks) >= _maxTaskRunning || _walker.Remaining == 1) {
208210
RunAnalysis(node, stopWatch);
209211
} else {
212+
ace.AddOne();
210213
StartAnalysis(node, ace, stopWatch).DoNotWait();
211214
}
212215
}
@@ -247,7 +250,6 @@ private Task StartAnalysis(IDependencyChainNode<PythonAnalyzerEntry> node, Async
247250
/// </summary>
248251
private void Analyze(IDependencyChainNode<PythonAnalyzerEntry> node, AsyncCountdownEvent ace, Stopwatch stopWatch) {
249252
try {
250-
ace?.AddOne();
251253
var entry = node.Value;
252254

253255
if (!entry.IsValidVersion(_walker.Version, out var module, out var ast)) {
@@ -370,6 +372,10 @@ private void LogException(IPythonModule module, Exception exception) {
370372
if (_log != null) {
371373
_log.Log(TraceEventType.Verbose, $"Analysis of {module.Name}({module.ModuleType}) failed. {exception}");
372374
}
375+
376+
if (TestEnvironment.Current != null) {
377+
ExceptionDispatchInfo.Capture(exception).Throw();
378+
}
373379
}
374380

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

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: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,5 @@ 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 void TransferDocumentationAndLocation(this IPythonType s, IPythonType d) {
32-
if (s != d && s is PythonType src && d is PythonType dst) {
33-
var documentation = src.Documentation;
34-
if (!string.IsNullOrEmpty(documentation)) {
35-
dst.SetDocumentation(documentation);
36-
}
37-
dst.Location = src.Location;
38-
}
39-
}
4030
}
4131
}

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/Core/Impl/Extensions/TaskExtensions.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
using System.Runtime.ExceptionServices;
1818
using System.Threading;
1919
using System.Threading.Tasks;
20-
using Microsoft.Python.Core.Threading;
20+
using Microsoft.Python.Core.Testing;
2121

2222
namespace Microsoft.Python.Core {
2323
public static class TaskExtensions {
@@ -52,6 +52,12 @@ private static void SetCompletionResultToContinuation<T>(Task<T> task, object st
5252
/// <see cref="OperationCanceledException"/> is always ignored.
5353
/// </remarks>
5454
public static void DoNotWait(this Task task) {
55+
if (TestEnvironment.Current != null && TestEnvironment.Current.TryAddTaskToWait(task)) {
56+
if (!task.IsCompleted) {
57+
return;
58+
}
59+
}
60+
5561
if (task.IsCompleted) {
5662
ReThrowTaskException(task);
5763
return;

src/Core/Test/TestLogger.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ public void Dispose() {
3737
public TraceEventType LogLevel { get; set; } = TraceEventType.Verbose;
3838
public void Log(TraceEventType eventType, IFormattable message) => Log(eventType, message.ToString());
3939
public void Log(TraceEventType eventType, string message) {
40-
4140
var m = $"[{TestEnvironmentImpl.Elapsed()}]: {message}";
4241
switch (eventType) {
4342
case TraceEventType.Error:

0 commit comments

Comments
 (0)