diff --git a/src/Analysis/Ast/Impl/Types/ArgumentSet.cs b/src/Analysis/Ast/Impl/Types/ArgumentSet.cs index 180bbaf63..edefe2b83 100644 --- a/src/Analysis/Ast/Impl/Types/ArgumentSet.cs +++ b/src/Analysis/Ast/Impl/Types/ArgumentSet.cs @@ -17,7 +17,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Runtime.CompilerServices; using Microsoft.Python.Analysis.Analyzer; using Microsoft.Python.Analysis.Diagnostics; using Microsoft.Python.Analysis.Extensions; diff --git a/src/Analysis/Ast/Impl/Types/PythonClassType.cs b/src/Analysis/Ast/Impl/Types/PythonClassType.cs index 4e10f0826..ce664c623 100644 --- a/src/Analysis/Ast/Impl/Types/PythonClassType.cs +++ b/src/Analysis/Ast/Impl/Types/PythonClassType.cs @@ -32,6 +32,8 @@ namespace Microsoft.Python.Analysis.Types { [DebuggerDisplay("Class {Name}")] internal class PythonClassType : PythonType, IPythonClassType, IPythonTemplateType, IEquatable { private static readonly string[] _classMethods = { "mro", "__dict__", @"__weakref__" }; + + private Dictionary _specificTypeCache; private IPythonClassType _processing; private List _bases; private IReadOnlyList _mro; @@ -350,19 +352,20 @@ public IPythonType CreateSpecificType(IArgumentSet args) { } // For still undefined parameters try matching passed types in order - for (var i = 0; i < args.Arguments.Count; i++) { + for (int i = 0, gtIndex = 0; i < args.Arguments.Count; i++) { var arg = args.Arguments[i]; if (Equals(arg.Type)) { - continue; + continue; // Typically 'self'. } if (arg.Value is IMember member) { var type = member.GetPythonType(); if (!type.IsUnknown()) { - var gtd = i < genericTypeDefinitions.Count ? genericTypeDefinitions[i] : null; + var gtd = gtIndex < genericTypeDefinitions.Count ? genericTypeDefinitions[gtIndex] : null; if (gtd != null && !specificClassTypeParameters.ContainsKey(gtd.Name)) { specificClassTypeParameters[gtd.Name] = type; } + gtIndex++; } } } @@ -374,7 +377,11 @@ public IPythonType CreateSpecificType(IArgumentSet args) { .ToArray(); var specificName = CodeFormatter.FormatSequence(Name, '[', specificTypes); - var classType = new PythonClassType(specificName, new Location(DeclaringModule)); + _specificTypeCache = _specificTypeCache ?? new Dictionary(); + if (_specificTypeCache.TryGetValue(specificName, out var classType)) { + return classType; + } + _specificTypeCache[specificName] = classType = new PythonClassType(specificName, new Location(DeclaringModule)); // Methods returning generic types need to know how to match generic // parameter name to the actual supplied type. @@ -487,7 +494,7 @@ private void SetClassMembers(PythonClassType classType, IArgumentSet args) { // Functions handle generics internally upon the call to Call. foreach (var m in members) { switch (m.Value) { - case IPythonTemplateType tt: { + case IPythonTemplateType tt when tt.IsGeneric(): { var specificType = tt.CreateSpecificType(args); classType.AddMember(m.Key, specificType, true); break; diff --git a/src/Analysis/Ast/Test/ClassesTests.cs b/src/Analysis/Ast/Test/ClassesTests.cs index 19e6639d2..1d817f3cb 100644 --- a/src/Analysis/Ast/Test/ClassesTests.cs +++ b/src/Analysis/Ast/Test/ClassesTests.cs @@ -13,6 +13,7 @@ // See the Apache Version 2.0 License for specific language governing // permissions and limitations under the License. +using System.Diagnostics; using System.IO; using System.Linq; using System.Threading.Tasks; @@ -595,5 +596,43 @@ def __init__(self): .And.HaveVariable("e").OfType(BuiltinTypeId.Unknown) .And.HaveVariable("f").OfType(BuiltinTypeId.Unknown); } + + [TestMethod, Priority(0)] + public async Task ProxyBase() { + const string code = @" +from weakref import proxy + +class C0(): pass +class C1(C0): pass +class C2(C1): pass +class C3(C2): pass +class C4(C3): pass +class C5(C4): pass +class C6(C5): pass +class C7(C6): pass +class C8(C7): pass + +class Test(): + def __init__(self): + p = proxy(self) + + F1 = C1 + F2 = C2 + F3 = C3 + F4 = C4 + F5 = C5 + F6 = C6 + F7 = C7 + F8 = C8 +"; + // Verifies that analysis of the fragment completes in reasonable time. + // see https://github.com/microsoft/python-language-server/issues/1291. + var sw = Stopwatch.StartNew(); + await GetAnalysisAsync(code); + sw.Stop(); + // Desktop: product time is typically less few seconds second. + // Test run time: typically ~ 20 sec. + sw.ElapsedMilliseconds.Should().BeLessThan(60000); + } } }