Skip to content

Commit 7a0bded

Browse files
committed
Kotlin: support argument-range specifications for $default methods
1 parent 8d10b1b commit 7a0bded

File tree

4 files changed

+31
-11
lines changed

4 files changed

+31
-11
lines changed

java/ql/integration-tests/posix-only/kotlin/default-parameter-mad-flow/lib.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ class LibClass {
88
fun memberWithDefaults(x: Int, y: Int = 1) = 0
99
fun String.extensionMemberWithDefaults(x: Int, y: Int = 1) = 0
1010

11+
fun multiParameterTest(x: Int, y: Int, z: Int, w: Int = 0) = 0
12+
fun Int.multiParameterExtensionTest(x: Int, y: Int, w: Int = 0) = 0
13+
1114
}
1215

1316
class SomeToken {}
@@ -30,4 +33,3 @@ class SinkClass(x: Int, y: Int = 1) {
3033
fun String.extensionMemberSink(x: Int, y: Int = 1) {}
3134

3235
}
33-

java/ql/integration-tests/posix-only/kotlin/default-parameter-mad-flow/test.ql

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ private class Models extends SummaryModelCsv {
1111
";LibKt;true;topLevelWithDefaults;(int,int);;Argument[0];ReturnValue;value;manual",
1212
";LibKt;true;extensionWithDefaults;(String,int,int);;Argument[1];ReturnValue;value;manual",
1313
";LibClass;true;memberWithDefaults;(int,int);;Argument[0];ReturnValue;value;manual",
14-
";LibClass;true;extensionMemberWithDefaults;(String,int,int);;Argument[1];ReturnValue;value;manual"
14+
";LibClass;true;extensionMemberWithDefaults;(String,int,int);;Argument[1];ReturnValue;value;manual",
15+
";LibClass;true;multiParameterTest;(int,int,int,int);;Argument[0..1];ReturnValue;value;manual",
16+
";LibClass;true;multiParameterExtensionTest;(int,int,int,int);;Argument[0, 1];ReturnValue;value;manual",
1517
]
1618
}
1719
}

java/ql/integration-tests/posix-only/kotlin/default-parameter-mad-flow/user.kt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,20 @@ fun test(c: LibClass, sourcec: SourceClass, sinkc: SinkClass) {
1616
sink(c.memberWithDefaults(source(), 0)) // $ flow
1717
sink(c.memberWithDefaults(source())) // $ flow
1818

19+
sink(c.multiParameterTest(source(), 0, 0)) // $ flow
20+
sink(c.multiParameterTest(0, source(), 0)) // $ flow
21+
sink(c.multiParameterTest(0, 0, source()))
22+
1923
with(c) {
2024
sink("Hello world".extensionMemberWithDefaults(source(), 0)) // $ flow
2125
sink("Hello world".extensionMemberWithDefaults(source())) // $ flow
22-
};
26+
}
27+
28+
with(c) {
29+
sink(source().multiParameterExtensionTest(0, 0)) // $ flow
30+
sink(0.multiParameterExtensionTest(source(), 0)) // $ flow
31+
sink(0.multiParameterExtensionTest(0, source()))
32+
}
2333

2434
run {
2535
val st = SomeToken()

java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImplSpecific.qll

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ private import FlowSummaryImpl::Private
1010
private import FlowSummaryImpl::Public
1111
private import semmle.code.java.dataflow.ExternalFlow
1212
private import semmle.code.java.dataflow.FlowSummary as FlowSummary
13+
private import semmle.code.java.dataflow.internal.AccessPathSyntax as AccessPathSyntax
1314

1415
class SummarizedCallableBase = FlowSummary::SummarizedCallableBase;
1516

@@ -84,6 +85,9 @@ private predicate relatedArgSpec(Callable c, string spec) {
8485
* for the additional dispatch receiver parameter that occurs in the default-parameter proxy's argument
8586
* list. When no adjustment is required (e.g. for constructors, or non-argument-based specs), `defaultArgsSpec`
8687
* equals `originalArgSpec`.
88+
*
89+
* Note in the case where `originalArgSpec` uses an integer range, like `Argument[1..3]...`, this will produce multiple
90+
* results for `defaultsArgSpec`, like `{Argument[2]..., Argument[3]..., Argument[4]...}`.
8791
*/
8892
private predicate correspondingKotlinParameterDefaultsArgSpec(
8993
Callable originalCallable, Callable defaultsCallable, string originalArgSpec,
@@ -98,19 +102,21 @@ private predicate correspondingKotlinParameterDefaultsArgSpec(
98102
exists(string regex |
99103
// Note I use a regex and not AccessPathToken because this feeds summaryElement et al,
100104
// which would introduce mutual recursion with the definition of AccessPathToken.
101-
regex = "Argument\\[([0-9]+)\\](.*)" and
105+
regex = "Argument\\[([0-9,\\. ]+)\\](.*)" and
102106
(
103107
exists(string oldArgNumber, string rest, int paramOffset |
104108
oldArgNumber = originalArgSpec.regexpCapture(regex, 1) and
105109
rest = originalArgSpec.regexpCapture(regex, 2) and
106110
paramOffset =
107-
(
108-
defaultsCallable.getNumberOfParameters() -
109-
(originalCallable.getNumberOfParameters() + 2)
110-
) and
111-
if ktExtensionFunctions(originalCallable, _, _) and oldArgNumber = "0"
112-
then defaultsArgSpec = originalArgSpec
113-
else defaultsArgSpec = "Argument[" + (oldArgNumber.toInt() + paramOffset) + "]" + rest
111+
defaultsCallable.getNumberOfParameters() -
112+
(originalCallable.getNumberOfParameters() + 2) and
113+
exists(int oldArgParsed |
114+
oldArgParsed = AccessPathSyntax::AccessPath::parseInt(oldArgNumber.splitAt(",").trim())
115+
|
116+
if ktExtensionFunctions(originalCallable, _, _) and oldArgParsed = 0
117+
then defaultsArgSpec = "Argument[0]"
118+
else defaultsArgSpec = "Argument[" + (oldArgParsed + paramOffset) + "]" + rest
119+
)
114120
)
115121
or
116122
not originalArgSpec.regexpMatch(regex) and

0 commit comments

Comments
 (0)