From 26f42826d6fef769ea8ebb11d4459ecc798b7dfd Mon Sep 17 00:00:00 2001 From: Arthur Baars Date: Wed, 16 Oct 2024 22:54:21 +0200 Subject: [PATCH] Rust: parse formatting templates --- rust/ql/.generated.list | 1 - rust/ql/.gitattributes | 1 - .../codeql/rust/elements/FormatTemplate.qll | 248 ++++++++++++++++++ .../elements/internal/FormatArgsExprImpl.qll | 13 +- .../formatstrings/FormatString.expected | 1 + .../formatstrings/FormatTemplate.expected | 132 ++++++++++ .../formatstrings/FormatTemplate.ql | 21 ++ .../test/library-tests/formatstrings/main.rs | 77 ++++++ 8 files changed, 490 insertions(+), 4 deletions(-) create mode 100644 rust/ql/lib/codeql/rust/elements/FormatTemplate.qll create mode 100644 rust/ql/test/library-tests/formatstrings/FormatString.expected create mode 100644 rust/ql/test/library-tests/formatstrings/FormatTemplate.expected create mode 100644 rust/ql/test/library-tests/formatstrings/FormatTemplate.ql create mode 100644 rust/ql/test/library-tests/formatstrings/main.rs diff --git a/rust/ql/.generated.list b/rust/ql/.generated.list index 66650b80e7578..15d55f707c685 100644 --- a/rust/ql/.generated.list +++ b/rust/ql/.generated.list @@ -231,7 +231,6 @@ lib/codeql/rust/elements/internal/ForTypeImpl.qll b515639844778d0fbe51e6161a2ec1 lib/codeql/rust/elements/internal/FormatArgsArgConstructor.qll 8bd9b4e035ef8adeb3ac510dd68043934c0140facb933be1f240096d01cdfa11 74e9d3bbd8882ae59a7e88935d468e0a90a6529a4e2af6a3d83e93944470f0ee lib/codeql/rust/elements/internal/FormatArgsArgImpl.qll 601f7715e9a65bcfa7cea1979fa30d694b5bea29650d799d7dd3080b8eea58e9 ecb0800cdb8c0f93277982dad295ac6a5332e42eff4fb5c8ff19f903b9857003 lib/codeql/rust/elements/internal/FormatArgsExprConstructor.qll ce29ff5a839b885b1ab7a02d6a381ae474ab1be3e6ee7dcfd7595bdf28e4b558 63bf957426871905a51ea319662a59e38104c197a1024360aca364dc145b11e8 -lib/codeql/rust/elements/internal/FormatArgsExprImpl.qll bdb992ebc6be59311b486f40325b39f52a69921cfc66a731085cb184da00050f 6336e7770f9cb700f1b3914fd940c3423ab4e971b34ed8fcc79da80c1f1cdba3 lib/codeql/rust/elements/internal/FunctionConstructor.qll b50aea579938d03745dfbd8b5fa8498f7f83b967369f63d6875510e09ab7f5d2 19cca32aeaecaf9debc27329e8c39ecec69464bb1d89d7b09908a1d73a8d92a2 lib/codeql/rust/elements/internal/GenericArgImpl.qll 6b1b804c357425c223f926e560a688e81506f5a35b95485cecf704e88cc009ee cc1ccf6a23dadc397e82664f3911d4b385d4c8ca80b1ee16d5275d9c936148dd lib/codeql/rust/elements/internal/GenericArgListConstructor.qll 46859bb3eb09d77987a18642d65ba2e13471a4dc9c0a83a192fddc82e37c335c 2c7d54c876269a88d3461b05745e73b06532b1616cae9b614ac94b28735d8fc4 diff --git a/rust/ql/.gitattributes b/rust/ql/.gitattributes index 87f0154882a88..efcd3218cf303 100644 --- a/rust/ql/.gitattributes +++ b/rust/ql/.gitattributes @@ -233,7 +233,6 @@ /lib/codeql/rust/elements/internal/FormatArgsArgConstructor.qll linguist-generated /lib/codeql/rust/elements/internal/FormatArgsArgImpl.qll linguist-generated /lib/codeql/rust/elements/internal/FormatArgsExprConstructor.qll linguist-generated -/lib/codeql/rust/elements/internal/FormatArgsExprImpl.qll linguist-generated /lib/codeql/rust/elements/internal/FunctionConstructor.qll linguist-generated /lib/codeql/rust/elements/internal/GenericArgImpl.qll linguist-generated /lib/codeql/rust/elements/internal/GenericArgListConstructor.qll linguist-generated diff --git a/rust/ql/lib/codeql/rust/elements/FormatTemplate.qll b/rust/ql/lib/codeql/rust/elements/FormatTemplate.qll new file mode 100644 index 0000000000000..a2c7138fde283 --- /dev/null +++ b/rust/ql/lib/codeql/rust/elements/FormatTemplate.qll @@ -0,0 +1,248 @@ +/** + * This module provides the classes modeling formatting templates. See also https://doc.rust-lang.org/std/fmt + */ + +private import FormatArgsExpr +private import LiteralExpr + +/** + * A regular expression for matching format elements in a formatting template. The + * regular expression is generated from the following python code: + * + * ```python + * identifier = "([A-Za-z_][A-Za-z0-9_]*)" + * integer = "([0-9]+)" + * + * # argument := integer | identifier + * argument = "({integer}|{identifier})".format(integer=integer, identifier=identifier) + * + * # parameter := argument '$' + * parameter = "(({argument})\\$)".format(argument=argument) + * + * # count := parameter | integer + * count = "({parameter}|{integer})".format(integer=integer, parameter=parameter) + * + * # fill := character + * fill = "(.)" + * + * # align := '<' | '^' | '>' + * align = "([<^>])" + * + * # sign := '+' | '-' + * sign = "([+-])" + * + * # width := count + * width = count + * + * # precision := count | '*' + * precision = "({count}|(\\*))".format(count=count) + * + * # type := '' | '?' | 'x?' | 'X?' | identifier + * type = "(|\\?|x\\?|X\\?|{identifier})".format(identifier=identifier) + * + * # format_spec := [[fill]align][sign]['#']['0'][width]['.' precision]type + * format_spec = "({fill}?{align})?{sign}?(#)?(0)?{width}?(\\.{precision})?{type}".format(fill=fill, align=align, sign=sign, width=width, precision=precision, type=type) + * + * # format := '{' [ argument ] [ ':' format_spec ] [ ws ] * '}' + * format = "(\\{{{argument}?(:{format_spec})?\s*}\\})".format(argument=argument, format_spec=format_spec) + * + * ``` + */ +private string formatRegex() { + result = + "(\\{(([0-9]+)|([A-Za-z_][A-Za-z0-9_]*))?(:((.)?([<^>]))?([+-])?(#)?(0)?(((([0-9]+)|([A-Za-z_][A-Za-z0-9_]*))\\$)|([0-9]+))?(\\.((((([0-9]+)|([A-Za-z_][A-Za-z0-9_]*))\\$)|([0-9]+))|(\\*)))?(|\\?|x\\?|X\\?|([A-Za-z_][A-Za-z0-9_]*)))?\\s*\\})" +} + +private string textRegex() { result = "([^{}]|\\{\\{|\\}\\})+" } + +private string part(FormatArgsExpr parent, int occurrenceIndex, int occurrenceOffset) { + result = + parent + .getTemplate() + .(LiteralExpr) + .getTextValue() + // TODO: should also handle surrounding quotes and escaped characters + .regexpFind(textRegex() + "|" + formatRegex(), occurrenceIndex, occurrenceOffset) +} + +private newtype TFormatTemplateElem = + TFormat(FormatArgsExpr parent, string text, int index, int offset) { + text = part(parent, index, offset) and text.regexpMatch(formatRegex()) + } + +private newtype TFormatArgumentKind = + TElement() or + TWidth() or + TPrecision() + +private newtype TFormatArgumentT = + TFormatArgument( + TFormat parent, TFormatArgumentKind kind, string value, boolean positional, int offset + ) { + exists(string text, int formatOffset, int group | + group = [3, 4] and offset = formatOffset + 1 and kind = TElement() + or + group = [15, 16] and + offset = formatOffset + min(text.indexOf(value + "$")) and + kind = TWidth() + or + group = [23, 24] and + offset = formatOffset + max(text.indexOf(value + "$")) and + kind = TPrecision() + | + parent = TFormat(_, text, _, formatOffset) and + value = text.regexpCapture(formatRegex(), group) and + if group % 2 = 1 then positional = true else positional = false + ) + } + +/** + * A format element in a formatting template. For example the `{}` in: + * ```rust + * println!("Hello {}", "world"); + * ``` + */ +class Format extends TFormat { + private FormatArgsExpr parent; + private string text; + private int index; + private int offset; + + Format() { this = TFormat(parent, text, index, offset) } + + /** Gets a textual representation of this element. */ + string toString() { result = text } + + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Providing locations in CodeQL queries](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + parent + .getTemplate() + .getLocation() + .hasLocationInfo(filepath, startline, startcolumn - offset, _, _) and + endline = startline and + endcolumn = startcolumn + text.length() - 1 + } + + /** Gets a the parent of this `Format`. */ + FormatArgsExpr getParent() { result = parent } + + /** Gets the index of this `Format` node. */ + int getIndex() { result = index } + + /** + * Gets the name or position reference of this format, if any. For example `name` and `0` in: + * ```rust + * let name = "Alice"; + * println!("{name} in wonderland"); + * println!("{0} in wonderland", name); + * ``` + */ + FormatArgument getArgumentRef() { + result.getParent() = this and result = TFormatArgument(_, TElement(), _, _, _) + } + + /** + * Gets the name or position reference of the width parameter in this format, if any. For example `width` and `1` in: + * ```rust + * let width = 6; + * println!("{:width$}", PI); + * println!("{:1$}", PI, width); + * ``` + */ + FormatArgument getWidthArgument() { + result.getParent() = this and result = TFormatArgument(_, TWidth(), _, _, _) + } + + /** + * Gets the name or position reference of the width parameter in this format, if any. For example `prec` and `1` in: + * ```rust + * let prec = 6; + * println!("{:.prec$}", PI); + * println!("{:.1$}", PI, prec); + * ``` + */ + FormatArgument getPrecisionArgument() { + result.getParent() = this and result = TFormatArgument(_, TPrecision(), _, _, _) + } +} + +/** + * An argument in a format element in a formatting template. For example the `width`, `precision`, and `value` in: + * ```rust + * println!("Value {value:#width$.precision$}"); + * ``` + * or the `0`, `1` and `2` in: + * ```rust + * println!("Value {0:#1$.2$}", value, width, precision); + * ``` + */ +class FormatArgument extends TFormatArgumentT { + private Format parent; + string name; + private int offset; + + FormatArgument() { this = TFormatArgument(parent, _, name, _, offset) } + + /** Gets a textual representation of this element. */ + string toString() { result = name } + + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Providing locations in CodeQL queries](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + // TODO: handle locations in multi-line comments + // TODO: handle the case where the template is from a nested macro call + parent + .getParent() + .getTemplate() + .getLocation() + .hasLocationInfo(filepath, startline, startcolumn - offset, _, _) and + endline = startline and + endcolumn = startcolumn + name.length() - 1 + } + + /** Gets a the parent of this `FormatArgument`. */ + Format getParent() { result = parent } +} + +/** + * A positional `FormatArgument`. For example `0` in + * ```rust + * let name = "Alice"; + * println!("{0} in wonderland", name); + * ``` + */ +class PositionalFormatArgument extends FormatArgument { + PositionalFormatArgument() { this = TFormatArgument(_, _, _, true, _) } + + /** Gets the index of this positional argument */ + int getIndex() { result = name.toInt() } +} + +/** + * A named `FormatArgument`. For example `name` in + * ```rust + * let name = "Alice"; + * println!("{name} in wonderland"); + * ``` + */ +class NamedFormatArgument extends FormatArgument { + NamedFormatArgument() { this = TFormatArgument(_, _, _, false, _) } + + /** Gets the name of this named argument */ + string getName() { result = name } +} diff --git a/rust/ql/lib/codeql/rust/elements/internal/FormatArgsExprImpl.qll b/rust/ql/lib/codeql/rust/elements/internal/FormatArgsExprImpl.qll index 0cf47a9ed1fe0..7524a385e9efb 100644 --- a/rust/ql/lib/codeql/rust/elements/internal/FormatArgsExprImpl.qll +++ b/rust/ql/lib/codeql/rust/elements/internal/FormatArgsExprImpl.qll @@ -1,4 +1,3 @@ -// generated by codegen, remove this comment if you wish to edit this file /** * This module provides a hand-modifiable wrapper around the generated class `FormatArgsExpr`. * @@ -6,17 +5,27 @@ */ private import codeql.rust.elements.internal.generated.FormatArgsExpr +private import codeql.rust.elements.FormatTemplate /** * INTERNAL: This module contains the customizable definition of `FormatArgsExpr` and should not * be referenced directly. */ module Impl { + // the following QLdoc is generated: if you need to edit it, do it in the schema file /** * A FormatArgsExpr. For example: * ```rust * todo!() * ``` */ - class FormatArgsExpr extends Generated::FormatArgsExpr { } + class FormatArgsExpr extends Generated::FormatArgsExpr { + /** + * Gets the `index`th format of this `FormatArgsExpr`'s formatting template (0-based). + */ + Format getFormat(int index) { + result = + rank[index + 1](Format f, int i | f.getParent() = this and f.getIndex() = i | f order by i) + } + } } diff --git a/rust/ql/test/library-tests/formatstrings/FormatString.expected b/rust/ql/test/library-tests/formatstrings/FormatString.expected new file mode 100644 index 0000000000000..2a4f078a25fc6 --- /dev/null +++ b/rust/ql/test/library-tests/formatstrings/FormatString.expected @@ -0,0 +1 @@ +| 1 | diff --git a/rust/ql/test/library-tests/formatstrings/FormatTemplate.expected b/rust/ql/test/library-tests/formatstrings/FormatTemplate.expected new file mode 100644 index 0000000000000..6750a5dc5f03f --- /dev/null +++ b/rust/ql/test/library-tests/formatstrings/FormatTemplate.expected @@ -0,0 +1,132 @@ +getFormat +| main.rs:5:14:5:61 | FormatArgsExpr | main.rs:5:21:5:46 | {value:#width$.precision$} | 0 | +| main.rs:6:14:6:56 | FormatArgsExpr | main.rs:6:21:6:30 | {0:#1$.2$} | 0 | +| main.rs:7:14:7:40 | FormatArgsExpr | main.rs:7:21:7:22 | {} | 0 | +| main.rs:7:14:7:40 | FormatArgsExpr | main.rs:7:24:7:25 | {} | 1 | +| main.rs:11:14:11:34 | FormatArgsExpr | main.rs:11:22:11:23 | {} | 0 | +| main.rs:12:14:12:34 | FormatArgsExpr | main.rs:12:29:12:30 | {} | 0 | +| main.rs:13:14:13:27 | FormatArgsExpr | main.rs:13:15:13:18 | {:?} | 0 | +| main.rs:14:14:14:33 | FormatArgsExpr | main.rs:14:15:14:21 | {value} | 0 | +| main.rs:16:14:16:30 | FormatArgsExpr | main.rs:16:21:16:28 | {people} | 0 | +| main.rs:17:14:17:26 | FormatArgsExpr | main.rs:17:15:17:16 | {} | 0 | +| main.rs:17:14:17:26 | FormatArgsExpr | main.rs:17:18:17:19 | {} | 1 | +| main.rs:18:14:18:24 | FormatArgsExpr | main.rs:18:15:18:19 | {:04} | 0 | +| main.rs:19:14:19:32 | FormatArgsExpr | main.rs:19:15:19:19 | {:#?} | 0 | +| main.rs:21:14:21:34 | FormatArgsExpr | main.rs:21:15:21:17 | {1} | 0 | +| main.rs:21:14:21:34 | FormatArgsExpr | main.rs:21:19:21:20 | {} | 1 | +| main.rs:21:14:21:34 | FormatArgsExpr | main.rs:21:22:21:24 | {0} | 2 | +| main.rs:21:14:21:34 | FormatArgsExpr | main.rs:21:26:21:27 | {} | 3 | +| main.rs:22:14:22:31 | FormatArgsExpr | main.rs:22:21:22:24 | {:5} | 0 | +| main.rs:23:14:23:35 | FormatArgsExpr | main.rs:23:21:23:25 | {:1$} | 0 | +| main.rs:24:14:24:36 | FormatArgsExpr | main.rs:24:21:24:26 | {1:0$} | 0 | +| main.rs:25:14:25:47 | FormatArgsExpr | main.rs:25:21:25:29 | {:width$} | 0 | +| main.rs:27:14:27:36 | FormatArgsExpr | main.rs:27:21:27:29 | {:width$} | 0 | +| main.rs:28:24:28:42 | FormatArgsExpr | main.rs:28:31:28:35 | {:<5} | 0 | +| main.rs:29:24:29:43 | FormatArgsExpr | main.rs:29:31:29:36 | {:-<5} | 0 | +| main.rs:30:24:30:42 | FormatArgsExpr | main.rs:30:31:30:35 | {:^5} | 0 | +| main.rs:31:24:31:42 | FormatArgsExpr | main.rs:31:31:31:35 | {:>5} | 0 | +| main.rs:32:14:32:57 | FormatArgsExpr | main.rs:32:21:32:26 | {:^15} | 0 | +| main.rs:32:39:32:56 | FormatArgsExpr | main.rs:32:40:32:43 | {:?} | 0 | +| main.rs:33:24:33:39 | FormatArgsExpr | main.rs:33:31:33:34 | {:+} | 0 | +| main.rs:34:24:34:35 | FormatArgsExpr | main.rs:34:25:34:29 | {:#x} | 0 | +| main.rs:35:24:35:40 | FormatArgsExpr | main.rs:35:31:35:35 | {:05} | 0 | +| main.rs:36:24:36:41 | FormatArgsExpr | main.rs:36:31:36:35 | {:05} | 0 | +| main.rs:37:24:37:38 | FormatArgsExpr | main.rs:37:25:37:32 | {:#010x} | 0 | +| main.rs:39:14:39:45 | FormatArgsExpr | main.rs:39:21:39:23 | {0} | 0 | +| main.rs:39:14:39:45 | FormatArgsExpr | main.rs:39:28:39:33 | {1:.5} | 1 | +| main.rs:41:14:41:49 | FormatArgsExpr | main.rs:41:21:41:23 | {1} | 0 | +| main.rs:41:14:41:49 | FormatArgsExpr | main.rs:41:28:41:34 | {2:.0$} | 1 | +| main.rs:43:14:43:49 | FormatArgsExpr | main.rs:43:21:43:23 | {0} | 0 | +| main.rs:43:14:43:49 | FormatArgsExpr | main.rs:43:28:43:34 | {2:.1$} | 1 | +| main.rs:45:14:45:46 | FormatArgsExpr | main.rs:45:21:45:22 | {} | 0 | +| main.rs:45:14:45:46 | FormatArgsExpr | main.rs:45:27:45:31 | {:.*} | 1 | +| main.rs:47:14:47:48 | FormatArgsExpr | main.rs:47:21:47:23 | {1} | 0 | +| main.rs:47:14:47:48 | FormatArgsExpr | main.rs:47:28:47:33 | {2:.*} | 1 | +| main.rs:48:14:48:47 | FormatArgsExpr | main.rs:48:21:48:22 | {} | 0 | +| main.rs:48:14:48:47 | FormatArgsExpr | main.rs:48:27:48:32 | {2:.*} | 1 | +| main.rs:49:14:49:72 | FormatArgsExpr | main.rs:49:21:49:22 | {} | 0 | +| main.rs:49:14:49:72 | FormatArgsExpr | main.rs:49:27:49:41 | {number:.prec$} | 1 | +| main.rs:52:9:55:22 | FormatArgsExpr | main.rs:52:10:52:11 | {} | 0 | +| main.rs:52:9:55:22 | FormatArgsExpr | main.rs:52:15:52:23 | {name:.*} | 1 | +| main.rs:58:9:61:24 | FormatArgsExpr | main.rs:58:10:58:11 | {} | 0 | +| main.rs:58:9:61:24 | FormatArgsExpr | main.rs:58:15:58:23 | {name:.*} | 1 | +| main.rs:64:9:67:24 | FormatArgsExpr | main.rs:64:10:64:11 | {} | 0 | +| main.rs:64:9:67:24 | FormatArgsExpr | main.rs:64:15:64:25 | {name:>8.*} | 1 | +| main.rs:70:12:70:31 | FormatArgsExpr | main.rs:70:13:70:20 | {0:.1$e} | 0 | +| main.rs:71:12:71:31 | FormatArgsExpr | main.rs:71:13:71:20 | {0:.1$e} | 0 | +| main.rs:73:14:73:35 | FormatArgsExpr | main.rs:73:28:73:29 | {} | 0 | +getArgumentRef +| main.rs:5:21:5:46 | {value:#width$.precision$} | main.rs:5:22:5:26 | value | +| main.rs:6:21:6:30 | {0:#1$.2$} | main.rs:6:22:6:22 | 0 | +| main.rs:14:15:14:21 | {value} | main.rs:14:16:14:20 | value | +| main.rs:16:21:16:28 | {people} | main.rs:16:22:16:27 | people | +| main.rs:21:15:21:17 | {1} | main.rs:21:16:21:16 | 1 | +| main.rs:21:22:21:24 | {0} | main.rs:21:23:21:23 | 0 | +| main.rs:24:21:24:26 | {1:0$} | main.rs:24:22:24:22 | 1 | +| main.rs:39:21:39:23 | {0} | main.rs:39:22:39:22 | 0 | +| main.rs:39:28:39:33 | {1:.5} | main.rs:39:29:39:29 | 1 | +| main.rs:41:21:41:23 | {1} | main.rs:41:22:41:22 | 1 | +| main.rs:41:28:41:34 | {2:.0$} | main.rs:41:29:41:29 | 2 | +| main.rs:43:21:43:23 | {0} | main.rs:43:22:43:22 | 0 | +| main.rs:43:28:43:34 | {2:.1$} | main.rs:43:29:43:29 | 2 | +| main.rs:47:21:47:23 | {1} | main.rs:47:22:47:22 | 1 | +| main.rs:47:28:47:33 | {2:.*} | main.rs:47:29:47:29 | 2 | +| main.rs:48:27:48:32 | {2:.*} | main.rs:48:28:48:28 | 2 | +| main.rs:49:27:49:41 | {number:.prec$} | main.rs:49:28:49:33 | number | +| main.rs:52:15:52:23 | {name:.*} | main.rs:52:16:52:19 | name | +| main.rs:58:15:58:23 | {name:.*} | main.rs:58:16:58:19 | name | +| main.rs:64:15:64:25 | {name:>8.*} | main.rs:64:16:64:19 | name | +| main.rs:70:13:70:20 | {0:.1$e} | main.rs:70:14:70:14 | 0 | +| main.rs:71:13:71:20 | {0:.1$e} | main.rs:71:14:71:14 | 0 | +getWidthArgument +| main.rs:5:21:5:46 | {value:#width$.precision$} | main.rs:5:29:5:33 | width | +| main.rs:6:21:6:30 | {0:#1$.2$} | main.rs:6:25:6:25 | 1 | +| main.rs:23:21:23:25 | {:1$} | main.rs:23:23:23:23 | 1 | +| main.rs:24:21:24:26 | {1:0$} | main.rs:24:24:24:24 | 0 | +| main.rs:25:21:25:29 | {:width$} | main.rs:25:23:25:27 | width | +| main.rs:27:21:27:29 | {:width$} | main.rs:27:23:27:27 | width | +getPrecisionArgument +| main.rs:5:21:5:46 | {value:#width$.precision$} | main.rs:5:36:5:44 | precision | +| main.rs:6:21:6:30 | {0:#1$.2$} | main.rs:6:28:6:28 | 2 | +| main.rs:41:28:41:34 | {2:.0$} | main.rs:41:32:41:32 | 0 | +| main.rs:43:28:43:34 | {2:.1$} | main.rs:43:32:43:32 | 1 | +| main.rs:49:27:49:41 | {number:.prec$} | main.rs:49:36:49:39 | prec | +| main.rs:70:13:70:20 | {0:.1$e} | main.rs:70:17:70:17 | 1 | +| main.rs:71:13:71:20 | {0:.1$e} | main.rs:71:17:71:17 | 1 | +getIndex +| main.rs:6:22:6:22 | 0 | 0 | +| main.rs:6:25:6:25 | 1 | 1 | +| main.rs:6:28:6:28 | 2 | 2 | +| main.rs:21:16:21:16 | 1 | 1 | +| main.rs:21:23:21:23 | 0 | 0 | +| main.rs:23:23:23:23 | 1 | 1 | +| main.rs:24:22:24:22 | 1 | 1 | +| main.rs:24:24:24:24 | 0 | 0 | +| main.rs:39:22:39:22 | 0 | 0 | +| main.rs:39:29:39:29 | 1 | 1 | +| main.rs:41:22:41:22 | 1 | 1 | +| main.rs:41:29:41:29 | 2 | 2 | +| main.rs:41:32:41:32 | 0 | 0 | +| main.rs:43:22:43:22 | 0 | 0 | +| main.rs:43:29:43:29 | 2 | 2 | +| main.rs:43:32:43:32 | 1 | 1 | +| main.rs:47:22:47:22 | 1 | 1 | +| main.rs:47:29:47:29 | 2 | 2 | +| main.rs:48:28:48:28 | 2 | 2 | +| main.rs:70:14:70:14 | 0 | 0 | +| main.rs:70:17:70:17 | 1 | 1 | +| main.rs:71:14:71:14 | 0 | 0 | +| main.rs:71:17:71:17 | 1 | 1 | +getName +| main.rs:5:22:5:26 | value | value | +| main.rs:5:29:5:33 | width | width | +| main.rs:5:36:5:44 | precision | precision | +| main.rs:14:16:14:20 | value | value | +| main.rs:16:22:16:27 | people | people | +| main.rs:25:23:25:27 | width | width | +| main.rs:27:23:27:27 | width | width | +| main.rs:49:28:49:33 | number | number | +| main.rs:49:36:49:39 | prec | prec | +| main.rs:52:16:52:19 | name | name | +| main.rs:58:16:58:19 | name | name | +| main.rs:64:16:64:19 | name | name | diff --git a/rust/ql/test/library-tests/formatstrings/FormatTemplate.ql b/rust/ql/test/library-tests/formatstrings/FormatTemplate.ql new file mode 100644 index 0000000000000..eefd08962beb3 --- /dev/null +++ b/rust/ql/test/library-tests/formatstrings/FormatTemplate.ql @@ -0,0 +1,21 @@ +import rust +import codeql.rust.elements.FormatArgsExpr +import codeql.rust.elements.FormatTemplate + +query predicate getFormat(FormatArgsExpr arg, Format format, int index) { + format = arg.getFormat(index) +} + +query predicate getArgumentRef(Format format, FormatArgument arg) { arg = format.getArgumentRef() } + +query predicate getWidthArgument(Format format, FormatArgument arg) { + arg = format.getWidthArgument() +} + +query predicate getPrecisionArgument(Format format, FormatArgument arg) { + arg = format.getPrecisionArgument() +} + +query predicate getIndex(PositionalFormatArgument arg, int index) { arg.getIndex() = index } + +query predicate getName(NamedFormatArgument arg, string name) { arg.getName() = name } diff --git a/rust/ql/test/library-tests/formatstrings/main.rs b/rust/ql/test/library-tests/formatstrings/main.rs new file mode 100644 index 0000000000000..b056262f46fbd --- /dev/null +++ b/rust/ql/test/library-tests/formatstrings/main.rs @@ -0,0 +1,77 @@ +fn main() { + let width = 4; + let precision = 2; + let value = 10; + println!("Value {value:#width$.precision$}", value = 10.5); + println!("Value {0:#1$.2$}", value, width, precision); + println!("Value {} {}", value, width); + + // Examples from https://doc.rust-lang.org/std/fmt + println!("Hello"); + println!("Hello, {}!", "world"); + println!("The number is {}", 1); + println!("{:?}", (3, 4)); + println!("{value}", value = 4); + let people = "Rustaceans"; + println!("Hello {people}!"); + println!("{} {}", 1, 2); + println!("{:04}", 42); + println!("{:#?}", (100, 200)); + + println!("{1} {} {0} {}", 1, 2); + println!("Hello {:5}!", "x"); + println!("Hello {:1$}!", "x", 5); + println!("Hello {1:0$}!", 5, "x"); + println!("Hello {:width$}!", "x", width = 5); + let width = 5; + println!("Hello {:width$}!", "x"); + assert_eq!(format!("Hello {:<5}!", "x"), "Hello x !"); + assert_eq!(format!("Hello {:-<5}!", "x"), "Hello x----!"); + assert_eq!(format!("Hello {:^5}!", "x"), "Hello x !"); + assert_eq!(format!("Hello {:>5}!", "x"), "Hello x!"); + println!("Hello {:^15}!", format!("{:?}", Some("hi"))); + assert_eq!(format!("Hello {:+}!", 5), "Hello +5!"); + assert_eq!(format!("{:#x}!", 27), "0x1b!"); + assert_eq!(format!("Hello {:05}!", 5), "Hello 00005!"); + assert_eq!(format!("Hello {:05}!", -5), "Hello -0005!"); + assert_eq!(format!("{:#010x}!", 27), "0x0000001b!"); + + println!("Hello {0} is {1:.5}", "x", 0.01); + + println!("Hello {1} is {2:.0$}", 5, "x", 0.01); + + println!("Hello {0} is {2:.1$}", "x", 5, 0.01); + + println!("Hello {} is {:.*}", "x", 5, 0.01); + + println!("Hello {1} is {2:.*}", 5, "x", 0.01); + println!("Hello {} is {2:.*}", "x", 5, 0.01); + println!("Hello {} is {number:.prec$}", "x", prec = 5, number = 0.01); + + println!( + "{}, `{name:.*}` has 3 fractional digits", + "Hello", + 3, + name = 1234.56 + ); + println!( + "{}, `{name:.*}` has 3 characters", + "Hello", + 3, + name = "1234.56" + ); + println!( + "{}, `{name:>8.*}` has 3 right-aligned characters", + "Hello", + 3, + name = "1234.56" + ); + + print!("{0:.1$e}", 12345, 3); + print!("{0:.1$e}", 12355, 3); + + println!("The value is {}", 1.5); + + assert_eq!(format!("Hello {{}}"), "Hello {}"); + assert_eq!(format!("{{ Hello"), "{ Hello"); +}