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

Commit a5b6e5e

Browse files
Cherry pick fixes from Release branch into PTVS branch (#578)
* Fix #470 (#478) * Fix various common exceptions, log on a failed directory load (#498) * rethrow exception after telemetry instead of disposing * log an error instead of crashing when hitting some exceptions loading files, fix nullref when shutting down without an idle tracker * fix short circuiting of question mark checks to prevent null from being returned * print name of exception instead of relying on ToString * don't use MaybeEnumerate * upgrade message to Show * Mitigate some of #495 via MRO member selection and analysis set optimization (#517) This isn't a complete fix, but does seem to improve #495 in some cases. Adds back the early MRO return removed in #277, so now large class hierarchies won't over-propagate types (where some of the trouble with fastai happens do to the Transform class). I also optimized AnalysisHashSet a little to do length checks when possible. There are still times when things get caught up comparing unions for a while, but it seems to be nondeterministic. * Two path resolution bug fixes - Fix #509: PLS doesn't flag error in a relative import if it is found in another root directory (#519) - Fix #510: PLS doesn't resolve relative paths correctly * Catch exceptions when importing from search paths (#525) * catch exceptions when importing from search paths * retry instead of not found
1 parent 4d7e953 commit a5b6e5e

22 files changed

+319
-40
lines changed

src/Analysis/Engine/Impl/AnalysisHashSet.cs

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,19 @@ public bool SetEquals(IAnalysisSet other) {
275275
return true;
276276
}
277277

278+
// Only access Count once to prevent wasted time in bucket locks.
279+
var lCount = Count;
280+
var rCount = other.Count;
281+
282+
if (lCount == 0 && rCount == 0) {
283+
return true;
284+
}
285+
278286
if (Comparer == other.Comparer) {
287+
if (lCount != rCount) {
288+
return false;
289+
}
290+
279291
// Quick check for any unmatched hashcodes.
280292
// This can conclusively prove the sets are not equal, but cannot
281293
// prove equality.
@@ -290,17 +302,16 @@ public bool SetEquals(IAnalysisSet other) {
290302
}
291303
}
292304

293-
var otherHc = new HashSet<AnalysisValue>(other, _comparer);
294-
foreach (var key in this) {
295-
if (!otherHc.Remove(key)) {
296-
return false;
297-
}
298-
}
299-
if (otherHc.Any()) {
305+
if (lCount == 0 || rCount == 0) {
300306
return false;
301307
}
302308

303-
return true;
309+
if (lCount == 1 && rCount == 1) {
310+
return _comparer.Equals(this.First(), other.First());
311+
}
312+
313+
var hs = new HashSet<AnalysisValue>(other, _comparer);
314+
return hs.SetEquals(this);
304315
}
305316

306317
/// <summary>

src/Analysis/Engine/Impl/AnalysisSet.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,8 @@ public static bool ContainsAny(this IAnalysisSet set, IAnalysisSet values) {
534534
sealed class ObjectComparer : IEqualityComparer<AnalysisValue>, IEqualityComparer<IAnalysisSet> {
535535
public static readonly ObjectComparer Instance = new ObjectComparer();
536536

537+
private ObjectComparer() { }
538+
537539
public bool Equals(AnalysisValue x, AnalysisValue y) {
538540
#if FULL_VALIDATION
539541
if (x != null && y != null) {
@@ -574,7 +576,7 @@ sealed class UnionComparer : IEqualityComparer<AnalysisValue>, IEqualityComparer
574576

575577
public readonly int Strength;
576578

577-
public UnionComparer(int strength = 0) {
579+
private UnionComparer(int strength = 0) {
578580
Strength = strength;
579581
}
580582

src/Analysis/Engine/Impl/Analyzer/DDG.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,9 @@ public override bool Walk(FromImportStatement node) {
296296
case ImportNotFound notFound:
297297
MakeUnresolvedImport(notFound.FullName, node.Root);
298298
return false;
299+
case NoKnownParentPackage _:
300+
MakeNoKnownParentPackageImport(node.Root);
301+
return false;
299302
default:
300303
return false;
301304
}
@@ -464,6 +467,10 @@ private void MakeUnresolvedImport(string name, Node spanNode) {
464467
ProjectState.AddDiagnostic(spanNode, _unit, ErrorMessages.UnresolvedImport(name), DiagnosticSeverity.Warning, ErrorMessages.UnresolvedImportCode);
465468
}
466469

470+
private void MakeNoKnownParentPackageImport(Node spanNode) {
471+
ProjectState.AddDiagnostic(spanNode, _unit, Resources.ErrorRelativeImportNoPackage, DiagnosticSeverity.Warning, ErrorMessages.UnresolvedImportCode);
472+
}
473+
467474
private void ImportModule(in ImportStatement node, in IModule module, in DottedName moduleImportExpression, in NameExpression asNameExpression) {
468475
// "import fob.oar as baz" is handled as
469476
// baz = import_module('fob.oar')
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Python Tools for Visual Studio
2+
// Copyright(c) Microsoft Corporation
3+
// All rights reserved.
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the License); you may not use
6+
// this file except in compliance with the License. You may obtain a copy of the
7+
// License at http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS
10+
// OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY
11+
// IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
12+
// MERCHANTABILITY OR NON-INFRINGEMENT.
13+
//
14+
// See the Apache Version 2.0 License for specific language governing
15+
// permissions and limitations under the License.
16+
17+
namespace Microsoft.PythonTools.Analysis.DependencyResolution {
18+
internal class NoKnownParentPackage : IImportSearchResult { }
19+
}

src/Analysis/Engine/Impl/DependencyResolution/PathResolverSnapshot.cs

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -187,16 +187,11 @@ public IImportSearchResult GetImportsFromRelativePath(in string modulePath, in i
187187
return default;
188188
}
189189

190-
if (parentCount > lastEdge.PathLength) {
191-
// Can't get outside of the root
192-
return default;
193-
}
194-
195190
var fullNameList = relativePath.ToList();
196191
if (lastEdge.IsNonRooted) {
197192
// Handle relative imports only for modules in the same folder
198193
if (parentCount > 1) {
199-
return default;
194+
return new NoKnownParentPackage();
200195
}
201196

202197
if (parentCount == 1 && fullNameList.Count == 1 && lastEdge.Start.TryGetChild(fullNameList[0], out var nameNode)) {
@@ -209,12 +204,27 @@ public IImportSearchResult GetImportsFromRelativePath(in string modulePath, in i
209204
.ToString());
210205
}
211206

207+
var relativeInWorkDirectory = false;
208+
if (parentCount > lastEdge.PathLength - 2) {
209+
relativeInWorkDirectory = _workDirectory.EqualsOrdinal(lastEdge.FirstEdge.End.Name, IgnoreCaseInPaths);
210+
211+
// Relative path must be only inside package
212+
// Exception for working directory cause it can be a root directory of the package
213+
if (!relativeInWorkDirectory) {
214+
return new NoKnownParentPackage();
215+
}
216+
}
217+
212218
var relativeParentEdge = lastEdge.GetPrevious(parentCount);
213219

214220
var rootEdges = new List<Edge>();
215-
for (var i = 0; i < _roots.Count; i++) {
216-
if (RootContains(i, relativeParentEdge, out var rootEdge)) {
217-
rootEdges.Add(rootEdge);
221+
if (relativeInWorkDirectory) {
222+
rootEdges.Add(lastEdge.FirstEdge);
223+
} else {
224+
for (var i = 0; i < _roots.Count; i++) {
225+
if (RootContains(i, relativeParentEdge, out var rootEdge)) {
226+
rootEdges.Add(rootEdge);
227+
}
218228
}
219229
}
220230

@@ -226,7 +236,13 @@ public IImportSearchResult GetImportsFromRelativePath(in string modulePath, in i
226236
return default;
227237
}
228238

229-
var fullName = GetFullModuleNameBuilder(relativeParentEdge).Append(".", fullNameList).ToString();
239+
var fullNameBuilder = GetFullModuleNameBuilder(relativeParentEdge);
240+
if (!relativeParentEdge.IsFirst) {
241+
AppendName(fullNameBuilder, relativeParentEdge.End.Name);
242+
fullNameBuilder.Append(".");
243+
}
244+
var fullName = fullNameBuilder.Append(".", fullNameList).ToString();
245+
230246
return new ImportNotFound(fullName);
231247
}
232248

@@ -250,7 +266,7 @@ private static bool TryCreateModuleImport(Edge lastEdge, out ModuleImport module
250266
=> TryCreateModuleImport(lastEdge.FirstEdge.End, lastEdge.End, out moduleImport);
251267

252268
private static bool TryCreateModuleImport(Node rootNode, Node moduleNode, out ModuleImport moduleImport) {
253-
if (moduleNode.TryGetChild("__init__", out var initPyNode) && initPyNode.IsModule) {
269+
if (IsPackageWithInitPy(moduleNode, out var initPyNode)) {
254270
moduleImport = new ModuleImport(moduleNode.Name, initPyNode.FullModuleName, rootNode.Name, initPyNode.ModulePath, false);
255271
return true;
256272
}
@@ -517,7 +533,7 @@ private static bool TryFindImport(IEnumerable<Edge> rootEdges, List<string> full
517533
private static bool TryFindName(in Edge edge, in IEnumerable<string> nameParts, out Edge lastEdge) {
518534
lastEdge = edge;
519535
foreach (var name in nameParts) {
520-
if (lastEdge.End.IsModule) {
536+
if (lastEdge.End.IsModule && !IsPackageWithInitPy(lastEdge.End, out _)) {
521537
return false;
522538
}
523539
var index = lastEdge.End.GetChildIndex(name);
@@ -653,7 +669,7 @@ private static StringBuilder GetFullModuleNameBuilder(in Edge lastEdge) {
653669
while (edge != lastEdge) {
654670
AppendName(sb, edge.End.Name);
655671
edge = edge.Next;
656-
};
672+
}
657673

658674
return sb;
659675
}
@@ -744,6 +760,9 @@ private static int GetModuleNameStart(string rootedModulePath)
744760
private static int GetModuleNameEnd(string rootedModulePath)
745761
=> IsPythonCompiled(rootedModulePath) ? rootedModulePath.IndexOf('.', GetModuleNameStart(rootedModulePath)) : rootedModulePath.LastIndexOf('.');
746762

763+
private static bool IsPackageWithInitPy(Node node, out Node initPyNode)
764+
=> node.TryGetChild("__init__", out initPyNode) && initPyNode.IsModule;
765+
747766
private static bool IsNotInitPy(string name)
748767
=> !name.EqualsOrdinal("__init__");
749768

src/Analysis/Engine/Impl/Infrastructure/Extensions/StringExtensions.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,9 @@ public static bool EqualsIgnoreCase(this string s, string other)
195195
public static bool EqualsOrdinal(this string s, string other)
196196
=> string.Equals(s, other, StringComparison.Ordinal);
197197

198+
public static bool EqualsOrdinal(this string s, string other, bool ignoreCase)
199+
=> string.Equals(s, other, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);
200+
198201
public static bool EqualsOrdinal(this string s, int index, string other, int otherIndex, int length, bool ignoreCase = false)
199202
=> string.Compare(s, index, other, otherIndex, length, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal) == 0;
200203

src/Analysis/Engine/Impl/Intellisense/AnalysisQueue.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ private async Task ConsumerLoop() {
6767
return;
6868
} catch (Exception ex) when (!ex.IsCriticalException()) {
6969
UnhandledException?.Invoke(this, new UnhandledExceptionEventArgs(ex, false));
70-
Dispose();
70+
throw;
7171
}
7272
}
7373
RaiseEventOnThreadPool(AnalysisComplete);
@@ -208,7 +208,7 @@ public async Task Handler(CancellationToken cancellationToken) {
208208
}
209209

210210
private sealed class QueueItemComparer : IEqualityComparer<QueueItem> {
211-
public static IEqualityComparer<QueueItem> Instance { get; } = new QueueItemComparer();
211+
public static readonly IEqualityComparer<QueueItem> Instance = new QueueItemComparer();
212212

213213
private QueueItemComparer() { }
214214
public bool Equals(QueueItem x, QueueItem y) => Equals(x.Key, y.Key);

src/Analysis/Engine/Impl/Interpreter/Ast/AstModuleResolution.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,12 @@ public async Task<TryImportModuleResult> TryImportModuleAsync(string name, PathR
205205
} catch (OperationCanceledException) {
206206
_log?.Log(TraceLevel.Error, "ImportTimeout", name, "ImportFromSearchPaths");
207207
return TryImportModuleResult.Timeout;
208+
} catch (Exception ex) when (
209+
ex is IOException // FileNotFoundException, DirectoryNotFoundException, PathTooLongException, etc
210+
|| ex is UnauthorizedAccessException
211+
) {
212+
_log?.Log(TraceLevel.Error, "ImportException", name, "ImportFromSearchPaths", ex.GetType().Name, ex.Message);
213+
return TryImportModuleResult.NeedRetry;
208214
}
209215
}
210216

src/Analysis/Engine/Impl/LocationInfo.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,12 @@ public bool Equals(ILocationInfo other) {
8383
/// Provides an IEqualityComparer that compares line, column and project entries. By
8484
/// default locations are equaitable based upon only line/project entry.
8585
/// </summary>
86-
public static IEqualityComparer<ILocationInfo> FullComparer { get; } = new FullLocationComparer();
86+
public static IEqualityComparer<ILocationInfo> FullComparer => FullLocationComparer.Instance;
8787

8888
sealed class FullLocationComparer : IEqualityComparer<ILocationInfo>, IEqualityComparer<LocationInfo> {
89+
public static readonly FullLocationComparer Instance = new FullLocationComparer();
90+
91+
private FullLocationComparer() { }
8992
public bool Equals(LocationInfo x, LocationInfo y) => EqualsImpl(x, y);
9093
public bool Equals(ILocationInfo x, ILocationInfo y) => EqualsImpl(x, y);
9194

src/Analysis/Engine/Impl/OverloadResult.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -411,8 +411,8 @@ internal ParameterResult GetParameterResultFromParameterInfo(IParameterInfo para
411411
}
412412

413413
class OverloadResultComparer : EqualityComparer<OverloadResult> {
414-
public static IEqualityComparer<OverloadResult> Instance = new OverloadResultComparer(false);
415-
public static IEqualityComparer<OverloadResult> WeakInstance = new OverloadResultComparer(true);
414+
public static readonly IEqualityComparer<OverloadResult> Instance = new OverloadResultComparer(false);
415+
public static readonly IEqualityComparer<OverloadResult> WeakInstance = new OverloadResultComparer(true);
416416

417417
private readonly bool _weak;
418418

0 commit comments

Comments
 (0)