[clang] cd0d526 - [clang][dataflow] In `optional` model, match call return via hasType

Yitzhak Mandelbaum via cfe-commits cfe-commits at lists.llvm.org
Fri Jun 10 07:52:20 PDT 2022


Author: Sam Estep
Date: 2022-06-10T14:52:05Z
New Revision: cd0d52610d80116324f19983320ec6db2048450f

URL: https://github.com/llvm/llvm-project/commit/cd0d52610d80116324f19983320ec6db2048450f
DIFF: https://github.com/llvm/llvm-project/commit/cd0d52610d80116324f19983320ec6db2048450f.diff

LOG: [clang][dataflow] In `optional` model, match call return via hasType

Currently the unchecked-optional-access model fails on this example:

    #include <memory>
    #include <optional>

    void foo() {
      std::unique_ptr<std::optional<float>> x;
      *x = std::nullopt;
    }

You can verify the failure by saving the file as `foo.cpp` and running this command:

    clang-tidy -checks='-*,bugprone-unchecked-optional-access' foo.cpp -- -std=c++17

The failing `assert` is in the `transferAssignment` function of the `UncheckedOptionalAccessModel.cpp` file:

    assert(OptionalLoc != nullptr);

The symptom can be treated by replacing that `assert` with an early `return`:

    if (OptionalLoc == nullptr)
      return;

That would be better anyway since we cannot expect to always cover all possible LHS expressions, but it is out of scope for this patch and left as a followup.

Note that the failure did not occur on this very similar example:

    #include <optional>

    template <typename T>
    struct smart_ptr {
      T& operator*() &;
      T* operator->();
    };

    void foo() {
      smart_ptr<std::optional<float>> x;
      *x = std::nullopt;
    }

The difference is caused by the `isCallReturningOptional` matcher, which was previously checking the `functionDecl` of the `callee`. This patch changes it to instead use `hasType` directly on the call expression, fixing the failure for the `std::unique_ptr` example above.

Reviewed By: sgatev

Differential Revision: https://reviews.llvm.org/D127434

Added: 
    

Modified: 
    clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
    clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp b/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
index 00d22fbe8bb80..993d56e44a87d 100644
--- a/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
+++ b/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
@@ -165,8 +165,8 @@ auto isValueOrNotEqX() {
 }
 
 auto isCallReturningOptional() {
-  return callExpr(callee(functionDecl(returns(anyOf(
-      optionalOrAliasType(), referenceType(pointee(optionalOrAliasType())))))));
+  return callExpr(hasType(qualType(anyOf(
+      optionalOrAliasType(), referenceType(pointee(optionalOrAliasType()))))));
 }
 
 /// Creates a symbolic value for an `optional` value using `HasValueVal` as the

diff  --git a/clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp b/clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp
index 72efc724776b3..3c37bec82139c 100644
--- a/clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp
+++ b/clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp
@@ -179,6 +179,11 @@ struct is_void : is_same<void, typename remove_cv<T>::type> {};
 
 namespace detail {
 
+template <class T>
+auto try_add_lvalue_reference(int) -> type_identity<T&>;
+template <class T>
+auto try_add_lvalue_reference(...) -> type_identity<T>;
+
 template <class T>
 auto try_add_rvalue_reference(int) -> type_identity<T&&>;
 template <class T>
@@ -186,6 +191,10 @@ auto try_add_rvalue_reference(...) -> type_identity<T>;
 
 }  // namespace detail
 
+template <class T>
+struct add_lvalue_reference : decltype(detail::try_add_lvalue_reference<T>(0)) {
+};
+
 template <class T>
 struct add_rvalue_reference : decltype(detail::try_add_rvalue_reference<T>(0)) {
 };
@@ -2318,6 +2327,26 @@ TEST_P(UncheckedOptionalAccessTest, OptionalValueInitialization) {
       UnorderedElementsAre(Pair("merge", "unsafe: input.cc:19:7")));
 }
 
+TEST_P(UncheckedOptionalAccessTest, AssignThroughLvalueReferencePtr) {
+  ExpectLatticeChecksFor(
+      R"(
+    #include "unchecked_optional_access_test.h"
+
+    template <typename T>
+    struct smart_ptr {
+      typename std::add_lvalue_reference<T>::type operator*() &;
+    };
+
+    void target() {
+      smart_ptr<$ns::$optional<float>> x;
+      *x = $ns::nullopt;
+      (*x).value();
+      /*[[check]]*/
+    }
+  )",
+      UnorderedElementsAre(Pair("check", "unsafe: input.cc:12:7")));
+}
+
 // FIXME: Add support for:
 // - constructors (copy, move)
 // - assignment operators (default, copy, move)


        


More information about the cfe-commits mailing list