xref: /llvm-project/clang/unittests/StaticAnalyzer/CallDescriptionTest.cpp (revision 58bad2862cf136f9483eb005bbfa6915d459b46d)
1ec8e9564SArtem Dergachev //===- unittests/StaticAnalyzer/CallDescriptionTest.cpp -------------------===//
2ec8e9564SArtem Dergachev //
3ec8e9564SArtem Dergachev // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4ec8e9564SArtem Dergachev // See https://llvm.org/LICENSE.txt for license information.
5ec8e9564SArtem Dergachev // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6ec8e9564SArtem Dergachev //
7ec8e9564SArtem Dergachev //===----------------------------------------------------------------------===//
8ec8e9564SArtem Dergachev 
932ac21d0SKristóf Umann #include "CheckerRegistration.h"
10ec8e9564SArtem Dergachev #include "Reusables.h"
11ec8e9564SArtem Dergachev 
125644d152SBalazs Benics #include "clang/AST/ExprCXX.h"
1332ac21d0SKristóf Umann #include "clang/Analysis/PathDiagnostic.h"
1432ac21d0SKristóf Umann #include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h"
1532ac21d0SKristóf Umann #include "clang/StaticAnalyzer/Core/Checker.h"
160b9d3a6eSBalazs Benics #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
17ec8e9564SArtem Dergachev #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
1832ac21d0SKristóf Umann #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
1932ac21d0SKristóf Umann #include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h"
2032ac21d0SKristóf Umann #include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h"
21ec8e9564SArtem Dergachev #include "clang/Tooling/Tooling.h"
22ec8e9564SArtem Dergachev #include "gtest/gtest.h"
235644d152SBalazs Benics #include <type_traits>
24ec8e9564SArtem Dergachev 
25ec8e9564SArtem Dergachev namespace clang {
26ec8e9564SArtem Dergachev namespace ento {
27ec8e9564SArtem Dergachev namespace {
28ec8e9564SArtem Dergachev 
29ec8e9564SArtem Dergachev // A wrapper around CallDescriptionMap<bool> that allows verifying that
30ec8e9564SArtem Dergachev // all functions have been found. This is needed because CallDescriptionMap
31ec8e9564SArtem Dergachev // isn't supposed to support iteration.
32ec8e9564SArtem Dergachev class ResultMap {
33ec8e9564SArtem Dergachev   size_t Found, Total;
34ec8e9564SArtem Dergachev   CallDescriptionMap<bool> Impl;
35ec8e9564SArtem Dergachev 
36ec8e9564SArtem Dergachev public:
ResultMap(std::initializer_list<std::pair<CallDescription,bool>> Data)37ec8e9564SArtem Dergachev   ResultMap(std::initializer_list<std::pair<CallDescription, bool>> Data)
38ec8e9564SArtem Dergachev       : Found(0),
39ec8e9564SArtem Dergachev         Total(std::count_if(Data.begin(), Data.end(),
40ec8e9564SArtem Dergachev                             [](const std::pair<CallDescription, bool> &Pair) {
41ec8e9564SArtem Dergachev                               return Pair.second == true;
42ec8e9564SArtem Dergachev                             })),
43ec8e9564SArtem Dergachev         Impl(std::move(Data)) {}
44ec8e9564SArtem Dergachev 
lookup(const CallEvent & Call)45ec8e9564SArtem Dergachev   const bool *lookup(const CallEvent &Call) {
46ec8e9564SArtem Dergachev     const bool *Result = Impl.lookup(Call);
47ec8e9564SArtem Dergachev     // If it's a function we expected to find, remember that we've found it.
48ec8e9564SArtem Dergachev     if (Result && *Result)
49ec8e9564SArtem Dergachev       ++Found;
50ec8e9564SArtem Dergachev     return Result;
51ec8e9564SArtem Dergachev   }
52ec8e9564SArtem Dergachev 
53ec8e9564SArtem Dergachev   // Fail the test if we haven't found all the true-calls we were looking for.
~ResultMap()54ec8e9564SArtem Dergachev   ~ResultMap() { EXPECT_EQ(Found, Total); }
55ec8e9564SArtem Dergachev };
56ec8e9564SArtem Dergachev 
57ec8e9564SArtem Dergachev // Scan the code body for call expressions and see if we find all calls that
58ec8e9564SArtem Dergachev // we were supposed to find ("true" in the provided ResultMap) and that we
59ec8e9564SArtem Dergachev // don't find the ones that we weren't supposed to find
60ec8e9564SArtem Dergachev // ("false" in the ResultMap).
615644d152SBalazs Benics template <typename MatchedExprT>
62ec8e9564SArtem Dergachev class CallDescriptionConsumer : public ExprEngineConsumer {
63ec8e9564SArtem Dergachev   ResultMap &RM;
performTest(const Decl * D)64ec8e9564SArtem Dergachev   void performTest(const Decl *D) {
65ec8e9564SArtem Dergachev     using namespace ast_matchers;
665644d152SBalazs Benics     using T = MatchedExprT;
67ec8e9564SArtem Dergachev 
68ec8e9564SArtem Dergachev     if (!D->hasBody())
69ec8e9564SArtem Dergachev       return;
70ec8e9564SArtem Dergachev 
71ec8e9564SArtem Dergachev     const StackFrameContext *SFC =
72ec8e9564SArtem Dergachev         Eng.getAnalysisDeclContextManager().getStackFrame(D);
735644d152SBalazs Benics     const ProgramStateRef State = Eng.getInitialState(SFC);
74ec8e9564SArtem Dergachev 
755644d152SBalazs Benics     // FIXME: Maybe use std::variant and std::visit for these.
765644d152SBalazs Benics     const auto MatcherCreator = []() {
775644d152SBalazs Benics       if (std::is_same<T, CallExpr>::value)
785644d152SBalazs Benics         return callExpr();
795644d152SBalazs Benics       if (std::is_same<T, CXXConstructExpr>::value)
805644d152SBalazs Benics         return cxxConstructExpr();
815644d152SBalazs Benics       if (std::is_same<T, CXXMemberCallExpr>::value)
825644d152SBalazs Benics         return cxxMemberCallExpr();
835644d152SBalazs Benics       if (std::is_same<T, CXXOperatorCallExpr>::value)
845644d152SBalazs Benics         return cxxOperatorCallExpr();
855644d152SBalazs Benics       llvm_unreachable("Only these expressions are supported for now.");
865644d152SBalazs Benics     };
875644d152SBalazs Benics 
885644d152SBalazs Benics     const Expr *E = findNode<T>(D, MatcherCreator());
895644d152SBalazs Benics 
905644d152SBalazs Benics     CallEventManager &CEMgr = Eng.getStateManager().getCallEventManager();
915644d152SBalazs Benics     CallEventRef<> Call = [=, &CEMgr]() -> CallEventRef<CallEvent> {
92d65379c8Sisuckatcs       CFGBlock::ConstCFGElementRef ElemRef = {SFC->getCallSiteBlock(),
93d65379c8Sisuckatcs                                               SFC->getIndex()};
945644d152SBalazs Benics       if (std::is_base_of<CallExpr, T>::value)
95d65379c8Sisuckatcs         return CEMgr.getCall(E, State, SFC, ElemRef);
965644d152SBalazs Benics       if (std::is_same<T, CXXConstructExpr>::value)
975644d152SBalazs Benics         return CEMgr.getCXXConstructorCall(cast<CXXConstructExpr>(E),
98d65379c8Sisuckatcs                                            /*Target=*/nullptr, State, SFC,
99d65379c8Sisuckatcs                                            ElemRef);
1005644d152SBalazs Benics       llvm_unreachable("Only these expressions are supported for now.");
1015644d152SBalazs Benics     }();
1025644d152SBalazs Benics 
1035644d152SBalazs Benics     // If the call actually matched, check if we really expected it to match.
104ec8e9564SArtem Dergachev     const bool *LookupResult = RM.lookup(*Call);
1055644d152SBalazs Benics     EXPECT_TRUE(!LookupResult || *LookupResult);
106ec8e9564SArtem Dergachev 
107ec8e9564SArtem Dergachev     // ResultMap is responsible for making sure that we've found *all* calls.
108ec8e9564SArtem Dergachev   }
109ec8e9564SArtem Dergachev 
110ec8e9564SArtem Dergachev public:
CallDescriptionConsumer(CompilerInstance & C,ResultMap & RM)111ec8e9564SArtem Dergachev   CallDescriptionConsumer(CompilerInstance &C,
112ec8e9564SArtem Dergachev                           ResultMap &RM)
113ec8e9564SArtem Dergachev       : ExprEngineConsumer(C), RM(RM) {}
114ec8e9564SArtem Dergachev 
HandleTopLevelDecl(DeclGroupRef DG)115ec8e9564SArtem Dergachev   bool HandleTopLevelDecl(DeclGroupRef DG) override {
116ec8e9564SArtem Dergachev     for (const auto *D : DG)
117ec8e9564SArtem Dergachev       performTest(D);
118ec8e9564SArtem Dergachev     return true;
119ec8e9564SArtem Dergachev   }
120ec8e9564SArtem Dergachev };
121ec8e9564SArtem Dergachev 
1225644d152SBalazs Benics template <typename MatchedExprT = CallExpr>
123ec8e9564SArtem Dergachev class CallDescriptionAction : public ASTFrontendAction {
124ec8e9564SArtem Dergachev   ResultMap RM;
125ec8e9564SArtem Dergachev 
126ec8e9564SArtem Dergachev public:
CallDescriptionAction(std::initializer_list<std::pair<CallDescription,bool>> Data)127ec8e9564SArtem Dergachev   CallDescriptionAction(
128ec8e9564SArtem Dergachev       std::initializer_list<std::pair<CallDescription, bool>> Data)
129ec8e9564SArtem Dergachev       : RM(Data) {}
130ec8e9564SArtem Dergachev 
CreateASTConsumer(CompilerInstance & Compiler,StringRef File)131ec8e9564SArtem Dergachev   std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
132ec8e9564SArtem Dergachev                                                  StringRef File) override {
1335644d152SBalazs Benics     return std::make_unique<CallDescriptionConsumer<MatchedExprT>>(Compiler,
1345644d152SBalazs Benics                                                                    RM);
135ec8e9564SArtem Dergachev   }
136ec8e9564SArtem Dergachev };
137ec8e9564SArtem Dergachev 
TEST(CallDescription,SimpleNameMatching)1385644d152SBalazs Benics TEST(CallDescription, SimpleNameMatching) {
139ec8e9564SArtem Dergachev   EXPECT_TRUE(tooling::runToolOnCode(
1405644d152SBalazs Benics       std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({
141*58bad286SDonát Nagy           {{CDM::SimpleFunc, {"bar"}},
142*58bad286SDonát Nagy            false}, // false: there's no call to 'bar' in this code.
143*58bad286SDonát Nagy           {{CDM::SimpleFunc, {"foo"}},
144*58bad286SDonát Nagy            true}, // true: there's a call to 'foo' in this code.
1455644d152SBalazs Benics       })),
1465644d152SBalazs Benics       "void foo(); void bar() { foo(); }"));
1475644d152SBalazs Benics }
148ec8e9564SArtem Dergachev 
TEST(CallDescription,RequiredArguments)1495644d152SBalazs Benics TEST(CallDescription, RequiredArguments) {
150ec8e9564SArtem Dergachev   EXPECT_TRUE(tooling::runToolOnCode(
1515644d152SBalazs Benics       std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({
152*58bad286SDonát Nagy           {{CDM::SimpleFunc, {"foo"}, 1}, true},
153*58bad286SDonát Nagy           {{CDM::SimpleFunc, {"foo"}, 2}, false},
1545644d152SBalazs Benics       })),
1555644d152SBalazs Benics       "void foo(int); void foo(int, int); void bar() { foo(1); }"));
1565644d152SBalazs Benics }
157ec8e9564SArtem Dergachev 
TEST(CallDescription,LackOfRequiredArguments)1585644d152SBalazs Benics TEST(CallDescription, LackOfRequiredArguments) {
159ec8e9564SArtem Dergachev   EXPECT_TRUE(tooling::runToolOnCode(
1605644d152SBalazs Benics       std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({
161*58bad286SDonát Nagy           {{CDM::SimpleFunc, {"foo"}, std::nullopt}, true},
162*58bad286SDonát Nagy           {{CDM::SimpleFunc, {"foo"}, 2}, false},
1635644d152SBalazs Benics       })),
1645644d152SBalazs Benics       "void foo(int); void foo(int, int); void bar() { foo(1); }"));
1655644d152SBalazs Benics }
166ec8e9564SArtem Dergachev 
1675644d152SBalazs Benics constexpr StringRef MockStdStringHeader = R"code(
1685644d152SBalazs Benics   namespace std { inline namespace __1 {
1695644d152SBalazs Benics     template<typename T> class basic_string {
1705644d152SBalazs Benics       class Allocator {};
1715644d152SBalazs Benics     public:
1725644d152SBalazs Benics       basic_string();
1735644d152SBalazs Benics       explicit basic_string(const char*, const Allocator & = Allocator());
1745644d152SBalazs Benics       ~basic_string();
1755644d152SBalazs Benics       T *c_str();
1765644d152SBalazs Benics     };
1775644d152SBalazs Benics   } // namespace __1
1785644d152SBalazs Benics   using string = __1::basic_string<char>;
1795644d152SBalazs Benics   } // namespace std
1805644d152SBalazs Benics )code";
1815644d152SBalazs Benics 
TEST(CallDescription,QualifiedNames)1825644d152SBalazs Benics TEST(CallDescription, QualifiedNames) {
1835644d152SBalazs Benics   constexpr StringRef AdditionalCode = R"code(
1845644d152SBalazs Benics     void foo() {
1855644d152SBalazs Benics       using namespace std;
1865644d152SBalazs Benics       basic_string<char> s;
1875644d152SBalazs Benics       s.c_str();
1885644d152SBalazs Benics     })code";
1895644d152SBalazs Benics   const std::string Code = (Twine{MockStdStringHeader} + AdditionalCode).str();
190ec8e9564SArtem Dergachev   EXPECT_TRUE(tooling::runToolOnCode(
1915644d152SBalazs Benics       std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({
192*58bad286SDonát Nagy           {{CDM::CXXMethod, {"std", "basic_string", "c_str"}}, true},
193b22804b3SDmitri Gribenko       })),
1945644d152SBalazs Benics       Code));
1955644d152SBalazs Benics }
196ec8e9564SArtem Dergachev 
TEST(CallDescription,MatchConstructor)1975644d152SBalazs Benics TEST(CallDescription, MatchConstructor) {
1985644d152SBalazs Benics   constexpr StringRef AdditionalCode = R"code(
1995644d152SBalazs Benics     void foo() {
2005644d152SBalazs Benics       using namespace std;
2015644d152SBalazs Benics       basic_string<char> s("hello");
2025644d152SBalazs Benics     })code";
2035644d152SBalazs Benics   const std::string Code = (Twine{MockStdStringHeader} + AdditionalCode).str();
204ec8e9564SArtem Dergachev   EXPECT_TRUE(tooling::runToolOnCode(
2055644d152SBalazs Benics       std::unique_ptr<FrontendAction>(
2065644d152SBalazs Benics           new CallDescriptionAction<CXXConstructExpr>({
207*58bad286SDonát Nagy               {{CDM::CXXMethod, {"std", "basic_string", "basic_string"}, 2, 2},
208*58bad286SDonát Nagy                true},
2095644d152SBalazs Benics           })),
2105644d152SBalazs Benics       Code));
2115644d152SBalazs Benics }
2125644d152SBalazs Benics 
2135644d152SBalazs Benics // FIXME: Test matching destructors: {"std", "basic_string", "~basic_string"}
2145644d152SBalazs Benics //        This feature is actually implemented, but the test infra is not yet
2155644d152SBalazs Benics //        sophisticated enough for testing this. To do that, we will need to
2165644d152SBalazs Benics //        implement a much more advanced dispatching mechanism using the CFG for
2175644d152SBalazs Benics //        the implicit destructor events.
2185644d152SBalazs Benics 
TEST(CallDescription,MatchConversionOperator)2195644d152SBalazs Benics TEST(CallDescription, MatchConversionOperator) {
2205644d152SBalazs Benics   constexpr StringRef Code = R"code(
2215644d152SBalazs Benics     namespace aaa {
2225644d152SBalazs Benics     namespace bbb {
2235644d152SBalazs Benics     struct Bar {
2245644d152SBalazs Benics       operator int();
2255644d152SBalazs Benics     };
2265644d152SBalazs Benics     } // bbb
2275644d152SBalazs Benics     } // aaa
2285644d152SBalazs Benics     void foo() {
2295644d152SBalazs Benics       aaa::bbb::Bar x;
2305644d152SBalazs Benics       int tmp = x;
2315644d152SBalazs Benics     })code";
2325644d152SBalazs Benics   EXPECT_TRUE(tooling::runToolOnCode(
2335644d152SBalazs Benics       std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({
234*58bad286SDonát Nagy           {{CDM::CXXMethod, {"aaa", "bbb", "Bar", "operator int"}}, true},
2355644d152SBalazs Benics       })),
2365644d152SBalazs Benics       Code));
2375644d152SBalazs Benics }
2385644d152SBalazs Benics 
TEST(CallDescription,RejectOverQualifiedNames)2395644d152SBalazs Benics TEST(CallDescription, RejectOverQualifiedNames) {
2405644d152SBalazs Benics   constexpr auto Code = R"code(
2415644d152SBalazs Benics     namespace my {
2425644d152SBalazs Benics     namespace std {
2435644d152SBalazs Benics       struct container {
2445644d152SBalazs Benics         const char *data() const;
2455644d152SBalazs Benics       };
2465644d152SBalazs Benics     } // namespace std
2475644d152SBalazs Benics     } // namespace my
2485644d152SBalazs Benics 
2495644d152SBalazs Benics     void foo() {
2505644d152SBalazs Benics       using namespace my;
2515644d152SBalazs Benics       std::container v;
2525644d152SBalazs Benics       v.data();
2535644d152SBalazs Benics     })code";
2545644d152SBalazs Benics 
2555644d152SBalazs Benics   // FIXME: We should **not** match.
2565644d152SBalazs Benics   EXPECT_TRUE(tooling::runToolOnCode(
2575644d152SBalazs Benics       std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({
258*58bad286SDonát Nagy           {{CDM::CXXMethod, {"std", "container", "data"}}, true},
2595644d152SBalazs Benics       })),
2605644d152SBalazs Benics       Code));
2615644d152SBalazs Benics }
2625644d152SBalazs Benics 
TEST(CallDescription,DontSkipNonInlineNamespaces)2635644d152SBalazs Benics TEST(CallDescription, DontSkipNonInlineNamespaces) {
2645644d152SBalazs Benics   constexpr auto Code = R"code(
2655644d152SBalazs Benics     namespace my {
2665644d152SBalazs Benics     /*not inline*/ namespace v1 {
2675644d152SBalazs Benics       void bar();
2685644d152SBalazs Benics     } // namespace v1
2695644d152SBalazs Benics     } // namespace my
2705644d152SBalazs Benics     void foo() {
2715644d152SBalazs Benics       my::v1::bar();
2725644d152SBalazs Benics     })code";
2735644d152SBalazs Benics 
2745644d152SBalazs Benics   {
2755644d152SBalazs Benics     SCOPED_TRACE("my v1 bar");
2765644d152SBalazs Benics     EXPECT_TRUE(tooling::runToolOnCode(
2775644d152SBalazs Benics         std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({
278*58bad286SDonát Nagy             {{CDM::SimpleFunc, {"my", "v1", "bar"}}, true},
2795644d152SBalazs Benics         })),
2805644d152SBalazs Benics         Code));
2815644d152SBalazs Benics   }
2825644d152SBalazs Benics   {
2835644d152SBalazs Benics     // FIXME: We should **not** skip non-inline namespaces.
2845644d152SBalazs Benics     SCOPED_TRACE("my bar");
2855644d152SBalazs Benics     EXPECT_TRUE(tooling::runToolOnCode(
2865644d152SBalazs Benics         std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({
287*58bad286SDonát Nagy             {{CDM::SimpleFunc, {"my", "bar"}}, true},
2885644d152SBalazs Benics         })),
2895644d152SBalazs Benics         Code));
2905644d152SBalazs Benics   }
2915644d152SBalazs Benics }
2925644d152SBalazs Benics 
TEST(CallDescription,SkipTopInlineNamespaces)2935644d152SBalazs Benics TEST(CallDescription, SkipTopInlineNamespaces) {
2945644d152SBalazs Benics   constexpr auto Code = R"code(
2955644d152SBalazs Benics     inline namespace my {
2965644d152SBalazs Benics     namespace v1 {
2975644d152SBalazs Benics       void bar();
2985644d152SBalazs Benics     } // namespace v1
2995644d152SBalazs Benics     } // namespace my
3005644d152SBalazs Benics     void foo() {
3015644d152SBalazs Benics       using namespace v1;
3025644d152SBalazs Benics       bar();
3035644d152SBalazs Benics     })code";
3045644d152SBalazs Benics 
3055644d152SBalazs Benics   {
3065644d152SBalazs Benics     SCOPED_TRACE("my v1 bar");
3075644d152SBalazs Benics     EXPECT_TRUE(tooling::runToolOnCode(
3085644d152SBalazs Benics         std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({
309*58bad286SDonát Nagy             {{CDM::SimpleFunc, {"my", "v1", "bar"}}, true},
3105644d152SBalazs Benics         })),
3115644d152SBalazs Benics         Code));
3125644d152SBalazs Benics   }
3135644d152SBalazs Benics   {
3145644d152SBalazs Benics     SCOPED_TRACE("v1 bar");
3155644d152SBalazs Benics     EXPECT_TRUE(tooling::runToolOnCode(
3165644d152SBalazs Benics         std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({
317*58bad286SDonát Nagy             {{CDM::SimpleFunc, {"v1", "bar"}}, true},
3185644d152SBalazs Benics         })),
3195644d152SBalazs Benics         Code));
3205644d152SBalazs Benics   }
3215644d152SBalazs Benics }
3225644d152SBalazs Benics 
TEST(CallDescription,SkipAnonimousNamespaces)3235644d152SBalazs Benics TEST(CallDescription, SkipAnonimousNamespaces) {
3245644d152SBalazs Benics   constexpr auto Code = R"code(
3255644d152SBalazs Benics     namespace {
3265644d152SBalazs Benics     namespace std {
3275644d152SBalazs Benics     namespace {
3285644d152SBalazs Benics     inline namespace {
3295644d152SBalazs Benics       struct container {
3305644d152SBalazs Benics         const char *data() const { return nullptr; };
3315644d152SBalazs Benics       };
3325644d152SBalazs Benics     } // namespace inline anonymous
3335644d152SBalazs Benics     } // namespace anonymous
3345644d152SBalazs Benics     } // namespace std
3355644d152SBalazs Benics     } // namespace anonymous
3365644d152SBalazs Benics 
3375644d152SBalazs Benics     void foo() {
3385644d152SBalazs Benics       std::container v;
3395644d152SBalazs Benics       v.data();
3405644d152SBalazs Benics     })code";
3415644d152SBalazs Benics 
3425644d152SBalazs Benics   EXPECT_TRUE(tooling::runToolOnCode(
3435644d152SBalazs Benics       std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({
344*58bad286SDonát Nagy           {{CDM::CXXMethod, {"std", "container", "data"}}, true},
3455644d152SBalazs Benics       })),
3465644d152SBalazs Benics       Code));
3475644d152SBalazs Benics }
3485644d152SBalazs Benics 
TEST(CallDescription,AliasNames)3495644d152SBalazs Benics TEST(CallDescription, AliasNames) {
3505644d152SBalazs Benics   constexpr StringRef AliasNamesCode = R"code(
3515644d152SBalazs Benics   namespace std {
3525644d152SBalazs Benics     struct container {
3535644d152SBalazs Benics       const char *data() const;
3545644d152SBalazs Benics     };
3555644d152SBalazs Benics     using cont = container;
3565644d152SBalazs Benics   } // std
3575644d152SBalazs Benics )code";
3585644d152SBalazs Benics 
3595644d152SBalazs Benics   constexpr StringRef UseAliasInSpelling = R"code(
3605644d152SBalazs Benics     void foo() {
3615644d152SBalazs Benics       std::cont v;
3625644d152SBalazs Benics       v.data();
3635644d152SBalazs Benics     })code";
3645644d152SBalazs Benics   constexpr StringRef UseStructNameInSpelling = R"code(
3655644d152SBalazs Benics     void foo() {
3665644d152SBalazs Benics       std::container v;
3675644d152SBalazs Benics       v.data();
3685644d152SBalazs Benics     })code";
3695644d152SBalazs Benics   const std::string UseAliasInSpellingCode =
3705644d152SBalazs Benics       (Twine{AliasNamesCode} + UseAliasInSpelling).str();
3715644d152SBalazs Benics   const std::string UseStructNameInSpellingCode =
3725644d152SBalazs Benics       (Twine{AliasNamesCode} + UseStructNameInSpelling).str();
3735644d152SBalazs Benics 
3745644d152SBalazs Benics   // Test if the code spells the alias, wile we match against the struct name,
3755644d152SBalazs Benics   // and again matching against the alias.
3765644d152SBalazs Benics   {
3775644d152SBalazs Benics     SCOPED_TRACE("Using alias in spelling");
3785644d152SBalazs Benics     {
3795644d152SBalazs Benics       SCOPED_TRACE("std container data");
3805644d152SBalazs Benics       EXPECT_TRUE(tooling::runToolOnCode(
3815644d152SBalazs Benics           std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({
382*58bad286SDonát Nagy               {{CDM::CXXMethod, {"std", "container", "data"}}, true},
3835644d152SBalazs Benics           })),
3845644d152SBalazs Benics           UseAliasInSpellingCode));
3855644d152SBalazs Benics     }
3865644d152SBalazs Benics     {
3875644d152SBalazs Benics       // FIXME: We should be able to see-through aliases.
3885644d152SBalazs Benics       SCOPED_TRACE("std cont data");
3895644d152SBalazs Benics       EXPECT_TRUE(tooling::runToolOnCode(
3905644d152SBalazs Benics           std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({
391*58bad286SDonát Nagy               {{CDM::CXXMethod, {"std", "cont", "data"}}, false},
3925644d152SBalazs Benics           })),
3935644d152SBalazs Benics           UseAliasInSpellingCode));
3945644d152SBalazs Benics     }
3955644d152SBalazs Benics   }
3965644d152SBalazs Benics 
3975644d152SBalazs Benics   // Test if the code spells the struct name, wile we match against the struct
3985644d152SBalazs Benics   // name, and again matching against the alias.
3995644d152SBalazs Benics   {
4005644d152SBalazs Benics     SCOPED_TRACE("Using struct name in spelling");
4015644d152SBalazs Benics     {
4025644d152SBalazs Benics       SCOPED_TRACE("std container data");
4035644d152SBalazs Benics       EXPECT_TRUE(tooling::runToolOnCode(
4045644d152SBalazs Benics           std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({
405*58bad286SDonát Nagy               {{CDM::CXXMethod, {"std", "container", "data"}}, true},
4065644d152SBalazs Benics           })),
4075644d152SBalazs Benics           UseAliasInSpellingCode));
4085644d152SBalazs Benics     }
4095644d152SBalazs Benics     {
4105644d152SBalazs Benics       // FIXME: We should be able to see-through aliases.
4115644d152SBalazs Benics       SCOPED_TRACE("std cont data");
4125644d152SBalazs Benics       EXPECT_TRUE(tooling::runToolOnCode(
4135644d152SBalazs Benics           std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({
414*58bad286SDonát Nagy               {{CDM::CXXMethod, {"std", "cont", "data"}}, false},
4155644d152SBalazs Benics           })),
4165644d152SBalazs Benics           UseAliasInSpellingCode));
4175644d152SBalazs Benics     }
4185644d152SBalazs Benics   }
4195644d152SBalazs Benics }
4205644d152SBalazs Benics 
TEST(CallDescription,AliasSingleNamespace)4215644d152SBalazs Benics TEST(CallDescription, AliasSingleNamespace) {
4225644d152SBalazs Benics   constexpr StringRef Code = R"code(
4235644d152SBalazs Benics     namespace aaa {
4245644d152SBalazs Benics     namespace bbb {
4255644d152SBalazs Benics     namespace ccc {
4265644d152SBalazs Benics       void bar();
4275644d152SBalazs Benics     }} // namespace bbb::ccc
4285644d152SBalazs Benics     namespace bbb_alias = bbb;
4295644d152SBalazs Benics     } // namespace aaa
4305644d152SBalazs Benics     void foo() {
4315644d152SBalazs Benics       aaa::bbb_alias::ccc::bar();
4325644d152SBalazs Benics     })code";
4335644d152SBalazs Benics   {
4345644d152SBalazs Benics     SCOPED_TRACE("aaa bbb ccc bar");
4355644d152SBalazs Benics     EXPECT_TRUE(tooling::runToolOnCode(
4365644d152SBalazs Benics         std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({
437*58bad286SDonát Nagy             {{CDM::SimpleFunc, {"aaa", "bbb", "ccc", "bar"}}, true},
4385644d152SBalazs Benics         })),
4395644d152SBalazs Benics         Code));
4405644d152SBalazs Benics   }
4415644d152SBalazs Benics   {
4425644d152SBalazs Benics     // FIXME: We should be able to see-through namespace aliases.
4435644d152SBalazs Benics     SCOPED_TRACE("aaa bbb_alias ccc bar");
4445644d152SBalazs Benics     EXPECT_TRUE(tooling::runToolOnCode(
4455644d152SBalazs Benics         std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({
446*58bad286SDonát Nagy             {{CDM::SimpleFunc, {"aaa", "bbb_alias", "ccc", "bar"}}, false},
4475644d152SBalazs Benics         })),
4485644d152SBalazs Benics         Code));
4495644d152SBalazs Benics   }
4505644d152SBalazs Benics }
4515644d152SBalazs Benics 
TEST(CallDescription,AliasMultipleNamespaces)4525644d152SBalazs Benics TEST(CallDescription, AliasMultipleNamespaces) {
4535644d152SBalazs Benics   constexpr StringRef Code = R"code(
4545644d152SBalazs Benics     namespace aaa {
4555644d152SBalazs Benics     namespace bbb {
4565644d152SBalazs Benics     namespace ccc {
4575644d152SBalazs Benics       void bar();
4585644d152SBalazs Benics     }}} // namespace aaa::bbb::ccc
4595644d152SBalazs Benics     namespace aaa_bbb_ccc = aaa::bbb::ccc;
4605644d152SBalazs Benics     void foo() {
4615644d152SBalazs Benics       using namespace aaa_bbb_ccc;
4625644d152SBalazs Benics       bar();
4635644d152SBalazs Benics     })code";
4645644d152SBalazs Benics   {
4655644d152SBalazs Benics     SCOPED_TRACE("aaa bbb ccc bar");
4665644d152SBalazs Benics     EXPECT_TRUE(tooling::runToolOnCode(
4675644d152SBalazs Benics         std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({
468*58bad286SDonát Nagy             {{CDM::SimpleFunc, {"aaa", "bbb", "ccc", "bar"}}, true},
4695644d152SBalazs Benics         })),
4705644d152SBalazs Benics         Code));
4715644d152SBalazs Benics   }
4725644d152SBalazs Benics   {
4735644d152SBalazs Benics     // FIXME: We should be able to see-through namespace aliases.
4745644d152SBalazs Benics     SCOPED_TRACE("aaa_bbb_ccc bar");
4755644d152SBalazs Benics     EXPECT_TRUE(tooling::runToolOnCode(
4765644d152SBalazs Benics         std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({
477*58bad286SDonát Nagy             {{CDM::SimpleFunc, {"aaa_bbb_ccc", "bar"}}, false},
4785644d152SBalazs Benics         })),
4795644d152SBalazs Benics         Code));
4805644d152SBalazs Benics   }
4815644d152SBalazs Benics }
4825644d152SBalazs Benics 
TEST(CallDescription,NegativeMatchQualifiedNames)4835644d152SBalazs Benics TEST(CallDescription, NegativeMatchQualifiedNames) {
4845644d152SBalazs Benics   EXPECT_TRUE(tooling::runToolOnCode(
4855644d152SBalazs Benics       std::unique_ptr<FrontendAction>(new CallDescriptionAction<>({
486*58bad286SDonát Nagy           {{CDM::Unspecified, {"foo", "bar"}}, false},
487*58bad286SDonát Nagy           {{CDM::Unspecified, {"bar", "foo"}}, false},
488*58bad286SDonát Nagy           {{CDM::Unspecified, {"foo"}}, true},
4895644d152SBalazs Benics       })),
4905644d152SBalazs Benics       "void foo(); struct bar { void foo(); }; void test() { foo(); }"));
4915644d152SBalazs Benics }
492f301096fSArtem Dergachev 
TEST(CallDescription,MatchBuiltins)4935644d152SBalazs Benics TEST(CallDescription, MatchBuiltins) {
494fb299caeSNagyDonat   // Test the matching modes CDM::CLibrary and CDM::CLibraryMaybeHardened,
495fb299caeSNagyDonat   // which can recognize builtin variants of C library functions.
496fb299caeSNagyDonat   {
497fb299caeSNagyDonat     SCOPED_TRACE("hardened variants of functions");
498f301096fSArtem Dergachev     EXPECT_TRUE(tooling::runToolOnCode(
4995644d152SBalazs Benics         std::unique_ptr<FrontendAction>(new CallDescriptionAction<>(
500fb299caeSNagyDonat             {{{CDM::Unspecified, {"memset"}, 3}, false},
501fb299caeSNagyDonat              {{CDM::CLibrary, {"memset"}, 3}, false},
502fb299caeSNagyDonat              {{CDM::CLibraryMaybeHardened, {"memset"}, 3}, true}})),
503f301096fSArtem Dergachev         "void foo() {"
504f301096fSArtem Dergachev         "  int x;"
505f301096fSArtem Dergachev         "  __builtin___memset_chk(&x, 0, sizeof(x),"
506f301096fSArtem Dergachev         "                         __builtin_object_size(&x, 0));"
507f301096fSArtem Dergachev         "}"));
508fb299caeSNagyDonat   }
509abc87369SBalazs Benics   {
510abc87369SBalazs Benics     SCOPED_TRACE("multiple similar builtins");
511abc87369SBalazs Benics     EXPECT_TRUE(tooling::runToolOnCode(
512abc87369SBalazs Benics         std::unique_ptr<FrontendAction>(new CallDescriptionAction<>(
51352a460f9SNagyDonat             {{{CDM::CLibrary, {"memcpy"}, 3}, false},
51452a460f9SNagyDonat              {{CDM::CLibrary, {"wmemcpy"}, 3}, true}})),
515abc87369SBalazs Benics         R"(void foo(wchar_t *x, wchar_t *y) {
516abc87369SBalazs Benics             __builtin_wmemcpy(x, y, sizeof(wchar_t));
517abc87369SBalazs Benics           })"));
518abc87369SBalazs Benics   }
519abc87369SBalazs Benics   {
520abc87369SBalazs Benics     SCOPED_TRACE("multiple similar builtins reversed order");
521abc87369SBalazs Benics     EXPECT_TRUE(tooling::runToolOnCode(
522abc87369SBalazs Benics         std::unique_ptr<FrontendAction>(new CallDescriptionAction<>(
52352a460f9SNagyDonat             {{{CDM::CLibrary, {"wmemcpy"}, 3}, true},
52452a460f9SNagyDonat              {{CDM::CLibrary, {"memcpy"}, 3}, false}})),
525abc87369SBalazs Benics         R"(void foo(wchar_t *x, wchar_t *y) {
526abc87369SBalazs Benics             __builtin_wmemcpy(x, y, sizeof(wchar_t));
527abc87369SBalazs Benics           })"));
528abc87369SBalazs Benics   }
529abc87369SBalazs Benics   {
530fb299caeSNagyDonat     SCOPED_TRACE("multiple similar builtins with hardened variant");
531fb299caeSNagyDonat     EXPECT_TRUE(tooling::runToolOnCode(
532fb299caeSNagyDonat         std::unique_ptr<FrontendAction>(new CallDescriptionAction<>(
533fb299caeSNagyDonat             {{{CDM::CLibraryMaybeHardened, {"memcpy"}, 3}, false},
534fb299caeSNagyDonat              {{CDM::CLibraryMaybeHardened, {"wmemcpy"}, 3}, true}})),
535fb299caeSNagyDonat         R"(typedef __typeof(sizeof(int)) size_t;
536fb299caeSNagyDonat           extern wchar_t *__wmemcpy_chk (wchar_t *__restrict __s1,
537fb299caeSNagyDonat                                           const wchar_t *__restrict __s2,
538fb299caeSNagyDonat                                           size_t __n, size_t __ns1);
539fb299caeSNagyDonat           void foo(wchar_t *x, wchar_t *y) {
540fb299caeSNagyDonat             __wmemcpy_chk(x, y, sizeof(wchar_t), 1234);
541fb299caeSNagyDonat           })"));
542fb299caeSNagyDonat   }
543fb299caeSNagyDonat   {
544fb299caeSNagyDonat     SCOPED_TRACE(
545fb299caeSNagyDonat         "multiple similar builtins with hardened variant reversed order");
546fb299caeSNagyDonat     EXPECT_TRUE(tooling::runToolOnCode(
547fb299caeSNagyDonat         std::unique_ptr<FrontendAction>(new CallDescriptionAction<>(
548fb299caeSNagyDonat             {{{CDM::CLibraryMaybeHardened, {"wmemcpy"}, 3}, true},
549fb299caeSNagyDonat              {{CDM::CLibraryMaybeHardened, {"memcpy"}, 3}, false}})),
550fb299caeSNagyDonat         R"(typedef __typeof(sizeof(int)) size_t;
551fb299caeSNagyDonat           extern wchar_t *__wmemcpy_chk (wchar_t *__restrict __s1,
552fb299caeSNagyDonat                                           const wchar_t *__restrict __s2,
553fb299caeSNagyDonat                                           size_t __n, size_t __ns1);
554fb299caeSNagyDonat           void foo(wchar_t *x, wchar_t *y) {
555fb299caeSNagyDonat             __wmemcpy_chk(x, y, sizeof(wchar_t), 1234);
556fb299caeSNagyDonat           })"));
557fb299caeSNagyDonat   }
558fb299caeSNagyDonat   {
559abc87369SBalazs Benics     SCOPED_TRACE("lookbehind and lookahead mismatches");
560abc87369SBalazs Benics     EXPECT_TRUE(tooling::runToolOnCode(
56152a460f9SNagyDonat         std::unique_ptr<FrontendAction>(
56252a460f9SNagyDonat             new CallDescriptionAction<>({{{CDM::CLibrary, {"func"}}, false}})),
563abc87369SBalazs Benics         R"(
564abc87369SBalazs Benics           void funcXXX();
565abc87369SBalazs Benics           void XXXfunc();
566abc87369SBalazs Benics           void XXXfuncXXX();
567abc87369SBalazs Benics           void test() {
568abc87369SBalazs Benics             funcXXX();
569abc87369SBalazs Benics             XXXfunc();
570abc87369SBalazs Benics             XXXfuncXXX();
571abc87369SBalazs Benics           })"));
572abc87369SBalazs Benics   }
573abc87369SBalazs Benics   {
574abc87369SBalazs Benics     SCOPED_TRACE("lookbehind and lookahead matches");
575abc87369SBalazs Benics     EXPECT_TRUE(tooling::runToolOnCode(
57652a460f9SNagyDonat         std::unique_ptr<FrontendAction>(
57752a460f9SNagyDonat             new CallDescriptionAction<>({{{CDM::CLibrary, {"func"}}, true}})),
578abc87369SBalazs Benics         R"(
579abc87369SBalazs Benics           void func();
580abc87369SBalazs Benics           void func_XXX();
581abc87369SBalazs Benics           void XXX_func();
582abc87369SBalazs Benics           void XXX_func_XXX();
583abc87369SBalazs Benics 
584abc87369SBalazs Benics           void test() {
585abc87369SBalazs Benics             func(); // exact match
586abc87369SBalazs Benics             func_XXX();
587abc87369SBalazs Benics             XXX_func();
588abc87369SBalazs Benics             XXX_func_XXX();
589abc87369SBalazs Benics           })"));
590abc87369SBalazs Benics   }
591ec8e9564SArtem Dergachev }
592ec8e9564SArtem Dergachev 
59332ac21d0SKristóf Umann //===----------------------------------------------------------------------===//
59432ac21d0SKristóf Umann // Testing through a checker interface.
59532ac21d0SKristóf Umann //
59632ac21d0SKristóf Umann // Above, the static analyzer isn't run properly, only the bare minimum to
59732ac21d0SKristóf Umann // create CallEvents. This causes CallEvents through function pointers to not
59832ac21d0SKristóf Umann // refer to the pointee function, but this works fine if we run
59932ac21d0SKristóf Umann // AnalysisASTConsumer.
60032ac21d0SKristóf Umann //===----------------------------------------------------------------------===//
60132ac21d0SKristóf Umann 
60232ac21d0SKristóf Umann class CallDescChecker
60332ac21d0SKristóf Umann     : public Checker<check::PreCall, check::PreStmt<CallExpr>> {
604*58bad286SDonát Nagy   CallDescriptionSet Set = {{CDM::SimpleFunc, {"bar"}, 0}};
60532ac21d0SKristóf Umann 
60632ac21d0SKristóf Umann public:
checkPreCall(const CallEvent & Call,CheckerContext & C) const60732ac21d0SKristóf Umann   void checkPreCall(const CallEvent &Call, CheckerContext &C) const {
60832ac21d0SKristóf Umann     if (Set.contains(Call)) {
60932ac21d0SKristóf Umann       C.getBugReporter().EmitBasicReport(
61032ac21d0SKristóf Umann           Call.getDecl(), this, "CallEvent match", categories::LogicError,
61132ac21d0SKristóf Umann           "CallEvent match",
61232ac21d0SKristóf Umann           PathDiagnosticLocation{Call.getDecl(), C.getSourceManager()});
61332ac21d0SKristóf Umann     }
61432ac21d0SKristóf Umann   }
61532ac21d0SKristóf Umann 
checkPreStmt(const CallExpr * CE,CheckerContext & C) const61632ac21d0SKristóf Umann   void checkPreStmt(const CallExpr *CE, CheckerContext &C) const {
61732ac21d0SKristóf Umann     if (Set.containsAsWritten(*CE)) {
61832ac21d0SKristóf Umann       C.getBugReporter().EmitBasicReport(
61932ac21d0SKristóf Umann           CE->getCalleeDecl(), this, "CallExpr match", categories::LogicError,
62032ac21d0SKristóf Umann           "CallExpr match",
62132ac21d0SKristóf Umann           PathDiagnosticLocation{CE->getCalleeDecl(), C.getSourceManager()});
62232ac21d0SKristóf Umann     }
62332ac21d0SKristóf Umann   }
62432ac21d0SKristóf Umann };
62532ac21d0SKristóf Umann 
addCallDescChecker(AnalysisASTConsumer & AnalysisConsumer,AnalyzerOptions & AnOpts)62632ac21d0SKristóf Umann void addCallDescChecker(AnalysisASTConsumer &AnalysisConsumer,
62732ac21d0SKristóf Umann                         AnalyzerOptions &AnOpts) {
62832ac21d0SKristóf Umann   AnOpts.CheckersAndPackages = {{"test.CallDescChecker", true}};
62932ac21d0SKristóf Umann   AnalysisConsumer.AddCheckerRegistrationFn([](CheckerRegistry &Registry) {
63032ac21d0SKristóf Umann     Registry.addChecker<CallDescChecker>("test.CallDescChecker", "Description",
63132ac21d0SKristóf Umann                                          "");
63232ac21d0SKristóf Umann   });
63332ac21d0SKristóf Umann }
63432ac21d0SKristóf Umann 
TEST(CallDescription,CheckCallExprMatching)63532ac21d0SKristóf Umann TEST(CallDescription, CheckCallExprMatching) {
63632ac21d0SKristóf Umann   // Imprecise matching shouldn't catch the call to bar, because its obscured
63732ac21d0SKristóf Umann   // by a function pointer.
63832ac21d0SKristóf Umann   constexpr StringRef FnPtrCode = R"code(
63932ac21d0SKristóf Umann     void bar();
64032ac21d0SKristóf Umann     void foo() {
64132ac21d0SKristóf Umann       void (*fnptr)() = bar;
64232ac21d0SKristóf Umann       fnptr();
64332ac21d0SKristóf Umann     })code";
64432ac21d0SKristóf Umann   std::string Diags;
64532ac21d0SKristóf Umann   EXPECT_TRUE(runCheckerOnCode<addCallDescChecker>(FnPtrCode.str(), Diags,
64632ac21d0SKristóf Umann                                                    /*OnlyEmitWarnings*/ true));
64732ac21d0SKristóf Umann   EXPECT_EQ("test.CallDescChecker: CallEvent match\n", Diags);
64832ac21d0SKristóf Umann 
64932ac21d0SKristóf Umann   // This should be caught properly by imprecise matching, as the call is done
65032ac21d0SKristóf Umann   // purely through syntactic means.
65132ac21d0SKristóf Umann   constexpr StringRef Code = R"code(
65232ac21d0SKristóf Umann     void bar();
65332ac21d0SKristóf Umann     void foo() {
65432ac21d0SKristóf Umann       bar();
65532ac21d0SKristóf Umann     })code";
65632ac21d0SKristóf Umann   Diags.clear();
65732ac21d0SKristóf Umann   EXPECT_TRUE(runCheckerOnCode<addCallDescChecker>(Code.str(), Diags,
65832ac21d0SKristóf Umann                                                    /*OnlyEmitWarnings*/ true));
65932ac21d0SKristóf Umann   EXPECT_EQ("test.CallDescChecker: CallEvent match\n"
66032ac21d0SKristóf Umann             "test.CallDescChecker: CallExpr match\n",
66132ac21d0SKristóf Umann             Diags);
66232ac21d0SKristóf Umann }
66332ac21d0SKristóf Umann 
664ec8e9564SArtem Dergachev } // namespace
665ec8e9564SArtem Dergachev } // namespace ento
666ec8e9564SArtem Dergachev } // namespace clang
667