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/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 12 #include "clang/Tooling/Tooling.h" 13 #include "gtest/gtest.h" 14 15 namespace clang { 16 namespace ento { 17 namespace { 18 19 // A wrapper around CallDescriptionMap<bool> that allows verifying that 20 // all functions have been found. This is needed because CallDescriptionMap 21 // isn't supposed to support iteration. 22 class ResultMap { 23 size_t Found, Total; 24 CallDescriptionMap<bool> Impl; 25 26 public: 27 ResultMap(std::initializer_list<std::pair<CallDescription, bool>> Data) 28 : Found(0), 29 Total(std::count_if(Data.begin(), Data.end(), 30 [](const std::pair<CallDescription, bool> &Pair) { 31 return Pair.second == true; 32 })), 33 Impl(std::move(Data)) {} 34 35 const bool *lookup(const CallEvent &Call) { 36 const bool *Result = Impl.lookup(Call); 37 // If it's a function we expected to find, remember that we've found it. 38 if (Result && *Result) 39 ++Found; 40 return Result; 41 } 42 43 // Fail the test if we haven't found all the true-calls we were looking for. 44 ~ResultMap() { EXPECT_EQ(Found, Total); } 45 }; 46 47 // Scan the code body for call expressions and see if we find all calls that 48 // we were supposed to find ("true" in the provided ResultMap) and that we 49 // don't find the ones that we weren't supposed to find 50 // ("false" in the ResultMap). 51 class CallDescriptionConsumer : public ExprEngineConsumer { 52 ResultMap &RM; 53 void performTest(const Decl *D) { 54 using namespace ast_matchers; 55 56 if (!D->hasBody()) 57 return; 58 59 const CallExpr *CE = findNode<CallExpr>(D, callExpr()); 60 const StackFrameContext *SFC = 61 Eng.getAnalysisDeclContextManager().getStackFrame(D); 62 ProgramStateRef State = Eng.getInitialState(SFC); 63 CallEventRef<> Call = 64 Eng.getStateManager().getCallEventManager().getCall(CE, State, SFC); 65 66 const bool *LookupResult = RM.lookup(*Call); 67 // Check that we've found the function in the map 68 // with the correct description. 69 EXPECT_TRUE(LookupResult && *LookupResult); 70 71 // ResultMap is responsible for making sure that we've found *all* calls. 72 } 73 74 public: 75 CallDescriptionConsumer(CompilerInstance &C, 76 ResultMap &RM) 77 : ExprEngineConsumer(C), RM(RM) {} 78 79 bool HandleTopLevelDecl(DeclGroupRef DG) override { 80 for (const auto *D : DG) 81 performTest(D); 82 return true; 83 } 84 }; 85 86 class CallDescriptionAction : public ASTFrontendAction { 87 ResultMap RM; 88 89 public: 90 CallDescriptionAction( 91 std::initializer_list<std::pair<CallDescription, bool>> Data) 92 : RM(Data) {} 93 94 std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler, 95 StringRef File) override { 96 return std::make_unique<CallDescriptionConsumer>(Compiler, RM); 97 } 98 }; 99 100 TEST(CallEvent, CallDescription) { 101 // Test simple name matching. 102 EXPECT_TRUE(tooling::runToolOnCode( 103 new CallDescriptionAction({ 104 {{"bar"}, false}, // false: there's no call to 'bar' in this code. 105 {{"foo"}, true}, // true: there's a call to 'foo' in this code. 106 }), "void foo(); void bar() { foo(); }")); 107 108 // Test arguments check. 109 EXPECT_TRUE(tooling::runToolOnCode( 110 new CallDescriptionAction({ 111 {{"foo", 1}, true}, 112 {{"foo", 2}, false}, 113 }), "void foo(int); void foo(int, int); void bar() { foo(1); }")); 114 115 // Test lack of arguments check. 116 EXPECT_TRUE(tooling::runToolOnCode( 117 new CallDescriptionAction({ 118 {{"foo", None}, true}, 119 {{"foo", 2}, false}, 120 }), "void foo(int); void foo(int, int); void bar() { foo(1); }")); 121 122 // Test qualified names. 123 EXPECT_TRUE(tooling::runToolOnCode( 124 new CallDescriptionAction({ 125 {{{"std", "basic_string", "c_str"}}, true}, 126 }), 127 "namespace std { inline namespace __1 {" 128 " template<typename T> class basic_string {" 129 " public:" 130 " T *c_str();" 131 " };" 132 "}}" 133 "void foo() {" 134 " using namespace std;" 135 " basic_string<char> s;" 136 " s.c_str();" 137 "}")); 138 139 // A negative test for qualified names. 140 EXPECT_TRUE(tooling::runToolOnCode( 141 new CallDescriptionAction({ 142 {{{"foo", "bar"}}, false}, 143 {{{"bar", "foo"}}, false}, 144 {{"foo"}, true}, 145 }), "void foo(); struct bar { void foo(); }; void test() { foo(); }")); 146 147 // Test CDF_MaybeBuiltin - a flag that allows matching weird builtins. 148 EXPECT_TRUE(tooling::runToolOnCode( 149 new CallDescriptionAction({ 150 {{"memset", 3}, false}, 151 {{CDF_MaybeBuiltin, "memset", 3}, true} 152 }), 153 "void foo() {" 154 " int x;" 155 " __builtin___memset_chk(&x, 0, sizeof(x)," 156 " __builtin_object_size(&x, 0));" 157 "}")); 158 } 159 160 } // namespace 161 } // namespace ento 162 } // namespace clang 163