Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions java/ql/src/semmle/code/java/StringFormat.qll
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class StringFormatMethod extends FormatMethod {
StringFormatMethod() {
(
this.hasName("format") or
this.hasName("formatted") or
this.hasName("printf") or
this.hasName("readLine") or
this.hasName("readPassword")
Expand All @@ -38,6 +39,8 @@ class StringFormatMethod extends FormatMethod {
override int getFormatStringIndex() {
result = 0 and this.getSignature() = "format(java.lang.String,java.lang.Object[])"
or
result = -1 and this.getSignature() = "formatted(java.lang.Object[])"
or
result = 0 and this.getSignature() = "printf(java.lang.String,java.lang.Object[])"
or
result = 1 and
Expand Down Expand Up @@ -91,6 +94,12 @@ class FmtSyntax extends TFmtSyntax {
predicate isLogger() { this = TFmtLogger() }
}

private Expr getArgumentOrQualifier(Call c, int i) {
result = c.getArgument(i)
or
result = c.getQualifier() and i = -1
}

/**
* Holds if `c` wraps a call to a `StringFormatMethod`, such that `fmtix` is
* the index of the format string argument to `c` and the following and final
Expand All @@ -111,7 +120,7 @@ private predicate formatWrapper(Callable c, int fmtix, FmtSyntax syntax) {
or
fmtcall.getCallee().(LoggerFormatMethod).getFormatStringIndex() = i and syntax = TFmtLogger()
) and
fmtcall.getArgument(i) = fmt.getAnAccess() and
getArgumentOrQualifier(fmtcall, i) = fmt.getAnAccess() and
fmtcall.getArgument(i + 1) = args.getAnAccess()
)
}
Expand Down Expand Up @@ -155,7 +164,7 @@ class FormattingCall extends Call {
}

/** Gets the argument to this call in the position of the format string */
Expr getFormatArgument() { result = this.getArgument(this.getFormatStringIndex()) }
Expr getFormatArgument() { result = getArgumentOrQualifier(this, this.getFormatStringIndex()) }

/** Gets an argument to be formatted. */
Expr getAnArgumentToBeFormatted() {
Expand Down
5 changes: 5 additions & 0 deletions java/ql/test/query-tests/StringFormat/A.java
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,9 @@ void g(boolean b, int i) {
String.format("%s%s", a2); // ok
String.format("%s", a2); // unused
}

void formatted() {
"%s%s".formatted(""); // missing
"%s".formatted("", ""); // unused
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@
| A.java:74:5:74:47 | format(...) | This format call refers to 2 argument(s) but only supplies 1 argument(s). |
| A.java:79:5:79:31 | format(...) | This format call refers to 3 argument(s) but only supplies 2 argument(s). |
| A.java:84:5:84:31 | format(...) | This format call refers to 3 argument(s) but only supplies 2 argument(s). |
| A.java:90:5:90:24 | formatted(...) | This format call refers to 2 argument(s) but only supplies 1 argument(s). |
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@
| A.java:76:5:76:57 | format(...) | This format call refers to 2 argument(s) but supplies 3 argument(s). |
| A.java:81:5:81:27 | format(...) | This format call refers to 1 argument(s) but supplies 2 argument(s). |
| A.java:86:5:86:27 | format(...) | This format call refers to 1 argument(s) but supplies 2 argument(s). |
| A.java:91:5:91:26 | formatted(...) | This format call refers to 1 argument(s) but supplies 2 argument(s). |
1 change: 1 addition & 0 deletions java/ql/test/query-tests/StringFormat/options
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
//semmle-extractor-options: --javac-args --enable-preview -source 14 -target 14