llvmbot wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang-tidy

Author: Valentyn Yukhymenko (BaLiKfromUA)

<details>
<summary>Changes</summary>

### Problem

`bugprone-unchecked-optional-access` produces a lot of false positives if type 
inside of `bsl::optional` or   `bdlb::NullableValue` is **allocator-aware**.

This is a very common pattern, especially due to frequent use of `bsl::string`.

[Compiler explorer example to showcase false-positives with BDE 
library](https://compiler-explorer.com/z/P4zh7KbGx)

### Context

https://github.com/llvm/llvm-project/pull/101450 added support for analysing 
`bsl::optional` access patterns.

However, mock `bsl::optional` type has been very simplified for testing 
purposes which lead to missing false-positives related to _inheritance_ logic 
for this type.

[According to this 
article](https://bloomberg.github.io/bde/articles/bsl_optional.html#interoperability-between-bsl-optional-and-bdlb-nullablevalue),
  there are two ways of inheritance for `bsl::optional` and 
`bdlb::NullableValue`:
1. C++17 non-allocator-aware type
` bdlb::NullableValue&lt;T&gt; -&gt; bsl::optional&lt;T&gt; -&gt; 
std::optional&lt;T&gt;`
2.  C++17 **Allocator-Aware**, and pre-C++17
`bdlb::NullableValue&lt;T&gt; -&gt; bsl::optional&lt;T&gt;`

But this is not a full picture :(

In practice, there is an additional layer in the inheritance chain:
`BloombergLP::bslstl::Optional_Base`.

Thus, the actual inheritance structure is:
1. C++17 non-allocator-aware
`bdlb::NullableValue&lt;T&gt; -&gt; bsl::optional&lt;T&gt; -&gt; 
BloombergLP::bslstl::Optional_Base&lt;T&gt; -&gt; std::optional&lt;T&gt;`
2.  C++17 **Allocator-Aware**, and pre-C++17
`bdlb::NullableValue&lt;T&gt; -&gt; bsl::optional&lt;T&gt; -&gt; 
BloombergLP::bslstl::Optional_Base&lt;T&gt;`

[Source code to show this 
inheritance](https://github.com/bloomberg/bde/blob/f8b09a9298a5a76741c0820344c8850bf0b2e177/groups/bsl/bslstl/bslstl_optional.h#L1851)

### Root cause

IIUC, because of this inheritance logic, function calls to 
`bsl::optional::hasValue()` are processed like:
1. `std::optional::has_value()` for non-allocator-aware type.
2. `BloombergLP::bslstl::Optional_Base::has_value()` for allocator-aware type.

Obviously, similar conversion are true for other common methods like `.value()`

**This PR tries to solve this issue by improving mocks and adding 
`BloombergLP::bslstl::Optional_Base&lt;T&gt;` to list of supported optional 
types**




---
Full diff: https://github.com/llvm/llvm-project/pull/168863.diff


5 Files Affected:

- (modified) clang-tools-extra/docs/ReleaseNotes.rst (+3-2) 
- (modified) 
clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/unchecked-optional-access/bde/types/bsl_optional.h
 (+50-30) 
- (added) 
clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/unchecked-optional-access/std/types/optional.h
 (+57) 
- (modified) 
clang-tools-extra/test/clang-tidy/checkers/bugprone/unchecked-optional-access.cpp
 (+40) 
- (modified) 
clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp (+6) 


``````````diff
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst 
b/clang-tools-extra/docs/ReleaseNotes.rst
index a6f80e3721db1..5ebb717bfdc53 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -384,8 +384,9 @@ Changes in existing checks
 - Improved :doc:`bugprone-unchecked-optional-access
   <clang-tidy/checks/bugprone/unchecked-optional-access>` check by supporting
   ``NullableValue::makeValue`` and ``NullableValue::makeValueInplace`` to
-  prevent false-positives for ``BloombergLP::bdlb::NullableValue`` type, and by
-  adding the `IgnoreValueCalls` option to suppress diagnostics for
+  prevent false-positives for ``BloombergLP::bdlb::NullableValue``. Fixed
+  false-positives for ``bsl::optional`` containing allocator-aware type.
+  Added the `IgnoreValueCalls` option to suppress diagnostics for
   ``optional::value()`` and the `IgnoreSmartPointerDereference` option to
   ignore optionals reached via smart-pointer-like dereference, while still
   diagnosing UB-prone dereferences via ``operator*`` and ``operator->``.
diff --git 
a/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/unchecked-optional-access/bde/types/bsl_optional.h
 
b/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/unchecked-optional-access/bde/types/bsl_optional.h
index 7e1a129e04a55..7d7d14cde6429 100644
--- 
a/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/unchecked-optional-access/bde/types/bsl_optional.h
+++ 
b/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/unchecked-optional-access/bde/types/bsl_optional.h
@@ -1,44 +1,40 @@
 #ifndef 
LLVM_CLANG_TOOLS_EXTRA_TEST_CLANG_TIDY_CHECKERS_INPUTS_BDE_TYPES_OPTIONAL_H_
 #define 
LLVM_CLANG_TOOLS_EXTRA_TEST_CLANG_TIDY_CHECKERS_INPUTS_BDE_TYPES_OPTIONAL_H_
 
-/// Mock of `bsl::optional`.
+#include "../../std/types/optional.h"
+
 namespace bsl {
+  class string {};
+}
 
-// clang-format off
-template <typename T> struct remove_reference      { using type = T; };
-template <typename T> struct remove_reference<T&>  { using type = T; };
-template <typename T> struct remove_reference<T&&> { using type = T; };
-// clang-format on
+/// Mock of `BloombergLP::bslstl::Optional_Base`
+namespace BloombergLP::bslstl {
 
-template <typename T>
-using remove_reference_t = typename remove_reference<T>::type;
+template <class T>
+constexpr bool isAllocatorAware() {
+  return false;
+}
 
-template <typename T>
-constexpr T &&forward(remove_reference_t<T> &t) noexcept;
+template <>
+constexpr bool isAllocatorAware<bsl::string>() {
+  return true;
+}
 
-template <typename T>
-constexpr T &&forward(remove_reference_t<T> &&t) noexcept;
+// Note: real `Optional_Base` uses `BloombergLP::bslma::UsesBslmaAllocator`
+// to check if type is alocator-aware.
+// This is simplified mock to illustrate similar behaviour.
+template <class T, bool AA = isAllocatorAware<T>()>
+class Optional_Base {
 
-template <typename T>
-constexpr remove_reference_t<T> &&move(T &&x);
-
-struct nullopt_t {
-  constexpr explicit nullopt_t() {}
 };
 
-constexpr nullopt_t nullopt;
+template <class T>
+class Optional_Base<T, false> : public std::optional<T> {
+};
 
-template <typename T>
-class optional {
+template <class T>
+class Optional_Base<T, true> {
 public:
-  constexpr optional() noexcept;
-
-  constexpr optional(nullopt_t) noexcept;
-
-  optional(const optional &) = default;
-
-  optional(optional &&) = default;
-
   const T &operator*() const &;
   T &operator*() &;
   const T &&operator*() const &&;
@@ -65,9 +61,33 @@ class optional {
 
   void reset() noexcept;
 
-  void swap(optional &rhs) noexcept;
+  void swap(Optional_Base &rhs) noexcept;
+
+  template <typename U> Optional_Base &operator=(const U &u);
+};
+
+} // namespace BloombergLP::bslstl
+
+
+/// Mock of `bsl::optional`.
+namespace bsl {
 
-  template <typename U> optional &operator=(const U &u);
+struct nullopt_t {
+  constexpr explicit nullopt_t() {}
+};
+
+constexpr nullopt_t nullopt;
+
+template <typename T>
+class optional : public BloombergLP::bslstl::Optional_Base<T> {
+public:
+  constexpr optional() noexcept;
+
+  constexpr optional(nullopt_t) noexcept;
+
+  optional(const optional &) = default;
+
+  optional(optional &&) = default;
 };
 
 } // namespace bsl
diff --git 
a/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/unchecked-optional-access/std/types/optional.h
 
b/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/unchecked-optional-access/std/types/optional.h
new file mode 100644
index 0000000000000..75202b6cf1250
--- /dev/null
+++ 
b/clang-tools-extra/test/clang-tidy/checkers/bugprone/Inputs/unchecked-optional-access/std/types/optional.h
@@ -0,0 +1,57 @@
+#ifndef 
LLVM_CLANG_TOOLS_EXTRA_TEST_CLANG_TIDY_CHECKERS_INPUTS_STD_TYPES_OPTIONAL_H_
+#define 
LLVM_CLANG_TOOLS_EXTRA_TEST_CLANG_TIDY_CHECKERS_INPUTS_STD_TYPES_OPTIONAL_H_
+
+namespace std {
+
+struct nullopt_t {
+  constexpr explicit nullopt_t() {}
+};
+
+constexpr nullopt_t nullopt;
+
+template <typename T>
+class optional {
+public:
+  constexpr optional() noexcept;
+
+  constexpr optional(nullopt_t) noexcept;
+
+  optional(const optional &) = default;
+
+  optional(optional &&) = default;
+
+  const T &operator*() const &;
+  T &operator*() &;
+  const T &&operator*() const &&;
+  T &&operator*() &&;
+
+  const T *operator->() const;
+  T *operator->();
+
+  const T &value() const &;
+  T &value() &;
+  const T &&value() const &&;
+  T &&value() &&;
+
+  constexpr explicit operator bool() const noexcept;
+  constexpr bool has_value() const noexcept;
+
+  template <typename U>
+  constexpr T value_or(U &&v) const &;
+  template <typename U>
+  T value_or(U &&v) &&;
+
+  template <typename... Args>
+  T &emplace(Args &&...args);
+
+  void reset() noexcept;
+
+  void swap(optional &rhs) noexcept;
+
+  template <typename U> optional &operator=(const U &u);
+};
+
+} // namespace std
+
+
+#endif // 
LLVM_CLANG_TOOLS_EXTRA_TEST_CLANG_TIDY_CHECKERS_INPUTS_STD_TYPES_OPTIONAL_H_
\ No newline at end of file
diff --git 
a/clang-tools-extra/test/clang-tidy/checkers/bugprone/unchecked-optional-access.cpp
 
b/clang-tools-extra/test/clang-tidy/checkers/bugprone/unchecked-optional-access.cpp
index 8d70a9dc4861e..4b345cc2c8c5b 100644
--- 
a/clang-tools-extra/test/clang-tidy/checkers/bugprone/unchecked-optional-access.cpp
+++ 
b/clang-tools-extra/test/clang-tidy/checkers/bugprone/unchecked-optional-access.cpp
@@ -67,6 +67,21 @@ void bsl_optional_unchecked_value_access(const 
bsl::optional<int> &opt) {
   x = *opt;
 }
 
+void bsl_optional_unchecked_value_access_for_allocator_aware_class(const 
bsl::optional<bsl::string> &opt) {
+  opt.value();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: unchecked access to optional 
value [bugprone-unchecked-optional-access]
+
+  bsl::string x = *opt;
+  // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: unchecked access to optional 
value [bugprone-unchecked-optional-access]
+
+  if (!opt) {
+    return;
+  }
+
+  opt.value();
+  x = *opt;
+}
+
 void bsl_optional_checked_access(const bsl::optional<int> &opt) {
   if (opt.has_value()) {
     opt.value();
@@ -109,6 +124,31 @@ void nullable_value_unchecked_value_access(const 
BloombergLP::bdlb::NullableValu
   x = *opt;
 }
 
+void nullable_value_unchecked_value_access_for_allocator_aware_type(const 
BloombergLP::bdlb::NullableValue<bsl::string> &opt) {
+  opt.value();
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: unchecked access to optional 
value [bugprone-unchecked-optional-access]
+
+  bsl::string x = *opt;
+  // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: unchecked access to optional 
value [bugprone-unchecked-optional-access]
+
+  if (opt.isNull()) {
+    opt.value();
+  }
+  // CHECK-MESSAGES: :[[@LINE-2]]:5: warning: unchecked access to optional 
value [bugprone-unchecked-optional-access]
+
+  if (!opt) {
+    opt.value();
+  }
+  // CHECK-MESSAGES: :[[@LINE-2]]:5: warning: unchecked access to optional 
value [bugprone-unchecked-optional-access]
+
+  if (!opt.has_value()) {
+    return;
+  }
+
+  opt.value();
+  x = *opt;
+}
+
 void nullable_value_optional_checked_access(const 
BloombergLP::bdlb::NullableValue<int> &opt) {
   if (opt.has_value()) {
     opt.value();
diff --git 
a/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp 
b/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
index d90f5d4eaf7bb..1a4409f277c18 100644
--- a/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
+++ b/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
@@ -78,6 +78,12 @@ static bool hasOptionalClassName(const CXXRecordDecl &RD) {
                             isFullyQualifiedNamespaceEqualTo(*N, "folly"));
   }
 
+  if (RD.getName() == "Optional_Base") {
+    const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext());
+    return N != nullptr &&
+           isFullyQualifiedNamespaceEqualTo(*N, "bslstl", "BloombergLP");
+  }
+
   if (RD.getName() == "NullableValue") {
     const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext());
     return N != nullptr &&

``````````

</details>


https://github.com/llvm/llvm-project/pull/168863
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to