mirror of
https://github.com/github/codeql.git
synced 2026-07-12 23:15:40 +02:00
Merge branch 'main' into sqltaint
This commit is contained in:
@@ -38,6 +38,8 @@ If you have an idea for a query that you would like to share with other CodeQL u
|
||||
|
||||
- The queries and libraries must be autoformatted, for example using the "Format Document" command in [CodeQL for Visual Studio Code](https://help.semmle.com/codeql/codeql-for-vscode/procedures/about-codeql-for-vscode.html).
|
||||
|
||||
If you prefer, you can use this [pre-commit hook](misc/scripts/pre-commit) that automatically checks whether your files are correctly formatted. See the [pre-commit hook installation guide](docs/install-pre-commit-hook.md) for instructions on how to install the hook.
|
||||
|
||||
4. **Compilation**
|
||||
|
||||
- Compilation of the query and any associated libraries and tests must be resilient to future development of the [supported](docs/supported-queries.md) libraries. This means that the functionality cannot use internal libraries, cannot depend on the output of `getAQlClass`, and cannot make use of regexp matching on `toString`.
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
|
||||
image::image(int width, int height)
|
||||
{
|
||||
int x, y;
|
||||
|
||||
// allocate width * height pixels
|
||||
pixels = new uint32_t[width * height];
|
||||
|
||||
// fill width * height pixels
|
||||
for (y = 0; y < height; y++)
|
||||
{
|
||||
for (x = 0; x < width; x++)
|
||||
{
|
||||
pixels[(y * width) + height] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
<overview>
|
||||
<p>The result of a multiplication is used in the size of an allocation. If the multiplication can be made to overflow, a much smaller amount of memory may be allocated than the rest of the code expects. This may lead to overflowing writes when the buffer is accessed later.</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>To fix this issue, ensure that the arithmetic used in the size of an allocation cannot overflow before memory is allocated.</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>In the following example, an array of size <code>width * height</code> is allocated and stored as <code>pixels</code>. If <code>width</code> and <code>height</code> are set such that the multiplication overflows and wraps to a small value (say, 4) then the initialization code that follows the allocation will write beyond the end of the array.</p>
|
||||
<sample src="AllocMultiplicationOverflow.cpp"/>
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>
|
||||
Cplusplus.com: <a href="http://www.cplusplus.com/articles/DE18T05o/">Integer overflow</a>.
|
||||
</li>
|
||||
</references>
|
||||
|
||||
</qhelp>
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* @name Multiplication result may overflow and be used in allocation
|
||||
* @description Using a multiplication result that may overflow in the size of an allocation may lead to buffer overflows when the allocated memory is used.
|
||||
* @kind path-problem
|
||||
* @problem.severity warning
|
||||
* @precision low
|
||||
* @tags security
|
||||
* correctness
|
||||
* external/cwe/cwe-190
|
||||
* external/cwe/cwe-128
|
||||
* @id cpp/multiplication-overflow-in-alloc
|
||||
*/
|
||||
|
||||
import cpp
|
||||
import semmle.code.cpp.models.interfaces.Allocation
|
||||
import semmle.code.cpp.dataflow.DataFlow
|
||||
import DataFlow::PathGraph
|
||||
|
||||
class MultToAllocConfig extends DataFlow::Configuration {
|
||||
MultToAllocConfig() { this = "MultToAllocConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node node) {
|
||||
// a multiplication of two non-constant expressions
|
||||
exists(MulExpr me |
|
||||
me = node.asExpr() and
|
||||
forall(Expr e | e = me.getAnOperand() | not exists(e.getValue()))
|
||||
)
|
||||
}
|
||||
|
||||
override predicate isSink(DataFlow::Node node) {
|
||||
// something that affects an allocation size
|
||||
node.asExpr() = any(AllocationExpr ae).getSizeExpr().getAChild*()
|
||||
}
|
||||
}
|
||||
|
||||
from MultToAllocConfig config, DataFlow::PathNode source, DataFlow::PathNode sink
|
||||
where config.hasFlowPath(source, sink)
|
||||
select sink, source, sink,
|
||||
"Potentially overflowing value from $@ is used in the size of this allocation.", source,
|
||||
"multiplication"
|
||||
@@ -0,0 +1,17 @@
|
||||
edges
|
||||
| test.cpp:22:17:22:21 | ... * ... | test.cpp:23:33:23:37 | size1 |
|
||||
nodes
|
||||
| test.cpp:13:33:13:37 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:15:31:15:35 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:19:34:19:38 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:22:17:22:21 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:23:33:23:37 | size1 | semmle.label | size1 |
|
||||
| test.cpp:30:27:30:31 | ... * ... | semmle.label | ... * ... |
|
||||
| test.cpp:31:27:31:31 | ... * ... | semmle.label | ... * ... |
|
||||
#select
|
||||
| test.cpp:13:33:13:37 | ... * ... | test.cpp:13:33:13:37 | ... * ... | test.cpp:13:33:13:37 | ... * ... | Potentially overflowing value from $@ is used in the size of this allocation. | test.cpp:13:33:13:37 | ... * ... | multiplication |
|
||||
| test.cpp:15:31:15:35 | ... * ... | test.cpp:15:31:15:35 | ... * ... | test.cpp:15:31:15:35 | ... * ... | Potentially overflowing value from $@ is used in the size of this allocation. | test.cpp:15:31:15:35 | ... * ... | multiplication |
|
||||
| test.cpp:19:34:19:38 | ... * ... | test.cpp:19:34:19:38 | ... * ... | test.cpp:19:34:19:38 | ... * ... | Potentially overflowing value from $@ is used in the size of this allocation. | test.cpp:19:34:19:38 | ... * ... | multiplication |
|
||||
| test.cpp:23:33:23:37 | size1 | test.cpp:22:17:22:21 | ... * ... | test.cpp:23:33:23:37 | size1 | Potentially overflowing value from $@ is used in the size of this allocation. | test.cpp:22:17:22:21 | ... * ... | multiplication |
|
||||
| test.cpp:30:27:30:31 | ... * ... | test.cpp:30:27:30:31 | ... * ... | test.cpp:30:27:30:31 | ... * ... | Potentially overflowing value from $@ is used in the size of this allocation. | test.cpp:30:27:30:31 | ... * ... | multiplication |
|
||||
| test.cpp:31:27:31:31 | ... * ... | test.cpp:31:27:31:31 | ... * ... | test.cpp:31:27:31:31 | ... * ... | Potentially overflowing value from $@ is used in the size of this allocation. | test.cpp:31:27:31:31 | ... * ... | multiplication |
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Security/CWE/CWE-190/AllocMultiplicationOverflow.ql
|
||||
@@ -0,0 +1,32 @@
|
||||
|
||||
typedef unsigned long size_t;
|
||||
void *malloc(size_t size);
|
||||
|
||||
int getAnInt();
|
||||
|
||||
void test()
|
||||
{
|
||||
int x = getAnInt();
|
||||
int y = getAnInt();
|
||||
|
||||
char *buffer1 = (char *)malloc(x + y); // GOOD
|
||||
char *buffer2 = (char *)malloc(x * y); // BAD
|
||||
int *buffer3 = (int *)malloc(x * sizeof(int)); // GOOD
|
||||
int *buffer4 = (int *)malloc(x * y * sizeof(int)); // BAD
|
||||
|
||||
if ((x <= 1000) && (y <= 1000))
|
||||
{
|
||||
char *buffer5 = (char *)malloc(x * y); // GOOD [FALSE POSITIVE]
|
||||
}
|
||||
|
||||
size_t size1 = x * y;
|
||||
char *buffer5 = (char *)malloc(size1); // BAD
|
||||
|
||||
size_t size2 = x;
|
||||
size2 *= y;
|
||||
char *buffer6 = (char *)malloc(size2); // BAD [NOT DETECTED]
|
||||
|
||||
char *buffer7 = new char[x * 10]; // GOOD
|
||||
char *buffer8 = new char[x * y]; // BAD
|
||||
char *buffer9 = new char[x * x]; // BAD
|
||||
}
|
||||
62
cpp/ql/test/library-tests/dataflow/fields/conflated.cpp
Normal file
62
cpp/ql/test/library-tests/dataflow/fields/conflated.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
int user_input();
|
||||
void sink(int);
|
||||
|
||||
struct A {
|
||||
int* p;
|
||||
int x;
|
||||
};
|
||||
|
||||
void pointer_without_allocation(const A& ra) {
|
||||
*ra.p = user_input();
|
||||
sink(*ra.p); // $ MISSING: ast,ir
|
||||
}
|
||||
|
||||
void argument_source(void*);
|
||||
void sink(void*);
|
||||
|
||||
void pointer_without_allocation_2() {
|
||||
char *raw;
|
||||
argument_source(raw);
|
||||
sink(raw); // $ ast MISSING: ir
|
||||
}
|
||||
|
||||
A* makeA() {
|
||||
return new A;
|
||||
}
|
||||
|
||||
void no_InitializeDynamicAllocation_instruction() {
|
||||
A* pa = makeA();
|
||||
pa->x = user_input();
|
||||
sink(pa->x); // $ ast MISSING: ir
|
||||
}
|
||||
|
||||
void fresh_or_arg(A* arg, bool unknown) {
|
||||
A* pa;
|
||||
pa = unknown ? arg : new A;
|
||||
pa->x = user_input();
|
||||
sink(pa->x); // $ ast MISSING: ir
|
||||
}
|
||||
|
||||
struct LinkedList {
|
||||
LinkedList* next;
|
||||
int y;
|
||||
|
||||
LinkedList() = default;
|
||||
LinkedList(LinkedList* next) : next(next) {}
|
||||
};
|
||||
|
||||
// Note: This example also suffers from #113: there is no ChiInstruction that merges the result of the
|
||||
// InitializeDynamicAllocation instruction into {AllAliasedMemory}. But even when that's fixed there's
|
||||
// still no dataflow because `ll->next->y = user_input()` writes to {AllAliasedMemory}.
|
||||
void too_many_indirections() {
|
||||
LinkedList* ll = new LinkedList;
|
||||
ll->next = new LinkedList;
|
||||
ll->next->y = user_input();
|
||||
sink(ll->next->y); // $ ast MISSING: ir
|
||||
}
|
||||
|
||||
void too_many_indirections_2(LinkedList* next) {
|
||||
LinkedList* ll = new LinkedList(next);
|
||||
ll->next->y = user_input();
|
||||
sink(ll->next->y); // $ ast MISSING: ir
|
||||
}
|
||||
@@ -121,6 +121,13 @@ postWithInFlow
|
||||
| by_reference.cpp:127:30:127:38 | inner_ptr [inner post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| complex.cpp:11:22:11:23 | a_ [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| complex.cpp:12:22:12:23 | b_ [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| conflated.cpp:10:3:10:7 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| conflated.cpp:10:7:10:7 | p [inner post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| conflated.cpp:29:7:29:7 | x [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| conflated.cpp:36:7:36:7 | x [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| conflated.cpp:53:7:53:10 | next [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| conflated.cpp:54:13:54:13 | y [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| conflated.cpp:60:13:60:13 | y [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| constructors.cpp:20:24:20:25 | a_ [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| constructors.cpp:21:24:21:25 | b_ [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
| qualifiers.cpp:9:36:9:36 | a [post update] | PostUpdateNode should not be the target of local flow. |
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
uniqueEnclosingCallable
|
||||
uniqueType
|
||||
uniqueNodeLocation
|
||||
| E.cpp:15:31:15:33 | buf | Node should have one location but has 2. |
|
||||
| aliasing.cpp:2:11:2:13 | (unnamed parameter 0) | Node should have one location but has 2. |
|
||||
| conflated.cpp:2:11:2:13 | (unnamed parameter 0) | Node should have one location but has 2. |
|
||||
| conflated.cpp:14:22:14:25 | buf | Node should have one location but has 2. |
|
||||
| file://:0:0:0:0 | (unnamed parameter 0) | Node should have one location but has 0. |
|
||||
| file://:0:0:0:0 | (unnamed parameter 0) | Node should have one location but has 0. |
|
||||
| file://:0:0:0:0 | (unnamed parameter 0) | Node should have one location but has 0. |
|
||||
@@ -129,6 +133,8 @@ postWithInFlow
|
||||
| complex.cpp:54:12:54:12 | Chi | PostUpdateNode should not be the target of local flow. |
|
||||
| complex.cpp:55:12:55:12 | Chi | PostUpdateNode should not be the target of local flow. |
|
||||
| complex.cpp:56:12:56:12 | Chi | PostUpdateNode should not be the target of local flow. |
|
||||
| conflated.cpp:45:39:45:42 | Chi | PostUpdateNode should not be the target of local flow. |
|
||||
| conflated.cpp:53:3:53:27 | Chi | PostUpdateNode should not be the target of local flow. |
|
||||
| constructors.cpp:20:24:20:29 | Chi | PostUpdateNode should not be the target of local flow. |
|
||||
| constructors.cpp:21:24:21:29 | Chi | PostUpdateNode should not be the target of local flow. |
|
||||
| constructors.cpp:23:28:23:28 | Chi | PostUpdateNode should not be the target of local flow. |
|
||||
|
||||
@@ -309,6 +309,22 @@
|
||||
| complex.cpp:62:7:62:8 | b2 | AST only |
|
||||
| complex.cpp:65:7:65:8 | b3 | AST only |
|
||||
| complex.cpp:68:7:68:8 | b4 | AST only |
|
||||
| conflated.cpp:10:3:10:7 | * ... | AST only |
|
||||
| conflated.cpp:10:4:10:5 | ra | AST only |
|
||||
| conflated.cpp:19:19:19:21 | raw | AST only |
|
||||
| conflated.cpp:20:8:20:10 | raw | AST only |
|
||||
| conflated.cpp:29:3:29:4 | pa | AST only |
|
||||
| conflated.cpp:29:7:29:7 | x | AST only |
|
||||
| conflated.cpp:36:3:36:4 | pa | AST only |
|
||||
| conflated.cpp:36:7:36:7 | x | AST only |
|
||||
| conflated.cpp:53:7:53:10 | next | AST only |
|
||||
| conflated.cpp:54:3:54:4 | ll | AST only |
|
||||
| conflated.cpp:54:7:54:10 | next | AST only |
|
||||
| conflated.cpp:54:13:54:13 | y | AST only |
|
||||
| conflated.cpp:59:35:59:38 | next | AST only |
|
||||
| conflated.cpp:60:3:60:4 | ll | AST only |
|
||||
| conflated.cpp:60:7:60:10 | next | AST only |
|
||||
| conflated.cpp:60:13:60:13 | y | AST only |
|
||||
| constructors.cpp:20:24:20:25 | a_ | AST only |
|
||||
| constructors.cpp:21:24:21:25 | b_ | AST only |
|
||||
| constructors.cpp:28:10:28:10 | f | AST only |
|
||||
|
||||
@@ -57,6 +57,7 @@
|
||||
| complex.cpp:54:6:54:10 | inner |
|
||||
| complex.cpp:55:6:55:10 | inner |
|
||||
| complex.cpp:56:6:56:10 | inner |
|
||||
| conflated.cpp:53:3:53:4 | ll |
|
||||
| constructors.cpp:20:24:20:25 | this |
|
||||
| constructors.cpp:21:24:21:25 | this |
|
||||
| qualifiers.cpp:9:30:9:33 | this |
|
||||
|
||||
@@ -366,6 +366,23 @@
|
||||
| complex.cpp:62:7:62:8 | b2 |
|
||||
| complex.cpp:65:7:65:8 | b3 |
|
||||
| complex.cpp:68:7:68:8 | b4 |
|
||||
| conflated.cpp:10:3:10:7 | * ... |
|
||||
| conflated.cpp:10:4:10:5 | ra |
|
||||
| conflated.cpp:19:19:19:21 | raw |
|
||||
| conflated.cpp:20:8:20:10 | raw |
|
||||
| conflated.cpp:29:3:29:4 | pa |
|
||||
| conflated.cpp:29:7:29:7 | x |
|
||||
| conflated.cpp:36:3:36:4 | pa |
|
||||
| conflated.cpp:36:7:36:7 | x |
|
||||
| conflated.cpp:53:3:53:4 | ll |
|
||||
| conflated.cpp:53:7:53:10 | next |
|
||||
| conflated.cpp:54:3:54:4 | ll |
|
||||
| conflated.cpp:54:7:54:10 | next |
|
||||
| conflated.cpp:54:13:54:13 | y |
|
||||
| conflated.cpp:59:35:59:38 | next |
|
||||
| conflated.cpp:60:3:60:4 | ll |
|
||||
| conflated.cpp:60:7:60:10 | next |
|
||||
| conflated.cpp:60:13:60:13 | y |
|
||||
| constructors.cpp:20:24:20:25 | a_ |
|
||||
| constructors.cpp:20:24:20:25 | this |
|
||||
| constructors.cpp:21:24:21:25 | b_ |
|
||||
|
||||
@@ -336,6 +336,27 @@ edges
|
||||
| complex.cpp:62:7:62:8 | b2 [inner, f, b_] | complex.cpp:40:17:40:17 | b [inner, f, b_] |
|
||||
| complex.cpp:65:7:65:8 | b3 [inner, f, a_] | complex.cpp:40:17:40:17 | b [inner, f, a_] |
|
||||
| complex.cpp:65:7:65:8 | b3 [inner, f, b_] | complex.cpp:40:17:40:17 | b [inner, f, b_] |
|
||||
| conflated.cpp:19:19:19:21 | ref arg raw | conflated.cpp:20:8:20:10 | raw |
|
||||
| conflated.cpp:29:3:29:4 | pa [post update] [x] | conflated.cpp:30:8:30:9 | pa [x] |
|
||||
| conflated.cpp:29:3:29:22 | ... = ... | conflated.cpp:29:3:29:4 | pa [post update] [x] |
|
||||
| conflated.cpp:29:11:29:20 | call to user_input | conflated.cpp:29:3:29:22 | ... = ... |
|
||||
| conflated.cpp:30:8:30:9 | pa [x] | conflated.cpp:30:12:30:12 | x |
|
||||
| conflated.cpp:36:3:36:4 | pa [post update] [x] | conflated.cpp:37:8:37:9 | pa [x] |
|
||||
| conflated.cpp:36:3:36:22 | ... = ... | conflated.cpp:36:3:36:4 | pa [post update] [x] |
|
||||
| conflated.cpp:36:11:36:20 | call to user_input | conflated.cpp:36:3:36:22 | ... = ... |
|
||||
| conflated.cpp:37:8:37:9 | pa [x] | conflated.cpp:37:12:37:12 | x |
|
||||
| conflated.cpp:54:3:54:4 | ll [post update] [next, y] | conflated.cpp:55:8:55:9 | ll [next, y] |
|
||||
| conflated.cpp:54:3:54:28 | ... = ... | conflated.cpp:54:7:54:10 | next [post update] [y] |
|
||||
| conflated.cpp:54:7:54:10 | next [post update] [y] | conflated.cpp:54:3:54:4 | ll [post update] [next, y] |
|
||||
| conflated.cpp:54:17:54:26 | call to user_input | conflated.cpp:54:3:54:28 | ... = ... |
|
||||
| conflated.cpp:55:8:55:9 | ll [next, y] | conflated.cpp:55:12:55:15 | next [y] |
|
||||
| conflated.cpp:55:12:55:15 | next [y] | conflated.cpp:55:18:55:18 | y |
|
||||
| conflated.cpp:60:3:60:4 | ll [post update] [next, y] | conflated.cpp:61:8:61:9 | ll [next, y] |
|
||||
| conflated.cpp:60:3:60:28 | ... = ... | conflated.cpp:60:7:60:10 | next [post update] [y] |
|
||||
| conflated.cpp:60:7:60:10 | next [post update] [y] | conflated.cpp:60:3:60:4 | ll [post update] [next, y] |
|
||||
| conflated.cpp:60:17:60:26 | call to user_input | conflated.cpp:60:3:60:28 | ... = ... |
|
||||
| conflated.cpp:61:8:61:9 | ll [next, y] | conflated.cpp:61:12:61:15 | next [y] |
|
||||
| conflated.cpp:61:12:61:15 | next [y] | conflated.cpp:61:18:61:18 | y |
|
||||
| constructors.cpp:26:15:26:15 | f [a_] | constructors.cpp:28:10:28:10 | f [a_] |
|
||||
| constructors.cpp:26:15:26:15 | f [b_] | constructors.cpp:29:10:29:10 | f [b_] |
|
||||
| constructors.cpp:28:10:28:10 | f [a_] | constructors.cpp:28:12:28:12 | call to a |
|
||||
@@ -827,6 +848,32 @@ nodes
|
||||
| complex.cpp:62:7:62:8 | b2 [inner, f, b_] | semmle.label | b2 [inner, f, b_] |
|
||||
| complex.cpp:65:7:65:8 | b3 [inner, f, a_] | semmle.label | b3 [inner, f, a_] |
|
||||
| complex.cpp:65:7:65:8 | b3 [inner, f, b_] | semmle.label | b3 [inner, f, b_] |
|
||||
| conflated.cpp:19:19:19:21 | ref arg raw | semmle.label | ref arg raw |
|
||||
| conflated.cpp:20:8:20:10 | raw | semmle.label | raw |
|
||||
| conflated.cpp:29:3:29:4 | pa [post update] [x] | semmle.label | pa [post update] [x] |
|
||||
| conflated.cpp:29:3:29:22 | ... = ... | semmle.label | ... = ... |
|
||||
| conflated.cpp:29:11:29:20 | call to user_input | semmle.label | call to user_input |
|
||||
| conflated.cpp:30:8:30:9 | pa [x] | semmle.label | pa [x] |
|
||||
| conflated.cpp:30:12:30:12 | x | semmle.label | x |
|
||||
| conflated.cpp:36:3:36:4 | pa [post update] [x] | semmle.label | pa [post update] [x] |
|
||||
| conflated.cpp:36:3:36:22 | ... = ... | semmle.label | ... = ... |
|
||||
| conflated.cpp:36:11:36:20 | call to user_input | semmle.label | call to user_input |
|
||||
| conflated.cpp:37:8:37:9 | pa [x] | semmle.label | pa [x] |
|
||||
| conflated.cpp:37:12:37:12 | x | semmle.label | x |
|
||||
| conflated.cpp:54:3:54:4 | ll [post update] [next, y] | semmle.label | ll [post update] [next, y] |
|
||||
| conflated.cpp:54:3:54:28 | ... = ... | semmle.label | ... = ... |
|
||||
| conflated.cpp:54:7:54:10 | next [post update] [y] | semmle.label | next [post update] [y] |
|
||||
| conflated.cpp:54:17:54:26 | call to user_input | semmle.label | call to user_input |
|
||||
| conflated.cpp:55:8:55:9 | ll [next, y] | semmle.label | ll [next, y] |
|
||||
| conflated.cpp:55:12:55:15 | next [y] | semmle.label | next [y] |
|
||||
| conflated.cpp:55:18:55:18 | y | semmle.label | y |
|
||||
| conflated.cpp:60:3:60:4 | ll [post update] [next, y] | semmle.label | ll [post update] [next, y] |
|
||||
| conflated.cpp:60:3:60:28 | ... = ... | semmle.label | ... = ... |
|
||||
| conflated.cpp:60:7:60:10 | next [post update] [y] | semmle.label | next [post update] [y] |
|
||||
| conflated.cpp:60:17:60:26 | call to user_input | semmle.label | call to user_input |
|
||||
| conflated.cpp:61:8:61:9 | ll [next, y] | semmle.label | ll [next, y] |
|
||||
| conflated.cpp:61:12:61:15 | next [y] | semmle.label | next [y] |
|
||||
| conflated.cpp:61:18:61:18 | y | semmle.label | y |
|
||||
| constructors.cpp:26:15:26:15 | f [a_] | semmle.label | f [a_] |
|
||||
| constructors.cpp:26:15:26:15 | f [b_] | semmle.label | f [b_] |
|
||||
| constructors.cpp:28:10:28:10 | f [a_] | semmle.label | f [a_] |
|
||||
@@ -1028,6 +1075,11 @@ nodes
|
||||
| complex.cpp:42:18:42:18 | call to a | complex.cpp:55:19:55:28 | call to user_input | complex.cpp:42:18:42:18 | call to a | call to a flows from $@ | complex.cpp:55:19:55:28 | call to user_input | call to user_input |
|
||||
| complex.cpp:43:18:43:18 | call to b | complex.cpp:54:19:54:28 | call to user_input | complex.cpp:43:18:43:18 | call to b | call to b flows from $@ | complex.cpp:54:19:54:28 | call to user_input | call to user_input |
|
||||
| complex.cpp:43:18:43:18 | call to b | complex.cpp:56:19:56:28 | call to user_input | complex.cpp:43:18:43:18 | call to b | call to b flows from $@ | complex.cpp:56:19:56:28 | call to user_input | call to user_input |
|
||||
| conflated.cpp:20:8:20:10 | raw | conflated.cpp:19:19:19:21 | ref arg raw | conflated.cpp:20:8:20:10 | raw | raw flows from $@ | conflated.cpp:19:19:19:21 | ref arg raw | ref arg raw |
|
||||
| conflated.cpp:30:12:30:12 | x | conflated.cpp:29:11:29:20 | call to user_input | conflated.cpp:30:12:30:12 | x | x flows from $@ | conflated.cpp:29:11:29:20 | call to user_input | call to user_input |
|
||||
| conflated.cpp:37:12:37:12 | x | conflated.cpp:36:11:36:20 | call to user_input | conflated.cpp:37:12:37:12 | x | x flows from $@ | conflated.cpp:36:11:36:20 | call to user_input | call to user_input |
|
||||
| conflated.cpp:55:18:55:18 | y | conflated.cpp:54:17:54:26 | call to user_input | conflated.cpp:55:18:55:18 | y | y flows from $@ | conflated.cpp:54:17:54:26 | call to user_input | call to user_input |
|
||||
| conflated.cpp:61:18:61:18 | y | conflated.cpp:60:17:60:26 | call to user_input | conflated.cpp:61:18:61:18 | y | y flows from $@ | conflated.cpp:60:17:60:26 | call to user_input | call to user_input |
|
||||
| constructors.cpp:28:12:28:12 | call to a | constructors.cpp:34:11:34:20 | call to user_input | constructors.cpp:28:12:28:12 | call to a | call to a flows from $@ | constructors.cpp:34:11:34:20 | call to user_input | call to user_input |
|
||||
| constructors.cpp:28:12:28:12 | call to a | constructors.cpp:36:11:36:20 | call to user_input | constructors.cpp:28:12:28:12 | call to a | call to a flows from $@ | constructors.cpp:36:11:36:20 | call to user_input | call to user_input |
|
||||
| constructors.cpp:29:12:29:12 | call to b | constructors.cpp:35:14:35:23 | call to user_input | constructors.cpp:29:12:29:12 | call to b | call to b flows from $@ | constructors.cpp:35:14:35:23 | call to user_input | call to user_input |
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
lgtm,codescanning
|
||||
* CIL extraction has been improved to store `modreq` and `modopt` custom modifiers.
|
||||
The extracted information is surfaced through the `CustomModifierReceiver` class. Additionally,
|
||||
the information is also used to evaluate the new `Setter::isInitOnly` predicate.
|
||||
@@ -76,9 +76,10 @@ namespace Semmle.Extraction.CIL.Entities
|
||||
|
||||
var typeSignature = md.DecodeSignature(Cx.TypeSignatureDecoder, this);
|
||||
|
||||
Parameters = MakeParameters(typeSignature.ParameterTypes).ToArray();
|
||||
var parameters = GetParameterExtractionProducts(typeSignature.ParameterTypes).ToArray();
|
||||
Parameters = parameters.OfType<Parameter>().ToArray();
|
||||
|
||||
foreach (var c in Parameters)
|
||||
foreach (var c in parameters)
|
||||
yield return c;
|
||||
|
||||
foreach (var c in PopulateFlags)
|
||||
@@ -95,7 +96,12 @@ namespace Semmle.Extraction.CIL.Entities
|
||||
}
|
||||
|
||||
yield return Tuples.metadata_handle(this, Cx.Assembly, MetadataTokens.GetToken(handle));
|
||||
yield return Tuples.cil_method(this, Name, declaringType, typeSignature.ReturnType);
|
||||
|
||||
foreach (var m in GetMethodExtractionProducts(Name, declaringType, typeSignature.ReturnType))
|
||||
{
|
||||
yield return m;
|
||||
}
|
||||
|
||||
yield return Tuples.cil_method_source_declaration(this, this);
|
||||
yield return Tuples.cil_method_location(this, Cx.Assembly);
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace Semmle.Extraction.CIL.Entities
|
||||
/// <summary>
|
||||
/// An entity representing a field.
|
||||
/// </summary>
|
||||
internal abstract class Field : GenericContext, IMember
|
||||
internal abstract class Field : GenericContext, IMember, ICustomModifierReceiver
|
||||
{
|
||||
protected Field(Context cx) : base(cx)
|
||||
{
|
||||
@@ -45,7 +45,13 @@ namespace Semmle.Extraction.CIL.Entities
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return Tuples.cil_field(this, DeclaringType, Name, Type);
|
||||
var t = Type;
|
||||
if (t is ModifiedType mt)
|
||||
{
|
||||
t = mt.Unmodified;
|
||||
yield return Tuples.cil_custom_modifiers(this, mt.Modifier, mt.IsRequired);
|
||||
}
|
||||
yield return Tuples.cil_field(this, DeclaringType, Name, t);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -76,12 +76,16 @@ namespace Semmle.Extraction.CIL.Entities
|
||||
|
||||
var typeSignature = mr.DecodeMethodSignature(Cx.TypeSignatureDecoder, this);
|
||||
|
||||
Parameters = MakeParameters(typeSignature.ParameterTypes).ToArray();
|
||||
foreach (var p in Parameters) yield return p;
|
||||
var parameters = GetParameterExtractionProducts(typeSignature.ParameterTypes).ToArray();
|
||||
Parameters = parameters.OfType<Parameter>().ToArray();
|
||||
foreach (var p in parameters) yield return p;
|
||||
|
||||
foreach (var f in PopulateFlags) yield return f;
|
||||
|
||||
yield return Tuples.cil_method(this, Name, DeclaringType, typeSignature.ReturnType);
|
||||
foreach (var m in GetMethodExtractionProducts(Name, DeclaringType, typeSignature.ReturnType))
|
||||
{
|
||||
yield return m;
|
||||
}
|
||||
|
||||
if (SourceDeclaration != null)
|
||||
yield return Tuples.cil_method_source_declaration(this, SourceDeclaration);
|
||||
|
||||
@@ -3,14 +3,13 @@ using System.Reflection.Metadata;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.IO;
|
||||
using Semmle.Util;
|
||||
|
||||
namespace Semmle.Extraction.CIL.Entities
|
||||
{
|
||||
/// <summary>
|
||||
/// A method entity.
|
||||
/// </summary>
|
||||
internal abstract class Method : TypeContainer, IMember
|
||||
internal abstract class Method : TypeContainer, IMember, ICustomModifierReceiver
|
||||
{
|
||||
protected MethodTypeParameter[]? genericParams;
|
||||
protected GenericContext gc;
|
||||
@@ -21,6 +20,8 @@ namespace Semmle.Extraction.CIL.Entities
|
||||
this.gc = gc;
|
||||
}
|
||||
|
||||
public ITypeSignature ReturnType => signature.ReturnType;
|
||||
|
||||
public override IEnumerable<Type> TypeParameters => gc.TypeParameters.Concat(DeclaringType.TypeParameters);
|
||||
|
||||
public override IEnumerable<Type> MethodParameters =>
|
||||
@@ -76,7 +77,7 @@ namespace Semmle.Extraction.CIL.Entities
|
||||
|
||||
public abstract bool IsStatic { get; }
|
||||
|
||||
protected IEnumerable<Parameter> MakeParameters(IEnumerable<Type> parameterTypes)
|
||||
protected IEnumerable<IExtractionProduct> GetParameterExtractionProducts(IEnumerable<Type> parameterTypes)
|
||||
{
|
||||
var i = 0;
|
||||
|
||||
@@ -86,7 +87,26 @@ namespace Semmle.Extraction.CIL.Entities
|
||||
}
|
||||
|
||||
foreach (var p in parameterTypes)
|
||||
yield return Cx.Populate(new Parameter(Cx, this, i++, p));
|
||||
{
|
||||
var t = p;
|
||||
if (t is ModifiedType mt)
|
||||
{
|
||||
t = mt.Unmodified;
|
||||
yield return Tuples.cil_custom_modifiers(this, mt.Modifier, mt.IsRequired);
|
||||
}
|
||||
yield return Cx.Populate(new Parameter(Cx, this, i++, t));
|
||||
}
|
||||
}
|
||||
|
||||
protected IEnumerable<IExtractionProduct> GetMethodExtractionProducts(string name, Type declaringType, Type returnType)
|
||||
{
|
||||
var t = returnType;
|
||||
if (t is ModifiedType mt)
|
||||
{
|
||||
t = mt.Unmodified;
|
||||
yield return Tuples.cil_custom_modifiers(this, mt.Modifier, mt.IsRequired);
|
||||
}
|
||||
yield return Tuples.cil_method(this, name, declaringType, t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,14 +77,19 @@ namespace Semmle.Extraction.CIL.Entities
|
||||
throw new InternalError($"Unexpected constructed method handle kind {ms.Method.Kind}");
|
||||
}
|
||||
|
||||
Parameters = MakeParameters(constructedTypeSignature.ParameterTypes).ToArray();
|
||||
foreach (var p in Parameters)
|
||||
var parameters = GetParameterExtractionProducts(constructedTypeSignature.ParameterTypes).ToArray();
|
||||
Parameters = parameters.OfType<Parameter>().ToArray();
|
||||
foreach (var p in parameters)
|
||||
yield return p;
|
||||
|
||||
foreach (var f in PopulateFlags)
|
||||
yield return f;
|
||||
|
||||
yield return Tuples.cil_method(this, Name, DeclaringType, constructedTypeSignature.ReturnType);
|
||||
foreach (var m in GetMethodExtractionProducts(Name, DeclaringType, constructedTypeSignature.ReturnType))
|
||||
{
|
||||
yield return m;
|
||||
}
|
||||
|
||||
yield return Tuples.cil_method_source_declaration(this, SourceDeclaration);
|
||||
|
||||
if (typeParams.Length != unboundMethod.GenericParameterCount)
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace Semmle.Extraction.CIL.Entities
|
||||
{
|
||||
/// <summary>
|
||||
/// Modified types are not written directly to trap files. Instead, the modifiers are stored
|
||||
/// on the modifiable entity (field type, property/method/function pointer parameter or return types).
|
||||
/// </summary>
|
||||
internal sealed class ModifiedType : Type
|
||||
{
|
||||
public ModifiedType(Context cx, Type unmodified, Type modifier, bool isRequired) : base(cx)
|
||||
{
|
||||
Unmodified = unmodified;
|
||||
Modifier = modifier;
|
||||
IsRequired = isRequired;
|
||||
}
|
||||
|
||||
public Type Unmodified { get; }
|
||||
public Type Modifier { get; }
|
||||
public bool IsRequired { get; }
|
||||
|
||||
public override CilTypeKind Kind => throw new NotImplementedException();
|
||||
|
||||
public override Namespace? ContainingNamespace => throw new NotImplementedException();
|
||||
|
||||
public override Type? ContainingType => throw new NotImplementedException();
|
||||
|
||||
public override IEnumerable<Type> TypeParameters => throw new NotImplementedException();
|
||||
|
||||
public override int ThisTypeParameterCount => throw new NotImplementedException();
|
||||
|
||||
public override Type Construct(IEnumerable<Type> typeArguments) => throw new NotImplementedException();
|
||||
|
||||
public override string Name => $"{Unmodified.Name} {(IsRequired ? "modreq" : "modopt")}({Modifier.Name})";
|
||||
|
||||
public override void WriteAssemblyPrefix(TextWriter trapFile) => throw new NotImplementedException();
|
||||
|
||||
public override void WriteId(TextWriter trapFile, bool inContext) => throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ namespace Semmle.Extraction.CIL.Entities
|
||||
/// <summary>
|
||||
/// A property.
|
||||
/// </summary>
|
||||
internal sealed class Property : LabelledEntity
|
||||
internal sealed class Property : LabelledEntity, ICustomModifierReceiver
|
||||
{
|
||||
private readonly Handle handle;
|
||||
private readonly Type type;
|
||||
@@ -54,7 +54,15 @@ namespace Semmle.Extraction.CIL.Entities
|
||||
yield return Tuples.metadata_handle(this, Cx.Assembly, MetadataTokens.GetToken(handle));
|
||||
var sig = pd.DecodeSignature(Cx.TypeSignatureDecoder, type);
|
||||
|
||||
yield return Tuples.cil_property(this, type, Cx.ShortName(pd.Name), sig.ReturnType);
|
||||
var name = Cx.ShortName(pd.Name);
|
||||
|
||||
var t = sig.ReturnType;
|
||||
if (t is ModifiedType mt)
|
||||
{
|
||||
t = mt.Unmodified;
|
||||
yield return Tuples.cil_custom_modifiers(this, mt.Modifier, mt.IsRequired);
|
||||
}
|
||||
yield return Tuples.cil_property(this, type, name, t);
|
||||
|
||||
var accessors = pd.GetAccessors();
|
||||
if (!accessors.Getter.IsNil)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Reflection.Metadata;
|
||||
using System.Collections.Immutable;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace Semmle.Extraction.CIL.Entities
|
||||
{
|
||||
@@ -138,21 +139,28 @@ namespace Semmle.Extraction.CIL.Entities
|
||||
private class Modified : ITypeSignature
|
||||
{
|
||||
private readonly ITypeSignature unmodifiedType;
|
||||
private readonly ITypeSignature modifier;
|
||||
private readonly bool isRequired;
|
||||
|
||||
public Modified(ITypeSignature unmodifiedType)
|
||||
public Modified(ITypeSignature unmodifiedType, ITypeSignature modifier, bool isRequired)
|
||||
{
|
||||
this.unmodifiedType = unmodifiedType;
|
||||
this.modifier = modifier;
|
||||
this.isRequired = isRequired;
|
||||
}
|
||||
|
||||
public void WriteId(TextWriter trapFile, GenericContext gc)
|
||||
{
|
||||
unmodifiedType.WriteId(trapFile, gc);
|
||||
trapFile.Write(isRequired ? " modreq(" : " modopt(");
|
||||
modifier.WriteId(trapFile, gc);
|
||||
trapFile.Write(")");
|
||||
}
|
||||
}
|
||||
|
||||
ITypeSignature ISignatureTypeProvider<ITypeSignature, object>.GetModifiedType(ITypeSignature modifier, ITypeSignature unmodifiedType, bool isRequired)
|
||||
{
|
||||
return new Modified(unmodifiedType);
|
||||
return new Modified(unmodifiedType, modifier, isRequired);
|
||||
}
|
||||
|
||||
private class Pinned : ITypeSignature
|
||||
|
||||
@@ -35,7 +35,7 @@ namespace Semmle.Extraction.CIL.Entities
|
||||
genericContext.GetGenericTypeParameter(index);
|
||||
|
||||
Type ISignatureTypeProvider<Type, GenericContext>.GetModifiedType(Type modifier, Type unmodifiedType, bool isRequired) =>
|
||||
unmodifiedType; // !! Not implemented properly
|
||||
new ModifiedType(cx, unmodifiedType, modifier, isRequired);
|
||||
|
||||
Type ISignatureTypeProvider<Type, GenericContext>.GetPinnedType(Type elementType) =>
|
||||
cx.Populate(new PointerType(cx, elementType));
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Semmle.Extraction.CIL
|
||||
{
|
||||
internal interface ICustomModifierReceiver
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -188,6 +188,9 @@ namespace Semmle.Extraction.CIL
|
||||
internal static Tuple cil_virtual(Method method) =>
|
||||
new Tuple("cil_virtual", method);
|
||||
|
||||
internal static Tuple cil_custom_modifiers(ICustomModifierReceiver receiver, Type modifier, bool isRequired) =>
|
||||
new Tuple("cil_custom_modifiers", receiver, modifier, isRequired ? 1 : 0);
|
||||
|
||||
internal static Tuple containerparent(Folder parent, IFileOrFolder child) =>
|
||||
new Tuple("containerparent", parent, child);
|
||||
|
||||
|
||||
@@ -66,12 +66,25 @@ namespace Semmle.Extraction.CSharp.Entities
|
||||
|
||||
private void ExtractArguments(TextWriter trapFile)
|
||||
{
|
||||
var ctorArguments = attributeSyntax?.ArgumentList?.Arguments.Where(a => a.NameEquals == null).ToList();
|
||||
|
||||
var childIndex = 0;
|
||||
foreach (var constructorArgument in symbol.ConstructorArguments)
|
||||
for (var i = 0; i < symbol.ConstructorArguments.Length; i++)
|
||||
{
|
||||
var constructorArgument = symbol.ConstructorArguments[i];
|
||||
var paramName = symbol.AttributeConstructor?.Parameters[i].Name;
|
||||
var argSyntax = ctorArguments?.SingleOrDefault(a => a.NameColon != null && a.NameColon.Name.Identifier.Text == paramName);
|
||||
|
||||
if (argSyntax == null && // couldn't find named argument
|
||||
ctorArguments?.Count > childIndex && // there're more arguments
|
||||
ctorArguments[childIndex].NameColon == null) // the argument is positional
|
||||
{
|
||||
argSyntax = ctorArguments[childIndex];
|
||||
}
|
||||
|
||||
CreateExpressionFromArgument(
|
||||
constructorArgument,
|
||||
attributeSyntax?.ArgumentList.Arguments[childIndex].Expression,
|
||||
argSyntax?.Expression,
|
||||
this,
|
||||
childIndex++);
|
||||
}
|
||||
|
||||
@@ -87,6 +87,11 @@ namespace Semmle.Extraction.CSharp.Entities
|
||||
foreach (var l in Locations)
|
||||
trapFile.type_location(this, l);
|
||||
}
|
||||
|
||||
if (symbol.IsAnonymousType)
|
||||
{
|
||||
trapFile.anonymous_types(this);
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Lazy<Type[]> typeArgumentsLazy;
|
||||
|
||||
@@ -271,6 +271,11 @@ namespace Semmle.Extraction.CSharp
|
||||
trapFile.WriteTuple("extend", type, super);
|
||||
}
|
||||
|
||||
internal static void anonymous_types(this TextWriter trapFile, Type type)
|
||||
{
|
||||
trapFile.WriteTuple("anonymous_types", type);
|
||||
}
|
||||
|
||||
internal static void field_location(this TextWriter trapFile, Field field, Location location)
|
||||
{
|
||||
trapFile.WriteTuple("field_location", field, location);
|
||||
|
||||
30
csharp/extractor/Semmle.Extraction/AssemblyScope.cs
Normal file
30
csharp/extractor/Semmle.Extraction/AssemblyScope.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace Semmle.Extraction
|
||||
{
|
||||
/// <summary>
|
||||
/// The scope of symbols in an assembly.
|
||||
/// </summary>
|
||||
public class AssemblyScope : IExtractionScope
|
||||
{
|
||||
private readonly IAssemblySymbol assembly;
|
||||
private readonly string filepath;
|
||||
|
||||
public AssemblyScope(IAssemblySymbol symbol, string path, bool isOutput)
|
||||
{
|
||||
assembly = symbol;
|
||||
filepath = path;
|
||||
IsGlobalScope = isOutput;
|
||||
}
|
||||
|
||||
public bool IsGlobalScope { get; }
|
||||
|
||||
public bool InFileScope(string path) => path == filepath;
|
||||
|
||||
public bool InScope(ISymbol symbol) =>
|
||||
SymbolEqualityComparer.Default.Equals(symbol.ContainingAssembly, assembly) ||
|
||||
SymbolEqualityComparer.Default.Equals(symbol, assembly);
|
||||
|
||||
public bool FromSource => false;
|
||||
}
|
||||
}
|
||||
@@ -215,7 +215,7 @@ namespace Semmle.Extraction
|
||||
}
|
||||
catch (Exception ex) // lgtm[cs/catch-of-all-exceptions]
|
||||
{
|
||||
ExtractionError("Uncaught exception", ex.Message, GeneratedLocation.Create(this), ex.StackTrace);
|
||||
ExtractionError("Uncaught exception", ex.Message, Entities.Location.Create(this), ex.StackTrace);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -250,6 +250,8 @@ namespace Semmle.Extraction
|
||||
|
||||
private IExtractionScope scope { get; }
|
||||
|
||||
public SyntaxTree? SourceTree => scope is SourceScope sc ? sc.SourceTree : null;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the given symbol needs to be defined in this context.
|
||||
/// This is the case if the symbol is contained in the source/assembly, or
|
||||
@@ -451,7 +453,7 @@ namespace Semmle.Extraction
|
||||
}
|
||||
else
|
||||
{
|
||||
ExtractionError(message, "", GeneratedLocation.Create(this));
|
||||
ExtractionError(message, "", Entities.Location.Create(this));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -522,13 +524,21 @@ namespace Semmle.Extraction
|
||||
Message message;
|
||||
|
||||
if (node != null)
|
||||
{
|
||||
message = Message.Create(context, ex.Message, node, ex.StackTrace);
|
||||
}
|
||||
else if (symbol != null)
|
||||
{
|
||||
message = Message.Create(context, ex.Message, symbol, ex.StackTrace);
|
||||
}
|
||||
else if (ex is InternalError ie)
|
||||
{
|
||||
message = new Message(ie.Text, ie.EntityText, Entities.Location.Create(context, ie.Location), ex.StackTrace);
|
||||
}
|
||||
else
|
||||
message = new Message("Uncaught exception", ex.Message, GeneratedLocation.Create(context), ex.StackTrace);
|
||||
{
|
||||
message = new Message("Uncaught exception", ex.Message, Entities.Location.Create(context), ex.StackTrace);
|
||||
}
|
||||
|
||||
context.ExtractionError(message);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace Semmle.Extraction.Entities
|
||||
|
||||
protected override void Populate(TextWriter trapFile)
|
||||
{
|
||||
trapFile.extractor_messages(this, msg.Severity, "C# extractor", msg.Text, msg.EntityText, msg.Location ?? GeneratedLocation.Create(cx), msg.StackTrace);
|
||||
trapFile.extractor_messages(this, msg.Severity, "C# extractor", msg.Text, msg.EntityText, msg.Location ?? Location.Create(cx), msg.StackTrace);
|
||||
}
|
||||
|
||||
public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.NoLabel;
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace Semmle.Extraction.Entities
|
||||
|
||||
public override bool Equals(object? obj) => obj != null && obj.GetType() == typeof(GeneratedLocation);
|
||||
|
||||
public static GeneratedLocation Create(Context cx) => GeneratedLocationFactory.Instance.CreateEntity(cx, typeof(GeneratedLocation), null);
|
||||
public static new GeneratedLocation Create(Context cx) => GeneratedLocationFactory.Instance.CreateEntity(cx, typeof(GeneratedLocation), null);
|
||||
|
||||
private class GeneratedLocationFactory : ICachedEntityFactory<string?, GeneratedLocation>
|
||||
{
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
|
||||
namespace Semmle.Extraction.Entities
|
||||
{
|
||||
public abstract class Location : CachedEntity<Microsoft.CodeAnalysis.Location?>
|
||||
@@ -13,6 +15,13 @@ namespace Semmle.Extraction.Entities
|
||||
? NonGeneratedSourceLocation.Create(cx, loc)
|
||||
: Assembly.Create(cx, loc);
|
||||
|
||||
public static Location Create(Context cx)
|
||||
{
|
||||
return cx.SourceTree == null
|
||||
? GeneratedLocation.Create(cx)
|
||||
: Create(cx, Microsoft.CodeAnalysis.Location.Create(cx.SourceTree, TextSpan.FromBounds(0, 0)));
|
||||
}
|
||||
|
||||
public override Microsoft.CodeAnalysis.Location? ReportingLocation => symbol;
|
||||
|
||||
public override TrapStackBehaviour TrapStackBehaviour => TrapStackBehaviour.OptionalLabel;
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
using System.Linq;
|
||||
|
||||
namespace Semmle.Extraction
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines which entities belong in the trap file
|
||||
/// for the currently extracted entity. This is used to ensure that
|
||||
/// trap files do not contain redundant information. Generally a symbol
|
||||
/// should have an affinity with exactly one trap file, except for constructed
|
||||
/// symbols.
|
||||
/// </summary>
|
||||
public interface IExtractionScope
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether the given symbol belongs in the trap file.
|
||||
/// </summary>
|
||||
/// <param name="symbol">The symbol to populate.</param>
|
||||
bool InScope(ISymbol symbol);
|
||||
|
||||
/// <summary>
|
||||
/// Whether the given file belongs in the trap file.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to populate.</param>
|
||||
bool InFileScope(string path);
|
||||
|
||||
bool IsGlobalScope { get; }
|
||||
|
||||
bool FromSource { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The scope of symbols in an assembly.
|
||||
/// </summary>
|
||||
public class AssemblyScope : IExtractionScope
|
||||
{
|
||||
private readonly IAssemblySymbol assembly;
|
||||
private readonly string filepath;
|
||||
|
||||
public AssemblyScope(IAssemblySymbol symbol, string path, bool isOutput)
|
||||
{
|
||||
assembly = symbol;
|
||||
filepath = path;
|
||||
IsGlobalScope = isOutput;
|
||||
}
|
||||
|
||||
public bool IsGlobalScope { get; }
|
||||
|
||||
public bool InFileScope(string path) => path == filepath;
|
||||
|
||||
public bool InScope(ISymbol symbol) =>
|
||||
SymbolEqualityComparer.Default.Equals(symbol.ContainingAssembly, assembly) ||
|
||||
SymbolEqualityComparer.Default.Equals(symbol, assembly);
|
||||
|
||||
public bool FromSource => false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The scope of symbols in a source file.
|
||||
/// </summary>
|
||||
public class SourceScope : IExtractionScope
|
||||
{
|
||||
private readonly SyntaxTree sourceTree;
|
||||
|
||||
public SourceScope(SyntaxTree tree)
|
||||
{
|
||||
sourceTree = tree;
|
||||
}
|
||||
|
||||
public bool IsGlobalScope => false;
|
||||
|
||||
public bool InFileScope(string path) => path == sourceTree.FilePath;
|
||||
|
||||
public bool InScope(ISymbol symbol) => symbol.Locations.Any(loc => loc.SourceTree == sourceTree);
|
||||
|
||||
public bool FromSource => true;
|
||||
}
|
||||
}
|
||||
30
csharp/extractor/Semmle.Extraction/IExtractionScope.cs
Normal file
30
csharp/extractor/Semmle.Extraction/IExtractionScope.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace Semmle.Extraction
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines which entities belong in the trap file
|
||||
/// for the currently extracted entity. This is used to ensure that
|
||||
/// trap files do not contain redundant information. Generally a symbol
|
||||
/// should have an affinity with exactly one trap file, except for constructed
|
||||
/// symbols.
|
||||
/// </summary>
|
||||
public interface IExtractionScope
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether the given symbol belongs in the trap file.
|
||||
/// </summary>
|
||||
/// <param name="symbol">The symbol to populate.</param>
|
||||
bool InScope(ISymbol symbol);
|
||||
|
||||
/// <summary>
|
||||
/// Whether the given file belongs in the trap file.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to populate.</param>
|
||||
bool InFileScope(string path);
|
||||
|
||||
bool IsGlobalScope { get; }
|
||||
|
||||
bool FromSource { get; }
|
||||
}
|
||||
}
|
||||
27
csharp/extractor/Semmle.Extraction/SourceScope.cs
Normal file
27
csharp/extractor/Semmle.Extraction/SourceScope.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
using System.Linq;
|
||||
|
||||
namespace Semmle.Extraction
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// The scope of symbols in a source file.
|
||||
/// </summary>
|
||||
public class SourceScope : IExtractionScope
|
||||
{
|
||||
public SyntaxTree SourceTree { get; }
|
||||
|
||||
public SourceScope(SyntaxTree tree)
|
||||
{
|
||||
SourceTree = tree;
|
||||
}
|
||||
|
||||
public bool IsGlobalScope => false;
|
||||
|
||||
public bool InFileScope(string path) => path == SourceTree.FilePath;
|
||||
|
||||
public bool InScope(ISymbol symbol) => symbol.Locations.Any(loc => loc.SourceTree == SourceTree);
|
||||
|
||||
public bool FromSource => true;
|
||||
}
|
||||
}
|
||||
@@ -18,3 +18,4 @@ import ControlFlow
|
||||
import DataFlow
|
||||
import Attribute
|
||||
import Stubs
|
||||
import CustomModifierReceiver
|
||||
|
||||
21
csharp/ql/src/semmle/code/cil/CustomModifierReceiver.qll
Normal file
21
csharp/ql/src/semmle/code/cil/CustomModifierReceiver.qll
Normal file
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Provides a class to represent `modopt` and `modreq` declarations.
|
||||
*/
|
||||
|
||||
private import CIL
|
||||
private import dotnet
|
||||
|
||||
/**
|
||||
* A class to represent entities that can receive custom modifiers. Custom modifiers can be attached to
|
||||
* - the type of a `Field`,
|
||||
* - the return type of a `Method` or `Property`,
|
||||
* - the type of parameters.
|
||||
* A `CustomModifierReceiver` is therefore either a `Field`, `Property`, `Method`, or `Parameter`.
|
||||
*/
|
||||
class CustomModifierReceiver extends Declaration, @cil_custom_modifier_receiver {
|
||||
/** Holds if this targeted type has `modifier` applied as `modreq`. */
|
||||
predicate hasRequiredCustomModifier(Type modifier) { cil_custom_modifiers(this, modifier, 1) }
|
||||
|
||||
/** Holds if this targeted type has `modifier` applied as `modopt`. */
|
||||
predicate hasOptionalCustomModifier(Type modifier) { cil_custom_modifiers(this, modifier, 0) }
|
||||
}
|
||||
@@ -82,7 +82,7 @@ class Member extends DotNet::Member, Declaration, @cil_member {
|
||||
}
|
||||
|
||||
/** A property. */
|
||||
class Property extends DotNet::Property, Member, @cil_property {
|
||||
class Property extends DotNet::Property, Member, CustomModifierReceiver, @cil_property {
|
||||
override string getName() { cil_property(this, _, result, _) }
|
||||
|
||||
/** Gets the type of this property. */
|
||||
|
||||
@@ -66,7 +66,8 @@ class MethodImplementation extends EntryPoint, @cil_method_implementation {
|
||||
* A method, which corresponds to any callable in C#, including constructors,
|
||||
* destructors, operators, accessors and so on.
|
||||
*/
|
||||
class Method extends DotNet::Callable, Element, Member, TypeContainer, DataFlowNode, @cil_method {
|
||||
class Method extends DotNet::Callable, Element, Member, TypeContainer, DataFlowNode,
|
||||
CustomModifierReceiver, @cil_method {
|
||||
/**
|
||||
* Gets a method implementation, if any. Note that there can
|
||||
* be several implementations in different assemblies.
|
||||
@@ -246,6 +247,13 @@ class Setter extends Accessor {
|
||||
Setter() { cil_setter(_, this) }
|
||||
|
||||
override Property getProperty() { cil_setter(result, this) }
|
||||
|
||||
/** Holds if this setter is an `init` accessor. */
|
||||
predicate isInitOnly() {
|
||||
exists(Type t | t.getQualifiedName() = "System.Runtime.CompilerServices.IsExternalInit" |
|
||||
this.hasRequiredCustomModifier(t)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -57,7 +57,7 @@ class LocalVariable extends StackVariable, @cil_local_variable {
|
||||
}
|
||||
|
||||
/** A method parameter. */
|
||||
class Parameter extends DotNet::Parameter, StackVariable, @cil_parameter {
|
||||
class Parameter extends DotNet::Parameter, StackVariable, CustomModifierReceiver, @cil_parameter {
|
||||
/** Gets the method declaring this parameter. */
|
||||
override Method getMethod() { this = result.getARawParameter() }
|
||||
|
||||
@@ -122,7 +122,7 @@ class ThisParameter extends Parameter {
|
||||
}
|
||||
|
||||
/** A field. */
|
||||
class Field extends DotNet::Field, Variable, Member, @cil_field {
|
||||
class Field extends DotNet::Field, Variable, Member, CustomModifierReceiver, @cil_field {
|
||||
override string toString() { result = getName() }
|
||||
|
||||
override string toStringWithTypes() {
|
||||
|
||||
@@ -746,7 +746,7 @@ class Class extends RefType, @class_type {
|
||||
* ```
|
||||
*/
|
||||
class AnonymousClass extends Class {
|
||||
AnonymousClass() { this.getName().matches("<%") }
|
||||
AnonymousClass() { anonymous_types(this) }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1162,37 +1162,6 @@ module Ssa {
|
||||
not intraInstanceCallEdge(c1, c2)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a call to `x.c` can change the value of `x.fp`. The actual
|
||||
* update occurs in `setter`.
|
||||
*/
|
||||
private predicate setsOwnFieldOrPropTransitive(
|
||||
InstanceCallable c, FieldOrProp fp, InstanceCallable setter
|
||||
) {
|
||||
setsOwnFieldOrProp(setter, fp) and
|
||||
// `intraInstanceCallEdge*(c, setter)` applies `fastTC` and therefore misses
|
||||
// important magic optimization; consequently apply magic manually by explicit
|
||||
// recursion
|
||||
c = setter
|
||||
or
|
||||
exists(InstanceCallable mid | setsOwnFieldOrPropTransitive(mid, fp, setter) |
|
||||
intraInstanceCallEdge(c, mid)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a call to `c` can change the value of `fp` on some instance.
|
||||
* The actual update occurs in `setter`.
|
||||
*/
|
||||
private predicate generalSetter(Callable c, FieldOrProp fp, Callable setter) {
|
||||
exists(InstanceCallable ownsetter |
|
||||
setsOwnFieldOrPropTransitive(ownsetter, fp, setter) and
|
||||
crossInstanceCallEdge(c, ownsetter)
|
||||
)
|
||||
or
|
||||
setsOtherFieldOrProp(c, fp) and c = setter
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
predicate callAt(BasicBlock bb, int i, Call call) {
|
||||
bb.getNode(i) = call.getAControlFlowNode() and
|
||||
@@ -1211,103 +1180,89 @@ module Ssa {
|
||||
not ref(bb, i, fp, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `c` is a relevant part of the call graph for
|
||||
* `updatesNamedFieldOrPropPart1` based on following edges in forward direction.
|
||||
*/
|
||||
private predicate pruneFromLeft(Callable c) {
|
||||
exists(Call call, TrackedFieldOrProp f |
|
||||
updateCandidate(_, _, f, call) and
|
||||
c = getARuntimeTarget(call, _) and
|
||||
generalSetter(_, f.getAssignable(), _)
|
||||
)
|
||||
or
|
||||
exists(Callable mid | pruneFromLeft(mid) | callEdge(mid, c))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `c` is a relevant part of the call graph for
|
||||
* `updatesNamedFieldOrPropPart1` based on following edges in backward direction.
|
||||
*/
|
||||
private predicate pruneFromRight(Callable c) {
|
||||
relevantDefinition(c, _, _) and
|
||||
pruneFromLeft(c)
|
||||
or
|
||||
exists(Callable mid | pruneFromRight(mid) |
|
||||
callEdge(c, mid) and
|
||||
pruneFromLeft(c)
|
||||
)
|
||||
}
|
||||
|
||||
private class PrunedCallable extends Callable {
|
||||
PrunedCallable() { pruneFromRight(this) }
|
||||
}
|
||||
|
||||
private predicate callEdgePruned(PrunedCallable c1, PrunedCallable c2) { callEdge(c1, c2) }
|
||||
|
||||
private predicate callEdgePrunedPlus(PrunedCallable c1, PrunedCallable c2) =
|
||||
fastTC(callEdgePruned/2)(c1, c2)
|
||||
|
||||
pragma[noinline]
|
||||
private predicate updatesNamedFieldOrPropPart1Prefix0(
|
||||
Call call, TrackedFieldOrProp tfp, Callable c1, FieldOrProp fp
|
||||
private predicate source(
|
||||
Call call, TrackedFieldOrProp tfp, FieldOrProp fp, Callable c, boolean fresh
|
||||
) {
|
||||
updateCandidate(_, _, tfp, call) and
|
||||
c = getARuntimeTarget(call, _) and
|
||||
fp = tfp.getAssignable() and
|
||||
generalSetter(_, fp, _) and
|
||||
c1 = getARuntimeTarget(call, _)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate relevantDefinitionProj(PrunedCallable c, FieldOrProp fp) {
|
||||
relevantDefinition(c, fp, _)
|
||||
}
|
||||
|
||||
pragma[noopt]
|
||||
predicate updatesNamedFieldOrPropPart1Prefix(
|
||||
Call call, TrackedFieldOrProp tfp, Callable c1, Callable setter, FieldOrProp fp
|
||||
) {
|
||||
updatesNamedFieldOrPropPart1Prefix0(call, tfp, c1, fp) and
|
||||
relevantDefinitionProj(setter, fp) and
|
||||
(c1 = setter or callEdgePrunedPlus(c1, setter))
|
||||
if c instanceof Constructor then fresh = true else fresh = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `call` may change the value of `tfp` on some instance, which may or
|
||||
* may not alias with `this`. The actual update occurs in `setter`.
|
||||
* A callable in a potential call-chain between a source that cares about the
|
||||
* value of some field `f` and a sink that may overwrite `f`. The Boolean
|
||||
* `fresh` indicates whether the instance `this` in `c` has been freshly
|
||||
* allocated along the call-chain.
|
||||
*/
|
||||
pragma[noopt]
|
||||
private predicate updatesNamedFieldOrPropPart1(
|
||||
Call call, TrackedFieldOrProp tfp, Callable setter
|
||||
) {
|
||||
exists(Callable c1, Callable c2, FieldOrProp fp |
|
||||
updatesNamedFieldOrPropPart1Prefix(call, tfp, c1, setter, fp) and
|
||||
generalSetter(c2, fp, setter)
|
||||
|
|
||||
c1 = c2 or callEdgePrunedPlus(c1, c2)
|
||||
private newtype TCallableNode =
|
||||
MkCallableNode(Callable c, boolean fresh) { source(_, _, _, c, fresh) or edge(_, c, fresh) }
|
||||
|
||||
private predicate edge(TCallableNode n, Callable c2, boolean f2) {
|
||||
exists(Callable c1, boolean f1 | n = MkCallableNode(c1, f1) |
|
||||
intraInstanceCallEdge(c1, c2) and f2 = f1
|
||||
or
|
||||
crossInstanceCallEdge(c1, c2) and
|
||||
if c2 instanceof Constructor then f2 = true else f2 = false
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `call` may change the value of `tfp` on `this`. The actual update occurs
|
||||
* in `setter`.
|
||||
*/
|
||||
private predicate updatesNamedFieldOrPropPart2(
|
||||
Call call, TrackedFieldOrProp tfp, Callable setter
|
||||
private predicate edge(TCallableNode n1, TCallableNode n2) {
|
||||
exists(Callable c2, boolean f2 |
|
||||
edge(n1, c2, f2) and
|
||||
n2 = MkCallableNode(c2, f2)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate source(
|
||||
Call call, FieldOrPropSourceVariable fps, FieldOrProp fp, TCallableNode n
|
||||
) {
|
||||
updateCandidate(_, _, tfp, call) and
|
||||
setsOwnFieldOrPropTransitive(getARuntimeTarget(call, _), tfp.getAssignable(), setter)
|
||||
exists(Callable c, boolean fresh |
|
||||
source(call, fps, fp, c, fresh) and
|
||||
n = MkCallableNode(c, fresh)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate sink(Callable c, FieldOrProp fp, TCallableNode n) {
|
||||
relevantDefinition(c, fp, _) and
|
||||
(
|
||||
setsOwnFieldOrProp(c, fp) and n = MkCallableNode(c, false)
|
||||
or
|
||||
setsOtherFieldOrProp(c, fp) and n = MkCallableNode(c, _)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate prunedNode(TCallableNode n) {
|
||||
sink(_, _, n)
|
||||
or
|
||||
exists(TCallableNode mid | edge(n, mid) and prunedNode(mid))
|
||||
}
|
||||
|
||||
private predicate prunedEdge(TCallableNode n1, TCallableNode n2) {
|
||||
prunedNode(n1) and
|
||||
prunedNode(n2) and
|
||||
edge(n1, n2)
|
||||
}
|
||||
|
||||
private predicate edgePlus(TCallableNode c1, TCallableNode c2) = fastTC(prunedEdge/2)(c1, c2)
|
||||
|
||||
pragma[noopt]
|
||||
private predicate updatesNamedFieldOrProp_(
|
||||
FieldOrPropSourceVariable fps, Call call, Callable setter
|
||||
) {
|
||||
exists(TCallableNode src, TCallableNode sink, FieldOrProp fp |
|
||||
source(call, fps, fp, src) and
|
||||
sink(setter, fp, sink) and
|
||||
(src = sink or edgePlus(src, sink))
|
||||
)
|
||||
}
|
||||
|
||||
private predicate updatesNamedFieldOrPropPossiblyLive(
|
||||
BasicBlock bb, int i, TrackedFieldOrProp fp, Call call, Callable setter
|
||||
) {
|
||||
updateCandidate(bb, i, fp, call) and
|
||||
(
|
||||
updatesNamedFieldOrPropPart1(call, fp, setter)
|
||||
or
|
||||
updatesNamedFieldOrPropPart2(call, fp, setter)
|
||||
)
|
||||
updatesNamedFieldOrProp_(fp, call, setter)
|
||||
}
|
||||
|
||||
private int firstRefAfterCall(BasicBlock bb, int i, TrackedFieldOrProp fp) {
|
||||
@@ -1457,58 +1412,44 @@ module Ssa {
|
||||
)
|
||||
}
|
||||
|
||||
private predicate source(
|
||||
Call call, CapturedWrittenLocalScopeSourceVariable v,
|
||||
CapturedWrittenLocalScopeVariable captured, Callable c, boolean libraryDelegateCall
|
||||
) {
|
||||
updateCandidate(_, _, v, call) and
|
||||
c = getARuntimeTarget(call, libraryDelegateCall) and
|
||||
captured = v.getAssignable() and
|
||||
relevantDefinition(_, captured, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `c` is a relevant part of the call graph for
|
||||
* `updatesCapturedVariable` based on following edges in forward direction.
|
||||
*/
|
||||
private predicate pruneFromLeft(Callable c) {
|
||||
exists(Call call, CapturedWrittenLocalScopeSourceVariable v |
|
||||
updateCandidate(_, _, v, call) and
|
||||
c = getARuntimeTarget(call, _) and
|
||||
relevantDefinition(_, v.getAssignable(), _)
|
||||
)
|
||||
private predicate reachbleFromSource(Callable c) {
|
||||
source(_, _, _, c, _)
|
||||
or
|
||||
exists(Callable mid | pruneFromLeft(mid) | callEdge(mid, c))
|
||||
exists(Callable mid | reachbleFromSource(mid) | callEdge(mid, c))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `c` is a relevant part of the call graph for
|
||||
* `updatesCapturedVariable` based on following edges in backward direction.
|
||||
*/
|
||||
private predicate pruneFromRight(Callable c) {
|
||||
relevantDefinition(c, _, _) and
|
||||
pruneFromLeft(c)
|
||||
private predicate sink(Callable c, CapturedWrittenLocalScopeVariable captured) {
|
||||
reachbleFromSource(c) and
|
||||
relevantDefinition(c, captured, _)
|
||||
}
|
||||
|
||||
private predicate prunedCallable(Callable c) {
|
||||
sink(c, _)
|
||||
or
|
||||
exists(Callable mid | pruneFromRight(mid) |
|
||||
callEdge(c, mid) and
|
||||
pruneFromLeft(c)
|
||||
)
|
||||
exists(Callable mid | callEdge(c, mid) and prunedCallable(mid))
|
||||
}
|
||||
|
||||
private class PrunedCallable extends Callable {
|
||||
PrunedCallable() { pruneFromRight(this) }
|
||||
private predicate prunedEdge(Callable c1, Callable c2) {
|
||||
prunedCallable(c1) and
|
||||
prunedCallable(c2) and
|
||||
callEdge(c1, c2)
|
||||
}
|
||||
|
||||
private predicate callEdgePruned(PrunedCallable c1, PrunedCallable c2) { callEdge(c1, c2) }
|
||||
|
||||
private predicate callEdgePrunedPlus(PrunedCallable c1, PrunedCallable c2) =
|
||||
fastTC(callEdgePruned/2)(c1, c2)
|
||||
|
||||
pragma[noinline]
|
||||
private predicate relevantDefinitionProj(PrunedCallable c, CapturedWrittenLocalScopeVariable v) {
|
||||
relevantDefinition(c, v, _)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate updatesCapturedVariablePrefix(
|
||||
Call call, CapturedWrittenLocalScopeSourceVariable v, PrunedCallable c,
|
||||
CapturedWrittenLocalScopeVariable captured, boolean libraryDelegateCall
|
||||
) {
|
||||
updateCandidate(_, _, v, call) and
|
||||
captured = v.getAssignable() and
|
||||
relevantDefinitionProj(_, captured) and
|
||||
c = getARuntimeTarget(call, libraryDelegateCall)
|
||||
}
|
||||
private predicate edgePlus(Callable c1, Callable c2) = fastTC(prunedEdge/2)(c1, c2)
|
||||
|
||||
/**
|
||||
* Holds if `call` may change the value of captured variable `v`. The actual
|
||||
@@ -1519,18 +1460,15 @@ module Ssa {
|
||||
*/
|
||||
pragma[noopt]
|
||||
private predicate updatesCapturedVariableWriter(
|
||||
Call call, CapturedWrittenLocalScopeSourceVariable v, PrunedCallable writer,
|
||||
boolean additionalCalls
|
||||
Call call, CapturedWrittenLocalScopeSourceVariable v, Callable writer, boolean additionalCalls
|
||||
) {
|
||||
exists(
|
||||
PrunedCallable c, CapturedWrittenLocalScopeVariable captured, boolean libraryDelegateCall
|
||||
|
|
||||
updatesCapturedVariablePrefix(call, v, c, captured, libraryDelegateCall) and
|
||||
relevantDefinitionProj(writer, captured) and
|
||||
exists(Callable src, CapturedWrittenLocalScopeVariable captured, boolean libraryDelegateCall |
|
||||
source(call, v, captured, src, libraryDelegateCall) and
|
||||
sink(writer, captured) and
|
||||
(
|
||||
c = writer and additionalCalls = libraryDelegateCall
|
||||
src = writer and additionalCalls = libraryDelegateCall
|
||||
or
|
||||
callEdgePrunedPlus(c, writer) and additionalCalls = true
|
||||
edgePlus(src, writer) and additionalCalls = true
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -1641,8 +1579,8 @@ module Ssa {
|
||||
* block `bb` may be read by a callable reachable from the call `c`.
|
||||
*/
|
||||
private predicate implicitReadCandidate(
|
||||
BasicBlock bb, int i, ControlFlow::Nodes::ElementNode c,
|
||||
CapturedReadLocalScopeSourceVariable v
|
||||
BasicBlock bb, int i, CapturedReadLocalScopeSourceVariable v,
|
||||
ControlFlow::Nodes::ElementNode c
|
||||
) {
|
||||
c.getElement() instanceof Call and
|
||||
exists(BasicBlock bb0, int i0 | bb0.getNode(i0) = c |
|
||||
@@ -1679,55 +1617,44 @@ module Ssa {
|
||||
)
|
||||
}
|
||||
|
||||
private predicate source(
|
||||
ControlFlow::Nodes::ElementNode call, CapturedReadLocalScopeSourceVariable v,
|
||||
CapturedReadLocalScopeVariable captured, Callable c, boolean libraryDelegateCall
|
||||
) {
|
||||
implicitReadCandidate(_, _, v, call) and
|
||||
c = getARuntimeTarget(call.getElement(), libraryDelegateCall) and
|
||||
captured = v.getAssignable() and
|
||||
capturerReads(_, captured)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `c` is a relevant part of the call graph for
|
||||
* `readsCapturedVariable` based on following edges in forward direction.
|
||||
*/
|
||||
private predicate pruneFromLeft(Callable c) {
|
||||
exists(Call call, CapturedReadLocalScopeSourceVariable v |
|
||||
implicitReadCandidate(_, _, call.getAControlFlowNode(), v) and
|
||||
c = getARuntimeTarget(call, _)
|
||||
)
|
||||
private predicate reachbleFromSource(Callable c) {
|
||||
source(_, _, _, c, _)
|
||||
or
|
||||
exists(Callable mid | pruneFromLeft(mid) | callEdge(mid, c))
|
||||
exists(Callable mid | reachbleFromSource(mid) | callEdge(mid, c))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `c` is a relevant part of the call graph for
|
||||
* `readsCapturedVariable` based on following edges in backward direction.
|
||||
*/
|
||||
private predicate pruneFromRight(Callable c) {
|
||||
exists(CapturedReadLocalScopeSourceVariable v |
|
||||
capturerReads(c, v.getAssignable()) and
|
||||
capturedVariableWrite(_, _, v) and
|
||||
pruneFromLeft(c)
|
||||
)
|
||||
private predicate sink(Callable c, CapturedReadLocalScopeVariable captured) {
|
||||
reachbleFromSource(c) and
|
||||
capturerReads(c, captured)
|
||||
}
|
||||
|
||||
private predicate prunedCallable(Callable c) {
|
||||
sink(c, _)
|
||||
or
|
||||
exists(Callable mid | pruneFromRight(mid) |
|
||||
callEdge(c, mid) and
|
||||
pruneFromLeft(c)
|
||||
)
|
||||
exists(Callable mid | callEdge(c, mid) and prunedCallable(mid))
|
||||
}
|
||||
|
||||
private class PrunedCallable extends Callable {
|
||||
PrunedCallable() { pruneFromRight(this) }
|
||||
private predicate prunedEdge(Callable c1, Callable c2) {
|
||||
prunedCallable(c1) and
|
||||
prunedCallable(c2) and
|
||||
callEdge(c1, c2)
|
||||
}
|
||||
|
||||
private predicate callEdgePruned(PrunedCallable c1, PrunedCallable c2) { callEdge(c1, c2) }
|
||||
|
||||
private predicate callEdgePrunedPlus(PrunedCallable c1, PrunedCallable c2) =
|
||||
fastTC(callEdgePruned/2)(c1, c2)
|
||||
|
||||
pragma[noinline]
|
||||
private predicate readsCapturedVariablePrefix(
|
||||
ControlFlow::Node call, CapturedReadLocalScopeSourceVariable v, PrunedCallable c,
|
||||
CapturedReadLocalScopeVariable captured, boolean libraryDelegateCall
|
||||
) {
|
||||
implicitReadCandidate(_, _, call, v) and
|
||||
captured = v.getAssignable() and
|
||||
capturerReads(_, captured) and
|
||||
c = getARuntimeTarget(call.getElement(), libraryDelegateCall)
|
||||
}
|
||||
private predicate edgePlus(Callable c1, Callable c2) = fastTC(prunedEdge/2)(c1, c2)
|
||||
|
||||
/**
|
||||
* Holds if `call` may read the value of captured variable `v`. The actual
|
||||
@@ -1741,15 +1668,13 @@ module Ssa {
|
||||
ControlFlow::Nodes::ElementNode call, CapturedReadLocalScopeSourceVariable v, Callable reader,
|
||||
boolean additionalCalls
|
||||
) {
|
||||
exists(
|
||||
PrunedCallable c, CapturedReadLocalScopeVariable captured, boolean libraryDelegateCall
|
||||
|
|
||||
readsCapturedVariablePrefix(call, v, c, captured, libraryDelegateCall) and
|
||||
capturerReads(reader, captured) and
|
||||
exists(Callable src, CapturedReadLocalScopeVariable captured, boolean libraryDelegateCall |
|
||||
source(call, v, captured, src, libraryDelegateCall) and
|
||||
sink(reader, captured) and
|
||||
(
|
||||
c = reader and additionalCalls = libraryDelegateCall
|
||||
src = reader and additionalCalls = libraryDelegateCall
|
||||
or
|
||||
callEdgePrunedPlus(c, reader) and additionalCalls = true
|
||||
edgePlus(src, reader) and additionalCalls = true
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -1821,7 +1746,7 @@ module Ssa {
|
||||
ControlFlow::Nodes::ElementNode c, boolean additionalCalls
|
||||
) {
|
||||
exists(Callable reader, SourceVariable sv |
|
||||
implicitReadCandidate(bb, i, c, v) and
|
||||
implicitReadCandidate(bb, i, v, c) and
|
||||
readsCapturedVariable(c, v, reader, additionalCalls) and
|
||||
sv = def.getSourceVariable() and
|
||||
reader = sv.getEnclosingCallable() and
|
||||
|
||||
@@ -588,16 +588,19 @@ predicate implementsEquals(ValueOrRefType t) { getInvokedEqualsMethod(t).getDecl
|
||||
* from the `object.Equals(object)` method inherited by `t`.
|
||||
*/
|
||||
Method getInvokedEqualsMethod(ValueOrRefType t) {
|
||||
result = getInheritedEqualsMethod(t) and
|
||||
result = getInheritedEqualsMethod(t, _) and
|
||||
not exists(getInvokedIEquatableEqualsMethod(t, result))
|
||||
or
|
||||
exists(EqualsMethod eq |
|
||||
result = getInvokedIEquatableEqualsMethod(t, eq) and
|
||||
getInheritedEqualsMethod(t) = eq
|
||||
getInheritedEqualsMethod(t, _) = eq
|
||||
)
|
||||
}
|
||||
|
||||
private EqualsMethod getInheritedEqualsMethod(ValueOrRefType t) { t.hasMethod(result) }
|
||||
pragma[noinline]
|
||||
private EqualsMethod getInheritedEqualsMethod(ValueOrRefType t, ValueOrRefType decl) {
|
||||
t.hasMethod(result) and decl = result.getDeclaringType()
|
||||
}
|
||||
|
||||
/**
|
||||
* Equals method `eq` is inherited by `t`, `t` overrides `IEquatable<T>.Equals(T)`
|
||||
@@ -621,10 +624,9 @@ private EqualsMethod getInheritedEqualsMethod(ValueOrRefType t) { t.hasMethod(re
|
||||
*/
|
||||
private IEquatableEqualsMethod getInvokedIEquatableEqualsMethod(ValueOrRefType t, EqualsMethod eq) {
|
||||
t.hasMethod(result) and
|
||||
eq = getInheritedEqualsMethod(t.getBaseClass()) and
|
||||
exists(IEquatableEqualsMethod ieem |
|
||||
result = ieem.getAnOverrider*() and
|
||||
eq.getDeclaringType() = ieem.getDeclaringType()
|
||||
eq = getInheritedEqualsMethod(t.getBaseClass(), ieem.getDeclaringType())
|
||||
|
|
||||
not ieem.fromSource()
|
||||
or
|
||||
|
||||
@@ -413,6 +413,9 @@ extend(
|
||||
unique int sub: @type ref,
|
||||
int super: @type_or_ref ref);
|
||||
|
||||
anonymous_types(
|
||||
unique int id: @type ref);
|
||||
|
||||
@interface_or_ref = @interface_type | @typeref;
|
||||
|
||||
implement(
|
||||
@@ -1712,6 +1715,7 @@ cil_field(
|
||||
@cil_variable = @cil_field | @cil_stack_variable;
|
||||
@cil_stack_variable = @cil_local_variable | @cil_parameter;
|
||||
@cil_member = @cil_method | @cil_type | @cil_field | @cil_property | @cil_event;
|
||||
@cil_custom_modifier_receiver = @cil_method | @cil_property | @cil_parameter | @cil_field; // todo: add function pointer type
|
||||
|
||||
#keyset[method, index]
|
||||
cil_parameter(
|
||||
@@ -1726,6 +1730,12 @@ cil_parameter_out(unique int id: @cil_parameter ref);
|
||||
cil_setter(unique int prop: @cil_property ref,
|
||||
int method: @cil_method ref);
|
||||
|
||||
#keyset[id, modifier]
|
||||
cil_custom_modifiers(
|
||||
int id: @cil_custom_modifier_receiver ref,
|
||||
int modifier: @cil_type ref,
|
||||
int kind: int ref); // modreq: 1, modopt: 0
|
||||
|
||||
cil_getter(unique int prop: @cil_property ref,
|
||||
int method: @cil_method ref);
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -47,18 +47,23 @@ arguments
|
||||
| attributes.cs:57:6:57:16 | [My(...)] | 0 | attributes.cs:57:18:57:21 | true |
|
||||
| attributes.cs:57:6:57:16 | [My(...)] | 1 | attributes.cs:57:28:57:29 | "" |
|
||||
| attributes.cs:57:6:57:16 | [My(...)] | 2 | attributes.cs:57:36:57:36 | 0 |
|
||||
| attributes.cs:76:2:76:5 | [Args(...)] | 0 | attributes.cs:76:7:76:8 | 42 |
|
||||
| attributes.cs:76:2:76:5 | [Args(...)] | 1 | attributes.cs:76:11:76:14 | null |
|
||||
| attributes.cs:76:2:76:5 | [Args(...)] | 2 | attributes.cs:76:17:76:25 | typeof(...) |
|
||||
| attributes.cs:76:2:76:5 | [Args(...)] | 3 | attributes.cs:76:28:76:30 | access to constant A |
|
||||
| attributes.cs:76:2:76:5 | [Args(...)] | 4 | attributes.cs:76:33:76:53 | array creation of type Int32[] |
|
||||
| attributes.cs:76:2:76:5 | [Args(...)] | 5 | attributes.cs:76:63:76:93 | array creation of type Object[] |
|
||||
| attributes.cs:79:6:79:9 | [Args(...)] | 0 | attributes.cs:79:11:79:16 | ... + ... |
|
||||
| attributes.cs:79:6:79:9 | [Args(...)] | 1 | attributes.cs:79:19:79:39 | array creation of type Int32[] |
|
||||
| attributes.cs:79:6:79:9 | [Args(...)] | 2 | attributes.cs:79:42:79:45 | null |
|
||||
| attributes.cs:79:6:79:9 | [Args(...)] | 3 | attributes.cs:79:48:79:52 | (...) ... |
|
||||
| attributes.cs:79:6:79:9 | [Args(...)] | 4 | attributes.cs:79:55:79:58 | null |
|
||||
| attributes.cs:79:6:79:9 | [Args(...)] | 5 | attributes.cs:79:68:79:98 | array creation of type Object[] |
|
||||
| attributes.cs:58:6:58:8 | [My2(...)] | 0 | attributes.cs:58:28:58:32 | false |
|
||||
| attributes.cs:58:6:58:8 | [My2(...)] | 1 | attributes.cs:58:13:58:16 | true |
|
||||
| attributes.cs:58:6:58:8 | [My2(...)] | 2 | attributes.cs:58:6:58:8 | 12 |
|
||||
| attributes.cs:58:6:58:8 | [My2(...)] | 3 | attributes.cs:58:22:58:22 | 1 |
|
||||
| attributes.cs:58:6:58:8 | [My2(...)] | 4 | attributes.cs:58:39:58:40 | 42 |
|
||||
| attributes.cs:77:2:77:5 | [Args(...)] | 0 | attributes.cs:77:7:77:8 | 42 |
|
||||
| attributes.cs:77:2:77:5 | [Args(...)] | 1 | attributes.cs:77:11:77:14 | null |
|
||||
| attributes.cs:77:2:77:5 | [Args(...)] | 2 | attributes.cs:77:17:77:25 | typeof(...) |
|
||||
| attributes.cs:77:2:77:5 | [Args(...)] | 3 | attributes.cs:77:28:77:30 | access to constant A |
|
||||
| attributes.cs:77:2:77:5 | [Args(...)] | 4 | attributes.cs:77:33:77:53 | array creation of type Int32[] |
|
||||
| attributes.cs:77:2:77:5 | [Args(...)] | 5 | attributes.cs:77:63:77:93 | array creation of type Object[] |
|
||||
| attributes.cs:80:6:80:9 | [Args(...)] | 0 | attributes.cs:80:11:80:16 | ... + ... |
|
||||
| attributes.cs:80:6:80:9 | [Args(...)] | 1 | attributes.cs:80:19:80:39 | array creation of type Int32[] |
|
||||
| attributes.cs:80:6:80:9 | [Args(...)] | 2 | attributes.cs:80:42:80:45 | null |
|
||||
| attributes.cs:80:6:80:9 | [Args(...)] | 3 | attributes.cs:80:48:80:52 | (...) ... |
|
||||
| attributes.cs:80:6:80:9 | [Args(...)] | 4 | attributes.cs:80:55:80:58 | null |
|
||||
| attributes.cs:80:6:80:9 | [Args(...)] | 5 | attributes.cs:80:68:80:98 | array creation of type Object[] |
|
||||
constructorArguments
|
||||
| Assembly1.dll:0:0:0:0 | [Custom(...)] | 0 | Assembly1.dll:0:0:0:0 | 1 |
|
||||
| Assembly1.dll:0:0:0:0 | [Custom(...)] | 0 | Assembly1.dll:0:0:0:0 | 3 |
|
||||
@@ -101,16 +106,20 @@ constructorArguments
|
||||
| attributes.cs:46:6:46:16 | [Conditional(...)] | 0 | attributes.cs:46:18:46:25 | "DEBUG2" |
|
||||
| attributes.cs:54:6:54:16 | [My(...)] | 0 | attributes.cs:54:18:54:22 | false |
|
||||
| attributes.cs:57:6:57:16 | [My(...)] | 0 | attributes.cs:57:18:57:21 | true |
|
||||
| attributes.cs:76:2:76:5 | [Args(...)] | 0 | attributes.cs:76:7:76:8 | 42 |
|
||||
| attributes.cs:76:2:76:5 | [Args(...)] | 1 | attributes.cs:76:11:76:14 | null |
|
||||
| attributes.cs:76:2:76:5 | [Args(...)] | 2 | attributes.cs:76:17:76:25 | typeof(...) |
|
||||
| attributes.cs:76:2:76:5 | [Args(...)] | 3 | attributes.cs:76:28:76:30 | access to constant A |
|
||||
| attributes.cs:76:2:76:5 | [Args(...)] | 4 | attributes.cs:76:33:76:53 | array creation of type Int32[] |
|
||||
| attributes.cs:79:6:79:9 | [Args(...)] | 0 | attributes.cs:79:11:79:16 | ... + ... |
|
||||
| attributes.cs:79:6:79:9 | [Args(...)] | 1 | attributes.cs:79:19:79:39 | array creation of type Int32[] |
|
||||
| attributes.cs:79:6:79:9 | [Args(...)] | 2 | attributes.cs:79:42:79:45 | null |
|
||||
| attributes.cs:79:6:79:9 | [Args(...)] | 3 | attributes.cs:79:48:79:52 | (...) ... |
|
||||
| attributes.cs:79:6:79:9 | [Args(...)] | 4 | attributes.cs:79:55:79:58 | null |
|
||||
| attributes.cs:58:6:58:8 | [My2(...)] | 0 | attributes.cs:58:28:58:32 | false |
|
||||
| attributes.cs:58:6:58:8 | [My2(...)] | 1 | attributes.cs:58:13:58:16 | true |
|
||||
| attributes.cs:58:6:58:8 | [My2(...)] | 2 | attributes.cs:58:6:58:8 | 12 |
|
||||
| attributes.cs:58:6:58:8 | [My2(...)] | 3 | attributes.cs:58:22:58:22 | 1 |
|
||||
| attributes.cs:77:2:77:5 | [Args(...)] | 0 | attributes.cs:77:7:77:8 | 42 |
|
||||
| attributes.cs:77:2:77:5 | [Args(...)] | 1 | attributes.cs:77:11:77:14 | null |
|
||||
| attributes.cs:77:2:77:5 | [Args(...)] | 2 | attributes.cs:77:17:77:25 | typeof(...) |
|
||||
| attributes.cs:77:2:77:5 | [Args(...)] | 3 | attributes.cs:77:28:77:30 | access to constant A |
|
||||
| attributes.cs:77:2:77:5 | [Args(...)] | 4 | attributes.cs:77:33:77:53 | array creation of type Int32[] |
|
||||
| attributes.cs:80:6:80:9 | [Args(...)] | 0 | attributes.cs:80:11:80:16 | ... + ... |
|
||||
| attributes.cs:80:6:80:9 | [Args(...)] | 1 | attributes.cs:80:19:80:39 | array creation of type Int32[] |
|
||||
| attributes.cs:80:6:80:9 | [Args(...)] | 2 | attributes.cs:80:42:80:45 | null |
|
||||
| attributes.cs:80:6:80:9 | [Args(...)] | 3 | attributes.cs:80:48:80:52 | (...) ... |
|
||||
| attributes.cs:80:6:80:9 | [Args(...)] | 4 | attributes.cs:80:55:80:58 | null |
|
||||
namedArguments
|
||||
| Assembly1.dll:0:0:0:0 | [Custom(...)] | Prop2 | Assembly1.dll:0:0:0:0 | array creation of type Object[] |
|
||||
| Assembly1.dll:0:0:0:0 | [Custom(...)] | Prop2 | Assembly1.dll:0:0:0:0 | array creation of type Object[] |
|
||||
@@ -119,5 +128,6 @@ namedArguments
|
||||
| attributes.cs:41:10:41:13 | [Args(...)] | Prop | attributes.cs:41:90:41:120 | array creation of type Object[] |
|
||||
| attributes.cs:57:6:57:16 | [My(...)] | x | attributes.cs:57:36:57:36 | 0 |
|
||||
| attributes.cs:57:6:57:16 | [My(...)] | y | attributes.cs:57:28:57:29 | "" |
|
||||
| attributes.cs:76:2:76:5 | [Args(...)] | Prop | attributes.cs:76:63:76:93 | array creation of type Object[] |
|
||||
| attributes.cs:79:6:79:9 | [Args(...)] | Prop | attributes.cs:79:68:79:98 | array creation of type Object[] |
|
||||
| attributes.cs:58:6:58:8 | [My2(...)] | X | attributes.cs:58:39:58:40 | 42 |
|
||||
| attributes.cs:77:2:77:5 | [Args(...)] | Prop | attributes.cs:77:63:77:93 | array creation of type Object[] |
|
||||
| attributes.cs:80:6:80:9 | [Args(...)] | Prop | attributes.cs:80:68:80:98 | array creation of type Object[] |
|
||||
|
||||
@@ -13,9 +13,10 @@
|
||||
| attributes.cs:47:17:47:19 | foo | attributes.cs:46:6:46:16 | [Conditional(...)] | System.Diagnostics.ConditionalAttribute |
|
||||
| attributes.cs:52:23:52:23 | x | attributes.cs:52:14:52:16 | [Foo(...)] | Foo |
|
||||
| attributes.cs:55:10:55:11 | M1 | attributes.cs:54:6:54:16 | [My(...)] | MyAttribute |
|
||||
| attributes.cs:58:10:58:11 | M2 | attributes.cs:57:6:57:16 | [My(...)] | MyAttribute |
|
||||
| attributes.cs:77:14:77:14 | X | attributes.cs:76:2:76:5 | [Args(...)] | ArgsAttribute |
|
||||
| attributes.cs:81:9:81:18 | SomeMethod | attributes.cs:79:6:79:9 | [Args(...)] | ArgsAttribute |
|
||||
| attributes.cs:59:10:59:11 | M2 | attributes.cs:57:6:57:16 | [My(...)] | MyAttribute |
|
||||
| attributes.cs:59:10:59:11 | M2 | attributes.cs:58:6:58:8 | [My2(...)] | My2Attribute |
|
||||
| attributes.cs:78:14:78:14 | X | attributes.cs:77:2:77:5 | [Args(...)] | ArgsAttribute |
|
||||
| attributes.cs:82:9:82:18 | SomeMethod | attributes.cs:80:6:80:9 | [Args(...)] | ArgsAttribute |
|
||||
| attributes.dll:0:0:0:0 | attributes, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null | attributes.cs:10:12:10:24 | [AssemblyTitle(...)] | System.Reflection.AssemblyTitleAttribute |
|
||||
| attributes.dll:0:0:0:0 | attributes, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null | attributes.cs:11:12:11:30 | [AssemblyDescription(...)] | System.Reflection.AssemblyDescriptionAttribute |
|
||||
| attributes.dll:0:0:0:0 | attributes, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null | attributes.cs:12:12:12:32 | [AssemblyConfiguration(...)] | System.Reflection.AssemblyConfigurationAttribute |
|
||||
|
||||
@@ -130,118 +130,146 @@ attributes.cs:
|
||||
# 54| -1: [TypeMention] MyAttribute
|
||||
# 54| 0: [BoolLiteral] false
|
||||
# 55| 4: [BlockStmt] {...}
|
||||
# 58| 7: [Method] M2
|
||||
# 58| -1: [TypeMention] Void
|
||||
# 59| 7: [Method] M2
|
||||
# 59| -1: [TypeMention] Void
|
||||
#-----| 0: (Attributes)
|
||||
# 57| 1: [Attribute] [My(...)]
|
||||
# 57| -1: [TypeMention] MyAttribute
|
||||
# 57| 0: [BoolLiteral] true
|
||||
# 57| 1: [StringLiteral] ""
|
||||
# 57| 2: [IntLiteral] 0
|
||||
# 58| 4: [BlockStmt] {...}
|
||||
# 61| [Class] MyAttribute
|
||||
# 58| 2: [Attribute] [My2(...)]
|
||||
# 58| -1: [TypeMention] My2Attribute
|
||||
# 58| 0: [BoolLiteral] false
|
||||
# 58| 1: [BoolLiteral] true
|
||||
# 58| 3: [IntLiteral] 1
|
||||
# 58| 4: [IntLiteral] 42
|
||||
# 59| 4: [BlockStmt] {...}
|
||||
# 62| [Class] MyAttribute
|
||||
#-----| 3: (Base types)
|
||||
# 61| 0: [TypeMention] Attribute
|
||||
# 63| 4: [Field] x
|
||||
# 63| -1: [TypeMention] int
|
||||
# 64| 5: [IndexerProperty] y
|
||||
# 64| -1: [TypeMention] string
|
||||
# 64| 3: [Getter] get_y
|
||||
# 64| 4: [Setter] set_y
|
||||
# 62| 0: [TypeMention] Attribute
|
||||
# 64| 4: [Field] x
|
||||
# 64| -1: [TypeMention] int
|
||||
# 65| 5: [IndexerProperty] y
|
||||
# 65| -1: [TypeMention] string
|
||||
# 65| 3: [Getter] get_y
|
||||
# 65| 4: [Setter] set_y
|
||||
#-----| 2: (Parameters)
|
||||
# 64| 0: [Parameter] value
|
||||
# 65| 6: [InstanceConstructor] MyAttribute
|
||||
# 65| 0: [Parameter] value
|
||||
# 66| 6: [InstanceConstructor] MyAttribute
|
||||
#-----| 2: (Parameters)
|
||||
# 65| 0: [Parameter] b
|
||||
# 65| -1: [TypeMention] bool
|
||||
# 65| 4: [BlockStmt] {...}
|
||||
# 68| [Enum] E
|
||||
# 68| 5: [Field] A
|
||||
# 68| 1: [AssignExpr] ... = ...
|
||||
# 68| 0: [MemberConstantAccess] access to constant A
|
||||
# 68| 1: [IntLiteral] 42
|
||||
# 70| [Class] ArgsAttribute
|
||||
# 66| 0: [Parameter] b
|
||||
# 66| -1: [TypeMention] bool
|
||||
# 66| 4: [BlockStmt] {...}
|
||||
# 69| [Enum] E
|
||||
# 69| 5: [Field] A
|
||||
# 69| 1: [AssignExpr] ... = ...
|
||||
# 69| 0: [MemberConstantAccess] access to constant A
|
||||
# 69| 1: [IntLiteral] 42
|
||||
# 71| [Class] ArgsAttribute
|
||||
#-----| 3: (Base types)
|
||||
# 70| 0: [TypeMention] Attribute
|
||||
# 72| 4: [Property] Prop
|
||||
# 72| -1: [TypeMention] Object[]
|
||||
# 72| 1: [TypeMention] object
|
||||
# 72| 3: [Getter] get_Prop
|
||||
# 72| 4: [Setter] set_Prop
|
||||
# 71| 0: [TypeMention] Attribute
|
||||
# 73| 4: [Property] Prop
|
||||
# 73| -1: [TypeMention] Object[]
|
||||
# 73| 1: [TypeMention] object
|
||||
# 73| 3: [Getter] get_Prop
|
||||
# 73| 4: [Setter] set_Prop
|
||||
#-----| 2: (Parameters)
|
||||
# 72| 0: [Parameter] value
|
||||
# 73| 5: [InstanceConstructor] ArgsAttribute
|
||||
# 73| 0: [Parameter] value
|
||||
# 74| 5: [InstanceConstructor] ArgsAttribute
|
||||
#-----| 2: (Parameters)
|
||||
# 73| 0: [Parameter] i
|
||||
# 73| -1: [TypeMention] int
|
||||
# 73| 1: [Parameter] o
|
||||
# 73| -1: [TypeMention] object
|
||||
# 73| 2: [Parameter] t
|
||||
# 73| -1: [TypeMention] Type
|
||||
# 73| 3: [Parameter] e
|
||||
# 73| -1: [TypeMention] E
|
||||
# 73| 4: [Parameter] arr
|
||||
# 73| -1: [TypeMention] Int32[]
|
||||
# 73| 1: [TypeMention] int
|
||||
# 73| 4: [BlockStmt] {...}
|
||||
# 77| [Class] X
|
||||
# 74| 0: [Parameter] i
|
||||
# 74| -1: [TypeMention] int
|
||||
# 74| 1: [Parameter] o
|
||||
# 74| -1: [TypeMention] object
|
||||
# 74| 2: [Parameter] t
|
||||
# 74| -1: [TypeMention] Type
|
||||
# 74| 3: [Parameter] e
|
||||
# 74| -1: [TypeMention] E
|
||||
# 74| 4: [Parameter] arr
|
||||
# 74| -1: [TypeMention] Int32[]
|
||||
# 74| 1: [TypeMention] int
|
||||
# 74| 4: [BlockStmt] {...}
|
||||
# 78| [Class] X
|
||||
#-----| 0: (Attributes)
|
||||
# 76| 1: [Attribute] [Args(...)]
|
||||
# 76| -1: [TypeMention] ArgsAttribute
|
||||
# 76| 0: [IntLiteral] 42
|
||||
# 76| 1: [NullLiteral] null
|
||||
# 76| 2: [TypeofExpr] typeof(...)
|
||||
# 76| 0: [TypeAccess] access to type X
|
||||
# 76| 0: [TypeMention] X
|
||||
# 76| 3: [MemberConstantAccess] access to constant A
|
||||
# 76| -1: [TypeAccess] access to type E
|
||||
# 76| 0: [TypeMention] E
|
||||
# 76| 4: [ArrayCreation] array creation of type Int32[]
|
||||
# 76| -2: [TypeMention] Int32[]
|
||||
# 76| 1: [TypeMention] int
|
||||
# 76| -1: [ArrayInitializer] { ..., ... }
|
||||
# 76| 0: [IntLiteral] 1
|
||||
# 76| 1: [IntLiteral] 2
|
||||
# 76| 2: [IntLiteral] 3
|
||||
# 76| 5: [ArrayCreation] array creation of type Object[]
|
||||
# 76| -2: [TypeMention] Object[]
|
||||
# 76| 1: [TypeMention] object
|
||||
# 76| -1: [ArrayInitializer] { ..., ... }
|
||||
# 76| 0: [CastExpr] (...) ...
|
||||
# 76| 1: [IntLiteral] 1
|
||||
# 76| 1: [TypeofExpr] typeof(...)
|
||||
# 76| 0: [TypeAccess] access to type Int32
|
||||
# 76| 0: [TypeMention] int
|
||||
# 81| 5: [Method] SomeMethod
|
||||
# 81| -1: [TypeMention] int
|
||||
# 77| 1: [Attribute] [Args(...)]
|
||||
# 77| -1: [TypeMention] ArgsAttribute
|
||||
# 77| 0: [IntLiteral] 42
|
||||
# 77| 1: [NullLiteral] null
|
||||
# 77| 2: [TypeofExpr] typeof(...)
|
||||
# 77| 0: [TypeAccess] access to type X
|
||||
# 77| 0: [TypeMention] X
|
||||
# 77| 3: [MemberConstantAccess] access to constant A
|
||||
# 77| -1: [TypeAccess] access to type E
|
||||
# 77| 0: [TypeMention] E
|
||||
# 77| 4: [ArrayCreation] array creation of type Int32[]
|
||||
# 77| -2: [TypeMention] Int32[]
|
||||
# 77| 1: [TypeMention] int
|
||||
# 77| -1: [ArrayInitializer] { ..., ... }
|
||||
# 77| 0: [IntLiteral] 1
|
||||
# 77| 1: [IntLiteral] 2
|
||||
# 77| 2: [IntLiteral] 3
|
||||
# 77| 5: [ArrayCreation] array creation of type Object[]
|
||||
# 77| -2: [TypeMention] Object[]
|
||||
# 77| 1: [TypeMention] object
|
||||
# 77| -1: [ArrayInitializer] { ..., ... }
|
||||
# 77| 0: [CastExpr] (...) ...
|
||||
# 77| 1: [IntLiteral] 1
|
||||
# 77| 1: [TypeofExpr] typeof(...)
|
||||
# 77| 0: [TypeAccess] access to type Int32
|
||||
# 77| 0: [TypeMention] int
|
||||
# 82| 5: [Method] SomeMethod
|
||||
# 82| -1: [TypeMention] int
|
||||
#-----| 0: (Attributes)
|
||||
# 79| 1: [Attribute] [Args(...)]
|
||||
# 79| -1: [TypeMention] ArgsAttribute
|
||||
# 79| 0: [AddExpr] ... + ...
|
||||
# 79| 0: [IntLiteral] 42
|
||||
# 79| 1: [IntLiteral] 0
|
||||
# 79| 1: [ArrayCreation] array creation of type Int32[]
|
||||
# 79| -2: [TypeMention] Int32[]
|
||||
# 79| 1: [TypeMention] int
|
||||
# 79| -1: [ArrayInitializer] { ..., ... }
|
||||
# 79| 0: [IntLiteral] 1
|
||||
# 79| 1: [IntLiteral] 2
|
||||
# 79| 2: [IntLiteral] 3
|
||||
# 79| 2: [NullLiteral] null
|
||||
# 79| 3: [CastExpr] (...) ...
|
||||
# 79| 0: [TypeAccess] access to type E
|
||||
# 79| 0: [TypeMention] E
|
||||
# 79| 1: [IntLiteral] 12
|
||||
# 79| 4: [NullLiteral] null
|
||||
# 79| 5: [ArrayCreation] array creation of type Object[]
|
||||
# 79| -2: [TypeMention] Object[]
|
||||
# 79| 1: [TypeMention] object
|
||||
# 79| -1: [ArrayInitializer] { ..., ... }
|
||||
# 79| 0: [CastExpr] (...) ...
|
||||
# 79| 1: [IntLiteral] 1
|
||||
# 79| 1: [TypeofExpr] typeof(...)
|
||||
# 79| 0: [TypeAccess] access to type Int32
|
||||
# 79| 0: [TypeMention] int
|
||||
# 81| 4: [BlockStmt] {...}
|
||||
# 81| 0: [ReturnStmt] return ...;
|
||||
# 81| 0: [IntLiteral] 1
|
||||
# 80| 1: [Attribute] [Args(...)]
|
||||
# 80| -1: [TypeMention] ArgsAttribute
|
||||
# 80| 0: [AddExpr] ... + ...
|
||||
# 80| 0: [IntLiteral] 42
|
||||
# 80| 1: [IntLiteral] 0
|
||||
# 80| 1: [ArrayCreation] array creation of type Int32[]
|
||||
# 80| -2: [TypeMention] Int32[]
|
||||
# 80| 1: [TypeMention] int
|
||||
# 80| -1: [ArrayInitializer] { ..., ... }
|
||||
# 80| 0: [IntLiteral] 1
|
||||
# 80| 1: [IntLiteral] 2
|
||||
# 80| 2: [IntLiteral] 3
|
||||
# 80| 2: [NullLiteral] null
|
||||
# 80| 3: [CastExpr] (...) ...
|
||||
# 80| 0: [TypeAccess] access to type E
|
||||
# 80| 0: [TypeMention] E
|
||||
# 80| 1: [IntLiteral] 12
|
||||
# 80| 4: [NullLiteral] null
|
||||
# 80| 5: [ArrayCreation] array creation of type Object[]
|
||||
# 80| -2: [TypeMention] Object[]
|
||||
# 80| 1: [TypeMention] object
|
||||
# 80| -1: [ArrayInitializer] { ..., ... }
|
||||
# 80| 0: [CastExpr] (...) ...
|
||||
# 80| 1: [IntLiteral] 1
|
||||
# 80| 1: [TypeofExpr] typeof(...)
|
||||
# 80| 0: [TypeAccess] access to type Int32
|
||||
# 80| 0: [TypeMention] int
|
||||
# 82| 4: [BlockStmt] {...}
|
||||
# 82| 0: [ReturnStmt] return ...;
|
||||
# 82| 0: [IntLiteral] 1
|
||||
# 85| [Class] My2Attribute
|
||||
#-----| 3: (Base types)
|
||||
# 85| 0: [TypeMention] Attribute
|
||||
# 87| 4: [Property] X
|
||||
# 87| -1: [TypeMention] int
|
||||
# 87| 3: [Getter] get_X
|
||||
# 87| 4: [Setter] set_X
|
||||
#-----| 2: (Parameters)
|
||||
# 87| 0: [Parameter] value
|
||||
# 88| 5: [InstanceConstructor] My2Attribute
|
||||
#-----| 2: (Parameters)
|
||||
# 88| 0: [Parameter] a
|
||||
# 88| -1: [TypeMention] bool
|
||||
# 88| 1: [Parameter] b
|
||||
# 88| -1: [TypeMention] bool
|
||||
# 88| 2: [Parameter] i
|
||||
# 88| -1: [TypeMention] int
|
||||
# 88| 1: [IntLiteral] 12
|
||||
# 88| 3: [Parameter] j
|
||||
# 88| -1: [TypeMention] int
|
||||
# 88| 1: [IntLiteral] 13
|
||||
# 88| 4: [BlockStmt] {...}
|
||||
|
||||
@@ -55,6 +55,7 @@ class Bar
|
||||
void M1() { }
|
||||
|
||||
[MyAttribute(true, y = "", x = 0)]
|
||||
[My2(b: true, j: 1, a: false, X = 42)]
|
||||
void M2() { }
|
||||
}
|
||||
|
||||
@@ -80,3 +81,9 @@ public class X
|
||||
[return: Args(42 + 0, new int[] { 1, 2, 3 }, null, (E)12, null, Prop = new object[] { 1, typeof(int) })]
|
||||
int SomeMethod() { return 1; }
|
||||
}
|
||||
|
||||
class My2Attribute : Attribute
|
||||
{
|
||||
public int X { get; set; }
|
||||
public My2Attribute(bool a, bool b, int i = 12, int j = 13) { }
|
||||
}
|
||||
10
csharp/ql/test/library-tests/cil/init-only-prop/Program.cs
Normal file
10
csharp/ql/test/library-tests/cil/init-only-prop/Program.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
// semmle-extractor-options: --cil
|
||||
|
||||
using System;
|
||||
|
||||
class Test
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
}
|
||||
}
|
||||
13
csharp/ql/test/library-tests/cil/init-only-prop/Test.cs_
Normal file
13
csharp/ql/test/library-tests/cil/init-only-prop/Test.cs_
Normal file
@@ -0,0 +1,13 @@
|
||||
namespace System.Runtime.CompilerServices
|
||||
{
|
||||
class IsExternalInit { }
|
||||
}
|
||||
|
||||
namespace cil_init_prop
|
||||
{
|
||||
class SomeClass
|
||||
{
|
||||
public int Prop1 { get; set; }
|
||||
public int Prop2 { get; init; }
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,107 @@
|
||||
| AsRef | System.Runtime.InteropServices.InAttribute | modreq |
|
||||
| BeginInvoke | System.Runtime.InteropServices.InAttribute | modreq |
|
||||
| EndInvoke | System.Runtime.InteropServices.InAttribute | modreq |
|
||||
| EventWriteTransfer | System.Runtime.InteropServices.InAttribute | modreq |
|
||||
| GetPinnableReference | System.Runtime.InteropServices.InAttribute | modreq |
|
||||
| Invoke | System.Runtime.InteropServices.InAttribute | modreq |
|
||||
| Max | System.Runtime.InteropServices.InAttribute | modreq |
|
||||
| Min | System.Runtime.InteropServices.InAttribute | modreq |
|
||||
| Value | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| _bufferedValues | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| _bufferedValuesIndex | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| _callbackPartitions | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| _canceled | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| _container | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| _fullyInitialized | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| _head | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| _initialized | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| _isFullyInitialized | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| _isWriterInProgress | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| _kernelEvent | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| _localTimeZone | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| _next | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| _notifyWhenNoCallbacksRunning | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| _oldKeepAlive | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| _owner | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| _pauseTicks | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| _previous | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| _queues | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| _saDurationFormats | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| _saLongTimes | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| _saShortTimes | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| _slotArray | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| _state | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| _tail | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| _threadIDExecutingCallbacks | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| _timer | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| _version | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| _waCalendars | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| currentTimeZone | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| g_nameCache | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| get_Current | System.Runtime.InteropServices.InAttribute | modreq |
|
||||
| get_Item | System.Runtime.InteropServices.InAttribute | modreq |
|
||||
| m_Dispatchers | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| m_Next | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| m_array | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| m_channelData | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| m_combinedState | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| m_completionCountdown | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| m_completionEvent | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| m_continuationObject | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| m_currentCount | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| m_declaringType | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| m_etwProvider | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| m_eventData | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| m_eventObj | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| m_eventPipeProvider | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| m_exceptionalChildren | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| m_exceptionsHolder | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| m_faultExceptions | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| m_first | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| m_head | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| m_headIndex | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| m_internalCancellationRequested | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| m_isHandled | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| m_last | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| m_lock | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| m_mask | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| m_nameIsCached | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| m_rawManifest | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| m_signature | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| m_stateFlags | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| m_tail | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| m_tailIndex | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| m_taskId | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| m_taskSchedulerId | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| m_waitHandle | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| numOutstandingThreadRequests | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| property Current | System.Runtime.InteropServices.InAttribute | modreq |
|
||||
| property Item | System.Runtime.InteropServices.InAttribute | modreq |
|
||||
| s_DefaultThreadCurrentCulture | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| s_DefaultThreadCurrentUICulture | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| s_Invariant | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| s_LcidCachedCultures | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| s_NameCachedCultures | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| s_anonymouslyHostedDynamicMethodsModule | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| s_cachedCultures | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| s_cachedRegions | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| s_currentRegionInfo | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| s_defaultBinder | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| s_defaultInstance | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| s_indentSize | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| s_initialized | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| s_invariant | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| s_invariantInfo | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| s_jajpDTFI | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| s_japaneseEraInfo | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| s_knownWords | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| s_lastProcessorCountRefreshTicks | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| s_processorCount | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| s_provider | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| s_providers | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| s_regionNames | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| s_userDefaultCulture | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| s_userDefaultUICulture | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| s_zhtwDTFI | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
| set_Prop2 | System.Runtime.CompilerServices.IsExternalInit | modreq |
|
||||
| threadPoolInitialized | System.Runtime.CompilerServices.IsVolatile | modreq |
|
||||
@@ -0,0 +1,13 @@
|
||||
import semmle.code.cil.Type
|
||||
|
||||
bindingset[kind]
|
||||
private string getKind(int kind) { if kind = 1 then result = "modreq" else result = "modopt" }
|
||||
|
||||
from string receiver, string modifier, int kind
|
||||
where
|
||||
exists(Type modType, CustomModifierReceiver cmr |
|
||||
receiver = cmr.toString() and
|
||||
cil_custom_modifiers(cmr, modType, kind) and
|
||||
modType.getQualifiedName() = modifier
|
||||
)
|
||||
select receiver, modifier, getKind(kind)
|
||||
@@ -0,0 +1,2 @@
|
||||
| cil-init-prop.dll:0:0:0:0 | set_Prop1 | set |
|
||||
| cil-init-prop.dll:0:0:0:0 | set_Prop2 | init |
|
||||
@@ -0,0 +1,8 @@
|
||||
import semmle.code.cil.Method
|
||||
import semmle.code.csharp.Location
|
||||
|
||||
private string getType(Setter s) { if s.isInitOnly() then result = "init" else result = "set" }
|
||||
|
||||
from Setter s
|
||||
where s.getLocation().(Assembly).getName() = "cil-init-prop"
|
||||
select s, getType(s)
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
||||
description: Added 'anonymous_types' to store anonymous types.
|
||||
compatibility: backwards
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
||||
description: Added 'cil_custom_modifiers' to store custom modifiers ('modreq', 'modopt').
|
||||
compatibility: backwards
|
||||
@@ -200,7 +200,7 @@ blockquote.pull-quote {
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
blockquote.pull-quote p:first-of-type {
|
||||
blockquote.pull-quote:first-line {
|
||||
font-weight: bold;
|
||||
margin-top: 0px;
|
||||
}
|
||||
@@ -222,10 +222,12 @@ blockquote.pull-quote > :last-child {
|
||||
|
||||
.toggle .name:after {
|
||||
content: " ▶";
|
||||
font-family: "monospace";
|
||||
}
|
||||
|
||||
.toggle .name.open:after {
|
||||
content: " ▼";
|
||||
font-family: "monospace";
|
||||
}
|
||||
|
||||
/* -- PRINT VIEW ----------------------------------------------------------------------------*/
|
||||
@@ -261,4 +263,4 @@ blockquote.pull-quote > :last-child {
|
||||
div.footer {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,6 +57,8 @@ from any location in the pack by declaring ``import mycompany.java.CustomSinks``
|
||||
For more information, see ":ref:`Importing modules <importing-modules>`"
|
||||
in the QL language reference.
|
||||
|
||||
.. _qlpack-yml-properties:
|
||||
|
||||
``qlpack.yml`` properties
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -101,7 +103,7 @@ The following properties are supported in ``qlpack.yml`` files.
|
||||
* - ``upgrades``
|
||||
- ``.``
|
||||
- Packs with upgrades
|
||||
- The path to a directory within the pack that contains upgrade scripts, defined relative to the pack directory. The ``database upgrade`` action uses these scripts to update databases that were created by an older version of an extractor so they're compatible with the current extractor (see `Upgrade scripts for a language <upgrade-scripts-for-a-language>`__ below.)
|
||||
- The path to a directory within the pack that contains upgrade scripts, defined relative to the pack directory. The ``database upgrade`` action uses these scripts to update databases that were created by an older version of an extractor so they're compatible with the current extractor (see `Upgrade scripts for a language <#upgrade-scripts-for-a-language>`__ below.)
|
||||
|
||||
|
||||
.. _custom-ql-packs:
|
||||
|
||||
@@ -16,7 +16,7 @@ mapping with (usually) a single key. The instructions are executed in the order
|
||||
they appear in the query suite definition. After all the instructions in the
|
||||
suite definition have been executed, the result is a set of selected queries.
|
||||
|
||||
.. note::
|
||||
.. pull-quote:: Note
|
||||
|
||||
Any custom queries that you want to add to a query suite must be in a :doc:`QL
|
||||
pack <about-ql-packs>` and contain the correct query metadata.
|
||||
@@ -54,7 +54,7 @@ queries using:
|
||||
|
||||
- qlpack: <qlpack-name>
|
||||
|
||||
.. note::
|
||||
.. pull-quote:: Note
|
||||
|
||||
When pathnames appear in query suite definitions, they must always
|
||||
be given with a forward slash, ``/``, as a directory separator.
|
||||
@@ -254,7 +254,7 @@ without providing their full path. This gives you a simple way of specifying a
|
||||
set of queries, without needing to search inside QL packs and distributions.
|
||||
To declare a directory that contains "well-known" query suites, add the directory
|
||||
to the ``suites`` property in the ``qlpack.yml`` file at the root of your QL pack.
|
||||
For more information, see "`About QL packs <qlpack-overview.html#qlpack-yml-properties>`__."
|
||||
For more information, see ":ref:`About QL packs <qlpack-yml-properties>`."
|
||||
|
||||
Using query suites with CodeQL
|
||||
------------------------------
|
||||
|
||||
@@ -44,4 +44,4 @@ to the ``codeql-javascript`` QL pack::
|
||||
|
||||
AngularJS/DeadAngularJSEventListener.ql
|
||||
|
||||
For another example, see `Testing custom queries <test-queries.html#example>`__.
|
||||
For another example, see :doc:`Testing custom queries <testing-custom-queries>`.
|
||||
|
||||
@@ -8,7 +8,7 @@ You can write CodeQL queries to explore the control-flow graph of a Python progr
|
||||
About analyzing control flow
|
||||
--------------------------------------
|
||||
|
||||
To analyze the control-flow graph of a ``Scope`` we can use the two CodeQL classes ``ControlFlowNode`` and ``BasicBlock``. These classes allow you to ask such questions as "can you reach point A from point B?" or "Is it possible to reach point B *without* going through point A?". To report results we use the class ``AstNode``, which represents a syntactic element and corresponds to the source code - allowing the results of the query to be more easily understood. For more information, see `Control-flow graph <http://en.wikipedia.org/wiki/Control_flow_graph>`__ on Wikipedia.
|
||||
To analyze the control-flow graph of a ``Scope`` we can use the two CodeQL classes ``ControlFlowNode`` and ``BasicBlock``. These classes allow you to ask such questions as "can you reach point A from point B?" or "Is it possible to reach point B *without* going through point A?". To report results we use the class ``AstNode``, which represents a syntactic element and corresponds to the source code - allowing the results of the query to be more easily understood. For more information, see `Control-flow graph <https://en.wikipedia.org/wiki/Control_flow_graph>`__ on Wikipedia.
|
||||
|
||||
The ``ControlFlowNode`` class
|
||||
-----------------------------
|
||||
@@ -65,7 +65,7 @@ Example finding unreachable statements
|
||||
The ``BasicBlock`` class
|
||||
------------------------
|
||||
|
||||
The ``BasicBlock`` class represents a basic block of control flow nodes. The ``BasicBlock`` class is not that useful for writing queries directly, but is very useful for building complex analyses, such as data flow. The reason it is useful is that it shares many of the interesting properties of control flow nodes, such as, what can reach what, and what dominates what, but there are fewer basic blocks than control flow nodes - resulting in queries that are faster and use less memory. For more information, see `Basic block <http://en.wikipedia.org/wiki/Basic_block>`__ and `Dominator <http://en.wikipedia.org/wiki/Dominator_%28graph_theory%29>`__ on Wikipedia.
|
||||
The ``BasicBlock`` class represents a basic block of control flow nodes. The ``BasicBlock`` class is not that useful for writing queries directly, but is very useful for building complex analyses, such as data flow. The reason it is useful is that it shares many of the interesting properties of control flow nodes, such as, what can reach what, and what dominates what, but there are fewer basic blocks than control flow nodes - resulting in queries that are faster and use less memory. For more information, see `Basic block <https://en.wikipedia.org/wiki/Basic_block>`__ and `Dominator <https://en.wikipedia.org/wiki/Dominator_%28graph_theory%29>`__ on Wikipedia.
|
||||
|
||||
Example finding mutually exclusive basic blocks
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
@@ -703,8 +703,8 @@ Further reading
|
||||
.. _SizeofPackOperator: https://codeql.github.com/codeql-standard-libraries/cpp/semmle/code/cpp/exprs/Cast.qll/type.Cast$SizeofPackOperator.html
|
||||
.. _StmtExpr: https://codeql.github.com/codeql-standard-libraries/cpp/semmle/code/cpp/exprs/Expr.qll/type.Expr$StmtExpr.html
|
||||
.. _ThisExpr: https://codeql.github.com/codeql-standard-libraries/cpp/semmle/code/cpp/exprs/Expr.qll/type.Expr$ThisExpr.html
|
||||
.. _ThrowExpr: https://codeql.github.com/codeql-standard-libraries/cpp/semmle/code/cpp/exprs/Call.qll/type.Call$ThrowExpr.html
|
||||
.. _ReThrowExpr: https://codeql.github.com/codeql-standard-libraries/cpp/semmle/code/cpp/exprs/Call.qll/type.Call$ReThrowExpr.html
|
||||
.. _ThrowExpr: https://codeql.github.com/codeql-standard-libraries/cpp/semmle/code/cpp/exprs/Expr.qll/type.Expr$ThrowExpr.html
|
||||
.. _ReThrowExpr: https://codeql.github.com/codeql-standard-libraries/cpp/semmle/code/cpp/exprs/Expr.qll/type.Expr$ReThrowExpr.html
|
||||
.. _TypeidOperator: https://codeql.github.com/codeql-standard-libraries/cpp/semmle/code/cpp/exprs/Cast.qll/type.Cast$TypeidOperator.html
|
||||
.. _UuidofOperator: https://codeql.github.com/codeql-standard-libraries/cpp/semmle/code/cpp/exprs/Cast.qll/type.Cast$UuidofOperator.html
|
||||
.. _VoidType: https://codeql.github.com/codeql-standard-libraries/cpp/semmle/code/cpp/Type.qll/type.Type$VoidType.html
|
||||
|
||||
@@ -108,8 +108,8 @@ Count the number of lines of code, excluding the directory ``external``:
|
||||
.. code-block:: ql
|
||||
|
||||
select sum(SourceFile f |
|
||||
not exists(Folder external | external.getShortName() = "external" |
|
||||
external.getAFolder*().getAFile() = f) |
|
||||
not exists(Folder ext | ext.getShortName() = "external" |
|
||||
ext.getAFolder*().getAFile() = f) |
|
||||
f.getNumberOfLines())
|
||||
|
||||
Exercises
|
||||
@@ -961,7 +961,7 @@ Find all obsolete elements:
|
||||
|
||||
Model NUnit test fixtures:
|
||||
|
||||
.. code-block:: csharp
|
||||
.. code-block:: ql
|
||||
|
||||
class TestFixture extends Class
|
||||
{
|
||||
|
||||
@@ -22,7 +22,7 @@ library by beginning your query with:
|
||||
|
||||
Broadly speaking, the CodeQL library for Go provides two views of a Go code base: at the `syntactic
|
||||
level`, source code is represented as an `abstract syntax tree
|
||||
<https://wikipedia.org/wiki/Abstract_syntax_tree>`__ (AST), while at the `data-flow level` it is
|
||||
<https://en.wikipedia.org/wiki/Abstract_syntax_tree>`__ (AST), while at the `data-flow level` it is
|
||||
represented as a `data-flow graph <https://en.wikipedia.org/wiki/Data-flow_analysis>`__ (DFG). In
|
||||
between, there is also an intermediate representation of the program as a control-flow graph (CFG),
|
||||
though this representation is rarely useful on its own and mostly used to construct the higher-level
|
||||
|
||||
@@ -156,12 +156,12 @@ The class `Comment <https://codeql.github.com/codeql-standard-libraries/javascri
|
||||
|
||||
- `HtmlCommentStart <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/Comments.qll/type.Comments$HtmlCommentStart.html>`__: an HTML comment starting with ``<!--``
|
||||
|
||||
- `HtmlCommentEnd <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/Comments.qll/type.Comments$HtmlCommentEnd.html>`__: an HTML comment ending with ``-->``
|
||||
- `HtmlCommentEnd <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/Comments.qll/type.Comments$HtmlCommentEnd.html>`__: an HTML comment ending with ``-->``
|
||||
|
||||
- `BlockComment <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/Comments.qll/type.Comments$BlockComment.html>`__: a block comment potentially spanning multiple lines
|
||||
- `BlockComment <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/Comments.qll/type.Comments$BlockComment.html>`__: a block comment potentially spanning multiple lines
|
||||
|
||||
- `SlashStarComment <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/Comments.qll/type.Comments$SlashStarComment.html>`__: a plain JavaScript block comment surrounded with ``/*...*/``
|
||||
- `DocComment <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/Comments.qll/type.Comments$DocComment.html>`__: a documentation block comment surrounded with ``/**...*/``
|
||||
- `SlashStarComment <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/Comments.qll/type.Comments$SlashStarComment.html>`__: a plain JavaScript block comment surrounded with ``/*...*/``
|
||||
- `DocComment <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/Comments.qll/type.Comments$DocComment.html>`__: a documentation block comment surrounded with ``/**...*/``
|
||||
|
||||
The most important member predicates are as follows:
|
||||
|
||||
@@ -184,7 +184,7 @@ As an example of a query using only lexical information, consider the following
|
||||
Syntactic level
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
The majority of classes in the JavaScript library is concerned with representing a JavaScript program as a collection of `abstract syntax trees <http://en.wikipedia.org/wiki/Abstract_syntax_tree>`__ (ASTs).
|
||||
The majority of classes in the JavaScript library is concerned with representing a JavaScript program as a collection of `abstract syntax trees <https://en.wikipedia.org/wiki/Abstract_syntax_tree>`__ (ASTs).
|
||||
|
||||
The class `ASTNode <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/AST.qll/type.AST$ASTNode.html>`__ contains all entities representing nodes in the abstract syntax trees and defines generic tree traversal predicates:
|
||||
|
||||
@@ -215,7 +215,7 @@ From a syntactic point of view, each JavaScript program is composed of one or mo
|
||||
- `EventHandlerCode <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/AST.qll/type.AST$EventHandlerCode.html>`__: code from an event handler attribute such as ``onload``
|
||||
- `JavaScriptURL <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/AST.qll/type.AST$JavaScriptURL.html>`__: code from a URL with the ``javascript:`` scheme
|
||||
|
||||
- `Externs <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/AST.qll/type.AST$Externs.html>`__: a JavaScript file containing `externs <https://developers.google.com/closure/compiler/docs/api-tutorial3#externs>`__ definitions
|
||||
- `Externs <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/AST.qll/type.AST$Externs.html>`__: a JavaScript file containing `externs <https://developers.google.com/closure/compiler/docs/externs-and-exports>`__ definitions
|
||||
|
||||
Every `TopLevel <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/AST.qll/type.AST$TopLevel.html>`__ class is contained in a `File <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/Files.qll/type.Files$File.html>`__ class, but a single `File <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/Files.qll/type.Files$File.html>`__ may contain more than one `TopLevel <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/AST.qll/type.AST$TopLevel.html>`__. To go from a ``TopLevel tl`` to its `File <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/Files.qll/type.Files$File.html>`__, use ``tl.getFile()``; conversely, for a ``File f``, predicate ``f.getATopLevel()`` returns a top-level contained in ``f``. For every AST node, predicate ``ASTNode.getTopLevel()`` can be used to find the top-level it belongs to.
|
||||
|
||||
@@ -361,7 +361,7 @@ JavaScript provides several ways of defining functions: in ECMAScript 5, there a
|
||||
|
||||
- ``Function.getId()`` returns the `Identifier <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/Expr.qll/type.Expr$Identifier.html>`__ naming the function, which may not be defined for function expressions.
|
||||
- ``Function.getParameter(i)`` and ``Function.getAParameter()`` access the ``i``\ th parameter or any parameter, respectively; parameters are modeled by the class `Parameter <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/Variables.qll/type.Variables$Parameter.html>`__, which is a subclass of `BindingPattern <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/Variables.qll/type.Variables$BindingPattern.html>`__ (see below).
|
||||
- ``Function.getBody()`` returns the body of the function, which is usually a `Stmt <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/Stmt.qll/type.Stmt$Stmt.html>`__, but may be an `Expr <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/Expr.qll/type.Expr$Expr.html>`__ for arrow function expressions and legacy `expression closures <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Expression_closures>`__.
|
||||
- ``Function.getBody()`` returns the body of the function, which is usually a `Stmt <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/Stmt.qll/type.Stmt$Stmt.html>`__, but may be an `Expr <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/Expr.qll/type.Expr$Expr.html>`__ for arrow function expressions and legacy `expression closures <https://developer.mozilla.org/en-US/docs/Archive/Web/JavaScript/Expression_closures>`__.
|
||||
|
||||
As an example, here is a query that finds all expression closures:
|
||||
|
||||
@@ -560,10 +560,10 @@ The structure of the control flow graph is reflected in the member predicates of
|
||||
- ``ControlFlowNode.isJoin()`` determines whether this node has more than one predecessor.
|
||||
- ``ControlFlowNode.isStart()`` determines whether this node is a start node.
|
||||
|
||||
Many control-flow-based analyses are phrased in terms of `basic blocks <http://en.wikipedia.org/wiki/Basic_block>`__ rather than single control flow nodes, where a basic block is a maximal sequence of control flow nodes without branches or joins. The class `BasicBlock <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/BasicBlocks.qll/type.BasicBlocks$BasicBlock.html>`__ from `BasicBlocks.qll <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/BasicBlocks.qll/module.BasicBlocks.html>`__ represents all such basic blocks. Similar to `ControlFlowNode <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/CFG.qll/type.CFG$ControlFlowNode.html>`__, it provides member predicates ``getASuccessor()`` and ``getAPredecessor()`` to navigate the control flow graph at the level of basic blocks, and member predicates ``getANode()``, ``getNode(int)``, ``getFirstNode()`` and ``getLastNode()`` to access individual control flow nodes within a basic block. The predicate
|
||||
Many control-flow-based analyses are phrased in terms of `basic blocks <https://en.wikipedia.org/wiki/Basic_block>`__ rather than single control flow nodes, where a basic block is a maximal sequence of control flow nodes without branches or joins. The class `BasicBlock <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/BasicBlocks.qll/type.BasicBlocks$BasicBlock.html>`__ from `BasicBlocks.qll <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/BasicBlocks.qll/module.BasicBlocks.html>`__ represents all such basic blocks. Similar to `ControlFlowNode <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/CFG.qll/type.CFG$ControlFlowNode.html>`__, it provides member predicates ``getASuccessor()`` and ``getAPredecessor()`` to navigate the control flow graph at the level of basic blocks, and member predicates ``getANode()``, ``getNode(int)``, ``getFirstNode()`` and ``getLastNode()`` to access individual control flow nodes within a basic block. The predicate
|
||||
``Function.getEntryBB()`` returns the entry basic block in a function, that is, the basic block containing the function's entry node. Similarly, ``Function.getStartBB()`` provides access to the start basic block, which contains the function's start node. As for CFG nodes, ``getStartBB()`` should normally be preferred over ``getEntryBB()``.
|
||||
|
||||
As an example of an analysis using basic blocks, ``BasicBlock.isLiveAtEntry(v, u)`` determines whether variable ``v`` is `live <http://en.wikipedia.org/wiki/Live_variable_analysis>`__ at the entry of the given basic block, and if so binds ``u`` to a use of ``v`` that refers to its value at the entry. We can use it to find global variables that are used in a function where they are not live (that is, every read of the variable is preceded by a write), suggesting that the variable was meant to be declared as a local variable instead:
|
||||
As an example of an analysis using basic blocks, ``BasicBlock.isLiveAtEntry(v, u)`` determines whether variable ``v`` is `live <https://en.wikipedia.org/wiki/Live_variable_analysis>`__ at the entry of the given basic block, and if so binds ``u`` to a use of ``v`` that refers to its value at the entry. We can use it to find global variables that are used in a function where they are not live (that is, every read of the variable is preceded by a write), suggesting that the variable was meant to be declared as a local variable instead:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
@@ -582,7 +582,7 @@ Data flow
|
||||
Definitions and uses
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Library `DefUse.qll <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/DefUse.qll/module.DefUse.html>`__ provides classes and predicates to determine `def-use <http://en.wikipedia.org/wiki/Use-define_chain>`__ relationships between definitions and uses of variables.
|
||||
Library `DefUse.qll <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/DefUse.qll/module.DefUse.html>`__ provides classes and predicates to determine `def-use <https://en.wikipedia.org/wiki/Use-define_chain>`__ relationships between definitions and uses of variables.
|
||||
|
||||
Classes `VarDef <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/DefUse.qll/type.DefUse$VarDef.html>`__ and `VarUse <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/DefUse.qll/type.DefUse$VarUse.html>`__ contain all expressions that define and use a variable, respectively. For the former, you can use predicate ``VarDef.getAVariable()`` to find out which variables are defined by a given variable definition (recall that destructuring assignments in ECMAScript 2015 define several variables at the same time). Similarly, predicate ``VarUse.getVariable()`` returns the (single) variable being accessed by a variable use.
|
||||
|
||||
@@ -686,7 +686,7 @@ You can add custom type inference rules by defining new subclasses of ``DataFlow
|
||||
Call graph
|
||||
~~~~~~~~~~
|
||||
|
||||
The JavaScript library implements a simple `call graph <http://en.wikipedia.org/wiki/Call_graph>`__ construction algorithm to statically approximate the possible call targets of function calls and ``new`` expressions. Due to the dynamically typed nature of JavaScript and its support for higher-order functions and reflective language features, building static call graphs is quite difficult. Simple call graph algorithms tend to be incomplete, that is, they often fail to resolve all possible call targets. More sophisticated algorithms can suffer from the opposite problem of imprecision, that is, they may infer many spurious call targets.
|
||||
The JavaScript library implements a simple `call graph <https://en.wikipedia.org/wiki/Call_graph>`__ construction algorithm to statically approximate the possible call targets of function calls and ``new`` expressions. Due to the dynamically typed nature of JavaScript and its support for higher-order functions and reflective language features, building static call graphs is quite difficult. Simple call graph algorithms tend to be incomplete, that is, they often fail to resolve all possible call targets. More sophisticated algorithms can suffer from the opposite problem of imprecision, that is, they may infer many spurious call targets.
|
||||
|
||||
The call graph is represented by the member predicate ``getACallee()`` of class `DataFlow::InvokeNode <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/dataflow/Nodes.qll/type.Nodes$InvokeNode.html>`__, which computes possible callees of the given invocation, that is, functions that may at runtime be invoked by this expression.
|
||||
|
||||
@@ -801,7 +801,7 @@ Frameworks
|
||||
AngularJS
|
||||
^^^^^^^^^
|
||||
|
||||
The ``semmle.javascript.frameworks.AngularJS`` library provides support for working with `AngularJS (Angular 1.x) <https://www.angularjs.org/>`__ code. Its most important classes are:
|
||||
The ``semmle.javascript.frameworks.AngularJS`` library provides support for working with `AngularJS (Angular 1.x) <https://angularjs.org/>`__ code. Its most important classes are:
|
||||
|
||||
- `AngularJS::AngularModule <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/frameworks/AngularJS/AngularJSCore.qll/type.AngularJSCore$AngularModule.html>`__: an Angular module
|
||||
- `AngularJS::DirectiveDefinition <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/frameworks/AngularJS/ServiceDefinitions.qll/type.ServiceDefinitions$DirectiveDefinition.html>`__, `AngularJS::FactoryRecipeDefinition <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/frameworks/AngularJS/ServiceDefinitions.qll/type.ServiceDefinitions$FactoryRecipeDefinition.html>`__, `AngularJS::FilterDefinition <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/frameworks/AngularJS/ServiceDefinitions.qll/type.ServiceDefinitions$FilterDefinition.html>`__, `AngularJS::ControllerDefinition <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/frameworks/AngularJS/ServiceDefinitions.qll/type.ServiceDefinitions$ControllerDefinition.html>`__: a definition of a directive, service, filter or controller, respectively
|
||||
@@ -812,7 +812,7 @@ HTTP framework libraries
|
||||
|
||||
The library ``semmle.javacript.frameworks.HTTP`` provides classes modeling common concepts from various HTTP frameworks.
|
||||
|
||||
Currently supported frameworks are `Express <https://expressjs.com/>`__, the standard Node.js ``http`` and ``https`` modules, `Connect <https://github.com/senchalabs/connect>`__, `Koa <https://koajs.com>`__, `Hapi <https://hapijs.com/>`__ and `Restify <http://restify.com/>`__.
|
||||
Currently supported frameworks are `Express <https://expressjs.com/>`__, the standard Node.js ``http`` and ``https`` modules, `Connect <https://github.com/senchalabs/connect>`__, `Koa <https://koajs.com>`__, `Hapi <https://hapi.dev/>`__ and `Restify <http://restify.com/>`__.
|
||||
|
||||
The most important classes include (all in module ``HTTP``):
|
||||
|
||||
@@ -848,7 +848,7 @@ As an example of the use of these classes, here is a query that counts for every
|
||||
NPM
|
||||
^^^
|
||||
|
||||
The ``semmle.javascript.NPM`` library provides support for working with `NPM <http://npmjs.org/>`__ packages through the following classes:
|
||||
The ``semmle.javascript.NPM`` library provides support for working with `NPM <https://www.npmjs.com/>`__ packages through the following classes:
|
||||
|
||||
- `PackageJSON <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/NPM.qll/type.NPM$PackageJSON.html>`__: a ``package.json`` file describing an NPM package; various getter predicates are available for accessing detailed information about the package, which are described in the `online API documentation <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/NPM.qll/module.NPM.html>`__.
|
||||
- `BugTrackerInfo <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/NPM.qll/type.NPM$BugTrackerInfo.html>`__, `ContributorInfo <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/NPM.qll/type.NPM$ContributorInfo.html>`__, `RepositoryInfo <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/NPM.qll/type.NPM$RepositoryInfo.html>`__: these classes model parts of the ``package.json`` file providing information on bug tracking systems, contributors and repositories.
|
||||
@@ -877,7 +877,7 @@ As an example of the use of these classes, here is a query that identifies unuse
|
||||
React
|
||||
^^^^^
|
||||
|
||||
The ``semmle.javascript.frameworks.React`` library provides support for working with `React <https://facebook.github.io/react/>`__ code through the `ReactComponent <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/frameworks/React.qll/type.React$ReactComponent.html>`__ class, which models a React component defined either in the functional style or the class-based style (both ECMAScript 2015 classes and old-style ``React.createClass`` classes are supported).
|
||||
The ``semmle.javascript.frameworks.React`` library provides support for working with `React <https://reactjs.org/>`__ code through the `ReactComponent <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/frameworks/React.qll/type.React$ReactComponent.html>`__ class, which models a React component defined either in the functional style or the class-based style (both ECMAScript 2015 classes and old-style ``React.createClass`` classes are supported).
|
||||
|
||||
Databases
|
||||
^^^^^^^^^
|
||||
@@ -940,7 +940,7 @@ Both ``HTML::Element`` and ``HTML::Attribute`` have a predicate ``getRoot()`` th
|
||||
JSDoc
|
||||
^^^^^
|
||||
|
||||
The ``semmle.javascript.JSDoc`` library provides support for working with `JSDoc comments <http://usejsdoc.org/>`__. Documentation comments are parsed into an abstract syntax tree representation closely following the format employed by the `Doctrine <https://github.com/Constellation/doctrine>`__ JSDoc parser.
|
||||
The ``semmle.javascript.JSDoc`` library provides support for working with `JSDoc comments <https://jsdoc.app/>`__. Documentation comments are parsed into an abstract syntax tree representation closely following the format employed by the `Doctrine <https://github.com/eslint/doctrine>`__ JSDoc parser.
|
||||
|
||||
A JSDoc comment as a whole is represented by an entity of class `JSDoc <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/JSDoc.qll/type.JSDoc$JSDoc.html>`__, while individual tags are represented by class `JSDocTag <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/JSDoc.qll/type.JSDoc$JSDocTag.html>`__. Important member predicates of these two classes include:
|
||||
|
||||
@@ -972,7 +972,7 @@ For full details on these and other classes representing JSDoc comments and type
|
||||
JSX
|
||||
^^^
|
||||
|
||||
The ``semmle.javascript.JSX`` library provides support for working with `JSX code <https://facebook.github.io/react/docs/jsx-in-depth.html>`__.
|
||||
The ``semmle.javascript.JSX`` library provides support for working with `JSX code <https://reactjs.org/docs/jsx-in-depth.html>`__.
|
||||
|
||||
Similar to the representation of HTML documents, JSX fragments are modeled as a tree of `JSXElement <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/JSX.qll/type.JSX$JSXElement.html>`__\ s, each of which may have zero or more `JSXAttribute <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/JSX.qll/type.JSX$JSXAttribute.html>`__\ s.
|
||||
|
||||
@@ -1011,7 +1011,7 @@ Various subclasses of `RegExpTerm <https://codeql.github.com/codeql-standard-lib
|
||||
YAML
|
||||
^^^^
|
||||
|
||||
The ``semmle.javascript.YAML`` library provides support for working with `YAML <http://yaml.org/>`__ files that were processed by the JavaScript extractor when building the CodeQL database.
|
||||
The ``semmle.javascript.YAML`` library provides support for working with `YAML <https://yaml.org/>`__ files that were processed by the JavaScript extractor when building the CodeQL database.
|
||||
|
||||
YAML files are modeled as trees of YAML nodes. Each YAML node is represented by an entity of class `YAMLNode <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/YAML.qll/type.YAML$YAMLNode.html>`__, which provides, among others, the following member predicates:
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ The CodeQL library for Python incorporates a large number of classes. Each class
|
||||
Syntactic classes
|
||||
-----------------
|
||||
|
||||
This part of the library represents the Python source code. The ``Module``, ``Class``, and ``Function`` classes correspond to Python modules, classes, and functions respectively, collectively these are known as ``Scope`` classes. Each ``Scope`` contains a list of statements each of which is represented by a subclass of the class ``Stmt``. Statements themselves can contain other statements or expressions which are represented by subclasses of ``Expr``. Finally, there are a few additional classes for the parts of more complex expressions such as list comprehensions. Collectively these classes are subclasses of ``AstNode`` and form an Abstract syntax tree (AST). The root of each AST is a ``Module``. Symbolic information is attached to the AST in the form of variables (represented by the class ``Variable``). For more information, see `Abstract syntax tree <http://en.wikipedia.org/wiki/Abstract_syntax_tree>`__ and `Symbolic information <http://en.wikipedia.org/wiki/Symbol_table>`__ on Wikipedia.
|
||||
This part of the library represents the Python source code. The ``Module``, ``Class``, and ``Function`` classes correspond to Python modules, classes, and functions respectively, collectively these are known as ``Scope`` classes. Each ``Scope`` contains a list of statements each of which is represented by a subclass of the class ``Stmt``. Statements themselves can contain other statements or expressions which are represented by subclasses of ``Expr``. Finally, there are a few additional classes for the parts of more complex expressions such as list comprehensions. Collectively these classes are subclasses of ``AstNode`` and form an Abstract syntax tree (AST). The root of each AST is a ``Module``. Symbolic information is attached to the AST in the form of variables (represented by the class ``Variable``). For more information, see `Abstract syntax tree <https://en.wikipedia.org/wiki/Abstract_syntax_tree>`__ and `Symbolic information <https://en.wikipedia.org/wiki/Symbol_table>`__ on Wikipedia.
|
||||
|
||||
Scope
|
||||
^^^^^
|
||||
@@ -241,7 +241,7 @@ Other
|
||||
Control flow classes
|
||||
--------------------
|
||||
|
||||
This part of the library represents the control flow graph of each ``Scope`` (classes, functions, and modules). Each ``Scope`` contains a graph of ``ControlFlowNode`` elements. Each scope has a single entry point and at least one (potentially many) exit points. To speed up control and data flow analysis, control flow nodes are grouped into basic blocks. For more information, see `Basic block <http://en.wikipedia.org/wiki/Basic_block>`__ on Wikipedia.
|
||||
This part of the library represents the control flow graph of each ``Scope`` (classes, functions, and modules). Each ``Scope`` contains a graph of ``ControlFlowNode`` elements. Each scope has a single entry point and at least one (potentially many) exit points. To speed up control and data flow analysis, control flow nodes are grouped into basic blocks. For more information, see `Basic block <https://en.wikipedia.org/wiki/Basic_block>`__ on Wikipedia.
|
||||
|
||||
Example
|
||||
^^^^^^^
|
||||
@@ -311,7 +311,7 @@ For example, which ``ClassValue``\ s are iterable can be determined using the qu
|
||||
where cls.hasAttribute("__iter__")
|
||||
select cls
|
||||
|
||||
➤ `See this in the query console on LGTM.com <https://lgtm.com/query/5151030165280978402/>`__ This query returns a list of classes for the projects analyzed. If you want to include the results for ``builtin`` classes, which do not have any Python source code, show the non-source results. For more information, see `builtin classes <http://docs.python.org/library/stdtypes.html>`__ in the Python documentation.
|
||||
➤ `See this in the query console on LGTM.com <https://lgtm.com/query/5151030165280978402/>`__ This query returns a list of classes for the projects analyzed. If you want to include the results for ``builtin`` classes, which do not have any Python source code, show the non-source results. For more information, see `builtin classes <https://docs.python.org/3/library/stdtypes.html>`__ in the Python documentation.
|
||||
|
||||
Summary
|
||||
^^^^^^^
|
||||
|
||||
@@ -138,7 +138,7 @@ The CodeQL class `ClassOrInterface <https://codeql.github.com/codeql-standard-li
|
||||
|
||||
Note that the superclass of a class is an expression, not a type annotation. If the superclass has type arguments, it will be an expression of kind `ExpressionWithTypeArguments <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/TypeScript.qll/type.TypeScript$ExpressionWithTypeArguments.html>`__.
|
||||
|
||||
Also see the documentation for classes in the "`CodeQL libraries for JavaScript <introduce-libraries-js#classes>`__."
|
||||
Also see the documentation for classes in the "`CodeQL libraries for JavaScript <https://codeql.github.com/docs/codeql-language-guides/codeql-library-for-javascript/#classes>`__."
|
||||
|
||||
To select the type references to a class or an interface, use ``getTypeName()``.
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ This query reports flow paths which:
|
||||
- Step through variables, function calls, properties, strings, arrays, promises, exceptions, and steps added by `isAdditionalTaintStep <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/dataflow/TaintTracking.qll/predicate.TaintTracking$TaintTracking$Configuration$isAdditionalTaintStep.2.html>`__.
|
||||
- End at a node matched by `isSink <https://codeql.github.com/codeql-standard-libraries/javascript/semmle/javascript/dataflow/Configuration.qll/predicate.Configuration$Configuration$isSink.1.html>`__.
|
||||
|
||||
See also: "`Global data flow <analyzing-data-flow-in-javascript-and-typescript.html#global-data-flow>`__" and ":ref:`Creating path queries <creating-path-queries>`."
|
||||
See also: "`Global data flow <https://codeql.github.com/docs/codeql-language-guides/analyzing-data-flow-in-javascript-and-typescript/#global-data-flow>`__" and ":ref:`Creating path queries <creating-path-queries>`."
|
||||
|
||||
DataFlow module
|
||||
---------------
|
||||
|
||||
@@ -97,7 +97,7 @@ When you have defined the basic query then you can refine the query to include f
|
||||
Improving the query using the 'SSA' library
|
||||
-------------------------------------------
|
||||
|
||||
The ``SSA`` library represents variables in static single assignment (SSA) form. In this form, each variable is assigned exactly once and every variable is defined before it is used. The use of SSA variables simplifies queries considerably as much of the local data flow analysis has been done for us. For more information, see `Static single assignment <http://en.wikipedia.org/wiki/Static_single_assignment_form>`__ on Wikipedia.
|
||||
The ``SSA`` library represents variables in static single assignment (SSA) form. In this form, each variable is assigned exactly once and every variable is defined before it is used. The use of SSA variables simplifies queries considerably as much of the local data flow analysis has been done for us. For more information, see `Static single assignment <https://en.wikipedia.org/wiki/Static_single_assignment_form>`__ on Wikipedia.
|
||||
|
||||
Including examples where the string size is stored before use
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -54,7 +54,7 @@ The value numbering library exposes its interface primarily through the ``GVN``
|
||||
|
||||
To get the ``GVN`` of an ``Expr``, use the ``globalValueNumber`` predicate.
|
||||
|
||||
.. note::
|
||||
.. pull-quote:: Note
|
||||
|
||||
While the ``GVN`` class has ``toString`` and ``getLocation`` methods, these are only provided as debugging aids. They give the ``toString`` and ``getLocation`` of an arbitrary ``Expr`` within the set.
|
||||
|
||||
@@ -90,7 +90,7 @@ The hash consing API
|
||||
|
||||
The hash consing library exposes its interface primarily through the ``HashCons`` class. Each instance of ``HashCons`` represents a set of expressions within one function that have the same syntax (including referring to the same variables). To get an expression in the set represented by a particular ``HashCons``, use the ``getAnExpr()`` member predicate.
|
||||
|
||||
.. note::
|
||||
.. pull-quote:: Note
|
||||
|
||||
While the ``HashCons`` class has ``toString`` and ``getLocation`` methods, these are only provided as debugging aids. They give the ``toString`` and ``getLocation`` of an arbitrary ``Expr`` within the set.
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ example from ``Mux.qll``.
|
||||
}
|
||||
|
||||
This has the effect that all calls to `the function Vars from the
|
||||
package mux <http://www.gorillatoolkit.org/pkg/mux#Vars>`__ are
|
||||
package mux <https://github.com/gorilla/mux>`__ are
|
||||
treated as sources of untrusted data.
|
||||
|
||||
Flow propagation
|
||||
|
||||
@@ -24,7 +24,7 @@ Class hierarchy for ``Value``:
|
||||
Points-to analysis and type inference
|
||||
-------------------------------------
|
||||
|
||||
Points-to analysis, sometimes known as pointer analysis, allows us to determine which objects an expression may "point to" at runtime. Type inference allows us to infer what the types (classes) of an expression may be at runtime. For more information, see `Pointer analysis <http://en.wikipedia.org/wiki/Pointer_analysis>`__ and `Type inference <http://en.wikipedia.org/wiki/Type_inference>`__ on Wikipedia.
|
||||
Points-to analysis, sometimes known as pointer analysis, allows us to determine which objects an expression may "point to" at runtime. Type inference allows us to infer what the types (classes) of an expression may be at runtime. For more information, see `Pointer analysis <https://en.wikipedia.org/wiki/Pointer_analysis>`__ and `Type inference <https://en.wikipedia.org/wiki/Type_inference>`__ on Wikipedia.
|
||||
|
||||
The predicate ``ControlFlowNode.pointsTo(...)`` shows which object a control flow node may "point to" at runtime.
|
||||
|
||||
|
||||
@@ -11,9 +11,9 @@ The type-tracking library makes it possible to track values through properties a
|
||||
usually to recognize method calls and properties accessed on a specific type of object.
|
||||
|
||||
This is an advanced topic and is intended for readers already familiar with the
|
||||
`SourceNode <analyzing-data-flow-in-javascript-and-typescript.html#source-nodes>`__ class as well as
|
||||
`taint tracking <analyzing-data-flow-in-javascript-and-typescript.html#using-global-analyzing-data-flow-and-tracking-tainted-data-in-python>`__.
|
||||
For TypeScript analysis also consider reading about `static type information <codeql-library-for-typescript.html.html#static-type-information>`__ first.
|
||||
`SourceNode <https://codeql.github.com/docs/codeql-language-guides/analyzing-data-flow-in-javascript-and-typescript/#source-nodes>`__ class as well as
|
||||
`taint tracking <https://codeql.github.com/docs/codeql-language-guides/analyzing-data-flow-in-javascript-and-typescript/#using-global-taint-tracking>`__.
|
||||
For TypeScript analysis also consider reading about `static type information <https://codeql.github.com/docs/codeql-language-guides/codeql-library-for-typescript/#static-type-information>`__ first.
|
||||
|
||||
|
||||
The problem of recognizing method calls
|
||||
@@ -458,7 +458,7 @@ Here's an example that the model from this tutorial won't find:
|
||||
let wrapper = wrapDB(firebase.database())
|
||||
wrapper.db.ref("forecast"); // <-- not found
|
||||
|
||||
This is an example of where `data-flow configurations <analyzing-data-flow-in-javascript-and-typescript.html#global-data-flow>`__ are more powerful.
|
||||
This is an example of where `data-flow configurations <https://codeql.github.com/docs/codeql-language-guides/analyzing-data-flow-in-javascript-and-typescript/#global-data-flow>`__ are more powerful.
|
||||
|
||||
When to use type tracking
|
||||
-------------------------
|
||||
@@ -491,7 +491,7 @@ Prefer type tracking when:
|
||||
|
||||
Prefer data-flow configurations when:
|
||||
|
||||
- Tracking user-controlled data -- use `taint tracking <analyzing-data-flow-in-javascript-and-typescript.html#using-global-analyzing-data-flow-and-tracking-tainted-data-in-python>`__.
|
||||
- Tracking user-controlled data -- use `taint tracking <https://codeql.github.com/docs/codeql-language-guides/analyzing-data-flow-in-javascript-and-typescript/#using-global-taint-tracking>`__.
|
||||
- Differentiating between different kinds of user-controlled data -- see ":doc:`Using flow labels for precise data flow analysis <using-flow-labels-for-precise-data-flow-analysis>`."
|
||||
- Tracking transformations of a value through generic utility functions.
|
||||
- Tracking values through string manipulation.
|
||||
@@ -499,7 +499,7 @@ Prefer data-flow configurations when:
|
||||
|
||||
Lastly, depending on the code base being analyzed, some alternatives to consider are:
|
||||
|
||||
- Using `static type information <codeql-library-for-typescript.html.html#static-type-information>`__,
|
||||
- Using `static type information <https://codeql.github.com/docs/codeql-language-guides/codeql-library-for-typescript/#static-type-information>`__,
|
||||
if analyzing TypeScript code.
|
||||
|
||||
- Relying on local data flow.
|
||||
|
||||
@@ -39,6 +39,12 @@ source_encoding = 'utf-8-sig'
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# The default language for syntax highlighting. We need to explicitly set this to "none",
|
||||
# otherwise Sphinx tries to highlight any unlabeled code samples as "python3".
|
||||
# See https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-highlight_language.
|
||||
|
||||
highlight_language = "none"
|
||||
|
||||
# Import the QL Lexer to use for syntax highlighting
|
||||
import os
|
||||
import sys
|
||||
|
||||
@@ -44,7 +44,7 @@ When you write this process in QL, it closely resembles the above structure. Not
|
||||
For more information about the important concepts and syntactic constructs of QL, see the individual reference topics such as ":doc:`Expressions <expressions>`" and ":doc:`Recursion <recursion>`."
|
||||
The explanations and examples help you understand how the language works, and how to write more advanced QL code.
|
||||
|
||||
For formal specifications of the QL language and QLDoc comments, see the ":doc:`QL language specification <ql-language-specification>`" and ":doc:`QLDoc comment specification <qldoc-comment-specification>`."
|
||||
For a formal specification of the QL language, see the ":doc:`QL language specification <ql-language-specification>`."
|
||||
|
||||
QL and object orientation
|
||||
-------------------------
|
||||
@@ -64,9 +64,9 @@ Here are a few prominent conceptual and functional differences between general p
|
||||
Further reading
|
||||
---------------
|
||||
|
||||
`Academic references <https://help.semmle.com/publications.html>`__ also provide an overview of QL and its semantics. Other useful references on database query languages and Datalog:
|
||||
`Academic references <https://codeql.github.com/publications/>`__ also provide an overview of QL and its semantics. Other useful references on database query languages and Datalog:
|
||||
|
||||
- `Database theory: Query languages <http://www.lsv.ens-cachan.fr/~segoufin/Papers/Mypapers/DB-chapter.pdf>`__
|
||||
- `Logic Programming and Databases book - Amazon page <http://www.amazon.co.uk/Programming-Databases-Surveys-Computer-Science/dp/3642839541>`__
|
||||
- `Database theory: Query languages <http://www.lsv.fr/~segoufin/Papers/Mypapers/DB-chapter.pdf>`__
|
||||
- `Logic Programming and Databases book <https://doi.org/10.1007/978-3-642-83952-8>`__
|
||||
- `Foundations of Databases <http://webdam.inria.fr/Alice/>`__
|
||||
- `Datalog <https://en.wikipedia.org/wiki/Datalog>`__
|
||||
|
||||
@@ -33,7 +33,9 @@ to the name that it aliases.
|
||||
Module aliases
|
||||
==============
|
||||
|
||||
Use the following syntax to define an alias for a :ref:`module <modules>`::
|
||||
Use the following syntax to define an alias for a :ref:`module <modules>`:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
module ModAlias = ModuleName;
|
||||
|
||||
@@ -52,14 +54,18 @@ a deprecation warning is displayed.
|
||||
Type aliases
|
||||
============
|
||||
|
||||
Use the following syntax to define an alias for a :ref:`type <types>`::
|
||||
Use the following syntax to define an alias for a :ref:`type <types>`:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
class TypeAlias = TypeName;
|
||||
|
||||
Note that ``class`` is just a keyword. You can define an alias for any type—namely, :ref:`primitive types <primitive-types>`,
|
||||
:ref:`database types <database-types>` and user-defined :ref:`classes <classes>`.
|
||||
|
||||
For example, you can use an alias to abbreviate the name of the primitive type ``boolean`` to ``bool``::
|
||||
For example, you can use an alias to abbreviate the name of the primitive type ``boolean`` to ``bool``:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
class bool = boolean;
|
||||
|
||||
@@ -80,21 +86,27 @@ Or, to use a class ``OneTwo`` defined in a :ref:`module <explicit-modules>` ``M`
|
||||
Predicate aliases
|
||||
=================
|
||||
|
||||
Use the following syntax to define an alias for a :ref:`non-member predicate <non-member-predicates>`::
|
||||
Use the following syntax to define an alias for a :ref:`non-member predicate <non-member-predicates>`:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
predicate PredAlias = PredicateName/Arity;
|
||||
|
||||
This works for predicates :ref:`with <predicates-with-result>` or :ref:`without <predicates-without-result>` result.
|
||||
|
||||
For example, suppose you frequently use the following predicate, which calculates the successor of a positive integer
|
||||
less than ten::
|
||||
less than ten:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
int getSuccessor(int i) {
|
||||
result = i + 1 and
|
||||
i in [1 .. 9]
|
||||
}
|
||||
|
||||
You can use an alias to abbreviate the name to ``succ``::
|
||||
You can use an alias to abbreviate the name to ``succ``:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
predicate succ = getSuccessor/1;
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ Overview of annotations
|
||||
|
||||
This section describes what the different annotations do, and when you can use them.
|
||||
You can also find a summary table in the Annotations section of the
|
||||
`QL language specification <ql-language-specification#annotations-in-java>`_.
|
||||
`QL language specification <https://codeql.github.com/docs/ql-language-reference/ql-language-specification/#annotations>`_.
|
||||
|
||||
.. index:: abstract
|
||||
.. _abstract:
|
||||
@@ -83,7 +83,9 @@ to describe specific configurations. Any non-abstract subtypes must override it
|
||||
indirectly) to describe what sources of data they each track.
|
||||
|
||||
In other words, all non-abstract classes that extend ``Configuration`` must override ``isSource`` in their
|
||||
own body, or they must inherit from another class that overrides ``isSource``::
|
||||
own body, or they must inherit from another class that overrides ``isSource``:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
class ConfigA extends Configuration {
|
||||
...
|
||||
@@ -203,7 +205,8 @@ change this definition. In this case, ``hasName`` should be final:
|
||||
|
||||
**Available for**: |classes|
|
||||
|
||||
.. important::
|
||||
.. pull-quote:: Important
|
||||
|
||||
This annotation is deprecated. Instead of annotating a name with ``library``, put it in a
|
||||
private (or privately imported) module.
|
||||
|
||||
@@ -328,8 +331,10 @@ When you use this annotation, be aware of the following issues:
|
||||
In particular, you can't chain predicate :ref:`calls <calls>` or call predicates on a
|
||||
:ref:`cast <casts>`. You must write them as multiple conjuncts and explicitly order them.
|
||||
|
||||
For example, suppose you have the following definitions::
|
||||
|
||||
For example, suppose you have the following definitions:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
class Small extends int {
|
||||
Small() { this in [1 .. 10] }
|
||||
Small getSucc() { result = this + 1}
|
||||
@@ -343,8 +348,10 @@ When you use this annotation, be aware of the following issues:
|
||||
s.getSucc().getSucc() = 3
|
||||
}
|
||||
|
||||
If you add ``noopt`` pragmas, you must rewrite the predicates. For example::
|
||||
|
||||
If you add ``noopt`` pragmas, you must rewrite the predicates. For example:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
pragma[noopt]
|
||||
predicate p(int i) {
|
||||
exists(Small s | s = i and s.getSucc() = 2)
|
||||
|
||||
@@ -23,7 +23,7 @@ A QL program is evaluated from the bottom up, so a predicate is usually only eva
|
||||
all the predicates it depends on are evaluated.
|
||||
|
||||
The database includes sets of ordered tuples for the `built-in predicates
|
||||
<ql-language-specification#built-ins>`_ and :ref:`external predicates <external>`.
|
||||
<https://codeql.github.com/docs/ql-language-reference/ql-language-specification/#built-ins>`_ and :ref:`external predicates <external>`.
|
||||
Each evaluation starts from these sets of tuples.
|
||||
The remaining predicates and types in the program are organized into a number of layers, based
|
||||
on the dependencies between them.
|
||||
@@ -35,7 +35,7 @@ results of the program. The results are sorted according to any ordering directi
|
||||
(``order by``) in the queries.
|
||||
|
||||
For more details about each step of the evaluation process, see the "`QL language specification
|
||||
<ql-language-specification#evaluations-of-ql-programs>`_."
|
||||
<https://codeql.github.com/docs/ql-language-reference/ql-language-specification/#evaluation>`_."
|
||||
|
||||
Validity of programs
|
||||
********************
|
||||
@@ -48,21 +48,27 @@ Here are some common ways that you might define infinite predicates. These all g
|
||||
compilation errors:
|
||||
|
||||
- The following query conceptually selects all values of type ``int``, without restricting them.
|
||||
The QL compiler returns the error ``'i' is not bound to a value``::
|
||||
The QL compiler returns the error ``'i' is not bound to a value``:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
from int i
|
||||
select i
|
||||
|
||||
- The following predicate generates two errors: ``'n' is not bound to a value`` and ``'result' is
|
||||
not bound to a value``::
|
||||
not bound to a value``:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
int timesTwo(int n) {
|
||||
result = n * 2
|
||||
}
|
||||
|
||||
- The following class ``Person`` contains all strings that start with ``"Peter"``. There are
|
||||
infinitely many such strings, so this is another invalid definition. The QL compiler gives the
|
||||
error message ``'this' is not bound to a value``::
|
||||
error message ``'this' is not bound to a value``:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
class Person extends string {
|
||||
Person() {
|
||||
@@ -96,7 +102,9 @@ To do this, you can use the following mechanisms:
|
||||
:ref:`binds <predicate-binding>` all its arguments.
|
||||
Therefore, if you :ref:`call <calls>` a predicate on a variable, the variable becomes bound.
|
||||
|
||||
.. important:: If a predicate uses non-standard binding sets, then it does **not** always bind
|
||||
.. pull-quote:: Important
|
||||
|
||||
If a predicate uses non-standard binding sets, then it does **not** always bind
|
||||
all its arguments. In such a case, whether the predicate call binds a specific argument
|
||||
depends on which other arguments are bound, and what the binding sets say about the
|
||||
argument in question. For more information, see ":ref:`binding-sets`."
|
||||
|
||||
@@ -37,7 +37,7 @@ You can express certain values directly in QL, such as numbers, booleans, and st
|
||||
possibly starting with a minus sign (``-``).
|
||||
For example:
|
||||
|
||||
.. code-block:: ql
|
||||
.. code-block:: ql
|
||||
|
||||
0
|
||||
42
|
||||
@@ -45,7 +45,9 @@ You can express certain values directly in QL, such as numbers, booleans, and st
|
||||
|
||||
- :ref:`Float <float>` literals: These are sequences of decimal digits separated by a dot
|
||||
(``.``), possibly starting with a minus sign (``-``).
|
||||
For example::
|
||||
For example:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
2.0
|
||||
123.456
|
||||
@@ -56,12 +58,12 @@ You can express certain values directly in QL, such as numbers, booleans, and st
|
||||
characters represent themselves, but there are a few characters that you need to "escape"
|
||||
with a backslash. The following are examples of string literals:
|
||||
|
||||
.. code-block:: ql
|
||||
.. code-block:: ql
|
||||
|
||||
"hello"
|
||||
"They said, \"Please escape quotation marks!\""
|
||||
|
||||
See `String literals <ql-language-specification#string-literals-string>`_
|
||||
See `String literals <https://codeql.github.com/docs/ql-language-reference/ql-language-specification/#string-literals-string>`__
|
||||
in the QL language specification for more details.
|
||||
|
||||
Note: there is no "date literal" in QL. Instead, to specify a :ref:`date <date>`, you should
|
||||
@@ -135,7 +137,7 @@ In the following example, the class ``C`` inherits two definitions of the predic
|
||||
``getANumber()``—one from ``A`` and one from ``B``.
|
||||
Instead of overriding both definitions, it uses the definition from ``B``.
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
class A extends int {
|
||||
A() { this = 1 }
|
||||
@@ -213,7 +215,7 @@ The following aggregates are available in QL:
|
||||
For example, the following aggregation returns the number of files that have more than
|
||||
``500`` lines:
|
||||
|
||||
.. code-block:: ql
|
||||
.. code-block:: ql
|
||||
|
||||
count(File f | f.getTotalNumberOfLines() > 500 | f)
|
||||
|
||||
@@ -229,7 +231,7 @@ The following aggregates are available in QL:
|
||||
For example, the following aggregation returns the name of the ``.js`` file (or files) with the
|
||||
largest number of lines:
|
||||
|
||||
.. code-block:: ql
|
||||
.. code-block:: ql
|
||||
|
||||
max(File f | f.getExtension() = "js" | f.getBaseName() order by f.getTotalNumberOfLines())
|
||||
|
||||
@@ -237,7 +239,7 @@ The following aggregates are available in QL:
|
||||
below, that is, the string that comes first in the lexicographic ordering of all the possible
|
||||
values of ``s``. (In this case, it returns ``"De Morgan"``.)
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
min(string s | s = "Tarski" or s = "Dedekind" or s = "De Morgan" | s)
|
||||
|
||||
@@ -249,7 +251,9 @@ The following aggregates are available in QL:
|
||||
returns no values. In other words, it evaluates to the empty set.
|
||||
|
||||
For example, the following aggregation returns the average of the integers ``0``, ``1``,
|
||||
``2``, and ``3``::
|
||||
``2``, and ``3``:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
avg(int i | i = [0 .. 3] | i)
|
||||
|
||||
@@ -260,7 +264,9 @@ The following aggregates are available in QL:
|
||||
If there are no possible assignments to the aggregation variables that satisfy the formula, then the sum is ``0``.
|
||||
|
||||
For example, the following aggregation returns the sum of ``i * j`` for all possible values
|
||||
of ``i`` and ``j``::
|
||||
of ``i`` and ``j``:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
sum(int i, int j | i = [0 .. 2] and j = [3 .. 5] | i * j)
|
||||
|
||||
@@ -274,14 +280,16 @@ The following aggregates are available in QL:
|
||||
For example, the following aggregation returns the string ``"3210"``, that is, the
|
||||
concatenation of the strings ``"0"``, ``"1"``, ``"2"``, and ``"3"`` in descending order:
|
||||
|
||||
.. code-block:: ql
|
||||
.. code-block:: ql
|
||||
|
||||
concat(int i | i = [0 .. 3] | i.toString() order by i desc)
|
||||
|
||||
The ``concat`` aggregate can also take a second expression, separated from the first one by
|
||||
a comma. This second expression is inserted as a separator between each concatenated value.
|
||||
|
||||
For example, the following aggregation returns ``"0|1|2|3"``::
|
||||
For example, the following aggregation returns ``"0|1|2|3"``:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
concat(int i | i = [0 .. 3] | i.toString(), "|")
|
||||
|
||||
@@ -294,7 +302,9 @@ The following aggregates are available in QL:
|
||||
|
||||
For example, the following aggregation returns the value that is ranked 4th out of all the
|
||||
possible values. In this case, ``8`` is the 4th integer in the range from ``5`` through
|
||||
``15``::
|
||||
``15``:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
rank[4](int i | i = [5 .. 15] | i)
|
||||
|
||||
@@ -317,7 +327,9 @@ The following aggregates are available in QL:
|
||||
|
||||
For example, the following query returns the positive integers ``1``, ``2``, ``3``, ``4``, ``5``.
|
||||
For negative integers ``x``, the expressions ``x`` and ``x.abs()`` have different values, so the
|
||||
value for ``y`` in the aggregate expression is not uniquely determined. ::
|
||||
value for ``y`` in the aggregate expression is not uniquely determined.
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
from int x
|
||||
where x in [-5 .. 5] and x != 0
|
||||
@@ -394,48 +406,54 @@ aggregation in a simpler form:
|
||||
then you can omit the ``<variable declarations>`` and ``<formula>`` parts and write it
|
||||
as follows:
|
||||
|
||||
.. code-block:: ql
|
||||
.. code-block:: ql
|
||||
|
||||
<aggregate>(<expression>)
|
||||
|
||||
For example, the following aggregations determine how many times the letter ``l`` occurs in
|
||||
string ``"hello"``. These forms are equivalent::
|
||||
string ``"hello"``. These forms are equivalent:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
count(int i | i = "hello".indexOf("l") | i)
|
||||
count("hello".indexOf("l"))
|
||||
|
||||
#. If there only one aggregation variable, you can omit the ``<expression>`` part instead.
|
||||
#. If there is only one aggregation variable, you can omit the ``<expression>`` part instead.
|
||||
In this case, the expression is considered to be the aggregation variable itself.
|
||||
For example, the following aggregations are equivalent::
|
||||
|
||||
For example, the following aggregations are equivalent:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
avg(int i | i = [0 .. 3] | i)
|
||||
avg(int i | i = [0 .. 3])
|
||||
|
||||
#. As a special case, you can omit the ``<expression>`` part from ``count`` even if there is more
|
||||
than one aggregation variable. In such a case, it counts the number of distinct tuples of
|
||||
aggregation variables that satisfy the formula. In other words, the expression part is
|
||||
considered to be the constant ``1``. For example, the following aggregations are equivalent::
|
||||
|
||||
considered to be the constant ``1``. For example, the following aggregations are equivalent:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
count(int i, int j | i in [1 .. 3] and j in [1 .. 3] | 1)
|
||||
count(int i, int j | i in [1 .. 3] and j in [1 .. 3])
|
||||
|
||||
#. You can omit the ``<formula>`` part, but in that case you should include two vertical bars:
|
||||
|
||||
.. code-block:: ql
|
||||
.. code-block:: ql
|
||||
|
||||
<aggregate>(<variable declarations> | | <expression>)
|
||||
|
||||
This is useful if you don't want to restrict the aggregation variables any further.
|
||||
For example, the following aggregation returns the maximum number of lines across all files:
|
||||
|
||||
.. code-block:: ql
|
||||
.. code-block:: ql
|
||||
|
||||
max(File f | | f.getTotalNumberOfLines())
|
||||
|
||||
#. Finally, you can also omit both the ``<formula>`` and ``<expression>`` parts. For example,
|
||||
the following aggregations are equivalent ways to count the number of files in a database:
|
||||
|
||||
.. code-block:: ql
|
||||
.. code-block:: ql
|
||||
|
||||
count(File f | any() | 1)
|
||||
count(File f | | 1)
|
||||
@@ -552,8 +570,9 @@ The following table lists some examples of different forms of ``any`` expression
|
||||
| ``any(int i | i = [0 .. 3] | i * i)`` | the integers ``0``, ``1``, ``4``, and ``9`` |
|
||||
+------------------------------------------+-------------------------------------------------+
|
||||
|
||||
.. note::
|
||||
There is also a `built-in predicate <ql-language-specification#non-member-built-ins>`_
|
||||
.. pull-quote:: Note
|
||||
|
||||
There is also a `built-in predicate <https://codeql.github.com/docs/ql-language-reference/ql-language-specification/#non-member-built-ins>`_
|
||||
``any()``. This is a predicate that always holds.
|
||||
|
||||
Unary operations
|
||||
@@ -637,7 +656,9 @@ is exactly equivalent to ``((Foo)x)``.
|
||||
Casts are useful if you want to call a :ref:`member predicate <member-predicates>` that is only defined for a more
|
||||
specific type. For example, the following query selects Java
|
||||
`classes <https://codeql.github.com/codeql-standard-libraries/java/semmle/code/java/Type.qll/type.Type$Class.html>`_
|
||||
that have a direct supertype called "List"::
|
||||
that have a direct supertype called "List":
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
import java
|
||||
|
||||
@@ -668,7 +689,9 @@ Unlike other expressions, a don't-care expression does not have a type. In pract
|
||||
means that ``_`` doesn't have any :ref:`member predicates <member-predicates>`, so you can't
|
||||
call ``_.somePredicate()``.
|
||||
|
||||
For example, the following query selects all the characters in the string ``"hello"``::
|
||||
For example, the following query selects all the characters in the string ``"hello"``:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
from string s
|
||||
where s = "hello".charAt(_)
|
||||
|
||||
@@ -37,7 +37,7 @@ Order
|
||||
|
||||
To compare two expressions using one of these order operators, each expression must have a type
|
||||
and those types must be :ref:`compatible <type-compatibility>` and
|
||||
`orderable <ql-language-specification#ordering>`_.
|
||||
`orderable <https://codeql.github.com/docs/ql-language-reference/ql-language-specification/#ordering>`_.
|
||||
|
||||
+--------------------------+--------+
|
||||
| Name | Symbol |
|
||||
@@ -318,7 +318,8 @@ The following query selects files that are not HTML files.
|
||||
where not f.getFileType().isHtml()
|
||||
select f
|
||||
|
||||
.. note::
|
||||
.. pull-quote:: Note
|
||||
|
||||
You should be careful when using ``not`` in a recursive definition, as this could lead to
|
||||
non-monotonic recursion. For more information, ":ref:`non-monotonic-recursion`."
|
||||
|
||||
@@ -381,13 +382,14 @@ disjunction.
|
||||
**Example**
|
||||
|
||||
With the following definition, an integer is in the class ``OneTwoThree`` if it is equal to
|
||||
``1``, ``2``, or ``3``::
|
||||
``1``, ``2``, or ``3``:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
class OneTwoThree extends int {
|
||||
OneTwoThree() {
|
||||
this = 1 or this = 2 or this = 3
|
||||
}
|
||||
...
|
||||
}
|
||||
|
||||
.. index:: implies
|
||||
@@ -418,15 +420,15 @@ The following query selects any ``SmallInt`` that is odd, or a multiple of ``4``
|
||||
.. [#] The difference between ``A != B`` and ``not A = B`` is due to the underlying quantifiers.
|
||||
If you think of ``A`` and ``B`` as sets of values, then ``A != B`` means:
|
||||
|
||||
.. code-block:: ql
|
||||
.. code-block:: ql
|
||||
|
||||
exists( a, b | a in A and b in B | a != b )
|
||||
exists( a, b | a in A and b in B | a != b )
|
||||
|
||||
On the other hand, ``not A = B`` means:
|
||||
|
||||
.. code-block:: ql
|
||||
.. code-block:: ql
|
||||
|
||||
not exists( a, b | a in A and b in B | a = b )
|
||||
|
||||
This is equivalent to ``forall( a, b | a in A and b in B | a != b )``, which is very
|
||||
different from the first formula.
|
||||
different from the first formula.
|
||||
|
||||
@@ -35,8 +35,6 @@ Learn all about QL, the powerful query language that underlies the code scanning
|
||||
|
||||
- :doc:`QL language specification <ql-language-specification>`: A formal specification for the QL language. It provides a comprehensive reference for terminology, syntax, and other technical details about QL.
|
||||
|
||||
- :doc:`QLDoc comment specification <qldoc-comment-specification>`: A formal specification for QLDoc comments.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:hidden:
|
||||
@@ -56,4 +54,3 @@ Learn all about QL, the powerful query language that underlies the code scanning
|
||||
name-resolution
|
||||
evaluation-of-ql-programs
|
||||
ql-language-specification
|
||||
qldoc-comment-specification
|
||||
|
||||
@@ -8,7 +8,7 @@ Lexical syntax
|
||||
The QL syntax includes different kinds of keywords, identifiers, and comments.
|
||||
|
||||
For an overview of the lexical syntax, see "`Lexical syntax
|
||||
<ql-language-specification#lexical-syntax>`_" in the QL language specification.
|
||||
<https://codeql.github.com/docs/ql-language-reference/ql-language-specification/#lexical-syntax>`_" in the QL language specification.
|
||||
|
||||
.. index:: comment, QLDoc
|
||||
.. _comments:
|
||||
@@ -16,12 +16,10 @@ For an overview of the lexical syntax, see "`Lexical syntax
|
||||
Comments
|
||||
********
|
||||
|
||||
All standard one-line and multiline comments, as described in the "`QL language specification
|
||||
<ql-language-specification#comments>`_," are ignored by the QL
|
||||
All standard one-line and multiline comments are ignored by the QL
|
||||
compiler and are only visible in the source code.
|
||||
You can also write another kind of comment, namely **QLDoc comments**. These comments describe
|
||||
QL entities and are displayed as pop-up information in QL editors. For information about QLDoc
|
||||
comments, see the ":doc:`QLDoc comment specification <qldoc-comment-specification>`."
|
||||
QL entities and are displayed as pop-up information in QL editors.
|
||||
|
||||
The following example uses these three different kinds of comments:
|
||||
|
||||
|
||||
@@ -17,7 +17,9 @@ Defining a module
|
||||
|
||||
There are various ways to define modules—here is an example of the simplest way, declaring an
|
||||
:ref:`explicit module <explicit-modules>` named ``Example`` containing
|
||||
a class ``OneTwoThree``::
|
||||
a class ``OneTwoThree``:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
module Example {
|
||||
class OneTwoThree extends int {
|
||||
@@ -27,7 +29,7 @@ a class ``OneTwoThree``::
|
||||
}
|
||||
}
|
||||
|
||||
The name of a module can be any `identifier <ql-language-specification#identifiers>`_
|
||||
The name of a module can be any `identifier <https://codeql.github.com/docs/ql-language-reference/ql-language-specification/#identifiers>`_
|
||||
that starts with an uppercase or lowercase letter.
|
||||
|
||||
``.ql`` or ``.qll`` files also implicitly define modules.
|
||||
@@ -115,7 +117,9 @@ the module name, and then the module body enclosed in braces. It can contain any
|
||||
of the elements listed in ":ref:`module-bodies`" below, apart from select clauses.
|
||||
|
||||
For example, you could add the following QL snippet to the library file **OneTwoThreeLib.qll**
|
||||
defined :ref:`above <library-modules>`::
|
||||
defined :ref:`above <library-modules>`:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
...
|
||||
module M {
|
||||
@@ -182,5 +186,5 @@ for example ``import javascript as js``.
|
||||
The ``<module_expression>`` itself can be a module name, a selection, or a qualified
|
||||
reference. For more information, see ":ref:`name-resolution`."
|
||||
|
||||
For information about how import statements are looked up, see "`Module resolution <ql-language-specification#module-resolution>`__"
|
||||
For information about how import statements are looked up, see "`Module resolution <https://codeql.github.com/docs/ql-language-reference/ql-language-specification/#module-resolution>`__"
|
||||
in the QL language specification.
|
||||
@@ -80,7 +80,7 @@ statement as follows:
|
||||
#. If the compiler can't find the library file using the above two checks, it looks up ``examples/security/MyLibrary.qll``
|
||||
relative to each library path entry.
|
||||
The library path is usually specified using the ``libraryPathDependencies`` of the ``qlpack.yml`` file, though it may also depend on the tools you use to run your query, and whether you have specified any extra settings.
|
||||
For more information, see "`Library path <ql-language-specification#library-path>`__" in the QL language specification.
|
||||
For more information, see "`Library path <https://codeql.github.com/docs/ql-language-reference/ql-language-specification/#library-path>`__" in the QL language specification.
|
||||
|
||||
If the compiler cannot resolve an import statement, then it gives a compilation error.
|
||||
|
||||
@@ -106,7 +106,7 @@ Consider the following :ref:`library module <library-modules>`:
|
||||
|
||||
**CountriesLib.qll**
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
class Countries extends string {
|
||||
Countries() {
|
||||
@@ -129,7 +129,9 @@ Consider the following :ref:`library module <library-modules>`:
|
||||
}
|
||||
|
||||
You could write a query that imports ``CountriesLib`` and then uses ``M::EuropeanCountries``
|
||||
to refer to the class ``EuropeanCountries``::
|
||||
to refer to the class ``EuropeanCountries``:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
import CountriesLib
|
||||
|
||||
@@ -137,7 +139,9 @@ to refer to the class ``EuropeanCountries``::
|
||||
select ec
|
||||
|
||||
Alternatively, you could import the contents of ``M`` directly by using the selection
|
||||
``CountriesLib::M`` in the import statement::
|
||||
``CountriesLib::M`` in the import statement:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
import CountriesLib::M
|
||||
|
||||
@@ -154,7 +158,7 @@ Namespaces
|
||||
**********
|
||||
|
||||
When writing QL, it's useful to understand how namespaces (also known as
|
||||
`environments <ql-language-specification#name-resolution>`_) work.
|
||||
`environments <https://codeql.github.com/docs/ql-language-reference/ql-language-specification/#name-resolution>`_) work.
|
||||
|
||||
As in many other programming languages, a namespace is a mapping from **keys** to
|
||||
**entities**. A key is a kind of identifier, for example a name, and a QL entity is
|
||||
@@ -181,7 +185,7 @@ In particular:
|
||||
- The **global module namespace** is empty.
|
||||
- The **global type namespace** has entries for the :ref:`primitive types <primitive-types>` ``int``, ``float``,
|
||||
``string``, ``boolean``, and ``date``, as well as any :ref:`database types <database-types>` defined in the database schema.
|
||||
- The **global predicate namespace** includes all the `built-in predicates <ql-language-specification#built-ins>`_,
|
||||
- The **global predicate namespace** includes all the `built-in predicates <https://codeql.github.com/docs/ql-language-reference/ql-language-specification/#built-ins>`_,
|
||||
as well as any :ref:`database predicates <database-predicates>`.
|
||||
|
||||
In practice, this means that you can use the built-in types and predicates directly in a QL module (without
|
||||
@@ -246,7 +250,7 @@ were defined in the :ref:`QL tutorials <ql-tutorials>`:
|
||||
|
||||
**Villagers.qll**
|
||||
|
||||
::
|
||||
.. code-block:: ql
|
||||
|
||||
import tutorial
|
||||
|
||||
@@ -297,7 +301,7 @@ The type namespace of ``S`` has entries for:
|
||||
The predicate namespace of ``Villagers`` has entries for:
|
||||
- The predicate ``isBald``, with arity 1.
|
||||
- Any predicates (and their arities) exported by ``tutorial``.
|
||||
- The `built-in predicates <ql-language-specification#built-ins>`_.
|
||||
- The `built-in predicates <https://codeql.github.com/docs/ql-language-reference/ql-language-specification/#built-ins>`_.
|
||||
|
||||
The predicate namespace of ``S`` has entries for:
|
||||
- All the above predicates.
|
||||
|
||||
@@ -8,7 +8,9 @@ Predicates
|
||||
Predicates are used to describe the logical relations that make up a QL program.
|
||||
|
||||
Strictly speaking, a predicate evaluates to a set of tuples. For example, consider the
|
||||
following two predicate definitions::
|
||||
following two predicate definitions:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
predicate isCountry(string country) {
|
||||
country = "Germany"
|
||||
@@ -35,7 +37,7 @@ The `arity <https://en.wikipedia.org/wiki/Arity>`_ of these predicates is one an
|
||||
In general, all tuples in a predicate have the same number of elements. The **arity** of
|
||||
a predicate is that number of elements, not including a possible ``result`` variable. For more information, see ":ref:`predicates-with-result`."
|
||||
|
||||
There are a number of `built-in predicates <ql-language-specification#built-ins>`_
|
||||
There are a number of `built-in predicates <https://codeql.github.com/docs/ql-language-reference/ql-language-specification/#built-ins>`_
|
||||
in QL. You can use these in any queries without needing to :ref:`import <importing-modules>`
|
||||
any additional modules. In addition to these built-in predicates, you can also define your
|
||||
own:
|
||||
@@ -49,13 +51,14 @@ When defining a predicate, you should specify:
|
||||
|
||||
#. The keyword ``predicate`` (for a :ref:`predicate without result <predicates-without-result>`),
|
||||
or the type of the result (for a :ref:`predicate with result <predicates-with-result>`).
|
||||
#. The name of the predicate. This is an `identifier <ql-language-specification#identifiers>`_
|
||||
#. The name of the predicate. This is an `identifier <https://codeql.github.com/docs/ql-language-reference/ql-language-specification/#identifiers>`_
|
||||
starting with a lowercase letter.
|
||||
#. The arguments to the predicate, if any, separated by commas. For each argument, specify the
|
||||
argument type and an identifier for the argument variable.
|
||||
#. The predicate body itself. This is a logical formula enclosed in braces.
|
||||
|
||||
.. note::
|
||||
.. pull-quote:: Note
|
||||
|
||||
An :ref:`abstract` or :ref:`external` predicate has no body. To define such a predicate,
|
||||
end the predicate definition with a semicolon (``;``) instead.
|
||||
|
||||
@@ -142,7 +145,9 @@ on itself.
|
||||
For example, you could use recursion to refine the above example. As it stands, the relation
|
||||
defined in ``getANeighbor`` is not symmetric—it does not capture the fact that if x is a
|
||||
neighbor of y, then y is a neighbor of x. A simple way to capture this is to call this
|
||||
predicate recursively, as shown below::
|
||||
predicate recursively, as shown below:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
string getANeighbor(string country) {
|
||||
country = "France" and result = "Belgium"
|
||||
|
||||
@@ -7,10 +7,6 @@ QL language specification
|
||||
|
||||
This is a formal specification for the QL language. It provides a comprehensive reference for terminology, syntax, and other technical details about QL.
|
||||
|
||||
.. This ``highlight`` directive prevents code blocks in this file being highlighted as QL (the default language for this Sphinx project).
|
||||
|
||||
.. highlight:: none
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
@@ -390,27 +386,30 @@ The store
|
||||
|
||||
QL programs evaluate in the context of a *store*. This section specifies several definitions related to the store.
|
||||
|
||||
A *fact* is a predicate or type along with an ordered tuple. A fact is written as the predicate name or type name followed immediately by the tuple. Here are some examples of facts:
|
||||
A *fact* is a predicate or type along with a named tuple. A fact is written as the predicate name or type name followed immediately by the tuple. Here are some examples of facts:
|
||||
|
||||
::
|
||||
|
||||
successor(0, 1)
|
||||
Tree.toString(@method_tree(12), "def println")
|
||||
Location.class(@location(43))
|
||||
Location.getURL(@location(43), "file:///etc/hosts:2:0:2:12")
|
||||
successor(fst: 0, snd:1)
|
||||
Tree.toString(this:@method_tree(12), result:"def println")
|
||||
Location.class(this:@location(43))
|
||||
Location.getURL(this: @location(43), result:"file:///etc/hosts:2:0:2:12")
|
||||
|
||||
A *store* is a mutable set of facts. The store can be mutated by adding more facts to it.
|
||||
|
||||
An ordered tuple *directly satisfies* a predicate or type with a given if there is a fact in the store with the given tuple and predicate or type.
|
||||
An named tuple *directly satisfies* a predicate or type with a given tuple if there is a fact in the store with the given tuple and predicate or type.
|
||||
|
||||
A value ``v`` is in a type ``t`` under any of the following conditions:
|
||||
|
||||
- The type of ``v`` is ``t`` and ``t`` is a primitive type.
|
||||
- The tuple ``(v)`` directly satisfies ``t``.
|
||||
- There is a tuple with ``this`` component ``v`` that directly satisfies ``t``.
|
||||
|
||||
An ordered tuple *satisfies a predicate* ``p`` under the following circumstances. If ``p`` is not a member predicate, then the tuple satisfies the predicate whenever it directly satisfies the predicate.
|
||||
An ordered tuple ``v`` *directly satisfies* a predicate with a given tuple if there is a fact in the store with the given predicate and a named tuple ``v'``
|
||||
such that taking the ordered tuple formed by the ``this`` component of ``v'`` followed by the component for each argument equals the ordered tuple.
|
||||
|
||||
Otherwise, the tuple must be the tuple of a fact in the store with predicate ``q``, where ``q`` shares a root definition with ``p``. The first element of the tuple must be in the type before the dot in ``q``, and there must be no other predicate that overrides ``q`` such that this is true (see "`Classes <#classes>`__" for details on overriding and root definitions).
|
||||
An ordered tuple *satisfies a predicate* ``p`` under the following circumstances. If ``p`` is not a member predicate, then the tuple satisfies the predicate whenever the named tuple satisfies the tuple.
|
||||
|
||||
Otherwise, the tuple must be the tuple of a fact in the store with predicate ``q``, where ``q`` shares a root definition with ``p``. The `first` element of the tuple must be in the type before the dot in ``q``, and there must be no other predicate that overrides ``q`` such that this is true (see "`Classes <#classes>`__" for details on overriding and root definitions).
|
||||
|
||||
An ordered tuple ``(a0, an)`` satisfies the ``+`` closure of a predicate if there is a sequence of binary tuples ``(a0, a1)``, ``(a1, a2)``, ..., ``(an-1, an)`` that all satisfy the predicate. An ordered tuple ``(a, b)`` satisfies the ``*`` closure of a predicate if it either satisfies the ``+`` closure, or if ``a`` and ``b`` are the same, and if moreover they are in each argument type of the predicate.
|
||||
|
||||
@@ -446,7 +445,7 @@ A one-line comment is two slash characters (``/``, U+002F) followed by any seque
|
||||
|
||||
// This is a comment
|
||||
|
||||
A multiline comment is a *comment start*, followed by a *comment body*, followed by a *comment end*. A comment start is a slash (``/``, U+002F) followed by an asterisk (``*``, U+002A), and a comment end is an asterisk followed by a slash. A comment body is any sequence of characters that does not include a comment end. Here is an example multiline comment:
|
||||
A multiline comment is a *comment start*, followed by a *comment body*, followed by a *comment end*. A comment start is a slash (``/``, U+002F) followed by an asterisk (``*``, U+002A), and a comment end is an asterisk followed by a slash. A comment body is any sequence of characters that does not include a comment end and does not start with an asterisk. Here is an example multiline comment:
|
||||
|
||||
::
|
||||
|
||||
@@ -456,6 +455,21 @@ A multiline comment is a *comment start*, followed by a *comment body*, followed
|
||||
It had a multiline comment.
|
||||
*/
|
||||
|
||||
QLDoc (qldoc)
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
A QLDoc comment is a *qldoc comment start*, followed by a *qldoc comment body*, followed by a *qldoc comment end*. A comment start is a slash (``/``, U+002F) followed by two asterisks (``*``, U+002A), and a qldoc comment end is an asterisk followed by a slash. A qldoc comment body is any sequence of characters that does not include a comment end. Here is an example QLDoc comment:
|
||||
|
||||
::
|
||||
|
||||
/**
|
||||
It was the best of code.
|
||||
It was the worst of code.
|
||||
It had a qldoc comment.
|
||||
*/
|
||||
|
||||
The "content" of a QLDoc comment is the comment body of the comment, omitting the initial ``/**``, the trailing ``*/``, and the leading whitespace followed by ``*`` on each internal line.
|
||||
|
||||
Keywords
|
||||
~~~~~~~~
|
||||
|
||||
@@ -738,6 +752,48 @@ A predicate may have several different binding sets, which can be stated by usin
|
||||
| ``bindingset`` | | yes | yes | yes | | | | |
|
||||
+----------------+---------+------------+-------------------+-----------------------+---------+--------+---------+---------+
|
||||
|
||||
QLDoc
|
||||
-----
|
||||
|
||||
QLDoc is used for documenting QL entities and bindings. QLDoc that is used as part of the
|
||||
declaration is said to be declared.
|
||||
|
||||
Ambiguous QLDoc
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
If QLDoc can be parsed as part of a file module or as part of the first declaration in the file then
|
||||
it is parsed as part of the first declaration.
|
||||
|
||||
Inheriting QLDoc
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
If no QLDoc is provided then it may be inherited.
|
||||
|
||||
In the case of an alias then it may be inherited from the right-hand side of the alias.
|
||||
|
||||
In the case of a member predicate we collect all member predicates that it overrides with declared QLDoc. If there is a member predicate in that collection that
|
||||
overrides every other member predicate in that collection, then the QLDoc of that member predicate is used as the QLDoc.
|
||||
|
||||
In the case of a field we collect all fields that it overrides with declared QLDoc. If there is a field in that collection that
|
||||
overrides every other field in that collection, then the QLDoc of that field is used as the QLDoc.
|
||||
|
||||
Content
|
||||
~~~~~~~
|
||||
|
||||
The content of a QLDoc comment is interpreted as `CommonMark <https://commonmark.org/>`__, with the following extensions:
|
||||
|
||||
- Automatic interpretation of links and email addresses.
|
||||
- Use of appropriate characters for ellipses, dashes, apostrophes, and quotes.
|
||||
|
||||
The content of a QLDoc comment may contain metadata tags as follows:
|
||||
|
||||
The tag begins with any number of whitespace characters, followed by an ``@`` sign. At this point there may be any number of non-whitespace characters, which form the key of the tag. Then, a single whitespace character which separates the key from the value. The value of the tag is formed by the remainder of the line, and any subsequent lines until another ``@`` tag is seen, or the end of the content is reached. Any sequence of consecutive whitespace characters in the value are replaced by a single space.
|
||||
|
||||
Metadata
|
||||
~~~~~~~~
|
||||
|
||||
If the query file starts with whitespace followed by a QLDoc comment, then the tags from that QLDoc comment form the query metadata.
|
||||
|
||||
Top-level entities
|
||||
------------------
|
||||
|
||||
@@ -750,7 +806,7 @@ A *predicate* is declared as a sequence of annotations, a head, and an optional
|
||||
|
||||
::
|
||||
|
||||
predicate ::= annotations head optbody
|
||||
predicate ::= qldoc? annotations head optbody
|
||||
|
||||
A predicate definition adds a mapping from the predicate name and arity to the predicate declaration to the current module's declared predicate environment.
|
||||
|
||||
@@ -785,7 +841,7 @@ A class definition has the following syntax:
|
||||
|
||||
::
|
||||
|
||||
class ::= annotations "class" classname "extends" type ("," type)* "{" member* "}"
|
||||
class ::= qldoc? annotations "class" classname "extends" type ("," type)* "{" member* "}"
|
||||
|
||||
The identifier following the ``class`` keyword is the name of the class.
|
||||
|
||||
@@ -802,20 +858,20 @@ A valid class may not inherit from a final class, from itself, or from more than
|
||||
Class environments
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
For each of modules, types, predicates, and fields a class *inherits*, *declares*, and *exports* an environment. These are defined as follows (with X denoting the type of entity we are currently considering):
|
||||
For each of member predicates and fields a class *inherits* and *declares*, and *exports* an environment. These are defined as follows (with X denoting the type of entity we are currently considering):
|
||||
|
||||
- The *inherited X environment* of a class is the union of the exported X environments of its base types.
|
||||
- The *inherited X environment* of a class is the union of the exported X environments of types it inherits from, excluding any elements that are ``overridden`` by another element.
|
||||
|
||||
- The *declared X environment* of a class is the multimap of X declarations in the class itself.
|
||||
|
||||
- The *exported X environment* of a class is the overriding union of its declared X environment (excluding ``private`` declaration entries) with its inherited X environment.
|
||||
|
||||
- The *external X environment* of a class is the visible X environment of the enclosing module.
|
||||
|
||||
- The *visible X environment* is the overriding union of the declared X environment and the inherited X environment; overriding unioned with the external X environment.
|
||||
- The *visible X environment* is the overriding union of the declared X environment and the inherited X environment.
|
||||
|
||||
The program is invalid if any of these environments is not definite.
|
||||
|
||||
For each of member predicates and fields a domain type *exports* an environment. This is the union of the exported ``X`` environments of types the class inherits from, excluding any elements that are ``overridden`` by another element.
|
||||
|
||||
Members
|
||||
~~~~~~~
|
||||
|
||||
@@ -824,8 +880,8 @@ Each member of a class is either a *character*, a predicate, or a field:
|
||||
::
|
||||
|
||||
member ::= character | predicate | field
|
||||
character ::= annotations classname "(" ")" "{" formula "}"
|
||||
field ::= annotations var_decl ";"
|
||||
character ::= qldoc? annotations classname "(" ")" "{" formula "}"
|
||||
field ::= qldoc? annotations var_decl ";"
|
||||
|
||||
Characters
|
||||
^^^^^^^^^^
|
||||
@@ -839,7 +895,7 @@ Member predicates
|
||||
|
||||
A predicate that is a member of a class is called a *member predicate*. The name of the predicate is the identifier just before the open parenthesis.
|
||||
|
||||
A member predicate adds a mapping from the predicate name and arity to the predicate declaration in the class's declared predicate environment.
|
||||
A member predicate adds a mapping from the predicate name and arity to the predicate declaration in the class's declared member predicate environment.
|
||||
|
||||
A valid member predicate can be annotated with ``abstract``, ``cached``, ``final``, ``private``, ``deprecated``, and ``override``.
|
||||
|
||||
@@ -861,13 +917,22 @@ A valid class must include a non-private predicate named ``toString`` with no ar
|
||||
|
||||
A valid class may not inherit from two different classes that include a predicate with the same name and number of arguments, unless either one of the predicates overrides the other, or the class defines a predicate that overrides both of them.
|
||||
|
||||
The typing environment for a member predicate or character is the same as if it were a non-member predicate, except that it additionally maps ``this`` to a type. If the member is a character, then the typing environment maps ``this`` to the class domain type of the class. Otherwise, it maps ``this`` to the class type of the class itself.
|
||||
The typing environment for a member predicate or character is the same as if it were a non-member predicate, except that it additionally maps ``this`` to a type and also maps any fields on a class to a type. If the member is a character, then the typing environment maps ``this`` to the class domain type of the class. Otherwise, it maps ``this`` to the class type of the class itself.
|
||||
The typing environment also maps any field to the type of the field.
|
||||
|
||||
Fields
|
||||
^^^^^^
|
||||
|
||||
A field declaration introduces a mapping from the field name to the field declaration in the class's declared field environment.
|
||||
|
||||
A field ``f`` with enclosing class ``C`` *overrides* a field ``f'`` with enclosing class ``D`` when ``f`` is annotated ``override``, ``C`` inherits from ``D``, ``p'`` is visible in ``C``, and both ``p`` and ``p'`` have the same name.
|
||||
|
||||
A valid class may not inherit from two different classes that include a field with the same name, unless either one of the fields overrides the other, or the class defines a field that overrides both of them.
|
||||
|
||||
A valid field must override another field if it is annotated ``override``.
|
||||
|
||||
When field ``f`` overrides field ``g`` the type of ``f`` must be a subtype of the type of ``g``. ``f`` may not be a final field.
|
||||
|
||||
Select clauses
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
@@ -1094,13 +1159,24 @@ The expressions in parentheses are the *arguments* of the call. The expression b
|
||||
|
||||
The type environment for the arguments is the same as for the call.
|
||||
|
||||
A valid call with results must *resolve* to exactly one predicate. The ways a call can resolve are as follows:
|
||||
A valid call with results *resolves* to a set of predicates. The ways a call can resolve are as follows:
|
||||
|
||||
- If the call has no receiver, then it can resolve to a non-member predicate. If the predicate name is a simple identifier, then the predicate is resolved by looking up its name and arity in the visible predicate environment of the enclosing class or module.
|
||||
- If the call has no receiver and the predicate name is a simple identifier, then the predicate is resolved by looking up its name and arity in the visible member-predicate environment of the enclosing class.
|
||||
|
||||
If the predicate name is a selection identifier, then the qualifier is resolved as a module (see "`Module resolution <#module-resolution>`__"). The identifier is then resolved in the exported predicate environment of the qualifier module.
|
||||
- If the call has no receiver and the predicate name is a simple identifier, then the predicate is resolved by looking up its name and arity in the visible predicate environment of the enclosing module.
|
||||
|
||||
- If the call has a super expression as the receiver, then it resolves to a member predicate in a class the enclosing class inherits from. If the super expression is unqualified, then the super-class is the single class that the current class inherits from. If there is not exactly one such class, then the program is invalid. Otherwise the super-class is the class named by the qualifier of the super expression. The predicate is resolved by looking up its name and arity in the exported predicate environment of the super-class. If there is more than one such predicate, then the predicate call is not valid.
|
||||
- If the call has no receiver and the predicate name is a selection identifier, then the qualifier is resolved as a module (see "`Module resolution <#module-resolution>`__"). The identifier is then resolved in the exported predicate environment of the qualifier module.
|
||||
|
||||
- If the call has a super expression as the receiver, then it resolves to a member predicate in a class the enclosing class inherits from. If the super expression is unqualified, then the super-class is the single class that the current class inherits from. If there is not exactly one such class, then the program is invalid. Otherwise the super-class is the class named by the qualifier of the super expression. The predicate is resolved by looking up its name and arity in the exported predicate environment of the super-class.
|
||||
|
||||
- If the type of the receiver is the same as the enclosing class, the predicate is resolved by looking up its name and arity in the visible predicate environment of the class.
|
||||
|
||||
- If the type of the receiver is not the same as the enclosing class, the predicate is resolved by looking up its name and arity in the exported predicate environment of the class or domain type.
|
||||
|
||||
If all the predicates that the call resolves to are declared on a primitive type, we then restrict to the set of predicates where each argument of the call is a subtype of the corresponding predicate argument type.
|
||||
Then we find all predicates ``p`` from this new set such that there is not another predicate ``p'`` where each argument of ``p'`` is a subtype of the corresponding argument in ``p``. We then say the call resolves to this set instead.
|
||||
|
||||
A valid call must only resolve to a single predicate.
|
||||
|
||||
For each argument other than a don't-care expression, the type of the argument must be compatible with the type of the corresponding argument type of the predicate, otherwise the call is invalid.
|
||||
|
||||
@@ -1114,6 +1190,8 @@ If the resolved predicate is built in, then the call may not include a closure.
|
||||
|
||||
- The number 1 if the predicate has a result, otherwise 0.
|
||||
|
||||
If the call includes a closure, then all declared predicate arguments, the enclosing type of the declaration (if it exists), and the result type of the declaration (if it exists) must be compatible. If one of those types is a subtype of ``int``, then all the other arguments must be a subtype of ``int``.
|
||||
|
||||
If the call resolves to a member predicate, then the *receiver values* are as follows. If the call has a receiver, then the receiver values are the values of that receiver. If the call does not have a receiver, then the single receiver value is the value of ``this`` in the contextual named tuple.
|
||||
|
||||
The *tuple prefixes* of a call with results include one value from each of the argument expressions' values, in the same order as the order of the arguments. If the call resolves to a non-member predicate, then those values are exactly the tuple prefixes of the call. If the call instead resolves to a member predicate, then the tuple prefixes additionally include a receiver value, ordered before the argument values.
|
||||
@@ -1139,7 +1217,7 @@ An aggregation can be written in one of two forms:
|
||||
|
||||
aggorderby ::= expr ("asc" | "desc")?
|
||||
|
||||
The expression enclosed in square brackets (``[`` and ``]``, U+005B and U+005D), if present, is called the *rank expression*. It must have type ``int`` in the enclosing environment.
|
||||
The expression enclosed in square brackets (``[`` and ``]``, U+005B and U+005D), if present, is called the *rank expression*. It must have type ``int``.
|
||||
|
||||
The ``as_exprs``, if present, are called the *aggregation expressions*. If an aggregation expression is of the form ``expr as v`` then the expression is said to be *named* v.
|
||||
|
||||
@@ -1269,7 +1347,9 @@ The grammar given in this section is disambiguated first by precedence, and seco
|
||||
- binary ``*`` , ``/`` and ``%``
|
||||
- binary ``+`` and ``-``
|
||||
|
||||
Additionally, whenever a sequence of tokens can be interpreted either as a call to a predicate with result (with specified closure), or as a binary operation with operator ``+`` or ``*``, the syntax is interpreted as a call to a predicate with result.
|
||||
Whenever a sequence of tokens can be interpreted either as a call to a predicate with result (with specified closure), or as a binary operation with operator ``+`` or ``*``, the syntax is interpreted as a call to a predicate with result.
|
||||
|
||||
Whenever a sequence of tokens can be interpreted either as arithmetic with a parenthesized variable or as a prefix cast of a unary operation, the syntax is interpreted as a cast.
|
||||
|
||||
Formulas
|
||||
--------
|
||||
@@ -1476,9 +1556,9 @@ Aliases define new names for existing QL entities.
|
||||
|
||||
::
|
||||
|
||||
alias ::= annotations "predicate" literalId "=" predicateRef "/" int ";"
|
||||
| annotations "class" classname "=" type ";"
|
||||
| annotations "module" modulename "=" moduleId ";"
|
||||
alias ::= qldoc? annotations "predicate" literalId "=" predicateRef "/" int ";"
|
||||
| qldoc? annotations "class" classname "=" type ";"
|
||||
| qldoc? annotations "module" modulename "=" moduleId ";"
|
||||
|
||||
|
||||
An alias introduces a binding from the new name to the entity referred to by the right-hand side in the current module's declared predicate, type, or module environment respectively.
|
||||
@@ -1832,7 +1912,7 @@ Predicates, and types can *depend* and *strictly depend* on each other. Such dep
|
||||
|
||||
- If a predicate contains a variable declaration with negative or zero polarity of a variable whose declared type is a class type ``C``, then the predicate strictly depends on ``C.class``.
|
||||
|
||||
- If a predicate contains an expression whose type is a class type ``C``, then the predicate depends on ``C.class``. If the expression has negative or zero polarity then the dependency is strict.
|
||||
- If a predicate contains an expression whose type is a class type ``C`` which is not a variable reference, then the predicate depends on ``C.class``. If the expression has negative or zero polarity then the dependency is strict.
|
||||
|
||||
- A predicate containing a predicate call depends on the predicate to which the call resolves. If the call has negative or zero polarity then the dependency is strict.
|
||||
|
||||
@@ -1865,29 +1945,53 @@ The store is first initialized with the *database content* of all built-in predi
|
||||
|
||||
Each layer of the stratification is *populated* in order. To populate a layer, each predicate in the layer is repeatedly populated until the store stops changing. The way that a predicate is populated is as follows:
|
||||
|
||||
- To populate a predicate that has a formula as a body, find all named tuples with the variables of the predicate's arguments that match the body formula and the types of the variables. If the predicate has a result, then the matching named tuples should additionally have a value for ``result`` that is in the result type of the predicate. If the predicate is a member predicate, then the tuples should additionally have a value for ``this`` that is of the type assigned to ``this`` by the typing environment. For each such tuple, convert the named tuple to an ordered tuple by sequencing the values of the tuple, starting with ``this`` if present, followed by the predicate's arguments, followed by ``result`` if present. Add each such converted tuple to the predicate in the store.
|
||||
- To populate a predicate that has a formula as a body, find each named tuple ``t`` that has the following properties:
|
||||
|
||||
- The tuple matches the body formula.
|
||||
- The variables should be the predicate's arguments.
|
||||
- If the predicate has a result, then the tuples should additionally have a value for ``result``.
|
||||
- If the predicate is a member predicate or characteristic predicate of a class ``C`` then the tuples should additionally have a value for ``this`` and each visible field on the class.
|
||||
- The values corresponding to the arguments should all be a member of the declared types of the arguments.
|
||||
- The values corresponding to ``result`` should all be a member of the result type.
|
||||
- The values corresponding to the fields should all be a member of the declared types of the fields.
|
||||
- If the predicate is a member predicate of a class ``C`` and not a characteristic predicate, then the tuples should additionally extend some tuple in ``C.class``.
|
||||
- If the predicate is a characteristic predicate of a class ``C``, then there should be a tuple ``t'`` in ``C.extends`` such that for each visible field in ``C``, any field that is equal to or overrides a field in ``t'`` should have the same value in ``t``. ``this`` should also map to the same value in ``t`` and ``t'``.
|
||||
|
||||
For each such tuple remove any components that correspond to fields and add it to the predicate in the store.
|
||||
|
||||
- To populate an abstract predicate, do nothing.
|
||||
|
||||
- The population of predicates with a higher-order body is left only partially specified. A number of tuples are added to the given predicate in the store. The tuples that are added must be fully determined by the QL program and by the state of the store.
|
||||
|
||||
- To populate the type ``C.extends`` for a class ``C``, identify each value ``v`` that has the following properties: It is in all non-class base types of ``C``, and for each class base type ``B`` of ``C`` it is in ``B.B``. For each such ``v``, add ``(v)`` to ``C.extends``.
|
||||
- To populate the type ``C.extends`` for a class ``C``, identify each named tuple that has the following properties:
|
||||
|
||||
- To populate the type ``C.C`` for a class ``C``, if ``C`` has a characteristic predicate, then add all tuples from that predicate to the store. Otherwise add each tuple in ``C.extends`` into the store.
|
||||
- The value of ``this`` is in all non-class base types of ``C``.
|
||||
- The keys of the tuple are ``this`` and the union of the public fields from each base type.
|
||||
- For each class base type ``B`` of ``C`` there is a named tuple with variables from the public fields of ``B`` and ``this`` that the given tuple and some tuple in ``B.B`` both extend.
|
||||
|
||||
For each such tuple add it to ``C.extends``.
|
||||
|
||||
- To populate the type ``C.C`` for a class ``C``, if ``C`` has a characteristic predicate, then add all tuples from that predicate to the store. Otherwise add all tuples ``t`` such that:
|
||||
|
||||
- The variables of ``t`` should be ``this`` and the visible fields of ``C``.
|
||||
- The values corresponding to the fields should all be a member of the declared types of the fields.
|
||||
- If the predicate is a characteristic predicate of a class ``C``, then there should be a tuple ``t'`` in ``C.extends`` such that for each visible field in ``C``, any field that is equal to or overrides a field in ``t'`` should have the same value in ``t``. ``this`` should also map to the same value in ``t`` and ``t'``.
|
||||
|
||||
- To populate the type ``C.class`` for a non-abstract class type ``C``, add each tuple in ``C.C`` to ``C.class``.
|
||||
|
||||
- To populate the type ``C.class`` for an abstract class type ``C``, for each class ``D`` that has ``C`` as a base type add all tuples in ``D.class`` to ``C.class``.
|
||||
- To populate the type ``C.class`` for an abstract class type ``C``, identify each named tuple that has the following properties:
|
||||
- It is a member of ``C.C``.
|
||||
- For each class ``D`` that has ``C`` as a base type then there is a named tuple with variables from the public fields of ``C`` and ``this`` that the given tuple and a tuple in ``D.class`` both extend.
|
||||
|
||||
- To populate a select clause, find all named tuples with the variables declared in the ``from`` clause that match the formula given in the ``where`` clause, if there is one. For each named tuple, convert it to a set of ordered tuples where each element of the ordered tuple is, in the context of the named tuple, a value of one of the corresponding select expressions. Collect all ordered tuples that can be produced from all of the restricted named tuples in this way. Add each such converted tuple to the select clause in the store.
|
||||
|
||||
Query evaluation
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
A query is evaluated as follows:
|
||||
|
||||
#. Identify all named tuples in the predicate targeted by the query.
|
||||
#. Sequence the ordered tuples lexicographically. The first elements of the lexicographic order are the tuple elements specified by the ordering directives of the predicate targeted by the query, if it has any. Each such element is ordered either ascending (``asc``) or descending (``desc``) as specified by the ordering directive, or ascending if the ordering directive does not specify. This lexicographic order is only a partial order, if there are fewer ordering directives than elements of the tuples. An implementation may produce any sequence of the ordered tuples that satisfies this partial order.
|
||||
#. Identify all facts about query predicates.
|
||||
#. If there is a select clause then find all named tuples with the variables declared in the ``from`` clause that match the formula given in the ``where`` clause, if there is one. For each named tuple, convert it to a set of ordered tuples where each element of the ordered tuple is, in the context of the named tuple, a value of one of the corresponding select expressions. Then sequence the ordered tuples lexicographically. The first elements of the lexicographic order are the tuple elements specified by the ordering directives of the predicate targeted by the query, if it has any. Each such element is ordered either ascending (``asc``) or descending (``desc``) as specified by the ordering directive, or ascending if the ordering directive does not specify. This lexicographic order is only a partial order, if there are fewer ordering directives than elements of the tuples. An implementation may produce any sequence of the ordered tuples that satisfies this partial order.
|
||||
#. The result is the facts from the query predicates plus the list of ordered tuples from the select clause if it exists.
|
||||
|
||||
Summary of syntax
|
||||
-----------------
|
||||
@@ -1896,7 +2000,7 @@ The complete grammar for QL is as follows:
|
||||
|
||||
::
|
||||
|
||||
ql ::= moduleBody
|
||||
ql ::= qldoc? moduleBody
|
||||
|
||||
module ::= annotation* "module" modulename "{" moduleBody "}"
|
||||
|
||||
@@ -1919,7 +2023,7 @@ The complete grammar for QL is as follows:
|
||||
|
||||
orderby ::= simpleId ("asc" | "desc")?
|
||||
|
||||
predicate ::= annotations head optbody
|
||||
predicate ::= qldoc? annotations head optbody
|
||||
|
||||
annotations ::= annotation*
|
||||
|
||||
@@ -1946,13 +2050,13 @@ The complete grammar for QL is as follows:
|
||||
| "{" formula "}"
|
||||
| "=" literalId "(" (predicateRef "/" int ("," predicateRef "/" int)*)? ")" "(" (exprs)? ")"
|
||||
|
||||
class ::= annotations "class" classname "extends" type ("," type)* "{" member* "}"
|
||||
class ::= qldoc? annotations "class" classname "extends" type ("," type)* "{" member* "}"
|
||||
|
||||
member ::= character | predicate | field
|
||||
|
||||
character ::= annotations classname "(" ")" "{" formula "}"
|
||||
character ::= qldoc? annotations classname "(" ")" "{" formula "}"
|
||||
|
||||
field ::= annotations var_decl ";"
|
||||
field ::= qldoc? annotations var_decl ";"
|
||||
|
||||
moduleId ::= simpleId | moduleId "::" simpleId
|
||||
|
||||
@@ -1960,9 +2064,9 @@ The complete grammar for QL is as follows:
|
||||
|
||||
exprs ::= expr ("," expr)*
|
||||
|
||||
alias := annotations "predicate" literalId "=" predicateRef "/" int ";"
|
||||
| annotations "class" classname "=" type ";"
|
||||
| annotations "module" modulename "=" moduleId ";"
|
||||
alias := qldoc? annotations "predicate" literalId "=" predicateRef "/" int ";"
|
||||
| qldoc? annotations "class" classname "=" type ";"
|
||||
| qldoc? annotations "module" modulename "=" moduleId ";"
|
||||
|
||||
var_decls ::= var_decl ("," var_decl)*
|
||||
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
:tocdepth: 1
|
||||
|
||||
.. _qldoc-comment-specification:
|
||||
|
||||
QLDoc comment specification
|
||||
===========================
|
||||
|
||||
This document is a formal specification for QLDoc comments.
|
||||
|
||||
About QLDoc comments
|
||||
--------------------
|
||||
|
||||
You can provide documentation for a QL entity by adding a QLDoc comment in the source file. The QLDoc comment is displayed as pop-up information in QL editors, for example when you hover over a predicate name.
|
||||
|
||||
Notation
|
||||
--------
|
||||
|
||||
A 'QLDoc comment' is a valid QL comment that begins with ``/**`` and ends with ``*/``.
|
||||
|
||||
The 'content' of a QLDoc comment is the textual body of the comment, omitting the initial ``/**``, the trailing ``*/``, and the leading whitespace followed by ``*`` on each internal line.
|
||||
|
||||
A QLDoc comment 'precedes' the next QL syntax element after it in the file.
|
||||
|
||||
Association
|
||||
-----------
|
||||
|
||||
A QLDoc comment may be 'associated with' any of the following QL syntax elements:
|
||||
|
||||
- Class declarations
|
||||
- Non-member predicate declarations
|
||||
- Member predicate declarations
|
||||
- Modules
|
||||
|
||||
For class and predicate declarations, the associated QLDoc comment (if any) is the closest preceding QLDoc comment.
|
||||
|
||||
For modules, the associated QLDoc comment (if any) is the QLDoc comment which is the first element in the file, and moreover is not associated with any other QL element.
|
||||
|
||||
Inheritance
|
||||
-----------
|
||||
|
||||
If a member predicate has no directly associated QLDoc and overrides a set of member predicates which all have the same QLDoc, then the member predicate inherits that QLDoc.
|
||||
|
||||
Content
|
||||
-------
|
||||
|
||||
The content of a QLDoc comment is interpreted as standard Markdown, with the following extensions:
|
||||
|
||||
- Fenced code blocks using backticks.
|
||||
- Automatic interpretation of links and email addresses.
|
||||
- Use of appropriate characters for ellipses, dashes, apostrophes, and quotes.
|
||||
|
||||
The content of a QLDoc comment may contain metadata tags as follows:
|
||||
|
||||
The tag begins with any number of whitespace characters, followed by an ``@`` sign. At this point there may be any number of non-whitespace characters, which form the key of the tag. Then, a single whitespace character which separates the key from the value. The value of the tag is formed by the remainder of the line, and any subsequent lines until another ``@`` tag is seen, or the end of the content is reached.
|
||||
@@ -24,7 +24,9 @@ Select clauses
|
||||
**************
|
||||
|
||||
When writing a query module, you can include a **select clause** (usually at the end of the
|
||||
file) of the following form::
|
||||
file) of the following form:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
from /* ... variable declarations ... */
|
||||
where /* ... logical formula ... */
|
||||
@@ -105,7 +107,9 @@ This predicate returns the following results:
|
||||
|
||||
A benefit of writing a query predicate instead of a select clause is that you can call the
|
||||
predicate in other parts of the code too. For example, you can call ``getProduct`` inside
|
||||
the body of a :ref:`class <classes>`::
|
||||
the body of a :ref:`class <classes>`:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
class MultipleOfThree extends int {
|
||||
MultipleOfThree() { this = getProduct(_, _) }
|
||||
|
||||
@@ -29,7 +29,9 @@ Counting from 0 to 100
|
||||
======================
|
||||
|
||||
The following query uses the predicate ``getANumber()`` to list all integers from 0 to 100
|
||||
(inclusive)::
|
||||
(inclusive):
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
int getANumber() {
|
||||
result = 0
|
||||
@@ -46,7 +48,9 @@ Mutual recursion
|
||||
================
|
||||
|
||||
Predicates can be mutually recursive, that is, you can have a cycle of predicates that
|
||||
depend on each other. For example, here is a QL query that counts to 100 using even numbers::
|
||||
depend on each other. For example, here is a QL query that counts to 100 using even numbers:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
int getAnEven() {
|
||||
result = 0
|
||||
@@ -89,7 +93,9 @@ helpful abbreviations:
|
||||
``p``, and so on.
|
||||
|
||||
Using this ``+`` notation is often simpler than defining the recursive predicate explicitly.
|
||||
In this case, an explicit definition could look like this::
|
||||
In this case, an explicit definition could look like this:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
Person getAnAncestor() {
|
||||
result = this.getAParent()
|
||||
@@ -107,7 +113,9 @@ helpful abbreviations:
|
||||
For example, the result of ``p.getAParent*()`` is an ancestor of ``p`` (as above), or ``p``
|
||||
itself.
|
||||
|
||||
In this case, the explicit definition looks like this::
|
||||
In this case, the explicit definition looks like this:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
Person getAnAncestor2() {
|
||||
result = this
|
||||
@@ -176,7 +184,9 @@ According to this definition, the predicate ``isParadox()`` holds precisely when
|
||||
This is impossible, so there is no fixed point solution to the recursion.
|
||||
|
||||
If the recursion appears under an even number of negations, then this isn't a problem.
|
||||
For example, consider the following (slightly macabre) member predicate of class ``Person``::
|
||||
For example, consider the following (slightly macabre) member predicate of class ``Person``:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
predicate isExtinct() {
|
||||
this.isDead() and
|
||||
|
||||
@@ -50,7 +50,7 @@ independent of the database that you are querying.
|
||||
|
||||
|
||||
QL has a range of built-in operations defined on primitive types. These are available by using dispatch on expressions of the appropriate type. For example, ``1.toString()`` is the string representation of the integer constant ``1``. For a full list of built-in operations available in QL, see the
|
||||
section on `built-ins <ql-language-specification#built-ins>`__ in the QL language specification.
|
||||
section on `built-ins <https://codeql.github.com/docs/ql-language-reference/ql-language-specification/#built-ins>`__ in the QL language specification.
|
||||
|
||||
.. index:: class
|
||||
.. _classes:
|
||||
@@ -76,7 +76,7 @@ Defining a class
|
||||
To define a class, you write:
|
||||
|
||||
#. The keyword ``class``.
|
||||
#. The name of the class. This is an `identifier <ql-language-specification#identifiers>`_
|
||||
#. The name of the class. This is an `identifier <https://codeql.github.com/docs/ql-language-reference/ql-language-specification/#identifiers>`_
|
||||
starting with an uppercase letter.
|
||||
#. The types to extend.
|
||||
#. The :ref:`body of the class <class-bodies>`, enclosed in braces.
|
||||
@@ -163,7 +163,9 @@ The expression ``(OneTwoThree)`` is a :ref:`cast <casts>`. It ensures that ``1``
|
||||
``getAString()``.
|
||||
|
||||
Member predicates are especially useful because you can chain them together. For example, you
|
||||
can use ``toUpperCase()``, a built-in function defined for ``string``::
|
||||
can use ``toUpperCase()``, a built-in function defined for ``string``:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
1.(OneTwoThree).getAString().toUpperCase()
|
||||
|
||||
@@ -172,9 +174,7 @@ This call returns ``"ONE, TWO OR THREE: 1"``.
|
||||
.. index:: this
|
||||
.. _this:
|
||||
|
||||
.. note:
|
||||
|
||||
.. code-block:: ql
|
||||
.. pull-quote:: Note
|
||||
|
||||
Characteristic predicates and member predicates often use the variable ``this``.
|
||||
This variable always refers to a member of the class—in this case a value belonging to the
|
||||
@@ -195,7 +195,9 @@ declarations (that is, variable declarations) within its body. You can use these
|
||||
predicate declarations inside the class. Much like the :ref:`variable <this>` ``this``, fields
|
||||
must be constrained in the :ref:`characteristic predicate <characteristic-predicates>`.
|
||||
|
||||
For example::
|
||||
For example:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
class SmallInt extends int {
|
||||
SmallInt() { this = [1 .. 10] }
|
||||
@@ -283,7 +285,9 @@ inherited predicate, and by adding the ``override`` :ref:`annotation <override>`
|
||||
This is useful if you want to refine the predicate to give a more specific result for the
|
||||
values in the subclass.
|
||||
|
||||
For example, extending the class from the :ref:`first example <defining-a-class>`::
|
||||
For example, extending the class from the :ref:`first example <defining-a-class>`:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
class OneTwo extends OneTwoThree {
|
||||
OneTwo() {
|
||||
@@ -298,7 +302,9 @@ For example, extending the class from the :ref:`first example <defining-a-class>
|
||||
The member predicate ``getAString()`` overrides the original definition of ``getAString()``
|
||||
from ``OneTwoThree``.
|
||||
|
||||
Now, consider the following query::
|
||||
Now, consider the following query:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
from OneTwoThree o
|
||||
select o, o.getAString()
|
||||
@@ -318,7 +324,9 @@ look like this:
|
||||
|
||||
In QL, unlike other object-oriented languages, different subtypes of the same types don't need to be
|
||||
disjoint. For example, you could define another subclass of ``OneTwoThree``, which overlaps
|
||||
with ``OneTwo``::
|
||||
with ``OneTwo``:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
class TwoThree extends OneTwoThree {
|
||||
TwoThree() {
|
||||
@@ -366,7 +374,9 @@ value, namely 2.
|
||||
It inherits member predicates from ``OneTwo`` and ``TwoThree``. It also (indirectly) inherits
|
||||
from ``OneTwoThree`` and ``int``.
|
||||
|
||||
.. note:: If a subclass inherits multiple definitions for the same predicate name, then it
|
||||
.. pull-quote:: Note
|
||||
|
||||
If a subclass inherits multiple definitions for the same predicate name, then it
|
||||
must :ref:`override <overriding-member-predicates>` those definitions to avoid ambiguity.
|
||||
:ref:`Super expressions <super>` are often useful in this situation.
|
||||
|
||||
@@ -397,7 +407,9 @@ in the characteristic predicate of a class.
|
||||
Algebraic datatypes
|
||||
*******************
|
||||
|
||||
.. note:: The syntax for algebraic datatypes is considered experimental and is subject to
|
||||
.. pull-quote:: Note
|
||||
|
||||
The syntax for algebraic datatypes is considered experimental and is subject to
|
||||
change. However, they appear in the `standard QL libraries <https://github.com/github/codeql>`_
|
||||
so the following sections should help you understand those examples.
|
||||
|
||||
@@ -425,7 +437,9 @@ It also means that a unique ``NoCall`` value is produced.
|
||||
Defining an algebraic datatype
|
||||
==============================
|
||||
|
||||
To define an algebraic datatype, use the following general syntax::
|
||||
To define an algebraic datatype, use the following general syntax:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
newtype <TypeName> = <branches>
|
||||
|
||||
@@ -435,7 +449,7 @@ The branch definitions have the following form:
|
||||
|
||||
<BranchName>(<arguments>) { <body> }
|
||||
|
||||
- The type name and the branch names must be `identifiers <ql-language-specification#identifiers>`_
|
||||
- The type name and the branch names must be `identifiers <https://codeql.github.com/docs/ql-language-reference/ql-language-specification/#identifiers>`_
|
||||
starting with an uppercase letter. Conventionally, they start with ``T``.
|
||||
- The different branches of an algebraic datatype are separated by ``or``.
|
||||
- The arguments to a branch, if any, are :ref:`variable declarations <variable-declarations>`
|
||||
@@ -474,7 +488,9 @@ In the standard QL language libraries, this is usually done as follows:
|
||||
For example, the following code snippet from the CodeQL data-flow library for C# defines classes
|
||||
for dealing with tainted or untainted values. In this case, it doesn't make sense for
|
||||
``TaintType`` to extend a database type. It is part of the taint analysis, not the underlying
|
||||
program, so it's helpful to extend a new type (namely ``TTaintType``)::
|
||||
program, so it's helpful to extend a new type (namely ``TTaintType``):
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
private newtype TTaintType =
|
||||
TExactValue()
|
||||
@@ -531,7 +547,9 @@ For example, the following construction is legal:
|
||||
}
|
||||
|
||||
However, a similar implementation that restricts ``InitialValueSource`` in a class extension is not valid.
|
||||
If we had implemented ``DefiniteInitialization`` as a class extension instead, it would trigger a type test for ``InitialValueSource``. This results in an illegal recursion ``DefiniteInitialization -> InitialValueSource -> UnknownInitialGarbage -> ¬DefiniteInitialization`` since ``UnknownInitialGarbage`` relies on ``DefiniteInitialization``::
|
||||
If we had implemented ``DefiniteInitialization`` as a class extension instead, it would trigger a type test for ``InitialValueSource``. This results in an illegal recursion ``DefiniteInitialization -> InitialValueSource -> UnknownInitialGarbage -> ¬DefiniteInitialization`` since ``UnknownInitialGarbage`` relies on ``DefiniteInitialization``:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
// THIS WON'T WORK: The implicit type check for InitialValueSource involves an illegal recursion
|
||||
// DefiniteInitialization -> InitialValueSource -> UnknownInitialGarbage -> ¬DefiniteInitialization!
|
||||
|
||||
@@ -23,7 +23,7 @@ Declaring a variable
|
||||
********************
|
||||
|
||||
All variable declarations consist of a :ref:`type <types>` and a name for the variable.
|
||||
The name can be any `identifier <ql-language-specification#identifiers>`_
|
||||
The name can be any `identifier <https://codeql.github.com/docs/ql-language-reference/ql-language-specification/#identifiers>`_
|
||||
that starts with an uppercase or lowercase letter.
|
||||
|
||||
For example, ``int i``, ``SsaDefinitionNode node``, and ``LocalScopeVariable lsv`` declare
|
||||
|
||||
@@ -11,7 +11,7 @@ For details see:
|
||||
language-support.rst
|
||||
framework-support.rst
|
||||
|
||||
For details of the CodeQL libraries, see `CodeQL standard libraries <https://help.semmle.com/QL/ql-libraries.html>`_.
|
||||
For details of the CodeQL libraries, see `CodeQL standard libraries <https://codeql.github.com/codeql-standard-libraries/>`_.
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user