Read-only mirrors. Development, issues and pull requests are on GitHub.

summaryrefslogtreecommitdiffhomepage
path: root/docs
diff options
context:
space:
mode:
authorMartin Hořeňovský2023-12-13 17:07:59 +0100
committerMartin Hořeňovský2023-12-13 17:24:23 +0100
commit3acb8b30f1ce0c801de0e3880ea1f6467dd0105c (patch)
tree9ea19e3e5aaed557cd3c5a71616ced0c81b83cfb /docs
parent3f23192e55008f9a4b00590f7c6b5e87f273b099 (diff)
downloadcatch3-3acb8b30f1ce0c801de0e3880ea1f6467dd0105c.tar.gz
More detailed examples for lifetimes in combined matcher exprs
Example taken from #2777
Diffstat (limited to 'docs')
-rw-r--r--docs/matchers.md42
1 files changed, 30 insertions, 12 deletions
diff --git a/docs/matchers.md b/docs/matchers.md
index b96cc5f4..d5be1f5a 100644
--- a/docs/matchers.md
+++ b/docs/matchers.md
@@ -50,25 +50,43 @@ Both of the string matchers used in the examples above live in the
`catch_matchers_string.hpp` header, so to compile the code above also
requires `#include <catch2/matchers/catch_matchers_string.hpp>`.
+### Combining operators and lifetimes
+
**IMPORTANT**: The combining operators do not take ownership of the
-matcher objects being combined. This means that if you store combined
-matcher object, you have to ensure that the matchers being combined
-outlive its last use. What this means is that the following code leads
-to a use-after-free (UAF):
+matcher objects being combined.
+
+This means that if you store combined matcher object, you have to ensure
+that the individual matchers being combined outlive the combined matcher.
+Note that the negation matcher from `!` also counts as combining matcher
+for this.
+Explained on an example, this is fine
```cpp
-#include <catch2/catch_test_macros.hpp>
-#include <catch2/matchers/catch_matchers_string.hpp>
+CHECK_THAT(value, WithinAbs(0, 2e-2) && !WithinULP(0., 1));
+```
-TEST_CASE("Bugs, bugs, bugs", "[Bug]"){
- std::string str = "Bugs as a service";
+and so is this
+```cpp
+auto is_close_to_zero = WithinAbs(0, 2e-2);
+auto is_zero = WithinULP(0., 1);
- auto match_expression = Catch::Matchers::EndsWith( "as a service" ) ||
- (Catch::Matchers::StartsWith( "Big data" ) && !Catch::Matchers::ContainsSubstring( "web scale" ) );
- REQUIRE_THAT(str, match_expression);
-}
+CHECK_THAT(value, is_close_to_zero && !is_zero);
```
+but this is not
+```cpp
+auto is_close_to_zero = WithinAbs(0, 2e-2);
+auto is_zero = WithinULP(0., 1);
+auto is_close_to_but_not_zero = is_close_to_zero && !is_zero;
+
+CHECK_THAT(a_value, is_close_to_but_not_zero); // UAF
+```
+
+because `!is_zero` creates a temporary instance of Negation matcher,
+which the `is_close_to_but_not_zero` refers to. After the line ends,
+the temporary is destroyed and the combined `is_close_to_but_not_zero`
+matcher now refers to non-existent object, so using it causes use-after-free.
+
## Built-in matchers