From c582e1ceaee6b01906551e25d016cf264d8bf378 Mon Sep 17 00:00:00 2001 From: Cameron Trando Date: Fri, 16 Aug 2019 17:01:49 -0700 Subject: [PATCH 1/8] instructions --- .../Impl/Specializations/Typing/Types/GenericTypeParameter.cs | 2 ++ src/Analysis/Ast/Impl/Types/PythonClassType.Generics.cs | 3 +++ 2 files changed, 5 insertions(+) diff --git a/src/Analysis/Ast/Impl/Specializations/Typing/Types/GenericTypeParameter.cs b/src/Analysis/Ast/Impl/Specializations/Typing/Types/GenericTypeParameter.cs index 0cdc143fb..6f14adc08 100644 --- a/src/Analysis/Ast/Impl/Specializations/Typing/Types/GenericTypeParameter.cs +++ b/src/Analysis/Ast/Impl/Specializations/Typing/Types/GenericTypeParameter.cs @@ -92,6 +92,8 @@ public static IPythonType FromTypeVar(IArgumentSet argSet, IPythonModule declari return !string.IsNullOrEmpty(typeString) ? argSet.Eval.GetTypeFromString(typeString) : a.GetPythonType(); }).ToArray() ?? Array.Empty(); + // TODO get bound here might have to parse string and look at it + var documentation = GetDocumentation(args, constraints); return new GenericTypeParameter(name, declaringModule, constraints, documentation, location); } diff --git a/src/Analysis/Ast/Impl/Types/PythonClassType.Generics.cs b/src/Analysis/Ast/Impl/Types/PythonClassType.Generics.cs index 88d4f7985..623cf7fd7 100644 --- a/src/Analysis/Ast/Impl/Types/PythonClassType.Generics.cs +++ b/src/Analysis/Ast/Impl/Types/PythonClassType.Generics.cs @@ -251,6 +251,9 @@ private IReadOnlyDictionary GetSpecificTypes } } } + + // TODO resolve bound constraints here by looping over the values in genericTypeToSpecificType + return genericTypeToSpecificType; } From e0cfc9064bc05b0d94610d050c189645488b62cc Mon Sep 17 00:00:00 2001 From: Cameron Trando Date: Tue, 20 Aug 2019 14:09:51 -0700 Subject: [PATCH 2/8] Fixing return value Generic Type Parameter with bound --- .../Definitions/IGenericTypeParameter.cs | 9 +++-- .../Typing/Types/GenericTypeParameter.cs | 33 +++++++++++++---- .../Specializations/Typing/TypingModule.cs | 2 +- .../Impl/Types/PythonClassType.Generics.cs | 13 +++++++ .../Ast/Impl/Types/PythonFunctionOverload.cs | 5 +++ .../Ast/Impl/Typeshed/stdlib/3.4/pathlib.pyi | 2 +- src/Analysis/Ast/Test/GenericsTests.cs | 35 +++++++++++++++++++ 7 files changed, 89 insertions(+), 10 deletions(-) diff --git a/src/Analysis/Ast/Impl/Specializations/Typing/Definitions/IGenericTypeParameter.cs b/src/Analysis/Ast/Impl/Specializations/Typing/Definitions/IGenericTypeParameter.cs index 9aca942a0..a5bc9b814 100644 --- a/src/Analysis/Ast/Impl/Specializations/Typing/Definitions/IGenericTypeParameter.cs +++ b/src/Analysis/Ast/Impl/Specializations/Typing/Definitions/IGenericTypeParameter.cs @@ -21,11 +21,16 @@ namespace Microsoft.Python.Analysis.Specializations.Typing { /// /// Represents generic type definition. Typically value returned by TypeVar. /// - public interface IGenericTypeParameter: IPythonType, IEquatable { + /// See 'https://docs.python.org/3/library/typing.html#typing.TypeVar' + public interface IGenericTypeParameter : IPythonType, IEquatable { /// /// List of constraints for the type. /// - /// See 'https://docs.python.org/3/library/typing.html#typing.TypeVar' IReadOnlyList Constraints { get; } + + /// + /// Bounded type, i.e upper bound this type parameter can represent + /// + IPythonType Bound { get; } } } diff --git a/src/Analysis/Ast/Impl/Specializations/Typing/Types/GenericTypeParameter.cs b/src/Analysis/Ast/Impl/Specializations/Typing/Types/GenericTypeParameter.cs index 6f14adc08..a55ba1abb 100644 --- a/src/Analysis/Ast/Impl/Specializations/Typing/Types/GenericTypeParameter.cs +++ b/src/Analysis/Ast/Impl/Specializations/Typing/Types/GenericTypeParameter.cs @@ -25,11 +25,17 @@ namespace Microsoft.Python.Analysis.Specializations.Typing.Types { internal sealed class GenericTypeParameter : PythonType, IGenericTypeParameter { - public GenericTypeParameter(string name, IPythonModule declaringModule, IReadOnlyList constraints, string documentation, IndexSpan location) + public GenericTypeParameter(string name, IPythonModule declaringModule, IReadOnlyList constraints, + IPythonType bound, string documentation, IndexSpan location) : base(name, new Location(declaringModule), documentation) { Constraints = constraints ?? Array.Empty(); + Bound = bound; } + + #region IGenericTypeParameter public IReadOnlyList Constraints { get; } + public IPythonType Bound { get; } + #endregion public override BuiltinTypeId TypeId => BuiltinTypeId.Type; public override PythonMemberType MemberType => PythonMemberType.Generic; @@ -77,6 +83,22 @@ private static bool TypeVarArgumentsValid(IArgumentSet argSet) { return true; } + /// + /// Given arguments to TypeVar, finds the bound type + /// + private static IPythonType GetBound(IArgumentSet argSet) { + var eval = argSet.Eval; + var rawBound = argSet.GetArgumentValue("bound"); + switch (rawBound) { + case IPythonType t: + return t; + case IPythonConstant c when c.GetString() != null: + return eval.GetTypeFromString(c.GetString()); + default: + return null; + } + } + public static IPythonType FromTypeVar(IArgumentSet argSet, IPythonModule declaringModule, IndexSpan location = default) { if (!TypeVarArgumentsValid(argSet)) { return declaringModule.Interpreter.UnknownType; @@ -85,17 +107,16 @@ public static IPythonType FromTypeVar(IArgumentSet argSet, IPythonModule declari var args = argSet.Arguments; var constraintArgs = argSet.ListArgument?.Values ?? Array.Empty(); - var name = (args[0].Value as IPythonConstant)?.GetString(); + var name = argSet.GetArgumentValue("name")?.GetString(); var constraints = constraintArgs.Select(a => { // Type constraints may be specified as type name strings. var typeString = (a as IPythonConstant)?.GetString(); return !string.IsNullOrEmpty(typeString) ? argSet.Eval.GetTypeFromString(typeString) : a.GetPythonType(); }).ToArray() ?? Array.Empty(); - - // TODO get bound here might have to parse string and look at it - + var bound = GetBound(argSet); var documentation = GetDocumentation(args, constraints); - return new GenericTypeParameter(name, declaringModule, constraints, documentation, location); + + return new GenericTypeParameter(name, declaringModule, constraints, bound, documentation, location); } private static string GetDocumentation(IReadOnlyList args, IReadOnlyList constraints) { diff --git a/src/Analysis/Ast/Impl/Specializations/Typing/TypingModule.cs b/src/Analysis/Ast/Impl/Specializations/Typing/TypingModule.cs index 0db5964bb..967409990 100644 --- a/src/Analysis/Ast/Impl/Specializations/Typing/TypingModule.cs +++ b/src/Analysis/Ast/Impl/Specializations/Typing/TypingModule.cs @@ -354,7 +354,7 @@ private IPythonType CreateAnyStr() { var docArgs = new[] { $"'{name}'" }.Concat(constraints.Select(c => c.Name)); var documentation = CodeFormatter.FormatSequence("TypeVar", '(', docArgs); - return new GenericTypeParameter(name, this, constraints, documentation, default); + return new GenericTypeParameter(name, this, constraints, default, documentation, default); } private IPythonType CreateGenericClassParameter(IReadOnlyList typeArgs) { diff --git a/src/Analysis/Ast/Impl/Types/PythonClassType.Generics.cs b/src/Analysis/Ast/Impl/Types/PythonClassType.Generics.cs index 623cf7fd7..46def499d 100644 --- a/src/Analysis/Ast/Impl/Types/PythonClassType.Generics.cs +++ b/src/Analysis/Ast/Impl/Types/PythonClassType.Generics.cs @@ -71,6 +71,7 @@ public IPythonType CreateSpecificType(IArgumentSet args) { // Storing generic parameters allows methods returning generic types // to know what type parameter returns what specific type StoreGenericParameters(classType, genericTypeParameters, genericTypeToSpecificType); + ResolveBounds(classType); // Set generic name if (!classType._genericParameters.IsNullOrEmpty()) { @@ -288,6 +289,18 @@ private void StoreGenericParameters(PythonClassType classType, IGenericTypeParam } } + /// + /// Resolves bounds for generic type parameters + /// + private void ResolveBounds(PythonClassType classType) { + foreach(var key in classType._genericParameters.Keys.ToList()) { + var parameterType = classType._genericParameters[key]; + if(parameterType is IGenericTypeParameter gtp && gtp.Bound != null) { + classType._genericParameters[key] = gtp.Bound; + } + } + } + /// /// Given generic type such as Generic[T1, T2, ...] attempts to extract specific types /// for its parameters from an argument value. Handles common cases such as dictionary, diff --git a/src/Analysis/Ast/Impl/Types/PythonFunctionOverload.cs b/src/Analysis/Ast/Impl/Types/PythonFunctionOverload.cs index bc81fa3de..2f87c0eb9 100644 --- a/src/Analysis/Ast/Impl/Types/PythonFunctionOverload.cs +++ b/src/Analysis/Ast/Impl/Types/PythonFunctionOverload.cs @@ -188,6 +188,11 @@ private IMember CreateSpecificReturnFromTypeVar(IPythonClassType selfClassType, return new PythonInstance(specificType); } + // Try getting the type from the type parameter bound + if(returnType.Bound != null) { + return new PythonInstance(returnType.Bound); + } + // Try returning the constraint // TODO: improve this, the heuristic is pretty basic and tailored to simple func(_T) -> _T var name = StaticReturnValue.GetPythonType()?.Name; diff --git a/src/Analysis/Ast/Impl/Typeshed/stdlib/3.4/pathlib.pyi b/src/Analysis/Ast/Impl/Typeshed/stdlib/3.4/pathlib.pyi index 307979b55..1ba655d4b 100644 --- a/src/Analysis/Ast/Impl/Typeshed/stdlib/3.4/pathlib.pyi +++ b/src/Analysis/Ast/Impl/Typeshed/stdlib/3.4/pathlib.pyi @@ -8,7 +8,7 @@ import sys _P = TypeVar('_P', bound='PurePath') if sys.version_info >= (3, 6): - _PurePathBase = os.PathLike[str] + _PurePathBase = os.PathLike else: _PurePathBase = object diff --git a/src/Analysis/Ast/Test/GenericsTests.cs b/src/Analysis/Ast/Test/GenericsTests.cs index 35a6146c5..9cd73fc2f 100644 --- a/src/Analysis/Ast/Test/GenericsTests.cs +++ b/src/Analysis/Ast/Test/GenericsTests.cs @@ -1122,5 +1122,40 @@ def log(self, message: str) -> None: var analysis = await GetAnalysisAsync(code); analysis.Should().HaveVariable("x").OfType(BuiltinTypeId.Int); } + + [TestMethod, Priority(0)] + public async Task GenericBound() { + const string code = @" +from typing import TypeVar, Generic +from logging import Logger, getLogger + +T = TypeVar('T', bound='A') + +class A: ... + +class Test(Generic[T]): + def get(self) -> T: ... + +x = Test().get() +"; + var analysis = await GetAnalysisAsync(code); + analysis.Should().HaveVariable("x").OfType("A"); + } + + [TestMethod, Priority(0)] + public async Task GenericPath() { + const string code = @" +import pathlib + +h = pathlib._PurePathBase +root = pathlib.Path('/some/directory') +subdir = root / 'subdir' +child = subdir / 'file.txt' +"; + var analysis = await GetAnalysisAsync(code, PythonVersions.Python37); + analysis.Should().HaveVariable("root").OfType("Path").Which; + analysis.Should().HaveVariable("subdir").OfType("PurePath"); + analysis.Should().HaveVariable("child").OfType("PurePath"); + } } } From 216300f6e80d79d4228f480c03f16849950f851d Mon Sep 17 00:00:00 2001 From: Cameron Trando Date: Tue, 20 Aug 2019 14:12:02 -0700 Subject: [PATCH 3/8] removing todo --- src/Analysis/Ast/Impl/Types/PythonClassType.Generics.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Analysis/Ast/Impl/Types/PythonClassType.Generics.cs b/src/Analysis/Ast/Impl/Types/PythonClassType.Generics.cs index 46def499d..f7c1f79d9 100644 --- a/src/Analysis/Ast/Impl/Types/PythonClassType.Generics.cs +++ b/src/Analysis/Ast/Impl/Types/PythonClassType.Generics.cs @@ -253,8 +253,6 @@ private IReadOnlyDictionary GetSpecificTypes } } - // TODO resolve bound constraints here by looping over the values in genericTypeToSpecificType - return genericTypeToSpecificType; } From 54a51954bfae69d62c9f35b1c046451a7610d21f Mon Sep 17 00:00:00 2001 From: Cameron Trando Date: Thu, 22 Aug 2019 14:12:22 -0700 Subject: [PATCH 4/8] Removing edits to stub --- src/Analysis/Ast/Impl/Typeshed/stdlib/3.4/pathlib.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Analysis/Ast/Impl/Typeshed/stdlib/3.4/pathlib.pyi b/src/Analysis/Ast/Impl/Typeshed/stdlib/3.4/pathlib.pyi index 1ba655d4b..307979b55 100644 --- a/src/Analysis/Ast/Impl/Typeshed/stdlib/3.4/pathlib.pyi +++ b/src/Analysis/Ast/Impl/Typeshed/stdlib/3.4/pathlib.pyi @@ -8,7 +8,7 @@ import sys _P = TypeVar('_P', bound='PurePath') if sys.version_info >= (3, 6): - _PurePathBase = os.PathLike + _PurePathBase = os.PathLike[str] else: _PurePathBase = object From b601978afdd74a669c3fee2a63cbe9478089f64d Mon Sep 17 00:00:00 2001 From: Cameron Trando Date: Thu, 22 Aug 2019 14:14:38 -0700 Subject: [PATCH 5/8] compile error --- src/Analysis/Ast/Test/GenericsTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Analysis/Ast/Test/GenericsTests.cs b/src/Analysis/Ast/Test/GenericsTests.cs index 9cd73fc2f..155f3a313 100644 --- a/src/Analysis/Ast/Test/GenericsTests.cs +++ b/src/Analysis/Ast/Test/GenericsTests.cs @@ -1153,7 +1153,7 @@ import pathlib child = subdir / 'file.txt' "; var analysis = await GetAnalysisAsync(code, PythonVersions.Python37); - analysis.Should().HaveVariable("root").OfType("Path").Which; + analysis.Should().HaveVariable("root").OfType("Path"); analysis.Should().HaveVariable("subdir").OfType("PurePath"); analysis.Should().HaveVariable("child").OfType("PurePath"); } From b16632d7ba4a25e86b7e2260567cb71b29bf80c1 Mon Sep 17 00:00:00 2001 From: Cameron Trando Date: Thu, 22 Aug 2019 23:39:05 -0700 Subject: [PATCH 6/8] Fixing tests, adding type parameter resolution when evaluating return values --- .../Evaluation/ExpressionEval.Annotations.cs | 9 +++++++++ .../Evaluation/ExpressionEval.Callables.cs | 3 ++- .../Impl/Extensions/PythonClassExtensions.cs | 13 +++++++++++++ .../Impl/Types/PythonClassType.Generics.cs | 15 +-------------- .../Ast/Impl/Types/PythonFunctionOverload.cs | 16 +++++++++++----- src/Analysis/Ast/Test/GenericsTests.cs | 19 +++++++++++++++++++ 6 files changed, 55 insertions(+), 20 deletions(-) diff --git a/src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.Annotations.cs b/src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.Annotations.cs index 4bd4b0815..d0b3d5747 100644 --- a/src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.Annotations.cs +++ b/src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.Annotations.cs @@ -13,6 +13,7 @@ // See the Apache Version 2.0 License for specific language governing // permissions and limitations under the License. +using Microsoft.Python.Analysis.Specializations.Typing; using Microsoft.Python.Analysis.Types; using Microsoft.Python.Parsing.Ast; @@ -26,6 +27,14 @@ public IPythonType GetTypeFromAnnotation(Expression expr, out bool isGeneric, Lo switch (expr) { case null: return null; + case NameExpression nameExpr: + // x: T + var name = GetValueFromExpression(nameExpr); + if(name is IGenericTypeParameter gtp) { + isGeneric = true; + return gtp; + } + break; case CallExpression callExpr: // x: NamedTuple(...) return GetValueFromCallable(callExpr)?.GetPythonType() ?? UnknownType; diff --git a/src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.Callables.cs b/src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.Callables.cs index de3a6903c..fee0f4711 100644 --- a/src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.Callables.cs +++ b/src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.Callables.cs @@ -325,6 +325,7 @@ public IReadOnlyList CreateFunctionParameters(IPythonClassType s if (self != null && function.HasClassFirstArgument()) { var p0 = fd.Parameters.FirstOrDefault(); if (p0 != null && !string.IsNullOrEmpty(p0.Name)) { + var annType = GetTypeFromAnnotation(p0.Annotation, out var isGeneric); // Actual parameter type will be determined when method is invoked. // The reason is that if method might be called on a derived class. // Declare self or cls in this scope. @@ -332,7 +333,7 @@ public IReadOnlyList CreateFunctionParameters(IPythonClassType s DeclareVariable(p0.Name, new PythonInstance(self), VariableSource.Declaration, p0.NameExpression); } // Set parameter info. - var pi = new ParameterInfo(Ast, p0, self, null, false); + var pi = new ParameterInfo(Ast, p0, isGeneric ? annType : self, null, false); parameters.Add(pi); skip++; } diff --git a/src/Analysis/Ast/Impl/Extensions/PythonClassExtensions.cs b/src/Analysis/Ast/Impl/Extensions/PythonClassExtensions.cs index 0e7408757..7ee3f4a85 100644 --- a/src/Analysis/Ast/Impl/Extensions/PythonClassExtensions.cs +++ b/src/Analysis/Ast/Impl/Extensions/PythonClassExtensions.cs @@ -14,6 +14,7 @@ // permissions and limitations under the License. using Microsoft.Python.Analysis.Analyzer; +using Microsoft.Python.Analysis.Specializations.Typing; using Microsoft.Python.Analysis.Types; using Microsoft.Python.Core; @@ -56,5 +57,17 @@ public static bool IsPrivateMember(this IPythonClassType cls, string memberName) var unmangledName = cls.UnmangleMemberName(memberName); return unmangledName.StartsWithOrdinal("__") && memberName.EqualsOrdinal($"_{cls.Name}{unmangledName}"); } + + /// + /// Gets specific type for the given generic type parameter, resolving bounds as well + /// + public static bool GetSpecificType(this IPythonClassType cls, IGenericTypeParameter param, out IPythonType specificType) { + cls.GenericParameters.TryGetValue(param, out specificType); + // If type has not been found, check if the type parameter has an upper bound and use that + if (specificType is IGenericTypeParameter gtp && gtp.Bound != null) { + specificType = gtp.Bound; + } + return specificType != null; + } } } diff --git a/src/Analysis/Ast/Impl/Types/PythonClassType.Generics.cs b/src/Analysis/Ast/Impl/Types/PythonClassType.Generics.cs index f7c1f79d9..63a233bb8 100644 --- a/src/Analysis/Ast/Impl/Types/PythonClassType.Generics.cs +++ b/src/Analysis/Ast/Impl/Types/PythonClassType.Generics.cs @@ -71,7 +71,6 @@ public IPythonType CreateSpecificType(IArgumentSet args) { // Storing generic parameters allows methods returning generic types // to know what type parameter returns what specific type StoreGenericParameters(classType, genericTypeParameters, genericTypeToSpecificType); - ResolveBounds(classType); // Set generic name if (!classType._genericParameters.IsNullOrEmpty()) { @@ -286,19 +285,7 @@ private void StoreGenericParameters(PythonClassType classType, IGenericTypeParam } } } - - /// - /// Resolves bounds for generic type parameters - /// - private void ResolveBounds(PythonClassType classType) { - foreach(var key in classType._genericParameters.Keys.ToList()) { - var parameterType = classType._genericParameters[key]; - if(parameterType is IGenericTypeParameter gtp && gtp.Bound != null) { - classType._genericParameters[key] = gtp.Bound; - } - } - } - + /// /// Given generic type such as Generic[T1, T2, ...] attempts to extract specific types /// for its parameters from an argument value. Handles common cases such as dictionary, diff --git a/src/Analysis/Ast/Impl/Types/PythonFunctionOverload.cs b/src/Analysis/Ast/Impl/Types/PythonFunctionOverload.cs index 2f87c0eb9..4b340ec1d 100644 --- a/src/Analysis/Ast/Impl/Types/PythonFunctionOverload.cs +++ b/src/Analysis/Ast/Impl/Types/PythonFunctionOverload.cs @@ -140,7 +140,7 @@ private IMember GetSpecificReturnType(IPythonClassType selfClassType, IArgumentS break; case IGenericTypeParameter gtd1 when selfClassType != null: - return CreateSpecificReturnFromTypeVar(selfClassType, gtd1); // -> _T + return CreateSpecificReturnFromTypeVar(selfClassType, args, gtd1); // -> _T case IGenericTypeParameter gtd2 when args != null: // -> T on standalone function. return args.Arguments.FirstOrDefault(a => gtd2.Equals(a.Type))?.Value as IMember; @@ -172,8 +172,8 @@ private IMember CreateSpecificReturnFromClassType(IPythonClassType selfClassType return null; } - private IMember CreateSpecificReturnFromTypeVar(IPythonClassType selfClassType, IGenericTypeParameter returnType) { - if (selfClassType.GenericParameters.TryGetValue(returnType, out var specificType)) { + private IMember CreateSpecificReturnFromTypeVar(IPythonClassType selfClassType, IArgumentSet args, IGenericTypeParameter returnType) { + if (selfClassType.GetSpecificType(returnType, out var specificType)) { return new PythonInstance(specificType); } @@ -184,12 +184,18 @@ private IMember CreateSpecificReturnFromTypeVar(IPythonClassType selfClassType, .FirstOrDefault(b => b.GetMember(ClassMember.Name) != null && b.GenericParameters.ContainsKey(returnType)); // Try and infer return value from base class - if (baseType != null && baseType.GenericParameters.TryGetValue(returnType, out specificType)) { + if (baseType != null && baseType.GetSpecificType(returnType, out specificType)) { return new PythonInstance(specificType); } + // Try getting type from passed in arguments + var typeFromArgs = args?.Arguments.FirstOrDefault(a => returnType.Equals(a.Type))?.Value as IMember; + if (typeFromArgs != null) { + return typeFromArgs; + } + // Try getting the type from the type parameter bound - if(returnType.Bound != null) { + if (returnType.Bound != null) { return new PythonInstance(returnType.Bound); } diff --git a/src/Analysis/Ast/Test/GenericsTests.cs b/src/Analysis/Ast/Test/GenericsTests.cs index 155f3a313..aec8dba6c 100644 --- a/src/Analysis/Ast/Test/GenericsTests.cs +++ b/src/Analysis/Ast/Test/GenericsTests.cs @@ -1048,6 +1048,25 @@ def set_width(self, w: float) -> 'Square': analysis.Should().HaveVariable("square").Which.Should().HaveType("Square"); } + [TestMethod, Priority(0)] + public async Task GenericSelf() { + const string code = @" +from typing import TypeVar + +T = TypeVar('T') + +class C: + def test(self: T) -> T: + pass + +class D(C): ... + +x = D().test() +"; + var analysis = await GetAnalysisAsync(code, PythonVersions.LatestAvailable3X); + analysis.Should().HaveVariable("x").Which.Should().HaveType("D"); + } + [TestMethod, Priority(0)] public async Task GenericClassToDifferentTypes() { const string code = @" From 7cc8be45bde11a6ee4e3dff7d03502e5c3670a9e Mon Sep 17 00:00:00 2001 From: Cameron Trando Date: Fri, 23 Aug 2019 16:37:23 -0700 Subject: [PATCH 7/8] PR feedback --- src/Analysis/Ast/Impl/Extensions/MemberExtensions.cs | 4 ++++ .../Typing/Definitions/IGenericTypeParameter.cs | 2 +- .../Typing/Types/GenericTypeParameter.cs | 10 +++++----- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/Analysis/Ast/Impl/Extensions/MemberExtensions.cs b/src/Analysis/Ast/Impl/Extensions/MemberExtensions.cs index a8ff6d9e1..3ffcdb84c 100644 --- a/src/Analysis/Ast/Impl/Extensions/MemberExtensions.cs +++ b/src/Analysis/Ast/Impl/Extensions/MemberExtensions.cs @@ -35,6 +35,10 @@ public static bool IsUnknown(this IMember m) { public static bool IsOfType(this IMember m, BuiltinTypeId typeId) => m?.GetPythonType().TypeId == typeId; + public static string GetString(this IMember m) { + return (m as IPythonConstant)?.GetString(); + } + public static IPythonType GetPythonType(this IMember m) { switch (m) { case IPythonType pt: diff --git a/src/Analysis/Ast/Impl/Specializations/Typing/Definitions/IGenericTypeParameter.cs b/src/Analysis/Ast/Impl/Specializations/Typing/Definitions/IGenericTypeParameter.cs index a5bc9b814..1376a6ed1 100644 --- a/src/Analysis/Ast/Impl/Specializations/Typing/Definitions/IGenericTypeParameter.cs +++ b/src/Analysis/Ast/Impl/Specializations/Typing/Definitions/IGenericTypeParameter.cs @@ -29,7 +29,7 @@ public interface IGenericTypeParameter : IPythonType, IEquatable Constraints { get; } /// - /// Bounded type, i.e upper bound this type parameter can represent + /// Bound type, i.e upper bound this type parameter can represent /// IPythonType Bound { get; } } diff --git a/src/Analysis/Ast/Impl/Specializations/Typing/Types/GenericTypeParameter.cs b/src/Analysis/Ast/Impl/Specializations/Typing/Types/GenericTypeParameter.cs index a55ba1abb..eb7a59412 100644 --- a/src/Analysis/Ast/Impl/Specializations/Typing/Types/GenericTypeParameter.cs +++ b/src/Analysis/Ast/Impl/Specializations/Typing/Types/GenericTypeParameter.cs @@ -86,16 +86,16 @@ private static bool TypeVarArgumentsValid(IArgumentSet argSet) { /// /// Given arguments to TypeVar, finds the bound type /// - private static IPythonType GetBound(IArgumentSet argSet) { + private static IPythonType GetBoundType(IArgumentSet argSet) { var eval = argSet.Eval; - var rawBound = argSet.GetArgumentValue("bound"); + var rawBound = argSet.GetArgumentValue("bound"); switch (rawBound) { case IPythonType t: return t; case IPythonConstant c when c.GetString() != null: return eval.GetTypeFromString(c.GetString()); default: - return null; + return rawBound.GetPythonType(); } } @@ -110,10 +110,10 @@ public static IPythonType FromTypeVar(IArgumentSet argSet, IPythonModule declari var name = argSet.GetArgumentValue("name")?.GetString(); var constraints = constraintArgs.Select(a => { // Type constraints may be specified as type name strings. - var typeString = (a as IPythonConstant)?.GetString(); + var typeString = a.GetString(); return !string.IsNullOrEmpty(typeString) ? argSet.Eval.GetTypeFromString(typeString) : a.GetPythonType(); }).ToArray() ?? Array.Empty(); - var bound = GetBound(argSet); + var bound = GetBoundType(argSet); var documentation = GetDocumentation(args, constraints); return new GenericTypeParameter(name, declaringModule, constraints, bound, documentation, location); From 48da027db41664f6dfe13239f23109c56c692218 Mon Sep 17 00:00:00 2001 From: Cameron Trando Date: Mon, 26 Aug 2019 10:40:43 -0700 Subject: [PATCH 8/8] update comment --- .../Ast/Impl/Analyzer/Evaluation/ExpressionEval.Callables.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.Callables.cs b/src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.Callables.cs index fee0f4711..b10ab0e68 100644 --- a/src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.Callables.cs +++ b/src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.Callables.cs @@ -332,7 +332,8 @@ public IReadOnlyList CreateFunctionParameters(IPythonClassType s if (declareVariables) { DeclareVariable(p0.Name, new PythonInstance(self), VariableSource.Declaration, p0.NameExpression); } - // Set parameter info. + // Set parameter info, declare type as annotation type for generic self + // e.g def test(self: T) var pi = new ParameterInfo(Ast, p0, isGeneric ? annType : self, null, false); parameters.Add(pi); skip++;