CallDescriptionTest.cpp 5.4 KB

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