public ArgumentSet()

in src/Analysis/Ast/Impl/Types/ArgumentSet.cs [90:294]


        public ArgumentSet(IPythonFunctionType fn, int overloadIndex, IPythonType instanceType, CallExpression callExpr, IExpressionEvaluator eval) {
            Eval = eval;
            OverloadIndex = overloadIndex;
            DeclaringModule = fn.DeclaringModule;
            Expression = callExpr;

            if (callExpr == null) {
                // Typically invoked by specialization code without call expression in the code.
                // Caller usually does not care about arguments.
                _evaluated = true;
                return;
            }

            var overload = fn.Overloads[overloadIndex];
            var fdParameters = overload.FunctionDefinition?.Parameters.Where(p => !p.IsPositionalOnlyMarker).ToArray();

            // Some specialized functions have more complicated definitions, so we pass
            // parameters to those, TypeVar() is an example, so we allow the latter logic to handle
            // argument instatiation. For simple specialized functions, it is enough to handle here.
            if (fn.IsSpecialized && overload.Parameters.Count == 0) {
                // Specialized functions typically don't have AST definitions.
                // We construct the arguments from the call expression. If an argument does not have a name, 
                // we try using name from the function definition based on the argument's position.
                _arguments = new List<Argument>();
                for (var i = 0; i < callExpr.Args.Count; i++) {
                    var name = callExpr.Args[i].Name;
                    if (string.IsNullOrEmpty(name)) {
                        name = i < overload.Parameters.Count ? overload.Parameters[i].Name : $"arg{i}";
                    }
                    var node = fdParameters?.ElementAtOrDefault(i);
                    _arguments.Add(new Argument(name, ParameterKind.Normal, callExpr.Args[i].Expression, null, node));
                }
                return;
            }

            var callLocation = callExpr.Target?.GetLocation(eval);

            // https://www.python.org/dev/peps/pep-3102/#id5
            // For each formal parameter, there is a slot which will be used to contain
            // the value of the argument assigned to that parameter. Slots which have
            // had values assigned to them are marked as 'filled'.Slots which have
            // no value assigned to them yet are considered 'empty'.

            var slots = new Argument[overload.Parameters.Count];
            for (var i = 0; i < overload.Parameters.Count; i++) {
                var node = fdParameters?.ElementAtOrDefault(i);
                slots[i] = new Argument(overload.Parameters[i], node);
            }

            // Locate sequence argument, if any
            var sa = slots.Where(s => s.Kind == ParameterKind.List).ToArray();
            if (sa.Length > 1) {
                // Error should have been reported at the function definition location by the parser.
                return;
            }

            var da = slots.Where(s => s.Kind == ParameterKind.Dictionary).ToArray();
            if (da.Length > 1) {
                // Error should have been reported at the function definition location by the parser.
                return;
            }

            _listArgument = sa.Length == 1 && sa[0].Name.Length > 0 ? new ListArg(sa[0].Name, sa[0].ValueExpression, sa[0].Location) : null;
            _dictArgument = da.Length == 1 ? new DictArg(da[0].Name, da[0].ValueExpression, da[0].Location) : null;

            // Class methods
            var formalParamIndex = 0;
            if (fn.DeclaringType != null && fn.HasClassFirstArgument() && slots.Length > 0) {
                slots[0].Value = instanceType ?? fn.DeclaringType;
                formalParamIndex++;
            }

            try {
                // Positional arguments
                var callParamIndex = 0;
                for (; callParamIndex < callExpr.Args.Count; callParamIndex++, formalParamIndex++) {
                    var arg = callExpr.Args[callParamIndex];

                    if (!string.IsNullOrEmpty(arg.Name) && !arg.Name.StartsWithOrdinal("**")) {
                        // Keyword argument. Done with positionals.
                        break;
                    }

                    if (formalParamIndex >= overload.Parameters.Count) {
                        // We ran out of formal parameters and yet haven't seen
                        // any sequence or dictionary ones. This looks like an error.
                        _errors.Add(new DiagnosticsEntry(Resources.Analysis_TooManyFunctionArguments, callLocation.Span,
                            ErrorCodes.TooManyFunctionArguments, Severity.Warning, DiagnosticSource.Analysis));
                        return;
                    }

                    var formalParam = overload.Parameters[formalParamIndex];
                    if (formalParam.Kind == ParameterKind.List) {
                        if (string.IsNullOrEmpty(formalParam.Name)) {
                            // If the next unfilled slot is a vararg slot, and it does not have a name, then it is an error.
                            _errors.Add(new DiagnosticsEntry(Resources.Analysis_TooManyPositionalArgumentBeforeStar, arg.GetLocation(eval).Span,
                                ErrorCodes.TooManyPositionalArgumentsBeforeStar, Severity.Warning, DiagnosticSource.Analysis));
                            return;
                        }

                        // If the next unfilled slot is a vararg slot then all remaining
                        // non-keyword arguments are placed into the vararg slot.
                        if (_listArgument == null) {
                            _errors.Add(new DiagnosticsEntry(Resources.Analysis_TooManyFunctionArguments, arg.GetLocation(eval).Span,
                                ErrorCodes.TooManyFunctionArguments, Severity.Warning, DiagnosticSource.Analysis));
                            return;
                        }

                        for (; callParamIndex < callExpr.Args.Count; callParamIndex++) {
                            arg = callExpr.Args[callParamIndex];
                            if (!string.IsNullOrEmpty(arg.Name)) {
                                // Keyword argument. Done here.
                                break;
                            }

                            _listArgument._Expressions.Add(arg.Expression);
                        }

                        break; // Sequence or dictionary parameter found. Done here.
                    }

                    if (formalParam.Kind == ParameterKind.Dictionary) {
                        // Next slot is a dictionary slot, but we have positional arguments still.
                        _errors.Add(new DiagnosticsEntry(Resources.Analysis_TooManyPositionalArgumentBeforeStar, arg.GetLocation(eval).Span,
                            ErrorCodes.TooManyPositionalArgumentsBeforeStar, Severity.Warning, DiagnosticSource.Analysis));
                        return;
                    }

                    // Regular parameter
                    slots[formalParamIndex].ValueExpression = arg.Expression;
                }

                // Keyword arguments
                for (; callParamIndex < callExpr.Args.Count; callParamIndex++) {
                    var arg = callExpr.Args[callParamIndex];

                    if (string.IsNullOrEmpty(arg.Name)) {
                        _errors.Add(new DiagnosticsEntry(Resources.Analysis_PositionalArgumentAfterKeyword, arg.GetLocation(eval).Span,
                            ErrorCodes.PositionalArgumentAfterKeyword, Severity.Warning, DiagnosticSource.Analysis));
                        return;
                    }

                    var nvp = slots.FirstOrDefault(s => s.Name.EqualsOrdinal(arg.Name));
                    if (nvp == null) {
                        // 'def f(a, b)' and then 'f(0, c=1)'. Per spec:
                        // if there is a 'keyword dictionary' argument, the argument is added
                        // to the dictionary using the keyword name as the dictionary key,
                        // unless there is already an entry with that key, in which case it is an error.
                        if (_dictArgument == null) {
                            _errors.Add(new DiagnosticsEntry(Resources.Analysis_UnknownParameterName, arg.GetLocation(eval).Span,
                                ErrorCodes.UnknownParameterName, Severity.Warning, DiagnosticSource.Analysis));
                            return;
                        }

                        if (_dictArgument.Arguments.ContainsKey(arg.Name)) {
                            _errors.Add(new DiagnosticsEntry(Resources.Analysis_ParameterAlreadySpecified.FormatUI(arg.Name), arg.GetLocation(eval).Span,
                                ErrorCodes.ParameterAlreadySpecified, Severity.Warning, DiagnosticSource.Analysis));
                            return;
                        }

                        _dictArgument._Expressions[arg.Name] = arg.Expression;
                        continue;
                    }

                    if (nvp.Kind == ParameterKind.PositionalOnly) {
                        _errors.Add(new DiagnosticsEntry(Resources.Analysis_PositionalOnlyArgumentNamed.FormatInvariant(arg.Name), arg.GetLocation(eval).Span,
                            ErrorCodes.PositionalOnlyNamed, Severity.Warning, DiagnosticSource.Analysis));
                        return;
                    }

                    if (nvp.ValueExpression != null || nvp.Value != null) {
                        // Slot is already filled.
                        _errors.Add(new DiagnosticsEntry(Resources.Analysis_ParameterAlreadySpecified.FormatUI(arg.Name), arg.GetLocation(eval).Span,
                            ErrorCodes.ParameterAlreadySpecified, Severity.Warning, DiagnosticSource.Analysis));
                        return;
                    }

                    // OK keyword parameter
                    nvp.ValueExpression = arg.Expression;
                }

                // We went through all positionals and keywords.
                // For each remaining empty slot: if there is a default value for that slot,
                // then fill the slot with the default value. If there is no default value,
                // then it is an error.
                foreach (var slot in slots.Where(s => s.Kind != ParameterKind.List && s.Kind != ParameterKind.Dictionary && s.Value == null)) {
                    if (slot.ValueExpression == null) {
                        var parameter = overload.Parameters.First(p => p.Name == slot.Name);
                        if (parameter.DefaultValue == null) {
                            // TODO: parameter is not assigned and has no default value.
                            _errors.Add(new DiagnosticsEntry(Resources.Analysis_ParameterMissing.FormatUI(slot.Name), callLocation.Span,
                                ErrorCodes.ParameterMissing, Severity.Warning, DiagnosticSource.Analysis));
                        }
                        // Note that parameter default value expression is from the function definition AST
                        // while actual argument values are from the calling file AST.
                        slot.ValueExpression = CreateExpression(parameter.Name, parameter.DefaultValueString);
                        slot.Value = parameter.DefaultValue;
                        slot.ValueIsDefault = true;
                    }
                }
            } finally {
                // Optimistically return what we gathered, even if there are errors.
                _arguments = slots.Where(s => s.Kind != ParameterKind.List && s.Kind != ParameterKind.Dictionary).ToList();
            }
        }