Merge pull request #3156 from tausbn/python-autoformat-all-ql-files

Python: Autoformat all `.ql` files.
This commit is contained in:
Rasmus Wriedt Larsen
2020-03-30 16:24:18 +02:00
committed by GitHub
502 changed files with 2667 additions and 2767 deletions

View File

@@ -16,14 +16,14 @@ import python
from CallNode call_to_super, string name
where
exists(GlobalVariable gv, ControlFlowNode cn |
call_to_super = ClassValue::super_().getACall() and
gv.getId() = "super" and
cn = call_to_super.getArg(0) and
name = call_to_super.getScope().getScope().(Class).getName() and
exists(ClassValue other |
cn.pointsTo(other) and
not other.getScope().getName() = name
exists(GlobalVariable gv, ControlFlowNode cn |
call_to_super = ClassValue::super_().getACall() and
gv.getId() = "super" and
cn = call_to_super.getArg(0) and
name = call_to_super.getScope().getScope().(Class).getName() and
exists(ClassValue other |
cn.pointsTo(other) and
not other.getScope().getName() = name
)
)
)
select call_to_super.getNode(), "First argument to super() should be " + name + "."

View File

@@ -16,6 +16,8 @@ import python
from Compare comparison, Expr left, Expr right
where
comparison.compares(left, _, right) and left.isConstant() and right.isConstant() and
comparison.compares(left, _, right) and
left.isConstant() and
right.isConstant() and
not exists(Assert a | a.getTest() = comparison)
select comparison, "Comparison of constants; use 'True' or 'False' instead."

View File

@@ -16,6 +16,5 @@ import python
import Expressions.RedundantComparison
from RedundantComparison comparison
where
comparison.maybeMissingSelf()
where comparison.maybeMissingSelf()
select comparison, "Comparison of identical values; may be missing 'self'."

View File

@@ -15,16 +15,19 @@
import python
import semmle.python.Comparisons
/* Holds if the comparison `comp` is of the complex form `a op b op c` and not of
/*
* Holds if the comparison `comp` is of the complex form `a op b op c` and not of
* the simple form `a op b`.
*/
private predicate is_complex(Expr comp) {
exists(comp.(Compare).getOp(1))
or
is_complex(comp.(UnaryExpr).getOperand())
}
/** A test is useless if for every block that it controls there is another test that is at least as
/**
* A test is useless if for every block that it controls there is another test that is at least as
* strict and also controls that block.
*/
private predicate useless_test(Comparison comp, ComparisonControlBlock controls, boolean isTrue) {
@@ -34,17 +37,15 @@ private predicate useless_test(Comparison comp, ComparisonControlBlock controls,
}
private predicate useless_test_ast(AstNode comp, AstNode previous, boolean isTrue) {
forex(Comparison compnode, ConditionBlock block|
forex(Comparison compnode, ConditionBlock block |
compnode.getNode() = comp and
block.getLastNode().getNode() = previous
|
|
useless_test(compnode, block, isTrue)
)
}
from Expr test, Expr other, boolean isTrue
where
useless_test_ast(test, other, isTrue) and not useless_test_ast(test.getAChildNode+(), other, _)
where
useless_test_ast(test, other, isTrue) and not useless_test_ast(test.getAChildNode+(), other, _)
select test, "Test is always " + isTrue + ", because of $@", other, "this condition"

View File

@@ -17,13 +17,12 @@ import semmle.python.strings
predicate dict_key(Dict d, Expr k, string s) {
k = d.getAKey() and
(
s = ((Num)k).getN()
s = k.(Num).getN()
or
// We use <20> to mark unrepresentable characters
// so two instances of <20> may represent different strings in the source code
not "<22>" = s.charAt(_) and
exists(StrConst c |
c = k |
exists(StrConst c | c = k |
s = "u\"" + c.getText() + "\"" and c.isUnicode()
or
s = "b\"" + c.getText() + "\"" and not c.isUnicode()
@@ -32,13 +31,15 @@ predicate dict_key(Dict d, Expr k, string s) {
}
from Dict d, Expr k1, Expr k2
where exists(string s | dict_key(d, k1, s) and dict_key(d, k2, s) and k1 != k2) and
(
exists(BasicBlock b, int i1, int i2 |
k1.getAFlowNode() = b.getNode(i1) and
k2.getAFlowNode() = b.getNode(i2) and
i1 < i2
) or
k1.getAFlowNode().getBasicBlock().strictlyDominates(k2.getAFlowNode().getBasicBlock())
)
where
exists(string s | dict_key(d, k1, s) and dict_key(d, k2, s) and k1 != k2) and
(
exists(BasicBlock b, int i1, int i2 |
k1.getAFlowNode() = b.getNode(i1) and
k2.getAFlowNode() = b.getNode(i2) and
i1 < i2
)
or
k1.getAFlowNode().getBasicBlock().strictlyDominates(k2.getAFlowNode().getBasicBlock())
)
select k1, "Dictionary key " + repr(k1) + " is subsequently $@.", k2, "overwritten"

View File

@@ -13,23 +13,22 @@
import python
class DelCall extends Call {
DelCall() {
((Attribute)this.getFunc()).getName() = "__del__"
}
predicate isSuperCall() {
exists(Function f | f = this.getScope() and f.getName() = "__del__" |
// We pass in `self` as the first argument...
f.getArg(0).asName().getVariable() = ((Name)this.getArg(0)).getVariable() or
// ... or the call is of the form `super(Type, self).__del__()`, or the equivalent
// Python 3: `super().__del__()`.
exists(Call superCall | superCall = ((Attribute)this.getFunc()).getObject() |
((Name)superCall.getFunc()).getId() = "super"
)
)
}
DelCall() { this.getFunc().(Attribute).getName() = "__del__" }
predicate isSuperCall() {
exists(Function f | f = this.getScope() and f.getName() = "__del__" |
// We pass in `self` as the first argument...
f.getArg(0).asName().getVariable() = this.getArg(0).(Name).getVariable()
or
// ... or the call is of the form `super(Type, self).__del__()`, or the equivalent
// Python 3: `super().__del__()`.
exists(Call superCall | superCall = this.getFunc().(Attribute).getObject() |
superCall.getFunc().(Name).getId() = "super"
)
)
}
}
from DelCall del
where not del.isSuperCall()
select del, "The __del__ special method is called explicitly."
select del, "The __del__ special method is called explicitly."

View File

@@ -15,4 +15,4 @@ import AdvancedFormatting
from AdvancedFormattingCall call, AdvancedFormatString fmt
where call.getAFormat() = fmt and fmt.isImplicitlyNumbered() and fmt.isExplicitlyNumbered()
select fmt, "Formatting string mixes implicitly and explicitly numbered fields."
select fmt, "Formatting string mixes implicitly and explicitly numbered fields."

View File

@@ -11,16 +11,18 @@
*/
import python
import python
import AdvancedFormatting
int field_count(AdvancedFormatString fmt) { result = max(fmt.getFieldNumber(_, _)) + 1 }
from AdvancedFormattingCall call, AdvancedFormatString fmt, int arg_count, int max_field
where arg_count = call.providedArgCount() and max_field = field_count(fmt) and
call.getAFormat() = fmt and not exists(call.getStarargs()) and
forall(AdvancedFormatString other | other = call.getAFormat() | field_count(other) < arg_count)
select call, "Too many arguments for string format. Format $@ requires only " + max_field + ", but " +
arg_count.toString() + " are provided.", fmt, "\"" + fmt.getText() + "\""
where
arg_count = call.providedArgCount() and
max_field = field_count(fmt) and
call.getAFormat() = fmt and
not exists(call.getStarargs()) and
forall(AdvancedFormatString other | other = call.getAFormat() | field_count(other) < arg_count)
select call,
"Too many arguments for string format. Format $@ requires only " + max_field + ", but " +
arg_count.toString() + " are provided.", fmt, "\"" + fmt.getText() + "\""

View File

@@ -14,14 +14,18 @@ import python
import AdvancedFormatting
from AdvancedFormattingCall call, AdvancedFormatString fmt, string name, string fmt_repr
where call.getAFormat() = fmt and
name = call.getAKeyword().getArg() and
forall(AdvancedFormatString format | format = call.getAFormat() | not format.getFieldName(_, _) = name)
and not exists(call.getKwargs()) and
(strictcount(call.getAFormat()) = 1 and fmt_repr = "format \"" + fmt.getText() + "\""
or
strictcount(call.getAFormat()) != 1 and fmt_repr = "any format used."
)
select call, "Surplus named argument for string format. An argument named '" + name +
"' is provided, but it is not required by $@.", fmt, fmt_repr
where
call.getAFormat() = fmt and
name = call.getAKeyword().getArg() and
forall(AdvancedFormatString format | format = call.getAFormat() |
not format.getFieldName(_, _) = name
) and
not exists(call.getKwargs()) and
(
strictcount(call.getAFormat()) = 1 and fmt_repr = "format \"" + fmt.getText() + "\""
or
strictcount(call.getAFormat()) != 1 and fmt_repr = "any format used."
)
select call,
"Surplus named argument for string format. An argument named '" + name +
"' is provided, but it is not required by $@.", fmt, fmt_repr

View File

@@ -15,9 +15,11 @@ import python
import AdvancedFormatting
from AdvancedFormattingCall call, AdvancedFormatString fmt, string name
where call.getAFormat() = fmt and
not name = call.getAKeyword().getArg() and
fmt.getFieldName(_, _) = name
and not exists(call.getKwargs())
select call, "Missing named argument for string format. Format $@ requires '" + name + "', but it is omitted.",
fmt, "\"" + fmt.getText() + "\""
where
call.getAFormat() = fmt and
not name = call.getAKeyword().getArg() and
fmt.getFieldName(_, _) = name and
not exists(call.getKwargs())
select call,
"Missing named argument for string format. Format $@ requires '" + name + "', but it is omitted.",
fmt, "\"" + fmt.getText() + "\""

View File

@@ -14,10 +14,16 @@
import python
import AdvancedFormatting
from AdvancedFormattingCall call, AdvancedFormatString fmt,
int arg_count, int max_field, string provided
where arg_count = call.providedArgCount() and max_field = max(fmt.getFieldNumber(_, _)) and
call.getAFormat() = fmt and not exists(call.getStarargs()) and arg_count <= max_field and
(if arg_count = 1 then provided = " is provided." else provided = " are provided.")
select call, "Too few arguments for string format. Format $@ requires at least " + (max_field+1) + ", but " +
arg_count.toString() + provided, fmt, "\"" + fmt.getText() + "\""
from
AdvancedFormattingCall call, AdvancedFormatString fmt, int arg_count, int max_field,
string provided
where
arg_count = call.providedArgCount() and
max_field = max(fmt.getFieldNumber(_, _)) and
call.getAFormat() = fmt and
not exists(call.getStarargs()) and
arg_count <= max_field and
(if arg_count = 1 then provided = " is provided." else provided = " are provided.")
select call,
"Too few arguments for string format. Format $@ requires at least " + (max_field + 1) + ", but " +
arg_count.toString() + provided, fmt, "\"" + fmt.getText() + "\""

View File

@@ -12,7 +12,8 @@
import python
/* This assumes that any indexing operation where the value is not a sequence or numpy array involves hashing.
/*
* This assumes that any indexing operation where the value is not a sequence or numpy array involves hashing.
* For sequences, the index must be an int, which are hashable, so we don't need to treat them specially.
* For numpy arrays, the index may be a list, which are not hashable and needs to be treated specially.
*/
@@ -30,7 +31,9 @@ predicate has_custom_getitem(Value v) {
}
predicate explicitly_hashed(ControlFlowNode f) {
exists(CallNode c, GlobalVariable hash | c.getArg(0) = f and c.getFunction().(NameNode).uses(hash) and hash.getId() = "hash")
exists(CallNode c, GlobalVariable hash |
c.getArg(0) = f and c.getFunction().(NameNode).uses(hash) and hash.getId() = "hash"
)
}
predicate unhashable_subscript(ControlFlowNode f, ClassValue c, ControlFlowNode origin) {
@@ -44,9 +47,7 @@ predicate unhashable_subscript(ControlFlowNode f, ClassValue c, ControlFlowNode
}
predicate is_unhashable(ControlFlowNode f, ClassValue cls, ControlFlowNode origin) {
exists(Value v |
f.pointsTo(v, origin) and v.getClass() = cls
|
exists(Value v | f.pointsTo(v, origin) and v.getClass() = cls |
not cls.hasAttribute("__hash__") and not cls.failedInference(_) and cls.isNewStyle()
or
cls.lookup("__hash__") = Value::named("None")
@@ -67,16 +68,18 @@ predicate is_unhashable(ControlFlowNode f, ClassValue cls, ControlFlowNode origi
* it.
*/
predicate typeerror_is_caught(ControlFlowNode f) {
exists (Try try |
exists(Try try |
try.getBody().contains(f.getNode()) and
try.getAHandler().getType().pointsTo(ClassValue::typeError()))
try.getAHandler().getType().pointsTo(ClassValue::typeError())
)
}
from ControlFlowNode f, ClassValue c, ControlFlowNode origin
where
not typeerror_is_caught(f)
and
(explicitly_hashed(f) and is_unhashable(f, c, origin)
or
unhashable_subscript(f, c, origin))
not typeerror_is_caught(f) and
(
explicitly_hashed(f) and is_unhashable(f, c, origin)
or
unhashable_subscript(f, c, origin)
)
select f.getNode(), "This $@ of $@ is unhashable.", origin, "instance", c, c.getQualifiedName()

View File

@@ -14,7 +14,14 @@ import python
import IsComparisons
from Compare comp, Cmpop op, ClassValue c, string alt
where invalid_portable_is_comparison(comp, op, c) and
not cpython_interned_constant(comp.getASubExpression()) and
(op instanceof Is and alt = "==" or op instanceof IsNot and alt = "!=")
select comp, "Values compared using '" + op.getSymbol() + "' when equivalence is not the same as identity. Use '" + alt + "' instead."
where
invalid_portable_is_comparison(comp, op, c) and
not cpython_interned_constant(comp.getASubExpression()) and
(
op instanceof Is and alt = "=="
or
op instanceof IsNot and alt = "!="
)
select comp,
"Values compared using '" + op.getSymbol() +
"' when equivalence is not the same as identity. Use '" + alt + "' instead."

View File

@@ -15,10 +15,13 @@ import python
import Exceptions.NotImplemented
from Call c, Value v, ClassValue t, Expr f, AstNode origin
where f = c.getFunc() and f.pointsTo(v, origin) and t = v.getClass() and
not t.isCallable() and not t.failedInference(_)
and not t.hasAttribute("__get__")
and not v = Value::named("None")
and not use_of_not_implemented_in_raise(_, f)
where
f = c.getFunc() and
f.pointsTo(v, origin) and
t = v.getClass() and
not t.isCallable() and
not t.failedInference(_) and
not t.hasAttribute("__get__") and
not v = Value::named("None") and
not use_of_not_implemented_in_raise(_, f)
select c, "Call to a $@ of $@.", origin, "non-callable", t, t.toString()

View File

@@ -14,10 +14,12 @@ import python
import IsComparisons
from Compare comp, Cmpop op, ClassValue c
where invalid_portable_is_comparison(comp, op, c) and
exists(Expr sub |
sub = comp.getASubExpression() |
cpython_interned_constant(sub) and
not universally_interned_constant(sub)
)
select comp, "The result of this comparison with '" + op.getSymbol() + "' may differ between implementations of Python."
where
invalid_portable_is_comparison(comp, op, c) and
exists(Expr sub | sub = comp.getASubExpression() |
cpython_interned_constant(sub) and
not universally_interned_constant(sub)
)
select comp,
"The result of this comparison with '" + op.getSymbol() +
"' may differ between implementations of Python."

View File

@@ -14,9 +14,8 @@ import python
import semmle.python.regex
from Regex r, int offset
where r.escapingChar(offset) and r.getChar(offset+1) = "b" and
exists(int start, int end |
start < offset and end > offset |
r.charSet(start, end)
)
select r, "Backspace escape in regular expression at offset " + offset + "."
where
r.escapingChar(offset) and
r.getChar(offset + 1) = "b" and
exists(int start, int end | start < offset and end > offset | r.charSet(start, end))
select r, "Backspace escape in regular expression at offset " + offset + "."

View File

@@ -15,20 +15,28 @@ import semmle.python.regex
predicate duplicate_char_in_class(Regex r, string char) {
exists(int i, int j, int x, int y, int start, int end |
i != x and j != y and
start < i and j < end and
start < x and y < end and
r.character(i, j) and char = r.getText().substring(i, j) and
r.character(x, y) and char = r.getText().substring(x, y) and
i != x and
j != y and
start < i and
j < end and
start < x and
y < end and
r.character(i, j) and
char = r.getText().substring(i, j) and
r.character(x, y) and
char = r.getText().substring(x, y) and
r.charSet(start, end)
) and
/* Exclude <20> as we use it for any unencodable character */
char != "<22>" and
//Ignore whitespace in verbose mode
not (r.getAMode() = "VERBOSE" and (char = " " or char = "\t" or char = "\r" or char = "\n"))
not (
r.getAMode() = "VERBOSE" and
(char = " " or char = "\t" or char = "\r" or char = "\n")
)
}
from Regex r, string char
where duplicate_char_in_class(r, char)
select r, "This regular expression includes duplicate character '" + char + "' in a set of characters."
select r,
"This regular expression includes duplicate character '" + char + "' in a set of characters."

View File

@@ -16,5 +16,3 @@ import semmle.python.regex
from Regex r, string missing, string part
where r.getText().regexpMatch(".*\\(P<\\w+>.*") and missing = "?" and part = "named group"
select r, "Regular expression is missing '" + missing + "' in " + part + "."

View File

@@ -16,10 +16,11 @@ import semmle.python.regex
predicate unmatchable_caret(Regex r, int start) {
not r.getAMode() = "MULTILINE" and
not r.getAMode() = "VERBOSE" and
r.specialCharacter(start, start+1, "^") and
not r.firstItem(start, start+1)
r.specialCharacter(start, start + 1, "^") and
not r.firstItem(start, start + 1)
}
from Regex r, int offset
where unmatchable_caret(r, offset)
select r, "This regular expression includes an unmatchable caret at offset " + offset.toString() + "."
select r,
"This regular expression includes an unmatchable caret at offset " + offset.toString() + "."

View File

@@ -16,11 +16,11 @@ import semmle.python.regex
predicate unmatchable_dollar(Regex r, int start) {
not r.getAMode() = "MULTILINE" and
not r.getAMode() = "VERBOSE" and
r.specialCharacter(start, start+1, "$")
and
not r.lastItem(start, start+1)
r.specialCharacter(start, start + 1, "$") and
not r.lastItem(start, start + 1)
}
from Regex r, int offset
where unmatchable_dollar(r, offset)
select r, "This regular expression includes an unmatchable dollar at offset " + offset.toString() + "."
select r,
"This regular expression includes an unmatchable dollar at offset " + offset.toString() + "."

View File

@@ -1,38 +1,37 @@
/**
* @name Result of integer division may be truncated
* @description The arguments to a division statement may be integers, which
* may cause the result to be truncated in Python 2.
* @kind problem
* @tags maintainability
* correctness
* @problem.severity warning
* @sub-severity high
* @precision very-high
* @id py/truncated-division
*/
/**
* @name Result of integer division may be truncated
* @description The arguments to a division statement may be integers, which
* may cause the result to be truncated in Python 2.
* @kind problem
* @tags maintainability
* correctness
* @problem.severity warning
* @sub-severity high
* @precision very-high
* @id py/truncated-division
*/
import python
from BinaryExpr div, ControlFlowNode left, ControlFlowNode right
where
// Only relevant for Python 2, as all later versions implement true division
major_version() = 2
and
major_version() = 2 and
exists(BinaryExprNode bin, Value lval, Value rval |
bin = div.getAFlowNode()
and bin.getNode().getOp() instanceof Div
and bin.getLeft().pointsTo(lval, left)
and lval.getClass() = ClassValue::int_()
and bin.getRight().pointsTo(rval, right)
and rval.getClass() = ClassValue::int_()
bin = div.getAFlowNode() and
bin.getNode().getOp() instanceof Div and
bin.getLeft().pointsTo(lval, left) and
lval.getClass() = ClassValue::int_() and
bin.getRight().pointsTo(rval, right) and
rval.getClass() = ClassValue::int_() and
// Ignore instances where integer division leaves no remainder
and not lval.(NumericValue).getIntValue() % rval.(NumericValue).getIntValue() = 0
and not bin.getNode().getEnclosingModule().hasFromFuture("division")
not lval.(NumericValue).getIntValue() % rval.(NumericValue).getIntValue() = 0 and
not bin.getNode().getEnclosingModule().hasFromFuture("division") and
// Filter out results wrapped in `int(...)`
and not exists(CallNode c |
c = ClassValue::int_().getACall()
and c.getAnArg() = bin
not exists(CallNode c |
c = ClassValue::int_().getACall() and
c.getAnArg() = bin
)
)
select div, "Result of division may be truncated as its $@ and $@ arguments may both be integers.",
left.getLocation(), "left", right.getLocation(), "right"
left.getLocation(), "left", right.getLocation(), "right"

View File

@@ -22,14 +22,13 @@ predicate string_const(Expr s) {
from StrConst s
where
// Implicitly concatenated string is in a list and that list contains at least one other string.
exists(List l, Expr other |
not s = other and
l.getAnElt() = s and
l.getAnElt() = other and
string_const(other)
) and
exists(s.getAnImplicitlyConcatenatedPart()) and
not s.isParenthesized()
// Implicitly concatenated string is in a list and that list contains at least one other string.
exists(List l, Expr other |
not s = other and
l.getAnElt() = s and
l.getAnElt() = other and
string_const(other)
) and
exists(s.getAnImplicitlyConcatenatedPart()) and
not s.isParenthesized()
select s, "Implicit string concatenation. Maybe missing a comma?"

View File

@@ -15,43 +15,47 @@ import python
/* f consists of a single return statement, whose value is a call. The arguments of the call are exactly the parameters of f */
predicate simple_wrapper(Lambda l, Expr wrapped) {
exists(Function f, Call c | f = l.getInnerScope() and c = l.getExpression() |
wrapped = c.getFunc() and
count(f.getAnArg()) = count(c.getAnArg()) and
forall(int arg | exists(f.getArg(arg)) |
f.getArgName(arg) = ((Name)c.getArg(arg)).getId()) and
/* Either no **kwargs or they must match */
(not exists(f.getKwarg()) and not exists(c.getKwargs()) or
((Name)f.getKwarg()).getId() = ((Name)c.getKwargs()).getId()) and
/* Either no *args or they must match */
(not exists(f.getVararg()) and not exists(c.getStarargs()) or
((Name)f.getVararg()).getId() = ((Name)c.getStarargs()).getId()) and
/* No named parameters in call */
not exists(c.getAKeyword())
)
and
wrapped = c.getFunc() and
count(f.getAnArg()) = count(c.getAnArg()) and
forall(int arg | exists(f.getArg(arg)) | f.getArgName(arg) = c.getArg(arg).(Name).getId()) and
/* Either no **kwargs or they must match */
(
not exists(f.getKwarg()) and not exists(c.getKwargs())
or
f.getKwarg().(Name).getId() = c.getKwargs().(Name).getId()
) and
/* Either no *args or they must match */
(
not exists(f.getVararg()) and not exists(c.getStarargs())
or
f.getVararg().(Name).getId() = c.getStarargs().(Name).getId()
) and
/* No named parameters in call */
not exists(c.getAKeyword())
) and
// f is not necessarily a drop-in replacement for the lambda if there are default argument values
not exists(l.getArgs().getADefault())
}
/* The expression called will refer to the same object if evaluated when the lambda is created or when the lambda is executed. */
predicate unnecessary_lambda(Lambda l, Expr e) {
simple_wrapper(l, e) and
simple_wrapper(l, e) and
(
/* plain class */
exists(ClassValue c | e.pointsTo(c))
or
/* plain function */
exists(FunctionValue f | e.pointsTo(f))
or
/* bound-method of enclosing instance */
exists(ClassValue cls, Attribute a |
cls.getScope() = l.getScope().getScope() and a = e |
((Name)a.getObject()).getId() = "self" and
cls.hasAttribute(a.getName())
)
/* plain class */
exists(ClassValue c | e.pointsTo(c))
or
/* plain function */
exists(FunctionValue f | e.pointsTo(f))
or
/* bound-method of enclosing instance */
exists(ClassValue cls, Attribute a | cls.getScope() = l.getScope().getScope() and a = e |
a.getObject().(Name).getId() = "self" and
cls.hasAttribute(a.getName())
)
)
}
from Lambda l, Expr e
where unnecessary_lambda(l, e)
select l, "This 'lambda' is just a simple wrapper around a callable object. Use that object directly."
select l,
"This 'lambda' is just a simple wrapper around a callable object. Use that object directly."

View File

@@ -16,11 +16,12 @@
import python
import Expressions.CallArgs
from Call call, FunctionObject func, string name
where
illegally_named_parameter_objectapi(call, func, name) and
not func.isAbstract() and
not exists(FunctionObject overridden | func.overrides(overridden) and overridden.getFunction().getAnArg().(Name).getId() = name)
select
call, "Keyword argument '" + name + "' is not a supported parameter name of $@.", func, func.descriptiveString()
illegally_named_parameter_objectapi(call, func, name) and
not func.isAbstract() and
not exists(FunctionObject overridden |
func.overrides(overridden) and overridden.getFunction().getAnArg().(Name).getId() = name
)
select call, "Keyword argument '" + name + "' is not a supported parameter name of $@.", func,
func.descriptiveString()

View File

@@ -18,29 +18,30 @@ import semmle.python.strings
predicate string_format(BinaryExpr operation, StrConst str, Value args, AstNode origin) {
operation.getOp() instanceof Mod and
exists(Value fmt, Context ctx |
operation.getLeft().pointsTo(ctx, fmt, str) and
operation.getRight().pointsTo(ctx, args, origin)
operation.getLeft().pointsTo(ctx, fmt, str) and
operation.getRight().pointsTo(ctx, args, origin)
)
}
int sequence_length(Value args) {
/* Guess length of sequence */
exists(Tuple seq, AstNode origin |
seq.pointsTo(args,origin) |
exists(Tuple seq, AstNode origin | seq.pointsTo(args, origin) |
result = strictcount(seq.getAnElt()) and
not seq.getAnElt() instanceof Starred
)
or
exists(ImmutableLiteral i |
i.getLiteralValue() = args |
result = 1
)
exists(ImmutableLiteral i | i.getLiteralValue() = args | result = 1)
}
from BinaryExpr operation, StrConst fmt, Value args, int slen, int alen, AstNode origin, string provided
where string_format(operation, fmt, args, origin) and slen = sequence_length(args) and alen = format_items(fmt) and slen != alen and
(if slen = 1 then provided = " is provided." else provided = " are provided.")
select operation, "Wrong number of $@ for string format. Format $@ takes " + alen.toString() + ", but " + slen.toString() + provided,
origin, "arguments",
fmt, fmt.getText()
from
BinaryExpr operation, StrConst fmt, Value args, int slen, int alen, AstNode origin,
string provided
where
string_format(operation, fmt, args, origin) and
slen = sequence_length(args) and
alen = format_items(fmt) and
slen != alen and
(if slen = 1 then provided = " is provided." else provided = " are provided.")
select operation,
"Wrong number of $@ for string format. Format $@ takes " + alen.toString() + ", but " +
slen.toString() + provided, origin, "arguments", fmt, fmt.getText()

View File

@@ -16,15 +16,20 @@ import CallArgs
from Call call, FunctionObject func, string too, string should, int limit
where
(
too_many_args_objectapi(call, func, limit) and too = "too many arguments" and should = "no more than "
or
too_few_args_objectapi(call, func, limit) and too = "too few arguments" and should = "no fewer than "
) and
not func.isAbstract() and
not exists(FunctionObject overridden | func.overrides(overridden) and correct_args_if_called_as_method_objectapi(call, overridden))
/* The semantics of `__new__` can be a bit subtle, so we simply exclude `__new__` methods */
and not func.getName() = "__new__"
select call, "Call to $@ with " + too + "; should be " + should + limit.toString() + ".", func, func.descriptiveString()
(
too_many_args_objectapi(call, func, limit) and
too = "too many arguments" and
should = "no more than "
or
too_few_args_objectapi(call, func, limit) and
too = "too few arguments" and
should = "no fewer than "
) and
not func.isAbstract() and
not exists(FunctionObject overridden |
func.overrides(overridden) and correct_args_if_called_as_method_objectapi(call, overridden)
) and
/* The semantics of `__new__` can be a bit subtle, so we simply exclude `__new__` methods */
not func.getName() = "__new__"
select call, "Call to $@ with " + too + "; should be " + should + limit.toString() + ".", func,
func.descriptiveString()