1 //===- unittests/StaticAnalyzer/CallDescriptionTest.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 "Reusables.h" 10 11 #include "clang/AST/ExprCXX.h" 12 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 13 #include "clang/Tooling/Tooling.h" 14 #include "gtest/gtest.h" 15 #include <type_traits> 16 17 namespace clang { 18 namespace ento { 19 namespace { 20 21 // A wrapper around CallDescriptionMap<bool> that allows verifying that 22 // all functions have been found. This is needed because CallDescriptionMap 23 // isn't supposed to support iteration. 24 class ResultMap { 25 size_t Found, Total; 26 CallDescriptionMap<bool> Impl; 27 28 public: 29 ResultMap(std::initializer_list<std::pair<CallDescription, bool>> Data) 30 : Found(0), 31 Total(std::count_if(Data.begin(), Data.end(), 32 [](const std::pair<CallDescription, bool> &Pair) { 33 return Pair.second == true; 34 })), 35 Impl(std::move(Data)) {} 36 37 const bool *lookup(const CallEvent &Call) { 38 const bool *Result = Impl.lookup(Call); 39 // If it's a function we expected to find, remember that we've found it. 40 if (Result && *Result) 41 ++Found; 42 return Result; 43 } 44 45 // Fail the test if we haven't found all the true-calls we were looking for. 46 ~ResultMap() { EXPECT_EQ(Found, Total); } 47 }; 48 49 // Scan the code body for call expressions and see if we find all calls that 50 // we were supposed to find ("true" in the provided ResultMap) and that we 51 // don't find the ones that we weren't supposed to find 52 // ("false" in the ResultMap). 53 template <typename MatchedExprT> 54 class CallDescriptionConsumer : public ExprEngineConsumer { 55 ResultMap &RM; 56 void performTest(const Decl *D) { 57 using namespace ast_matchers; 58 using T = MatchedExprT; 59 60 if (!D->hasBody()) 61 return; 62 63 const StackFrameContext *SFC = 64 Eng.getAnalysisDeclContextManager().getStackFrame(D); 65 const ProgramStateRef State = Eng.getInitialState(SFC); 66 67 // FIXME: Maybe use std::variant and std::visit for these. 68 const auto MatcherCreator = []() { 69 if (std::is_same<T, CallExpr>::value) 70 return callExpr(); 71 if (std::is_same<T, CXXConstructExpr>::value) 72 return cxxConstructExpr(); 73 if (std::is_same<T, CXXMemberCallExpr>::value) 74 return cxxMemberCallExpr(); 75 if (std::is_same<T, CXXOperatorCallExpr>::value) 76 return cxxOperatorCallExpr(); 77 llvm_unreachable("Only these expressions are supported for now."); 78 }; 79 80 const Expr *E = findNode<T>(D, MatcherCreator()); 81 82 CallEventManager &CEMgr = Eng.getStateManager().getCallEventManager(); 83 CallEventRef<> Call = [=, &CEMgr]() -> CallEventRef<CallEvent> { 84 if (std::is_base_of<CallExpr, T>::value) 85 return CEMgr.getCall(E, State, SFC); 86 if (std::is_same<T, CXXConstructExpr>::value) 87 return CEMgr.getCXXConstructorCall(cast<CXXConstructExpr>(E), 88 /*Target=*/nullptr, State, SFC); 89 llvm_unreachable("Only these expressions are supported for now."); 90 }(); 91 92 // If the call actually matched, check if we really expected it to match. 93 const bool *LookupResult = RM.lookup(*Call); 94 EXPECT_TRUE(!LookupResult || *LookupResult); 95 96 // ResultMap is responsible for making sure that we've found *all* calls. 97 } 98 99 public: 100 CallDescriptionConsumer(CompilerInstance &C, 101 ResultMap &RM) 102 : ExprEngineConsumer(C), RM(RM) {} 103 104 bool HandleTopLevelDecl(DeclGroupRef DG) override { 105 for (const auto *D : DG) 106 performTest(D); 107 return true; 108 } 109 }; 110 111 template <typename MatchedExprT = CallExpr> 112 class CallDescriptionAction : public ASTFrontendAction { 113 ResultMap RM; 114 115 public: 116 CallDescriptionAction( 117 std::initializer_list<std::pair<CallDescription, bool>> Data) 118 : RM(Data) {} 119 120 std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler, 121 StringRef File) override { 122 return std::make_unique<CallDescriptionConsumer<MatchedExprT>>(Compiler, 123 RM); 124 } 125 }; 126 127 TEST(CallDescription, SimpleNameMatching) { 128 EXPECT_TRUE(tooling::runToolOnCode( 129 std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({ 130 {{"bar"}, false}, // false: there's no call to 'bar' in this code. 131 {{"foo"}, true}, // true: there's a call to 'foo' in this code. 132 })), 133 "void foo(); void bar() { foo(); }")); 134 } 135 136 TEST(CallDescription, RequiredArguments) { 137 EXPECT_TRUE(tooling::runToolOnCode( 138 std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({ 139 {{"foo", 1}, true}, 140 {{"foo", 2}, false}, 141 })), 142 "void foo(int); void foo(int, int); void bar() { foo(1); }")); 143 } 144 145 TEST(CallDescription, LackOfRequiredArguments) { 146 EXPECT_TRUE(tooling::runToolOnCode( 147 std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({ 148 {{"foo", None}, true}, 149 {{"foo", 2}, false}, 150 })), 151 "void foo(int); void foo(int, int); void bar() { foo(1); }")); 152 } 153 154 constexpr StringRef MockStdStringHeader = R"code( 155 namespace std { inline namespace __1 { 156 template<typename T> class basic_string { 157 class Allocator {}; 158 public: 159 basic_string(); 160 explicit basic_string(const char*, const Allocator & = Allocator()); 161 ~basic_string(); 162 T *c_str(); 163 }; 164 } // namespace __1 165 using string = __1::basic_string<char>; 166 } // namespace std 167 )code"; 168 169 TEST(CallDescription, QualifiedNames) { 170 constexpr StringRef AdditionalCode = R"code( 171 void foo() { 172 using namespace std; 173 basic_string<char> s; 174 s.c_str(); 175 })code"; 176 const std::string Code = (Twine{MockStdStringHeader} + AdditionalCode).str(); 177 EXPECT_TRUE(tooling::runToolOnCode( 178 std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({ 179 {{{"std", "basic_string", "c_str"}}, true}, 180 })), 181 Code)); 182 } 183 184 TEST(CallDescription, MatchConstructor) { 185 constexpr StringRef AdditionalCode = R"code( 186 void foo() { 187 using namespace std; 188 basic_string<char> s("hello"); 189 })code"; 190 const std::string Code = (Twine{MockStdStringHeader} + AdditionalCode).str(); 191 EXPECT_TRUE(tooling::runToolOnCode( 192 std::unique_ptr<FrontendAction>( 193 new CallDescriptionAction<CXXConstructExpr>({ 194 {{{"std", "basic_string", "basic_string"}, 2, 2}, true}, 195 })), 196 Code)); 197 } 198 199 // FIXME: Test matching destructors: {"std", "basic_string", "~basic_string"} 200 // This feature is actually implemented, but the test infra is not yet 201 // sophisticated enough for testing this. To do that, we will need to 202 // implement a much more advanced dispatching mechanism using the CFG for 203 // the implicit destructor events. 204 205 TEST(CallDescription, MatchConversionOperator) { 206 constexpr StringRef Code = R"code( 207 namespace aaa { 208 namespace bbb { 209 struct Bar { 210 operator int(); 211 }; 212 } // bbb 213 } // aaa 214 void foo() { 215 aaa::bbb::Bar x; 216 int tmp = x; 217 })code"; 218 EXPECT_TRUE(tooling::runToolOnCode( 219 std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({ 220 {{{"aaa", "bbb", "Bar", "operator int"}}, true}, 221 })), 222 Code)); 223 } 224 225 TEST(CallDescription, RejectOverQualifiedNames) { 226 constexpr auto Code = R"code( 227 namespace my { 228 namespace std { 229 struct container { 230 const char *data() const; 231 }; 232 } // namespace std 233 } // namespace my 234 235 void foo() { 236 using namespace my; 237 std::container v; 238 v.data(); 239 })code"; 240 241 // FIXME: We should **not** match. 242 EXPECT_TRUE(tooling::runToolOnCode( 243 std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({ 244 {{{"std", "container", "data"}}, true}, 245 })), 246 Code)); 247 } 248 249 TEST(CallDescription, DontSkipNonInlineNamespaces) { 250 constexpr auto Code = R"code( 251 namespace my { 252 /*not inline*/ namespace v1 { 253 void bar(); 254 } // namespace v1 255 } // namespace my 256 void foo() { 257 my::v1::bar(); 258 })code"; 259 260 { 261 SCOPED_TRACE("my v1 bar"); 262 EXPECT_TRUE(tooling::runToolOnCode( 263 std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({ 264 {{{"my", "v1", "bar"}}, true}, 265 })), 266 Code)); 267 } 268 { 269 // FIXME: We should **not** skip non-inline namespaces. 270 SCOPED_TRACE("my bar"); 271 EXPECT_TRUE(tooling::runToolOnCode( 272 std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({ 273 {{{"my", "bar"}}, true}, 274 })), 275 Code)); 276 } 277 } 278 279 TEST(CallDescription, SkipTopInlineNamespaces) { 280 constexpr auto Code = R"code( 281 inline namespace my { 282 namespace v1 { 283 void bar(); 284 } // namespace v1 285 } // namespace my 286 void foo() { 287 using namespace v1; 288 bar(); 289 })code"; 290 291 { 292 SCOPED_TRACE("my v1 bar"); 293 EXPECT_TRUE(tooling::runToolOnCode( 294 std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({ 295 {{{"my", "v1", "bar"}}, true}, 296 })), 297 Code)); 298 } 299 { 300 SCOPED_TRACE("v1 bar"); 301 EXPECT_TRUE(tooling::runToolOnCode( 302 std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({ 303 {{{"v1", "bar"}}, true}, 304 })), 305 Code)); 306 } 307 } 308 309 TEST(CallDescription, SkipAnonimousNamespaces) { 310 constexpr auto Code = R"code( 311 namespace { 312 namespace std { 313 namespace { 314 inline namespace { 315 struct container { 316 const char *data() const { return nullptr; }; 317 }; 318 } // namespace inline anonymous 319 } // namespace anonymous 320 } // namespace std 321 } // namespace anonymous 322 323 void foo() { 324 std::container v; 325 v.data(); 326 })code"; 327 328 EXPECT_TRUE(tooling::runToolOnCode( 329 std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({ 330 {{{"std", "container", "data"}}, true}, 331 })), 332 Code)); 333 } 334 335 TEST(CallDescription, AliasNames) { 336 constexpr StringRef AliasNamesCode = R"code( 337 namespace std { 338 struct container { 339 const char *data() const; 340 }; 341 using cont = container; 342 } // std 343 )code"; 344 345 constexpr StringRef UseAliasInSpelling = R"code( 346 void foo() { 347 std::cont v; 348 v.data(); 349 })code"; 350 constexpr StringRef UseStructNameInSpelling = R"code( 351 void foo() { 352 std::container v; 353 v.data(); 354 })code"; 355 const std::string UseAliasInSpellingCode = 356 (Twine{AliasNamesCode} + UseAliasInSpelling).str(); 357 const std::string UseStructNameInSpellingCode = 358 (Twine{AliasNamesCode} + UseStructNameInSpelling).str(); 359 360 // Test if the code spells the alias, wile we match against the struct name, 361 // and again matching against the alias. 362 { 363 SCOPED_TRACE("Using alias in spelling"); 364 { 365 SCOPED_TRACE("std container data"); 366 EXPECT_TRUE(tooling::runToolOnCode( 367 std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({ 368 {{{"std", "container", "data"}}, true}, 369 })), 370 UseAliasInSpellingCode)); 371 } 372 { 373 // FIXME: We should be able to see-through aliases. 374 SCOPED_TRACE("std cont data"); 375 EXPECT_TRUE(tooling::runToolOnCode( 376 std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({ 377 {{{"std", "cont", "data"}}, false}, 378 })), 379 UseAliasInSpellingCode)); 380 } 381 } 382 383 // Test if the code spells the struct name, wile we match against the struct 384 // name, and again matching against the alias. 385 { 386 SCOPED_TRACE("Using struct name in spelling"); 387 { 388 SCOPED_TRACE("std container data"); 389 EXPECT_TRUE(tooling::runToolOnCode( 390 std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({ 391 {{{"std", "container", "data"}}, true}, 392 })), 393 UseAliasInSpellingCode)); 394 } 395 { 396 // FIXME: We should be able to see-through aliases. 397 SCOPED_TRACE("std cont data"); 398 EXPECT_TRUE(tooling::runToolOnCode( 399 std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({ 400 {{{"std", "cont", "data"}}, false}, 401 })), 402 UseAliasInSpellingCode)); 403 } 404 } 405 } 406 407 TEST(CallDescription, AliasSingleNamespace) { 408 constexpr StringRef Code = R"code( 409 namespace aaa { 410 namespace bbb { 411 namespace ccc { 412 void bar(); 413 }} // namespace bbb::ccc 414 namespace bbb_alias = bbb; 415 } // namespace aaa 416 void foo() { 417 aaa::bbb_alias::ccc::bar(); 418 })code"; 419 { 420 SCOPED_TRACE("aaa bbb ccc bar"); 421 EXPECT_TRUE(tooling::runToolOnCode( 422 std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({ 423 {{{"aaa", "bbb", "ccc", "bar"}}, true}, 424 })), 425 Code)); 426 } 427 { 428 // FIXME: We should be able to see-through namespace aliases. 429 SCOPED_TRACE("aaa bbb_alias ccc bar"); 430 EXPECT_TRUE(tooling::runToolOnCode( 431 std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({ 432 {{{"aaa", "bbb_alias", "ccc", "bar"}}, false}, 433 })), 434 Code)); 435 } 436 } 437 438 TEST(CallDescription, AliasMultipleNamespaces) { 439 constexpr StringRef Code = R"code( 440 namespace aaa { 441 namespace bbb { 442 namespace ccc { 443 void bar(); 444 }}} // namespace aaa::bbb::ccc 445 namespace aaa_bbb_ccc = aaa::bbb::ccc; 446 void foo() { 447 using namespace aaa_bbb_ccc; 448 bar(); 449 })code"; 450 { 451 SCOPED_TRACE("aaa bbb ccc bar"); 452 EXPECT_TRUE(tooling::runToolOnCode( 453 std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({ 454 {{{"aaa", "bbb", "ccc", "bar"}}, true}, 455 })), 456 Code)); 457 } 458 { 459 // FIXME: We should be able to see-through namespace aliases. 460 SCOPED_TRACE("aaa_bbb_ccc bar"); 461 EXPECT_TRUE(tooling::runToolOnCode( 462 std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({ 463 {{{"aaa_bbb_ccc", "bar"}}, false}, 464 })), 465 Code)); 466 } 467 } 468 469 TEST(CallDescription, NegativeMatchQualifiedNames) { 470 EXPECT_TRUE(tooling::runToolOnCode( 471 std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({ 472 {{{"foo", "bar"}}, false}, 473 {{{"bar", "foo"}}, false}, 474 {{"foo"}, true}, 475 })), 476 "void foo(); struct bar { void foo(); }; void test() { foo(); }")); 477 } 478 479 TEST(CallDescription, MatchBuiltins) { 480 // Test CDF_MaybeBuiltin - a flag that allows matching weird builtins. 481 EXPECT_TRUE(tooling::runToolOnCode( 482 std::unique_ptr<FrontendAction>(new CallDescriptionAction<>( 483 {{{"memset", 3}, false}, {{CDF_MaybeBuiltin, "memset", 3}, true}})), 484 "void foo() {" 485 " int x;" 486 " __builtin___memset_chk(&x, 0, sizeof(x)," 487 " __builtin_object_size(&x, 0));" 488 "}")); 489 } 490 491 } // namespace 492 } // namespace ento 493 } // namespace clang 494