mirror of
https://github.com/github/codeql.git
synced 2026-01-04 18:20:18 +01:00
Will need subsequent PRs fixing up test failures (due to deprecated methods moving around), but other than that everything should be straight-forward.
269 lines
11 KiB
Plaintext
269 lines
11 KiB
Plaintext
/** INTERNAL - Methods used by queries that test whether functions are invoked correctly. */
|
|
|
|
import python
|
|
import Testing.Mox
|
|
|
|
private int varargs_length_objectapi(Call call) {
|
|
not exists(call.getStarargs()) and result = 0
|
|
or
|
|
exists(TupleObject t | call.getStarargs().refersTo(t) | result = t.getLength())
|
|
or
|
|
result = count(call.getStarargs().(List).getAnElt())
|
|
}
|
|
|
|
private int varargs_length(Call call) {
|
|
not exists(call.getStarargs()) and result = 0
|
|
or
|
|
exists(TupleValue t | call.getStarargs().pointsTo(t) | result = t.length())
|
|
or
|
|
result = count(call.getStarargs().(List).getAnElt())
|
|
}
|
|
|
|
/** Gets a keyword argument that is not a keyword-only parameter. */
|
|
private Keyword not_keyword_only_arg_objectapi(Call call, FunctionObject func) {
|
|
func.getACall().getNode() = call and
|
|
result = call.getAKeyword() and
|
|
not func.getFunction().getAKeywordOnlyArg().getId() = result.getArg()
|
|
}
|
|
|
|
/** Gets a keyword argument that is not a keyword-only parameter. */
|
|
private Keyword not_keyword_only_arg(Call call, FunctionValue func) {
|
|
func.getACall().getNode() = call and
|
|
result = call.getAKeyword() and
|
|
not func.getScope().getAKeywordOnlyArg().getId() = result.getArg()
|
|
}
|
|
|
|
/**
|
|
* Gets the count of arguments that are passed as positional parameters even if they
|
|
* are named in the call.
|
|
* This is the sum of the number of positional arguments, the number of elements in any explicit tuple passed as *arg
|
|
* plus the number of keyword arguments that do not match keyword-only arguments (if the function does not take **kwargs).
|
|
*/
|
|
private int positional_arg_count_for_call_objectapi(Call call, Object callable) {
|
|
call = get_a_call_objectapi(callable).getNode() and
|
|
exists(int positional_keywords |
|
|
exists(FunctionObject func | func = get_function_or_initializer_objectapi(callable) |
|
|
not func.getFunction().hasKwArg() and
|
|
positional_keywords = count(not_keyword_only_arg_objectapi(call, func))
|
|
or
|
|
func.getFunction().hasKwArg() and positional_keywords = 0
|
|
)
|
|
|
|
|
result = count(call.getAnArg()) + varargs_length_objectapi(call) + positional_keywords
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Gets the count of arguments that are passed as positional parameters even if they
|
|
* are named in the call.
|
|
* This is the sum of the number of positional arguments, the number of elements in any explicit tuple passed as *arg
|
|
* plus the number of keyword arguments that do not match keyword-only arguments (if the function does not take **kwargs).
|
|
*/
|
|
private int positional_arg_count_for_call(Call call, Value callable) {
|
|
call = get_a_call(callable).getNode() and
|
|
exists(int positional_keywords |
|
|
exists(FunctionValue func | func = get_function_or_initializer(callable) |
|
|
not func.getScope().hasKwArg() and
|
|
positional_keywords = count(not_keyword_only_arg(call, func))
|
|
or
|
|
func.getScope().hasKwArg() and positional_keywords = 0
|
|
)
|
|
|
|
|
result = count(call.getAnArg()) + varargs_length_objectapi(call) + positional_keywords
|
|
)
|
|
}
|
|
|
|
/** Gets the number of arguments in `call`. */
|
|
int arg_count_objectapi(Call call) {
|
|
result = count(call.getAnArg()) + varargs_length_objectapi(call) + count(call.getAKeyword())
|
|
}
|
|
|
|
/** Gets the number of arguments in `call`. */
|
|
int arg_count(Call call) {
|
|
result = count(call.getAnArg()) + varargs_length(call) + count(call.getAKeyword())
|
|
}
|
|
|
|
/** Gets a call corresponding to the given class or function. */
|
|
private ControlFlowNode get_a_call_objectapi(Object callable) {
|
|
result = callable.(ClassObject).getACall()
|
|
or
|
|
result = callable.(FunctionObject).getACall()
|
|
}
|
|
|
|
/** Gets a call corresponding to the given class or function. */
|
|
private ControlFlowNode get_a_call(Value callable) {
|
|
result = callable.(ClassValue).getACall()
|
|
or
|
|
result = callable.(FunctionValue).getACall()
|
|
}
|
|
|
|
/** Gets the function object corresponding to the given class or function. */
|
|
FunctionObject get_function_or_initializer_objectapi(Object func_or_cls) {
|
|
result = func_or_cls.(FunctionObject)
|
|
or
|
|
result = func_or_cls.(ClassObject).declaredAttribute("__init__")
|
|
}
|
|
|
|
/** Gets the function object corresponding to the given class or function. */
|
|
FunctionValue get_function_or_initializer(Value func_or_cls) {
|
|
result = func_or_cls.(FunctionValue)
|
|
or
|
|
result = func_or_cls.(ClassValue).declaredAttribute("__init__")
|
|
}
|
|
|
|
/**Whether there is an illegally named parameter called `name` in the `call` to `func` */
|
|
predicate illegally_named_parameter_objectapi(Call call, Object func, string name) {
|
|
not func.isC() and
|
|
name = call.getANamedArgumentName() and
|
|
call.getAFlowNode() = get_a_call_objectapi(func) and
|
|
not get_function_or_initializer_objectapi(func).isLegalArgumentName(name)
|
|
}
|
|
|
|
/**Whether there is an illegally named parameter called `name` in the `call` to `func` */
|
|
predicate illegally_named_parameter(Call call, Value func, string name) {
|
|
not func.isBuiltin() and
|
|
name = call.getANamedArgumentName() and
|
|
call.getAFlowNode() = get_a_call(func) and
|
|
not get_function_or_initializer(func).isLegalArgumentName(name)
|
|
}
|
|
|
|
/**Whether there are too few arguments in the `call` to `callable` where `limit` is the lowest number of legal arguments */
|
|
predicate too_few_args_objectapi(Call call, Object callable, int limit) {
|
|
// Exclude cases where an incorrect name is used as that is covered by 'Wrong name for an argument in a call'
|
|
not illegally_named_parameter_objectapi(call, callable, _) and
|
|
not exists(call.getStarargs()) and
|
|
not exists(call.getKwargs()) and
|
|
arg_count_objectapi(call) < limit and
|
|
exists(FunctionObject func | func = get_function_or_initializer_objectapi(callable) |
|
|
call = func.getAFunctionCall().getNode() and
|
|
limit = func.minParameters() and
|
|
// The combination of misuse of `mox.Mox().StubOutWithMock()`
|
|
// and a bug in mox's implementation of methods results in having to
|
|
// pass 1 too few arguments to the mocked function.
|
|
not (useOfMoxInModule(call.getEnclosingModule()) and func.isNormalMethod())
|
|
or
|
|
call = func.getAMethodCall().getNode() and limit = func.minParameters() - 1
|
|
or
|
|
callable instanceof ClassObject and
|
|
call.getAFlowNode() = get_a_call_objectapi(callable) and
|
|
limit = func.minParameters() - 1
|
|
)
|
|
}
|
|
|
|
/**Whether there are too few arguments in the `call` to `callable` where `limit` is the lowest number of legal arguments */
|
|
predicate too_few_args(Call call, Value callable, int limit) {
|
|
// Exclude cases where an incorrect name is used as that is covered by 'Wrong name for an argument in a call'
|
|
not illegally_named_parameter(call, callable, _) and
|
|
not exists(call.getStarargs()) and
|
|
not exists(call.getKwargs()) and
|
|
arg_count(call) < limit and
|
|
exists(FunctionValue func | func = get_function_or_initializer(callable) |
|
|
call = func.getAFunctionCall().getNode() and
|
|
limit = func.minParameters() and
|
|
/*
|
|
* The combination of misuse of `mox.Mox().StubOutWithMock()`
|
|
* and a bug in mox's implementation of methods results in having to
|
|
* pass 1 too few arguments to the mocked function.
|
|
*/
|
|
|
|
not (useOfMoxInModule(call.getEnclosingModule()) and func.isNormalMethod())
|
|
or
|
|
call = func.getAMethodCall().getNode() and limit = func.minParameters() - 1
|
|
or
|
|
callable instanceof ClassValue and
|
|
call.getAFlowNode() = get_a_call(callable) and
|
|
limit = func.minParameters() - 1
|
|
)
|
|
}
|
|
|
|
/**Whether there are too many arguments in the `call` to `func` where `limit` is the highest number of legal arguments */
|
|
predicate too_many_args_objectapi(Call call, Object callable, int limit) {
|
|
// Exclude cases where an incorrect name is used as that is covered by 'Wrong name for an argument in a call'
|
|
not illegally_named_parameter_objectapi(call, callable, _) and
|
|
exists(FunctionObject func |
|
|
func = get_function_or_initializer_objectapi(callable) and
|
|
not func.getFunction().hasVarArg() and
|
|
limit >= 0
|
|
|
|
|
call = func.getAFunctionCall().getNode() and limit = func.maxParameters()
|
|
or
|
|
call = func.getAMethodCall().getNode() and limit = func.maxParameters() - 1
|
|
or
|
|
callable instanceof ClassObject and
|
|
call.getAFlowNode() = get_a_call_objectapi(callable) and
|
|
limit = func.maxParameters() - 1
|
|
) and
|
|
positional_arg_count_for_call_objectapi(call, callable) > limit
|
|
}
|
|
|
|
/**Whether there are too many arguments in the `call` to `func` where `limit` is the highest number of legal arguments */
|
|
predicate too_many_args(Call call, Value callable, int limit) {
|
|
// Exclude cases where an incorrect name is used as that is covered by 'Wrong name for an argument in a call'
|
|
not illegally_named_parameter(call, callable, _) and
|
|
exists(FunctionValue func |
|
|
func = get_function_or_initializer(callable) and
|
|
not func.getScope().hasVarArg() and
|
|
limit >= 0
|
|
|
|
|
call = func.getAFunctionCall().getNode() and limit = func.maxParameters()
|
|
or
|
|
call = func.getAMethodCall().getNode() and limit = func.maxParameters() - 1
|
|
or
|
|
callable instanceof ClassValue and
|
|
call.getAFlowNode() = get_a_call(callable) and
|
|
limit = func.maxParameters() - 1
|
|
) and
|
|
positional_arg_count_for_call(call, callable) > limit
|
|
}
|
|
|
|
/** Holds if `call` has too many or too few arguments for `func` */
|
|
predicate wrong_args_objectapi(Call call, FunctionObject func, int limit, string too) {
|
|
too_few_args_objectapi(call, func, limit) and too = "too few"
|
|
or
|
|
too_many_args_objectapi(call, func, limit) and too = "too many"
|
|
}
|
|
|
|
/** Holds if `call` has too many or too few arguments for `func` */
|
|
predicate wrong_args(Call call, FunctionValue func, int limit, string too) {
|
|
too_few_args(call, func, limit) and too = "too few"
|
|
or
|
|
too_many_args(call, func, limit) and too = "too many"
|
|
}
|
|
|
|
/**
|
|
* Holds if `call` has correct number of arguments for `func`.
|
|
* Implies nothing about whether `call` could call `func`.
|
|
*/
|
|
bindingset[call, func]
|
|
predicate correct_args_if_called_as_method_objectapi(Call call, FunctionObject func) {
|
|
arg_count_objectapi(call) + 1 >= func.minParameters() and
|
|
arg_count_objectapi(call) < func.maxParameters()
|
|
}
|
|
|
|
/**
|
|
* Holds if `call` has correct number of arguments for `func`.
|
|
* Implies nothing about whether `call` could call `func`.
|
|
*/
|
|
bindingset[call, func]
|
|
predicate correct_args_if_called_as_method(Call call, FunctionValue func) {
|
|
arg_count(call) + 1 >= func.minParameters() and
|
|
arg_count(call) < func.maxParameters()
|
|
}
|
|
|
|
/** Holds if `call` is a call to `overriding`, which overrides `func`. */
|
|
predicate overridden_call_objectapi(FunctionObject func, FunctionObject overriding, Call call) {
|
|
overriding.overrides(func) and
|
|
overriding.getACall().getNode() = call
|
|
}
|
|
|
|
/** Holds if `call` is a call to `overriding`, which overrides `func`. */
|
|
predicate overridden_call(FunctionValue func, FunctionValue overriding, Call call) {
|
|
overriding.overrides(func) and
|
|
overriding.getACall().getNode() = call
|
|
}
|
|
|
|
/** Holds if `func` will raise a `NotImplemented` error. */
|
|
predicate isAbstract(FunctionValue func) {
|
|
func.getARaisedType() = ClassValue::notImplementedError()
|
|
}
|