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