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

Cherry pick fixes from Release branch into PTVS branch #578

Merged
merged 5 commits into from
Feb 4, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
27 changes: 19 additions & 8 deletions src/Analysis/Engine/Impl/AnalysisHashSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,19 @@ public bool SetEquals(IAnalysisSet other) {
return true;
}

// Only access Count once to prevent wasted time in bucket locks.
var lCount = Count;
var rCount = other.Count;

if (lCount == 0 && rCount == 0) {
return true;
}

if (Comparer == other.Comparer) {
if (lCount != rCount) {
return false;
}

// Quick check for any unmatched hashcodes.
// This can conclusively prove the sets are not equal, but cannot
// prove equality.
Expand All @@ -290,17 +302,16 @@ public bool SetEquals(IAnalysisSet other) {
}
}

var otherHc = new HashSet<AnalysisValue>(other, _comparer);
foreach (var key in this) {
if (!otherHc.Remove(key)) {
return false;
}
}
if (otherHc.Any()) {
if (lCount == 0 || rCount == 0) {
return false;
}

return true;
if (lCount == 1 && rCount == 1) {
return _comparer.Equals(this.First(), other.First());
}

var hs = new HashSet<AnalysisValue>(other, _comparer);
return hs.SetEquals(this);
}

/// <summary>
Expand Down
4 changes: 3 additions & 1 deletion src/Analysis/Engine/Impl/AnalysisSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,8 @@ public static bool ContainsAny(this IAnalysisSet set, IAnalysisSet values) {
sealed class ObjectComparer : IEqualityComparer<AnalysisValue>, IEqualityComparer<IAnalysisSet> {
public static readonly ObjectComparer Instance = new ObjectComparer();

private ObjectComparer() { }

public bool Equals(AnalysisValue x, AnalysisValue y) {
#if FULL_VALIDATION
if (x != null && y != null) {
Expand Down Expand Up @@ -574,7 +576,7 @@ sealed class UnionComparer : IEqualityComparer<AnalysisValue>, IEqualityComparer

public readonly int Strength;

public UnionComparer(int strength = 0) {
private UnionComparer(int strength = 0) {
Strength = strength;
}

Expand Down
7 changes: 7 additions & 0 deletions src/Analysis/Engine/Impl/Analyzer/DDG.cs
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,9 @@ public override bool Walk(FromImportStatement node) {
case ImportNotFound notFound:
MakeUnresolvedImport(notFound.FullName, node.Root);
return false;
case NoKnownParentPackage _:
MakeNoKnownParentPackageImport(node.Root);
return false;
default:
return false;
}
Expand Down Expand Up @@ -464,6 +467,10 @@ private void MakeUnresolvedImport(string name, Node spanNode) {
ProjectState.AddDiagnostic(spanNode, _unit, ErrorMessages.UnresolvedImport(name), DiagnosticSeverity.Warning, ErrorMessages.UnresolvedImportCode);
}

private void MakeNoKnownParentPackageImport(Node spanNode) {
ProjectState.AddDiagnostic(spanNode, _unit, Resources.ErrorRelativeImportNoPackage, DiagnosticSeverity.Warning, ErrorMessages.UnresolvedImportCode);
}

private void ImportModule(in ImportStatement node, in IModule module, in DottedName moduleImportExpression, in NameExpression asNameExpression) {
// "import fob.oar as baz" is handled as
// baz = import_module('fob.oar')
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Python Tools for Visual Studio
// 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.

namespace Microsoft.PythonTools.Analysis.DependencyResolution {
internal class NoKnownParentPackage : IImportSearchResult { }
}
Original file line number Diff line number Diff line change
Expand Up @@ -187,16 +187,11 @@ public IImportSearchResult GetImportsFromRelativePath(in string modulePath, in i
return default;
}

if (parentCount > lastEdge.PathLength) {
// Can't get outside of the root
return default;
}

var fullNameList = relativePath.ToList();
if (lastEdge.IsNonRooted) {
// Handle relative imports only for modules in the same folder
if (parentCount > 1) {
return default;
return new NoKnownParentPackage();
}

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

var relativeInWorkDirectory = false;
if (parentCount > lastEdge.PathLength - 2) {
relativeInWorkDirectory = _workDirectory.EqualsOrdinal(lastEdge.FirstEdge.End.Name, IgnoreCaseInPaths);

// Relative path must be only inside package
// Exception for working directory cause it can be a root directory of the package
if (!relativeInWorkDirectory) {
return new NoKnownParentPackage();
}
}

var relativeParentEdge = lastEdge.GetPrevious(parentCount);

var rootEdges = new List<Edge>();
for (var i = 0; i < _roots.Count; i++) {
if (RootContains(i, relativeParentEdge, out var rootEdge)) {
rootEdges.Add(rootEdge);
if (relativeInWorkDirectory) {
rootEdges.Add(lastEdge.FirstEdge);
} else {
for (var i = 0; i < _roots.Count; i++) {
if (RootContains(i, relativeParentEdge, out var rootEdge)) {
rootEdges.Add(rootEdge);
}
}
}

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

var fullName = GetFullModuleNameBuilder(relativeParentEdge).Append(".", fullNameList).ToString();
var fullNameBuilder = GetFullModuleNameBuilder(relativeParentEdge);
if (!relativeParentEdge.IsFirst) {
AppendName(fullNameBuilder, relativeParentEdge.End.Name);
fullNameBuilder.Append(".");
}
var fullName = fullNameBuilder.Append(".", fullNameList).ToString();

return new ImportNotFound(fullName);
}

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

private static bool TryCreateModuleImport(Node rootNode, Node moduleNode, out ModuleImport moduleImport) {
if (moduleNode.TryGetChild("__init__", out var initPyNode) && initPyNode.IsModule) {
if (IsPackageWithInitPy(moduleNode, out var initPyNode)) {
moduleImport = new ModuleImport(moduleNode.Name, initPyNode.FullModuleName, rootNode.Name, initPyNode.ModulePath, false);
return true;
}
Expand Down Expand Up @@ -517,7 +533,7 @@ private static bool TryFindImport(IEnumerable<Edge> rootEdges, List<string> full
private static bool TryFindName(in Edge edge, in IEnumerable<string> nameParts, out Edge lastEdge) {
lastEdge = edge;
foreach (var name in nameParts) {
if (lastEdge.End.IsModule) {
if (lastEdge.End.IsModule && !IsPackageWithInitPy(lastEdge.End, out _)) {
return false;
}
var index = lastEdge.End.GetChildIndex(name);
Expand Down Expand Up @@ -653,7 +669,7 @@ private static StringBuilder GetFullModuleNameBuilder(in Edge lastEdge) {
while (edge != lastEdge) {
AppendName(sb, edge.End.Name);
edge = edge.Next;
};
}

return sb;
}
Expand Down Expand Up @@ -744,6 +760,9 @@ private static int GetModuleNameStart(string rootedModulePath)
private static int GetModuleNameEnd(string rootedModulePath)
=> IsPythonCompiled(rootedModulePath) ? rootedModulePath.IndexOf('.', GetModuleNameStart(rootedModulePath)) : rootedModulePath.LastIndexOf('.');

private static bool IsPackageWithInitPy(Node node, out Node initPyNode)
=> node.TryGetChild("__init__", out initPyNode) && initPyNode.IsModule;

private static bool IsNotInitPy(string name)
=> !name.EqualsOrdinal("__init__");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,9 @@ public static bool EqualsIgnoreCase(this string s, string other)
public static bool EqualsOrdinal(this string s, string other)
=> string.Equals(s, other, StringComparison.Ordinal);

public static bool EqualsOrdinal(this string s, string other, bool ignoreCase)
=> string.Equals(s, other, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);

public static bool EqualsOrdinal(this string s, int index, string other, int otherIndex, int length, bool ignoreCase = false)
=> string.Compare(s, index, other, otherIndex, length, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal) == 0;

Expand Down
4 changes: 2 additions & 2 deletions src/Analysis/Engine/Impl/Intellisense/AnalysisQueue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ private async Task ConsumerLoop() {
return;
} catch (Exception ex) when (!ex.IsCriticalException()) {
UnhandledException?.Invoke(this, new UnhandledExceptionEventArgs(ex, false));
Dispose();
throw;
}
}
RaiseEventOnThreadPool(AnalysisComplete);
Expand Down Expand Up @@ -208,7 +208,7 @@ public async Task Handler(CancellationToken cancellationToken) {
}

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

private QueueItemComparer() { }
public bool Equals(QueueItem x, QueueItem y) => Equals(x.Key, y.Key);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,12 @@ public async Task<TryImportModuleResult> TryImportModuleAsync(string name, PathR
} catch (OperationCanceledException) {
_log?.Log(TraceLevel.Error, "ImportTimeout", name, "ImportFromSearchPaths");
return TryImportModuleResult.Timeout;
} catch (Exception ex) when (
ex is IOException // FileNotFoundException, DirectoryNotFoundException, PathTooLongException, etc
|| ex is UnauthorizedAccessException
) {
_log?.Log(TraceLevel.Error, "ImportException", name, "ImportFromSearchPaths", ex.GetType().Name, ex.Message);
return TryImportModuleResult.NeedRetry;
}
}

Expand Down
5 changes: 4 additions & 1 deletion src/Analysis/Engine/Impl/LocationInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,12 @@ public bool Equals(ILocationInfo other) {
/// Provides an IEqualityComparer that compares line, column and project entries. By
/// default locations are equaitable based upon only line/project entry.
/// </summary>
public static IEqualityComparer<ILocationInfo> FullComparer { get; } = new FullLocationComparer();
public static IEqualityComparer<ILocationInfo> FullComparer => FullLocationComparer.Instance;

sealed class FullLocationComparer : IEqualityComparer<ILocationInfo>, IEqualityComparer<LocationInfo> {
public static readonly FullLocationComparer Instance = new FullLocationComparer();

private FullLocationComparer() { }
public bool Equals(LocationInfo x, LocationInfo y) => EqualsImpl(x, y);
public bool Equals(ILocationInfo x, ILocationInfo y) => EqualsImpl(x, y);

Expand Down
4 changes: 2 additions & 2 deletions src/Analysis/Engine/Impl/OverloadResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -411,8 +411,8 @@ internal ParameterResult GetParameterResultFromParameterInfo(IParameterInfo para
}

class OverloadResultComparer : EqualityComparer<OverloadResult> {
public static IEqualityComparer<OverloadResult> Instance = new OverloadResultComparer(false);
public static IEqualityComparer<OverloadResult> WeakInstance = new OverloadResultComparer(true);
public static readonly IEqualityComparer<OverloadResult> Instance = new OverloadResultComparer(false);
public static readonly IEqualityComparer<OverloadResult> WeakInstance = new OverloadResultComparer(true);

private readonly bool _weak;

Expand Down
13 changes: 8 additions & 5 deletions src/Analysis/Engine/Impl/PythonAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -586,10 +586,11 @@ internal AnalysisValue GetCached(object key, Func<AnalysisValue> maker) {

internal BuiltinInstanceInfo GetInstance(IPythonType type) => GetBuiltinType(type).Instance;

internal BuiltinClassInfo GetBuiltinType(IPythonType type) =>
(BuiltinClassInfo)GetCached(type,
() => MakeBuiltinType(type)
) ?? ClassInfos[BuiltinTypeId.Object];
internal BuiltinClassInfo GetBuiltinType(IPythonType type)
// Cached value may or may not be a class info. Previous calls to GetAnalysisValueFromObjects
// may have cached a different object for the type. For example, IPythonFunction would cache
// BuiltinFunctionInfo and not BuiltinClassInfo. Therefore, don't use direct cast.
=> GetCached(type, () => MakeBuiltinType(type)) as BuiltinClassInfo ?? MakeBuiltinType(type);

private BuiltinClassInfo MakeBuiltinType(IPythonType type) {
switch (type.TypeId) {
Expand Down Expand Up @@ -903,7 +904,9 @@ private AggregateProjectEntry GetAggregateWorker(IProjectEntry[] all) {
}

class AggregateComparer : IEqualityComparer<IProjectEntry[]> {
public static AggregateComparer Instance = new AggregateComparer();
public static readonly AggregateComparer Instance = new AggregateComparer();

private AggregateComparer() { }

public bool Equals(IProjectEntry[] x, IProjectEntry[] y) {
if (x.Length != y.Length) {
Expand Down
9 changes: 9 additions & 0 deletions src/Analysis/Engine/Impl/Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/Analysis/Engine/Impl/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,9 @@
<data name="ErrorUnresolvedImport" xml:space="preserve">
<value>unresolved import '{0}'</value>
</data>
<data name="ErrorRelativeImportNoPackage" xml:space="preserve">
<value>attempted relative import with no known parent package</value>
</data>
<data name="ErrorUseBeforeDef" xml:space="preserve">
<value>'{0}' used before definition</value>
</data>
Expand Down
3 changes: 2 additions & 1 deletion src/Analysis/Engine/Impl/Values/BuiltinNamespace.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.PythonTools.Analysis.Infrastructure;
using Microsoft.PythonTools.Interpreter;
using Microsoft.PythonTools.Parsing.Ast;
Expand Down Expand Up @@ -105,7 +106,7 @@ public TMemberContainer ContainedValue {

public virtual ILocatedMember GetLocatedMember() => null;

public override IEnumerable<ILocationInfo> Locations => GetLocatedMember()?.Locations.MaybeEnumerate();
public override IEnumerable<ILocationInfo> Locations => GetLocatedMember()?.Locations ?? Enumerable.Empty<ILocationInfo>();

public override bool Equals(object obj) {
if (obj is BuiltinNamespace<TMemberContainer> bn && GetType() == bn.GetType()) {
Expand Down
4 changes: 4 additions & 0 deletions src/Analysis/Engine/Impl/Values/ClassInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -831,6 +831,10 @@ public static IAnalysisSet GetMemberFromMroNoReferences(IEnumerable<IAnalysisSet
result = result.Union(ns.GetMember(node, unit, name));
}
}

if (result != null && result.Count > 0) {
break;
}
}
return result;
}
Expand Down
6 changes: 3 additions & 3 deletions src/Analysis/Engine/Impl/Values/SpecializedNamespace.cs
Original file line number Diff line number Diff line change
Expand Up @@ -209,15 +209,15 @@ public override bool IsOfType(IAnalysisSet klass) {
return _original.IsOfType(klass);
}

public override IEnumerable<ILocationInfo> Locations => _original?.Locations.MaybeEnumerate();
public override IEnumerable<ILocationInfo> Locations => _original?.Locations ?? Enumerable.Empty<ILocationInfo>();

public override string Name => _original == null ? base.Name : _original.Name;

public override IEnumerable<OverloadResult> Overloads => _original?.Overloads.MaybeEnumerate();
public override IEnumerable<OverloadResult> Overloads => _original?.Overloads ?? Enumerable.Empty<OverloadResult>();

public override IPythonType PythonType => _original?.PythonType;

internal override IEnumerable<ILocationInfo> References => _original?.References.MaybeEnumerate();
internal override IEnumerable<ILocationInfo> References => _original?.References ?? Enumerable.Empty<ILocationInfo>();

public override PythonMemberType MemberType => _original == null ? PythonMemberType.Unknown : _original.MemberType;

Expand Down
28 changes: 28 additions & 0 deletions src/Analysis/Engine/Test/AddTestSpecificSearchPathAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Python Tools for Visual Studio
// 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;

namespace AnalysisTests {
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class AddTestSpecificSearchPathAttribute : Attribute {
public string RelativeSearchPath { get; }

public AddTestSpecificSearchPathAttribute(string relativeSearchPath) {
RelativeSearchPath = relativeSearchPath;
}
}
}
Loading