diff --git a/src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.Callables.cs b/src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.Callables.cs index 00baa9b4d..098595dde 100644 --- a/src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.Callables.cs +++ b/src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.Callables.cs @@ -44,6 +44,7 @@ public IMember GetValueFromCallable(CallExpression expr) { // so we can invoke Call over the instance. Second, an type info // so we can create an instance of the type (as in C() where C is class). IMember value = null; + var args = ArgumentSet.Empty(expr, this); switch (target) { case IPythonBoundType bt: // Bound property, method or an iterator. value = GetValueFromBound(bt, expr); @@ -52,7 +53,7 @@ public IMember GetValueFromCallable(CallExpression expr) { value = GetValueFromInstanceCall(pi, expr); break; case IPythonFunctionType ft: // Standalone function or a class method call. - var instance = ft.DeclaringType != null ? new PythonInstance(ft.DeclaringType) : null; + var instance = ft.DeclaringType?.CreateInstance(args); value = GetValueFromFunctionType(ft, instance, expr); break; case IPythonClassType cls: @@ -61,7 +62,7 @@ public IMember GetValueFromCallable(CallExpression expr) { case IPythonType t: // Target is type (info), the call creates instance. // For example, 'x = C; y = x()' or 'x = C()' where C is class - value = new PythonInstance(t); + value = t.CreateInstance(args); break; } @@ -93,14 +94,14 @@ public IMember GetValueFromClassCtor(IPythonClassType cls, CallExpression expr) var init = cls.GetMember(@"__init__"); if (init != null) { using (OpenScope(cls.DeclaringModule, cls.ClassDefinition, out _)) { - var a = new ArgumentSet(init, 0, new PythonInstance(cls), expr, this); + var a = new ArgumentSet(init, 0, cls, expr, this); if (a.Errors.Count > 0) { // AddDiagnostics(Module.Uri, a.Errors); } args = a.Evaluate(); } } - return cls.CreateInstance(cls.Name, args); + return cls.CreateInstance(args); } private IMember GetValueFromBound(IPythonBoundType t, CallExpression expr) { @@ -146,12 +147,14 @@ public IMember GetValueFromFunctionType(IPythonFunctionType fn, IPythonInstance // Pick the best overload. FunctionDefinition fd; ArgumentSet args; + var instanceType = instance?.GetPythonType(); + if (fn.Overloads.Count == 1) { fd = fn.Overloads[0].FunctionDefinition; - args = new ArgumentSet(fn, 0, instance, expr, this); + args = new ArgumentSet(fn, 0, instanceType, expr, this); args = args.Evaluate(); } else { - args = FindOverload(fn, instance, expr); + args = FindOverload(fn, instanceType, expr); if (args == null) { return UnknownType; } @@ -167,11 +170,10 @@ public IMember GetValueFromFunctionType(IPythonFunctionType fn, IPythonInstance } } - // If instance is not the same as the declaring type, then call most probably comes + // If instance type is not the same as the declaring type, then call most probably comes // from the derived class which means that the original 'self' and 'cls' variables // are no longer valid and function has to be re-evaluated with new arguments. // Note that there is nothing to re-evaluate in stubs. - var instanceType = instance?.GetPythonType(); if (instanceType == null || fn.DeclaringType == null || fn.IsSpecialized || instanceType.IsSpecialized || fn.DeclaringType.IsSpecialized || instanceType.Equals(fn.DeclaringType) || @@ -179,9 +181,9 @@ public IMember GetValueFromFunctionType(IPythonFunctionType fn, IPythonInstance LoadFunctionDependencyModules(fn); - var t = instance?.Call(fn.Name, args) ?? fn.Call(null, fn.Name, args); - if (!t.IsUnknown()) { - return t; + var m = instance?.Call(fn.Name, args) ?? fn.Call(null, fn.Name, args); + if (!m.IsUnknown()) { + return m; } } @@ -244,14 +246,14 @@ private IMember TryEvaluateWithArguments(IPythonFunctionType fn, IArgumentSet ar return result; } - private ArgumentSet FindOverload(IPythonFunctionType fn, IPythonInstance instance, CallExpression expr) { + private ArgumentSet FindOverload(IPythonFunctionType fn, IPythonType instanceType, CallExpression expr) { if (fn.Overloads.Count == 1) { return null; } var sets = new List(); for (var i = 0; i < fn.Overloads.Count; i++) { - var a = new ArgumentSet(fn, i, instance, expr, this); + var a = new ArgumentSet(fn, i, instanceType, expr, this); var args = a.Evaluate(); sets.Add(args); } @@ -329,7 +331,8 @@ public IReadOnlyList CreateFunctionParameters(IPythonClassType s // The reason is that if method might be called on a derived class. // Declare self or cls in this scope. if (declareVariables) { - DeclareVariable(p0.Name, new PythonInstance(self), VariableSource.Declaration, p0.NameExpression); + DeclareVariable(p0.Name, self.CreateInstance(ArgumentSet.Empty(p0.NameExpression, this)), + VariableSource.Declaration, p0.NameExpression); } // Set parameter info, declare type as annotation type for generic self // e.g def test(self: T) @@ -377,7 +380,8 @@ private void DeclareParameter(Parameter p, ParameterInfo pi) { } else { paramType = pi?.DefaultValue?.GetPythonType() ?? UnknownType; } - DeclareVariable(p.Name, new PythonInstance(paramType), VariableSource.Declaration, p.NameExpression); + DeclareVariable(p.Name, paramType.CreateInstance(ArgumentSet.Empty(p.NameExpression, this)), + VariableSource.Declaration, p.NameExpression); } } } diff --git a/src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.Collections.cs b/src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.Collections.cs index e641849c9..8a4b13f36 100644 --- a/src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.Collections.cs +++ b/src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.Collections.cs @@ -45,11 +45,11 @@ public IMember GetValueFromIndex(IndexExpression expr) { var type = target.GetPythonType(); if (type != null) { if (!(target is IPythonInstance instance)) { - instance = new PythonInstance(type); + instance = type.CreateInstance(ArgumentSet.Empty(expr, this)); } var index = GetValueFromExpression(expr.Index); if (index != null) { - return type.Index(instance, new ArgumentSet(new []{index}, expr, this)); + return type.Index(instance, new ArgumentSet(new[] { index }, expr, this)); } } diff --git a/src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.Constants.cs b/src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.Constants.cs index 52185a35c..37a2c7403 100644 --- a/src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.Constants.cs +++ b/src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.Constants.cs @@ -38,7 +38,7 @@ public IPythonInstance GetConstantFromLiteral(Expression expr) { } var t = SuppressBuiltinLookup ? UnknownType : (GetTypeFromLiteral(expr) ?? UnknownType); - return new PythonInstance(t); + return t.CreateInstance(ArgumentSet.Empty(expr, this)); } public IPythonType GetTypeFromLiteral(Expression expr) { diff --git a/src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.Generics.cs b/src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.Generics.cs index 8ff168e52..7a70a01cb 100644 --- a/src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.Generics.cs +++ b/src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.Generics.cs @@ -172,12 +172,12 @@ private IMember CreateClassInstance(PythonClassType cls, IReadOnlyList var initOverload = initFunc?.DeclaringType == cls ? initFunc.Overloads.FirstOrDefault() : null; var argSet = initOverload != null - ? new ArgumentSet(initFunc, 0, null, callExpr, this) + ? new ArgumentSet(initFunc, 0, cls, callExpr, this) : new ArgumentSet(constructorArguments, callExpr, this); argSet.Evaluate(); var specificType = cls.CreateSpecificType(argSet); - return new PythonInstance(specificType); + return specificType.CreateInstance(argSet); } private ScopeStatement GetScope(IMember m) { diff --git a/src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.cs b/src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.cs index dceef6fd7..1136a9091 100644 --- a/src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.cs +++ b/src/Analysis/Ast/Impl/Analyzer/Evaluation/ExpressionEval.cs @@ -258,7 +258,7 @@ private IMember GetValueFromMember(MemberExpression expr) { f.AddReference(GetLocationOfName(expr)); return f.ToUnbound(); } - instance = new PythonInstance(type); + instance = type.CreateInstance(ArgumentSet.Empty(expr, this)); } instance = instance ?? m as IPythonInstance; diff --git a/src/Analysis/Ast/Impl/Analyzer/Handlers/AssignmentHandler.cs b/src/Analysis/Ast/Impl/Analyzer/Handlers/AssignmentHandler.cs index a1582af45..dffcf6951 100644 --- a/src/Analysis/Ast/Impl/Analyzer/Handlers/AssignmentHandler.cs +++ b/src/Analysis/Ast/Impl/Analyzer/Handlers/AssignmentHandler.cs @@ -13,12 +13,12 @@ // See the Apache Version 2.0 License for specific language governing // permissions and limitations under the License. -using System.Diagnostics; -using System.Linq; using Microsoft.Python.Analysis.Types; using Microsoft.Python.Analysis.Values; using Microsoft.Python.Core; using Microsoft.Python.Parsing.Ast; +using System.Diagnostics; +using System.Linq; namespace Microsoft.Python.Analysis.Analyzer.Handlers { internal sealed class AssignmentHandler : StatementHandler { @@ -30,10 +30,12 @@ public void HandleAssignment(AssignmentStatement node) { } var value = Eval.GetValueFromExpression(node.Right) ?? Eval.UnknownType; + // Filter out parenthesis expression in assignment because it makes no difference. + var lhs = node.Left.Select(s => s.RemoveParenthesis()); // Check PEP hint first var valueType = Eval.GetTypeFromPepHint(node.Right); if (valueType != null) { - HandleTypedVariable(valueType, value, node.Left.FirstOrDefault()); + HandleTypedVariable(valueType, value, lhs.FirstOrDefault()); return; } @@ -44,44 +46,49 @@ public void HandleAssignment(AssignmentStatement node) { value = Eval.UnknownType; } - if (node.Left.FirstOrDefault() is SequenceExpression seq) { - // Tuple = Tuple. Transfer values. - var seqHandler = new SequenceExpressionHandler(Walker); - seqHandler.HandleAssignment(seq.Items, node.Right, value); - return; + foreach (var expr in lhs) { + switch (expr) { + case SequenceExpression seq: + // Tuple = Tuple. Transfer values. + var seqHandler = new SequenceExpressionHandler(Walker); + seqHandler.HandleAssignment(seq, value); + break; + case ExpressionWithAnnotation annExpr: + HandleAnnotatedExpression(annExpr, value); + break; + case NameExpression nameExpr: + HandleNameExpression(nameExpr, value); + break; + case MemberExpression memberExpr: + TryHandleClassVariable(memberExpr, value); + break; + } } + } - // Process annotations, if any. - foreach (var expr in node.Left.OfType()) { - // x: List[str] = [...] - HandleAnnotatedExpression(expr, value); - } + private bool IsValidAssignment(string name, Location loc) => !Eval.GetInScope(name).IsDeclaredAfter(loc); - foreach (var ne in node.Left.OfType()) { - IScope scope; - if (Eval.CurrentScope.NonLocals[ne.Name] != null) { - Eval.LookupNameInScopes(ne.Name, out scope, LookupOptions.Nonlocal); - scope?.Variables[ne.Name].Assign(value, Eval.GetLocationOfName(ne)); - continue; - } - if (Eval.CurrentScope.Globals[ne.Name] != null) { - Eval.LookupNameInScopes(ne.Name, out scope, LookupOptions.Global); - scope?.Variables[ne.Name].Assign(value, Eval.GetLocationOfName(ne)); - continue; - } + private void HandleNameExpression(NameExpression ne, IMember value) { + IScope scope; + if (Eval.CurrentScope.NonLocals[ne.Name] != null) { + Eval.LookupNameInScopes(ne.Name, out scope, LookupOptions.Nonlocal); + scope?.Variables[ne.Name].Assign(value, Eval.GetLocationOfName(ne)); + return; + } - var source = value.IsGeneric() ? VariableSource.Generic : VariableSource.Declaration; - var location = Eval.GetLocationOfName(ne); - if (IsValidAssignment(ne.Name, location)) { - Eval.DeclareVariable(ne.Name, value ?? Module.Interpreter.UnknownType, source, location); - } + if (Eval.CurrentScope.Globals[ne.Name] != null) { + Eval.LookupNameInScopes(ne.Name, out scope, LookupOptions.Global); + scope?.Variables[ne.Name].Assign(value, Eval.GetLocationOfName(ne)); + return; } - TryHandleClassVariable(node, value); + var source = value.IsGeneric() ? VariableSource.Generic : VariableSource.Declaration; + var location = Eval.GetLocationOfName(ne); + if (IsValidAssignment(ne.Name, location)) { + Eval.DeclareVariable(ne.Name, value ?? Module.Interpreter.UnknownType, source, location); + } } - private bool IsValidAssignment(string name, Location loc) => !Eval.GetInScope(name).IsDeclaredAfter(loc); - public void HandleAnnotatedExpression(ExpressionWithAnnotation expr, IMember value) { if (expr?.Annotation == null) { return; @@ -95,8 +102,7 @@ public void HandleAnnotatedExpression(ExpressionWithAnnotation expr, IMember val HandleTypedVariable(variableType, value, expr.Expression); } - private void TryHandleClassVariable(AssignmentStatement node, IMember value) { - var mex = node.Left.OfType().FirstOrDefault(); + private void TryHandleClassVariable(MemberExpression mex, IMember value) { if (!string.IsNullOrEmpty(mex?.Name) && mex.Target is NameExpression nex && nex.Name.EqualsOrdinal("self")) { var m = Eval.LookupNameInScopes(nex.Name, out _, LookupOptions.Local); var cls = m.GetPythonType(); @@ -121,7 +127,8 @@ private void HandleTypedVariable(IPythonType variableType, IMember value, Expres instance = value; } } - instance = instance ?? variableType?.CreateInstance(variableType.Name, ArgumentSet.Empty(expr, Eval)) ?? Eval.UnknownType; + var args = ArgumentSet.Empty(expr, Eval); + instance = instance ?? variableType?.CreateInstance(args) ?? Eval.UnknownType.CreateInstance(ArgumentSet.WithoutContext); if (expr is NameExpression ne) { Eval.DeclareVariable(ne.Name, instance, VariableSource.Declaration, ne); diff --git a/src/Analysis/Ast/Impl/Analyzer/Handlers/LoopHandler.cs b/src/Analysis/Ast/Impl/Analyzer/Handlers/LoopHandler.cs index 20a387590..f568e31ac 100644 --- a/src/Analysis/Ast/Impl/Analyzer/Handlers/LoopHandler.cs +++ b/src/Analysis/Ast/Impl/Analyzer/Handlers/LoopHandler.cs @@ -35,7 +35,7 @@ public bool HandleFor(ForStatement node) { // x = [('abc', 42, True), ('abc', 23, False)] // for some_str, (some_int, some_bool) in x: var h = new SequenceExpressionHandler(Walker); - h.HandleAssignment(seq.Items, node.List, value); + h.HandleAssignment(seq, value); break; } diff --git a/src/Analysis/Ast/Impl/Analyzer/Handlers/SequenceExpressionHandler.cs b/src/Analysis/Ast/Impl/Analyzer/Handlers/SequenceExpressionHandler.cs index 9752a8abf..ac066f9da 100644 --- a/src/Analysis/Ast/Impl/Analyzer/Handlers/SequenceExpressionHandler.cs +++ b/src/Analysis/Ast/Impl/Analyzer/Handlers/SequenceExpressionHandler.cs @@ -25,36 +25,17 @@ namespace Microsoft.Python.Analysis.Analyzer.Handlers { internal sealed class SequenceExpressionHandler : StatementHandler { public SequenceExpressionHandler(AnalysisWalker walker) : base(walker) { } - public void HandleAssignment(IEnumerable lhs, Expression rhs, IMember value) { - if (rhs is TupleExpression tex) { - Assign(lhs, tex, Eval); - } else { - Assign(lhs, value, Eval); - } - } - - internal static void Assign(IEnumerable lhs, TupleExpression rhs, ExpressionEval eval) { - var names = NamesFromSequenceExpression(lhs).ToArray(); - var values = ValuesFromSequenceExpression(rhs.Items, eval).ToArray(); - for (var i = 0; i < names.Length; i++) { - IMember value = null; - if (values.Length > 0) { - value = i < values.Length ? values[i] : values[values.Length - 1]; - } - - if (!string.IsNullOrEmpty(names[i]?.Name)) { - eval.DeclareVariable(names[i].Name, value ?? eval.UnknownType, VariableSource.Declaration, names[i]); - } - } + public void HandleAssignment(SequenceExpression seq, IMember value) { + Assign(seq, value, Eval); } - internal static void Assign(IEnumerable lhs, IMember value, ExpressionEval eval) { + internal static void Assign(SequenceExpression seq, IMember value, ExpressionEval eval) { var typeEnum = new ValueEnumerator(value, eval.UnknownType); - Assign(lhs, typeEnum, eval); + Assign(seq, typeEnum, eval); } - private static void Assign(IEnumerable items, ValueEnumerator valueEnum, ExpressionEval eval) { - foreach (var item in items) { + private static void Assign(SequenceExpression seq, ValueEnumerator valueEnum, ExpressionEval eval) { + foreach (var item in seq.Items) { switch (item) { case StarredExpression stx when stx.Expression is NameExpression nex && !string.IsNullOrEmpty(nex.Name): eval.DeclareVariable(nex.Name, valueEnum.Next, VariableSource.Declaration, nex); @@ -65,20 +46,29 @@ private static void Assign(IEnumerable items, ValueEnumerator valueE case NameExpression nex when !string.IsNullOrEmpty(nex.Name): eval.DeclareVariable(nex.Name, valueEnum.Next, VariableSource.Declaration, nex); break; + // Nested sequence expression in sequence, Tuple[Tuple[int, str], int], List[Tuple[int], str] + // TODO: Because of bug with how collection types are constructed, they don't make nested collection types + // into instances, meaning we have to create it here + case SequenceExpression se when valueEnum.Peek is IPythonCollection || valueEnum.Peek is IPythonCollectionType: + var collection = valueEnum.Next; + var pc = collection as IPythonCollection; + var pct = collection as IPythonCollectionType; + Assign(se, pc ?? pct.CreateInstance(ArgumentSet.Empty(se, eval)), eval); + break; case SequenceExpression se: - Assign(se.Items, valueEnum, eval); + Assign(se, valueEnum, eval); break; } } } - private static IEnumerable NamesFromSequenceExpression(IEnumerable items) { + private static IEnumerable NamesFromSequenceExpression(SequenceExpression rootSeq) { var names = new List(); - foreach (var item in items) { + foreach (var item in rootSeq.Items) { var expr = item.RemoveParenthesis(); switch (expr) { case SequenceExpression seq: - names.AddRange(NamesFromSequenceExpression(seq.Items)); + names.AddRange(NamesFromSequenceExpression(seq)); break; case NameExpression nex: names.Add(nex); @@ -87,21 +77,5 @@ private static IEnumerable NamesFromSequenceExpression(IEnumerab } return names; } - - private static IEnumerable ValuesFromSequenceExpression(IEnumerable items, ExpressionEval eval) { - var members = new List(); - foreach (var item in items) { - var value = eval.GetValueFromExpression(item); - switch (value) { - case IPythonCollection coll: - members.AddRange(coll.Contents); - break; - default: - members.Add(value); - break; - } - } - return members; - } } } diff --git a/src/Analysis/Ast/Impl/Analyzer/Handlers/WithHandler.cs b/src/Analysis/Ast/Impl/Analyzer/Handlers/WithHandler.cs index 712080f4c..aff342d44 100644 --- a/src/Analysis/Ast/Impl/Analyzer/Handlers/WithHandler.cs +++ b/src/Analysis/Ast/Impl/Analyzer/Handlers/WithHandler.cs @@ -39,13 +39,19 @@ public void HandleWith(WithStatement node) { } switch (item.Variable) { + // Handle with Test() as a case NameExpression nameExpr when !string.IsNullOrEmpty(nameExpr.Name): Eval.DeclareVariable(nameExpr.Name, context, VariableSource.Declaration, item); break; - case ParenthesisExpression parExpr: + // Handle with Test() as (a) + case ParenthesisExpression parExpr when parExpr.Expression is NameExpression nameExpr && !string.IsNullOrEmpty(nameExpr.Name): + Eval.DeclareVariable(nameExpr.Name, context, VariableSource.Declaration, item); + break; + // Handle with Test() as (a, b) + // Single element list [a] is a sequence expression so also handled here case SequenceExpression seqExpr: var sequenceHandler = new SequenceExpressionHandler(Walker); - SequenceExpressionHandler.Assign(new[] { item.Variable }, context, Eval); + sequenceHandler.HandleAssignment(seqExpr, context); break; } } diff --git a/src/Analysis/Ast/Impl/Analyzer/Symbols/FunctionEvaluator.cs b/src/Analysis/Ast/Impl/Analyzer/Symbols/FunctionEvaluator.cs index f50d4f705..924b2bc73 100644 --- a/src/Analysis/Ast/Impl/Analyzer/Symbols/FunctionEvaluator.cs +++ b/src/Analysis/Ast/Impl/Analyzer/Symbols/FunctionEvaluator.cs @@ -86,10 +86,10 @@ private IPythonType TryDetermineReturnValue() { // Annotations are typically types while actually functions return // instances unless specifically annotated to a type such as Type[T]. // TODO: try constructing argument set from types. Consider Tuple[_T1, _T2] where _T1 = TypeVar('_T1', str, bytes) - var t = annotationType.CreateInstance(annotationType.Name, ArgumentSet.WithoutContext); + var t = annotationType.CreateInstance(ArgumentSet.Empty(FunctionDefinition.ReturnAnnotation, Eval)); // If instance could not be created, such as when return type is List[T] and // type of T is not yet known, just use the type. - var instance = t.IsUnknown() ? annotationType : t; + var instance = t.IsUnknown() ? (IMember) annotationType : t; _overload.SetReturnValue(instance, true); _overload.SetReturnValue(instance, true); } else { // Check if function is a generator diff --git a/src/Analysis/Ast/Impl/Extensions/ArgumentSetExtensions.cs b/src/Analysis/Ast/Impl/Extensions/ArgumentSetExtensions.cs index 6699f54d8..12acaba44 100644 --- a/src/Analysis/Ast/Impl/Extensions/ArgumentSetExtensions.cs +++ b/src/Analysis/Ast/Impl/Extensions/ArgumentSetExtensions.cs @@ -15,7 +15,6 @@ using System.Collections.Generic; using System.Linq; -using System.Security.Cryptography.X509Certificates; using Microsoft.Python.Analysis.Analyzer.Evaluation; using Microsoft.Python.Analysis.Types; using Microsoft.Python.Analysis.Types.Collections; diff --git a/src/Analysis/Ast/Impl/Modules/PythonModule.cs b/src/Analysis/Ast/Impl/Modules/PythonModule.cs index b55efdedf..aae9465c2 100644 --- a/src/Analysis/Ast/Impl/Modules/PythonModule.cs +++ b/src/Analysis/Ast/Impl/Modules/PythonModule.cs @@ -130,7 +130,7 @@ internal PythonModule(ModuleCreationOptions creationOptions, IServiceContainer s public bool IsAbstract => false; public virtual bool IsSpecialized => false; - public IMember CreateInstance(string typeName, IArgumentSet args) => this; + public IPythonInstance CreateInstance(IArgumentSet args) => new PythonInstance(this); public override PythonMemberType MemberType => PythonMemberType.Module; public IMember Call(IPythonInstance instance, string memberName, IArgumentSet args) => GetMember(memberName); public IMember Index(IPythonInstance instance, IArgumentSet args) => Interpreter.UnknownType; diff --git a/src/Analysis/Ast/Impl/Modules/PythonVariableModule.cs b/src/Analysis/Ast/Impl/Modules/PythonVariableModule.cs index 6d4367218..70e4c9fab 100644 --- a/src/Analysis/Ast/Impl/Modules/PythonVariableModule.cs +++ b/src/Analysis/Ast/Impl/Modules/PythonVariableModule.cs @@ -72,7 +72,7 @@ public PythonVariableModule(IPythonModule module): base(module) { public IMember Call(IPythonInstance instance, string memberName, IArgumentSet args) => GetMember(memberName); public IMember Index(IPythonInstance instance, IArgumentSet args) => Interpreter.UnknownType; - public IMember CreateInstance(string typeName = null, IArgumentSet args = null) => this; + public IPythonInstance CreateInstance(IArgumentSet args = null) => new PythonInstance(this); public bool Equals(IPythonModule other) => other is PythonVariableModule module && Name.EqualsOrdinal(module.Name); diff --git a/src/Analysis/Ast/Impl/Specializations/BuiltinsSpecializations.cs b/src/Analysis/Ast/Impl/Specializations/BuiltinsSpecializations.cs index ea7694ec6..5a1b2283c 100644 --- a/src/Analysis/Ast/Impl/Specializations/BuiltinsSpecializations.cs +++ b/src/Analysis/Ast/Impl/Specializations/BuiltinsSpecializations.cs @@ -116,7 +116,7 @@ public static IMember Open(IPythonModule declaringModule, IPythonFunctionOverloa } var returnType = io?.GetMember(returnTypeName)?.GetPythonType(); - return returnType != null ? new PythonInstance(returnType) : null; + return returnType != null ? returnType.CreateInstance(argSet) : null; } public static IMember GetAttr(IPythonModule module, IPythonFunctionOverload overload, IArgumentSet argSet, IndexSpan indexSpan) { diff --git a/src/Analysis/Ast/Impl/Specializations/Typing/Types/AnyType.cs b/src/Analysis/Ast/Impl/Specializations/Typing/Types/AnyType.cs index 73eef02f4..f63241b4c 100644 --- a/src/Analysis/Ast/Impl/Specializations/Typing/Types/AnyType.cs +++ b/src/Analysis/Ast/Impl/Specializations/Typing/Types/AnyType.cs @@ -33,7 +33,7 @@ public AnyType(IPythonModule declaringModule) : base(declaringModule) { } public IMember Call(IPythonInstance instance, string memberName, IArgumentSet args) => DeclaringModule.Interpreter.UnknownType; - public IMember CreateInstance(string typeName, IArgumentSet args) => new PythonInstance(this); + public IPythonInstance CreateInstance(IArgumentSet args) => new PythonInstance(this); public IMember GetMember(string name) => null; public IEnumerable GetMemberNames() => Array.Empty(); diff --git a/src/Analysis/Ast/Impl/Specializations/Typing/Types/GenericType.cs b/src/Analysis/Ast/Impl/Specializations/Typing/Types/GenericType.cs index 49c978c48..6bb6b2544 100644 --- a/src/Analysis/Ast/Impl/Specializations/Typing/Types/GenericType.cs +++ b/src/Analysis/Ast/Impl/Specializations/Typing/Types/GenericType.cs @@ -122,15 +122,15 @@ private SpecializedGenericType(string name, string qualifiedName, IPythonModule public bool IsAbstract => true; public bool IsSpecialized => true; - public IMember CreateInstance(string typeName, IArgumentSet args) { + public IPythonInstance CreateInstance(IArgumentSet args) { var types = GetTypesFromValues(args.Arguments); if (types.Count != args.Arguments.Count) { throw new ArgumentException(@"Generic type instance construction arguments must be all of IPythonType", nameof(args)); } var specific = CreateSpecificType(args); return specific == null - ? DeclaringModule.Interpreter.UnknownType - : specific.CreateInstance(typeName); + ? DeclaringModule.Interpreter.UnknownType.CreateInstance(args) + : specific.CreateInstance(args); } public IMember Call(IPythonInstance instance, string memberName, IArgumentSet args) => DeclaringModule.Interpreter.UnknownType; diff --git a/src/Analysis/Ast/Impl/Specializations/Typing/Types/NamedTupleType.cs b/src/Analysis/Ast/Impl/Specializations/Typing/Types/NamedTupleType.cs index 2ecd670e5..7e71f52e0 100644 --- a/src/Analysis/Ast/Impl/Specializations/Typing/Types/NamedTupleType.cs +++ b/src/Analysis/Ast/Impl/Specializations/Typing/Types/NamedTupleType.cs @@ -66,7 +66,7 @@ public NamedTupleType(string tupleName, IReadOnlyList itemNames, IReadOn public override void RemoveReferences(IPythonModule module) => _locatedMember.RemoveReferences(module); #endregion - public override IMember CreateInstance(string typeName, IArgumentSet args) => new TypingTuple(this); + public override IPythonInstance CreateInstance(IArgumentSet args) => new TypingTuple(this); // NamedTuple does not create instances, it defines a type. public override IMember Call(IPythonInstance instance, string memberName, IArgumentSet args) => this; @@ -76,7 +76,7 @@ public NamedTupleType(string tupleName, IReadOnlyList itemNames, IReadOn public override IMember GetMember(string name) { var index = ItemNames.IndexOf(n => n == name); if (index >= 0 && index < ItemTypes.Count) { - return new PythonInstance(ItemTypes[index]); + return ItemTypes[index].CreateInstance(ArgumentSet.WithoutContext); } return base.GetMember(name); } diff --git a/src/Analysis/Ast/Impl/Specializations/Typing/Types/OptionalType.cs b/src/Analysis/Ast/Impl/Specializations/Typing/Types/OptionalType.cs index 0c89c3a7a..62cf914ad 100644 --- a/src/Analysis/Ast/Impl/Specializations/Typing/Types/OptionalType.cs +++ b/src/Analysis/Ast/Impl/Specializations/Typing/Types/OptionalType.cs @@ -17,6 +17,7 @@ using System.Collections.Generic; using System.Linq; using Microsoft.Python.Analysis.Types; +using Microsoft.Python.Analysis.Values; namespace Microsoft.Python.Analysis.Specializations.Typing.Types { internal sealed class OptionalType : PythonTypeWrapper, IPythonUnionType { @@ -38,7 +39,7 @@ public IEnumerator GetEnumerator() public IPythonUnionType Add(IPythonType t) => this; public IPythonUnionType Add(IPythonUnionType types) => this; - public override IMember CreateInstance(string typeName, IArgumentSet args) - => InnerType.CreateInstance(typeName, args); + public override IPythonInstance CreateInstance(IArgumentSet args) + => InnerType.CreateInstance(args); } } diff --git a/src/Analysis/Ast/Impl/Specializations/Typing/Types/TypeAlias.cs b/src/Analysis/Ast/Impl/Specializations/Typing/Types/TypeAlias.cs index 67d93be1c..9c226e3af 100644 --- a/src/Analysis/Ast/Impl/Specializations/Typing/Types/TypeAlias.cs +++ b/src/Analysis/Ast/Impl/Specializations/Typing/Types/TypeAlias.cs @@ -14,6 +14,7 @@ // permissions and limitations under the License. using Microsoft.Python.Analysis.Types; +using Microsoft.Python.Analysis.Values; namespace Microsoft.Python.Analysis.Specializations.Typing.Types { internal sealed class TypeAlias: PythonTypeWrapper { @@ -24,5 +25,6 @@ public TypeAlias(string name, IPythonType type) : base(type) { public override string QualifiedName => $"typing:{Name}"; public override bool IsSpecialized => true; + public override IPythonInstance CreateInstance(IArgumentSet args) => new PythonInstance(this); } } diff --git a/src/Analysis/Ast/Impl/Specializations/Typing/Types/TypingDictionaryType.cs b/src/Analysis/Ast/Impl/Specializations/Typing/Types/TypingDictionaryType.cs index 6f0e55bd4..de00a54fc 100644 --- a/src/Analysis/Ast/Impl/Specializations/Typing/Types/TypingDictionaryType.cs +++ b/src/Analysis/Ast/Impl/Specializations/Typing/Types/TypingDictionaryType.cs @@ -48,8 +48,8 @@ public TypingDictionaryType(string name, IPythonType keyType, IPythonType valueT public override string Name { get; } public override string QualifiedName { get; } - public override IMember CreateInstance(string typeName, IArgumentSet args) => new TypingDictionary(this); - public override IMember Index(IPythonInstance instance, IArgumentSet args) => new PythonInstance(ValueType); + public override IPythonInstance CreateInstance(IArgumentSet args) => new TypingDictionary(this); + public override IMember Index(IPythonInstance instance, IArgumentSet args) => ValueType.CreateInstance(args); public override bool IsSpecialized => true; private TypingTupleType CreateItemType() { diff --git a/src/Analysis/Ast/Impl/Specializations/Typing/Types/TypingListType.cs b/src/Analysis/Ast/Impl/Specializations/Typing/Types/TypingListType.cs index 2c1d3ba27..7fd3a8234 100644 --- a/src/Analysis/Ast/Impl/Specializations/Typing/Types/TypingListType.cs +++ b/src/Analysis/Ast/Impl/Specializations/Typing/Types/TypingListType.cs @@ -52,10 +52,10 @@ public TypingListType(string typeName, BuiltinTypeId typeId, IPythonType itemTyp public override bool IsAbstract => false; public override bool IsSpecialized => true; - public override IMember CreateInstance(string typeName, IArgumentSet args) => new TypingList(this); + public override IPythonInstance CreateInstance(IArgumentSet args) => new TypingList(this); public IPythonType ItemType { get; } - public override IMember Index(IPythonInstance instance, IArgumentSet args) => new PythonInstance(ItemType); + public override IMember Index(IPythonInstance instance, IArgumentSet args) => ItemType.CreateInstance(args); public override bool Equals(object obj) { if (!(obj is TypingListType other)) { diff --git a/src/Analysis/Ast/Impl/Specializations/Typing/Types/TypingTupleType.cs b/src/Analysis/Ast/Impl/Specializations/Typing/Types/TypingTupleType.cs index 52358c16c..8860aee39 100644 --- a/src/Analysis/Ast/Impl/Specializations/Typing/Types/TypingTupleType.cs +++ b/src/Analysis/Ast/Impl/Specializations/Typing/Types/TypingTupleType.cs @@ -45,7 +45,7 @@ public TypingTupleType(IReadOnlyList itemTypes, IPythonModule decla public override bool IsAbstract => false; public override bool IsSpecialized => true; - public override IMember CreateInstance(string typeName, IArgumentSet args) + public override IPythonInstance CreateInstance(IArgumentSet args) => new TypingTuple(this); public override IMember Index(IPythonInstance instance, IArgumentSet args) { @@ -54,7 +54,8 @@ public override IMember Index(IPythonInstance instance, IArgumentSet args) { n = ItemTypes.Count + n; // -1 means last, etc. } if (n >= 0 && n < ItemTypes.Count) { - return ItemTypes[n]; + var t = ItemTypes[n]; + return t.CreateInstance(args); } return UnknownType; } diff --git a/src/Analysis/Ast/Impl/Specializations/Typing/TypingModule.cs b/src/Analysis/Ast/Impl/Specializations/Typing/TypingModule.cs index 1761b4a01..36657bee4 100644 --- a/src/Analysis/Ast/Impl/Specializations/Typing/TypingModule.cs +++ b/src/Analysis/Ast/Impl/Specializations/Typing/TypingModule.cs @@ -124,7 +124,7 @@ private void SpecializeMembers() { _members["Union"] = new SpecializedGenericType("Union", CreateUnion, this); _members["Counter"] = Specialized.Function("Counter", this, GetMemberDocumentation("Counter"), - new PythonInstance(Interpreter.GetBuiltinType(BuiltinTypeId.Int))); + Interpreter.GetBuiltinType(BuiltinTypeId.Int)).CreateInstance(ArgumentSet.WithoutContext); // TODO: make these classes that support __float__, etc per spec. //_members["SupportsInt"] = Interpreter.GetBuiltinType(BuiltinTypeId.Int); diff --git a/src/Analysis/Ast/Impl/Specializations/Typing/Values/TypingDictionary.cs b/src/Analysis/Ast/Impl/Specializations/Typing/Values/TypingDictionary.cs index 9c3fac182..433b510b1 100644 --- a/src/Analysis/Ast/Impl/Specializations/Typing/Values/TypingDictionary.cs +++ b/src/Analysis/Ast/Impl/Specializations/Typing/Values/TypingDictionary.cs @@ -25,9 +25,8 @@ namespace Microsoft.Python.Analysis.Specializations.Typing.Values { /// internal class TypingDictionary : PythonDictionary { private readonly TypingDictionaryType _dictType; - - public TypingDictionary(TypingDictionaryType dictType) - : base(dictType, EmptyDictionary.Instance) { + + public TypingDictionary(TypingDictionaryType dictType) : base(dictType, EmptyDictionary.Instance) { _dictType = dictType; } @@ -37,13 +36,16 @@ public override IPythonIterator GetIterator() { return new TypingIterator(iteratorType, this); } - public override IMember Index(IArgumentSet args) => new PythonInstance(_dictType.ValueType); + public override IMember Index(IArgumentSet args) => + _dictType.ValueType.CreateInstance(args); public override IMember Call(string memberName, IArgumentSet args) { + var itemType = _dictType.ItemType; + var valueType = _dictType.ValueType; // Specializations switch (memberName) { case @"get": - return new PythonInstance(_dictType.ValueType); + return valueType.CreateInstance(args); case @"items": return GetItems(); case @"keys": @@ -57,9 +59,9 @@ public override IMember Call(string memberName, IArgumentSet args) { case @"iteritems": return GetItems().GetIterator(); case @"pop": - return new PythonInstance(_dictType.ValueType); + return valueType.CreateInstance(args); case @"popitem": - return new PythonInstance(_dictType.ItemType); + return itemType.CreateInstance(args); } return base.Call(memberName, args); } @@ -74,6 +76,6 @@ private TypingList GetValues() private TypingList _items; private TypingList GetItems() - =>_items ?? (_items = new TypingList(TypingTypeFactory.CreateItemsViewType(_dictType.DeclaringModule.Interpreter, _dictType))); + => _items ?? (_items = new TypingList(TypingTypeFactory.CreateItemsViewType(_dictType.DeclaringModule.Interpreter, _dictType))); } } diff --git a/src/Analysis/Ast/Impl/Specializations/Typing/Values/TypingIterator.cs b/src/Analysis/Ast/Impl/Specializations/Typing/Values/TypingIterator.cs index 124c7c862..8b5bb31d6 100644 --- a/src/Analysis/Ast/Impl/Specializations/Typing/Values/TypingIterator.cs +++ b/src/Analysis/Ast/Impl/Specializations/Typing/Values/TypingIterator.cs @@ -39,7 +39,7 @@ public override IMember Next { } else if (_index < _iteratorType.ItemTypes.Count) { itemType = _iteratorType.ItemTypes[_index++]; } - return itemType?.CreateInstance(itemType.Name, ArgumentSet.WithoutContext) ?? UnknownType; + return itemType?.CreateInstance(ArgumentSet.WithoutContext) ?? UnknownType; } } } diff --git a/src/Analysis/Ast/Impl/Specializations/Typing/Values/TypingTuple.cs b/src/Analysis/Ast/Impl/Specializations/Typing/Values/TypingTuple.cs index f1d3dedeb..379439fcf 100644 --- a/src/Analysis/Ast/Impl/Specializations/Typing/Values/TypingTuple.cs +++ b/src/Analysis/Ast/Impl/Specializations/Typing/Values/TypingTuple.cs @@ -20,8 +20,8 @@ namespace Microsoft.Python.Analysis.Specializations.Typing.Values { internal class TypingTuple : PythonCollection { - private readonly TypingTupleType _collectionType; - public TypingTuple(TypingTupleType collectionType) + private readonly ITypingTupleType _collectionType; + public TypingTuple(ITypingTupleType collectionType) : base(collectionType, collectionType.ItemTypes) { _collectionType = collectionType; } @@ -33,6 +33,6 @@ public override IPythonIterator GetIterator() { } public override IMember Index(IArgumentSet args) - => _collectionType.Index(this, args).GetPythonType().CreateInstance(null, ArgumentSet.Empty(args.Expression, args.Eval)); + => _collectionType.Index(this, args).GetPythonType().CreateInstance(ArgumentSet.Empty(args.Expression, args.Eval)); } } diff --git a/src/Analysis/Ast/Impl/Specializations/Typing/Values/TypingType.cs b/src/Analysis/Ast/Impl/Specializations/Typing/Values/TypingType.cs index e062dacf2..e05fbeea1 100644 --- a/src/Analysis/Ast/Impl/Specializations/Typing/Values/TypingType.cs +++ b/src/Analysis/Ast/Impl/Specializations/Typing/Values/TypingType.cs @@ -41,7 +41,7 @@ public TypingType(IPythonModule declaringModule, IPythonType type): base(declari public bool IsSpecialized => true; public IMember Call(IPythonInstance instance, string memberName, IArgumentSet args) => _type.Call(instance, memberName, args); - public IMember CreateInstance(string typeName, IArgumentSet args) => _type; + public IPythonInstance CreateInstance(IArgumentSet args) => _type.CreateInstance(args); public IMember GetMember(string name) => _type.GetMember(name); public IEnumerable GetMemberNames() => _type.GetMemberNames(); public IMember Index(IPythonInstance instance, IArgumentSet args) => _type.Index(instance, args); diff --git a/src/Analysis/Ast/Impl/Types/ArgumentSet.cs b/src/Analysis/Ast/Impl/Types/ArgumentSet.cs index fa4c1bebf..95d7d91e2 100644 --- a/src/Analysis/Ast/Impl/Types/ArgumentSet.cs +++ b/src/Analysis/Ast/Impl/Types/ArgumentSet.cs @@ -83,10 +83,10 @@ public ArgumentSet(IReadOnlyList args, Expression expr, IExpressionEval /// /// Function type. /// Function overload to call. - /// Type instance the function is bound to. For derived classes it is different from the declared type. + /// Type of the instance the function is bound to. For derived classes it is different from the declared type. /// Call expression that invokes the function. /// Evaluator that can calculate values of arguments from their respective expressions. - public ArgumentSet(IPythonFunctionType fn, int overloadIndex, IPythonInstance instance, CallExpression callExpr, IExpressionEvaluator eval) { + public ArgumentSet(IPythonFunctionType fn, int overloadIndex, IPythonType instanceType, CallExpression callExpr, IExpressionEvaluator eval) { Eval = eval; OverloadIndex = overloadIndex; DeclaringModule = fn.DeclaringModule; @@ -154,7 +154,7 @@ public ArgumentSet(IPythonFunctionType fn, int overloadIndex, IPythonInstance in // Class methods var formalParamIndex = 0; if (fn.DeclaringType != null && fn.HasClassFirstArgument() && slots.Length > 0) { - slots[0].Value = instance != null ? instance.GetPythonType() : fn.DeclaringType; + slots[0].Value = instanceType ?? fn.DeclaringType; formalParamIndex++; } diff --git a/src/Analysis/Ast/Impl/Types/Collections/PythonCollectionType.cs b/src/Analysis/Ast/Impl/Types/Collections/PythonCollectionType.cs index 839d7adf2..7e8c16fc2 100644 --- a/src/Analysis/Ast/Impl/Types/Collections/PythonCollectionType.cs +++ b/src/Analysis/Ast/Impl/Types/Collections/PythonCollectionType.cs @@ -57,7 +57,7 @@ bool isMutable public override PythonMemberType MemberType => PythonMemberType.Class; public override IMember GetMember(string name) => name == @"__iter__" ? IteratorType : base.GetMember(name); - public override IMember CreateInstance(string typeName, IArgumentSet args) + public override IPythonInstance CreateInstance(IArgumentSet args) => new PythonCollection(this, args.Arguments.Select(a => a.Value).OfType().ToArray()); public override IMember Call(IPythonInstance instance, string memberName, IArgumentSet args) diff --git a/src/Analysis/Ast/Impl/Types/Collections/PythonDictionaryType.cs b/src/Analysis/Ast/Impl/Types/Collections/PythonDictionaryType.cs index a4835deec..d528a8d21 100644 --- a/src/Analysis/Ast/Impl/Types/Collections/PythonDictionaryType.cs +++ b/src/Analysis/Ast/Impl/Types/Collections/PythonDictionaryType.cs @@ -14,6 +14,7 @@ // permissions and limitations under the License. using System.Collections.Generic; +using Microsoft.Python.Analysis.Values; using Microsoft.Python.Analysis.Values.Collections; using Microsoft.Python.Core; @@ -23,7 +24,7 @@ public PythonDictionaryType(IPythonModule declaringModule, bool isMutable = true : base(BuiltinTypeId.Dict, declaringModule, isMutable) { } - public override IMember CreateInstance(string typeName, IArgumentSet args) { + public override IPythonInstance CreateInstance(IArgumentSet args) { var contents = args.Arguments.Count == 1 ? args.Arguments[0].Value as IReadOnlyDictionary : EmptyDictionary.Instance; diff --git a/src/Analysis/Ast/Impl/Types/Definitions/IPythonType.cs b/src/Analysis/Ast/Impl/Types/Definitions/IPythonType.cs index d90ec08d7..d546f7261 100644 --- a/src/Analysis/Ast/Impl/Types/Definitions/IPythonType.cs +++ b/src/Analysis/Ast/Impl/Types/Definitions/IPythonType.cs @@ -62,7 +62,7 @@ public interface IPythonType : ILocatedMember, IMemberContainer { /// Name of the type. Used in specialization scenarios /// where constructor may want to create specialized type. /// Any custom arguments required to create the instance. - IMember CreateInstance(string typeName = null, IArgumentSet args = null); + IPythonInstance CreateInstance(IArgumentSet args); /// /// Invokes method or property on the specified instance. diff --git a/src/Analysis/Ast/Impl/Types/PythonClassType.Generics.cs b/src/Analysis/Ast/Impl/Types/PythonClassType.Generics.cs index 9490079e6..bb58c303a 100644 --- a/src/Analysis/Ast/Impl/Types/PythonClassType.Generics.cs +++ b/src/Analysis/Ast/Impl/Types/PythonClassType.Generics.cs @@ -366,7 +366,7 @@ private void SetClassMembers(IPythonClassType templateClass, IArgumentSet args) } if (specificType != null) { - AddMember(m.Key, new PythonInstance(specificType), true); + AddMember(m.Key, specificType.CreateInstance(args), true); } break; } diff --git a/src/Analysis/Ast/Impl/Types/PythonClassType.cs b/src/Analysis/Ast/Impl/Types/PythonClassType.cs index 0f35ff1d9..708f254d4 100644 --- a/src/Analysis/Ast/Impl/Types/PythonClassType.cs +++ b/src/Analysis/Ast/Impl/Types/PythonClassType.cs @@ -149,10 +149,10 @@ public override string Documentation { } // Constructor call - public override IMember CreateInstance(string typeName, IArgumentSet args) { + public override IPythonInstance CreateInstance(IArgumentSet args) { var builtins = DeclaringModule.Interpreter.ModuleResolution.BuiltinsModule; // Specializations - switch (typeName) { + switch (Name) { case "list": return PythonCollectionType.CreateList(builtins, args); case "dict": { diff --git a/src/Analysis/Ast/Impl/Types/PythonFunctionOverload.cs b/src/Analysis/Ast/Impl/Types/PythonFunctionOverload.cs index e3158c24c..785ef804b 100644 --- a/src/Analysis/Ast/Impl/Types/PythonFunctionOverload.cs +++ b/src/Analysis/Ast/Impl/Types/PythonFunctionOverload.cs @@ -84,7 +84,7 @@ internal void AddReturnValue(IMember value) { if (!_fromAnnotation && !currentType.Equals(valueType)) { var type = PythonUnionType.Combine(currentType, valueType); // Track instance vs type info. - StaticReturnValue = value is IPythonInstance ? new PythonInstance(type) : (IMember)type; + StaticReturnValue = value is IPythonInstance ? type.CreateInstance(ArgumentSet.WithoutContext) : (IMember)type; } } @@ -173,8 +173,9 @@ private IMember CreateSpecificReturnFromClassType(IPythonClassType selfClassType } if (typeArgs != null) { - var specificReturnValue = returnClassType.CreateSpecificType(new ArgumentSet(typeArgs, args?.Expression, args?.Eval)); - return new PythonInstance(specificReturnValue); + var newArgs = new ArgumentSet(typeArgs, args?.Expression, args?.Eval); + var specificReturnValue = returnClassType.CreateSpecificType(newArgs); + return specificReturnValue.CreateInstance(newArgs); } return null; @@ -182,7 +183,7 @@ private IMember CreateSpecificReturnFromClassType(IPythonClassType selfClassType private IMember CreateSpecificReturnFromTypeVar(IPythonClassType selfClassType, IArgumentSet args, IGenericTypeParameter returnType) { if (selfClassType.GetSpecificType(returnType.Name, out var specificType)) { - return new PythonInstance(specificType); + return specificType.CreateInstance(args); } // Find first base class type in which function was declared @@ -193,7 +194,7 @@ private IMember CreateSpecificReturnFromTypeVar(IPythonClassType selfClassType, // Try and infer return value from base class if (baseType != null && baseType.GetSpecificType(returnType.Name, out specificType)) { - return new PythonInstance(specificType); + return specificType.CreateInstance(args); } // Try getting type from passed in arguments @@ -203,7 +204,7 @@ private IMember CreateSpecificReturnFromTypeVar(IPythonClassType selfClassType, // Try getting the type from the type parameter bound if (returnType.Bound != null) { - return new PythonInstance(returnType.Bound); + return returnType.Bound.CreateInstance(args); } // Try returning the constraint diff --git a/src/Analysis/Ast/Impl/Types/PythonType.cs b/src/Analysis/Ast/Impl/Types/PythonType.cs index 8058a08c8..4abb24c38 100644 --- a/src/Analysis/Ast/Impl/Types/PythonType.cs +++ b/src/Analysis/Ast/Impl/Types/PythonType.cs @@ -78,7 +78,7 @@ public virtual string QualifiedName /// Name of the type. Used in specialization scenarios /// where constructor may want to create specialized type. /// Any custom arguments required to create the instance. - public virtual IMember CreateInstance(string typeName, IArgumentSet args) => new PythonInstance(this); + public virtual IPythonInstance CreateInstance(IArgumentSet args) => new PythonInstance(this); /// /// Invokes method or property on the specified instance. diff --git a/src/Analysis/Ast/Impl/Types/PythonTypeWrapper.cs b/src/Analysis/Ast/Impl/Types/PythonTypeWrapper.cs index f24ffa4a9..3c38194b1 100644 --- a/src/Analysis/Ast/Impl/Types/PythonTypeWrapper.cs +++ b/src/Analysis/Ast/Impl/Types/PythonTypeWrapper.cs @@ -71,8 +71,8 @@ public PythonTypeWrapper(BuiltinTypeId builtinTypeId, IPythonModule declaringMod public virtual bool IsAbstract => InnerType.IsAbstract; public virtual bool IsSpecialized => InnerType.IsSpecialized; - public virtual IMember CreateInstance(string typeName, IArgumentSet args) - => IsAbstract ? null : InnerType.CreateInstance(typeName, args); + public virtual IPythonInstance CreateInstance(IArgumentSet args) + => IsAbstract ? null : InnerType.CreateInstance(args); public virtual IMember Call(IPythonInstance instance, string memberName, IArgumentSet args) => InnerType.Call(instance, memberName, args); public virtual IMember Index(IPythonInstance instance, IArgumentSet args) diff --git a/src/Analysis/Ast/Impl/Types/PythonUnionType.cs b/src/Analysis/Ast/Impl/Types/PythonUnionType.cs index 005268134..6888d657a 100644 --- a/src/Analysis/Ast/Impl/Types/PythonUnionType.cs +++ b/src/Analysis/Ast/Impl/Types/PythonUnionType.cs @@ -69,7 +69,7 @@ public bool IsBuiltin { public bool IsAbstract => false; public bool IsSpecialized => true; - public IMember CreateInstance(string typeName, IArgumentSet args) => new PythonUnion(this); + public IPythonInstance CreateInstance(IArgumentSet args) => new PythonUnion(this); public IMember Call(IPythonInstance instance, string memberName, IArgumentSet args) { IPythonType[] types; diff --git a/src/Analysis/Ast/Impl/Utilities/ValueEnumerator.cs b/src/Analysis/Ast/Impl/Utilities/ValueEnumerator.cs index e9a63bdfd..a1d4cb7d5 100644 --- a/src/Analysis/Ast/Impl/Utilities/ValueEnumerator.cs +++ b/src/Analysis/Ast/Impl/Utilities/ValueEnumerator.cs @@ -6,7 +6,7 @@ namespace Microsoft.Python.Analysis.Utilities { internal sealed class ValueEnumerator { private readonly IMember _value; - private readonly IMember _unknown; + private readonly IPythonType _unknown; private readonly IMember[] _values; private int _index; @@ -15,7 +15,7 @@ internal sealed class ValueEnumerator { /// /// Collection to iterate over /// Default type when we cannot find type from collection - public ValueEnumerator(IMember value, IMember unknown) { + public ValueEnumerator(IMember value, IPythonType unknown) { _value = value; _unknown = unknown; switch (value) { @@ -32,19 +32,23 @@ public ValueEnumerator(IMember value, IMember unknown) { public IMember Next { get { - IMember t; + IMember t = Peek; + _index++; + return t; + } + } + + public IMember Peek { + get { if (_values.Length > 0) { - t = _index < _values.Length ? _values[_index] : _values[_values.Length - 1]; + return _index < _values.Length ? _values[_index] : _values[_values.Length - 1]; } else { - t = Filler; + return Filler.CreateInstance(ArgumentSet.WithoutContext); } - - _index++; - return t; } } - private IMember Filler { + private IPythonType Filler { get { switch (_value?.GetPythonType()) { case ITypingListType tlt: diff --git a/src/Analysis/Ast/Impl/Values/Collections/PythonCollection.cs b/src/Analysis/Ast/Impl/Values/Collections/PythonCollection.cs index ab49f8193..3034acd1a 100644 --- a/src/Analysis/Ast/Impl/Values/Collections/PythonCollection.cs +++ b/src/Analysis/Ast/Impl/Values/Collections/PythonCollection.cs @@ -38,6 +38,7 @@ public PythonCollection( if (flatten && c.Count == 1 && c[0] is IPythonCollection seq) { Contents = seq.Contents; } else { + // TODO find elements that are IPythonType and make them into PythonInstances Contents = c; } IsExact = exact; diff --git a/src/Analysis/Ast/Test/ArgumentSetTests.cs b/src/Analysis/Ast/Test/ArgumentSetTests.cs index 3bfcefa23..24cf057f0 100644 --- a/src/Analysis/Ast/Test/ArgumentSetTests.cs +++ b/src/Analysis/Ast/Test/ArgumentSetTests.cs @@ -456,7 +456,7 @@ private async Task GetClassArgSetAsync(string code, string classNam var cls = analysis.Should().HaveClass(className).Which; var f = cls.Should().HaveMethod(funcName).Which; var call = GetCall(analysis.Ast); - return new ArgumentSet(f, 0, new PythonInstance(cls), call, analysis.ExpressionEvaluator); + return new ArgumentSet(f, 0, cls, call, analysis.ExpressionEvaluator); } private CallExpression GetCall(PythonAst ast) { diff --git a/src/Analysis/Ast/Test/AssignmentTests.cs b/src/Analysis/Ast/Test/AssignmentTests.cs index b586c1200..caa845cbe 100644 --- a/src/Analysis/Ast/Test/AssignmentTests.cs +++ b/src/Analysis/Ast/Test/AssignmentTests.cs @@ -370,6 +370,298 @@ public async Task IncompleteTuple() { .And.HaveVariable("b").OfType(BuiltinTypeId.Int); } + [TestMethod, Priority(0)] + public async Task UnpackListToTuple() { + const string code = @" +(a, b) = [1, 2] +"; + var analysis = await GetAnalysisAsync(code); + analysis.Should().HaveVariable("a").OfType(BuiltinTypeId.Int) + .And.HaveVariable("b").OfType(BuiltinTypeId.Int); + } + + [TestMethod, Priority(0)] + public async Task UnpackSingleElementTuple() { + const string code = @" +(foo) = 1234 +((x, y)) = 1, '2' +"; + var analysis = await GetAnalysisAsync(code); + analysis.Should().HaveVariable("foo").OfType(BuiltinTypeId.Int) + .And.HaveVariable("x").OfType(BuiltinTypeId.Int) + .And.HaveVariable("y").OfType(BuiltinTypeId.Str); + } + + [TestMethod, Priority(0)] + public async Task UnpackingTypingTuple() { + const string code = @" +from typing import Tuple + +def foo() -> Tuple[int, int]: + return (1, -1) + +result = foo() +(a, b) = result +"; + var analysis = await GetAnalysisAsync(code); + analysis.Should().HaveVariable("a").OfType(BuiltinTypeId.Int) + .And.HaveVariable("b").OfType(BuiltinTypeId.Int); + } + + [TestMethod, Priority(0)] + public async Task UnpackingTypingTupleFromList() { + const string code = @" +from typing import Tuple, List + +def foo() -> List[Tuple[int, int]]: + return [(1, -1)] + +def bar() -> List[Tuple[int, str, float]]: + return [(1, 'str', float(2)), (1,'test',float(5))] + +(a, b) = foo()[0] +(c,d,e) = bar()[1] +"; + var analysis = await GetAnalysisAsync(code); + analysis.Should().HaveVariable("a").OfType(BuiltinTypeId.Int) + .And.HaveVariable("b").OfType(BuiltinTypeId.Int) + .And.HaveVariable("c").OfType(BuiltinTypeId.Int) + .And.HaveVariable("d").OfType(BuiltinTypeId.Str) + .And.HaveVariable("e").OfType(BuiltinTypeId.Float); + } + + [TestMethod, Priority(0)] + public async Task UnpackingNestedTupleInList() { + const string code = @" +def foo(): + return [1,2], 3 + +[var1, var2], var3 = foo(); +"; + var analysis = await GetAnalysisAsync(code); + analysis.Should().HaveVariable("var1").OfType(BuiltinTypeId.Int) + .And.HaveVariable("var2").OfType(BuiltinTypeId.Int) + .And.HaveVariable("var3").OfType(BuiltinTypeId.Int); + } + + [TestMethod, Priority(0)] + public async Task UnpackingTypingListFromTuple() { + const string code = @" +from typing import Tuple, List + +def foo() -> Tuple[List[int]]: + return ([1,2,3]) + +def bar() -> Tuple[Tuple[int, str, float], List[str]]: + return ((1, 'str', float(2)), ['hello', 'world']) + +a = foo() +a1 = a[0] +[b, c, d] = a1 + +e = bar() +f = bar()[0] +g = bar()[1] + +(h, j, k) = f +[l, m] = g +"; + var analysis = await GetAnalysisAsync(code); + analysis.Should().HaveVariable("a").OfType(BuiltinTypeId.Tuple) + .And.HaveVariable("a1").OfType(BuiltinTypeId.List) + .And.HaveVariable("b").OfType(BuiltinTypeId.Int) + .And.HaveVariable("c").OfType(BuiltinTypeId.Int) + .And.HaveVariable("d").OfType(BuiltinTypeId.Int); + + analysis.Should().HaveVariable("e").OfType(BuiltinTypeId.Tuple) + .And.HaveVariable("f").OfType(BuiltinTypeId.Tuple) + .And.HaveVariable("g").OfType(BuiltinTypeId.List); + + analysis.Should().HaveVariable("h").OfType(BuiltinTypeId.Int) + .And.HaveVariable("j").OfType(BuiltinTypeId.Str) + .And.HaveVariable("k").OfType(BuiltinTypeId.Float); + + analysis.Should().HaveVariable("l").OfType(BuiltinTypeId.Str) + .And.HaveVariable("m").OfType(BuiltinTypeId.Str); + } + + + [TestMethod, Priority(0)] + public async Task UnpackingTypingList() { + const string code = @" +from typing import List + +def foo() -> List[int]: + return [1, -1] + +result = foo() +[a, b] = result +"; + var analysis = await GetAnalysisAsync(code); + analysis.Should().HaveVariable("a").OfType(BuiltinTypeId.Int) + .And.HaveVariable("b").OfType(BuiltinTypeId.Int); + } + + [TestMethod, Priority(0)] + public async Task UnpackingNestedTypingListInTuple() { + const string code = @" +from typing import List, Tuple +def foo() -> List[Tuple[str, int]]: + return [('hi', 1)] + +[var1, var2] = foo() +[(a, b), (c, d)] = foo() +"; + var analysis = await GetAnalysisAsync(code); + analysis.Should().HaveVariable("var1").OfType(BuiltinTypeId.Tuple) + .And.HaveVariable("var2").OfType(BuiltinTypeId.Tuple); + + analysis.Should().HaveVariable("a").OfType(BuiltinTypeId.Str) + .And.HaveVariable("b").OfType(BuiltinTypeId.Int) + .And.HaveVariable("c").OfType(BuiltinTypeId.Str) + .And.HaveVariable("d").OfType(BuiltinTypeId.Int); + } + + [TestMethod, Priority(0)] + public async Task UnpackingNestedTypingTupleInList() { + const string code = @" +from typing import List, Tuple +def foo() -> List[List[int], int]: + return [1,2], 3 + +[var1, var2], var3 = foo() +"; + var analysis = await GetAnalysisAsync(code); + analysis.Should().HaveVariable("var1").OfType(BuiltinTypeId.Int) + .And.HaveVariable("var2").OfType(BuiltinTypeId.Int) + .And.HaveVariable("var3").OfType(BuiltinTypeId.Int); + } + + [TestMethod, Priority(0)] + public async Task UnpackingComplexTypingNestedExpressions() { + const string code = @" +from typing import List, Tuple +def foo() -> Tuple[List[Tuple[List[str], int]], int]: + return [(['hi'], 1), (['test'], 5)], 2 + +[var1, var2], var3 = foo() +[(a, b), (c, d)], f = foo() +"; + var analysis = await GetAnalysisAsync(code); + + analysis.Should().HaveVariable("var1").OfType(BuiltinTypeId.Tuple) + .And.HaveVariable("var2").OfType(BuiltinTypeId.Tuple) + .And.HaveVariable("var3").OfType(BuiltinTypeId.Int); + + analysis.Should().HaveVariable("a").OfType(BuiltinTypeId.List) + .And.HaveVariable("b").OfType(BuiltinTypeId.Int) + .And.HaveVariable("c").OfType(BuiltinTypeId.List) + .And.HaveVariable("d").OfType(BuiltinTypeId.Int) + .And.HaveVariable("f").OfType(BuiltinTypeId.Int); + } + + [TestMethod, Priority(0)] + public async Task UnpackingComplexNestedExpressions() { + const string code = @" +def foo(): + return [(['hi'], 1), (['test'], 5)], 2 + +[var1, var2], var3 = foo() +[(a, b), (c, d)], f = foo() +"; + var analysis = await GetAnalysisAsync(code); + + analysis.Should().HaveVariable("var1").OfType(BuiltinTypeId.Tuple) + .And.HaveVariable("var2").OfType(BuiltinTypeId.Tuple) + .And.HaveVariable("var3").OfType(BuiltinTypeId.Int); + + analysis.Should().HaveVariable("a").OfType(BuiltinTypeId.List) + .And.HaveVariable("b").OfType(BuiltinTypeId.Int) + .And.HaveVariable("c").OfType(BuiltinTypeId.List) + .And.HaveVariable("d").OfType(BuiltinTypeId.Int) + .And.HaveVariable("f").OfType(BuiltinTypeId.Int); + } + + [TestMethod, Priority(0)] + public async Task UnpackingMultipleAssignment() { + const string code = @" +a = b, c = [0, 1] +"; + var analysis = await GetAnalysisAsync(code); + + analysis.Should().HaveVariable("a").OfType(BuiltinTypeId.List) + .And.HaveVariable("b").OfType(BuiltinTypeId.Int) + .And.HaveVariable("c").OfType(BuiltinTypeId.Int); + } + + [TestMethod, Priority(0)] + public async Task UnpackingNestedTuple() { + const string code = @" +b, c = (1, (1,2)) +d, e, f, g = (1, (1,2), 2, ('test', 'test')) +"; + var analysis = await GetAnalysisAsync(code); + + analysis.Should().HaveVariable("b").OfType(BuiltinTypeId.Int) + .And.HaveVariable("c").OfType(BuiltinTypeId.Tuple); + + analysis.Should().HaveVariable("d").OfType(BuiltinTypeId.Int) + .And.HaveVariable("e").OfType(BuiltinTypeId.Tuple) + .And.HaveVariable("f").OfType(BuiltinTypeId.Int) + .And.HaveVariable("g").OfType(BuiltinTypeId.Tuple); + } + + [TestMethod, Priority(0)] + public async Task UnpackingNestedList() { + const string code = @" +b, c = [1, [1,2]] +d, e, f, g = [1, (1,2), 2, ['test', 'test']] +"; + var analysis = await GetAnalysisAsync(code); + + analysis.Should().HaveVariable("b").OfType(BuiltinTypeId.Int) + .And.HaveVariable("c").OfType(BuiltinTypeId.List); + + analysis.Should().HaveVariable("d").OfType(BuiltinTypeId.Int) + .And.HaveVariable("e").OfType(BuiltinTypeId.Tuple) + .And.HaveVariable("f").OfType(BuiltinTypeId.Int) + .And.HaveVariable("g").OfType(BuiltinTypeId.List); + } + + + [TestMethod, Priority(0)] + public async Task UnpackingTypingNestedTuple() { + const string code = @" +from typing import Tuple +h: Tuple[int, Tuple[str, int]] +b, c = h +"; + var analysis = await GetAnalysisAsync(code); + + analysis.Should().HaveVariable("b").OfType(BuiltinTypeId.Int) + .And.HaveVariable("c").OfType(BuiltinTypeId.Tuple); + } + + [TestMethod, Priority(0)] + public async Task UnpackingTypingNestedList() { + const string code = @" +from typing import Tuple, List +t: Tuple[int, Tuple[str, int], List[int]] +b, c, d = t +e, (f, g), h = t +"; + var analysis = await GetAnalysisAsync(code); + + analysis.Should().HaveVariable("b").OfType(BuiltinTypeId.Int) + .And.HaveVariable("c").OfType(BuiltinTypeId.Tuple) + .And.HaveVariable("d").OfType(BuiltinTypeId.List); + + analysis.Should().HaveVariable("e").OfType(BuiltinTypeId.Int) + .And.HaveVariable("f").OfType(BuiltinTypeId.Str) + .And.HaveVariable("g").OfType(BuiltinTypeId.Int) + .And.HaveVariable("h").OfType(BuiltinTypeId.List); + } + [TestMethod, Priority(0)] public async Task Uts46dataModule() { const string code = @"from idna.uts46data import *"; diff --git a/src/Analysis/Ast/Test/CollectionsTests.cs b/src/Analysis/Ast/Test/CollectionsTests.cs index be9f31a1c..bdcd3dc0f 100644 --- a/src/Analysis/Ast/Test/CollectionsTests.cs +++ b/src/Analysis/Ast/Test/CollectionsTests.cs @@ -16,6 +16,7 @@ using System.Threading.Tasks; using Microsoft.Python.Analysis.Tests.FluentAssertions; using Microsoft.Python.Analysis.Types; +using Microsoft.Python.Analysis.Values; using Microsoft.Python.Parsing.Tests; using Microsoft.VisualStudio.TestTools.UnitTesting; using TestUtilities; @@ -51,6 +52,32 @@ public async Task ListAssign() { .And.HaveVariable("x4").OfType(BuiltinTypeId.Str); } + [TestMethod, Priority(0)] + public async Task TypingListEnum() { + const string code = @" +from typing import List +xs: List[str] = ['tmp'] +for x in xs: + pass +"; + var analysis = await GetAnalysisAsync(code); + analysis.Should().HaveVariable("x").OfType(BuiltinTypeId.Str); + } + + [TestMethod, Priority(0)] + public async Task TypingListInFuncEnum() { + const string code = @" +from typing import List +def f(xs: List[str]): + for x in xs: + pass +"; + var analysis = await GetAnalysisAsync(code); + analysis.GlobalScope.Should().OnlyHaveChildScope().Which.Should().HaveVariable("x") + .OfType(BuiltinTypeId.Str); + } + + [TestMethod, Priority(0)] public async Task ListCallCtor() { const string code = @" @@ -310,11 +337,18 @@ public async Task ForSequence() { print some_str print some_int print some_bool + +for another_str, another_int, another_bool in [('abc', 42, True), ('abc', 23, False)]: + pass "; var analysis = await GetAnalysisAsync(code); analysis.Should().HaveVariable("some_str").OfType(BuiltinTypeId.Str) .And.HaveVariable("some_int").OfType(BuiltinTypeId.Int) .And.HaveVariable("some_bool").OfType(BuiltinTypeId.Bool); + + analysis.Should().HaveVariable("another_str").OfType(BuiltinTypeId.Str) + .And.HaveVariable("another_int").OfType(BuiltinTypeId.Int) + .And.HaveVariable("another_bool").OfType(BuiltinTypeId.Bool); } [TestMethod, Priority(0)]