Extracted getParameters(Callable, TypeEvalContext)

This commit is contained in:
Andrey Vlasovskikh
2013-08-05 16:00:44 +04:00
parent 3d2c8f555c
commit dd431693ff
2 changed files with 266 additions and 256 deletions
@@ -1238,5 +1238,32 @@ public class PyUtil {
}
return element;
}
}
@NotNull
public static List<PyParameter> getParameters(@NotNull Callable callable, @NotNull TypeEvalContext context) {
PyType type = context.getType(callable);
if (type instanceof PyUnionType) {
type = ((PyUnionType)type).excludeNull();
}
if (type instanceof PyCallableType) {
final PyCallableType callableType = (PyCallableType)type;
final List<PyCallableParameter> callableTypeParameters = callableType.getParameters(context);
if (callableTypeParameters != null) {
boolean allParametersDefined = true;
final List<PyParameter> parameters = new ArrayList<PyParameter>();
for (PyCallableParameter callableParameter : callableTypeParameters) {
final PyParameter parameter = callableParameter.getParameter();
if (parameter == null) {
allParametersDefined = false;
break;
}
parameters.add(parameter);
}
if (allParametersDefined) {
return parameters;
}
}
}
return Arrays.asList(callable.getParameterList().getParameters());
}
}
@@ -73,280 +73,263 @@ public class CallArgumentsMappingImpl implements CallArgumentsMapping {
}
}
}
PyType type = context.getType(myMarkedCallee.getCallable());
if (type instanceof PyUnionType) {
type = ((PyUnionType)type).excludeNull();
}
if (type instanceof PyCallableType) {
final PyCallableType callableType = (PyCallableType)type;
final List<PyCallableParameter> callableTypeParameters = callableType.getParameters(context);
if (callableTypeParameters != null) {
final List<PyParameter> parameters = new ArrayList<PyParameter>();
for (PyCallableParameter callableParameter : callableTypeParameters) {
final PyParameter parameter = callableParameter.getParameter();
if (parameter == null) {
return;
final List<PyParameter> parameters = PyUtil.getParameters(myMarkedCallee.getCallable(), context);
// prepare parameter slots
Map<PyNamedParameter, PyExpression> slots = new LinkedHashMap<PyNamedParameter, PyExpression>();
PyNamedParameter kwd_par = null; // **param
PyNamedParameter tuple_par = null; // *param
Set<PyExpression> mapped_args = new HashSet<PyExpression>();
final int implicit_offset = resolved_callee.getImplicitOffset();
int positional_index = 0; // up to this index parameters are positional
// check positional arguments, fill slots
int i = 0;
for (PyParameter par : parameters) {
if (tuple_par == null && kwd_par == null && positional_index < implicit_offset) {
positional_index += 1;
continue;
}
PyNamedParameter n_par = par.getAsNamed();
if (n_par != null) {
if (n_par.isPositionalContainer()) tuple_par = n_par;
else if (n_par.isKeywordContainer()) kwd_par = n_par;
else {
slots.put(n_par, null); // regular parameter that may serve as positional/named
if (tuple_par == null && kwd_par == null) {
positional_index += 1; // only if we're not past *param / **param
}
parameters.add(parameter);
}
// prepare parameter slots
Map<PyNamedParameter, PyExpression> slots = new LinkedHashMap<PyNamedParameter, PyExpression>();
PyNamedParameter kwd_par = null; // **param
PyNamedParameter tuple_par = null; // *param
Set<PyExpression> mapped_args = new HashSet<PyExpression>();
final int implicit_offset = resolved_callee.getImplicitOffset();
int positional_index = 0; // up to this index parameters are positional
// check positional arguments, fill slots
int i = 0;
for (PyParameter par : parameters) {
if (tuple_par == null && kwd_par == null && positional_index < implicit_offset) {
positional_index += 1;
continue;
}
PyNamedParameter n_par = par.getAsNamed();
if (n_par != null) {
if (n_par.isPositionalContainer()) tuple_par = n_par;
else if (n_par.isKeywordContainer()) kwd_par = n_par;
}
else {
PyTupleParameter t_par = par.getAsTuple();
if (t_par != null) positional_index += 1; // tuple can only be positional
// else lone star, skip
}
i += 1;
}
// rule out 'self' or other implicit params
for (i=0; i < implicit_offset && i < parameters.size(); i+=1) {
slots.remove(parameters.get(i).getAsNamed());
positional_index += 1;
}
// now params to the left of positional_index are positional.
// map positional args to positional params.
// we assume that implicitly skipped parameters are never nested tuples. no idea when they could ever be.
int cnt = implicit_offset;
int positional_bound = arguments.length; // to the right of this pos args are verboten
ListIterator<PyExpression> unmatched_arg_iter = unmatched_args.listIterator();
while (unmatched_arg_iter.hasNext()) {
PyExpression arg = unmatched_arg_iter.next();
if (arg instanceof PyStarArgument || arg instanceof PyKeywordArgument) {
positional_bound = cnt;
break;
}
if (cnt < parameters.size() && cnt < positional_index) {
final PyParameter par = parameters.get(cnt);
PyNamedParameter n_par = par.getAsNamed();
if (n_par != null) {
cnt += 1;
slots.put(n_par, PyUtil.peelArgument(arg));
mapped_args.add(arg);
}
else {
PyTupleParameter t_par = par.getAsTuple();
if (t_par != null) {
if (arg instanceof PyParenthesizedExpression) {
mapped_args.add(arg); // tuple itself is always mapped; its insides can fail
}
else {
slots.put(n_par, null); // regular parameter that may serve as positional/named
if (tuple_par == null && kwd_par == null) {
positional_index += 1; // only if we're not past *param / **param
PyType arg_type = context.getType(arg);
if (arg_type != null && arg_type.isBuiltin(context) && "list".equals(arg_type.getName())) {
mapped_args.add(arg); // we can't really analyze arbitrary lists statically yet
// but ListLiteralExpressions are handled by visitor
}
}
unmatched_arg_iter.previous();
MyParamVisitor visitor = new MyParamVisitor(unmatched_arg_iter, this);
visitor.enterTuple(t_par.getAsTuple()); // will recur as needed
unmatched_subargs.addAll(visitor.getUnmatchedSubargs()); // what didn't match inside
cnt += 1;
}
else {
PyTupleParameter t_par = par.getAsTuple();
if (t_par != null) positional_index += 1; // tuple can only be positional
// else lone star, skip
// else: goes to *param
}
}
else break;
}
// anything left after mapping of nested-tuple params?
for (Map.Entry<PyExpression, List<PyNamedParameter>> pair : myNestedMappedParams.entrySet()) {
PyExpression arg = pair.getKey();
List<PyNamedParameter> params = pair.getValue();
mapped_args.add(arg);
for (PyNamedParameter n_par : params) slots.remove(n_par);
}
for (PyExpression arg : unmatched_subargs) {
markArgument(arg, ArgFlag.IS_UNMAPPED);
}
// mark past-bound positional args
i = positional_bound;
while (i<arguments.length) {
PyExpression arg = arguments[i];
if (!(arg instanceof PyStarArgument) && !(arg instanceof PyKeywordArgument)) {
markArgument(arg, ArgFlag.IS_POS_PAST_KWD);
}
i += 1;
}
boolean seen_named_args = false;
// map named args to named params if possible
Map<String, PyNamedParameter> parameter_by_name = new LinkedHashMap<String, PyNamedParameter>();
for (PyParameter par : parameters) {
PyNamedParameter n_par = par.getAsNamed();
if (n_par != null) parameter_by_name.put(n_par.getName(), n_par);
}
for (PyExpression arg : arguments) {
if (arg instanceof PyKeywordArgument) { // to explicitly named param?
String arg_name = ((PyKeywordArgument)arg).getKeyword();
PyNamedParameter respective_par = parameter_by_name.get(arg_name);
if (respective_par != null && !respective_par.isKeywordContainer() && !respective_par.isPositionalContainer()) {
if (slots.get(respective_par) != null) markArgument(arg, ArgFlag.IS_DUP);
else slots.put(respective_par, arg);
}
else { // to **param?
if (kwd_par != null) {
myPlainMappedParams.put(arg, kwd_par);
mapped_args.add(arg);
}
i += 1;
}
// rule out 'self' or other implicit params
for (i=0; i < implicit_offset && i < parameters.size(); i+=1) {
slots.remove(parameters.get(i).getAsNamed());
positional_index += 1;
}
// now params to the left of positional_index are positional.
// map positional args to positional params.
// we assume that implicitly skipped parameters are never nested tuples. no idea when they could ever be.
int cnt = implicit_offset;
int positional_bound = arguments.length; // to the right of this pos args are verboten
ListIterator<PyExpression> unmatched_arg_iter = unmatched_args.listIterator();
while (unmatched_arg_iter.hasNext()) {
PyExpression arg = unmatched_arg_iter.next();
if (arg instanceof PyStarArgument || arg instanceof PyKeywordArgument) {
positional_bound = cnt;
seen_named_args = true;
}
}
// map *arg to positional params if possible
boolean tuple_arg_not_exhausted = false;
boolean tuple_dup_found = false;
if (cnt < parameters.size() && cnt < positional_index && myTupleArg != null) {
// check length of myTupleArg
PyType tuple_arg_type = null;
final PyExpression expression = PsiTreeUtil.getChildOfType(myTupleArg, PyExpression.class);
if (expression != null) {
tuple_arg_type = context.getType(expression);
}
int tuple_length;
boolean tuple_length_known;
if (tuple_arg_type instanceof PyTupleType) {
tuple_length = ((PyTupleType)tuple_arg_type).getElementCount();
tuple_length_known = true;
}
else {
tuple_length = 2000000; // no practical function will have so many positional params
tuple_length_known = false;
}
int mapped_params_count = 0;
while (cnt < parameters.size() && cnt < positional_index && mapped_params_count < tuple_length) {
PyParameter par = parameters.get(cnt);
if (par instanceof PySingleStarParameter) break;
PyNamedParameter n_par = par.getAsNamed();
if (slots.containsKey(n_par)) {
final PyExpression arg_here = slots.get(n_par);
if (arg_here != null) {
if (tuple_length_known) {
final EnumSet<ArgFlag> flags = myArgFlags.get(arg_here);
if (flags == null || flags.isEmpty()) {
markArgument(arg_here, ArgFlag.IS_DUP);
tuple_dup_found = true;
}
}
// else: unknown tuple length is just enough
// the spree is over
break;
}
if (cnt < parameters.size() && cnt < positional_index) {
final PyParameter par = parameters.get(cnt);
PyNamedParameter n_par = par.getAsNamed();
if (n_par != null) {
cnt += 1;
slots.put(n_par, PyUtil.peelArgument(arg));
mapped_args.add(arg);
}
else {
PyTupleParameter t_par = par.getAsTuple();
if (t_par != null) {
if (arg instanceof PyParenthesizedExpression) {
mapped_args.add(arg); // tuple itself is always mapped; its insides can fail
}
else {
PyType arg_type = context.getType(arg);
if (arg_type != null && arg_type.isBuiltin(context) && "list".equals(arg_type.getName())) {
mapped_args.add(arg); // we can't really analyze arbitrary lists statically yet
// but ListLiteralExpressions are handled by visitor
}
}
unmatched_arg_iter.previous();
MyParamVisitor visitor = new MyParamVisitor(unmatched_arg_iter, this);
visitor.enterTuple(t_par.getAsTuple()); // will recur as needed
unmatched_subargs.addAll(visitor.getUnmatchedSubargs()); // what didn't match inside
cnt += 1;
}
// else: goes to *param
}
else if (n_par != null) { // normally always true
myTupleMappedParams.add(n_par);
mapped_args.add(myTupleArg);
slots.remove(n_par);
}
else break;
}
// anything left after mapping of nested-tuple params?
for (Map.Entry<PyExpression, List<PyNamedParameter>> pair : myNestedMappedParams.entrySet()) {
PyExpression arg = pair.getKey();
List<PyNamedParameter> params = pair.getValue();
mapped_args.add(arg);
for (PyNamedParameter n_par : params) slots.remove(n_par);
else if (n_par == tuple_par) {
mapped_params_count = tuple_length; // we found *param for our *arg, consider it fully mapped
break;
}
for (PyExpression arg : unmatched_subargs) {
markArgument(arg, ArgFlag.IS_UNMAPPED);
}
// mark past-bound positional args
i = positional_bound;
while (i<arguments.length) {
PyExpression arg = arguments[i];
if (!(arg instanceof PyStarArgument) && !(arg instanceof PyKeywordArgument)) {
markArgument(arg, ArgFlag.IS_POS_PAST_KWD);
}
cnt += 1;
mapped_params_count += 1;
}
if (
tuple_length_known && (mapped_params_count < tuple_length) || // not exhausted
mapped_params_count == 0 // unknown length must consume at least first param
) {
tuple_arg_not_exhausted = true;
}
}
// map *param to the leftmost chunk of unmapped positional args
// NOTE: ignores the structure of nested-tuple params!
if (tuple_par != null) {
i = 0;
while (i < arguments.length && mapped_args.contains(arguments[i]) && isPositionalArg(arguments[i])) {
i += 1; // skip first mapped args
}
if (i < arguments.length && isPositionalArg(arguments[i])) {
while (i < arguments.length && !mapped_args.contains(arguments[i]) && isPositionalArg(arguments[i])) {
myPlainMappedParams.put(arguments[i], tuple_par);
mapped_args.add(arguments[i]);
i += 1;
}
boolean seen_named_args = false;
// map named args to named params if possible
Map<String, PyNamedParameter> parameter_by_name = new LinkedHashMap<String, PyNamedParameter>();
for (PyParameter par : parameters) {
PyNamedParameter n_par = par.getAsNamed();
if (n_par != null) parameter_by_name.put(n_par.getName(), n_par);
}
}
// map unmapped *arg to *param
if (myTupleArg != null && tuple_par != null) {
if (!mapped_args.contains(myTupleArg)) {
myTupleMappedParams.add(tuple_par);
mapped_args.add(myTupleArg);
}
else if (! seen_named_args && tuple_arg_not_exhausted) {
// case of (*(1, 2, 3)) -> (a, *b); map the rest of *arg to *param
myTupleMappedParams.add(tuple_par);
mapped_args.add(myTupleArg);
tuple_arg_not_exhausted = false;
}
}
if (tuple_arg_not_exhausted && ! tuple_dup_found) {
markArgument(myTupleArg, ArgFlag.IS_TOO_LONG);
}
// map unmapped named params to **kwarg
if (myKwdArg != null) {
for (PyParameter par : parameters) {
PyNamedParameter n_par = par.getAsNamed();
if (n_par != null && !n_par.isKeywordContainer() && !n_par.isPositionalContainer() && slots.get(n_par) == null) {
slots.put(n_par, myKwdArg);
}
for (PyExpression arg : arguments) {
if (arg instanceof PyKeywordArgument) { // to explicitly named param?
String arg_name = ((PyKeywordArgument)arg).getKeyword();
PyNamedParameter respective_par = parameter_by_name.get(arg_name);
if (respective_par != null && !respective_par.isKeywordContainer() && !respective_par.isPositionalContainer()) {
if (slots.get(respective_par) != null) markArgument(arg, ArgFlag.IS_DUP);
else slots.put(respective_par, arg);
}
else { // to **param?
if (kwd_par != null) {
myPlainMappedParams.put(arg, kwd_par);
mapped_args.add(arg);
}
}
seen_named_args = true;
}
}
}
// map unmapped **kwarg to **param
if (myKwdArg != null && kwd_par != null && !mapped_args.contains(myKwdArg)) {
myKwdMappedParams.add(kwd_par);
mapped_args.add(myKwdArg);
}
// fill in ret, mark unmapped named params
for (Map.Entry<PyNamedParameter, PyExpression> pair : slots.entrySet()) {
PyNamedParameter n_par = pair.getKey();
PyExpression arg = pair.getValue();
if (arg == null) {
if (!n_par.hasDefaultValue()) myUnmappedParams.add(n_par);
}
else {
if (arg == myTupleArg) {
myTupleMappedParams.add(n_par);
}
// map *arg to positional params if possible
boolean tuple_arg_not_exhausted = false;
boolean tuple_dup_found = false;
if (cnt < parameters.size() && cnt < positional_index && myTupleArg != null) {
// check length of myTupleArg
PyType tuple_arg_type = null;
final PyExpression expression = PsiTreeUtil.getChildOfType(myTupleArg, PyExpression.class);
if (expression != null) {
tuple_arg_type = context.getType(expression);
}
int tuple_length;
boolean tuple_length_known;
if (tuple_arg_type instanceof PyTupleType) {
tuple_length = ((PyTupleType)tuple_arg_type).getElementCount();
tuple_length_known = true;
}
else {
tuple_length = 2000000; // no practical function will have so many positional params
tuple_length_known = false;
}
int mapped_params_count = 0;
while (cnt < parameters.size() && cnt < positional_index && mapped_params_count < tuple_length) {
PyParameter par = parameters.get(cnt);
if (par instanceof PySingleStarParameter) break;
PyNamedParameter n_par = par.getAsNamed();
if (slots.containsKey(n_par)) {
final PyExpression arg_here = slots.get(n_par);
if (arg_here != null) {
if (tuple_length_known) {
final EnumSet<ArgFlag> flags = myArgFlags.get(arg_here);
if (flags == null || flags.isEmpty()) {
markArgument(arg_here, ArgFlag.IS_DUP);
tuple_dup_found = true;
}
}
// else: unknown tuple length is just enough
// the spree is over
break;
}
else if (n_par != null) { // normally always true
myTupleMappedParams.add(n_par);
mapped_args.add(myTupleArg);
slots.remove(n_par);
}
}
else if (n_par == tuple_par) {
mapped_params_count = tuple_length; // we found *param for our *arg, consider it fully mapped
break;
}
cnt += 1;
mapped_params_count += 1;
}
if (
tuple_length_known && (mapped_params_count < tuple_length) || // not exhausted
mapped_params_count == 0 // unknown length must consume at least first param
) {
tuple_arg_not_exhausted = true;
}
else if (arg == myKwdArg) {
myKwdMappedParams.add(n_par);
}
// map *param to the leftmost chunk of unmapped positional args
// NOTE: ignores the structure of nested-tuple params!
if (tuple_par != null) {
i = 0;
while (i < arguments.length && mapped_args.contains(arguments[i]) && isPositionalArg(arguments[i])) {
i += 1; // skip first mapped args
}
if (i < arguments.length && isPositionalArg(arguments[i])) {
while (i < arguments.length && !mapped_args.contains(arguments[i]) && isPositionalArg(arguments[i])) {
myPlainMappedParams.put(arguments[i], tuple_par);
mapped_args.add(arguments[i]);
i += 1;
}
}
else {
myPlainMappedParams.put(arg, n_par);
}
// map unmapped *arg to *param
if (myTupleArg != null && tuple_par != null) {
if (!mapped_args.contains(myTupleArg)) {
myTupleMappedParams.add(tuple_par);
mapped_args.add(myTupleArg);
}
else if (! seen_named_args && tuple_arg_not_exhausted) {
// case of (*(1, 2, 3)) -> (a, *b); map the rest of *arg to *param
myTupleMappedParams.add(tuple_par);
mapped_args.add(myTupleArg);
tuple_arg_not_exhausted = false;
}
}
if (tuple_arg_not_exhausted && ! tuple_dup_found) {
markArgument(myTupleArg, ArgFlag.IS_TOO_LONG);
}
// map unmapped named params to **kwarg
if (myKwdArg != null) {
for (PyParameter par : parameters) {
PyNamedParameter n_par = par.getAsNamed();
if (n_par != null && !n_par.isKeywordContainer() && !n_par.isPositionalContainer() && slots.get(n_par) == null) {
slots.put(n_par, myKwdArg);
}
}
}
// map unmapped **kwarg to **param
if (myKwdArg != null && kwd_par != null && !mapped_args.contains(myKwdArg)) {
myKwdMappedParams.add(kwd_par);
mapped_args.add(myKwdArg);
}
// fill in ret, mark unmapped named params
for (Map.Entry<PyNamedParameter, PyExpression> pair : slots.entrySet()) {
PyNamedParameter n_par = pair.getKey();
PyExpression arg = pair.getValue();
if (arg == null) {
if (!n_par.hasDefaultValue()) myUnmappedParams.add(n_par);
}
else {
if (arg == myTupleArg) {
myTupleMappedParams.add(n_par);
}
else if (arg == myKwdArg) {
myKwdMappedParams.add(n_par);
}
else {
myPlainMappedParams.put(arg, n_par);
}
}
}
// mark unmapped args
for (PyExpression arg : slots.values()) {
if (arg != null) mapped_args.add(arg);
}
for (PyExpression arg : arguments) {
if (!mapped_args.contains(arg)) {
final EnumSet<ArgFlag> flags = myArgFlags.get(arg);
if (flags == null || flags.isEmpty()) {
markArgument(arg, ArgFlag.IS_UNMAPPED);
}
}
}
}
// mark unmapped args
for (PyExpression arg : slots.values()) {
if (arg != null) mapped_args.add(arg);
}
for (PyExpression arg : arguments) {
if (!mapped_args.contains(arg)) {
final EnumSet<ArgFlag> flags = myArgFlags.get(arg);
if (flags == null || flags.isEmpty()) {
markArgument(arg, ArgFlag.IS_UNMAPPED);
}
}
}