1 //===- unittests/Analysis/FlowSensitive/SmartPointerAccessorCachingTest.cpp ==// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "clang/Analysis/FlowSensitive/SmartPointerAccessorCaching.h" 10 11 #include "clang/ASTMatchers/ASTMatchFinder.h" 12 #include "clang/Testing/TestAST.h" 13 #include "llvm/ADT/StringRef.h" 14 #include "gtest/gtest.h" 15 16 namespace clang::dataflow { 17 namespace { 18 19 using clang::ast_matchers::match; 20 21 template <typename MatcherT> 22 bool matches(llvm::StringRef Decls, llvm::StringRef TestInput, 23 MatcherT Matcher) { 24 TestAST InputAST(Decls.str() + TestInput.str()); 25 return !match(Matcher, InputAST.context()).empty(); 26 } 27 28 TEST(SmartPointerAccessorCachingTest, MatchesClassWithStarArrowGet) { 29 llvm::StringRef Decls(R"cc( 30 namespace std { 31 template <class T> 32 struct unique_ptr { 33 T* operator->() const; 34 T& operator*() const; 35 T* get() const; 36 }; 37 } // namespace std 38 39 template <class T> 40 using UniquePtrAlias = std::unique_ptr<T>; 41 42 struct S { int i; }; 43 )cc"); 44 45 EXPECT_TRUE(matches(Decls, 46 "int target(std::unique_ptr<S> P) { return (*P).i; }", 47 isSmartPointerLikeOperatorStar())); 48 EXPECT_TRUE(matches(Decls, 49 "int target(std::unique_ptr<S> P) { return P->i; }", 50 isSmartPointerLikeOperatorArrow())); 51 EXPECT_TRUE(matches(Decls, 52 "int target(std::unique_ptr<S> P) { return P.get()->i; }", 53 isSmartPointerLikeGetMethodCall())); 54 55 EXPECT_TRUE(matches(Decls, "int target(UniquePtrAlias<S> P) { return P->i; }", 56 isSmartPointerLikeOperatorArrow())); 57 } 58 59 TEST(SmartPointerAccessorCachingTest, NoMatchIfUnexpectedReturnTypes) { 60 llvm::StringRef Decls(R"cc( 61 namespace std { 62 // unique_ptr isn't really like this, but we aren't matching by name 63 template <class T, class U> 64 struct unique_ptr { 65 U* operator->() const; 66 T& operator*() const; 67 T* get() const; 68 }; 69 } // namespace std 70 71 struct S { int i; }; 72 struct T { int j; }; 73 )cc"); 74 75 EXPECT_FALSE(matches(Decls, 76 "int target(std::unique_ptr<S, T> P) { return (*P).i; }", 77 isSmartPointerLikeOperatorStar())); 78 EXPECT_FALSE(matches(Decls, 79 "int target(std::unique_ptr<S, T> P) { return P->j; }", 80 isSmartPointerLikeOperatorArrow())); 81 // The class matching arguably accidentally matches, just because the 82 // instantiation is with S, S. Hopefully doesn't happen too much in real code 83 // with such operator* and operator-> overloads. 84 EXPECT_TRUE(matches(Decls, 85 "int target(std::unique_ptr<S, S> P) { return P->i; }", 86 isSmartPointerLikeOperatorArrow())); 87 } 88 89 TEST(SmartPointerAccessorCachingTest, NoMatchIfBinaryStar) { 90 llvm::StringRef Decls(R"cc( 91 namespace std { 92 template <class T> 93 struct unique_ptr { 94 T* operator->() const; 95 T& operator*(int x) const; 96 T* get() const; 97 }; 98 } // namespace std 99 100 struct S { int i; }; 101 )cc"); 102 103 EXPECT_FALSE( 104 matches(Decls, "int target(std::unique_ptr<S> P) { return (P * 10).i; }", 105 isSmartPointerLikeOperatorStar())); 106 } 107 108 TEST(SmartPointerAccessorCachingTest, NoMatchIfNoConstOverloads) { 109 llvm::StringRef Decls(R"cc( 110 namespace std { 111 template <class T> 112 struct unique_ptr { 113 T* operator->(); 114 T& operator*(); 115 T* get(); 116 }; 117 } // namespace std 118 119 struct S { int i; }; 120 )cc"); 121 122 EXPECT_FALSE(matches(Decls, 123 "int target(std::unique_ptr<S> P) { return (*P).i; }", 124 isSmartPointerLikeOperatorStar())); 125 EXPECT_FALSE(matches(Decls, 126 "int target(std::unique_ptr<S> P) { return P->i; }", 127 isSmartPointerLikeOperatorArrow())); 128 EXPECT_FALSE( 129 matches(Decls, "int target(std::unique_ptr<S> P) { return P.get()->i; }", 130 isSmartPointerLikeGetMethodCall())); 131 } 132 133 TEST(SmartPointerAccessorCachingTest, NoMatchIfNoStarMethod) { 134 llvm::StringRef Decls(R"cc( 135 namespace std { 136 template <class T> 137 struct unique_ptr { 138 T* operator->(); 139 T* get(); 140 }; 141 } // namespace std 142 143 struct S { int i; }; 144 )cc"); 145 146 EXPECT_FALSE(matches(Decls, 147 "int target(std::unique_ptr<S> P) { return P->i; }", 148 isSmartPointerLikeOperatorArrow())); 149 EXPECT_FALSE(matches(Decls, 150 "int target(std::unique_ptr<S> P) { return P->i; }", 151 isSmartPointerLikeGetMethodCall())); 152 } 153 154 TEST(SmartPointerAccessorCachingTest, MatchesWithValueAndNonConstOverloads) { 155 llvm::StringRef Decls(R"cc( 156 namespace std { 157 template <class T> 158 struct optional { 159 const T* operator->() const; 160 T* operator->(); 161 const T& operator*() const; 162 T& operator*(); 163 const T& value() const; 164 T& value(); 165 }; 166 } // namespace std 167 168 struct S { int i; }; 169 )cc"); 170 171 EXPECT_TRUE(matches( 172 Decls, "int target(std::optional<S> &NonConst) { return (*NonConst).i; }", 173 isSmartPointerLikeOperatorStar())); 174 EXPECT_TRUE(matches( 175 Decls, "int target(const std::optional<S> &Const) { return (*Const).i; }", 176 isSmartPointerLikeOperatorStar())); 177 EXPECT_TRUE(matches( 178 Decls, "int target(std::optional<S> &NonConst) { return NonConst->i; }", 179 isSmartPointerLikeOperatorArrow())); 180 EXPECT_TRUE(matches( 181 Decls, "int target(const std::optional<S> &Const) { return Const->i; }", 182 isSmartPointerLikeOperatorArrow())); 183 EXPECT_TRUE(matches( 184 Decls, 185 "int target(std::optional<S> &NonConst) { return NonConst.value().i; }", 186 isSmartPointerLikeValueMethodCall())); 187 EXPECT_TRUE(matches( 188 Decls, 189 "int target(const std::optional<S> &Const) { return Const.value().i; }", 190 isSmartPointerLikeValueMethodCall())); 191 } 192 193 } // namespace 194 } // namespace clang::dataflow 195