diff options
author | Artem Dergachev <artem.dergachev@gmail.com> | 2019-07-01 23:02:03 +0000 |
---|---|---|
committer | Artem Dergachev <artem.dergachev@gmail.com> | 2019-07-01 23:02:03 +0000 |
commit | ec8e95640f066dfe2b97865cc2ce018e7a5f64b9 (patch) | |
tree | 1f12cab550902712f65b861e1af1fbca0010dcae /clang/unittests/StaticAnalyzer/CallDescriptionTest.cpp | |
parent | 02f91ddf1b353b28697b288edbe6a8a828705781 (diff) | |
download | bcm5719-llvm-ec8e95640f066dfe2b97865cc2ce018e7a5f64b9.tar.gz bcm5719-llvm-ec8e95640f066dfe2b97865cc2ce018e7a5f64b9.zip |
[analyzer] NFC: Add a convenient CallDescriptionMap class.
It encapsulates the procedure of figuring out whether a call event
corresponds to a function that's modeled by a checker.
Checker developers no longer need to worry about performance of
lookups into their own custom maps.
Add unittests - which finally test CallDescription itself as well.
Differential Revision: https://reviews.llvm.org/D62441
llvm-svn: 364866
Diffstat (limited to 'clang/unittests/StaticAnalyzer/CallDescriptionTest.cpp')
-rw-r--r-- | clang/unittests/StaticAnalyzer/CallDescriptionTest.cpp | 150 |
1 files changed, 150 insertions, 0 deletions
diff --git a/clang/unittests/StaticAnalyzer/CallDescriptionTest.cpp b/clang/unittests/StaticAnalyzer/CallDescriptionTest.cpp new file mode 100644 index 00000000000..573b6909b18 --- /dev/null +++ b/clang/unittests/StaticAnalyzer/CallDescriptionTest.cpp @@ -0,0 +1,150 @@ +//===- unittests/StaticAnalyzer/CallDescriptionTest.cpp -------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "Reusables.h" + +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/Tooling/Tooling.h" +#include "gtest/gtest.h" + +namespace clang { +namespace ento { +namespace { + +// A wrapper around CallDescriptionMap<bool> that allows verifying that +// all functions have been found. This is needed because CallDescriptionMap +// isn't supposed to support iteration. +class ResultMap { + size_t Found, Total; + CallDescriptionMap<bool> Impl; + +public: + ResultMap(std::initializer_list<std::pair<CallDescription, bool>> Data) + : Found(0), + Total(std::count_if(Data.begin(), Data.end(), + [](const std::pair<CallDescription, bool> &Pair) { + return Pair.second == true; + })), + Impl(std::move(Data)) {} + + const bool *lookup(const CallEvent &Call) { + const bool *Result = Impl.lookup(Call); + // If it's a function we expected to find, remember that we've found it. + if (Result && *Result) + ++Found; + return Result; + } + + // Fail the test if we haven't found all the true-calls we were looking for. + ~ResultMap() { EXPECT_EQ(Found, Total); } +}; + +// Scan the code body for call expressions and see if we find all calls that +// we were supposed to find ("true" in the provided ResultMap) and that we +// don't find the ones that we weren't supposed to find +// ("false" in the ResultMap). +class CallDescriptionConsumer : public ExprEngineConsumer { + ResultMap &RM; + void performTest(const Decl *D) { + using namespace ast_matchers; + + if (!D->hasBody()) + return; + + const CallExpr *CE = findNode<CallExpr>(D, callExpr()); + const StackFrameContext *SFC = + Eng.getAnalysisDeclContextManager().getStackFrame(D); + ProgramStateRef State = Eng.getInitialState(SFC); + CallEventRef<> Call = + Eng.getStateManager().getCallEventManager().getCall(CE, State, SFC); + + const bool *LookupResult = RM.lookup(*Call); + // Check that we've found the function in the map + // with the correct description. + EXPECT_TRUE(LookupResult && *LookupResult); + + // ResultMap is responsible for making sure that we've found *all* calls. + } + +public: + CallDescriptionConsumer(CompilerInstance &C, + ResultMap &RM) + : ExprEngineConsumer(C), RM(RM) {} + + bool HandleTopLevelDecl(DeclGroupRef DG) override { + for (const auto *D : DG) + performTest(D); + return true; + } +}; + +class CallDescriptionAction : public ASTFrontendAction { + ResultMap RM; + +public: + CallDescriptionAction( + std::initializer_list<std::pair<CallDescription, bool>> Data) + : RM(Data) {} + + std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler, + StringRef File) override { + return llvm::make_unique<CallDescriptionConsumer>(Compiler, RM); + } +}; + +TEST(CallEvent, CallDescription) { + // Test simple name matching. + EXPECT_TRUE(tooling::runToolOnCode( + new CallDescriptionAction({ + {{"bar"}, false}, // false: there's no call to 'bar' in this code. + {{"foo"}, true}, // true: there's a call to 'foo' in this code. + }), "void foo(); void bar() { foo(); }")); + + // Test arguments check. + EXPECT_TRUE(tooling::runToolOnCode( + new CallDescriptionAction({ + {{"foo", 1}, true}, + {{"foo", 2}, false}, + }), "void foo(int); void foo(int, int); void bar() { foo(1); }")); + + // Test lack of arguments check. + EXPECT_TRUE(tooling::runToolOnCode( + new CallDescriptionAction({ + {{"foo", None}, true}, + {{"foo", 2}, false}, + }), "void foo(int); void foo(int, int); void bar() { foo(1); }")); + + // Test qualified names. + EXPECT_TRUE(tooling::runToolOnCode( + new CallDescriptionAction({ + {{{"std", "basic_string", "c_str"}}, true}, + }), + "namespace std { inline namespace __1 {" + " template<typename T> class basic_string {" + " public:" + " T *c_str();" + " };" + "}}" + "void foo() {" + " using namespace std;" + " basic_string<char> s;" + " s.c_str();" + "}")); + + // A negative test for qualified names. + EXPECT_TRUE(tooling::runToolOnCode( + new CallDescriptionAction({ + {{{"foo", "bar"}}, false}, + {{{"bar", "foo"}}, false}, + {{"foo"}, true}, + }), "void foo(); struct bar { void foo(); }; void test() { foo(); }")); +} + +} // namespace +} // namespace ento +} // namespace clang |