|
14 | 14 | */ |
15 | 15 |
|
16 | 16 | import python |
17 | | -import Expressions.CallArgs |
18 | | -private import LegacyPointsTo |
| 17 | +private import semmle.python.dataflow.new.internal.DataFlowDispatch |
19 | 18 |
|
20 | | -from Call call, ClassValue cls, string too, string should, int limit, FunctionValue init |
| 19 | +/** |
| 20 | + * Gets the number of positional arguments in `call`, including elements of any |
| 21 | + * literal list passed as `*args`, plus keyword arguments that don't match |
| 22 | + * keyword-only parameters (when the function doesn't accept `**kwargs`). |
| 23 | + */ |
| 24 | +int positional_arg_count(Call call, Function init) { |
| 25 | + exists(int positional_keywords | |
| 26 | + ( |
| 27 | + not init.hasKwArg() and |
| 28 | + positional_keywords = |
| 29 | + count(Keyword kw | |
| 30 | + kw = call.getAKeyword() and |
| 31 | + not init.getAKeywordOnlyArg().getId() = kw.getArg() |
| 32 | + ) |
| 33 | + or |
| 34 | + init.hasKwArg() and positional_keywords = 0 |
| 35 | + ) and |
| 36 | + result = |
| 37 | + count(call.getAnArg()) + count(call.getStarargs().(List).getAnElt()) + positional_keywords |
| 38 | + ) |
| 39 | +} |
| 40 | + |
| 41 | +/** |
| 42 | + * Holds if `call` constructs `cls` with too many arguments, where `limit` is the maximum. |
| 43 | + */ |
| 44 | +predicate too_many_args(Call call, Class cls, int limit) { |
| 45 | + exists(Function init | |
| 46 | + resolveClassCall(call.getAFlowNode(), cls) and |
| 47 | + init = DuckTyping::getInit(cls) and |
| 48 | + not init.hasVarArg() and |
| 49 | + // Subtract 1 from max to account for `self` parameter |
| 50 | + limit = init.getMaxPositionalArguments() - 1 and |
| 51 | + limit >= 0 and |
| 52 | + positional_arg_count(call, init) > limit |
| 53 | + ) |
| 54 | +} |
| 55 | + |
| 56 | +/** |
| 57 | + * Holds if `call` constructs `cls` with too few arguments, where `limit` is the minimum. |
| 58 | + */ |
| 59 | +predicate too_few_args(Call call, Class cls, int limit) { |
| 60 | + exists(Function init | |
| 61 | + resolveClassCall(call.getAFlowNode(), cls) and |
| 62 | + init = DuckTyping::getInit(cls) and |
| 63 | + not exists(call.getStarargs()) and |
| 64 | + not exists(call.getKwargs()) and |
| 65 | + // Subtract 1 from min to account for `self` parameter |
| 66 | + limit = init.getMinPositionalArguments() - 1 and |
| 67 | + count(call.getAnArg()) + count(call.getAKeyword()) < limit |
| 68 | + ) |
| 69 | +} |
| 70 | + |
| 71 | +from Call call, Class cls, string too, string should, int limit, Function init |
21 | 72 | where |
22 | 73 | ( |
23 | 74 | too_many_args(call, cls, limit) and |
|
28 | 79 | too = "too few arguments" and |
29 | 80 | should = "no fewer than " |
30 | 81 | ) and |
31 | | - init = get_function_or_initializer(cls) |
| 82 | + init = DuckTyping::getInit(cls) |
32 | 83 | select call, "Call to $@ with " + too + "; should be " + should + limit.toString() + ".", init, |
33 | 84 | init.getQualifiedName() |
0 commit comments