From 7c51c3f3651361e4c3b893ca018162c3ee3bf300 Mon Sep 17 00:00:00 2001 From: Cameron Trando Date: Wed, 4 Sep 2019 12:24:26 -0700 Subject: [PATCH 1/2] Fixing bound --- .../Impl/Extensions/ArgumentSetExtensions.cs | 5 ++- .../Typing/Types/GenericTypeParameter.cs | 13 ++++++- .../Specializations/Typing/TypingModule.cs | 2 +- .../Ast/Impl/Types/PythonFunctionOverload.cs | 2 +- src/Analysis/Ast/Test/GenericsTests.cs | 38 +++++++++++++++++++ 5 files changed, 55 insertions(+), 5 deletions(-) diff --git a/src/Analysis/Ast/Impl/Extensions/ArgumentSetExtensions.cs b/src/Analysis/Ast/Impl/Extensions/ArgumentSetExtensions.cs index f45e95409..5bd03df70 100644 --- a/src/Analysis/Ast/Impl/Extensions/ArgumentSetExtensions.cs +++ b/src/Analysis/Ast/Impl/Extensions/ArgumentSetExtensions.cs @@ -33,8 +33,11 @@ public static IReadOnlyList> Arguments(this IArgument public static T Argument(this IArgumentSet args, int index) where T : class => args.Arguments[index].Value as T; + public static IArgument Argument(this IArgumentSet args, string name) + => args.Arguments.FirstOrDefault(a => name.Equals(a.Name)); + public static T GetArgumentValue(this IArgumentSet args, string name) where T : class { - var value = args.Arguments.FirstOrDefault(a => name.Equals(a.Name))?.Value; + var value = Argument(args, name)?.Value; if (value == null && args.DictionaryArgument?.Arguments != null && args.DictionaryArgument.Arguments.TryGetValue(name, out var m)) { return m as T; } diff --git a/src/Analysis/Ast/Impl/Specializations/Typing/Types/GenericTypeParameter.cs b/src/Analysis/Ast/Impl/Specializations/Typing/Types/GenericTypeParameter.cs index eb7a59412..31e9b6fa6 100644 --- a/src/Analysis/Ast/Impl/Specializations/Typing/Types/GenericTypeParameter.cs +++ b/src/Analysis/Ast/Impl/Specializations/Typing/Types/GenericTypeParameter.cs @@ -20,6 +20,7 @@ using Microsoft.Python.Analysis.Types; using Microsoft.Python.Analysis.Utilities; using Microsoft.Python.Analysis.Values; +using Microsoft.Python.Core.Diagnostics; using Microsoft.Python.Core.Text; using Microsoft.Python.Parsing; @@ -88,11 +89,17 @@ private static bool TypeVarArgumentsValid(IArgumentSet argSet) { /// private static IPythonType GetBoundType(IArgumentSet argSet) { var eval = argSet.Eval; - var rawBound = argSet.GetArgumentValue("bound"); + var boundArg = argSet.Argument("bound"); + // User did not pass in upper bound, bail + if(boundArg.ValueIsDefault) { + return null; + } + + var rawBound = boundArg.Value as IMember; switch (rawBound) { case IPythonType t: return t; - case IPythonConstant c when c.GetString() != null: + case IPythonConstant c when !string.IsNullOrEmpty(c.GetString()): return eval.GetTypeFromString(c.GetString()); default: return rawBound.GetPythonType(); @@ -100,6 +107,8 @@ private static IPythonType GetBoundType(IArgumentSet argSet) { } public static IPythonType FromTypeVar(IArgumentSet argSet, IPythonModule declaringModule, IndexSpan location = default) { + Check.ArgumentNotNull(nameof(argSet.Eval), argSet.Eval); + if (!TypeVarArgumentsValid(argSet)) { return declaringModule.Interpreter.UnknownType; } diff --git a/src/Analysis/Ast/Impl/Specializations/Typing/TypingModule.cs b/src/Analysis/Ast/Impl/Specializations/Typing/TypingModule.cs index 124a89306..4669d3ec3 100644 --- a/src/Analysis/Ast/Impl/Specializations/Typing/TypingModule.cs +++ b/src/Analysis/Ast/Impl/Specializations/Typing/TypingModule.cs @@ -54,7 +54,7 @@ private void SpecializeMembers() { o.SetParameters(new List { new ParameterInfo("name", Interpreter.GetBuiltinType(BuiltinTypeId.Str), ParameterKind.Normal, null), new ParameterInfo("constraints", Interpreter.GetBuiltinType(BuiltinTypeId.Str), ParameterKind.List, null), - new ParameterInfo("bound", Interpreter.GetBuiltinType(BuiltinTypeId.Str), ParameterKind.KeywordOnly, new PythonConstant(null, Interpreter.GetBuiltinType(BuiltinTypeId.NoneType))), + new ParameterInfo("bound", Interpreter.GetBuiltinType(BuiltinTypeId.Type), ParameterKind.KeywordOnly, new PythonConstant(null, Interpreter.GetBuiltinType(BuiltinTypeId.NoneType))), new ParameterInfo("covariant", Interpreter.GetBuiltinType(BuiltinTypeId.Bool), ParameterKind.KeywordOnly, new PythonConstant(false, Interpreter.GetBuiltinType(BuiltinTypeId.Bool))), new ParameterInfo("contravariant", Interpreter.GetBuiltinType(BuiltinTypeId.Bool), ParameterKind.KeywordOnly, new PythonConstant(false, Interpreter.GetBuiltinType(BuiltinTypeId.Bool))) }); diff --git a/src/Analysis/Ast/Impl/Types/PythonFunctionOverload.cs b/src/Analysis/Ast/Impl/Types/PythonFunctionOverload.cs index 4a94c0e5d..1154180f5 100644 --- a/src/Analysis/Ast/Impl/Types/PythonFunctionOverload.cs +++ b/src/Analysis/Ast/Impl/Types/PythonFunctionOverload.cs @@ -201,7 +201,7 @@ private IMember CreateSpecificReturnFromTypeVar(IPythonClassType selfClassType, } // Try getting the type from the type parameter bound - if (returnType.Bound != null) { + if (!returnType.Bound.IsUnknown()) { return new PythonInstance(returnType.Bound); } diff --git a/src/Analysis/Ast/Test/GenericsTests.cs b/src/Analysis/Ast/Test/GenericsTests.cs index d2b3e52c3..bb05c105e 100644 --- a/src/Analysis/Ast/Test/GenericsTests.cs +++ b/src/Analysis/Ast/Test/GenericsTests.cs @@ -1330,6 +1330,44 @@ def get(self) -> T: ... analysis.Should().HaveVariable("x").OfType("A"); } + [TestMethod, Priority(0)] + public async Task GenericDefaultBound() { + const string code = @" +from typing import TypeVar, Generic +from logging import Logger, getLogger + +T = TypeVar('T') + +class A: ... + +class Test(Generic[T]): + def get(self) -> T: ... + +x = Test().get() +"; + var analysis = await GetAnalysisAsync(code); + analysis.Should().HaveVariable("x").OfType("T"); + } + + [TestMethod, Priority(0)] + public async Task GenericUnknownBound() { + const string code = @" +from typing import TypeVar, Generic +from logging import Logger, getLogger + +T = TypeVar('T', bound='unknown_thing') + +class A: ... + +class Test(Generic[T]): + def get(self) -> T: ... + +x = Test().get() +"; + var analysis = await GetAnalysisAsync(code); + analysis.Should().HaveVariable("x").OfType("T"); + } + [TestMethod, Priority(0)] public async Task GenericPath() { const string code = @" From 7385179e8b0326a2427448c85a70a93f1df36794 Mon Sep 17 00:00:00 2001 From: Cameron Trando Date: Fri, 6 Sep 2019 10:31:02 -0700 Subject: [PATCH 2/2] build failure --- src/Analysis/Ast/Impl/Extensions/ArgumentSetExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Analysis/Ast/Impl/Extensions/ArgumentSetExtensions.cs b/src/Analysis/Ast/Impl/Extensions/ArgumentSetExtensions.cs index 622b1638c..12f1397bb 100644 --- a/src/Analysis/Ast/Impl/Extensions/ArgumentSetExtensions.cs +++ b/src/Analysis/Ast/Impl/Extensions/ArgumentSetExtensions.cs @@ -36,7 +36,7 @@ public static T Argument(this IArgumentSet args, int index) where T : class public static IArgument Argument(this IArgumentSet args, string name, bool excludeDefault = true) => args.Arguments.FirstOrDefault(a => name.Equals(a.Name) && !(excludeDefault &&a.ValueIsDefault)); - public static T GetArgumentValue(this IArgumentSet args, string name, bool excludeDefault) where T : class { + public static T GetArgumentValue(this IArgumentSet args, string name, bool excludeDefault = true) where T : class { var value = Argument(args, name, excludeDefault)?.Value; if (value == null && args.DictionaryArgument?.Arguments != null && args.DictionaryArgument.Arguments.TryGetValue(name, out var m)) { return m as T;