mirror of
https://github.com/github/codeql.git
synced 2026-04-30 11:15:13 +02:00
Merge pull request #3770 from tausbn/python-add-a-bunch-of-documentation
Python: Add a bunch of documentation.
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
/** INTERNAL - Methods used by queries that test whether functions are invoked correctly. */
|
||||
|
||||
import python
|
||||
import Testing.Mox
|
||||
|
||||
@@ -71,36 +73,38 @@ private int positional_arg_count_for_call(Call call, Value callable) {
|
||||
)
|
||||
}
|
||||
|
||||
/** 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*/
|
||||
/** 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*/
|
||||
/** 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*/
|
||||
/** 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*/
|
||||
/** 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
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import python
|
||||
|
||||
/** A string constant that looks like it may be used in string formatting operations. */
|
||||
library class PossibleAdvancedFormatString extends StrConst {
|
||||
PossibleAdvancedFormatString() { this.getText().matches("%{%}%") }
|
||||
|
||||
@@ -51,6 +52,7 @@ library class PossibleAdvancedFormatString extends StrConst {
|
||||
predicate isExplicitlyNumbered() { exists(this.fieldId(_, _).toInt()) }
|
||||
}
|
||||
|
||||
/** Holds if the formatting string `fmt` contains a sequence of braces `{` of length `len`, beginning at index `index`. */
|
||||
predicate brace_sequence(PossibleAdvancedFormatString fmt, int index, int len) {
|
||||
exists(string text | text = fmt.getText() |
|
||||
text.charAt(index) = "{" and not text.charAt(index - 1) = "{" and len = 1
|
||||
@@ -61,10 +63,12 @@ predicate brace_sequence(PossibleAdvancedFormatString fmt, int index, int len) {
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if index `index` in the format string `fmt` contains an escaped brace `{`. */
|
||||
predicate escaped_brace(PossibleAdvancedFormatString fmt, int index) {
|
||||
exists(int len | brace_sequence(fmt, index, len) | len % 2 = 0)
|
||||
}
|
||||
|
||||
/** Holds if index `index` in the format string `fmt` contains a left brace `{` that acts as an escape character. */
|
||||
predicate escaping_brace(PossibleAdvancedFormatString fmt, int index) {
|
||||
escaped_brace(fmt, index + 1)
|
||||
}
|
||||
@@ -105,15 +109,18 @@ private predicate advanced_format_call(Call format_expr, PossibleAdvancedFormatS
|
||||
)
|
||||
}
|
||||
|
||||
/** A string constant that has the `format` method applied to it. */
|
||||
class AdvancedFormatString extends PossibleAdvancedFormatString {
|
||||
AdvancedFormatString() { advanced_format_call(_, this, _) }
|
||||
}
|
||||
|
||||
/** A string formatting operation that uses the `format` method. */
|
||||
class AdvancedFormattingCall extends Call {
|
||||
AdvancedFormattingCall() { advanced_format_call(this, _, _) }
|
||||
|
||||
/** Count of the arguments actually provided */
|
||||
int providedArgCount() { advanced_format_call(this, _, result) }
|
||||
|
||||
/** Gets a formatting string for this call. */
|
||||
AdvancedFormatString getAFormat() { advanced_format_call(this, result, _) }
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
/** INTERNAL - Helper predicates for queries that inspect the comparison of objects using `is`. */
|
||||
|
||||
import python
|
||||
|
||||
/** Holds if the comparison `comp` uses `is` or `is not` (represented as `op`) to compare its `left` and `right` arguments. */
|
||||
predicate comparison_using_is(Compare comp, ControlFlowNode left, Cmpop op, ControlFlowNode right) {
|
||||
exists(CompareNode fcomp | fcomp = comp.getAFlowNode() |
|
||||
fcomp.operands(left, op, right) and
|
||||
@@ -7,6 +10,7 @@ predicate comparison_using_is(Compare comp, ControlFlowNode left, Cmpop op, Cont
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if the class `c` overrides the default notion of equality or comparison. */
|
||||
predicate overrides_eq_or_cmp(ClassValue c) {
|
||||
major_version() = 2 and c.hasAttribute("__eq__")
|
||||
or
|
||||
@@ -19,12 +23,14 @@ predicate overrides_eq_or_cmp(ClassValue c) {
|
||||
major_version() = 2 and c.hasAttribute("__cmp__")
|
||||
}
|
||||
|
||||
/** Holds if the class `cls` is likely to only have a single instance throughout the program. */
|
||||
predicate probablySingleton(ClassValue cls) {
|
||||
strictcount(Value inst | inst.getClass() = cls) = 1
|
||||
or
|
||||
cls = Value::named("None").getClass()
|
||||
}
|
||||
|
||||
/** Holds if using `is` to compare instances of the class `c` is likely to cause unexpected behavior. */
|
||||
predicate invalid_to_use_is_portably(ClassValue c) {
|
||||
overrides_eq_or_cmp(c) and
|
||||
// Exclude type/builtin-function/bool as it is legitimate to compare them using 'is' but they implement __eq__
|
||||
@@ -35,6 +41,7 @@ predicate invalid_to_use_is_portably(ClassValue c) {
|
||||
not probablySingleton(c)
|
||||
}
|
||||
|
||||
/** Holds if the control flow node `f` points to either `True`, `False`, or `None`. */
|
||||
predicate simple_constant(ControlFlowNode f) {
|
||||
exists(Value val | f.pointsTo(val) |
|
||||
val = Value::named("True") or val = Value::named("False") or val = Value::named("None")
|
||||
@@ -66,10 +73,12 @@ private predicate universally_interned_value(Expr e) {
|
||||
e.(StrConst).getText() = ""
|
||||
}
|
||||
|
||||
/** Holds if the expression `e` points to an interned constant in CPython. */
|
||||
predicate cpython_interned_constant(Expr e) {
|
||||
exists(Expr const | e.pointsTo(_, const) | cpython_interned_value(const))
|
||||
}
|
||||
|
||||
/** Holds if the expression `e` points to a value that can be reasonably expected to be interned across all implementations of Python. */
|
||||
predicate universally_interned_constant(Expr e) {
|
||||
exists(Expr const | e.pointsTo(_, const) | universally_interned_value(const))
|
||||
}
|
||||
@@ -92,6 +101,9 @@ private predicate comparison_one_type(Compare comp, Cmpop op, ClassValue cls) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if using `is` or `is not` as the operator `op` in the comparison `comp` would be invalid when applied to the class `cls`.
|
||||
*/
|
||||
predicate invalid_portable_is_comparison(Compare comp, Cmpop op, ClassValue cls) {
|
||||
// OK to use 'is' when defining '__eq__'
|
||||
not exists(Function eq | eq.getName() = "__eq__" or eq.getName() = "__ne__" |
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
/** Helper functions for queries that test redundant comparisons. */
|
||||
|
||||
import python
|
||||
|
||||
/** A comparison where the left and right hand sides appear to be identical. */
|
||||
class RedundantComparison extends Compare {
|
||||
RedundantComparison() {
|
||||
exists(Expr left, Expr right |
|
||||
@@ -8,6 +11,15 @@ class RedundantComparison extends Compare {
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if this comparison could be redundant due to a missing `self.`, for example
|
||||
* ```python
|
||||
* foo == foo
|
||||
* ```
|
||||
* instead of
|
||||
* ```python
|
||||
* self.foo == foo
|
||||
* ```
|
||||
*/
|
||||
predicate maybeMissingSelf() {
|
||||
exists(Name left |
|
||||
this.compares(left, _, _) and
|
||||
|
||||
@@ -191,10 +191,19 @@ class CommentedOutCodeBlock extends @py_comment {
|
||||
/** The length of this comment block (in comments) */
|
||||
int length() { result = count(Comment c | this.contains(c)) }
|
||||
|
||||
predicate hasLocationInfo(string filepath, int bl, int bc, int el, int ec) {
|
||||
this.(Comment).getLocation().hasLocationInfo(filepath, bl, bc, _, _) and
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
this.(Comment).getLocation().hasLocationInfo(filepath, startline, startcolumn, _, _) and
|
||||
exists(Comment end | commented_out_code_block(this, end) |
|
||||
end.getLocation().hasLocationInfo(_, _, _, el, ec)
|
||||
end.getLocation().hasLocationInfo(_, _, _, endline, endcolumn)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -15,9 +15,17 @@ import python
|
||||
* including the body (if any), as opposed to the location of its name only.
|
||||
*/
|
||||
class RangeFunction extends Function {
|
||||
predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
|
||||
super.getLocation().hasLocationInfo(path, sl, sc, _, _) and
|
||||
this.getBody().getLastItem().getLocation().hasLocationInfo(path, _, _, el, ec)
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) { super.getLocation().hasLocationInfo(filepath, startline, startcolumn, _, _) and
|
||||
this.getBody().getLastItem().getLocation().hasLocationInfo(filepath, _, _, endline, endcolumn)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,8 +34,16 @@ class RangeFunction extends Function {
|
||||
* including the body (if any), as opposed to the location of its name only.
|
||||
*/
|
||||
class RangeClass extends Class {
|
||||
predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
|
||||
super.getLocation().hasLocationInfo(path, sl, sc, _, _) and
|
||||
this.getBody().getLastItem().getLocation().hasLocationInfo(path, _, _, el, ec)
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) { super.getLocation().hasLocationInfo(filepath, startline, startcolumn, _, _) and
|
||||
this.getBody().getLastItem().getLocation().hasLocationInfo(filepath, _, _, endline, endcolumn)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/** Contains predicates concerning when and where files are opened and closed. */
|
||||
|
||||
import python
|
||||
import semmle.python.GuardedControlFlow
|
||||
import semmle.python.pointsto.Filters
|
||||
@@ -113,12 +115,14 @@ predicate close_method_call(CallNode call, ControlFlowNode self) {
|
||||
call.getFunction().(AttrNode).getObject("close") = self
|
||||
}
|
||||
|
||||
/** Holds if `close` is a function that appears to close files that are passed to it as an argument. */
|
||||
predicate function_closes_file(FunctionValue close) {
|
||||
close = Value::named("os.close")
|
||||
or
|
||||
function_should_close_parameter(close.getScope())
|
||||
}
|
||||
|
||||
/** INTERNAL - Helper predicate for `function_closes_file` */
|
||||
predicate function_should_close_parameter(Function func) {
|
||||
exists(EssaDefinition def |
|
||||
closes_file(def) and
|
||||
@@ -126,6 +130,7 @@ predicate function_should_close_parameter(Function func) {
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if the function `f` opens a file, either directly or indirectly. */
|
||||
predicate function_opens_file(FunctionValue f) {
|
||||
f = Value::named("open")
|
||||
or
|
||||
@@ -140,6 +145,7 @@ predicate function_opens_file(FunctionValue f) {
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if the variable `v` refers to a file opened at `open` which is subsequently returned from a function. */
|
||||
predicate file_is_returned(EssaVariable v, ControlFlowNode open) {
|
||||
exists(NameNode n, Return ret |
|
||||
var_is_open(v, open) and
|
||||
|
||||
@@ -468,7 +468,13 @@ Definition getUniqueDefinition(Expr use) {
|
||||
/** Helper class to get suitable locations for attributes */
|
||||
class NiceLocationExpr extends @py_expr {
|
||||
string toString() { result = this.(Expr).toString() }
|
||||
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `bc` of line `bl` to
|
||||
* column `ec` of line `el` in file `f`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
*/
|
||||
predicate hasLocationInfo(string f, int bl, int bc, int el, int ec) {
|
||||
/* Attribute location for x.y is that of 'y' so that url does not overlap with that of 'x' */
|
||||
exists(int abl, int abc | this.(Attribute).getLocation().hasLocationInfo(f, abl, abc, el, ec) |
|
||||
|
||||
17
python/ql/src/external/DefectFilter.qll
vendored
17
python/ql/src/external/DefectFilter.qll
vendored
@@ -26,7 +26,9 @@ class DefectResult extends int {
|
||||
|
||||
/** Gets the file in which this query result was reported. */
|
||||
File getFile() {
|
||||
exists(string path | defectResults(this, _, path, _, _, _, _, _) and result.getAbsolutePath() = path)
|
||||
exists(string path |
|
||||
defectResults(this, _, path, _, _, _, _, _) and result.getAbsolutePath() = path
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the file path in which this query result was reported. */
|
||||
@@ -47,8 +49,17 @@ class DefectResult extends int {
|
||||
/** Gets the message associated with this query result. */
|
||||
string getMessage() { defectResults(this, _, _, _, _, _, _, result) }
|
||||
|
||||
predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) {
|
||||
defectResults(this, _, path, sl, sc, el, ec, _)
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
defectResults(this, _, filepath, startline, startcolumn, endline, endcolumn, _)
|
||||
}
|
||||
|
||||
/** Gets the URL corresponding to the location of this query result. */
|
||||
|
||||
19
python/ql/src/external/ExternalArtifact.qll
vendored
19
python/ql/src/external/ExternalArtifact.qll
vendored
@@ -1,3 +1,7 @@
|
||||
/**
|
||||
* Provides classes for working with external data.
|
||||
*/
|
||||
|
||||
import python
|
||||
|
||||
class ExternalDefect extends @externalDefect {
|
||||
@@ -27,23 +31,38 @@ class ExternalMetric extends @externalMetric {
|
||||
string toString() { result = getQueryPath() + ": " + getLocation() + " - " + getValue() }
|
||||
}
|
||||
|
||||
/**
|
||||
* An external data item.
|
||||
*/
|
||||
class ExternalData extends @externalDataElement {
|
||||
/** Gets the path of the file this data was loaded from. */
|
||||
string getDataPath() { externalData(this, result, _, _) }
|
||||
|
||||
/**
|
||||
* Gets the path of the file this data was loaded from, with its
|
||||
* extension replaced by `.ql`.
|
||||
*/
|
||||
string getQueryPath() { result = getDataPath().regexpReplaceAll("\\.[^.]*$", ".ql") }
|
||||
|
||||
/** Gets the number of fields in this data item. */
|
||||
int getNumFields() { result = 1 + max(int i | externalData(this, _, i, _) | i) }
|
||||
|
||||
/** Gets the value of the field at position `index` of this data item. */
|
||||
string getField(int index) { externalData(this, _, index, result) }
|
||||
|
||||
/** Gets the integer value of the field at position `index` of this data item. */
|
||||
int getFieldAsInt(int index) { result = getField(index).toInt() }
|
||||
|
||||
/** Gets the floating-point value of the field at position `index` of this data item. */
|
||||
float getFieldAsFloat(int index) { result = getField(index).toFloat() }
|
||||
|
||||
/** Gets the value of the field at position `index` of this data item, interpreted as a date. */
|
||||
date getFieldAsDate(int index) { result = getField(index).toDate() }
|
||||
|
||||
/** Gets a textual representation of this data item. */
|
||||
string toString() { result = getQueryPath() + ": " + buildTupleString(0) }
|
||||
|
||||
/** Gets a textual representation of this data item, starting with the field at position `start`. */
|
||||
private string buildTupleString(int start) {
|
||||
start = getNumFields() - 1 and result = getField(start)
|
||||
or
|
||||
|
||||
37
python/ql/src/external/Thrift.qll
vendored
37
python/ql/src/external/Thrift.qll
vendored
@@ -33,18 +33,27 @@ class ThriftElement extends ExternalData {
|
||||
|
||||
private int column() { result = this.getFieldAsInt(6) }
|
||||
|
||||
predicate hasLocationInfo(string fp, int bl, int bc, int el, int ec) {
|
||||
fp = this.getPath() and
|
||||
bl = this.line() and
|
||||
bc = this.column() and
|
||||
el = this.line() and
|
||||
ec = this.column() + this.getValue().length() - 1
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
filepath = this.getPath() and
|
||||
startline = this.line() and
|
||||
startcolumn = this.column() and
|
||||
endline = this.line() and
|
||||
endcolumn = this.column() + this.getValue().length() - 1
|
||||
or
|
||||
exists(ThriftElement first, ThriftElement last |
|
||||
first = this.getChild(min(int l | exists(this.getChild(l)))) and
|
||||
last = this.getChild(max(int l | exists(this.getChild(l)))) and
|
||||
first.hasLocationInfo(fp, bl, bc, _, _) and
|
||||
last.hasLocationInfo(fp, _, _, el, ec)
|
||||
first.hasLocationInfo(filepath, startline, startcolumn, _, _) and
|
||||
last.hasLocationInfo(filepath, _, _, endline, endcolumn)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -62,11 +71,11 @@ abstract class ThriftNamedElement extends ThriftElement {
|
||||
not exists(this.getName()) and result = this.getKind() + " ???"
|
||||
}
|
||||
|
||||
override predicate hasLocationInfo(string fp, int bl, int bc, int el, int ec) {
|
||||
override predicate hasLocationInfo(string filepath, int startline, int startcolumn, int endline, int endcolumn) {
|
||||
exists(ThriftElement first |
|
||||
first = this.getChild(min(int l | exists(this.getChild(l)))) and
|
||||
first.hasLocationInfo(fp, bl, bc, _, _) and
|
||||
this.getNameElement().hasLocationInfo(fp, _, _, el, ec)
|
||||
first.hasLocationInfo(filepath, startline, startcolumn, _, _) and
|
||||
this.getNameElement().hasLocationInfo(filepath, _, _, endline, endcolumn)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -142,9 +151,9 @@ class ThriftFunction extends ThriftNamedElement {
|
||||
|
||||
ThriftType getReturnType() { result = this.getChild(1).getChild(0) }
|
||||
|
||||
override predicate hasLocationInfo(string fp, int bl, int bc, int el, int ec) {
|
||||
this.getChild(1).hasLocationInfo(fp, bl, bc, _, _) and
|
||||
this.getChild(2).hasLocationInfo(fp, _, _, el, ec)
|
||||
override predicate hasLocationInfo(string filepath, int startline, int startcolumn, int endline, int endcolumn) {
|
||||
this.getChild(1).hasLocationInfo(filepath, startline, startcolumn, _, _) and
|
||||
this.getChild(2).hasLocationInfo(filepath, _, _, endline, endcolumn)
|
||||
}
|
||||
|
||||
ThriftService getService() { result.getAFunction() = this }
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/**
|
||||
* Provides classes representing Python classes.
|
||||
*/
|
||||
|
||||
import python
|
||||
|
||||
/**
|
||||
@@ -37,6 +41,7 @@ class ClassExpr extends ClassExpr_ {
|
||||
result = this.getStarargs()
|
||||
}
|
||||
|
||||
/** Gets a call corresponding to a decorator of this class definition. */
|
||||
Call getADecoratorCall() {
|
||||
result.getArg(0) = this or
|
||||
result.getArg(0) = this.getADecoratorCall()
|
||||
@@ -114,9 +119,10 @@ class Class extends Class_, Scope, AstNode {
|
||||
/** Gets the name used to define this class */
|
||||
override string getName() { result = Class_.super.getName() }
|
||||
|
||||
/** Holds if this expression may have a side effect (as determined purely from its syntax). */
|
||||
predicate hasSideEffects() { any() }
|
||||
|
||||
/** Whether this is probably a mixin (has 'mixin' or similar in name or docstring) */
|
||||
/** Holds if this is probably a mixin (has 'mixin' or similar in name or docstring) */
|
||||
predicate isProbableMixin() {
|
||||
(
|
||||
this.getName().toLowerCase().matches("%mixin%")
|
||||
@@ -129,6 +135,7 @@ class Class extends Class_, Scope, AstNode {
|
||||
|
||||
override AstNode getAChildNode() { result = this.getAStmt() }
|
||||
|
||||
/** Gets a decorator of this class. */
|
||||
Expr getADecorator() { result = this.getParent().getADecorator() }
|
||||
|
||||
/** Gets the metaclass expression */
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/**
|
||||
* Provides classes representing comments in Python.
|
||||
*/
|
||||
|
||||
import python
|
||||
|
||||
/** A source code comment */
|
||||
@@ -56,17 +60,27 @@ class CommentBlock extends @py_comment {
|
||||
/** The length of this comment block (in comments) */
|
||||
int length() { result = max(int i | comment_block_part(this, _, i)) }
|
||||
|
||||
predicate hasLocationInfo(string filepath, int bl, int bc, int el, int ec) {
|
||||
this.(Comment).getLocation().hasLocationInfo(filepath, bl, bc, _, _) and
|
||||
exists(Comment end | end = this.last() | end.getLocation().hasLocationInfo(_, _, _, el, ec))
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) { this.(Comment).getLocation().hasLocationInfo(filepath, startline, startcolumn, _, _) and
|
||||
exists(Comment end | end = this.last() | end.getLocation().hasLocationInfo(_, _, _, endline, endcolumn))
|
||||
}
|
||||
|
||||
/** Holds if this comment block contains `c`. */
|
||||
predicate contains(Comment c) {
|
||||
comment_block_part(this, c, _)
|
||||
or
|
||||
this = c
|
||||
}
|
||||
|
||||
/** Gets a string representation of this comment block. */
|
||||
string getContents() {
|
||||
result =
|
||||
concat(Comment c, int i |
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
/**
|
||||
* Provides classes representing comparison operators.
|
||||
*/
|
||||
|
||||
import python
|
||||
|
||||
/** A class representing the six comparison operators, ==, !=, <, <=, > and >=. */
|
||||
@@ -34,6 +38,7 @@ class CompareOp extends int {
|
||||
this = ge() and result = le()
|
||||
}
|
||||
|
||||
/** Gets the textual representation of `this`. */
|
||||
string repr() {
|
||||
this = eq() and result = "=="
|
||||
or
|
||||
@@ -48,6 +53,7 @@ class CompareOp extends int {
|
||||
this = ge() and result = ">="
|
||||
}
|
||||
|
||||
/** Holds if `op` is the `Cmpop` corresponding to `this`. */
|
||||
predicate forOp(Cmpop op) {
|
||||
op instanceof Eq and this = eq()
|
||||
or
|
||||
@@ -70,16 +76,22 @@ class CompareOp extends int {
|
||||
}
|
||||
}
|
||||
|
||||
/** The `CompareOp` for "equals". */
|
||||
CompareOp eq() { result = 1 }
|
||||
|
||||
/** The `CompareOp` for "not equals". */
|
||||
CompareOp ne() { result = 2 }
|
||||
|
||||
/** The `CompareOp` for "less than". */
|
||||
CompareOp lt() { result = 3 }
|
||||
|
||||
/** The `CompareOp` for "less than or equal to". */
|
||||
CompareOp le() { result = 4 }
|
||||
|
||||
/** The `CompareOp` for "greater than". */
|
||||
CompareOp gt() { result = 5 }
|
||||
|
||||
/** The `CompareOp` for "greater than or equal to". */
|
||||
CompareOp ge() { result = 6 }
|
||||
|
||||
/* Workaround precision limits in floating point numbers */
|
||||
|
||||
@@ -10,8 +10,21 @@ class File extends Container {
|
||||
/** DEPRECATED: Use `getAbsolutePath` instead. */
|
||||
deprecated string getFullName() { result = this.getAbsolutePath() }
|
||||
|
||||
predicate hasLocationInfo(string filepath, int bl, int bc, int el, int ec) {
|
||||
this.getAbsolutePath() = filepath and bl = 0 and bc = 0 and el = 0 and ec = 0
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
this.getAbsolutePath() = filepath and
|
||||
startline = 0 and
|
||||
startcolumn = 0 and
|
||||
endline = 0 and
|
||||
endcolumn = 0
|
||||
}
|
||||
|
||||
/** Whether this file is a source code file. */
|
||||
@@ -79,8 +92,21 @@ class Folder extends Container {
|
||||
/** DEPRECATED: Use `getBaseName` instead. */
|
||||
deprecated string getSimple() { folders(this, _, result) }
|
||||
|
||||
predicate hasLocationInfo(string filepath, int bl, int bc, int el, int ec) {
|
||||
this.getAbsolutePath() = filepath and bl = 0 and bc = 0 and el = 0 and ec = 0
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
this.getAbsolutePath() = filepath and
|
||||
startline = 0 and
|
||||
startcolumn = 0 and
|
||||
endline = 0 and
|
||||
endcolumn = 0
|
||||
}
|
||||
|
||||
override string getAbsolutePath() { folders(this, result, _) }
|
||||
@@ -371,23 +397,39 @@ class Location extends @location {
|
||||
result = this.getPath().getAbsolutePath() + ":" + this.getStartLine().toString()
|
||||
}
|
||||
|
||||
predicate hasLocationInfo(string filepath, int bl, int bc, int el, int ec) {
|
||||
exists(File f | f.getAbsolutePath() = filepath |
|
||||
locations_default(this, f, bl, bc, el, ec)
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) { exists(File f | f.getAbsolutePath() = filepath |
|
||||
locations_default(this, f, startline, startcolumn, endline, endcolumn)
|
||||
or
|
||||
exists(Module m | m.getFile() = f | locations_ast(this, m, bl, bc, el, ec))
|
||||
exists(Module m | m.getFile() = f | locations_ast(this, m, startline, startcolumn, endline, endcolumn))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** A non-empty line in the source code */
|
||||
class Line extends @py_line {
|
||||
predicate hasLocationInfo(string filepath, int bl, int bc, int el, int ec) {
|
||||
exists(Module m |
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) { exists(Module m |
|
||||
m.getFile().getAbsolutePath() = filepath and
|
||||
el = bl and
|
||||
bc = 1 and
|
||||
py_line_lengths(this, m, bl, ec)
|
||||
endline = startline and
|
||||
startcolumn = 1 and
|
||||
py_line_lengths(this, m, startline, endcolumn)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1079,9 +1079,17 @@ class BasicBlock extends @py_flow_node {
|
||||
this.getASuccessor().reachesExit()
|
||||
}
|
||||
|
||||
predicate hasLocationInfo(string file, int line, int col, int endl, int endc) {
|
||||
this.startLocationInfo(file, line, col) and
|
||||
this.endLocationInfo(endl, endc)
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) { this.startLocationInfo(filepath, startline, startcolumn) and
|
||||
this.endLocationInfo(endline, endcolumn)
|
||||
}
|
||||
|
||||
/** Gets a true successor to this basic block */
|
||||
|
||||
@@ -378,8 +378,16 @@ abstract class TaintSource extends @py_flow_node {
|
||||
|
||||
Location getLocation() { result = this.(ControlFlowNode).getLocation() }
|
||||
|
||||
predicate hasLocationInfo(string fp, int bl, int bc, int el, int ec) {
|
||||
this.getLocation().hasLocationInfo(fp, bl, bc, el, ec)
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) { this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
|
||||
/** Gets a TaintedNode for this taint source */
|
||||
@@ -482,8 +490,16 @@ abstract class TaintSink extends @py_flow_node {
|
||||
|
||||
Location getLocation() { result = this.(ControlFlowNode).getLocation() }
|
||||
|
||||
predicate hasLocationInfo(string fp, int bl, int bc, int el, int ec) {
|
||||
this.getLocation().hasLocationInfo(fp, bl, bc, el, ec)
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) { this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -73,15 +73,28 @@ class Value extends TObject {
|
||||
*/
|
||||
predicate isBuiltin() { this.(ObjectInternal).isBuiltin() }
|
||||
|
||||
predicate hasLocationInfo(string filepath, int bl, int bc, int el, int ec) {
|
||||
this.(ObjectInternal).getOrigin().getLocation().hasLocationInfo(filepath, bl, bc, el, ec)
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) {
|
||||
this
|
||||
.(ObjectInternal)
|
||||
.getOrigin()
|
||||
.getLocation()
|
||||
.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
or
|
||||
not exists(this.(ObjectInternal).getOrigin()) and
|
||||
filepath = "" and
|
||||
bl = 0 and
|
||||
bc = 0 and
|
||||
el = 0 and
|
||||
ec = 0
|
||||
startline = 0 and
|
||||
startcolumn = 0 and
|
||||
endline = 0 and
|
||||
endcolumn = 0
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/** Contains the internal algebraic datatype backing the various values tracked by the points-to implementation. */
|
||||
|
||||
import python
|
||||
private import semmle.python.types.Builtins
|
||||
private import semmle.python.objects.ObjectInternal
|
||||
@@ -10,19 +12,19 @@ private import semmle.python.pointsto.PointsToContext
|
||||
*/
|
||||
cached
|
||||
newtype TObject =
|
||||
/* Builtin class objects */
|
||||
/** Builtin class objects */
|
||||
TBuiltinClassObject(Builtin bltn) {
|
||||
bltn.isClass() and
|
||||
not bltn = Builtin::unknownType() and
|
||||
not bltn = Builtin::special("type")
|
||||
} or
|
||||
/* Builtin function objects (module members) */
|
||||
/** Builtin function objects (module members) */
|
||||
TBuiltinFunctionObject(Builtin bltn) { bltn.isFunction() } or
|
||||
/* Builtin method objects (class members) */
|
||||
/** Builtin method objects (class members) */
|
||||
TBuiltinMethodObject(Builtin bltn) { bltn.isMethod() } or
|
||||
/* Builtin module objects */
|
||||
/** Builtin module objects */
|
||||
TBuiltinModuleObject(Builtin bltn) { bltn.isModule() } or
|
||||
/* Other builtin objects from the interpreter */
|
||||
/** Other builtin objects from the interpreter */
|
||||
TBuiltinOpaqueObject(Builtin bltn) {
|
||||
not bltn.isClass() and
|
||||
not bltn.isFunction() and
|
||||
@@ -34,31 +36,31 @@ newtype TObject =
|
||||
not exists(bltn.strValue()) and
|
||||
not py_special_objects(bltn, _)
|
||||
} or
|
||||
/* Python function objects (including lambdas) */
|
||||
/** Python function objects (including lambdas) */
|
||||
TPythonFunctionObject(ControlFlowNode callable) { callable.getNode() instanceof CallableExpr } or
|
||||
/* Python class objects */
|
||||
/** Python class objects */
|
||||
TPythonClassObject(ControlFlowNode classexpr) { classexpr.getNode() instanceof ClassExpr } or
|
||||
/* Package objects */
|
||||
/** Package objects */
|
||||
TPackageObject(Folder f) { isPreferredModuleForName(f, _) } or
|
||||
/* Python module objects */
|
||||
/** Python module objects */
|
||||
TPythonModule(Module m) {
|
||||
not m.isPackage() and
|
||||
isPreferredModuleForName(m.getFile(), _) and
|
||||
not exists(SyntaxError se | se.getFile() = m.getFile())
|
||||
} or
|
||||
/* `True` */
|
||||
/** `True` */
|
||||
TTrue() or
|
||||
/* `False` */
|
||||
/** `False` */
|
||||
TFalse() or
|
||||
/* `None` */
|
||||
/** `None` */
|
||||
TNone() or
|
||||
/* Represents any value about which nothing useful is known */
|
||||
/** Represents any value about which nothing useful is known */
|
||||
TUnknown() or
|
||||
/* Represents any value known to be a class, but not known to be any specific class */
|
||||
/** Represents any value known to be a class, but not known to be any specific class */
|
||||
TUnknownClass() or
|
||||
/* Represents the absence of a value. Used by points-to for tracking undefined variables */
|
||||
/** Represents the absence of a value. Used by points-to for tracking undefined variables */
|
||||
TUndefined() or
|
||||
/* The integer `n` */
|
||||
/** The integer `n` */
|
||||
TInt(int n) {
|
||||
// Powers of 2 are used for flags
|
||||
is_power_2(n)
|
||||
@@ -76,9 +78,9 @@ newtype TObject =
|
||||
or
|
||||
n = any(Builtin b).intValue()
|
||||
} or
|
||||
/* The float `f` */
|
||||
/** The float `f` */
|
||||
TFloat(float f) { f = any(FloatLiteral num).getValue() } or
|
||||
/* The unicode string `s` */
|
||||
/** The unicode string `s` */
|
||||
TUnicode(string s) {
|
||||
// Any string explicitly mentioned in the source code.
|
||||
exists(StrConst str |
|
||||
@@ -94,7 +96,7 @@ newtype TObject =
|
||||
or
|
||||
s = "__main__"
|
||||
} or
|
||||
/* The byte string `s` */
|
||||
/** The byte string `s` */
|
||||
TBytes(string s) {
|
||||
// Any string explicitly mentioned in the source code.
|
||||
exists(StrConst str |
|
||||
@@ -110,74 +112,74 @@ newtype TObject =
|
||||
or
|
||||
s = "__main__"
|
||||
} or
|
||||
/* An instance of `cls`, instantiated at `instantiation` given the `context`. */
|
||||
/** An instance of `cls`, instantiated at `instantiation` given the `context`. */
|
||||
TSpecificInstance(ControlFlowNode instantiation, ClassObjectInternal cls, PointsToContext context) {
|
||||
PointsToInternal::pointsTo(instantiation.(CallNode).getFunction(), context, cls, _) and
|
||||
cls.isSpecial() = false
|
||||
or
|
||||
literal_instantiation(instantiation, cls, context)
|
||||
} or
|
||||
/* A non-specific instance `cls` which enters the scope at `def` given the callee `context`. */
|
||||
/** A non-specific instance `cls` which enters the scope at `def` given the callee `context`. */
|
||||
TSelfInstance(ParameterDefinition def, PointsToContext context, PythonClassObjectInternal cls) {
|
||||
self_parameter(def, context, cls)
|
||||
} or
|
||||
/* A bound method */
|
||||
/** A bound method */
|
||||
TBoundMethod(ObjectInternal self, CallableObjectInternal function) {
|
||||
any(ObjectInternal obj).binds(self, _, function) and
|
||||
function.isDescriptor() = true
|
||||
} or
|
||||
/* Represents any value whose class is known, but nothing else */
|
||||
/** Represents any value whose class is known, but nothing else */
|
||||
TUnknownInstance(BuiltinClassObjectInternal cls) {
|
||||
cls != ObjectInternal::superType() and
|
||||
cls != ObjectInternal::builtin("bool") and
|
||||
cls != ObjectInternal::noneType()
|
||||
} or
|
||||
/* Represents an instance of `super` */
|
||||
/** Represents an instance of `super` */
|
||||
TSuperInstance(ObjectInternal self, ClassObjectInternal startclass) {
|
||||
super_instantiation(_, self, startclass, _)
|
||||
} or
|
||||
/* Represents an instance of `classmethod` */
|
||||
/** Represents an instance of `classmethod` */
|
||||
TClassMethod(CallNode instantiation, CallableObjectInternal function) {
|
||||
class_method(instantiation, function, _)
|
||||
} or
|
||||
/* Represents an instance of `staticmethod` */
|
||||
/** Represents an instance of `staticmethod` */
|
||||
TStaticMethod(CallNode instantiation, CallableObjectInternal function) {
|
||||
static_method(instantiation, function, _)
|
||||
} or
|
||||
/* Represents a builtin tuple */
|
||||
/** Represents a builtin tuple */
|
||||
TBuiltinTuple(Builtin bltn) { bltn.getClass() = Builtin::special("tuple") } or
|
||||
/* Represents a tuple in the Python source */
|
||||
/** Represents a tuple in the Python source */
|
||||
TPythonTuple(TupleNode origin, PointsToContext context) {
|
||||
origin.isLoad() and
|
||||
context.appliesTo(origin)
|
||||
} or
|
||||
/* Varargs tuple */
|
||||
/** Varargs tuple */
|
||||
TVarargsTuple(CallNode call, PointsToContext context, int offset, int length) {
|
||||
InterProceduralPointsTo::varargs_tuple(call, context, _, _, offset, length)
|
||||
} or
|
||||
/* `type` */
|
||||
/** `type` */
|
||||
TType() or
|
||||
/* Represents an instance of `property` */
|
||||
/** Represents an instance of `property` */
|
||||
TProperty(CallNode call, Context ctx, CallableObjectInternal getter) {
|
||||
PointsToInternal::pointsTo(call.getFunction(), ctx, ObjectInternal::property(), _) and
|
||||
PointsToInternal::pointsTo(call.getArg(0), ctx, getter, _)
|
||||
} or
|
||||
/* Represents the `setter` or `deleter` method of a property object. */
|
||||
/** Represents the `setter` or `deleter` method of a property object. */
|
||||
TPropertySetterOrDeleter(PropertyInternal property, string method) {
|
||||
exists(AttrNode attr | PointsToInternal::pointsTo(attr.getObject(method), _, property, _)) and
|
||||
(method = "setter" or method = "deleter")
|
||||
} or
|
||||
/* Represents a dynamically created class */
|
||||
/** Represents a dynamically created class */
|
||||
TDynamicClass(CallNode instantiation, ClassObjectInternal metacls, PointsToContext context) {
|
||||
PointsToInternal::pointsTo(instantiation.getFunction(), context, metacls, _) and
|
||||
not count(instantiation.getAnArg()) = 1 and
|
||||
Types::getMro(metacls).contains(TType())
|
||||
} or
|
||||
/* Represents `sys.version_info`. Acts like a tuple with a range of values depending on the version being analysed. */
|
||||
/** Represents `sys.version_info`. Acts like a tuple with a range of values depending on the version being analysed. */
|
||||
TSysVersionInfo() or
|
||||
/* Represents a module that is inferred to perhaps exist, but is not present in the database. */
|
||||
/** Represents a module that is inferred to perhaps exist, but is not present in the database. */
|
||||
TAbsentModule(string name) { missing_imported_module(_, _, name) } or
|
||||
/* Represents an attribute of a module that is inferred to perhaps exist, but is not present in the database. */
|
||||
/** Represents an attribute of a module that is inferred to perhaps exist, but is not present in the database. */
|
||||
TAbsentModuleAttribute(AbsentModuleObjectInternal mod, string attrname) {
|
||||
(
|
||||
PointsToInternal::pointsTo(any(AttrNode attr).getObject(attrname), _, mod, _)
|
||||
@@ -189,9 +191,9 @@ newtype TObject =
|
||||
not common_module_name(modname + "." + attrname)
|
||||
)
|
||||
} or
|
||||
/* Opaque object representing the result of calling a decorator on a function that we don't understand */
|
||||
/** Opaque object representing the result of calling a decorator on a function that we don't understand */
|
||||
TDecoratedFunction(CallNode call) { call.isFunctionDecoratorCall() } or
|
||||
/* Represents a subscript operation applied to a type. For type-hint analysis */
|
||||
/** Represents a subscript operation applied to a type. For type-hint analysis */
|
||||
TSubscriptedType(ObjectInternal generic, ObjectInternal index) {
|
||||
isType(generic) and
|
||||
generic.isNotSubscriptedType() and
|
||||
@@ -199,6 +201,7 @@ newtype TObject =
|
||||
Expressions::subscriptPartsPointsTo(_, _, generic, index)
|
||||
}
|
||||
|
||||
/** Holds if the object `t` is a type. */
|
||||
predicate isType(ObjectInternal t) {
|
||||
t.isClass() = true
|
||||
or
|
||||
@@ -421,7 +424,7 @@ predicate missing_imported_module(ControlFlowNode imp, Context ctx, string name)
|
||||
)
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Helper for missing modules to determine if name `x.y` is a module `x.y` or
|
||||
* an attribute `y` of module `x`. This list should be added to as required.
|
||||
*/
|
||||
|
||||
@@ -64,15 +64,23 @@ class Object extends @py_object {
|
||||
|
||||
private predicate hasOrigin() { py_flow_bb_node(this, _, _, _) }
|
||||
|
||||
predicate hasLocationInfo(string filepath, int bl, int bc, int el, int ec) {
|
||||
this.hasOrigin() and this.getOrigin().getLocation().hasLocationInfo(filepath, bl, bc, el, ec)
|
||||
/**
|
||||
* Holds if this element is at the specified location.
|
||||
* The location spans column `startcolumn` of line `startline` to
|
||||
* column `endcolumn` of line `endline` in file `filepath`.
|
||||
* For more information, see
|
||||
* [Locations](https://help.semmle.com/QL/learn-ql/ql/locations.html).
|
||||
*/
|
||||
predicate hasLocationInfo(
|
||||
string filepath, int startline, int startcolumn, int endline, int endcolumn
|
||||
) { this.hasOrigin() and this.getOrigin().getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||
or
|
||||
not this.hasOrigin() and
|
||||
filepath = ":Compiled Code" and
|
||||
bl = 0 and
|
||||
bc = 0 and
|
||||
el = 0 and
|
||||
ec = 0
|
||||
startline = 0 and
|
||||
startcolumn = 0 and
|
||||
endline = 0 and
|
||||
endcolumn = 0
|
||||
}
|
||||
|
||||
/** INTERNAL -- Do not use */
|
||||
|
||||
Reference in New Issue
Block a user