private IPythonType TryDetermineReturnValue() { var annotationType = Eval.GetTypeFromAnnotation(FunctionDefinition.ReturnAnnotation); if (!annotationType.IsUnknown()) { // 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(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() ? (IMember)annotationType : t; _overload.SetReturnValue(instance, true); _overload.SetReturnValue(instance, true); } else { // Check if function is a generator var suite = FunctionDefinition.Body as SuiteStatement; var yieldExpr = suite?.Statements.OfType <ExpressionStatement>().Select(s => s.Expression as YieldExpression).ExcludeDefault().FirstOrDefault(); if (yieldExpr != null) { // Function return is an iterator var yieldValue = Eval.GetValueFromExpression(yieldExpr.Expression) ?? Eval.UnknownType; var returnValue = new PythonGenerator(Eval.Interpreter, yieldValue); _overload.SetReturnValue(returnValue, true); } } return(annotationType); }
private void TryAddBase(List <IPythonType> bases, Arg arg) { // We cheat slightly and treat base classes as annotations. var b = Eval.GetTypeFromAnnotation(arg.Expression); if (b != null) { var t = b.GetPythonType(); bases.Add(t); t.AddReference(Eval.GetLocationOfName(arg.Expression)); } }
public override void Evaluate() { var stub = SymbolTable.ReplacedByStubs.Contains(Target) || _function.DeclaringModule.ModuleType == ModuleType.Stub || Module.ModuleType == ModuleType.Specialized; using (Eval.OpenScope(_function.DeclaringModule, FunctionDefinition, out _)) { // Process annotations. var annotationType = Eval.GetTypeFromAnnotation(FunctionDefinition.ReturnAnnotation); if (!annotationType.IsUnknown()) { // Annotations are typically types while actually functions return // instances unless specifically annotated to a type such as Type[T]. var instance = annotationType.CreateInstance(annotationType.Name, ArgumentSet.Empty); _overload.SetReturnValue(instance, true); } else { // Check if function is a generator var suite = FunctionDefinition.Body as SuiteStatement; var yieldExpr = suite?.Statements.OfType <ExpressionStatement>().Select(s => s.Expression as YieldExpression).ExcludeDefault().FirstOrDefault(); if (yieldExpr != null) { // Function return is an iterator var yieldValue = Eval.GetValueFromExpression(yieldExpr.Expression) ?? Eval.UnknownType; var returnValue = new PythonGenerator(Eval.Interpreter, yieldValue); _overload.SetReturnValue(returnValue, true); } } DeclareParameters(!stub); // Do process body of constructors since they may be declaring // variables that are later used to determine return type of other // methods and properties. var ctor = _function.Name.EqualsOrdinal("__init__") || _function.Name.EqualsOrdinal("__new__"); if (ctor || annotationType.IsUnknown() || Module.ModuleType == ModuleType.User) { // Return type from the annotation is sufficient for libraries and stubs, no need to walk the body. FunctionDefinition.Body?.Walk(this); // For libraries remove declared local function variables to free up some memory. var optionsProvider = Eval.Services.GetService <IAnalysisOptionsProvider>(); if (Module.ModuleType != ModuleType.User && optionsProvider?.Options.KeepLibraryLocalVariables != true) { ((VariableCollection)Eval.CurrentScope.Variables).Clear(); } } } Result = _function; }
public void HandleAnnotatedExpression(ExpressionWithAnnotation expr, IMember value) { if (expr?.Annotation == null) { return; } var variableType = Eval.GetTypeFromAnnotation(expr.Annotation); // If value is null, then this is a pure declaration like // x: List[str] // without a value. If value is provided, then this is // x: List[str] = [...] HandleTypedVariable(variableType, value, expr.Expression); }
public void EvaluateClass() { // Open class scope chain using (Eval.OpenScope(Module, _classDef, out var outerScope)) { var instance = Eval.GetInScope(_classDef.Name, outerScope); if (!(instance?.GetPythonType() is PythonClassType classInfo)) { if (instance != null) { // TODO: warning that variable is already declared of a different type. } return; } // Evaluate inner classes, if any EvaluateInnerClasses(_classDef); _class = classInfo; // Set bases to the class. var bases = new List <IPythonType>(); foreach (var a in _classDef.Bases.Where(a => string.IsNullOrEmpty(a.Name))) { // We cheat slightly and treat base classes as annotations. var b = Eval.GetTypeFromAnnotation(a.Expression); if (b != null) { var t = b.GetPythonType(); bases.Add(t); t.AddReference(Eval.GetLocationOfName(a.Expression)); } } _class.SetBases(bases); // Declare __class__ variable in the scope. Eval.DeclareVariable("__class__", _class, VariableSource.Declaration); ProcessClassBody(); } }
private void DeclareParameters(bool declareVariables) { // For class method no need to add extra parameters, but first parameter type should be the class. // For static and unbound methods do not add or set anything. // For regular bound methods add first parameter and set it to the class. var parameters = new List <ParameterInfo>(); var skip = 0; if (_self != null && _function.HasClassFirstArgument()) { var p0 = FunctionDefinition.Parameters.FirstOrDefault(); if (p0 != null && !string.IsNullOrEmpty(p0.Name)) { // 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. if (declareVariables) { Eval.DeclareVariable(p0.Name, new PythonInstance(_self), VariableSource.Declaration, p0.NameExpression); } // Set parameter info. var pi = new ParameterInfo(Ast, p0, _self, null, false); parameters.Add(pi); skip++; } } // Declare parameters in scope IMember defaultValue = null; for (var i = skip; i < FunctionDefinition.Parameters.Length; i++) { var isGeneric = false; var p = FunctionDefinition.Parameters[i]; if (!string.IsNullOrEmpty(p.Name)) { IPythonType paramType = null; if (p.DefaultValue != null) { defaultValue = Eval.GetValueFromExpression(p.DefaultValue); // If parameter has default value, look for the annotation locally first // since outer type may be getting redefined. Consider 's = None; def f(s: s = 123): ... paramType = Eval.GetTypeFromAnnotation(p.Annotation, out isGeneric, LookupOptions.Local | LookupOptions.Builtins); // Default value of None does not mean the parameter is None, just says it can be missing. defaultValue = defaultValue.IsUnknown() || defaultValue.IsOfType(BuiltinTypeId.NoneType) ? null : defaultValue; if (paramType == null && defaultValue != null) { paramType = defaultValue.GetPythonType(); } } // If all else fails, look up globally. paramType = paramType ?? Eval.GetTypeFromAnnotation(p.Annotation, out isGeneric) ?? Eval.UnknownType; var pi = new ParameterInfo(Ast, p, paramType, defaultValue, isGeneric); if (declareVariables) { DeclareParameter(p, pi); } parameters.Add(pi); } } _overload.SetParameters(parameters); }