From 3956a1fea8d1c08ab7822487f5aa0cbf33867b60 Mon Sep 17 00:00:00 2001 From: Taus Date: Wed, 26 Feb 2025 13:33:16 +0000 Subject: [PATCH 1/3] Python: Move min/maxParameter methods to `Function` These seem generally useful outside of points-to, and so it might be better to add them to the `Function` class instead. I took the liberty of renaming these to say `Arguments` rather than `Parameters`, as this is more in line with the nomenclature that we're using elsewhere. (The internal points-to methods retain the old names.) I'm somewhat ambivalent about the behaviour of `getMaxParameters` on functions with `*varargs`. The hard-coded `INT_MAX` return value is somewhat awkward, but the alternative (to only have the predicate defined when a specific maximum exists) seems like it would potentially cause a lot of headaches. --- python/ql/lib/semmle/python/Function.qll | 12 ++++++++++++ .../ql/lib/semmle/python/objects/ObjectAPI.qll | 16 ++-------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/python/ql/lib/semmle/python/Function.qll b/python/ql/lib/semmle/python/Function.qll index c3d5f7c801ea..1687af0038a3 100644 --- a/python/ql/lib/semmle/python/Function.qll +++ b/python/ql/lib/semmle/python/Function.qll @@ -163,6 +163,18 @@ class Function extends Function_, Scope, AstNode { ret.getValue() = result.getNode() ) } + + /** Gets the minimum number of positional arguments that can be correctly passed to this function. */ + int getMinArguments() { + result = count(this.getAnArg()) - count(this.getDefinition().getArgs().getADefault()) + } + + /** Gets the maximum number of positional arguments that can be correctly passed to this function. */ + int getMaxArguments() { + if exists(this.getVararg()) + then result = 2147483647 // INT_MAX + else result = count(this.getAnArg()) + } } /** A def statement. Note that FunctionDef extends Assign as a function definition binds the newly created function */ diff --git a/python/ql/lib/semmle/python/objects/ObjectAPI.qll b/python/ql/lib/semmle/python/objects/ObjectAPI.qll index dc1363b2ebe2..ed480cefc97d 100644 --- a/python/ql/lib/semmle/python/objects/ObjectAPI.qll +++ b/python/ql/lib/semmle/python/objects/ObjectAPI.qll @@ -738,21 +738,9 @@ class PythonFunctionValue extends FunctionValue { else result = "function " + this.getQualifiedName() } - override int minParameters() { - exists(Function f | - f = this.getScope() and - result = count(f.getAnArg()) - count(f.getDefinition().getArgs().getADefault()) - ) - } + override int minParameters() { result = this.getScope().getMinArguments() } - override int maxParameters() { - exists(Function f | - f = this.getScope() and - if exists(f.getVararg()) - then result = 2147483647 // INT_MAX - else result = count(f.getAnArg()) - ) - } + override int maxParameters() { result = this.getScope().getMaxArguments() } /** Gets a control flow node corresponding to a return statement in this function */ ControlFlowNode getAReturnedNode() { result = this.getScope().getAReturnValueFlowNode() } From 83cdcdbb0b27ae4a31ff2e2dc8626c1c7dee7ae6 Mon Sep 17 00:00:00 2001 From: Taus Date: Wed, 26 Feb 2025 13:53:49 +0000 Subject: [PATCH 2/3] Python: Add change note --- ...025-02-26-add-get-min-max-parameters-to-function-class.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 python/ql/lib/change-notes/2025-02-26-add-get-min-max-parameters-to-function-class.md diff --git a/python/ql/lib/change-notes/2025-02-26-add-get-min-max-parameters-to-function-class.md b/python/ql/lib/change-notes/2025-02-26-add-get-min-max-parameters-to-function-class.md new file mode 100644 index 000000000000..f35b0981bafc --- /dev/null +++ b/python/ql/lib/change-notes/2025-02-26-add-get-min-max-parameters-to-function-class.md @@ -0,0 +1,5 @@ +--- +category: minorAnalysis +--- + +- Added the methods `getMinArguments` and `getMaxArguments` to the `Function` class. These return the minimum and maximum positional arguments that the given function accepts. From bf3d9ee6a99a6ad42d80051fef648d4f02d26052 Mon Sep 17 00:00:00 2001 From: Taus Date: Tue, 4 Mar 2025 22:30:55 +0000 Subject: [PATCH 3/3] Python: Address review comments --- python/ql/lib/semmle/python/Function.qll | 12 +++++++++--- python/ql/lib/semmle/python/objects/ObjectAPI.qll | 4 ++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/python/ql/lib/semmle/python/Function.qll b/python/ql/lib/semmle/python/Function.qll index 1687af0038a3..589d7f471611 100644 --- a/python/ql/lib/semmle/python/Function.qll +++ b/python/ql/lib/semmle/python/Function.qll @@ -165,12 +165,18 @@ class Function extends Function_, Scope, AstNode { } /** Gets the minimum number of positional arguments that can be correctly passed to this function. */ - int getMinArguments() { + int getMinPositionalArguments() { result = count(this.getAnArg()) - count(this.getDefinition().getArgs().getADefault()) } - /** Gets the maximum number of positional arguments that can be correctly passed to this function. */ - int getMaxArguments() { + /** + * Gets the maximum number of positional arguments that can be correctly passed to this function. + * + * If the function has a `*vararg` parameter, there is no upper limit on the number of positional + * arguments that can be passed to the function. In this case, this method returns a very large + * number (currently `INT_MAX`, 2147483647, but this may change in the future). + */ + int getMaxPositionalArguments() { if exists(this.getVararg()) then result = 2147483647 // INT_MAX else result = count(this.getAnArg()) diff --git a/python/ql/lib/semmle/python/objects/ObjectAPI.qll b/python/ql/lib/semmle/python/objects/ObjectAPI.qll index ed480cefc97d..4a36ec6d847f 100644 --- a/python/ql/lib/semmle/python/objects/ObjectAPI.qll +++ b/python/ql/lib/semmle/python/objects/ObjectAPI.qll @@ -738,9 +738,9 @@ class PythonFunctionValue extends FunctionValue { else result = "function " + this.getQualifiedName() } - override int minParameters() { result = this.getScope().getMinArguments() } + override int minParameters() { result = this.getScope().getMinPositionalArguments() } - override int maxParameters() { result = this.getScope().getMaxArguments() } + override int maxParameters() { result = this.getScope().getMaxPositionalArguments() } /** Gets a control flow node corresponding to a return statement in this function */ ControlFlowNode getAReturnedNode() { result = this.getScope().getAReturnValueFlowNode() }