|
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, Class cls, Function init) { |
| 25 | + resolveClassCall(call.getAFlowNode(), cls) and |
| 26 | + init = DuckTyping::getInit(cls) and |
| 27 | + exists(int positional_keywords | |
| 28 | + if init.hasKwArg() |
| 29 | + then positional_keywords = 0 |
| 30 | + else |
| 31 | + positional_keywords = |
| 32 | + count(Keyword kw | |
| 33 | + kw = call.getAKeyword() and |
| 34 | + not init.getAKeywordOnlyArg().getId() = kw.getArg() |
| 35 | + ) |
| 36 | + | |
| 37 | + result = |
| 38 | + count(call.getAnArg()) + count(call.getStarargs().(List).getAnElt()) + positional_keywords |
| 39 | + ) |
| 40 | +} |
| 41 | + |
| 42 | +/** |
| 43 | + * Holds if `call` constructs `cls` with too many arguments, where `limit` is the maximum. |
| 44 | + */ |
| 45 | +predicate too_many_args(Call call, Class cls, int limit) { |
| 46 | + exists(Function init | |
| 47 | + not init.hasVarArg() and |
| 48 | + // Subtract 1 from max to account for `self` parameter |
| 49 | + limit = init.getMaxPositionalArguments() - 1 and |
| 50 | + limit >= 0 and |
| 51 | + positional_arg_count(call, cls, init) > limit |
| 52 | + ) |
| 53 | +} |
| 54 | + |
| 55 | +/** |
| 56 | + * Holds if `call` constructs `cls` with too few arguments, where `limit` is the minimum. |
| 57 | + */ |
| 58 | +predicate too_few_args(Call call, Class cls, int limit) { |
| 59 | + resolveClassCall(call.getAFlowNode(), cls) and |
| 60 | + exists(Function init | |
| 61 | + init = DuckTyping::getInit(cls) and |
| 62 | + not exists(call.getStarargs()) and |
| 63 | + not exists(call.getKwargs()) and |
| 64 | + // Subtract 1 from min to account for `self` parameter |
| 65 | + limit = init.getMinPositionalArguments() - 1 and |
| 66 | + count(call.getAnArg()) + count(call.getAKeyword()) < limit |
| 67 | + ) |
| 68 | +} |
| 69 | + |
| 70 | +from Call call, Class cls, string too, string should, int limit, Function init |
21 | 71 | where |
22 | 72 | ( |
23 | 73 | too_many_args(call, cls, limit) and |
|
28 | 78 | too = "too few arguments" and |
29 | 79 | should = "no fewer than " |
30 | 80 | ) and |
31 | | - init = get_function_or_initializer(cls) |
| 81 | + init = DuckTyping::getInit(cls) |
32 | 82 | select call, "Call to $@ with " + too + "; should be " + should + limit.toString() + ".", init, |
33 | 83 | init.getQualifiedName() |
0 commit comments