mirror of
https://github.com/github/codeql.git
synced 2026-06-25 06:37:07 +02:00
Compare commits
16 Commits
copilot/up
...
jketema/ko
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f6b3d1eade | ||
|
|
402c0f89bc | ||
|
|
cd23341dab | ||
|
|
ec91865a7f | ||
|
|
f0576046b1 | ||
|
|
32f7c541ae | ||
|
|
1a9bb2416a | ||
|
|
717ff62d70 | ||
|
|
8179bffe64 | ||
|
|
0a41157d77 | ||
|
|
07cf89568f | ||
|
|
42ebe56023 | ||
|
|
0f83586757 | ||
|
|
1b785a8ff6 | ||
|
|
e10743bd08 | ||
|
|
ac5fa629ef |
@@ -248,6 +248,7 @@ use_repo(
|
||||
"kotlin-compiler-2.2.20-Beta2",
|
||||
"kotlin-compiler-2.3.0",
|
||||
"kotlin-compiler-2.3.20",
|
||||
"kotlin-compiler-2.4.0",
|
||||
"kotlin-compiler-embeddable-1.8.0",
|
||||
"kotlin-compiler-embeddable-1.9.0-Beta",
|
||||
"kotlin-compiler-embeddable-1.9.20-Beta",
|
||||
@@ -259,6 +260,7 @@ use_repo(
|
||||
"kotlin-compiler-embeddable-2.2.20-Beta2",
|
||||
"kotlin-compiler-embeddable-2.3.0",
|
||||
"kotlin-compiler-embeddable-2.3.20",
|
||||
"kotlin-compiler-embeddable-2.4.0",
|
||||
"kotlin-stdlib-1.8.0",
|
||||
"kotlin-stdlib-1.9.0-Beta",
|
||||
"kotlin-stdlib-1.9.20-Beta",
|
||||
@@ -270,6 +272,7 @@ use_repo(
|
||||
"kotlin-stdlib-2.2.20-Beta2",
|
||||
"kotlin-stdlib-2.3.0",
|
||||
"kotlin-stdlib-2.3.20",
|
||||
"kotlin-stdlib-2.4.0",
|
||||
)
|
||||
|
||||
go_sdk = use_extension("@rules_go//go:extensions.bzl", "go_sdk")
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -121,13 +121,6 @@ private module Cached {
|
||||
result = getAChildExpr(parent)
|
||||
or
|
||||
result = parent.getAChildStmt()
|
||||
or
|
||||
result =
|
||||
any(TypeMention tm |
|
||||
tm.getTarget() = parent
|
||||
or
|
||||
tm.getParent+().getTarget() = parent
|
||||
)
|
||||
}
|
||||
|
||||
private predicate parent(ControlFlowElement child, ExprOrStmtParent parent) {
|
||||
|
||||
@@ -6,7 +6,6 @@ import Generics
|
||||
import Location
|
||||
import Namespace
|
||||
import Property
|
||||
import semmle.code.csharp.controlflow.ControlFlowElement
|
||||
private import Conversion
|
||||
private import semmle.code.csharp.metrics.Coupling
|
||||
private import TypeRef
|
||||
@@ -1287,7 +1286,7 @@ class TupleType extends ValueType, @tuple_type {
|
||||
* A type mention, that is, any mention of a type in a source code file.
|
||||
* For example, `int` is mentioned in `int M() { return 1; }`.
|
||||
*/
|
||||
class TypeMention extends ControlFlowElement, @type_mention {
|
||||
class TypeMention extends @type_mention {
|
||||
Type type;
|
||||
@type_mention_parent parent;
|
||||
|
||||
@@ -1320,13 +1319,13 @@ class TypeMention extends ControlFlowElement, @type_mention {
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
override TypeMention getParent() { result = parent }
|
||||
TypeMention getParent() { result = parent }
|
||||
|
||||
/** Gets a textual representation of this type mention. */
|
||||
override string toString() { result = type.toString() }
|
||||
string toString() { result = type.toString() }
|
||||
|
||||
/** Gets the location of this type mention. */
|
||||
override Location getALocation() { type_mention_location(this, result) }
|
||||
Location getLocation() { type_mention_location(this, result) }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -20,7 +20,7 @@ class ControlFlowElementOrCallable extends ExprOrStmtParent, TControlFlowElement
|
||||
*/
|
||||
class ControlFlowElement extends ControlFlowElementOrCallable, @control_flow_element {
|
||||
/** Gets the enclosing callable of this element, if any. */
|
||||
Callable getEnclosingCallable() { enclosingCallable(this, result) }
|
||||
Callable getEnclosingCallable() { none() }
|
||||
|
||||
/** Gets the assembly that this element was compiled into. */
|
||||
Assembly getAssembly() {
|
||||
|
||||
@@ -219,7 +219,7 @@ overlayChangedFiles(
|
||||
/** ELEMENTS **/
|
||||
|
||||
@element = @declaration | @stmt | @expr | @modifier | @attribute | @namespace_declaration
|
||||
| @using_directive | @type_parameter_constraints | @type_mention | @externalDataElement
|
||||
| @using_directive | @type_parameter_constraints | @externalDataElement
|
||||
| @xmllocatable | @asp_element | @namespace | @preprocessor_directive;
|
||||
|
||||
@declaration = @callable | @generic | @assignable | @namespace;
|
||||
@@ -1369,7 +1369,7 @@ compiler_generated(unique int id: @element ref);
|
||||
|
||||
/** CONTROL/DATA FLOW **/
|
||||
|
||||
@control_flow_element = @stmt | @expr | @parameter | @type_mention;
|
||||
@control_flow_element = @stmt | @expr | @parameter;
|
||||
|
||||
/* XML Files */
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
Java,"Java 7 to 26 [6]_","javac (OpenJDK and Oracle JDK),
|
||||
|
||||
Eclipse compiler for Java (ECJ) [7]_",``.java``
|
||||
Kotlin,"Kotlin 1.8.0 to 2.3.2\ *x*","kotlinc",``.kt``
|
||||
Kotlin,"Kotlin 1.8.0 to 2.4.0\ *x*","kotlinc",``.kt``
|
||||
JavaScript,ECMAScript 2022 or lower,Not applicable,"``.js``, ``.jsx``, ``.mjs``, ``.es``, ``.es6``, ``.htm``, ``.html``, ``.xhtm``, ``.xhtml``, ``.vue``, ``.hbs``, ``.ejs``, ``.njk``, ``.json``, ``.yaml``, ``.yml``, ``.raml``, ``.xml`` [8]_"
|
||||
Python [9]_,"2.7, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10, 3.11, 3.12, 3.13",Not applicable,``.py``
|
||||
Ruby [10]_,"up to 3.3",Not applicable,"``.rb``, ``.erb``, ``.gemspec``, ``Gemfile``"
|
||||
|
||||
@@ -13,7 +13,7 @@ func logSomething(entry *logrus.Entry) {
|
||||
entry.Traceln(text) // $ logger=text
|
||||
}
|
||||
|
||||
func logrusCalls() {
|
||||
func logrusCalls(selector int) {
|
||||
err := errors.New("Error")
|
||||
var fields logrus.Fields = nil
|
||||
var fn logrus.LogFunction = nil
|
||||
@@ -27,11 +27,15 @@ func logrusCalls() {
|
||||
tmp = logrus.WithFields(fields) // $ logger=fields
|
||||
logSomething(tmp)
|
||||
|
||||
logrus.Error(text) // $ logger=text
|
||||
logrus.Fatalf(fmt, text) // $ logger=fmt logger=text
|
||||
logrus.Panicln(text) // $ logger=text
|
||||
logrus.Infof(fmt, text) // $ logger=fmt logger=text
|
||||
logrus.FatalFn(fn) // $ logger=fn
|
||||
logrus.Error(text) // $ logger=text
|
||||
logrus.Infof(fmt, text) // $ logger=fmt logger=text
|
||||
if selector == 0 {
|
||||
logrus.Fatalf(fmt, text) // $ logger=fmt logger=text
|
||||
} else if selector == 1 {
|
||||
logrus.Panicln(text) // $ logger=text
|
||||
} else if selector == 2 {
|
||||
logrus.FatalFn(fn) // $ logger=fn
|
||||
}
|
||||
|
||||
// components corresponding to the format specifier "%T" are not considered vulnerable
|
||||
logrus.Infof("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
||||
|
||||
@@ -8,6 +8,6 @@ var v []byte
|
||||
|
||||
func main() {
|
||||
glogTest(len(v))
|
||||
stdlib()
|
||||
stdlib(len(v))
|
||||
slogTest()
|
||||
}
|
||||
|
||||
@@ -4,37 +4,69 @@ import (
|
||||
"log"
|
||||
)
|
||||
|
||||
func stdlib() {
|
||||
func stdlib(selector int) {
|
||||
var logger log.Logger
|
||||
logger.SetPrefix("prefix: ")
|
||||
logger.Fatal(text) // $ logger=text
|
||||
logger.Fatalf(fmt, text) // $ logger=fmt logger=text
|
||||
logger.Fatalln(text) // $ logger=text
|
||||
logger.Panic(text) // $ logger=text
|
||||
logger.Panicf(fmt, text) // $ logger=fmt logger=text
|
||||
logger.Panicln(text) // $ logger=text
|
||||
logger.Print(text) // $ logger=text
|
||||
logger.Printf(fmt, text) // $ logger=fmt logger=text
|
||||
logger.Println(text) // $ logger=text
|
||||
switch selector {
|
||||
case 0:
|
||||
logger.Fatal(text) // $ logger=text
|
||||
case 1:
|
||||
logger.Fatalf(fmt, text) // $ logger=fmt logger=text
|
||||
case 2:
|
||||
logger.Fatalln(text) // $ logger=text
|
||||
case 3:
|
||||
logger.Panic(text) // $ logger=text
|
||||
case 4:
|
||||
logger.Panicf(fmt, text) // $ logger=fmt logger=text
|
||||
case 5:
|
||||
logger.Panicln(text) // $ logger=text
|
||||
case 6:
|
||||
logger.Print(text) // $ logger=text
|
||||
case 7:
|
||||
logger.Printf(fmt, text) // $ logger=fmt logger=text
|
||||
case 8:
|
||||
logger.Println(text) // $ logger=text
|
||||
}
|
||||
|
||||
// components corresponding to the format specifier "%T" are not considered vulnerable
|
||||
logger.Fatalf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
||||
logger.Panicf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
||||
logger.Printf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
||||
switch selector {
|
||||
case 9:
|
||||
logger.Fatalf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
||||
case 10:
|
||||
logger.Panicf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
||||
case 11:
|
||||
logger.Printf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
||||
}
|
||||
|
||||
log.SetPrefix("prefix: ")
|
||||
log.Fatal(text) // $ logger=text
|
||||
log.Fatalf(fmt, text) // $ logger=fmt logger=text
|
||||
log.Fatalln(text) // $ logger=text
|
||||
log.Panic(text) // $ logger=text
|
||||
log.Panicf(fmt, text) // $ logger=fmt logger=text
|
||||
log.Panicln(text) // $ logger=text
|
||||
log.Print(text) // $ logger=text
|
||||
log.Printf(fmt, text) // $ logger=fmt logger=text
|
||||
log.Println(text) // $ logger=text
|
||||
switch selector {
|
||||
case 12:
|
||||
log.Fatal(text) // $ logger=text
|
||||
case 13:
|
||||
log.Fatalf(fmt, text) // $ logger=fmt logger=text
|
||||
case 14:
|
||||
log.Fatalln(text) // $ logger=text
|
||||
case 15:
|
||||
log.Panic(text) // $ logger=text
|
||||
case 16:
|
||||
log.Panicf(fmt, text) // $ logger=fmt logger=text
|
||||
case 17:
|
||||
log.Panicln(text) // $ logger=text
|
||||
case 18:
|
||||
log.Print(text) // $ logger=text
|
||||
case 19:
|
||||
log.Printf(fmt, text) // $ logger=fmt logger=text
|
||||
case 20:
|
||||
log.Println(text) // $ logger=text
|
||||
}
|
||||
|
||||
// components corresponding to the format specifier "%T" are not considered vulnerable
|
||||
log.Fatalf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
||||
log.Panicf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
||||
log.Printf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
||||
switch selector {
|
||||
case 21:
|
||||
log.Fatalf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
||||
case 22:
|
||||
log.Panicf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
||||
case 23:
|
||||
log.Printf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,265 @@
|
||||
| DuplicateSwitchCase.go:16:1:16:14 | function declaration | DuplicateSwitchCase.go:0:0:0:0 | exit |
|
||||
| DuplicateSwitchCase.go:16:6:16:9 | skip | DuplicateSwitchCase.go:16:1:16:14 | function declaration |
|
||||
| DuplicateSwitchCase.go:16:13:16:14 | skip | DuplicateSwitchCase.go:16:1:16:14 | exit |
|
||||
| epilogues.go:0:0:0:0 | entry | epilogues.go:3:1:3:12 | skip |
|
||||
| epilogues.go:3:1:3:12 | skip | epilogues.go:8:1:10:1 | skip |
|
||||
| epilogues.go:8:1:10:1 | skip | epilogues.go:12:21:12:23 | skip |
|
||||
| epilogues.go:12:1:14:1 | entry | epilogues.go:12:7:12:7 | argument corresponding to l |
|
||||
| epilogues.go:12:1:14:1 | function declaration | epilogues.go:16:20:16:27 | skip |
|
||||
| epilogues.go:12:7:12:7 | argument corresponding to l | epilogues.go:12:7:12:7 | initialization of l |
|
||||
| epilogues.go:12:7:12:7 | initialization of l | epilogues.go:12:25:12:27 | argument corresponding to msg |
|
||||
| epilogues.go:12:21:12:23 | skip | epilogues.go:12:1:14:1 | function declaration |
|
||||
| epilogues.go:12:25:12:27 | argument corresponding to msg | epilogues.go:12:25:12:27 | initialization of msg |
|
||||
| epilogues.go:12:25:12:27 | initialization of msg | epilogues.go:12:37:12:40 | argument corresponding to code |
|
||||
| epilogues.go:12:37:12:40 | argument corresponding to code | epilogues.go:12:37:12:40 | initialization of code |
|
||||
| epilogues.go:12:37:12:40 | initialization of code | epilogues.go:13:2:13:12 | selection of Println |
|
||||
| epilogues.go:13:2:13:12 | selection of Println | epilogues.go:13:14:13:14 | l |
|
||||
| epilogues.go:13:2:13:33 | call to Println | epilogues.go:12:1:14:1 | exit |
|
||||
| epilogues.go:13:14:13:14 | implicit dereference | epilogues.go:12:1:14:1 | exit |
|
||||
| epilogues.go:13:14:13:14 | implicit dereference | epilogues.go:13:14:13:21 | selection of prefix |
|
||||
| epilogues.go:13:14:13:14 | l | epilogues.go:13:14:13:14 | implicit dereference |
|
||||
| epilogues.go:13:14:13:21 | selection of prefix | epilogues.go:13:24:13:26 | msg |
|
||||
| epilogues.go:13:24:13:26 | msg | epilogues.go:13:29:13:32 | code |
|
||||
| epilogues.go:13:29:13:32 | code | epilogues.go:13:2:13:33 | call to Println |
|
||||
| epilogues.go:16:1:18:1 | entry | epilogues.go:16:7:16:7 | argument corresponding to l |
|
||||
| epilogues.go:16:1:18:1 | function declaration | epilogues.go:23:6:23:15 | skip |
|
||||
| epilogues.go:16:7:16:7 | argument corresponding to l | epilogues.go:16:7:16:7 | initialization of l |
|
||||
| epilogues.go:16:7:16:7 | initialization of l | epilogues.go:16:29:16:31 | argument corresponding to msg |
|
||||
| epilogues.go:16:20:16:27 | skip | epilogues.go:16:1:18:1 | function declaration |
|
||||
| epilogues.go:16:29:16:31 | argument corresponding to msg | epilogues.go:16:29:16:31 | initialization of msg |
|
||||
| epilogues.go:16:29:16:31 | initialization of msg | epilogues.go:17:2:17:12 | selection of Println |
|
||||
| epilogues.go:17:2:17:12 | selection of Println | epilogues.go:17:14:17:14 | l |
|
||||
| epilogues.go:17:2:17:27 | call to Println | epilogues.go:16:1:18:1 | exit |
|
||||
| epilogues.go:17:14:17:14 | l | epilogues.go:17:14:17:21 | selection of prefix |
|
||||
| epilogues.go:17:14:17:21 | selection of prefix | epilogues.go:17:24:17:26 | msg |
|
||||
| epilogues.go:17:24:17:26 | msg | epilogues.go:17:2:17:27 | call to Println |
|
||||
| epilogues.go:23:1:27:1 | entry | epilogues.go:24:5:24:5 | skip |
|
||||
| epilogues.go:23:1:27:1 | function declaration | epilogues.go:31:6:31:13 | skip |
|
||||
| epilogues.go:23:6:23:15 | skip | epilogues.go:23:1:27:1 | function declaration |
|
||||
| epilogues.go:24:5:24:5 | assignment to r | epilogues.go:24:21:24:21 | r |
|
||||
| epilogues.go:24:5:24:5 | skip | epilogues.go:24:10:24:16 | recover |
|
||||
| epilogues.go:24:10:24:16 | recover | epilogues.go:24:10:24:18 | call to recover |
|
||||
| epilogues.go:24:10:24:18 | call to recover | epilogues.go:24:5:24:5 | assignment to r |
|
||||
| epilogues.go:24:21:24:21 | r | epilogues.go:24:26:24:28 | nil |
|
||||
| epilogues.go:24:21:24:28 | ...!=... | epilogues.go:23:1:27:1 | exit |
|
||||
| epilogues.go:24:21:24:28 | ...!=... | epilogues.go:24:21:24:28 | ...!=... is false |
|
||||
| epilogues.go:24:21:24:28 | ...!=... | epilogues.go:24:21:24:28 | ...!=... is true |
|
||||
| epilogues.go:24:21:24:28 | ...!=... is false | epilogues.go:23:1:27:1 | exit |
|
||||
| epilogues.go:24:21:24:28 | ...!=... is true | epilogues.go:25:3:25:13 | selection of Println |
|
||||
| epilogues.go:24:26:24:28 | nil | epilogues.go:24:21:24:28 | ...!=... |
|
||||
| epilogues.go:25:3:25:13 | selection of Println | epilogues.go:25:15:25:26 | "recovered:" |
|
||||
| epilogues.go:25:3:25:30 | call to Println | epilogues.go:23:1:27:1 | exit |
|
||||
| epilogues.go:25:15:25:26 | "recovered:" | epilogues.go:25:29:25:29 | r |
|
||||
| epilogues.go:25:29:25:29 | r | epilogues.go:25:3:25:30 | call to Println |
|
||||
| epilogues.go:31:1:33:1 | entry | epilogues.go:31:15:31:15 | argument corresponding to x |
|
||||
| epilogues.go:31:1:33:1 | function declaration | epilogues.go:36:6:36:12 | skip |
|
||||
| epilogues.go:31:6:31:13 | skip | epilogues.go:31:1:33:1 | function declaration |
|
||||
| epilogues.go:31:15:31:15 | argument corresponding to x | epilogues.go:31:15:31:15 | initialization of x |
|
||||
| epilogues.go:31:15:31:15 | initialization of x | epilogues.go:32:9:32:9 | x |
|
||||
| epilogues.go:32:2:32:13 | return statement | epilogues.go:31:1:33:1 | exit |
|
||||
| epilogues.go:32:9:32:9 | x | epilogues.go:32:13:32:13 | 2 |
|
||||
| epilogues.go:32:9:32:13 | ...*... | epilogues.go:32:2:32:13 | return statement |
|
||||
| epilogues.go:32:13:32:13 | 2 | epilogues.go:32:9:32:13 | ...*... |
|
||||
| epilogues.go:36:1:38:1 | entry | epilogues.go:37:2:37:12 | selection of Println |
|
||||
| epilogues.go:36:1:38:1 | function declaration | epilogues.go:42:6:42:18 | skip |
|
||||
| epilogues.go:36:6:36:12 | skip | epilogues.go:36:1:38:1 | function declaration |
|
||||
| epilogues.go:37:2:37:12 | selection of Println | epilogues.go:37:14:37:19 | "void" |
|
||||
| epilogues.go:37:2:37:20 | call to Println | epilogues.go:36:1:38:1 | exit |
|
||||
| epilogues.go:37:14:37:19 | "void" | epilogues.go:37:2:37:20 | call to Println |
|
||||
| epilogues.go:42:1:48:1 | entry | epilogues.go:42:20:42:20 | argument corresponding to x |
|
||||
| epilogues.go:42:1:48:1 | function declaration | epilogues.go:51:6:51:21 | skip |
|
||||
| epilogues.go:42:6:42:18 | skip | epilogues.go:42:1:48:1 | function declaration |
|
||||
| epilogues.go:42:20:42:20 | argument corresponding to x | epilogues.go:42:20:42:20 | initialization of x |
|
||||
| epilogues.go:42:20:42:20 | initialization of x | epilogues.go:42:28:42:33 | zero value for result |
|
||||
| epilogues.go:42:28:42:33 | implicit read of result | epilogues.go:42:40:42:42 | implicit read of err |
|
||||
| epilogues.go:42:28:42:33 | initialization of result | epilogues.go:42:40:42:42 | zero value for err |
|
||||
| epilogues.go:42:28:42:33 | zero value for result | epilogues.go:42:28:42:33 | initialization of result |
|
||||
| epilogues.go:42:40:42:42 | implicit read of err | epilogues.go:42:1:48:1 | exit |
|
||||
| epilogues.go:42:40:42:42 | initialization of err | epilogues.go:43:5:43:5 | x |
|
||||
| epilogues.go:42:40:42:42 | zero value for err | epilogues.go:42:40:42:42 | initialization of err |
|
||||
| epilogues.go:43:5:43:5 | x | epilogues.go:43:9:43:9 | 0 |
|
||||
| epilogues.go:43:5:43:9 | ...<... | epilogues.go:43:5:43:9 | ...<... is false |
|
||||
| epilogues.go:43:5:43:9 | ...<... | epilogues.go:43:5:43:9 | ...<... is true |
|
||||
| epilogues.go:43:5:43:9 | ...<... is false | epilogues.go:47:9:47:9 | x |
|
||||
| epilogues.go:43:5:43:9 | ...<... is true | epilogues.go:44:3:44:8 | skip |
|
||||
| epilogues.go:43:9:43:9 | 0 | epilogues.go:43:5:43:9 | ...<... |
|
||||
| epilogues.go:44:3:44:8 | assignment to result | epilogues.go:45:3:45:8 | return statement |
|
||||
| epilogues.go:44:3:44:8 | skip | epilogues.go:44:13:44:13 | x |
|
||||
| epilogues.go:44:12:44:13 | -... | epilogues.go:44:3:44:8 | assignment to result |
|
||||
| epilogues.go:44:13:44:13 | x | epilogues.go:44:12:44:13 | -... |
|
||||
| epilogues.go:45:3:45:8 | return statement | epilogues.go:42:28:42:33 | implicit read of result |
|
||||
| epilogues.go:47:2:47:14 | return statement | epilogues.go:42:28:42:33 | implicit read of result |
|
||||
| epilogues.go:47:9:47:9 | implicit write of result | epilogues.go:47:12:47:14 | nil |
|
||||
| epilogues.go:47:9:47:9 | x | epilogues.go:47:9:47:9 | implicit write of result |
|
||||
| epilogues.go:47:12:47:14 | implicit write of err | epilogues.go:47:2:47:14 | return statement |
|
||||
| epilogues.go:47:12:47:14 | nil | epilogues.go:47:12:47:14 | implicit write of err |
|
||||
| epilogues.go:51:1:54:1 | entry | epilogues.go:51:23:51:23 | argument corresponding to x |
|
||||
| epilogues.go:51:1:54:1 | function declaration | epilogues.go:59:6:59:25 | skip |
|
||||
| epilogues.go:51:6:51:21 | skip | epilogues.go:51:1:54:1 | function declaration |
|
||||
| epilogues.go:51:23:51:23 | argument corresponding to x | epilogues.go:51:23:51:23 | initialization of x |
|
||||
| epilogues.go:51:23:51:23 | initialization of x | epilogues.go:51:31:51:31 | zero value for n |
|
||||
| epilogues.go:51:31:51:31 | implicit read of n | epilogues.go:51:1:54:1 | exit |
|
||||
| epilogues.go:51:31:51:31 | initialization of n | epilogues.go:52:2:52:2 | skip |
|
||||
| epilogues.go:51:31:51:31 | zero value for n | epilogues.go:51:31:51:31 | initialization of n |
|
||||
| epilogues.go:52:2:52:2 | assignment to n | epilogues.go:53:2:53:7 | return statement |
|
||||
| epilogues.go:52:2:52:2 | skip | epilogues.go:52:6:52:6 | x |
|
||||
| epilogues.go:52:6:52:6 | x | epilogues.go:52:10:52:10 | 1 |
|
||||
| epilogues.go:52:6:52:10 | ...+... | epilogues.go:52:2:52:2 | assignment to n |
|
||||
| epilogues.go:52:10:52:10 | 1 | epilogues.go:52:6:52:10 | ...+... |
|
||||
| epilogues.go:53:2:53:7 | return statement | epilogues.go:51:31:51:31 | implicit read of n |
|
||||
| epilogues.go:59:1:62:1 | entry | epilogues.go:59:27:59:27 | argument corresponding to l |
|
||||
| epilogues.go:59:1:62:1 | function declaration | epilogues.go:66:6:66:26 | skip |
|
||||
| epilogues.go:59:6:59:25 | skip | epilogues.go:59:1:62:1 | function declaration |
|
||||
| epilogues.go:59:27:59:27 | argument corresponding to l | epilogues.go:59:27:59:27 | initialization of l |
|
||||
| epilogues.go:59:27:59:27 | initialization of l | epilogues.go:59:41:59:45 | argument corresponding to items |
|
||||
| epilogues.go:59:41:59:45 | argument corresponding to items | epilogues.go:59:41:59:45 | initialization of items |
|
||||
| epilogues.go:59:41:59:45 | initialization of items | epilogues.go:60:8:60:8 | l |
|
||||
| epilogues.go:60:2:60:33 | defer statement | epilogues.go:61:2:61:12 | selection of Println |
|
||||
| epilogues.go:60:8:60:8 | l | epilogues.go:60:8:60:12 | selection of log |
|
||||
| epilogues.go:60:8:60:12 | selection of log | epilogues.go:60:14:60:20 | "count" |
|
||||
| epilogues.go:60:8:60:33 | call to log | epilogues.go:59:1:62:1 | exit |
|
||||
| epilogues.go:60:14:60:20 | "count" | epilogues.go:60:23:60:25 | len |
|
||||
| epilogues.go:60:23:60:25 | len | epilogues.go:60:27:60:31 | items |
|
||||
| epilogues.go:60:23:60:32 | call to len | epilogues.go:60:2:60:33 | defer statement |
|
||||
| epilogues.go:60:27:60:31 | items | epilogues.go:60:23:60:32 | call to len |
|
||||
| epilogues.go:61:2:61:12 | selection of Println | epilogues.go:61:14:61:25 | "processing" |
|
||||
| epilogues.go:61:2:61:38 | call to Println | epilogues.go:60:8:60:33 | call to log |
|
||||
| epilogues.go:61:14:61:25 | "processing" | epilogues.go:61:28:61:30 | len |
|
||||
| epilogues.go:61:28:61:30 | len | epilogues.go:61:32:61:36 | items |
|
||||
| epilogues.go:61:28:61:37 | call to len | epilogues.go:61:2:61:38 | call to Println |
|
||||
| epilogues.go:61:32:61:36 | items | epilogues.go:61:28:61:37 | call to len |
|
||||
| epilogues.go:66:1:71:1 | entry | epilogues.go:66:28:66:33 | argument corresponding to prefix |
|
||||
| epilogues.go:66:1:71:1 | function declaration | epilogues.go:77:6:77:20 | skip |
|
||||
| epilogues.go:66:6:66:26 | skip | epilogues.go:66:1:71:1 | function declaration |
|
||||
| epilogues.go:66:28:66:33 | argument corresponding to prefix | epilogues.go:66:28:66:33 | initialization of prefix |
|
||||
| epilogues.go:66:28:66:33 | initialization of prefix | epilogues.go:67:2:67:2 | skip |
|
||||
| epilogues.go:67:2:67:2 | assignment to l | epilogues.go:68:8:68:8 | l |
|
||||
| epilogues.go:67:2:67:2 | skip | epilogues.go:67:7:67:31 | struct literal |
|
||||
| epilogues.go:67:7:67:31 | struct literal | epilogues.go:67:25:67:30 | prefix |
|
||||
| epilogues.go:67:17:67:30 | init of key-value pair | epilogues.go:67:2:67:2 | assignment to l |
|
||||
| epilogues.go:67:25:67:30 | prefix | epilogues.go:67:17:67:30 | init of key-value pair |
|
||||
| epilogues.go:68:2:68:24 | defer statement | epilogues.go:69:10:69:10 | l |
|
||||
| epilogues.go:68:8:68:8 | l | epilogues.go:68:8:68:17 | selection of logValue |
|
||||
| epilogues.go:68:8:68:17 | selection of logValue | epilogues.go:68:19:68:23 | "bye" |
|
||||
| epilogues.go:68:8:68:24 | call to logValue | epilogues.go:66:1:71:1 | exit |
|
||||
| epilogues.go:68:19:68:23 | "bye" | epilogues.go:68:2:68:24 | defer statement |
|
||||
| epilogues.go:69:2:69:25 | defer statement | epilogues.go:70:2:70:12 | selection of Println |
|
||||
| epilogues.go:69:8:69:15 | selection of log | epilogues.go:69:17:69:21 | "ptr" |
|
||||
| epilogues.go:69:8:69:25 | call to log | epilogues.go:68:8:68:24 | call to logValue |
|
||||
| epilogues.go:69:9:69:10 | &... | epilogues.go:69:8:69:15 | selection of log |
|
||||
| epilogues.go:69:10:69:10 | l | epilogues.go:69:9:69:10 | &... |
|
||||
| epilogues.go:69:17:69:21 | "ptr" | epilogues.go:69:24:69:24 | 7 |
|
||||
| epilogues.go:69:24:69:24 | 7 | epilogues.go:69:2:69:25 | defer statement |
|
||||
| epilogues.go:70:2:70:12 | selection of Println | epilogues.go:70:14:70:19 | "body" |
|
||||
| epilogues.go:70:2:70:20 | call to Println | epilogues.go:69:8:69:25 | call to log |
|
||||
| epilogues.go:70:14:70:19 | "body" | epilogues.go:70:2:70:20 | call to Println |
|
||||
| epilogues.go:77:1:82:1 | entry | epilogues.go:77:22:77:22 | argument corresponding to x |
|
||||
| epilogues.go:77:1:82:1 | function declaration | epilogues.go:87:6:87:20 | skip |
|
||||
| epilogues.go:77:6:77:20 | skip | epilogues.go:77:1:82:1 | function declaration |
|
||||
| epilogues.go:77:22:77:22 | argument corresponding to x | epilogues.go:77:22:77:22 | initialization of x |
|
||||
| epilogues.go:77:22:77:22 | initialization of x | epilogues.go:78:8:80:2 | function literal |
|
||||
| epilogues.go:78:2:80:15 | defer statement | epilogues.go:81:2:81:12 | selection of Println |
|
||||
| epilogues.go:78:8:80:2 | entry | epilogues.go:78:13:78:17 | argument corresponding to label |
|
||||
| epilogues.go:78:8:80:2 | function literal | epilogues.go:80:4:80:9 | "done" |
|
||||
| epilogues.go:78:8:80:15 | function call | epilogues.go:77:1:82:1 | exit |
|
||||
| epilogues.go:78:13:78:17 | argument corresponding to label | epilogues.go:78:13:78:17 | initialization of label |
|
||||
| epilogues.go:78:13:78:17 | initialization of label | epilogues.go:78:27:78:27 | argument corresponding to n |
|
||||
| epilogues.go:78:27:78:27 | argument corresponding to n | epilogues.go:78:27:78:27 | initialization of n |
|
||||
| epilogues.go:78:27:78:27 | initialization of n | epilogues.go:79:3:79:13 | selection of Println |
|
||||
| epilogues.go:79:3:79:13 | selection of Println | epilogues.go:79:15:79:19 | label |
|
||||
| epilogues.go:79:3:79:23 | call to Println | epilogues.go:78:8:80:2 | exit |
|
||||
| epilogues.go:79:15:79:19 | label | epilogues.go:79:22:79:22 | n |
|
||||
| epilogues.go:79:22:79:22 | n | epilogues.go:79:3:79:23 | call to Println |
|
||||
| epilogues.go:80:4:80:9 | "done" | epilogues.go:80:12:80:12 | x |
|
||||
| epilogues.go:80:12:80:12 | x | epilogues.go:80:14:80:14 | 1 |
|
||||
| epilogues.go:80:12:80:14 | ...+... | epilogues.go:78:2:80:15 | defer statement |
|
||||
| epilogues.go:80:14:80:14 | 1 | epilogues.go:80:12:80:14 | ...+... |
|
||||
| epilogues.go:81:2:81:12 | selection of Println | epilogues.go:81:14:81:19 | "body" |
|
||||
| epilogues.go:81:2:81:23 | call to Println | epilogues.go:78:8:80:15 | function call |
|
||||
| epilogues.go:81:14:81:19 | "body" | epilogues.go:81:22:81:22 | x |
|
||||
| epilogues.go:81:22:81:22 | x | epilogues.go:81:2:81:23 | call to Println |
|
||||
| epilogues.go:87:1:98:1 | entry | epilogues.go:87:22:87:22 | argument corresponding to x |
|
||||
| epilogues.go:87:1:98:1 | function declaration | epilogues.go:102:6:102:24 | skip |
|
||||
| epilogues.go:87:6:87:20 | skip | epilogues.go:87:1:98:1 | function declaration |
|
||||
| epilogues.go:87:22:87:22 | argument corresponding to x | epilogues.go:87:22:87:22 | initialization of x |
|
||||
| epilogues.go:87:22:87:22 | initialization of x | epilogues.go:87:30:87:35 | zero value for result |
|
||||
| epilogues.go:87:30:87:35 | implicit read of result | epilogues.go:87:1:98:1 | exit |
|
||||
| epilogues.go:87:30:87:35 | initialization of result | epilogues.go:88:8:92:2 | function literal |
|
||||
| epilogues.go:87:30:87:35 | zero value for result | epilogues.go:87:30:87:35 | initialization of result |
|
||||
| epilogues.go:88:2:92:4 | defer statement | epilogues.go:93:5:93:5 | x |
|
||||
| epilogues.go:88:8:92:2 | entry | epilogues.go:89:6:89:6 | skip |
|
||||
| epilogues.go:88:8:92:2 | function literal | epilogues.go:88:2:92:4 | defer statement |
|
||||
| epilogues.go:88:8:92:4 | function call | epilogues.go:87:1:98:1 | exit |
|
||||
| epilogues.go:88:8:92:4 | function call | epilogues.go:87:30:87:35 | implicit read of result |
|
||||
| epilogues.go:89:6:89:6 | assignment to r | epilogues.go:89:22:89:22 | r |
|
||||
| epilogues.go:89:6:89:6 | skip | epilogues.go:89:11:89:17 | recover |
|
||||
| epilogues.go:89:11:89:17 | recover | epilogues.go:89:11:89:19 | call to recover |
|
||||
| epilogues.go:89:11:89:19 | call to recover | epilogues.go:89:6:89:6 | assignment to r |
|
||||
| epilogues.go:89:22:89:22 | r | epilogues.go:89:27:89:29 | nil |
|
||||
| epilogues.go:89:22:89:29 | ...!=... | epilogues.go:88:8:92:2 | exit |
|
||||
| epilogues.go:89:22:89:29 | ...!=... | epilogues.go:89:22:89:29 | ...!=... is false |
|
||||
| epilogues.go:89:22:89:29 | ...!=... | epilogues.go:89:22:89:29 | ...!=... is true |
|
||||
| epilogues.go:89:22:89:29 | ...!=... is false | epilogues.go:88:8:92:2 | exit |
|
||||
| epilogues.go:89:22:89:29 | ...!=... is true | epilogues.go:90:4:90:9 | skip |
|
||||
| epilogues.go:89:27:89:29 | nil | epilogues.go:89:22:89:29 | ...!=... |
|
||||
| epilogues.go:90:4:90:9 | assignment to result | epilogues.go:88:8:92:2 | exit |
|
||||
| epilogues.go:90:4:90:9 | skip | epilogues.go:90:13:90:14 | -... |
|
||||
| epilogues.go:90:13:90:14 | -... | epilogues.go:90:4:90:9 | assignment to result |
|
||||
| epilogues.go:93:5:93:5 | x | epilogues.go:93:9:93:9 | 0 |
|
||||
| epilogues.go:93:5:93:9 | ...<... | epilogues.go:93:5:93:9 | ...<... is false |
|
||||
| epilogues.go:93:5:93:9 | ...<... | epilogues.go:93:5:93:9 | ...<... is true |
|
||||
| epilogues.go:93:5:93:9 | ...<... is false | epilogues.go:96:2:96:7 | skip |
|
||||
| epilogues.go:93:5:93:9 | ...<... is true | epilogues.go:94:3:94:7 | panic |
|
||||
| epilogues.go:93:9:93:9 | 0 | epilogues.go:93:5:93:9 | ...<... |
|
||||
| epilogues.go:94:3:94:7 | panic | epilogues.go:94:9:94:13 | "neg" |
|
||||
| epilogues.go:94:3:94:14 | call to panic | epilogues.go:88:8:92:4 | function call |
|
||||
| epilogues.go:94:9:94:13 | "neg" | epilogues.go:94:3:94:14 | call to panic |
|
||||
| epilogues.go:96:2:96:7 | assignment to result | epilogues.go:97:9:97:14 | result |
|
||||
| epilogues.go:96:2:96:7 | skip | epilogues.go:96:11:96:11 | x |
|
||||
| epilogues.go:96:11:96:11 | x | epilogues.go:96:15:96:15 | x |
|
||||
| epilogues.go:96:11:96:15 | ...*... | epilogues.go:96:2:96:7 | assignment to result |
|
||||
| epilogues.go:96:15:96:15 | x | epilogues.go:96:11:96:15 | ...*... |
|
||||
| epilogues.go:97:2:97:14 | return statement | epilogues.go:88:8:92:4 | function call |
|
||||
| epilogues.go:97:9:97:14 | implicit write of result | epilogues.go:97:2:97:14 | return statement |
|
||||
| epilogues.go:97:9:97:14 | result | epilogues.go:97:9:97:14 | implicit write of result |
|
||||
| epilogues.go:102:1:110:1 | entry | epilogues.go:102:26:102:26 | argument corresponding to x |
|
||||
| epilogues.go:102:1:110:1 | function declaration | epilogues.go:115:6:115:22 | skip |
|
||||
| epilogues.go:102:6:102:24 | skip | epilogues.go:102:1:110:1 | function declaration |
|
||||
| epilogues.go:102:26:102:26 | argument corresponding to x | epilogues.go:102:26:102:26 | initialization of x |
|
||||
| epilogues.go:102:26:102:26 | initialization of x | epilogues.go:102:34:102:35 | zero value for ok |
|
||||
| epilogues.go:102:34:102:35 | implicit read of ok | epilogues.go:102:43:102:43 | implicit read of n |
|
||||
| epilogues.go:102:34:102:35 | initialization of ok | epilogues.go:102:43:102:43 | zero value for n |
|
||||
| epilogues.go:102:34:102:35 | zero value for ok | epilogues.go:102:34:102:35 | initialization of ok |
|
||||
| epilogues.go:102:43:102:43 | implicit read of n | epilogues.go:102:1:110:1 | exit |
|
||||
| epilogues.go:102:43:102:43 | initialization of n | epilogues.go:103:8:103:17 | epiRecover |
|
||||
| epilogues.go:102:43:102:43 | zero value for n | epilogues.go:102:43:102:43 | initialization of n |
|
||||
| epilogues.go:103:2:103:19 | defer statement | epilogues.go:104:5:104:5 | x |
|
||||
| epilogues.go:103:8:103:17 | epiRecover | epilogues.go:103:2:103:19 | defer statement |
|
||||
| epilogues.go:103:8:103:19 | call to epiRecover | epilogues.go:102:1:110:1 | exit |
|
||||
| epilogues.go:103:8:103:19 | call to epiRecover | epilogues.go:102:34:102:35 | implicit read of ok |
|
||||
| epilogues.go:104:5:104:5 | x | epilogues.go:104:10:104:10 | 0 |
|
||||
| epilogues.go:104:5:104:10 | ...==... | epilogues.go:104:5:104:10 | ...==... is false |
|
||||
| epilogues.go:104:5:104:10 | ...==... | epilogues.go:104:5:104:10 | ...==... is true |
|
||||
| epilogues.go:104:5:104:10 | ...==... is false | epilogues.go:107:2:107:2 | skip |
|
||||
| epilogues.go:104:5:104:10 | ...==... is true | epilogues.go:105:3:105:8 | return statement |
|
||||
| epilogues.go:104:10:104:10 | 0 | epilogues.go:104:5:104:10 | ...==... |
|
||||
| epilogues.go:105:3:105:8 | return statement | epilogues.go:103:8:103:19 | call to epiRecover |
|
||||
| epilogues.go:107:2:107:2 | assignment to n | epilogues.go:108:2:108:3 | skip |
|
||||
| epilogues.go:107:2:107:2 | skip | epilogues.go:107:6:107:6 | x |
|
||||
| epilogues.go:107:6:107:6 | x | epilogues.go:107:2:107:2 | assignment to n |
|
||||
| epilogues.go:108:2:108:3 | assignment to ok | epilogues.go:109:2:109:7 | return statement |
|
||||
| epilogues.go:108:2:108:3 | skip | epilogues.go:108:7:108:10 | true |
|
||||
| epilogues.go:108:7:108:10 | true | epilogues.go:108:2:108:3 | assignment to ok |
|
||||
| epilogues.go:109:2:109:7 | return statement | epilogues.go:103:8:103:19 | call to epiRecover |
|
||||
| epilogues.go:115:1:118:1 | entry | epilogues.go:116:8:116:17 | epiRecover |
|
||||
| epilogues.go:115:1:118:1 | function declaration | epilogues.go:0:0:0:0 | exit |
|
||||
| epilogues.go:115:6:115:22 | skip | epilogues.go:115:1:118:1 | function declaration |
|
||||
| epilogues.go:116:2:116:19 | defer statement | epilogues.go:117:2:117:6 | panic |
|
||||
| epilogues.go:116:8:116:17 | epiRecover | epilogues.go:116:2:116:19 | defer statement |
|
||||
| epilogues.go:116:8:116:19 | call to epiRecover | epilogues.go:115:1:118:1 | exit |
|
||||
| epilogues.go:117:2:117:6 | panic | epilogues.go:117:8:117:13 | "boom" |
|
||||
| epilogues.go:117:2:117:14 | call to panic | epilogues.go:116:8:116:19 | call to epiRecover |
|
||||
| epilogues.go:117:8:117:13 | "boom" | epilogues.go:117:2:117:14 | call to panic |
|
||||
| equalitytests.go:0:0:0:0 | entry | equalitytests.go:3:1:5:1 | skip |
|
||||
| equalitytests.go:3:1:5:1 | skip | equalitytests.go:7:1:9:1 | skip |
|
||||
| equalitytests.go:7:1:9:1 | skip | equalitytests.go:11:6:11:18 | skip |
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
| epilogues.go:115:6:115:22 | epiRecoverUnnamed | github.com/github/codeql-go/ql/test/library-tests/semmle/go/controlflow/ControlFlowGraph.epiRecoverUnnamed |
|
||||
| file://:0:0:0:0 | Exit | os.Exit |
|
||||
| file://:0:0:0:0 | Fatal | log.Fatal |
|
||||
| file://:0:0:0:0 | Fatal | log.Logger.Fatal |
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
// epiLogger has methods with both pointer and value receivers, used to check
|
||||
// that the receiver and arguments of a deferred call are evaluated at the
|
||||
// `defer` statement rather than in the function epilogue.
|
||||
type epiLogger struct {
|
||||
prefix string
|
||||
}
|
||||
|
||||
func (l *epiLogger) log(msg string, code int) {
|
||||
fmt.Println(l.prefix, msg, code)
|
||||
}
|
||||
|
||||
func (l epiLogger) logValue(msg string) {
|
||||
fmt.Println(l.prefix, msg)
|
||||
}
|
||||
|
||||
// epiRecover recovers from a panic. It is used as a deferred function so we can
|
||||
// check that control flow returns to the result-read nodes and the normal exit
|
||||
// node after recovering.
|
||||
func epiRecover() {
|
||||
if r := recover(); r != nil {
|
||||
fmt.Println("recovered:", r)
|
||||
}
|
||||
}
|
||||
|
||||
// epiPlain has no named result variable and a single `return` with a child
|
||||
// expression.
|
||||
func epiPlain(x int) int {
|
||||
return x * 2
|
||||
}
|
||||
|
||||
// epiVoid has no named result variable and no `return` statement at all.
|
||||
func epiVoid() {
|
||||
fmt.Println("void")
|
||||
}
|
||||
|
||||
// epiNamedMixed has named result variables and a mix of a bare `return` (no
|
||||
// child expressions) and a `return` with child expressions.
|
||||
func epiNamedMixed(x int) (result int, err error) {
|
||||
if x < 0 {
|
||||
result = -x
|
||||
return
|
||||
}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
// epiNamedBareOnly has a named result variable and only a bare `return`.
|
||||
func epiNamedBareOnly(x int) (n int) {
|
||||
n = x + 1
|
||||
return
|
||||
}
|
||||
|
||||
// epiDeferReceiverArgs has a deferred call with a (pointer) receiver and
|
||||
// arguments that are expressions, so we can check the receiver `l` and the
|
||||
// arguments `"count"` and `len(items)` are evaluated at the `defer` statement.
|
||||
func epiDeferReceiverArgs(l *epiLogger, items []int) {
|
||||
defer l.log("count", len(items))
|
||||
fmt.Println("processing", len(items))
|
||||
}
|
||||
|
||||
// epiDeferValueReceiver has deferred calls with a value receiver and an
|
||||
// address-of receiver, both with arguments evaluated at the `defer` statement.
|
||||
func epiDeferValueReceiver(prefix string) {
|
||||
l := epiLogger{prefix: prefix}
|
||||
defer l.logValue("bye")
|
||||
defer (&l).log("ptr", 7)
|
||||
fmt.Println("body")
|
||||
}
|
||||
|
||||
// epiDeferFuncLit has a deferred function literal with parameters, so we can
|
||||
// check that the arguments `"done"` and `x+1` are evaluated at the `defer`
|
||||
// statement and that control flow enters the function literal body when it is
|
||||
// invoked at the function epilogue.
|
||||
func epiDeferFuncLit(x int) {
|
||||
defer func(label string, n int) {
|
||||
fmt.Println(label, n)
|
||||
}("done", x+1)
|
||||
fmt.Println("body", x)
|
||||
}
|
||||
|
||||
// epiRecoverNamed has a named result variable and a deferred closure containing
|
||||
// `recover()`. After recovering on the panic path, control flow should return
|
||||
// to the result-read nodes and the normal exit node.
|
||||
func epiRecoverNamed(x int) (result int) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
result = -1
|
||||
}
|
||||
}()
|
||||
if x < 0 {
|
||||
panic("neg")
|
||||
}
|
||||
result = x * x
|
||||
return result
|
||||
}
|
||||
|
||||
// epiRecoverNamedBare has named result variables, a deferred function
|
||||
// containing `recover()`, and only bare `return` statements.
|
||||
func epiRecoverNamedBare(x int) (ok bool, n int) {
|
||||
defer epiRecover()
|
||||
if x == 0 {
|
||||
return
|
||||
}
|
||||
n = x
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
// epiRecoverUnnamed has no named result variables and a deferred function
|
||||
// containing `recover()`; after recovering, control flow should reach the
|
||||
// normal exit node directly (there are no result-read nodes).
|
||||
func epiRecoverUnnamed() {
|
||||
defer epiRecover()
|
||||
panic("boom")
|
||||
}
|
||||
@@ -53,6 +53,10 @@ _extractor_name_prefix = "%s-%s" % (
|
||||
"embeddable" if _for_embeddable else "standalone",
|
||||
)
|
||||
|
||||
_compiler_plugin_registrar_service_source = "src/main/resources/META-INF/services/org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar"
|
||||
|
||||
_compiler_plugin_registrar_service_target = "META-INF/services/org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar"
|
||||
|
||||
py_binary(
|
||||
name = "generate_dbscheme",
|
||||
srcs = ["generate_dbscheme.py"],
|
||||
@@ -64,8 +68,14 @@ _resources = [
|
||||
r[len("src/main/resources/"):],
|
||||
)
|
||||
for r in glob(["src/main/resources/**"])
|
||||
if r != _compiler_plugin_registrar_service_source
|
||||
]
|
||||
|
||||
_compiler_plugin_registrar_service = (
|
||||
_compiler_plugin_registrar_service_source,
|
||||
_compiler_plugin_registrar_service_target,
|
||||
)
|
||||
|
||||
kt_javac_options(
|
||||
name = "javac-options",
|
||||
release = "8",
|
||||
@@ -91,19 +101,32 @@ kt_javac_options(
|
||||
# * `resource_strip_prefix` is unique per jar, so we must also put other resources under the same version prefix
|
||||
genrule(
|
||||
name = "resources-%s" % v,
|
||||
srcs = [src for src, _ in _resources],
|
||||
srcs = [src for src, _ in _resources] + (
|
||||
[_compiler_plugin_registrar_service[0]] if not version_less(v, "2.4.0") else []
|
||||
),
|
||||
outs = [
|
||||
"%s/com/github/codeql/extractor.name" % v,
|
||||
] + [
|
||||
"%s/%s" % (v, target)
|
||||
for _, target in _resources
|
||||
],
|
||||
] + (
|
||||
["%s/%s" % (
|
||||
v,
|
||||
_compiler_plugin_registrar_service[1],
|
||||
)] if not version_less(v, "2.4.0") else []
|
||||
),
|
||||
cmd = "\n".join([
|
||||
"echo %s-%s > $(RULEDIR)/%s/com/github/codeql/extractor.name" % (_extractor_name_prefix, v, v),
|
||||
] + [
|
||||
"cp $(execpath %s) $(RULEDIR)/%s/%s" % (source, v, target)
|
||||
for source, target in _resources
|
||||
]),
|
||||
] + (
|
||||
["cp $(execpath %s) $(RULEDIR)/%s/%s" % (
|
||||
_compiler_plugin_registrar_service[0],
|
||||
v,
|
||||
_compiler_plugin_registrar_service[1],
|
||||
)] if not version_less(v, "2.4.0") else []
|
||||
)),
|
||||
),
|
||||
kt_jvm_library(
|
||||
name = "%s-%s" % (_extractor_name_prefix, v),
|
||||
|
||||
BIN
java/kotlin-extractor/deps/kotlin-compiler-2.4.0.jar
(Stored with Git LFS)
Normal file
BIN
java/kotlin-extractor/deps/kotlin-compiler-2.4.0.jar
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
java/kotlin-extractor/deps/kotlin-compiler-embeddable-2.4.0.jar
(Stored with Git LFS)
Normal file
BIN
java/kotlin-extractor/deps/kotlin-compiler-embeddable-2.4.0.jar
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
java/kotlin-extractor/deps/kotlin-stdlib-2.4.0.jar
(Stored with Git LFS)
Normal file
BIN
java/kotlin-extractor/deps/kotlin-stdlib-2.4.0.jar
(Stored with Git LFS)
Normal file
Binary file not shown.
@@ -27,7 +27,7 @@ import shutil
|
||||
import io
|
||||
import os
|
||||
|
||||
DEFAULT_VERSION = "2.3.20"
|
||||
DEFAULT_VERSION = "2.4.0"
|
||||
|
||||
|
||||
def options():
|
||||
|
||||
@@ -3,32 +3,21 @@
|
||||
|
||||
package com.github.codeql
|
||||
|
||||
import com.intellij.mock.MockProject
|
||||
import com.intellij.openapi.extensions.LoadingOrder
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
|
||||
class KotlinExtractorComponentRegistrar : Kotlin2ComponentRegistrar() {
|
||||
override fun registerProjectComponents(
|
||||
project: MockProject,
|
||||
configuration: CompilerConfiguration
|
||||
) {
|
||||
override fun doRegisterExtensions(configuration: CompilerConfiguration) {
|
||||
val invocationTrapFile = configuration[KEY_INVOCATION_TRAP_FILE]
|
||||
if (invocationTrapFile == null) {
|
||||
throw Exception("Required argument for TRAP invocation file not given")
|
||||
}
|
||||
// Register with LoadingOrder.LAST to ensure the extractor runs after other
|
||||
// IR generation plugins (like kotlinx.serialization) have generated their code.
|
||||
val extensionPoint = project.extensionArea.getExtensionPoint(IrGenerationExtension.extensionPointName)
|
||||
extensionPoint.registerExtension(
|
||||
registerExtractorExtension(
|
||||
KotlinExtractorExtension(
|
||||
invocationTrapFile,
|
||||
configuration[KEY_CHECK_TRAP_IDENTICAL] ?: false,
|
||||
configuration[KEY_COMPILATION_STARTTIME],
|
||||
configuration[KEY_EXIT_AFTER_EXTRACTION] ?: false
|
||||
),
|
||||
LoadingOrder.LAST,
|
||||
project
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,9 +173,9 @@ open class KotlinFileExtractor(
|
||||
when (d) {
|
||||
is IrFunction ->
|
||||
when (d.name.asString()) {
|
||||
"toString" -> d.valueParameters.isEmpty()
|
||||
"hashCode" -> d.valueParameters.isEmpty()
|
||||
"equals" -> d.valueParameters.singleOrNull()?.type?.isNullableAny() ?: false
|
||||
"toString" -> d.codeQlValueParameters.isEmpty()
|
||||
"hashCode" -> d.codeQlValueParameters.isEmpty()
|
||||
"equals" -> d.codeQlValueParameters.singleOrNull()?.type?.isNullableAny() ?: false
|
||||
else -> false
|
||||
} && isJavaBinaryDeclaration(d)
|
||||
else -> false
|
||||
@@ -721,7 +721,7 @@ open class KotlinFileExtractor(
|
||||
(it.type as? IrSimpleType)?.classFqName?.asString() != "kotlin.Deprecated"
|
||||
} +
|
||||
// Note we lose any arguments to @java.lang.Deprecated that were written in source.
|
||||
IrConstructorCallImpl.fromSymbolOwner(
|
||||
codeQlAnnotationFromSymbolOwner(
|
||||
UNDEFINED_OFFSET,
|
||||
UNDEFINED_OFFSET,
|
||||
jldConstructor.returnType,
|
||||
@@ -781,13 +781,13 @@ open class KotlinFileExtractor(
|
||||
val locId = tw.getLocation(constructorCall)
|
||||
tw.writeHasLocation(id, locId)
|
||||
|
||||
for (i in 0 until constructorCall.valueArgumentsCount) {
|
||||
val param = constructorCall.symbol.owner.valueParameters[i]
|
||||
for (i in 0 until constructorCall.codeQlValueArgumentsCount) {
|
||||
val param = constructorCall.symbol.owner.codeQlValueParameters[i]
|
||||
val prop =
|
||||
constructorCall.symbol.owner.parentAsClass.declarations
|
||||
.filterIsInstance<IrProperty>()
|
||||
.first { it.name == param.name }
|
||||
val v = constructorCall.getValueArgument(i) ?: param.defaultValue?.expression
|
||||
val v = constructorCall.codeQlGetValueArgument(i) ?: param.defaultValue?.expression
|
||||
val getter = prop.getter
|
||||
if (getter == null) {
|
||||
logger.warnElement("Expected annotation property to define a getter", prop)
|
||||
@@ -1115,9 +1115,9 @@ open class KotlinFileExtractor(
|
||||
returnId,
|
||||
0,
|
||||
returnId,
|
||||
f.valueParameters.size,
|
||||
f.codeQlValueParameters.size,
|
||||
{ argParent, idxOffset ->
|
||||
f.valueParameters.forEachIndexed { idx, param ->
|
||||
f.codeQlValueParameters.forEachIndexed { idx, param ->
|
||||
val syntheticParamId = useValueParameter(param, proxyFunctionId)
|
||||
extractVariableAccess(
|
||||
syntheticParamId,
|
||||
@@ -1695,9 +1695,9 @@ open class KotlinFileExtractor(
|
||||
returnId,
|
||||
0,
|
||||
returnId,
|
||||
f.valueParameters.size,
|
||||
f.codeQlValueParameters.size,
|
||||
{ argParentId, idxOffset ->
|
||||
f.valueParameters.mapIndexed { idx, param ->
|
||||
f.codeQlValueParameters.mapIndexed { idx, param ->
|
||||
val syntheticParamId = useValueParameter(param, functionId)
|
||||
extractVariableAccess(
|
||||
syntheticParamId,
|
||||
@@ -1792,7 +1792,7 @@ open class KotlinFileExtractor(
|
||||
extractBody: Boolean,
|
||||
extractMethodAndParameterTypeAccesses: Boolean
|
||||
) {
|
||||
if (f.valueParameters.none { it.defaultValue != null }) return
|
||||
if (f.codeQlValueParameters.none { it.defaultValue != null }) return
|
||||
|
||||
val id = getDefaultsMethodLabel(f)
|
||||
if (id == null) {
|
||||
@@ -1800,7 +1800,7 @@ open class KotlinFileExtractor(
|
||||
return
|
||||
}
|
||||
val locId = getLocation(f, null)
|
||||
val extReceiver = f.extensionReceiverParameter
|
||||
val extReceiver = f.codeQlExtensionReceiverParameter
|
||||
val dispatchReceiver = if (f.shouldExtractAsStatic) null else f.dispatchReceiverParameter
|
||||
val parameterTypes = getDefaultsMethodArgTypes(f)
|
||||
val allParamTypeResults =
|
||||
@@ -1869,7 +1869,7 @@ open class KotlinFileExtractor(
|
||||
tw.writeCompiler_generated(id, CompilerGeneratedKinds.DEFAULT_ARGUMENTS_METHOD.kind)
|
||||
|
||||
if (extractBody) {
|
||||
val nonSyntheticParams = listOfNotNull(dispatchReceiver) + f.valueParameters
|
||||
val nonSyntheticParams = listOfNotNull(dispatchReceiver) + f.codeQlValueParameters
|
||||
// This stack entry represents as if we're extracting the 'real' function `f`, giving
|
||||
// the indices of its non-synthetic parameters
|
||||
// such that when we extract the default expressions below, any reference to f's nth
|
||||
@@ -1895,12 +1895,12 @@ open class KotlinFileExtractor(
|
||||
val realParamsVarId = getValueParameterLabel(id, parameterTypes.size - 2)
|
||||
val intType = pluginContext.irBuiltIns.intType
|
||||
val paramIdxOffset =
|
||||
listOf(dispatchReceiver, f.extensionReceiverParameter).count { it != null }
|
||||
listOf(dispatchReceiver, f.codeQlExtensionReceiverParameter).count { it != null }
|
||||
extractBlockBody(id, locId).also { blockId ->
|
||||
var nextStmt = 0
|
||||
// For each parameter with a default, sub in the default value if the caller
|
||||
// hasn't supplied a value:
|
||||
f.valueParameters.forEachIndexed { paramIdx, param ->
|
||||
f.codeQlValueParameters.forEachIndexed { paramIdx, param ->
|
||||
val defaultVal = param.defaultValue
|
||||
if (defaultVal != null) {
|
||||
extractIfStmt(locId, blockId, nextStmt++, id).also { ifId ->
|
||||
@@ -1975,7 +1975,7 @@ open class KotlinFileExtractor(
|
||||
id
|
||||
)
|
||||
tw.writeHasLocation(thisCallId, locId)
|
||||
f.valueParameters.forEachIndexed { idx, param ->
|
||||
f.codeQlValueParameters.forEachIndexed { idx, param ->
|
||||
extractVariableAccess(
|
||||
tw.getLabelFor<DbParam>(getValueParameterLabel(id, idx)),
|
||||
param.type,
|
||||
@@ -2003,9 +2003,9 @@ open class KotlinFileExtractor(
|
||||
)
|
||||
.also { thisCallId ->
|
||||
val realFnIdxOffset =
|
||||
if (f.extensionReceiverParameter != null) 1 else 0
|
||||
if (f.codeQlExtensionReceiverParameter != null) 1 else 0
|
||||
val paramMappings =
|
||||
f.valueParameters.mapIndexed { idx, param ->
|
||||
f.codeQlValueParameters.mapIndexed { idx, param ->
|
||||
Triple(
|
||||
param.type,
|
||||
idx + paramIdxOffset,
|
||||
@@ -2156,7 +2156,7 @@ open class KotlinFileExtractor(
|
||||
val dispatchReceiver =
|
||||
f.dispatchReceiverParameter?.let { IrGetValueImpl(-1, -1, it.symbol) }
|
||||
val extensionReceiver =
|
||||
f.extensionReceiverParameter?.let { IrGetValueImpl(-1, -1, it.symbol) }
|
||||
f.codeQlExtensionReceiverParameter?.let { IrGetValueImpl(-1, -1, it.symbol) }
|
||||
|
||||
extractExpressionBody(overloadId, realFunctionLocId).also { returnId ->
|
||||
extractsDefaultsCall(
|
||||
@@ -2180,28 +2180,28 @@ open class KotlinFileExtractor(
|
||||
if (!f.hasAnnotation(jvmOverloadsFqName)) {
|
||||
if (
|
||||
f is IrConstructor &&
|
||||
f.valueParameters.isNotEmpty() &&
|
||||
f.valueParameters.all { it.defaultValue != null } &&
|
||||
f.codeQlValueParameters.isNotEmpty() &&
|
||||
f.codeQlValueParameters.all { it.defaultValue != null } &&
|
||||
f.parentClassOrNull?.let {
|
||||
// Don't create a default constructor for an annotation class, or a class
|
||||
// that explicitly declares a no-arg constructor.
|
||||
!it.isAnnotationClass &&
|
||||
it.declarations.none { d ->
|
||||
d is IrConstructor && d.valueParameters.isEmpty()
|
||||
d is IrConstructor && d.codeQlValueParameters.isEmpty()
|
||||
}
|
||||
} == true
|
||||
) {
|
||||
// Per https://kotlinlang.org/docs/classes.html#creating-instances-of-classes, a
|
||||
// single default overload gets created specifically
|
||||
// when we have all default parameters, regardless of `@JvmOverloads`.
|
||||
extractGeneratedOverload(f.valueParameters.map { _ -> null })
|
||||
extractGeneratedOverload(f.codeQlValueParameters.map { _ -> null })
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
val paramList: MutableList<IrValueParameter?> = f.valueParameters.toMutableList()
|
||||
for (n in (f.valueParameters.size - 1) downTo 0) {
|
||||
if (f.valueParameters[n].defaultValue != null) {
|
||||
val paramList: MutableList<IrValueParameter?> = f.codeQlValueParameters.toMutableList()
|
||||
for (n in (f.codeQlValueParameters.size - 1) downTo 0) {
|
||||
if (f.codeQlValueParameters[n].defaultValue != null) {
|
||||
paramList[n] = null // Remove this parameter, to be replaced by a default value
|
||||
extractGeneratedOverload(paramList)
|
||||
}
|
||||
@@ -2327,7 +2327,7 @@ open class KotlinFileExtractor(
|
||||
getClassByFqName(pluginContext, it)?.let { annotationClass ->
|
||||
annotationClass.owner.declarations.firstIsInstanceOrNull<IrConstructor>()?.let {
|
||||
annotationConstructor ->
|
||||
IrConstructorCallImpl.fromSymbolOwner(
|
||||
codeQlAnnotationFromSymbolOwner(
|
||||
UNDEFINED_OFFSET,
|
||||
UNDEFINED_OFFSET,
|
||||
annotationConstructor.returnType,
|
||||
@@ -2388,13 +2388,13 @@ open class KotlinFileExtractor(
|
||||
id
|
||||
}
|
||||
|
||||
val extReceiver = f.extensionReceiverParameter
|
||||
val extReceiver = f.codeQlExtensionReceiverParameter
|
||||
// The following parameter order is correct, because member $default methods (where
|
||||
// the order would be [dispatchParam], [extensionParam], normalParams) are not
|
||||
// extracted here
|
||||
val fParameters =
|
||||
listOfNotNull(extReceiver) +
|
||||
(overriddenAttributes?.valueParameters ?: f.valueParameters)
|
||||
(overriddenAttributes?.valueParameters ?: f.codeQlValueParameters)
|
||||
val paramTypes =
|
||||
fParameters.mapIndexed { i, vp ->
|
||||
extractValueParameter(
|
||||
@@ -3069,14 +3069,14 @@ open class KotlinFileExtractor(
|
||||
logger.errorElement("Unexpected dispatch receiver found", c)
|
||||
}
|
||||
|
||||
if (c.valueArgumentsCount < 1) {
|
||||
if (c.codeQlValueArgumentsCount < 1) {
|
||||
logger.errorElement("No arguments found", c)
|
||||
return
|
||||
}
|
||||
|
||||
extractArgument(id, c, callable, enclosingStmt, 0, "Operand null")
|
||||
|
||||
if (c.valueArgumentsCount > 1) {
|
||||
if (c.codeQlValueArgumentsCount > 1) {
|
||||
logger.errorElement("Extra arguments found", c)
|
||||
}
|
||||
}
|
||||
@@ -3095,21 +3095,21 @@ open class KotlinFileExtractor(
|
||||
logger.errorElement("Unexpected dispatch receiver found", c)
|
||||
}
|
||||
|
||||
if (c.valueArgumentsCount < 1) {
|
||||
if (c.codeQlValueArgumentsCount < 1) {
|
||||
logger.errorElement("No arguments found", c)
|
||||
return
|
||||
}
|
||||
|
||||
extractArgument(id, c, callable, enclosingStmt, 0, "LHS null")
|
||||
|
||||
if (c.valueArgumentsCount < 2) {
|
||||
if (c.codeQlValueArgumentsCount < 2) {
|
||||
logger.errorElement("No RHS found", c)
|
||||
return
|
||||
}
|
||||
|
||||
extractArgument(id, c, callable, enclosingStmt, 1, "RHS null")
|
||||
|
||||
if (c.valueArgumentsCount > 2) {
|
||||
if (c.codeQlValueArgumentsCount > 2) {
|
||||
logger.errorElement("Extra arguments found", c)
|
||||
}
|
||||
}
|
||||
@@ -3122,7 +3122,7 @@ open class KotlinFileExtractor(
|
||||
idx: Int,
|
||||
msg: String
|
||||
) {
|
||||
val op = c.getValueArgument(idx)
|
||||
val op = c.codeQlGetValueArgument(idx)
|
||||
if (op == null) {
|
||||
logger.errorElement(msg, c)
|
||||
} else {
|
||||
@@ -3267,8 +3267,8 @@ open class KotlinFileExtractor(
|
||||
// and which should be replaced by defaults. The final Object parameter is apparently always
|
||||
// null.
|
||||
(listOfNotNull(if (f.shouldExtractAsStatic) null else f.dispatchReceiverParameter?.type) +
|
||||
listOfNotNull(f.extensionReceiverParameter?.type) +
|
||||
f.valueParameters.map { it.type } +
|
||||
listOfNotNull(f.codeQlExtensionReceiverParameter?.type) +
|
||||
f.codeQlValueParameters.map { it.type } +
|
||||
listOf(pluginContext.irBuiltIns.intType, getDefaultsMethodLastArgType(f)))
|
||||
.map { erase(it) }
|
||||
|
||||
@@ -3345,7 +3345,7 @@ open class KotlinFileExtractor(
|
||||
val overriddenCallTarget =
|
||||
(callTarget as? IrSimpleFunction)?.allOverridden(includeSelf = true)?.firstOrNull {
|
||||
it.overriddenSymbols.isEmpty() &&
|
||||
it.valueParameters.any { p -> p.defaultValue != null }
|
||||
it.codeQlValueParameters.any { p -> p.defaultValue != null }
|
||||
} ?: callTarget
|
||||
if (isExternalDeclaration(overriddenCallTarget)) {
|
||||
// Likewise, ensure the overridden target gets extracted.
|
||||
@@ -3419,7 +3419,7 @@ open class KotlinFileExtractor(
|
||||
}
|
||||
|
||||
val valueArgsWithDummies =
|
||||
valueArguments.zip(callTarget.valueParameters).map { (expr, param) ->
|
||||
valueArguments.zip(callTarget.codeQlValueParameters).map { (expr, param) ->
|
||||
expr ?: IrConstImpl.defaultValueForType(0, 0, param.type)
|
||||
}
|
||||
|
||||
@@ -3529,7 +3529,7 @@ open class KotlinFileExtractor(
|
||||
callTarget: IrFunction,
|
||||
valueArguments: List<IrExpression?>
|
||||
): Boolean {
|
||||
val varargParam = callTarget.valueParameters.withIndex().find { it.value.isVararg }
|
||||
val varargParam = callTarget.codeQlValueParameters.withIndex().find { it.value.isVararg }
|
||||
// If the vararg param is the only one not specified, and it has no default value, then we
|
||||
// don't need to call a $default method,
|
||||
// as omitting it already implies passing an empty vararg array.
|
||||
@@ -3805,7 +3805,7 @@ open class KotlinFileExtractor(
|
||||
) =
|
||||
extractCallValueArguments(
|
||||
callId,
|
||||
(0 until call.valueArgumentsCount).map { call.getValueArgument(it) },
|
||||
(0 until call.codeQlValueArgumentsCount).map { call.codeQlGetValueArgument(it) },
|
||||
enclosingStmt,
|
||||
enclosingCallable,
|
||||
idxOffset
|
||||
@@ -3874,7 +3874,7 @@ open class KotlinFileExtractor(
|
||||
(owner.parentClassOrNull?.fqNameWhenAvailable?.asString() == type ||
|
||||
(owner.parent is IrExternalPackageFragment &&
|
||||
getFileClassFqName(owner)?.asString() == type)) &&
|
||||
owner.valueParameters
|
||||
owner.codeQlValueParameters
|
||||
.map { it.type.classFqName?.asString() }
|
||||
.toTypedArray() contentEquals parameterTypes
|
||||
}
|
||||
@@ -3926,8 +3926,8 @@ open class KotlinFileExtractor(
|
||||
val result =
|
||||
javaLangString?.declarations?.findSubType<IrFunction> {
|
||||
it.name.asString() == "valueOf" &&
|
||||
it.valueParameters.size == 1 &&
|
||||
it.valueParameters[0].type == pluginContext.irBuiltIns.anyNType
|
||||
it.codeQlValueParameters.size == 1 &&
|
||||
it.codeQlValueParameters[0].type == pluginContext.irBuiltIns.anyNType
|
||||
}
|
||||
if (result == null) {
|
||||
logger.error("Couldn't find declaration java.lang.String.valueOf(Object)")
|
||||
@@ -3951,7 +3951,7 @@ open class KotlinFileExtractor(
|
||||
val kotlinNoWhenBranchMatchedConstructor by lazy {
|
||||
val result =
|
||||
kotlinNoWhenBranchMatchedExn?.declarations?.findSubType<IrConstructor> {
|
||||
it.valueParameters.isEmpty()
|
||||
it.codeQlValueParameters.isEmpty()
|
||||
}
|
||||
if (result == null) {
|
||||
logger.error("Couldn't find no-arg constructor for kotlin.NoWhenBranchMatchedException")
|
||||
@@ -3990,7 +3990,7 @@ open class KotlinFileExtractor(
|
||||
verboseln("No match as function name is ${target.name.asString()} not $fName")
|
||||
return false
|
||||
}
|
||||
val extensionReceiverParameter = target.extensionReceiverParameter
|
||||
val extensionReceiverParameter = target.codeQlExtensionReceiverParameter
|
||||
val targetClass =
|
||||
if (extensionReceiverParameter == null) {
|
||||
if (isNullable == true) {
|
||||
@@ -4098,8 +4098,8 @@ open class KotlinFileExtractor(
|
||||
) {
|
||||
val typeArgs =
|
||||
if (extractMethodTypeArguments)
|
||||
(0 until c.typeArgumentsCount)
|
||||
.map { c.getTypeArgument(it) }
|
||||
(0 until c.codeQlTypeArgumentsCount)
|
||||
.map { c.codeQlGetTypeArgument(it) }
|
||||
.requireNoNullsOrNull()
|
||||
else listOf()
|
||||
|
||||
@@ -4116,9 +4116,9 @@ open class KotlinFileExtractor(
|
||||
parent,
|
||||
idx,
|
||||
enclosingStmt,
|
||||
(0 until c.valueArgumentsCount).map { c.getValueArgument(it) },
|
||||
(0 until c.codeQlValueArgumentsCount).map { c.codeQlGetValueArgument(it) },
|
||||
c.dispatchReceiver,
|
||||
c.extensionReceiver,
|
||||
c.codeQlExtensionReceiver,
|
||||
typeArgs,
|
||||
extractClassTypeArguments,
|
||||
c.superQualifierSymbol
|
||||
@@ -4126,12 +4126,12 @@ open class KotlinFileExtractor(
|
||||
}
|
||||
|
||||
fun extractSpecialEnumFunction(fnName: String) {
|
||||
if (c.typeArgumentsCount != 1) {
|
||||
if (c.codeQlTypeArgumentsCount != 1) {
|
||||
logger.errorElement("Expected to find exactly one type argument", c)
|
||||
return
|
||||
}
|
||||
|
||||
val enumType = (c.getTypeArgument(0) as? IrSimpleType)?.classifier?.owner
|
||||
val enumType = (c.codeQlGetTypeArgument(0) as? IrSimpleType)?.classifier?.owner
|
||||
if (enumType == null) {
|
||||
logger.errorElement("Couldn't find type of enum type", c)
|
||||
return
|
||||
@@ -4178,13 +4178,13 @@ open class KotlinFileExtractor(
|
||||
} else {
|
||||
extractExpressionExpr(receiver, callable, id, 0, enclosingStmt)
|
||||
}
|
||||
if (c.valueArgumentsCount < 1) {
|
||||
if (c.codeQlValueArgumentsCount < 1) {
|
||||
logger.errorElement("No RHS found", c)
|
||||
} else {
|
||||
if (c.valueArgumentsCount > 1) {
|
||||
if (c.codeQlValueArgumentsCount > 1) {
|
||||
logger.errorElement("Extra arguments found", c)
|
||||
}
|
||||
val arg = c.getValueArgument(0)
|
||||
val arg = c.codeQlGetValueArgument(0)
|
||||
if (arg == null) {
|
||||
logger.errorElement("RHS null", c)
|
||||
} else {
|
||||
@@ -4205,7 +4205,7 @@ open class KotlinFileExtractor(
|
||||
} else {
|
||||
extractExpressionExpr(receiver, callable, id, 0, enclosingStmt)
|
||||
}
|
||||
if (c.valueArgumentsCount > 0) {
|
||||
if (c.codeQlValueArgumentsCount > 0) {
|
||||
logger.errorElement("Extra arguments found", c)
|
||||
}
|
||||
}
|
||||
@@ -4219,7 +4219,7 @@ open class KotlinFileExtractor(
|
||||
}
|
||||
|
||||
fun binopExt(id: Label<out DbExpr>) {
|
||||
binopReceiver(id, c.extensionReceiver, "Extension receiver")
|
||||
binopReceiver(id, c.codeQlExtensionReceiver, "Extension receiver")
|
||||
}
|
||||
|
||||
fun unaryopDisp(id: Label<out DbExpr>) {
|
||||
@@ -4227,7 +4227,7 @@ open class KotlinFileExtractor(
|
||||
}
|
||||
|
||||
fun unaryopExt(id: Label<out DbExpr>) {
|
||||
unaryopReceiver(id, c.extensionReceiver, "Extension receiver")
|
||||
unaryopReceiver(id, c.codeQlExtensionReceiver, "Extension receiver")
|
||||
}
|
||||
|
||||
val dr = c.dispatchReceiver
|
||||
@@ -4249,7 +4249,7 @@ open class KotlinFileExtractor(
|
||||
parent,
|
||||
idx,
|
||||
enclosingStmt,
|
||||
listOf(c.extensionReceiver, c.getValueArgument(0)),
|
||||
listOf(c.codeQlExtensionReceiver, c.codeQlGetValueArgument(0)),
|
||||
null,
|
||||
null
|
||||
)
|
||||
@@ -4350,7 +4350,7 @@ open class KotlinFileExtractor(
|
||||
// != gets desugared into not and ==. Here we resugar it.
|
||||
c.origin == IrStatementOrigin.EXCLEQ &&
|
||||
isFunction(target, "kotlin", "Boolean", "not") &&
|
||||
c.valueArgumentsCount == 0 &&
|
||||
c.codeQlValueArgumentsCount == 0 &&
|
||||
dr != null &&
|
||||
dr is IrCall &&
|
||||
isBuiltinCallInternal(dr, "EQEQ") -> {
|
||||
@@ -4362,7 +4362,7 @@ open class KotlinFileExtractor(
|
||||
}
|
||||
c.origin == IrStatementOrigin.EXCLEQEQ &&
|
||||
isFunction(target, "kotlin", "Boolean", "not") &&
|
||||
c.valueArgumentsCount == 0 &&
|
||||
c.codeQlValueArgumentsCount == 0 &&
|
||||
dr != null &&
|
||||
dr is IrCall &&
|
||||
isBuiltinCallInternal(dr, "EQEQEQ") -> {
|
||||
@@ -4374,7 +4374,7 @@ open class KotlinFileExtractor(
|
||||
}
|
||||
c.origin == IrStatementOrigin.EXCLEQ &&
|
||||
isFunction(target, "kotlin", "Boolean", "not") &&
|
||||
c.valueArgumentsCount == 0 &&
|
||||
c.codeQlValueArgumentsCount == 0 &&
|
||||
dr != null &&
|
||||
dr is IrCall &&
|
||||
isBuiltinCallInternal(dr, "ieee754equals") -> {
|
||||
@@ -4576,7 +4576,7 @@ open class KotlinFileExtractor(
|
||||
parent,
|
||||
idx,
|
||||
enclosingStmt,
|
||||
listOf(c.extensionReceiver),
|
||||
listOf(c.codeQlExtensionReceiver),
|
||||
null,
|
||||
null
|
||||
)
|
||||
@@ -4596,8 +4596,8 @@ open class KotlinFileExtractor(
|
||||
val locId = tw.getLocation(c)
|
||||
extractExprContext(id, locId, callable, enclosingStmt)
|
||||
|
||||
if (c.typeArgumentsCount == 1) {
|
||||
val typeArgument = c.getTypeArgument(0)
|
||||
if (c.codeQlTypeArgumentsCount == 1) {
|
||||
val typeArgument = c.codeQlGetTypeArgument(0)
|
||||
if (typeArgument == null) {
|
||||
logger.errorElement("Type argument missing in an arrayOfNulls call", c)
|
||||
} else {
|
||||
@@ -4618,8 +4618,8 @@ open class KotlinFileExtractor(
|
||||
)
|
||||
}
|
||||
|
||||
if (c.valueArgumentsCount == 1) {
|
||||
val dim = c.getValueArgument(0)
|
||||
if (c.codeQlValueArgumentsCount == 1) {
|
||||
val dim = c.codeQlGetValueArgument(0)
|
||||
if (dim != null) {
|
||||
extractExpressionExpr(dim, callable, id, 0, enclosingStmt)
|
||||
} else {
|
||||
@@ -4651,8 +4651,8 @@ open class KotlinFileExtractor(
|
||||
c.type.getArrayElementTypeCodeQL(pluginContext.irBuiltIns)
|
||||
} else {
|
||||
// TODO: is there any reason not to always use getArrayElementTypeCodeQL?
|
||||
if (c.typeArgumentsCount == 1) {
|
||||
c.getTypeArgument(0).also {
|
||||
if (c.codeQlTypeArgumentsCount == 1) {
|
||||
c.codeQlGetTypeArgument(0).also {
|
||||
if (it == null) {
|
||||
logger.errorElement(
|
||||
"Type argument missing in an arrayOf call",
|
||||
@@ -4670,7 +4670,7 @@ open class KotlinFileExtractor(
|
||||
}
|
||||
|
||||
val arg =
|
||||
if (c.valueArgumentsCount == 1) c.getValueArgument(0)
|
||||
if (c.codeQlValueArgumentsCount == 1) c.codeQlGetValueArgument(0)
|
||||
else {
|
||||
logger.errorElement(
|
||||
"Expected to find only one (vararg) argument in ${c.symbol.owner.name.asString()} call",
|
||||
@@ -4719,7 +4719,7 @@ open class KotlinFileExtractor(
|
||||
return
|
||||
}
|
||||
|
||||
val ext = c.extensionReceiver
|
||||
val ext = c.codeQlExtensionReceiver
|
||||
if (ext == null) {
|
||||
logger.errorElement(
|
||||
"No extension receiver found for `KClass::java` call",
|
||||
@@ -4826,8 +4826,8 @@ open class KotlinFileExtractor(
|
||||
c.origin == IrStatementOrigin.EQ &&
|
||||
c.dispatchReceiver != null -> {
|
||||
val array = c.dispatchReceiver
|
||||
val arrayIdx = c.getValueArgument(0)
|
||||
val assignedValue = c.getValueArgument(1)
|
||||
val arrayIdx = c.codeQlGetValueArgument(0)
|
||||
val assignedValue = c.codeQlGetValueArgument(1)
|
||||
|
||||
if (array != null && arrayIdx != null && assignedValue != null) {
|
||||
|
||||
@@ -4882,22 +4882,22 @@ open class KotlinFileExtractor(
|
||||
}
|
||||
isBuiltinCall(c, "<unsafe-coerce>", "kotlin.jvm.internal") -> {
|
||||
|
||||
if (c.valueArgumentsCount != 1) {
|
||||
if (c.codeQlValueArgumentsCount != 1) {
|
||||
logger.errorElement(
|
||||
"Expected to find one argument for a kotlin.jvm.internal.<unsafe-coerce>() call, but found ${c.valueArgumentsCount}",
|
||||
"Expected to find one argument for a kotlin.jvm.internal.<unsafe-coerce>() call, but found ${c.codeQlValueArgumentsCount}",
|
||||
c
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
if (c.typeArgumentsCount != 2) {
|
||||
if (c.codeQlTypeArgumentsCount != 2) {
|
||||
logger.errorElement(
|
||||
"Expected to find two type arguments for a kotlin.jvm.internal.<unsafe-coerce>() call, but found ${c.typeArgumentsCount}",
|
||||
"Expected to find two type arguments for a kotlin.jvm.internal.<unsafe-coerce>() call, but found ${c.codeQlTypeArgumentsCount}",
|
||||
c
|
||||
)
|
||||
return
|
||||
}
|
||||
val valueArg = c.getValueArgument(0)
|
||||
val valueArg = c.codeQlGetValueArgument(0)
|
||||
if (valueArg == null) {
|
||||
logger.errorElement(
|
||||
"Cannot find value argument for a kotlin.jvm.internal.<unsafe-coerce>() call",
|
||||
@@ -4905,7 +4905,7 @@ open class KotlinFileExtractor(
|
||||
)
|
||||
return
|
||||
}
|
||||
val typeArg = c.getTypeArgument(1)
|
||||
val typeArg = c.codeQlGetTypeArgument(1)
|
||||
if (typeArg == null) {
|
||||
logger.errorElement(
|
||||
"Cannot find type argument for a kotlin.jvm.internal.<unsafe-coerce>() call",
|
||||
@@ -4924,7 +4924,7 @@ open class KotlinFileExtractor(
|
||||
extractExpressionExpr(valueArg, callable, id, 1, enclosingStmt)
|
||||
}
|
||||
isBuiltinCallInternal(c, "dataClassArrayMemberToString") -> {
|
||||
val arrayArg = c.getValueArgument(0)
|
||||
val arrayArg = c.codeQlGetValueArgument(0)
|
||||
val realArrayClass = arrayArg?.type?.classOrNull
|
||||
if (realArrayClass == null) {
|
||||
logger.errorElement(
|
||||
@@ -4936,8 +4936,8 @@ open class KotlinFileExtractor(
|
||||
val realCallee =
|
||||
javaUtilArrays?.declarations?.findSubType<IrFunction> { decl ->
|
||||
decl.name.asString() == "toString" &&
|
||||
decl.valueParameters.size == 1 &&
|
||||
decl.valueParameters[0].type.classOrNull?.let {
|
||||
decl.codeQlValueParameters.size == 1 &&
|
||||
decl.codeQlValueParameters[0].type.classOrNull?.let {
|
||||
it == realArrayClass
|
||||
} == true
|
||||
}
|
||||
@@ -4962,7 +4962,7 @@ open class KotlinFileExtractor(
|
||||
}
|
||||
}
|
||||
isBuiltinCallInternal(c, "dataClassArrayMemberHashCode") -> {
|
||||
val arrayArg = c.getValueArgument(0)
|
||||
val arrayArg = c.codeQlGetValueArgument(0)
|
||||
val realArrayClass = arrayArg?.type?.classOrNull
|
||||
if (realArrayClass == null) {
|
||||
logger.errorElement(
|
||||
@@ -4974,8 +4974,8 @@ open class KotlinFileExtractor(
|
||||
val realCallee =
|
||||
javaUtilArrays?.declarations?.findSubType<IrFunction> { decl ->
|
||||
decl.name.asString() == "hashCode" &&
|
||||
decl.valueParameters.size == 1 &&
|
||||
decl.valueParameters[0].type.classOrNull?.let {
|
||||
decl.codeQlValueParameters.size == 1 &&
|
||||
decl.codeQlValueParameters[0].type.classOrNull?.let {
|
||||
it == realArrayClass
|
||||
} == true
|
||||
}
|
||||
@@ -5155,7 +5155,7 @@ open class KotlinFileExtractor(
|
||||
val type = useType(eType)
|
||||
val isAnonymous = eType.isAnonymous
|
||||
val locId = tw.getLocation(e)
|
||||
val valueArgs = (0 until e.valueArgumentsCount).map { e.getValueArgument(it) }
|
||||
val valueArgs = (0 until e.codeQlValueArgumentsCount).map { e.codeQlGetValueArgument(it) }
|
||||
|
||||
val id =
|
||||
if (
|
||||
@@ -5211,10 +5211,10 @@ open class KotlinFileExtractor(
|
||||
realCallTarget is IrConstructor &&
|
||||
realCallTarget.parentClassOrNull?.fqNameWhenAvailable?.asString() ==
|
||||
"kotlin.Enum" &&
|
||||
realCallTarget.valueParameters.size == 2 &&
|
||||
realCallTarget.valueParameters[0].type ==
|
||||
realCallTarget.codeQlValueParameters.size == 2 &&
|
||||
realCallTarget.codeQlValueParameters[0].type ==
|
||||
pluginContext.irBuiltIns.stringType &&
|
||||
realCallTarget.valueParameters[1].type == pluginContext.irBuiltIns.intType
|
||||
realCallTarget.codeQlValueParameters[1].type == pluginContext.irBuiltIns.intType
|
||||
) {
|
||||
|
||||
val id0 =
|
||||
@@ -5287,7 +5287,7 @@ open class KotlinFileExtractor(
|
||||
}
|
||||
|
||||
val args =
|
||||
(0 until e.typeArgumentsCount).map { e.getTypeArgument(it) }.requireNoNullsOrNull()
|
||||
(0 until e.codeQlTypeArgumentsCount).map { e.codeQlGetTypeArgument(it) }.requireNoNullsOrNull()
|
||||
if (args == null) {
|
||||
logger.warnElement("Found null type argument in enum constructor call", e)
|
||||
return
|
||||
@@ -5365,7 +5365,7 @@ open class KotlinFileExtractor(
|
||||
// Check for an expression like x = get(x).op(e):
|
||||
val opReceiver = updateRhs.dispatchReceiver
|
||||
if (isExpectedLhs(opReceiver)) {
|
||||
updateRhs.getValueArgument(0)
|
||||
updateRhs.codeQlGetValueArgument(0)
|
||||
} else null
|
||||
} else null
|
||||
}
|
||||
@@ -5560,7 +5560,7 @@ open class KotlinFileExtractor(
|
||||
"set"
|
||||
)
|
||||
) {
|
||||
val updateRhs0 = arraySetCall.getValueArgument(1)
|
||||
val updateRhs0 = arraySetCall.codeQlGetValueArgument(1)
|
||||
if (updateRhs0 == null) {
|
||||
logger.errorElement("Update RHS not found", e)
|
||||
return false
|
||||
@@ -6403,12 +6403,12 @@ open class KotlinFileExtractor(
|
||||
val ids = getLocallyVisibleFunctionLabels(e.function)
|
||||
val locId = tw.getLocation(e)
|
||||
|
||||
val ext = e.function.extensionReceiverParameter
|
||||
val ext = e.function.codeQlExtensionReceiverParameter
|
||||
val parameters =
|
||||
if (ext != null) {
|
||||
listOf(ext) + e.function.valueParameters
|
||||
listOf(ext) + e.function.codeQlValueParameters
|
||||
} else {
|
||||
e.function.valueParameters
|
||||
e.function.codeQlValueParameters
|
||||
}
|
||||
|
||||
var types = parameters.map { it.type }
|
||||
@@ -6670,7 +6670,7 @@ open class KotlinFileExtractor(
|
||||
is IrFunction -> {
|
||||
if (
|
||||
ownerParent.dispatchReceiverParameter == owner &&
|
||||
ownerParent.extensionReceiverParameter != null
|
||||
ownerParent.codeQlExtensionReceiverParameter != null
|
||||
) {
|
||||
|
||||
val ownerParent2 = ownerParent.parent
|
||||
@@ -7089,7 +7089,7 @@ open class KotlinFileExtractor(
|
||||
makeReceiverInfo(callableReferenceExpr.dispatchReceiver, 0)
|
||||
private val extensionReceiverInfo =
|
||||
makeReceiverInfo(
|
||||
callableReferenceExpr.extensionReceiver,
|
||||
callableReferenceExpr.codeQlExtensionReceiver,
|
||||
if (dispatchReceiverInfo == null) 0 else 1
|
||||
)
|
||||
|
||||
@@ -7627,8 +7627,8 @@ open class KotlinFileExtractor(
|
||||
}
|
||||
|
||||
val expressionTypeArguments =
|
||||
(0 until propertyReferenceExpr.typeArgumentsCount).mapNotNull {
|
||||
propertyReferenceExpr.getTypeArgument(it)
|
||||
(0 until propertyReferenceExpr.codeQlTypeArgumentsCount).mapNotNull {
|
||||
propertyReferenceExpr.codeQlGetTypeArgument(it)
|
||||
}
|
||||
|
||||
val idPropertyRef = tw.getFreshIdLabel<DbPropertyref>()
|
||||
@@ -7829,7 +7829,7 @@ open class KotlinFileExtractor(
|
||||
|
||||
if (
|
||||
functionReferenceExpr.dispatchReceiver != null &&
|
||||
functionReferenceExpr.extensionReceiver != null
|
||||
functionReferenceExpr.codeQlExtensionReceiver != null
|
||||
) {
|
||||
logger.errorElement(
|
||||
"Unexpected: dispatchReceiver and extensionReceiver are both non-null",
|
||||
@@ -7840,7 +7840,7 @@ open class KotlinFileExtractor(
|
||||
|
||||
if (
|
||||
target.owner.dispatchReceiverParameter != null &&
|
||||
target.owner.extensionReceiverParameter != null
|
||||
target.owner.codeQlExtensionReceiverParameter != null
|
||||
) {
|
||||
logger.errorElement(
|
||||
"Unexpected: dispatch and extension parameters are both non-null",
|
||||
@@ -7899,8 +7899,8 @@ open class KotlinFileExtractor(
|
||||
null
|
||||
}
|
||||
expressionTypeArguments =
|
||||
(0 until functionReferenceExpr.typeArgumentsCount).mapNotNull {
|
||||
functionReferenceExpr.getTypeArgument(it)
|
||||
(0 until functionReferenceExpr.codeQlTypeArgumentsCount).mapNotNull {
|
||||
functionReferenceExpr.codeQlGetTypeArgument(it)
|
||||
}
|
||||
dispatchReceiverIdx = -1
|
||||
}
|
||||
@@ -7965,7 +7965,7 @@ open class KotlinFileExtractor(
|
||||
functionReferenceExpr,
|
||||
declarationParent,
|
||||
null,
|
||||
{ it.valueParameters.size == 1 }
|
||||
{ it.codeQlValueParameters.size == 1 }
|
||||
) {
|
||||
// The argument to FunctionReference's constructor is the function arity.
|
||||
extractConstantInteger(
|
||||
@@ -8572,7 +8572,7 @@ open class KotlinFileExtractor(
|
||||
reverse: Boolean = false
|
||||
) {
|
||||
val typeArguments =
|
||||
(0 until c.typeArgumentsCount).map { c.getTypeArgument(it) }.requireNoNullsOrNull()
|
||||
(0 until c.codeQlTypeArgumentsCount).map { c.codeQlGetTypeArgument(it) }.requireNoNullsOrNull()
|
||||
if (typeArguments == null) {
|
||||
logger.errorElement("Found a null type argument for a member access expression", c)
|
||||
} else {
|
||||
@@ -8923,11 +8923,11 @@ open class KotlinFileExtractor(
|
||||
tw.writeVariableBinding(lhsId, fieldId)
|
||||
|
||||
val parameters = mutableListOf<IrValueParameter>()
|
||||
val extParam = samMember.extensionReceiverParameter
|
||||
val extParam = samMember.codeQlExtensionReceiverParameter
|
||||
if (extParam != null) {
|
||||
parameters.add(extParam)
|
||||
}
|
||||
parameters.addAll(samMember.valueParameters)
|
||||
parameters.addAll(samMember.codeQlValueParameters)
|
||||
|
||||
fun extractArgument(
|
||||
p: IrValueParameter,
|
||||
@@ -9032,7 +9032,7 @@ open class KotlinFileExtractor(
|
||||
elementToReportOn: IrElement,
|
||||
declarationParent: IrDeclarationParent,
|
||||
compilerGeneratedKindOverride: CompilerGeneratedKinds? = null,
|
||||
superConstructorSelector: (IrFunction) -> Boolean = { it.valueParameters.isEmpty() },
|
||||
superConstructorSelector: (IrFunction) -> Boolean = { it.codeQlValueParameters.isEmpty() },
|
||||
extractSuperconstructorArgs: (Label<DbSuperconstructorinvocationstmt>) -> Unit = {},
|
||||
): Label<out DbClassorinterface> {
|
||||
// Write class
|
||||
|
||||
@@ -12,7 +12,7 @@ import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.symbols.*
|
||||
import org.jetbrains.kotlin.ir.types.addAnnotations
|
||||
import com.github.codeql.utils.versions.codeQlAddAnnotations
|
||||
import org.jetbrains.kotlin.ir.types.classFqName
|
||||
import org.jetbrains.kotlin.ir.types.classifierOrNull
|
||||
import org.jetbrains.kotlin.ir.types.classOrNull
|
||||
@@ -355,7 +355,7 @@ open class KotlinUsesExtractor(
|
||||
}
|
||||
|
||||
private fun propertySignature(p: IrProperty) =
|
||||
((p.getter ?: p.setter)?.extensionReceiverParameter?.let {
|
||||
((p.getter ?: p.setter)?.codeQlExtensionReceiverParameter?.let {
|
||||
useType(erase(it.type)).javaResult.signature
|
||||
} ?: "")
|
||||
|
||||
@@ -368,7 +368,7 @@ open class KotlinUsesExtractor(
|
||||
// useDeclarationParent -> useFunction
|
||||
// -> extractFunctionLaterIfExternalFileMember, which would result for `fun <T> f(t:
|
||||
// T) { ... }` for example.
|
||||
(listOfNotNull(d.extensionReceiverParameter) + d.valueParameters)
|
||||
(listOfNotNull(d.codeQlExtensionReceiverParameter) + d.codeQlValueParameters)
|
||||
.map { useType(erase(it.type)).javaResult.signature }
|
||||
.joinToString(separator = ",", prefix = "(", postfix = ")")
|
||||
is IrProperty -> propertySignature(d) + externalClassExtractor.propertySignature
|
||||
@@ -488,8 +488,8 @@ open class KotlinUsesExtractor(
|
||||
val result =
|
||||
replacementClass.declarations.findSubType<IrSimpleFunction> { replacementDecl ->
|
||||
replacementDecl.name == f.name &&
|
||||
replacementDecl.valueParameters.size == f.valueParameters.size &&
|
||||
replacementDecl.valueParameters.zip(f.valueParameters).all {
|
||||
replacementDecl.codeQlValueParameters.size == f.codeQlValueParameters.size &&
|
||||
replacementDecl.codeQlValueParameters.zip(f.codeQlValueParameters).all {
|
||||
erase(it.first.type) == erase(it.second.type)
|
||||
}
|
||||
}
|
||||
@@ -1265,7 +1265,7 @@ open class KotlinUsesExtractor(
|
||||
private fun getWildcardSuppressionDirective(t: IrAnnotationContainer): Boolean? =
|
||||
t.getAnnotation(jvmWildcardSuppressionAnnotation)?.let {
|
||||
@Suppress("USELESS_CAST") // `as? Boolean` is not needed for Kotlin < 2.1
|
||||
(it.getValueArgument(0) as? CodeQLIrConst<Boolean>)?.value as? Boolean ?: true
|
||||
(it.codeQlGetValueArgument(0) as? CodeQLIrConst<Boolean>)?.value as? Boolean ?: true
|
||||
}
|
||||
|
||||
private fun addJavaLoweringArgumentWildcards(
|
||||
@@ -1376,9 +1376,9 @@ open class KotlinUsesExtractor(
|
||||
f.parent,
|
||||
parentId,
|
||||
getFunctionShortName(f).nameInDB,
|
||||
(maybeParameterList ?: f.valueParameters).map { it.type },
|
||||
(maybeParameterList ?: f.codeQlValueParameters).map { it.type },
|
||||
getAdjustedReturnType(f),
|
||||
f.extensionReceiverParameter?.type,
|
||||
f.codeQlExtensionReceiverParameter?.type,
|
||||
getFunctionTypeParameters(f),
|
||||
classTypeArgsIncludingOuterClasses,
|
||||
overridesCollectionsMethodWithAlteredParameterTypes(f),
|
||||
@@ -1401,12 +1401,12 @@ open class KotlinUsesExtractor(
|
||||
// The name of the function; normally f.name.asString().
|
||||
name: String,
|
||||
// The types of the value parameters that the functions takes; normally
|
||||
// f.valueParameters.map { it.type }.
|
||||
// f.codeQlValueParameters.map { it.type }.
|
||||
parameterTypes: List<IrType>,
|
||||
// The return type of the function; normally f.returnType.
|
||||
returnType: IrType,
|
||||
// The extension receiver of the function, if any; normally
|
||||
// f.extensionReceiverParameter?.type.
|
||||
// f.codeQlExtensionReceiverParameter?.type.
|
||||
extensionParamType: IrType?,
|
||||
// The type parameters of the function. This does not include type parameters of enclosing
|
||||
// classes.
|
||||
@@ -1579,7 +1579,7 @@ open class KotlinUsesExtractor(
|
||||
parentClass.fqNameWhenAvailable?.asString() !=
|
||||
"java.util.concurrent.ConcurrentHashMap" ||
|
||||
getFunctionShortName(f).nameInDB != "keySet" ||
|
||||
f.valueParameters.isNotEmpty() ||
|
||||
f.codeQlValueParameters.isNotEmpty() ||
|
||||
f.returnType.classFqName?.asString() != "kotlin.collections.MutableSet"
|
||||
) {
|
||||
return f.returnType
|
||||
@@ -1587,7 +1587,7 @@ open class KotlinUsesExtractor(
|
||||
|
||||
val otherKeySet =
|
||||
parentClass.declarations.findSubType<IrFunction> {
|
||||
it.name.asString() == "keySet" && it.valueParameters.size == 1
|
||||
it.name.asString() == "keySet" && it.codeQlValueParameters.size == 1
|
||||
} ?: return f.returnType
|
||||
|
||||
return otherKeySet.returnType.codeQlWithHasQuestionMark(false)
|
||||
@@ -1695,8 +1695,8 @@ open class KotlinUsesExtractor(
|
||||
javaClass.declarations.findSubType<IrFunction> { decl ->
|
||||
!decl.isFakeOverride &&
|
||||
decl.name.asString() == jvmName &&
|
||||
decl.valueParameters.size == f.valueParameters.size &&
|
||||
decl.valueParameters.zip(f.valueParameters).all { p ->
|
||||
decl.codeQlValueParameters.size == f.codeQlValueParameters.size &&
|
||||
decl.codeQlValueParameters.zip(f.codeQlValueParameters).all { p ->
|
||||
erase(p.first.type).classifierOrNull ==
|
||||
erase(p.second.type).classifierOrNull
|
||||
}
|
||||
@@ -2125,7 +2125,7 @@ open class KotlinUsesExtractor(
|
||||
}
|
||||
|
||||
return if (t.arguments.isNotEmpty())
|
||||
t.addAnnotations(listOf(RawTypeAnnotation.annotationConstructor))
|
||||
t.codeQlAddAnnotations(listOf(RawTypeAnnotation.annotationConstructor))
|
||||
else t
|
||||
}
|
||||
}
|
||||
@@ -2153,7 +2153,7 @@ open class KotlinUsesExtractor(
|
||||
val idxOffset =
|
||||
if (
|
||||
declarationParent is IrFunction &&
|
||||
declarationParent.extensionReceiverParameter != null
|
||||
declarationParent.codeQlExtensionReceiverParameter != null
|
||||
)
|
||||
// For extension functions increase the index to match what the java extractor sees:
|
||||
1
|
||||
@@ -2187,7 +2187,7 @@ open class KotlinUsesExtractor(
|
||||
// Gets a field's corresponding property's extension receiver type, if any
|
||||
fun getExtensionReceiverType(f: IrField) =
|
||||
f.correspondingPropertySymbol?.owner?.let {
|
||||
(it.getter ?: it.setter)?.extensionReceiverParameter?.type
|
||||
(it.getter ?: it.setter)?.codeQlExtensionReceiverParameter?.type
|
||||
}
|
||||
|
||||
fun getFieldLabel(f: IrField): String {
|
||||
@@ -2222,14 +2222,14 @@ open class KotlinUsesExtractor(
|
||||
val setter = p.setter
|
||||
|
||||
val func = getter ?: setter
|
||||
val ext = func?.extensionReceiverParameter
|
||||
val ext = func?.codeQlExtensionReceiverParameter
|
||||
|
||||
return if (ext == null) {
|
||||
"@\"property;{$parentId};${p.name.asString()}\""
|
||||
} else {
|
||||
val returnType =
|
||||
getter?.returnType
|
||||
?: setter?.valueParameters?.singleOrNull()?.type
|
||||
?: setter?.codeQlValueParameters?.singleOrNull()?.type
|
||||
?: pluginContext.irBuiltIns.unitType
|
||||
val typeParams = getFunctionTypeParameters(func)
|
||||
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
package com.github.codeql
|
||||
|
||||
import com.github.codeql.utils.versions.codeQlAnnotationFromSymbolOwner
|
||||
import com.github.codeql.utils.versions.codeQlGetValueArgument
|
||||
import com.github.codeql.utils.versions.codeQlPutValueArgument
|
||||
import com.github.codeql.utils.versions.codeQlSetAnnotations
|
||||
import com.github.codeql.utils.versions.codeQlSetDispatchReceiverParameter
|
||||
import com.github.codeql.utils.versions.createImplicitParameterDeclarationWithWrappedDescriptor
|
||||
import java.lang.annotation.ElementType
|
||||
import java.util.HashSet
|
||||
@@ -95,7 +100,7 @@ class MetaAnnotationSupport(
|
||||
JvmAnnotationNames.REPEATABLE_ANNOTATION
|
||||
}
|
||||
return if (jvmRepeatable != null) {
|
||||
((jvmRepeatable.getValueArgument(0) as? IrClassReference)?.symbol as? IrClassSymbol)
|
||||
((jvmRepeatable.codeQlGetValueArgument(0) as? IrClassReference)?.symbol as? IrClassSymbol)
|
||||
?.owner
|
||||
} else {
|
||||
getOrCreateSyntheticRepeatableAnnotationContainer(annotationClass)
|
||||
@@ -117,12 +122,12 @@ class MetaAnnotationSupport(
|
||||
)
|
||||
return null
|
||||
} else {
|
||||
return IrConstructorCallImpl.fromSymbolOwner(
|
||||
return codeQlAnnotationFromSymbolOwner(
|
||||
containerClass.defaultType,
|
||||
containerConstructor.symbol
|
||||
)
|
||||
.apply {
|
||||
putValueArgument(
|
||||
codeQlPutValueArgument(
|
||||
0,
|
||||
IrVarargImpl(
|
||||
UNDEFINED_OFFSET,
|
||||
@@ -144,7 +149,7 @@ class MetaAnnotationSupport(
|
||||
|
||||
// Taken from AdditionalClassAnnotationLowering.kt
|
||||
private fun loadAnnotationTargets(targetEntry: IrConstructorCall): Set<KotlinTarget>? {
|
||||
val valueArgument = targetEntry.getValueArgument(0) as? IrVararg ?: return null
|
||||
val valueArgument = targetEntry.codeQlGetValueArgument(0) as? IrVararg ?: return null
|
||||
return valueArgument.elements
|
||||
.filterIsInstance<IrGetEnumValue>()
|
||||
.mapNotNull { KotlinTarget.valueOrNull(it.symbol.owner.name.asString()) }
|
||||
@@ -230,14 +235,14 @@ class MetaAnnotationSupport(
|
||||
)
|
||||
}
|
||||
|
||||
return IrConstructorCallImpl.fromSymbolOwner(
|
||||
return codeQlAnnotationFromSymbolOwner(
|
||||
UNDEFINED_OFFSET,
|
||||
UNDEFINED_OFFSET,
|
||||
targetConstructor.returnType,
|
||||
targetConstructor.symbol,
|
||||
0
|
||||
)
|
||||
.apply { putValueArgument(0, vararg) }
|
||||
.apply { codeQlPutValueArgument(0, vararg) }
|
||||
}
|
||||
|
||||
private val javaAnnotationRetention by lazy {
|
||||
@@ -263,7 +268,7 @@ class MetaAnnotationSupport(
|
||||
// Taken from AnnotationCodegen.kt (not available in Kotlin < 1.6.20)
|
||||
private fun IrClass.getAnnotationRetention(): KotlinRetention? {
|
||||
val retentionArgument =
|
||||
getAnnotation(StandardNames.FqNames.retention)?.getValueArgument(0) as? IrGetEnumValue
|
||||
getAnnotation(StandardNames.FqNames.retention)?.codeQlGetValueArgument(0) as? IrGetEnumValue
|
||||
?: return null
|
||||
val retentionArgumentValue = retentionArgument.symbol.owner
|
||||
return KotlinRetention.valueOf(retentionArgumentValue.name.asString())
|
||||
@@ -283,7 +288,7 @@ class MetaAnnotationSupport(
|
||||
val targetConstructor =
|
||||
retentionType.declarations.firstIsInstanceOrNull<IrConstructor>() ?: return null
|
||||
|
||||
return IrConstructorCallImpl.fromSymbolOwner(
|
||||
return codeQlAnnotationFromSymbolOwner(
|
||||
UNDEFINED_OFFSET,
|
||||
UNDEFINED_OFFSET,
|
||||
targetConstructor.returnType,
|
||||
@@ -291,7 +296,7 @@ class MetaAnnotationSupport(
|
||||
0
|
||||
)
|
||||
.apply {
|
||||
putValueArgument(
|
||||
codeQlPutValueArgument(
|
||||
0,
|
||||
IrGetEnumValueImpl(
|
||||
UNDEFINED_OFFSET,
|
||||
@@ -333,7 +338,7 @@ class MetaAnnotationSupport(
|
||||
return
|
||||
}
|
||||
val newParam = thisReceiever.copyTo(this)
|
||||
dispatchReceiverParameter = newParam
|
||||
codeQlSetDispatchReceiverParameter(newParam)
|
||||
body =
|
||||
factory
|
||||
.createBlockBody(UNDEFINED_OFFSET, UNDEFINED_OFFSET)
|
||||
@@ -406,7 +411,7 @@ class MetaAnnotationSupport(
|
||||
val repeatableContainerAnnotation =
|
||||
kotlinAnnotationRepeatableContainer?.constructors?.single()
|
||||
|
||||
containerClass.annotations =
|
||||
codeQlSetAnnotations(containerClass,
|
||||
annotationClass.annotations
|
||||
.filter {
|
||||
it.isAnnotationWithEqualFqName(StandardNames.FqNames.retention) ||
|
||||
@@ -415,7 +420,7 @@ class MetaAnnotationSupport(
|
||||
.map { it.deepCopyWithSymbols(containerClass) } +
|
||||
listOfNotNull(
|
||||
repeatableContainerAnnotation?.let {
|
||||
IrConstructorCallImpl.fromSymbolOwner(
|
||||
codeQlAnnotationFromSymbolOwner(
|
||||
UNDEFINED_OFFSET,
|
||||
UNDEFINED_OFFSET,
|
||||
it.returnType,
|
||||
@@ -424,6 +429,7 @@ class MetaAnnotationSupport(
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
containerClass
|
||||
}
|
||||
@@ -462,14 +468,14 @@ class MetaAnnotationSupport(
|
||||
containerClass.symbol,
|
||||
containerClass.defaultType
|
||||
)
|
||||
return IrConstructorCallImpl.fromSymbolOwner(
|
||||
return codeQlAnnotationFromSymbolOwner(
|
||||
UNDEFINED_OFFSET,
|
||||
UNDEFINED_OFFSET,
|
||||
repeatableConstructor.returnType,
|
||||
repeatableConstructor.symbol,
|
||||
0
|
||||
)
|
||||
.apply { putValueArgument(0, containerReference) }
|
||||
.apply { codeQlPutValueArgument(0, containerReference) }
|
||||
}
|
||||
|
||||
private val javaAnnotationDocumented by lazy {
|
||||
@@ -488,7 +494,7 @@ class MetaAnnotationSupport(
|
||||
javaAnnotationDocumented?.declarations?.firstIsInstanceOrNull<IrConstructor>()
|
||||
?: return null
|
||||
|
||||
return IrConstructorCallImpl.fromSymbolOwner(
|
||||
return codeQlAnnotationFromSymbolOwner(
|
||||
UNDEFINED_OFFSET,
|
||||
UNDEFINED_OFFSET,
|
||||
documentedConstructor.returnType,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.github.codeql
|
||||
|
||||
import com.github.codeql.KotlinUsesExtractor.LocallyVisibleFunctionLabels
|
||||
import com.github.codeql.utils.versions.codeQlExtensionReceiver
|
||||
import com.semmle.extractor.java.PopulateFile
|
||||
import com.semmle.util.unicode.UTF8Util
|
||||
import java.io.BufferedWriter
|
||||
@@ -331,7 +332,7 @@ open class FileTrapWriter(
|
||||
is IrCall -> {
|
||||
// Calls have incorrect startOffset, so we adjust them:
|
||||
val dr = e.dispatchReceiver?.let { getStartOffset(it) }
|
||||
val er = e.extensionReceiver?.let { getStartOffset(it) }
|
||||
val er = e.codeQlExtensionReceiver?.let { getStartOffset(it) }
|
||||
offsetMinOf(e.startOffset, dr, er)
|
||||
}
|
||||
else -> e.startOffset
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.github.codeql.comments
|
||||
|
||||
import com.github.codeql.*
|
||||
import com.github.codeql.utils.isLocalFunction
|
||||
import com.github.codeql.utils.versions.codeQlExtensionReceiverParameter
|
||||
import com.github.codeql.utils.versions.isDispatchReceiver
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
@@ -11,7 +12,7 @@ import org.jetbrains.kotlin.ir.util.parentClassOrNull
|
||||
|
||||
private fun IrValueParameter.isExtensionReceiver(): Boolean {
|
||||
val parentFun = parent as? IrFunction ?: return false
|
||||
return parentFun.extensionReceiverParameter == this
|
||||
return parentFun.codeQlExtensionReceiverParameter == this
|
||||
}
|
||||
|
||||
open class CommentExtractor(
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.github.codeql.utils
|
||||
|
||||
import com.github.codeql.utils.versions.CodeQLIrConst
|
||||
import com.github.codeql.utils.versions.codeQlGetValueArgument
|
||||
import com.github.codeql.utils.versions.codeQlValueArgumentsCount
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.ir.declarations.IrAnnotationContainer
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
@@ -76,9 +78,9 @@ private fun getSpecialJvmName(f: IrFunction): String? {
|
||||
fun getJvmName(container: IrAnnotationContainer): String? {
|
||||
for (a: IrConstructorCall in container.annotations) {
|
||||
val t = a.type
|
||||
if (t is IrSimpleType && a.valueArgumentsCount == 1) {
|
||||
if (t is IrSimpleType && a.codeQlValueArgumentsCount == 1) {
|
||||
val owner = t.classifier.owner
|
||||
val v = a.getValueArgument(0)
|
||||
val v = a.codeQlGetValueArgument(0)
|
||||
if (owner is IrClass) {
|
||||
val aPkg = owner.packageFqName?.asString()
|
||||
val name = owner.name.asString()
|
||||
|
||||
@@ -18,7 +18,7 @@ import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.impl.DescriptorlessExternalPackageFragmentSymbol
|
||||
import org.jetbrains.kotlin.ir.types.addAnnotations
|
||||
import com.github.codeql.utils.versions.codeQlAddAnnotations
|
||||
import org.jetbrains.kotlin.ir.types.classifierOrNull
|
||||
import org.jetbrains.kotlin.ir.types.makeNotNull
|
||||
import org.jetbrains.kotlin.ir.types.makeNullable
|
||||
@@ -192,7 +192,7 @@ object RawTypeAnnotation {
|
||||
addConstructor { isPrimary = true }
|
||||
}
|
||||
val constructor = annoClass.constructors.single()
|
||||
IrConstructorCallImpl.fromSymbolOwner(constructor.constructedClassType, constructor.symbol)
|
||||
codeQlAnnotationFromSymbolOwner(constructor.constructedClassType, constructor.symbol)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,7 +202,7 @@ fun IrType.toRawType(): IrType =
|
||||
when (val owner = this.classifier.owner) {
|
||||
is IrClass -> {
|
||||
if (this.arguments.isNotEmpty())
|
||||
this.addAnnotations(listOf(RawTypeAnnotation.annotationConstructor))
|
||||
this.codeQlAddAnnotations(listOf(RawTypeAnnotation.annotationConstructor))
|
||||
else this
|
||||
}
|
||||
is IrTypeParameter -> owner.superTypes[0].toRawType()
|
||||
@@ -215,7 +215,7 @@ fun IrType.toRawType(): IrType =
|
||||
fun IrClass.toRawType(): IrType {
|
||||
val result = this.typeWith(listOf())
|
||||
return if (this.typeParameters.isNotEmpty())
|
||||
result.addAnnotations(listOf(RawTypeAnnotation.annotationConstructor))
|
||||
result.codeQlAddAnnotations(listOf(RawTypeAnnotation.annotationConstructor))
|
||||
else result
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.github.codeql.utils.versions
|
||||
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
|
||||
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.addAnnotations
|
||||
|
||||
/**
|
||||
* Compatibility accessors for pre-2.4.0 API patterns.
|
||||
* In pre-2.4.0 versions, these delegate directly to the existing APIs.
|
||||
*/
|
||||
|
||||
// IrFunction: valueParameters
|
||||
val IrFunction.codeQlValueParameters: List<IrValueParameter>
|
||||
get() = valueParameters
|
||||
|
||||
// IrFunction: extensionReceiverParameter
|
||||
val IrFunction.codeQlExtensionReceiverParameter: IrValueParameter?
|
||||
get() = extensionReceiverParameter
|
||||
|
||||
// IrMemberAccessExpression: valueArgumentsCount
|
||||
val IrMemberAccessExpression<*>.codeQlValueArgumentsCount: Int
|
||||
get() = valueArgumentsCount
|
||||
|
||||
// IrMemberAccessExpression: getValueArgument
|
||||
fun IrMemberAccessExpression<*>.codeQlGetValueArgument(index: Int): IrExpression? = getValueArgument(index)
|
||||
|
||||
// IrMemberAccessExpression: putValueArgument
|
||||
fun IrMemberAccessExpression<*>.codeQlPutValueArgument(index: Int, value: IrExpression?) {
|
||||
putValueArgument(index, value)
|
||||
}
|
||||
|
||||
// IrMemberAccessExpression: extensionReceiver
|
||||
val IrMemberAccessExpression<*>.codeQlExtensionReceiver: IrExpression?
|
||||
get() = extensionReceiver
|
||||
|
||||
// IrMemberAccessExpression: typeArgumentsCount
|
||||
val IrMemberAccessExpression<*>.codeQlTypeArgumentsCount: Int
|
||||
get() = typeArgumentsCount
|
||||
|
||||
// IrMemberAccessExpression: getTypeArgument
|
||||
fun IrMemberAccessExpression<*>.codeQlGetTypeArgument(index: Int): IrType? = getTypeArgument(index)
|
||||
|
||||
// addAnnotations compat: in pre-2.4.0, addAnnotations expects List<IrConstructorCall>
|
||||
fun IrType.codeQlAddAnnotations(annotations: List<IrConstructorCall>): IrType =
|
||||
addAnnotations(annotations)
|
||||
|
||||
// IrMutableAnnotationContainer.annotations setter: in pre-2.4.0, annotations is var with List<IrConstructorCall>
|
||||
fun codeQlSetAnnotations(container: org.jetbrains.kotlin.ir.declarations.IrMutableAnnotationContainer, annotations: List<IrConstructorCall>) {
|
||||
container.annotations = annotations
|
||||
}
|
||||
|
||||
// IrFunction: set dispatch receiver parameter (pre-2.4.0 it's a var)
|
||||
fun IrFunction.codeQlSetDispatchReceiverParameter(param: IrValueParameter?) {
|
||||
dispatchReceiverParameter = param
|
||||
}
|
||||
|
||||
// In pre-2.4.0, annotations are List<IrConstructorCall> so IrConstructorCallImpl works directly.
|
||||
fun codeQlAnnotationFromSymbolOwner(
|
||||
startOffset: Int, endOffset: Int, type: IrType, symbol: IrConstructorSymbol, typeArgumentsCount: Int
|
||||
): IrConstructorCall =
|
||||
IrConstructorCallImpl.fromSymbolOwner(startOffset, endOffset, type, symbol, typeArgumentsCount)
|
||||
|
||||
fun codeQlAnnotationFromSymbolOwner(type: IrType, symbol: IrConstructorSymbol): IrConstructorCall =
|
||||
IrConstructorCallImpl.fromSymbolOwner(type, symbol)
|
||||
@@ -3,10 +3,34 @@
|
||||
|
||||
package com.github.codeql
|
||||
|
||||
import com.intellij.mock.MockProject
|
||||
import com.intellij.openapi.extensions.LoadingOrder
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
|
||||
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
|
||||
import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
|
||||
@OptIn(ExperimentalCompilerApi::class)
|
||||
abstract class Kotlin2ComponentRegistrar : ComponentRegistrar {
|
||||
/* Nothing to do; supportsK2 doesn't exist yet. */
|
||||
|
||||
private var project: MockProject? = null
|
||||
|
||||
override fun registerProjectComponents(
|
||||
project: MockProject,
|
||||
configuration: CompilerConfiguration
|
||||
) {
|
||||
this.project = project
|
||||
doRegisterExtensions(configuration)
|
||||
}
|
||||
|
||||
abstract fun doRegisterExtensions(configuration: CompilerConfiguration)
|
||||
|
||||
fun registerExtractorExtension(extension: IrGenerationExtension) {
|
||||
val p = project ?: throw IllegalStateException("registerExtractorExtension called before registerProjectComponents")
|
||||
// Register with LoadingOrder.LAST to ensure the extractor runs after other
|
||||
// IR generation plugins (like kotlinx.serialization) have generated their code.
|
||||
val extensionPoint = p.extensionArea.getExtensionPoint(IrGenerationExtension.extensionPointName)
|
||||
extensionPoint.registerExtension(extension, LoadingOrder.LAST, p)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,11 +3,35 @@
|
||||
|
||||
package com.github.codeql
|
||||
|
||||
import com.intellij.mock.MockProject
|
||||
import com.intellij.openapi.extensions.LoadingOrder
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
|
||||
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
|
||||
import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
|
||||
@OptIn(ExperimentalCompilerApi::class)
|
||||
abstract class Kotlin2ComponentRegistrar : ComponentRegistrar {
|
||||
override val supportsK2: Boolean
|
||||
get() = true
|
||||
|
||||
private var project: MockProject? = null
|
||||
|
||||
override fun registerProjectComponents(
|
||||
project: MockProject,
|
||||
configuration: CompilerConfiguration
|
||||
) {
|
||||
this.project = project
|
||||
doRegisterExtensions(configuration)
|
||||
}
|
||||
|
||||
abstract fun doRegisterExtensions(configuration: CompilerConfiguration)
|
||||
|
||||
fun registerExtractorExtension(extension: IrGenerationExtension) {
|
||||
val p = project ?: throw IllegalStateException("registerExtractorExtension called before registerProjectComponents")
|
||||
// Register with LoadingOrder.LAST to ensure the extractor runs after other
|
||||
// IR generation plugins (like kotlinx.serialization) have generated their code.
|
||||
val extensionPoint = p.extensionArea.getExtensionPoint(IrGenerationExtension.extensionPointName)
|
||||
extensionPoint.registerExtension(extension, LoadingOrder.LAST, p)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
@file:Suppress("DEPRECATION")
|
||||
|
||||
package com.github.codeql.utils.versions
|
||||
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
|
||||
import org.jetbrains.kotlin.ir.expressions.IrAnnotation
|
||||
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrAnnotationImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.fromSymbolOwner
|
||||
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.addAnnotations
|
||||
|
||||
/**
|
||||
* Compatibility accessors for pre-2.4.0 API patterns.
|
||||
* In 2.4.0, valueParameters/extensionReceiverParameter/extensionReceiver/
|
||||
* getValueArgument/putValueArgument/valueArgumentsCount/typeArgumentsCount/getTypeArgument
|
||||
* have been removed. This file provides the 2.4.0 implementations.
|
||||
*/
|
||||
|
||||
// IrFunction: valueParameters -> parameters filtered to Regular kind
|
||||
val IrFunction.codeQlValueParameters: List<IrValueParameter>
|
||||
get() = parameters.filter { it.kind == org.jetbrains.kotlin.ir.declarations.IrParameterKind.Regular }
|
||||
|
||||
// IrFunction: extensionReceiverParameter
|
||||
val IrFunction.codeQlExtensionReceiverParameter: IrValueParameter?
|
||||
get() = parameters.firstOrNull { it.kind == org.jetbrains.kotlin.ir.declarations.IrParameterKind.ExtensionReceiver }
|
||||
|
||||
// Helper: get the offset of value arguments in the arguments list
|
||||
private fun IrMemberAccessExpression<*>.valueArgumentOffset(): Int {
|
||||
val owner = symbol.owner as? IrFunction ?: return 0
|
||||
return owner.parameters.count { it.kind != org.jetbrains.kotlin.ir.declarations.IrParameterKind.Regular }
|
||||
}
|
||||
|
||||
// IrMemberAccessExpression: valueArgumentsCount
|
||||
// In 2.4.0, arguments[] includes dispatch/extension receivers before regular params
|
||||
val IrMemberAccessExpression<*>.codeQlValueArgumentsCount: Int
|
||||
get() = arguments.size - valueArgumentOffset()
|
||||
|
||||
// IrMemberAccessExpression: getValueArgument
|
||||
// In 2.4.0, arguments[] includes dispatch/extension receivers before regular params
|
||||
fun IrMemberAccessExpression<*>.codeQlGetValueArgument(index: Int): IrExpression? = arguments[index + valueArgumentOffset()]
|
||||
|
||||
// IrMemberAccessExpression: putValueArgument
|
||||
// In 2.4.0, arguments[] includes dispatch/extension receivers before regular params
|
||||
fun IrMemberAccessExpression<*>.codeQlPutValueArgument(index: Int, value: IrExpression?) {
|
||||
arguments[index + valueArgumentOffset()] = value
|
||||
}
|
||||
|
||||
// Re-add accessor for the extensionReceiver property removed in Kotlin 2.4.0.
|
||||
val IrMemberAccessExpression<*>.codeQlExtensionReceiver: IrExpression?
|
||||
get() {
|
||||
val erp = extensionReceiverParameterIndex() ?: return null
|
||||
return arguments[erp]
|
||||
}
|
||||
|
||||
// Find the argument index corresponding to the extension receiver parameter.
|
||||
// Calls and function references expose an IrFunction owner directly; property
|
||||
// references need to look through their getter or setter.
|
||||
private fun IrMemberAccessExpression<*>.extensionReceiverParameterIndex(): Int? {
|
||||
// Direct function owner (IrCall, IrFunctionReference, etc.)
|
||||
(symbol.owner as? IrFunction)?.codeQlExtensionReceiverParameter?.let {
|
||||
return it.indexInParameters
|
||||
}
|
||||
// Property reference: look at getter or setter function
|
||||
(this as? org.jetbrains.kotlin.ir.expressions.IrPropertyReference)?.let { propRef ->
|
||||
propRef.getter?.owner?.codeQlExtensionReceiverParameter?.let {
|
||||
return it.indexInParameters
|
||||
}
|
||||
propRef.setter?.owner?.codeQlExtensionReceiverParameter?.let {
|
||||
return it.indexInParameters
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
// IrMemberAccessExpression: typeArgumentsCount
|
||||
val IrMemberAccessExpression<*>.codeQlTypeArgumentsCount: Int
|
||||
get() = typeArguments.size
|
||||
|
||||
// IrMemberAccessExpression: getTypeArgument
|
||||
fun IrMemberAccessExpression<*>.codeQlGetTypeArgument(index: Int): IrType? = typeArguments[index]
|
||||
|
||||
// addAnnotations compat: in 2.4.0, addAnnotations expects List<IrAnnotation>
|
||||
// IrConstructorCall implements IrAnnotation in 2.4.0, so filterIsInstance is identity
|
||||
fun IrType.codeQlAddAnnotations(annotations: List<IrConstructorCall>): IrType =
|
||||
addAnnotations(annotations.filterIsInstance<IrAnnotation>())
|
||||
|
||||
// IrMutableAnnotationContainer.annotations setter: in 2.4.0, expects List<IrAnnotation>
|
||||
fun codeQlSetAnnotations(container: org.jetbrains.kotlin.ir.declarations.IrMutableAnnotationContainer, annotations: List<IrConstructorCall>) {
|
||||
container.annotations = annotations.filterIsInstance<IrAnnotation>()
|
||||
}
|
||||
|
||||
// IrFunction: set dispatch receiver parameter
|
||||
// In 2.4.0, dispatchReceiverParameter is val; modify the parameters list directly.
|
||||
fun IrFunction.codeQlSetDispatchReceiverParameter(param: IrValueParameter?) {
|
||||
val existing = parameters.indexOfFirst { it.kind == org.jetbrains.kotlin.ir.declarations.IrParameterKind.DispatchReceiver }
|
||||
val mutableParams = parameters.toMutableList()
|
||||
if (existing >= 0) {
|
||||
if (param != null) {
|
||||
mutableParams[existing] = param
|
||||
} else {
|
||||
mutableParams.removeAt(existing)
|
||||
}
|
||||
} else if (param != null) {
|
||||
param.kind = org.jetbrains.kotlin.ir.declarations.IrParameterKind.DispatchReceiver
|
||||
mutableParams.add(0, param)
|
||||
}
|
||||
parameters = mutableParams
|
||||
}
|
||||
|
||||
// In 2.4.0, annotation lists require IrAnnotation instances.
|
||||
// Use IrAnnotationImpl.fromSymbolOwner instead of IrConstructorCallImpl.fromSymbolOwner.
|
||||
fun codeQlAnnotationFromSymbolOwner(
|
||||
startOffset: Int, endOffset: Int, type: IrType, symbol: IrConstructorSymbol, typeArgumentsCount: Int
|
||||
): IrConstructorCall =
|
||||
IrAnnotationImpl.fromSymbolOwner(startOffset, endOffset, type, symbol, typeArgumentsCount)
|
||||
|
||||
fun codeQlAnnotationFromSymbolOwner(type: IrType, symbol: IrConstructorSymbol): IrConstructorCall =
|
||||
IrAnnotationImpl.fromSymbolOwner(type, symbol)
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.github.codeql
|
||||
|
||||
import com.intellij.mock.MockProject
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
|
||||
import org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar
|
||||
import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration
|
||||
|
||||
@OptIn(ExperimentalCompilerApi::class)
|
||||
@Suppress("DEPRECATION", "DEPRECATION_ERROR")
|
||||
abstract class Kotlin2ComponentRegistrar :
|
||||
CompilerPluginRegistrar(),
|
||||
org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar {
|
||||
override val supportsK2: Boolean
|
||||
get() = true
|
||||
|
||||
override val pluginId: String
|
||||
get() = "kotlin-extractor"
|
||||
|
||||
// ComponentRegistrar implementation (legacy path, still called by Kotlin compiler)
|
||||
override fun registerProjectComponents(
|
||||
project: MockProject,
|
||||
configuration: CompilerConfiguration
|
||||
) {
|
||||
// Registration is done via ExtensionStorage in Kotlin 2.4+.
|
||||
// This legacy entry point remains for compatibility with service discovery.
|
||||
}
|
||||
|
||||
private var extensionStorage: CompilerPluginRegistrar.ExtensionStorage? = null
|
||||
|
||||
override fun ExtensionStorage.registerExtensions(configuration: CompilerConfiguration) {
|
||||
this@Kotlin2ComponentRegistrar.extensionStorage = this
|
||||
doRegisterExtensions(configuration)
|
||||
}
|
||||
|
||||
abstract fun doRegisterExtensions(configuration: CompilerConfiguration)
|
||||
|
||||
protected fun registerExtractorExtension(extension: IrGenerationExtension) {
|
||||
val storage = extensionStorage
|
||||
?: throw IllegalStateException("registerExtractorExtension called before registerExtensions")
|
||||
with(storage) {
|
||||
IrGenerationExtension.registerExtension(extension)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.github.codeql.utils.versions
|
||||
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.IrParameterKind
|
||||
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
|
||||
|
||||
fun parameterIndexExcludingReceivers(vp: IrValueParameter): Int {
|
||||
val offset =
|
||||
(vp.parent as? IrFunction)?.let { f ->
|
||||
f.parameters.count { it.kind == IrParameterKind.DispatchReceiver || it.kind == IrParameterKind.ExtensionReceiver || it.kind == IrParameterKind.Context }
|
||||
} ?: 0
|
||||
return vp.indexInParameters - offset
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
com.github.codeql.KotlinExtractorComponentRegistrar
|
||||
@@ -11,6 +11,7 @@ VERSIONS = [
|
||||
"2.2.20-Beta2",
|
||||
"2.3.0",
|
||||
"2.3.20",
|
||||
"2.4.0",
|
||||
]
|
||||
|
||||
def _version_to_tuple(v):
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"markdownMessage": "The Kotlin version installed (`999.999.999`) is too recent for this version of CodeQL. Install a version lower than 2.3.30.",
|
||||
"markdownMessage": "The Kotlin version installed (`999.999.999`) is too recent for this version of CodeQL. Install a version lower than 2.4.10.",
|
||||
"severity": "error",
|
||||
"source": {
|
||||
"extractorName": "java",
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import pathlib
|
||||
|
||||
|
||||
def test(codeql, java_full):
|
||||
def test(codeql, java_full, kotlinc_2_3_20):
|
||||
java_srcs = " ".join([str(s) for s in pathlib.Path().glob("*.java")])
|
||||
codeql.database.create(
|
||||
command=[
|
||||
f"javac {java_srcs} -d build",
|
||||
"kotlinc -language-version 1.9 user.kt -cp build",
|
||||
f"{kotlinc_2_3_20} -language-version 1.9 user.kt -cp build",
|
||||
]
|
||||
)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import commands
|
||||
|
||||
|
||||
def test(codeql, java_full):
|
||||
commands.run("kotlinc -language-version 1.9 test.kt -d lib")
|
||||
codeql.database.create(command="kotlinc -language-version 1.9 user.kt -cp lib")
|
||||
def test(codeql, java_full, kotlinc_2_3_20):
|
||||
commands.run(f"{kotlinc_2_3_20} -language-version 1.9 test.kt -d lib")
|
||||
codeql.database.create(command=f"{kotlinc_2_3_20} -language-version 1.9 user.kt -cp lib")
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
def test(codeql, java_full):
|
||||
codeql.database.create(command="kotlinc -J-Xmx2G -language-version 1.9 SomeClass.kt")
|
||||
def test(codeql, java_full, kotlinc_2_3_20):
|
||||
codeql.database.create(command=f"{kotlinc_2_3_20} -J-Xmx2G -language-version 1.9 SomeClass.kt")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import commands
|
||||
|
||||
|
||||
def test(codeql, java_full):
|
||||
commands.run("kotlinc -language-version 1.9 A.kt")
|
||||
codeql.database.create(command="kotlinc -cp . -language-version 1.9 B.kt C.kt")
|
||||
def test(codeql, java_full, kotlinc_2_3_20):
|
||||
commands.run(f"{kotlinc_2_3_20} -language-version 1.9 A.kt")
|
||||
codeql.database.create(command=f"{kotlinc_2_3_20} -cp . -language-version 1.9 B.kt C.kt")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import commands
|
||||
|
||||
|
||||
def test(codeql, java_full):
|
||||
def test(codeql, java_full, kotlinc_2_3_20):
|
||||
commands.run(["javac", "Test.java", "-d", "bin"])
|
||||
codeql.database.create(command="kotlinc -language-version 1.9 user.kt -cp bin")
|
||||
codeql.database.create(command=f"{kotlinc_2_3_20} -language-version 1.9 user.kt -cp bin")
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import commands
|
||||
|
||||
|
||||
def test(codeql, java_full):
|
||||
def test(codeql, java_full, kotlinc_2_3_20):
|
||||
# Compile the JavaDefns2 copy outside tracing, to make sure the Kotlin view of it matches the Java view seen by the traced javac compilation of JavaDefns.java below.
|
||||
commands.run(["javac", "JavaDefns2.java"])
|
||||
codeql.database.create(
|
||||
command=[
|
||||
"kotlinc kotlindefns.kt",
|
||||
f"{kotlinc_2_3_20} kotlindefns.kt",
|
||||
"javac JavaUser.java JavaDefns.java -cp .",
|
||||
"kotlinc -language-version 1.9 -cp . kotlinuser.kt",
|
||||
f"{kotlinc_2_3_20} -language-version 1.9 -cp . kotlinuser.kt",
|
||||
]
|
||||
)
|
||||
|
||||
4
java/ql/lib/change-notes/2026-06-04-kotlin-2.4.0.md
Normal file
4
java/ql/lib/change-notes/2026-06-04-kotlin-2.4.0.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: feature
|
||||
---
|
||||
* Kotlin 2.4.0 can now be analysed.
|
||||
@@ -213,9 +213,11 @@ class ExprWithPointsTo extends Expr {
|
||||
* Gets what this expression might "refer-to" in the given `context`.
|
||||
*/
|
||||
predicate refersTo(Context context, Object obj, ClassObject cls, AstNode origin) {
|
||||
this.getAFlowNode()
|
||||
.(ControlFlowNodeWithPointsTo)
|
||||
.refersTo(context, obj, cls, origin.getAFlowNode())
|
||||
exists(ControlFlowNode this_, ControlFlowNode origin_ |
|
||||
this_.getNode() = this and origin_.getNode() = origin
|
||||
|
|
||||
this_.(ControlFlowNodeWithPointsTo).refersTo(context, obj, cls, origin_)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -226,7 +228,11 @@ class ExprWithPointsTo extends Expr {
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate refersTo(Object obj, AstNode origin) {
|
||||
this.getAFlowNode().(ControlFlowNodeWithPointsTo).refersTo(obj, origin.getAFlowNode())
|
||||
exists(ControlFlowNode this_, ControlFlowNode origin_ |
|
||||
this_.getNode() = this and origin_.getNode() = origin
|
||||
|
|
||||
this_.(ControlFlowNodeWithPointsTo).refersTo(obj, origin_)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -240,16 +246,22 @@ class ExprWithPointsTo extends Expr {
|
||||
* in the given `context`.
|
||||
*/
|
||||
predicate pointsTo(Context context, Value value, AstNode origin) {
|
||||
this.getAFlowNode()
|
||||
.(ControlFlowNodeWithPointsTo)
|
||||
.pointsTo(context, value, origin.getAFlowNode())
|
||||
exists(ControlFlowNode this_, ControlFlowNode origin_ |
|
||||
this_.getNode() = this and origin_.getNode() = origin
|
||||
|
|
||||
this_.(ControlFlowNodeWithPointsTo).pointsTo(context, value, origin_)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this expression might "point-to" to `value` which is from `origin`.
|
||||
*/
|
||||
predicate pointsTo(Value value, AstNode origin) {
|
||||
this.getAFlowNode().(ControlFlowNodeWithPointsTo).pointsTo(value, origin.getAFlowNode())
|
||||
exists(ControlFlowNode this_, ControlFlowNode origin_ |
|
||||
this_.getNode() = this and origin_.getNode() = origin
|
||||
|
|
||||
this_.(ControlFlowNodeWithPointsTo).pointsTo(value, origin_)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -475,7 +487,10 @@ class FunctionMetricsWithPointsTo extends FunctionMetrics {
|
||||
not non_coupling_method(result) and
|
||||
exists(Call call | call.getScope() = this |
|
||||
exists(FunctionObject callee | callee.getFunction() = result |
|
||||
call.getAFlowNode().getFunction().(ControlFlowNodeWithPointsTo).refersTo(callee)
|
||||
exists(CallNode call_ |
|
||||
call_.getNode() = call and
|
||||
call_.getFunction().(ControlFlowNodeWithPointsTo).refersTo(callee)
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(Attribute a | call.getFunc() = a |
|
||||
|
||||
@@ -64,7 +64,7 @@ private predicate jump_to_defn(ControlFlowNode use, Definition defn) {
|
||||
private predicate preferred_jump_to_defn(Expr use, Definition def) {
|
||||
not use instanceof ClassExpr and
|
||||
not use instanceof FunctionExpr and
|
||||
jump_to_defn(use.getAFlowNode(), def)
|
||||
exists(ControlFlowNode useNode | useNode.getNode() = use | jump_to_defn(useNode, def))
|
||||
}
|
||||
|
||||
private predicate unique_jump_to_defn(Expr use, Definition def) {
|
||||
@@ -452,7 +452,7 @@ private predicate self_parameter_jump_to_defn_attribute(
|
||||
* This exists primarily for testing use `getPreferredDefinition()` instead.
|
||||
*/
|
||||
Definition getADefinition(Expr use) {
|
||||
jump_to_defn(use.getAFlowNode(), result) and
|
||||
exists(ControlFlowNode useNode | useNode.getNode() = use | jump_to_defn(useNode, result)) and
|
||||
not use instanceof Call and
|
||||
not use.isArtificial() and
|
||||
// Not the use itself
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
category: deprecated
|
||||
---
|
||||
* The `AstNode.getAFlowNode()` predicate has been deprecated. Use `ControlFlowNode.getNode()` from the other direction instead: replace `e.getAFlowNode() = n` with `n.getNode() = e`. This is a preparatory step towards migrating the dataflow library off the legacy CFG; it has no semantic effect.
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: deprecated
|
||||
---
|
||||
* The `Function.getAReturnValueFlowNode()` predicate has been deprecated. Bind a `Return` node explicitly instead — `exists(Return ret | ret.getScope() = f and n.getNode() = ret.getValue())`. This is a preparatory step towards migrating the dataflow library off the legacy CFG; it has no semantic effect.
|
||||
@@ -17,12 +17,17 @@ abstract class AstNode extends AstNode_ {
|
||||
abstract Scope getScope();
|
||||
|
||||
/**
|
||||
* DEPRECATED: use `ControlFlowNode.getNode()` from the other direction instead;
|
||||
* that is, replace `e.getAFlowNode() = n` with `n.getNode() = e`. This API is
|
||||
* being removed to untangle the AST and CFG hierarchies in preparation for
|
||||
* migrating the dataflow library off the legacy CFG.
|
||||
*
|
||||
* Gets a flow node corresponding directly to this node.
|
||||
* NOTE: For some statements and other purely syntactic elements,
|
||||
* there may not be a `ControlFlowNode`
|
||||
* there may not be a `ControlFlowNode`.
|
||||
*/
|
||||
cached
|
||||
ControlFlowNode getAFlowNode() {
|
||||
deprecated ControlFlowNode getAFlowNode() {
|
||||
Stages::AST::ref() and
|
||||
py_flow_bb_node(result, this, _, _)
|
||||
}
|
||||
|
||||
@@ -28,7 +28,9 @@ class Expr extends Expr_, AstNode {
|
||||
/** Whether this expression may have a side effect (as determined purely from its syntax) */
|
||||
predicate hasSideEffects() {
|
||||
/* If an exception raised by this expression handled, count that as a side effect */
|
||||
this.getAFlowNode().getASuccessor().getNode() instanceof ExceptStmt
|
||||
exists(ControlFlowNode n | n.getNode() = this |
|
||||
n.getASuccessor().getNode() instanceof ExceptStmt
|
||||
)
|
||||
or
|
||||
this.getASubExpression().hasSideEffects()
|
||||
}
|
||||
@@ -68,7 +70,7 @@ class Attribute extends Attribute_ {
|
||||
/* syntax: Expr.name */
|
||||
override Expr getASubExpression() { result = this.getObject() }
|
||||
|
||||
override AttrNode getAFlowNode() { result = super.getAFlowNode() }
|
||||
deprecated override AttrNode getAFlowNode() { result = super.getAFlowNode() }
|
||||
|
||||
/** Gets the name of this attribute. That is the `name` in `obj.name` */
|
||||
string getName() { result = Attribute_.super.getAttr() }
|
||||
@@ -97,7 +99,7 @@ class Subscript extends Subscript_ {
|
||||
|
||||
Expr getObject() { result = Subscript_.super.getValue() }
|
||||
|
||||
override SubscriptNode getAFlowNode() { result = super.getAFlowNode() }
|
||||
deprecated override SubscriptNode getAFlowNode() { result = super.getAFlowNode() }
|
||||
}
|
||||
|
||||
/** A call expression, such as `func(...)` */
|
||||
@@ -113,7 +115,7 @@ class Call extends Call_ {
|
||||
|
||||
override string toString() { result = this.getFunc().toString() + "()" }
|
||||
|
||||
override CallNode getAFlowNode() { result = super.getAFlowNode() }
|
||||
deprecated override CallNode getAFlowNode() { result = super.getAFlowNode() }
|
||||
|
||||
/** Gets a tuple (*) argument of this call. */
|
||||
Expr getStarargs() { result = this.getAPositionalArg().(Starred).getValue() }
|
||||
@@ -201,7 +203,7 @@ class IfExp extends IfExp_ {
|
||||
result = this.getTest() or result = this.getBody() or result = this.getOrelse()
|
||||
}
|
||||
|
||||
override IfExprNode getAFlowNode() { result = super.getAFlowNode() }
|
||||
deprecated override IfExprNode getAFlowNode() { result = super.getAFlowNode() }
|
||||
}
|
||||
|
||||
/** A starred expression, such as the `*rest` in the assignment `first, *rest = seq` */
|
||||
@@ -411,7 +413,7 @@ class PlaceHolder extends PlaceHolder_ {
|
||||
|
||||
override string toString() { result = "$" + this.getId() }
|
||||
|
||||
override NameNode getAFlowNode() { result = super.getAFlowNode() }
|
||||
deprecated override NameNode getAFlowNode() { result = super.getAFlowNode() }
|
||||
}
|
||||
|
||||
/** A tuple expression such as `( 1, 3, 5, 7, 9 )` */
|
||||
@@ -478,7 +480,7 @@ class Name extends Name_ {
|
||||
|
||||
override string toString() { result = this.getId() }
|
||||
|
||||
override NameNode getAFlowNode() { result = super.getAFlowNode() }
|
||||
deprecated override NameNode getAFlowNode() { result = super.getAFlowNode() }
|
||||
|
||||
override predicate isArtificial() {
|
||||
/* Artificial variable names in comprehensions all start with "." */
|
||||
@@ -585,7 +587,7 @@ abstract class NameConstant extends Name, ImmutableLiteral {
|
||||
|
||||
override predicate isConstant() { any() }
|
||||
|
||||
override NameConstantNode getAFlowNode() { result = Name.super.getAFlowNode() }
|
||||
deprecated override NameConstantNode getAFlowNode() { result = Name.super.getAFlowNode() }
|
||||
|
||||
override predicate isArtificial() { none() }
|
||||
}
|
||||
|
||||
@@ -555,27 +555,27 @@ class DefinitionNode extends ControlFlowNode {
|
||||
cached
|
||||
DefinitionNode() {
|
||||
Stages::AST::ref() and
|
||||
exists(Assign a | a.getATarget().getAFlowNode() = this)
|
||||
exists(Assign a | this.getNode() = a.getATarget())
|
||||
or
|
||||
exists(AssignExpr a | a.getTarget().getAFlowNode() = this)
|
||||
exists(AssignExpr a | this.getNode() = a.getTarget())
|
||||
or
|
||||
exists(AnnAssign a | a.getTarget().getAFlowNode() = this and exists(a.getValue()))
|
||||
exists(AnnAssign a | this.getNode() = a.getTarget() and exists(a.getValue()))
|
||||
or
|
||||
exists(Alias a | a.getAsname().getAFlowNode() = this)
|
||||
exists(Alias a | this.getNode() = a.getAsname())
|
||||
or
|
||||
augstore(_, this)
|
||||
or
|
||||
// `x, y = 1, 2` where LHS is a combination of list or tuples
|
||||
exists(Assign a | list_or_tuple_nested_element(a.getATarget()).getAFlowNode() = this)
|
||||
exists(Assign a | this.getNode() = list_or_tuple_nested_element(a.getATarget()))
|
||||
or
|
||||
exists(For for | for.getTarget().getAFlowNode() = this)
|
||||
exists(For for | this.getNode() = for.getTarget())
|
||||
or
|
||||
exists(Parameter param | this = param.asName().getAFlowNode() and exists(param.getDefault()))
|
||||
exists(Parameter param | this.getNode() = param.asName() and exists(param.getDefault()))
|
||||
}
|
||||
|
||||
/** flow node corresponding to the value assigned for the definition corresponding to this flow node */
|
||||
ControlFlowNode getValue() {
|
||||
result = assigned_value(this.getNode()).getAFlowNode() and
|
||||
result.getNode() = assigned_value(this.getNode()) and
|
||||
(
|
||||
result.getBasicBlock().dominates(this.getBasicBlock())
|
||||
or
|
||||
@@ -584,7 +584,7 @@ class DefinitionNode extends ControlFlowNode {
|
||||
// since the default value for a parameter is evaluated in the same basic block as
|
||||
// the function definition, but the parameter belongs to the basic block of the function,
|
||||
// there is no dominance relationship between the two.
|
||||
exists(Parameter param | this = param.asName().getAFlowNode())
|
||||
exists(Parameter param | this.getNode() = param.asName())
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -901,7 +901,7 @@ class ExceptFlowNode extends ControlFlowNode {
|
||||
exists(ExceptStmt ex |
|
||||
this.getBasicBlock().dominates(result.getBasicBlock()) and
|
||||
ex = this.getNode() and
|
||||
result = ex.getType().getAFlowNode()
|
||||
result.getNode() = ex.getType()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -913,7 +913,7 @@ class ExceptFlowNode extends ControlFlowNode {
|
||||
exists(ExceptStmt ex |
|
||||
this.getBasicBlock().dominates(result.getBasicBlock()) and
|
||||
ex = this.getNode() and
|
||||
result = ex.getName().getAFlowNode()
|
||||
result.getNode() = ex.getName()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -928,7 +928,7 @@ class ExceptGroupFlowNode extends ControlFlowNode {
|
||||
*/
|
||||
ControlFlowNode getType() {
|
||||
this.getBasicBlock().dominates(result.getBasicBlock()) and
|
||||
result = this.getNode().(ExceptGroupStmt).getType().getAFlowNode()
|
||||
result.getNode() = this.getNode().(ExceptGroupStmt).getType()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -937,7 +937,7 @@ class ExceptGroupFlowNode extends ControlFlowNode {
|
||||
*/
|
||||
ControlFlowNode getName() {
|
||||
this.getBasicBlock().dominates(result.getBasicBlock()) and
|
||||
result = this.getNode().(ExceptGroupStmt).getName().getAFlowNode()
|
||||
result.getNode() = this.getNode().(ExceptGroupStmt).getName()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -153,8 +153,16 @@ class Function extends Function_, Scope, AstNode {
|
||||
|
||||
override predicate contains(AstNode inner) { Scope.super.contains(inner) }
|
||||
|
||||
/** Gets a control flow node for a return value of this function */
|
||||
ControlFlowNode getAReturnValueFlowNode() {
|
||||
/**
|
||||
* DEPRECATED: bind a `Return` node explicitly instead, e.g.
|
||||
* `exists(Return ret | ret.getScope() = this and n.getNode() = ret.getValue())`.
|
||||
* This API is being phased out together with `AstNode.getAFlowNode()` to
|
||||
* untangle the AST and CFG hierarchies in preparation for migrating the
|
||||
* dataflow library off the legacy CFG.
|
||||
*
|
||||
* Gets a control flow node for a return value of this function.
|
||||
*/
|
||||
deprecated ControlFlowNode getAReturnValueFlowNode() {
|
||||
exists(Return ret |
|
||||
ret.getScope() = this and
|
||||
ret.getValue() = result.getNode()
|
||||
|
||||
@@ -163,7 +163,7 @@ class ImportMember extends ImportMember_ {
|
||||
result = this.getModule().(ImportExpr).getImportedModuleName() + "." + this.getName()
|
||||
}
|
||||
|
||||
override ImportMemberNode getAFlowNode() { result = super.getAFlowNode() }
|
||||
deprecated override ImportMemberNode getAFlowNode() { result = super.getAFlowNode() }
|
||||
}
|
||||
|
||||
/** An import statement */
|
||||
|
||||
@@ -46,20 +46,23 @@ class SelfAttributeRead extends SelfAttribute {
|
||||
}
|
||||
|
||||
predicate guardedByHasattr() {
|
||||
exists(Variable var, ControlFlowNode n |
|
||||
var.getAUse() = this.getObject().getAFlowNode() and
|
||||
exists(Variable var, ControlFlowNode n, ControlFlowNode this_, ControlFlowNode obj_ |
|
||||
this_.getNode() = this and obj_.getNode() = this.getObject()
|
||||
|
|
||||
var.getAUse() = obj_ and
|
||||
hasattr(n, var.getAUse(), this.getName()) and
|
||||
n.strictlyDominates(this.getAFlowNode())
|
||||
n.strictlyDominates(this_)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
predicate locallyDefined() {
|
||||
exists(SelfAttributeStore store |
|
||||
this.getName() = store.getName() and
|
||||
this.getScope() = store.getScope()
|
||||
exists(SelfAttributeStore store, ControlFlowNode store_, ControlFlowNode this_ |
|
||||
store_.getNode() = store and this_.getNode() = this
|
||||
|
|
||||
store.getAFlowNode().strictlyDominates(this.getAFlowNode())
|
||||
this.getName() = store.getName() and
|
||||
this.getScope() = store.getScope() and
|
||||
store_.strictlyDominates(this_)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,24 +5,30 @@ private import semmle.python.dataflow.new.DataFlow
|
||||
|
||||
private predicate constCompare(DataFlow::GuardNode g, ControlFlowNode node, boolean branch) {
|
||||
exists(CompareNode cn | cn = g |
|
||||
exists(ImmutableLiteral const, Cmpop op |
|
||||
op = any(Eq eq) and branch = true
|
||||
or
|
||||
op = any(NotEq ne) and branch = false
|
||||
exists(ImmutableLiteral const, Cmpop op, ControlFlowNode c |
|
||||
c.getNode() = const and
|
||||
(
|
||||
op = any(Eq eq) and branch = true
|
||||
or
|
||||
op = any(NotEq ne) and branch = false
|
||||
)
|
||||
|
|
||||
cn.operands(const.getAFlowNode(), op, node)
|
||||
cn.operands(c, op, node)
|
||||
or
|
||||
cn.operands(node, op, const.getAFlowNode())
|
||||
cn.operands(node, op, c)
|
||||
)
|
||||
or
|
||||
exists(NameConstant const, Cmpop op |
|
||||
op = any(Is is_) and branch = true
|
||||
or
|
||||
op = any(IsNot isn) and branch = false
|
||||
exists(NameConstant const, Cmpop op, ControlFlowNode c |
|
||||
c.getNode() = const and
|
||||
(
|
||||
op = any(Is is_) and branch = true
|
||||
or
|
||||
op = any(IsNot isn) and branch = false
|
||||
)
|
||||
|
|
||||
cn.operands(const.getAFlowNode(), op, node)
|
||||
cn.operands(c, op, node)
|
||||
or
|
||||
cn.operands(node, op, const.getAFlowNode())
|
||||
cn.operands(node, op, c)
|
||||
)
|
||||
or
|
||||
exists(IterableNode const_iterable, Cmpop op |
|
||||
|
||||
@@ -228,7 +228,7 @@ private class ClassDefinitionAsAttrWrite extends AttrWrite, CfgNode {
|
||||
|
||||
override Node getValue() { result.asCfgNode() = node.getValue() }
|
||||
|
||||
override Node getObject() { result.asCfgNode() = cls.getAFlowNode() }
|
||||
override Node getObject() { result.asCfgNode().getNode() = cls }
|
||||
|
||||
override ExprNode getAttributeNameExpr() { none() }
|
||||
|
||||
|
||||
@@ -1913,8 +1913,8 @@ abstract class ReturnNode extends Node {
|
||||
class ExtractedReturnNode extends ReturnNode, CfgNode {
|
||||
// See `TaintTrackingImplementation::returnFlowStep`
|
||||
ExtractedReturnNode() {
|
||||
node = any(Return ret).getValue().getAFlowNode() or
|
||||
node = any(Yield yield).getAFlowNode()
|
||||
node.getNode() = any(Return ret).getValue() or
|
||||
node.getNode() = any(Yield yield)
|
||||
}
|
||||
|
||||
override ReturnKind getKind() { any() }
|
||||
@@ -1932,7 +1932,7 @@ class ExtractedReturnNode extends ReturnNode, CfgNode {
|
||||
class YieldNodeInContextManagerFunction extends ReturnNode, CfgNode {
|
||||
YieldNodeInContextManagerFunction() {
|
||||
hasContextmanagerDecorator(node.getScope()) and
|
||||
node = any(Yield yield).getValue().getAFlowNode()
|
||||
node.getNode() = any(Yield yield).getValue()
|
||||
}
|
||||
|
||||
override ReturnKind getKind() { any() }
|
||||
|
||||
@@ -185,8 +185,8 @@ private predicate synthDictSplatArgumentNodeStoreStep(
|
||||
*/
|
||||
predicate yieldStoreStep(Node nodeFrom, Content c, Node nodeTo) {
|
||||
exists(Yield yield |
|
||||
nodeTo.asCfgNode() = yield.getAFlowNode() and
|
||||
nodeFrom.asCfgNode() = yield.getValue().getAFlowNode() and
|
||||
nodeTo.asCfgNode().getNode() = yield and
|
||||
nodeFrom.asCfgNode().getNode() = yield.getValue() and
|
||||
// TODO: Consider if this will also need to transfer dictionary content
|
||||
// once dictionary comprehensions are supported.
|
||||
c instanceof ListElementContent
|
||||
|
||||
@@ -485,7 +485,7 @@ class ModuleVariableNode extends Node, TModuleVariableNode {
|
||||
|
||||
/** Gets a node that reads this variable, excluding reads that happen through `from ... import *`. */
|
||||
Node getALocalRead() {
|
||||
result.asCfgNode() = var.getALoad().getAFlowNode() and
|
||||
result.asCfgNode().getNode() = var.getALoad() and
|
||||
not result.getScope() = mod
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,19 @@ private import semmle.python.dataflow.new.DataFlow
|
||||
private import semmle.python.dataflow.new.internal.ImportStar
|
||||
private import semmle.python.dataflow.new.TypeTracking
|
||||
private import semmle.python.dataflow.new.internal.DataFlowPrivate
|
||||
private import semmle.python.essa.SsaDefinitions
|
||||
|
||||
/**
|
||||
* Holds if `init` is a package's `__init__.py` and `var` is a global variable in
|
||||
* `init` whose name matches a submodule of the package.
|
||||
*
|
||||
* Inlined from `SsaSource::init_module_submodule_defn` to avoid pulling
|
||||
* `semmle.python.essa.SsaDefinitions` into the new dataflow stack.
|
||||
*/
|
||||
private predicate initModuleSubmoduleDefn(GlobalVariable var, Module init) {
|
||||
init.isPackageInit() and
|
||||
exists(init.getPackage().getSubModule(var.getId())) and
|
||||
var.getScope() = init
|
||||
}
|
||||
|
||||
/**
|
||||
* Python modules and the way imports are resolved are... complicated. Here's a crash course in how
|
||||
@@ -326,7 +338,7 @@ module ImportResolution {
|
||||
// imported yet.
|
||||
exists(string submodule, Module package, EssaVariable var |
|
||||
submodule = var.getName() and
|
||||
SsaSource::init_module_submodule_defn(var.getSourceVariable(), package.getEntryNode()) and
|
||||
initModuleSubmoduleDefn(var.getSourceVariable(), package) and
|
||||
m = getModuleFromName(package.getPackageName() + "." + submodule) and
|
||||
result.asCfgNode() = var.getDefinition().(EssaNodeDefinition).getDefiningNode()
|
||||
)
|
||||
|
||||
@@ -94,8 +94,10 @@ private module SummaryTypeTrackerInput implements SummaryTypeTracker::Input {
|
||||
Node returnOf(Node callable, SummaryComponent return) {
|
||||
return = FlowSummaryImpl::Private::SummaryComponent::return() and
|
||||
// `result` should be the return value of a callable expression (lambda or function) referenced by `callable`
|
||||
result.asCfgNode() =
|
||||
callable.getALocalSource().asExpr().(CallableExpr).getInnerScope().getAReturnValueFlowNode()
|
||||
exists(Return ret |
|
||||
ret.getScope() = callable.getALocalSource().asExpr().(CallableExpr).getInnerScope() and
|
||||
result.asCfgNode().getNode() = ret.getValue()
|
||||
)
|
||||
}
|
||||
|
||||
// Relating callables to nodes
|
||||
|
||||
@@ -61,7 +61,7 @@ private module CaptureInput implements Shared::InputSig<Location, Cfg::BasicBloc
|
||||
class VariableWrite extends ControlFlowNode {
|
||||
CapturedVariable v;
|
||||
|
||||
VariableWrite() { this = v.getAStore().getAFlowNode().(DefinitionNode).getValue() }
|
||||
VariableWrite() { exists(DefinitionNode d | d.getNode() = v.getAStore() | this = d.getValue()) }
|
||||
|
||||
CapturedVariable getVariable() { result = v }
|
||||
|
||||
@@ -71,7 +71,7 @@ private module CaptureInput implements Shared::InputSig<Location, Cfg::BasicBloc
|
||||
class VariableRead extends Expr {
|
||||
CapturedVariable v;
|
||||
|
||||
VariableRead() { this = v.getALoad().getAFlowNode() }
|
||||
VariableRead() { this.getNode() = v.getALoad() }
|
||||
|
||||
CapturedVariable getVariable() { result = v }
|
||||
}
|
||||
|
||||
@@ -448,8 +448,7 @@ class TaintTrackingImplementation extends string instanceof TaintTracking::Confi
|
||||
context = TNoParam() and
|
||||
src = TTaintTrackingNode_(retval, TNoParam(), path, kind, this) and
|
||||
node.asCfgNode() = call and
|
||||
retval.asCfgNode() =
|
||||
any(Return ret | ret.getScope() = pyfunc.getScope()).getValue().getAFlowNode()
|
||||
retval.asCfgNode().getNode() = any(Return ret | ret.getScope() = pyfunc.getScope()).getValue()
|
||||
) and
|
||||
edgeLabel = "return"
|
||||
}
|
||||
@@ -471,8 +470,7 @@ class TaintTrackingImplementation extends string instanceof TaintTracking::Confi
|
||||
this.callContexts(call, src, pyfunc, context, callee) and
|
||||
retnode = TTaintTrackingNode_(retval, callee, path, kind, this) and
|
||||
node.asCfgNode() = call and
|
||||
retval.asCfgNode() =
|
||||
any(Return ret | ret.getScope() = pyfunc.getScope()).getValue().getAFlowNode()
|
||||
retval.asCfgNode().getNode() = any(Return ret | ret.getScope() = pyfunc.getScope()).getValue()
|
||||
) and
|
||||
edgeLabel = "call"
|
||||
}
|
||||
@@ -716,8 +714,10 @@ private class EssaTaintTracking extends string instanceof TaintTracking::Configu
|
||||
src = TTaintTrackingNode_(srcnode, context, path, srckind, this) and
|
||||
path.noAttribute()
|
||||
|
|
||||
assign.getValue().getAFlowNode() = srcnode.asCfgNode() and
|
||||
depth = iterable_unpacking_descent(assign.getATarget().getAFlowNode(), defn.getDefiningNode()) and
|
||||
srcnode.asCfgNode().getNode() = assign.getValue() and
|
||||
exists(SequenceNode left_parent | left_parent.getNode() = assign.getATarget() |
|
||||
depth = iterable_unpacking_descent(left_parent, defn.getDefiningNode())
|
||||
) and
|
||||
kind = taint_at_depth(srckind, depth)
|
||||
)
|
||||
}
|
||||
@@ -964,7 +964,7 @@ private TaintKind taint_at_depth(SequenceKind parent_kind, int depth) {
|
||||
* - with `left_defn` = `*y`, `left_parent` = `((x, *y), ...)`, result = 1
|
||||
*/
|
||||
int iterable_unpacking_descent(SequenceNode left_parent, ControlFlowNode left_defn) {
|
||||
exists(Assign a | a.getATarget().getASubExpression*().getAFlowNode() = left_parent) and
|
||||
exists(Assign a | left_parent.getNode() = a.getATarget().getASubExpression*()) and
|
||||
left_parent.getAnElement() = left_defn and
|
||||
// Handle `a, *b = some_iterable`
|
||||
if left_defn instanceof StarredNode then result = 0 else result = 1
|
||||
|
||||
@@ -56,7 +56,7 @@ module SsaSource {
|
||||
predicate with_definition(Variable v, ControlFlowNode defn) {
|
||||
exists(With with, Name var |
|
||||
with.getOptionalVars() = var and
|
||||
var.getAFlowNode() = defn
|
||||
defn.getNode() = var
|
||||
|
|
||||
var = v.getAStore()
|
||||
)
|
||||
@@ -67,7 +67,7 @@ module SsaSource {
|
||||
predicate pattern_capture_definition(Variable v, ControlFlowNode defn) {
|
||||
exists(MatchCapturePattern capture, Name var |
|
||||
capture.getVariable() = var and
|
||||
var.getAFlowNode() = defn
|
||||
defn.getNode() = var
|
||||
|
|
||||
var = v.getAStore()
|
||||
)
|
||||
@@ -78,7 +78,7 @@ module SsaSource {
|
||||
predicate pattern_alias_definition(Variable v, ControlFlowNode defn) {
|
||||
exists(MatchAsPattern pattern, Name var |
|
||||
pattern.getAlias() = var and
|
||||
var.getAFlowNode() = defn
|
||||
defn.getNode() = var
|
||||
|
|
||||
var = v.getAStore()
|
||||
)
|
||||
|
||||
@@ -59,7 +59,7 @@ module Bottle {
|
||||
|
||||
override Parameter getARoutedParameter() { none() }
|
||||
|
||||
override Function getARequestHandler() { result.getADecorator().getAFlowNode() = node }
|
||||
override Function getARequestHandler() { node.getNode() = result.getADecorator() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +73,10 @@ module Bottle {
|
||||
/** A response returned by a view callable. */
|
||||
class BottleReturnResponse extends Http::Server::HttpResponse::Range {
|
||||
BottleReturnResponse() {
|
||||
this.asCfgNode() = any(View::ViewCallable vc).getAReturnValueFlowNode()
|
||||
exists(Return ret |
|
||||
ret.getScope() = any(View::ViewCallable vc) and
|
||||
this.asCfgNode().getNode() = ret.getValue()
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getBody() { result = this }
|
||||
|
||||
@@ -2872,7 +2872,10 @@ module PrivateDjango {
|
||||
DataFlow::CfgNode
|
||||
{
|
||||
DjangoRedirectViewGetRedirectUrlReturn() {
|
||||
node = any(GetRedirectUrlFunction f).getAReturnValueFlowNode()
|
||||
exists(Return ret |
|
||||
ret.getScope() = any(GetRedirectUrlFunction f) and
|
||||
node.getNode() = ret.getValue()
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getRedirectLocation() { result = this }
|
||||
|
||||
@@ -129,7 +129,7 @@ module FastApi {
|
||||
result in [this.getArg(0), this.getArgByName("path")]
|
||||
}
|
||||
|
||||
override Function getARequestHandler() { result.getADecorator().getAFlowNode() = node }
|
||||
override Function getARequestHandler() { node.getNode() = result.getADecorator() }
|
||||
|
||||
override string getFramework() { result = "FastAPI" }
|
||||
|
||||
@@ -309,7 +309,10 @@ module FastApi {
|
||||
FastApiRouteSetup routeSetup;
|
||||
|
||||
FastApiRequestHandlerReturn() {
|
||||
node = routeSetup.getARequestHandler().getAReturnValueFlowNode()
|
||||
exists(Return ret |
|
||||
ret.getScope() = routeSetup.getARequestHandler() and
|
||||
node.getNode() = ret.getValue()
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getBody() { result = this }
|
||||
|
||||
@@ -371,7 +371,7 @@ module Flask {
|
||||
result in [this.getArg(0), this.getArgByName("rule")]
|
||||
}
|
||||
|
||||
override Function getARequestHandler() { result.getADecorator().getAFlowNode() = node }
|
||||
override Function getARequestHandler() { node.getNode() = result.getADecorator() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -536,7 +536,7 @@ module Flask {
|
||||
FlaskRouteHandlerReturn() {
|
||||
exists(Function routeHandler |
|
||||
routeHandler = any(FlaskRouteSetup rs).getARequestHandler() and
|
||||
node = routeHandler.getAReturnValueFlowNode() and
|
||||
exists(Return ret | ret.getScope() = routeHandler and node.getNode() = ret.getValue()) and
|
||||
not this instanceof Flask::Response::InstanceSource
|
||||
)
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ private module FlaskAdmin {
|
||||
result in [this.getArg(0), this.getArgByName("url")]
|
||||
}
|
||||
|
||||
override Function getARequestHandler() { result.getADecorator().getAFlowNode() = node }
|
||||
override Function getARequestHandler() { node.getNode() = result.getADecorator() }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -71,7 +71,7 @@ private module FlaskAdmin {
|
||||
|
||||
override Function getARequestHandler() {
|
||||
exists(Flask::FlaskViewClass cls |
|
||||
cls.getADecorator().getAFlowNode() = node and
|
||||
node.getNode() = cls.getADecorator() and
|
||||
result = cls.getARequestHandler()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -166,7 +166,10 @@ module Pyramid {
|
||||
/** A response returned by a view callable. */
|
||||
private class PyramidReturnResponse extends Http::Server::HttpResponse::Range {
|
||||
PyramidReturnResponse() {
|
||||
this.asCfgNode() = any(View::ViewCallable vc).getAReturnValueFlowNode() and
|
||||
exists(Return ret |
|
||||
ret.getScope() = any(View::ViewCallable vc) and
|
||||
this.asCfgNode().getNode() = ret.getValue()
|
||||
) and
|
||||
not this = instance()
|
||||
}
|
||||
|
||||
|
||||
@@ -2254,8 +2254,9 @@ module StdlibPrivate {
|
||||
DataFlow::CfgNode
|
||||
{
|
||||
WsgirefSimpleServerApplicationReturn() {
|
||||
exists(WsgirefSimpleServerApplication requestHandler |
|
||||
node = requestHandler.getAReturnValueFlowNode()
|
||||
exists(WsgirefSimpleServerApplication requestHandler, Return ret |
|
||||
ret.getScope() = requestHandler and
|
||||
node.getNode() = ret.getValue()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -182,7 +182,10 @@ private module Twisted {
|
||||
DataFlow::CfgNode
|
||||
{
|
||||
TwistedResourceRenderMethodReturn() {
|
||||
this.asCfgNode() = any(TwistedResourceRenderMethod meth).getAReturnValueFlowNode()
|
||||
exists(Return ret |
|
||||
ret.getScope() = any(TwistedResourceRenderMethod meth) and
|
||||
this.asCfgNode().getNode() = ret.getValue()
|
||||
)
|
||||
}
|
||||
|
||||
override DataFlow::Node getBody() { result = this }
|
||||
|
||||
@@ -77,7 +77,7 @@ module Stages {
|
||||
or
|
||||
exists(any(AstExtended::AstNode n).getParentNode())
|
||||
or
|
||||
exists(any(AstExtended::AstNode n).getAFlowNode())
|
||||
exists(PyFlow::ControlFlowNode cfg, AstExtended::AstNode n | cfg.getNode() = n)
|
||||
or
|
||||
exists(any(PyFlow::BasicBlock b).getImmediateDominator())
|
||||
or
|
||||
|
||||
@@ -56,8 +56,9 @@ abstract class CallableObjectInternal extends ObjectInternal {
|
||||
/** A Python function. */
|
||||
class PythonFunctionObjectInternal extends CallableObjectInternal, TPythonFunctionObject {
|
||||
override Function getScope() {
|
||||
exists(CallableExpr expr |
|
||||
this = TPythonFunctionObject(expr.getAFlowNode()) and
|
||||
exists(CallableExpr expr, ControlFlowNode exprCfg |
|
||||
exprCfg.getNode() = expr and
|
||||
this = TPythonFunctionObject(exprCfg) and
|
||||
result = expr.getInnerScope()
|
||||
)
|
||||
}
|
||||
@@ -80,11 +81,12 @@ class PythonFunctionObjectInternal extends CallableObjectInternal, TPythonFuncti
|
||||
|
||||
pragma[nomagic]
|
||||
override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) {
|
||||
exists(Function func, ControlFlowNode rval, ControlFlowNode forigin |
|
||||
exists(Function func, Return ret, ControlFlowNode rval, ControlFlowNode forigin |
|
||||
func = this.getScope() and
|
||||
callee.appliesToScope(func)
|
||||
|
|
||||
rval = func.getAReturnValueFlowNode() and
|
||||
ret.getScope() = func and
|
||||
rval.getNode() = ret.getValue() and
|
||||
PointsToInternal::pointsTo(rval, callee, obj, forigin) and
|
||||
origin = CfgOrigin::fromCfgNode(forigin)
|
||||
)
|
||||
@@ -160,10 +162,11 @@ class PythonFunctionObjectInternal extends CallableObjectInternal, TPythonFuncti
|
||||
}
|
||||
|
||||
private BasicBlock blockReturningNone(Function func) {
|
||||
exists(Return ret |
|
||||
exists(Return ret, ControlFlowNode ret_ |
|
||||
not exists(ret.getValue()) and
|
||||
ret.getScope() = func and
|
||||
result = ret.getAFlowNode().getBasicBlock()
|
||||
ret_.getNode() = ret and
|
||||
result = ret_.getBasicBlock()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -113,8 +113,9 @@ abstract class ClassObjectInternal extends ObjectInternal {
|
||||
class PythonClassObjectInternal extends ClassObjectInternal, TPythonClassObject {
|
||||
/** Gets the scope for this Python class */
|
||||
Class getScope() {
|
||||
exists(ClassExpr expr |
|
||||
this = TPythonClassObject(expr.getAFlowNode()) and
|
||||
exists(ClassExpr expr, ControlFlowNode exprCfg |
|
||||
exprCfg.getNode() = expr and
|
||||
this = TPythonClassObject(exprCfg) and
|
||||
result = expr.getInnerScope()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -745,7 +745,12 @@ class PythonFunctionValue extends FunctionValue {
|
||||
override int maxParameters() { result = this.getScope().getMaxPositionalArguments() }
|
||||
|
||||
/** Gets a control flow node corresponding to a return statement in this function */
|
||||
ControlFlowNode getAReturnedNode() { result = this.getScope().getAReturnValueFlowNode() }
|
||||
ControlFlowNode getAReturnedNode() {
|
||||
exists(Return ret |
|
||||
ret.getScope() = this.getScope() and
|
||||
result.getNode() = ret.getValue()
|
||||
)
|
||||
}
|
||||
|
||||
override ClassValue getARaisedType() { scope_raises(result, this.getScope()) }
|
||||
|
||||
|
||||
@@ -387,7 +387,7 @@ private PythonClassObjectInternal abcMetaClassObject() {
|
||||
private predicate neither_class_nor_static_method(Function f) {
|
||||
not exists(f.getADecorator())
|
||||
or
|
||||
exists(ControlFlowNode deco | deco = f.getADecorator().getAFlowNode() |
|
||||
exists(ControlFlowNode deco | deco.getNode() = f.getADecorator() |
|
||||
exists(ObjectInternal o | PointsToInternal::pointsTo(deco, _, o, _) |
|
||||
o != ObjectInternal::staticMethod() and
|
||||
o != ObjectInternal::classMethod()
|
||||
|
||||
@@ -711,7 +711,7 @@ private module InterModulePointsTo {
|
||||
ControlFlowNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin
|
||||
) {
|
||||
exists(string name, ImportExpr i |
|
||||
i.getAFlowNode() = f and
|
||||
f.getNode() = i and
|
||||
i.getImportedModuleName() = name and
|
||||
PointsToInternal::module_imported_as(value, name) and
|
||||
origin = f and
|
||||
@@ -2118,8 +2118,9 @@ module Types {
|
||||
result.getBuiltin() = cls.getBuiltin().getBaseClass() and n = 0
|
||||
or
|
||||
exists(Class pycls | pycls = cls.(PythonClassObjectInternal).getScope() |
|
||||
exists(ObjectInternal base |
|
||||
PointsToInternal::pointsTo(pycls.getBase(n).getAFlowNode(), _, base, _)
|
||||
exists(ObjectInternal base, ControlFlowNode baseNode |
|
||||
baseNode.getNode() = pycls.getBase(n) and
|
||||
PointsToInternal::pointsTo(baseNode, _, base, _)
|
||||
|
|
||||
result = base and base != ObjectInternal::unknown()
|
||||
or
|
||||
@@ -2223,7 +2224,10 @@ module Types {
|
||||
}
|
||||
|
||||
private ControlFlowNode decorator_call_callee(PythonClassObjectInternal cls) {
|
||||
result = cls.getScope().getADecorator().getAFlowNode().(CallNode).getFunction()
|
||||
exists(CallNode deco |
|
||||
deco.getNode() = cls.getScope().getADecorator() and
|
||||
result = deco.getFunction()
|
||||
)
|
||||
}
|
||||
|
||||
private boolean has_six_add_metaclass(PythonClassObjectInternal cls) {
|
||||
@@ -2262,7 +2266,7 @@ module Types {
|
||||
}
|
||||
|
||||
private EssaVariable metaclass_var(Class cls) {
|
||||
result.getASourceUse() = cls.getMetaClass().getAFlowNode()
|
||||
result.getASourceUse().getNode() = cls.getMetaClass()
|
||||
or
|
||||
major_version() = 2 and
|
||||
not exists(cls.getMetaClass()) and
|
||||
|
||||
@@ -181,7 +181,7 @@ class ClassObject extends Object {
|
||||
)
|
||||
}
|
||||
|
||||
ControlFlowNode declaredMetaClass() { result = this.getPyClass().getMetaClass().getAFlowNode() }
|
||||
ControlFlowNode declaredMetaClass() { result.getNode() = this.getPyClass().getMetaClass() }
|
||||
|
||||
/** Has type inference failed to compute the full class hierarchy for this class for the reason given. */
|
||||
predicate failedInference(string reason) { Types::failedInference(this.theClass(), reason) }
|
||||
@@ -195,8 +195,9 @@ class ClassObject extends Object {
|
||||
* It is guaranteed that getProbableSingletonInstance() returns at most one Object for each ClassObject.
|
||||
*/
|
||||
Object getProbableSingletonInstance() {
|
||||
exists(ControlFlowNodeWithPointsTo use, Expr origin |
|
||||
use.refersTo(result, this, origin.getAFlowNode())
|
||||
exists(ControlFlowNodeWithPointsTo use, Expr origin, ControlFlowNode origin_ |
|
||||
origin_.getNode() = origin and
|
||||
use.refersTo(result, this, origin_)
|
||||
|
|
||||
this.hasStaticallyUniqueInstance() and
|
||||
/* Ensure that original expression will be executed only one. */
|
||||
|
||||
@@ -427,7 +427,7 @@ class ExceptFlowNodeWithPointsTo extends ExceptFlowNode {
|
||||
}
|
||||
|
||||
private ControlFlowNodeWithPointsTo element_from_tuple_objectapi(Object tuple) {
|
||||
exists(Tuple t | t = tuple.getOrigin() and result = t.getAnElt().getAFlowNode())
|
||||
exists(Tuple t | t = tuple.getOrigin() and result.getNode() = t.getAnElt())
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -36,8 +36,8 @@ class RangeIterationVariableFact extends PointsToExtension {
|
||||
RangeIterationVariableFact() {
|
||||
exists(For f, ControlFlowNode iterable |
|
||||
iterable.getBasicBlock().dominates(this.(ControlFlowNode).getBasicBlock()) and
|
||||
f.getIter().getAFlowNode() = iterable and
|
||||
f.getTarget().getAFlowNode() = this and
|
||||
iterable.getNode() = f.getIter() and
|
||||
this.(ControlFlowNode).getNode() = f.getTarget() and
|
||||
exists(ObjectInternal range |
|
||||
PointsTo::pointsTo(iterable, _, range, _) and
|
||||
range.getClass() = ObjectInternal::builtin("range")
|
||||
|
||||
@@ -137,7 +137,10 @@ class PyFunctionObject extends FunctionObject {
|
||||
|
||||
/** Gets a control flow node corresponding to the value of a return statement */
|
||||
ControlFlowNodeWithPointsTo getAReturnedNode() {
|
||||
result = this.getFunction().getAReturnValueFlowNode()
|
||||
exists(Return ret |
|
||||
ret.getScope() = this.getFunction() and
|
||||
result.getNode() = ret.getValue()
|
||||
)
|
||||
}
|
||||
|
||||
override string descriptiveString() {
|
||||
@@ -170,7 +173,7 @@ class PyFunctionObject extends FunctionObject {
|
||||
predicate unconditionallyReturnsParameter(int n) {
|
||||
exists(SsaVariable pvar |
|
||||
exists(Parameter p | p = this.getFunction().getArg(n) |
|
||||
p.asName().getAFlowNode() = pvar.getDefinition()
|
||||
pvar.getDefinition().getNode() = p.asName()
|
||||
) and
|
||||
exists(NameNode rval |
|
||||
rval = pvar.getAUse() and
|
||||
|
||||
@@ -337,7 +337,7 @@ class TupleObject extends SequenceObject {
|
||||
or
|
||||
this instanceof TupleNode
|
||||
or
|
||||
exists(Function func | func.getVararg().getAFlowNode() = this)
|
||||
exists(Function func | this.(ControlFlowNode).getNode() = func.getVararg())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -352,7 +352,9 @@ module TupleObject {
|
||||
}
|
||||
|
||||
class NonEmptyTupleObject extends TupleObject {
|
||||
NonEmptyTupleObject() { exists(Function func | func.getVararg().getAFlowNode() = this) }
|
||||
NonEmptyTupleObject() {
|
||||
exists(Function func | this.(ControlFlowNode).getNode() = func.getVararg())
|
||||
}
|
||||
|
||||
override boolean booleanValue() { result = true }
|
||||
}
|
||||
|
||||
@@ -48,9 +48,11 @@ class CheckClass extends ClassObject {
|
||||
self_dict = sub.getObject()
|
||||
or
|
||||
/* Indirect assignment via temporary variable */
|
||||
exists(SsaVariable v |
|
||||
v.getAUse() = sub.getObject().getAFlowNode() and
|
||||
v.getDefinition().(DefinitionNode).getValue() = self_dict.getAFlowNode()
|
||||
exists(SsaVariable v, ControlFlowNode subObjCfg, ControlFlowNode selfDictCfg |
|
||||
subObjCfg.getNode() = sub.getObject() and selfDictCfg.getNode() = self_dict
|
||||
|
|
||||
v.getAUse() = subObjCfg and
|
||||
v.getDefinition().(DefinitionNode).getValue() = selfDictCfg
|
||||
)
|
||||
) and
|
||||
a.getATarget() = sub and
|
||||
@@ -62,9 +64,10 @@ class CheckClass extends ClassObject {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate monkeyPatched(string name) {
|
||||
exists(Attribute a |
|
||||
exists(Attribute a, ControlFlowNode objCfg |
|
||||
objCfg.getNode() = a.getObject() and
|
||||
a.getCtx() instanceof Store and
|
||||
PointsTo::points_to(a.getObject().getAFlowNode(), _, this, _, _) and
|
||||
PointsTo::points_to(objCfg, _, this, _, _) and
|
||||
a.getName() = name
|
||||
)
|
||||
}
|
||||
@@ -84,9 +87,9 @@ class CheckClass extends ClassObject {
|
||||
}
|
||||
|
||||
predicate interestingUndefined(SelfAttributeRead a) {
|
||||
exists(string name | name = a.getName() |
|
||||
exists(string name, ControlFlowNode aCfg | name = a.getName() and aCfg.getNode() = a |
|
||||
this.interestingContext(a, name) and
|
||||
not this.definedInBlock(a.getAFlowNode().getBasicBlock(), name)
|
||||
not this.definedInBlock(aCfg.getBasicBlock(), name)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -109,8 +112,9 @@ class CheckClass extends ClassObject {
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate definitionInBlock(BasicBlock b, string name) {
|
||||
exists(SelfAttributeStore sa |
|
||||
sa.getAFlowNode().getBasicBlock() = b and
|
||||
exists(SelfAttributeStore sa, ControlFlowNode saCfg |
|
||||
saCfg.getNode() = sa and
|
||||
saCfg.getBasicBlock() = b and
|
||||
sa.getName() = name and
|
||||
sa.getClass() = this.getPyClass()
|
||||
)
|
||||
|
||||
@@ -15,7 +15,9 @@
|
||||
import python
|
||||
import semmle.python.ApiGraphs
|
||||
|
||||
predicate doesnt_reraise(ExceptStmt ex) { ex.getAFlowNode().getBasicBlock().reachesExit() }
|
||||
predicate doesnt_reraise(ExceptStmt ex) {
|
||||
exists(ControlFlowNode exCfg | exCfg.getNode() = ex | exCfg.getBasicBlock().reachesExit())
|
||||
}
|
||||
|
||||
predicate catches_base_exception(ExceptStmt ex) {
|
||||
ex.getType() = API::builtin("BaseException").getAValueReachableFromSource().asExpr()
|
||||
|
||||
@@ -116,7 +116,7 @@ FunctionValue get_function_or_initializer(Value func_or_cls) {
|
||||
predicate illegally_named_parameter_objectapi(Call call, Object func, string name) {
|
||||
not func.isC() and
|
||||
name = call.getANamedArgumentName() and
|
||||
call.getAFlowNode() = get_a_call_objectapi(func) and
|
||||
exists(ControlFlowNode callCfg | callCfg.getNode() = call | callCfg = get_a_call_objectapi(func)) and
|
||||
not get_function_or_initializer_objectapi(func).isLegalArgumentName(name)
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ predicate illegally_named_parameter_objectapi(Call call, Object func, string nam
|
||||
predicate illegally_named_parameter(Call call, Value func, string name) {
|
||||
not func.isBuiltin() and
|
||||
name = call.getANamedArgumentName() and
|
||||
call.getAFlowNode() = get_a_call(func) and
|
||||
exists(ControlFlowNode callCfg | callCfg.getNode() = call | callCfg = get_a_call(func)) and
|
||||
not get_function_or_initializer(func).isLegalArgumentName(name)
|
||||
}
|
||||
|
||||
@@ -146,7 +146,9 @@ predicate too_few_args_objectapi(Call call, Object callable, int limit) {
|
||||
call = func.getAMethodCall().getNode() and limit = func.minParameters() - 1
|
||||
or
|
||||
callable instanceof ClassObject and
|
||||
call.getAFlowNode() = get_a_call_objectapi(callable) and
|
||||
exists(ControlFlowNode callCfg | callCfg.getNode() = call |
|
||||
callCfg = get_a_call_objectapi(callable)
|
||||
) and
|
||||
limit = func.minParameters() - 1
|
||||
)
|
||||
}
|
||||
@@ -172,7 +174,7 @@ predicate too_few_args(Call call, Value callable, int limit) {
|
||||
call = func.getAMethodCall().getNode() and limit = func.minParameters() - 1
|
||||
or
|
||||
callable instanceof ClassValue and
|
||||
call.getAFlowNode() = get_a_call(callable) and
|
||||
exists(ControlFlowNode callCfg | callCfg.getNode() = call | callCfg = get_a_call(callable)) and
|
||||
limit = func.minParameters() - 1
|
||||
)
|
||||
}
|
||||
@@ -191,7 +193,9 @@ predicate too_many_args_objectapi(Call call, Object callable, int limit) {
|
||||
call = func.getAMethodCall().getNode() and limit = func.maxParameters() - 1
|
||||
or
|
||||
callable instanceof ClassObject and
|
||||
call.getAFlowNode() = get_a_call_objectapi(callable) and
|
||||
exists(ControlFlowNode callCfg | callCfg.getNode() = call |
|
||||
callCfg = get_a_call_objectapi(callable)
|
||||
) and
|
||||
limit = func.maxParameters() - 1
|
||||
) and
|
||||
positional_arg_count_for_call_objectapi(call, callable) > limit
|
||||
@@ -211,7 +215,7 @@ predicate too_many_args(Call call, Value callable, int limit) {
|
||||
call = func.getAMethodCall().getNode() and limit = func.maxParameters() - 1
|
||||
or
|
||||
callable instanceof ClassValue and
|
||||
call.getAFlowNode() = get_a_call(callable) and
|
||||
exists(ControlFlowNode callCfg | callCfg.getNode() = call | callCfg = get_a_call(callable)) and
|
||||
limit = func.maxParameters() - 1
|
||||
) and
|
||||
positional_arg_count_for_call(call, callable) > limit
|
||||
|
||||
@@ -36,11 +36,15 @@ where
|
||||
exists(string s | dict_key(d, k1, s) and dict_key(d, k2, s) and k1 != k2) and
|
||||
(
|
||||
exists(BasicBlock b, int i1, int i2 |
|
||||
k1.getAFlowNode() = b.getNode(i1) and
|
||||
k2.getAFlowNode() = b.getNode(i2) and
|
||||
b.getNode(i1).getNode() = k1 and
|
||||
b.getNode(i2).getNode() = k2 and
|
||||
i1 < i2
|
||||
)
|
||||
or
|
||||
k1.getAFlowNode().getBasicBlock().strictlyDominates(k2.getAFlowNode().getBasicBlock())
|
||||
exists(ControlFlowNode k1Cfg, ControlFlowNode k2Cfg |
|
||||
k1Cfg.getNode() = k1 and k2Cfg.getNode() = k2
|
||||
|
|
||||
k1Cfg.getBasicBlock().strictlyDominates(k2Cfg.getBasicBlock())
|
||||
)
|
||||
)
|
||||
select k1, "Dictionary key " + repr(k1) + " is subsequently $@.", k2, "overwritten"
|
||||
|
||||
@@ -98,16 +98,18 @@ private predicate brace_pair(PossibleAdvancedFormatString fmt, int start, int en
|
||||
}
|
||||
|
||||
private predicate advanced_format_call(Call format_expr, PossibleAdvancedFormatString fmt, int args) {
|
||||
exists(CallNode call | call = format_expr.getAFlowNode() |
|
||||
exists(CallNode call, ControlFlowNode fmtCfg |
|
||||
call.getNode() = format_expr and fmtCfg.getNode() = fmt
|
||||
|
|
||||
call.getFunction().(ControlFlowNodeWithPointsTo).pointsTo(Value::named("format")) and
|
||||
call.getArg(0).(ControlFlowNodeWithPointsTo).pointsTo(_, fmt.getAFlowNode()) and
|
||||
call.getArg(0).(ControlFlowNodeWithPointsTo).pointsTo(_, fmtCfg) and
|
||||
args = count(format_expr.getAnArg()) - 1
|
||||
or
|
||||
call.getFunction()
|
||||
.(AttrNode)
|
||||
.getObject("format")
|
||||
.(ControlFlowNodeWithPointsTo)
|
||||
.pointsTo(_, fmt.getAFlowNode()) and
|
||||
.pointsTo(_, fmtCfg) and
|
||||
args = count(format_expr.getAnArg())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import python
|
||||
|
||||
/** Holds if the comparison `comp` uses `is` or `is not` (represented as `op`) to compare its `left` and `right` arguments. */
|
||||
predicate comparison_using_is(Compare comp, ControlFlowNode left, Cmpop op, ControlFlowNode right) {
|
||||
exists(CompareNode fcomp | fcomp = comp.getAFlowNode() |
|
||||
exists(CompareNode fcomp | fcomp.getNode() = comp |
|
||||
fcomp.operands(left, op, right) and
|
||||
(op instanceof Is or op instanceof IsNot)
|
||||
)
|
||||
|
||||
@@ -5,7 +5,7 @@ private import LegacyPointsTo
|
||||
|
||||
/** Holds if the comparison `comp` uses `is` or `is not` (represented as `op`) to compare its `left` and `right` arguments. */
|
||||
predicate comparison_using_is(Compare comp, ControlFlowNode left, Cmpop op, ControlFlowNode right) {
|
||||
exists(CompareNode fcomp | fcomp = comp.getAFlowNode() |
|
||||
exists(CompareNode fcomp | fcomp.getNode() = comp |
|
||||
fcomp.operands(left, op, right) and
|
||||
(op instanceof Is or op instanceof IsNot)
|
||||
)
|
||||
|
||||
@@ -19,7 +19,7 @@ where
|
||||
// Only relevant for Python 2, as all later versions implement true division
|
||||
major_version() = 2 and
|
||||
exists(BinaryExprNode bin, Value lval, Value rval |
|
||||
bin = div.getAFlowNode() and
|
||||
bin.getNode() = div and
|
||||
bin.getNode().getOp() instanceof Div and
|
||||
bin.getLeft().(ControlFlowNodeWithPointsTo).pointsTo(lval, left) and
|
||||
lval.getClass() = ClassValue::int_() and
|
||||
|
||||
@@ -19,7 +19,9 @@ where
|
||||
exists(Function init | init.isInitMethod() and r.getScope() = init) and
|
||||
r.getValue() = rv and
|
||||
not rv.pointsTo(Value::none_()) and
|
||||
not exists(FunctionValue f | f.getACall() = rv.getAFlowNode() | f.neverReturns()) and
|
||||
not exists(FunctionValue f, ControlFlowNode rvCfg | rvCfg.getNode() = rv |
|
||||
f.getACall() = rvCfg and f.neverReturns()
|
||||
) and
|
||||
// to avoid double reporting, don't trigger if returning result from other __init__ function
|
||||
not exists(Attribute meth | meth = rv.(Call).getFunc() | meth.getName() = "__init__")
|
||||
select r, "Explicit return in __init__ method."
|
||||
|
||||
@@ -69,7 +69,12 @@ where
|
||||
returns_meaningful_value(callee) and
|
||||
not wrapped_in_try_except(call) and
|
||||
exists(int unused |
|
||||
unused = count(ExprStmt e | e.getValue().getAFlowNode() = callee.getACall()) and
|
||||
unused =
|
||||
count(ExprStmt e |
|
||||
exists(ControlFlowNode eValCfg | eValCfg.getNode() = e.getValue() |
|
||||
eValCfg = callee.getACall()
|
||||
)
|
||||
) and
|
||||
total = count(callee.getACall())
|
||||
|
|
||||
percentage_used = (100.0 * (total - unused) / total).floor()
|
||||
|
||||
@@ -138,12 +138,12 @@ predicate function_opens_file(FunctionValue f) {
|
||||
f = Value::named("open")
|
||||
or
|
||||
exists(EssaVariable v, Return ret | ret.getScope() = f.getScope() |
|
||||
ret.getValue().getAFlowNode() = v.getAUse() and
|
||||
v.getNode() = ret.getValue().getAUse() and
|
||||
var_is_open(v, _)
|
||||
)
|
||||
or
|
||||
exists(Return ret, FunctionValue callee | ret.getScope() = f.getScope() |
|
||||
ret.getValue().getAFlowNode() = callee.getACall() and
|
||||
callee.getNode() = ret.getValue().getACall() and
|
||||
function_opens_file(callee)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ class CredentialSink extends DataFlow::Node {
|
||||
this.(DataFlow::ArgumentNode).argumentOf(_, pos)
|
||||
)
|
||||
or
|
||||
exists(Keyword k | k.getArg() = name and k.getValue().getAFlowNode() = this.asCfgNode())
|
||||
exists(Keyword k | k.getArg() = name and this.asCfgNode().getNode() = k.getValue())
|
||||
or
|
||||
exists(CompareNode cmp, NameNode n | n.getId() = name |
|
||||
cmp.operands(this.asCfgNode(), any(Eq eq), n)
|
||||
|
||||
@@ -25,7 +25,7 @@ from
|
||||
For loop, ControlFlowNodeWithPointsTo iter, Value str, Value seq, ControlFlowNode seq_origin,
|
||||
ControlFlowNode str_origin
|
||||
where
|
||||
loop.getIter().getAFlowNode() = iter and
|
||||
iter.getNode() = loop.getIter() and
|
||||
iter.pointsTo(str, str_origin) and
|
||||
iter.pointsTo(seq, seq_origin) and
|
||||
has_string_type(str) and
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
import python
|
||||
|
||||
predicate loop_variable_ssa(For f, Variable v, SsaVariable s) {
|
||||
f.getTarget().getAFlowNode() = s.getDefinition() and v = s.getVariable()
|
||||
s.getDefinition().getNode() = f.getTarget() and v = s.getVariable()
|
||||
}
|
||||
|
||||
predicate variableUsedInNestedLoops(For inner, For outer, Variable v, Name n) {
|
||||
|
||||
@@ -16,7 +16,7 @@ private import LegacyPointsTo
|
||||
|
||||
from For loop, ControlFlowNodeWithPointsTo iter, Value v, ClassValue t, ControlFlowNode origin
|
||||
where
|
||||
loop.getIter().getAFlowNode() = iter and
|
||||
iter.getNode() = loop.getIter() and
|
||||
iter.pointsTo(_, v, origin) and
|
||||
v.getClass() = t and
|
||||
not t.isIterable() and
|
||||
|
||||
@@ -24,11 +24,13 @@ predicate func_with_side_effects(Expr e) {
|
||||
}
|
||||
|
||||
predicate call_with_side_effect(Call e) {
|
||||
e.getAFlowNode() =
|
||||
API::moduleImport("subprocess")
|
||||
.getMember(["call", "check_call", "check_output"])
|
||||
.getACall()
|
||||
.asCfgNode()
|
||||
exists(ControlFlowNode eCfg | eCfg.getNode() = e |
|
||||
eCfg =
|
||||
API::moduleImport("subprocess")
|
||||
.getMember(["call", "check_call", "check_output"])
|
||||
.getACall()
|
||||
.asCfgNode()
|
||||
)
|
||||
}
|
||||
|
||||
predicate probable_side_effect(Expr e) {
|
||||
|
||||
@@ -133,7 +133,11 @@ class ListComprehensionDeclaration extends ListComp {
|
||||
major_version() = 2 and
|
||||
this.getIterationVariable(_).getId() = result.getId() and
|
||||
result.getScope() = this.getScope() and
|
||||
this.getAFlowNode().strictlyReaches(result.getAFlowNode()) and
|
||||
exists(ControlFlowNode thisCfg, ControlFlowNode resultCfg |
|
||||
thisCfg.getNode() = this and resultCfg.getNode() = result
|
||||
|
|
||||
thisCfg.strictlyReaches(resultCfg)
|
||||
) and
|
||||
result.isUse()
|
||||
}
|
||||
|
||||
|
||||
@@ -13,18 +13,21 @@
|
||||
import python
|
||||
import Definition
|
||||
|
||||
from ListComprehensionDeclaration l, Name use, Name defn
|
||||
from
|
||||
ListComprehensionDeclaration l, Name use, Name defn, ControlFlowNode lCfg, ControlFlowNode useCfg
|
||||
where
|
||||
use = l.getALeakedVariableUse() and
|
||||
defn = l.getDefinition() and
|
||||
l.getAFlowNode().strictlyReaches(use.getAFlowNode()) and
|
||||
lCfg.getNode() = l and
|
||||
useCfg.getNode() = use and
|
||||
lCfg.strictlyReaches(useCfg) and
|
||||
/* Make sure we aren't in a loop, as the variable may be redefined */
|
||||
not use.getAFlowNode().strictlyReaches(l.getAFlowNode()) and
|
||||
not useCfg.strictlyReaches(lCfg) and
|
||||
not l.contains(use) and
|
||||
not use.deletes(_) and
|
||||
not exists(SsaVariable v |
|
||||
v.getAUse() = use.getAFlowNode() and
|
||||
not v.getDefinition().strictlyDominates(l.getAFlowNode())
|
||||
v.getAUse() = useCfg and
|
||||
not v.getDefinition().strictlyDominates(lCfg)
|
||||
)
|
||||
select use,
|
||||
use.getId() + " may have a different value in Python 3, as the $@ will not be in scope.", defn,
|
||||
|
||||
@@ -26,8 +26,11 @@ private Stmt loop_probably_defines(Variable v) {
|
||||
|
||||
/** Holds if the variable used by `use` is probably defined in a loop */
|
||||
predicate probably_defined_in_loop(Name use) {
|
||||
exists(Stmt loop | loop = loop_probably_defines(use.getVariable()) |
|
||||
loop.getAFlowNode().strictlyReaches(use.getAFlowNode())
|
||||
exists(Stmt loop, ControlFlowNode loopCfg, ControlFlowNode useCfg |
|
||||
loop = loop_probably_defines(use.getVariable()) and
|
||||
loopCfg.getNode() = loop and
|
||||
useCfg.getNode() = use and
|
||||
loopCfg.strictlyReaches(useCfg)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user