xref: /llvm-project/clang/unittests/StaticAnalyzer/SymbolReaperTest.cpp (revision 2946cd701067404b99c39fb29dc9c74bd7193eb3)
1 //===- unittests/StaticAnalyzer/SymbolReaperTest.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 "clang/ASTMatchers/ASTMatchFinder.h"
10 #include "clang/Frontend/CompilerInstance.h"
11 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
12 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
13 #include "clang/CrossTU/CrossTranslationUnit.h"
14 #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
15 #include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h"
16 #include "clang/Tooling/Tooling.h"
17 #include "gtest/gtest.h"
18 
19 namespace clang {
20 namespace ento {
21 namespace {
22 
23 using namespace ast_matchers;
24 
25 // A re-usable consumer that constructs ExprEngine out of CompilerInvocation.
26 // TODO: Actually re-use it when we write our second test.
27 class ExprEngineConsumer : public ASTConsumer {
28 protected:
29   CompilerInstance &C;
30 
31 private:
32   // We need to construct all of these in order to construct ExprEngine.
33   CheckerManager ChkMgr;
34   cross_tu::CrossTranslationUnitContext CTU;
35   PathDiagnosticConsumers Consumers;
36   AnalysisManager AMgr;
37   SetOfConstDecls VisitedCallees;
38   FunctionSummariesTy FS;
39 
40 protected:
41   ExprEngine Eng;
42 
43   // Find a declaration in the current AST by name. This has nothing to do
44   // with ExprEngine but turns out to be handy.
45   // TODO: There's probably a better place for it.
46   template <typename T>
47   const T *findDeclByName(const Decl *Where, StringRef Name) {
48     auto Matcher = decl(hasDescendant(namedDecl(hasName(Name)).bind("d")));
49     auto Matches = match(Matcher, *Where, Eng.getContext());
50     assert(Matches.size() == 1 && "Ambiguous name!");
51     const T *Node = selectFirst<T>("d", Matches);
52     assert(Node && "Name not found!");
53     return Node;
54   }
55 
56 public:
57   ExprEngineConsumer(CompilerInstance &C)
58       : C(C), ChkMgr(C.getASTContext(), *C.getAnalyzerOpts()), CTU(C),
59         Consumers(),
60         AMgr(C.getASTContext(), C.getDiagnostics(), Consumers,
61              CreateRegionStoreManager, CreateRangeConstraintManager, &ChkMgr,
62              *C.getAnalyzerOpts()),
63         VisitedCallees(), FS(),
64         Eng(CTU, AMgr, &VisitedCallees, &FS, ExprEngine::Inline_Regular) {}
65 };
66 
67 class SuperRegionLivenessConsumer : public ExprEngineConsumer {
68   void performTest(const Decl *D) {
69     const auto *FD = findDeclByName<FieldDecl>(D, "x");
70     const auto *VD = findDeclByName<VarDecl>(D, "s");
71     assert(FD && VD);
72 
73     // The variable must belong to a stack frame,
74     // otherwise SymbolReaper would think it's a global.
75     const StackFrameContext *SFC =
76         Eng.getAnalysisDeclContextManager().getStackFrame(D);
77 
78     // Create regions for 's' and 's.x'.
79     const VarRegion *VR = Eng.getRegionManager().getVarRegion(VD, SFC);
80     const FieldRegion *FR = Eng.getRegionManager().getFieldRegion(FD, VR);
81 
82     // Pass a null location context to the SymbolReaper so that
83     // it was thinking that the variable is dead.
84     SymbolReaper SymReaper((StackFrameContext *)nullptr, (Stmt *)nullptr,
85                            Eng.getSymbolManager(), Eng.getStoreManager());
86 
87     SymReaper.markLive(FR);
88     EXPECT_TRUE(SymReaper.isLiveRegion(VR));
89   }
90 
91 public:
92   SuperRegionLivenessConsumer(CompilerInstance &C) : ExprEngineConsumer(C) {}
93   ~SuperRegionLivenessConsumer() override {}
94 
95   bool HandleTopLevelDecl(DeclGroupRef DG) override {
96     for (const auto *D : DG)
97       performTest(D);
98     return true;
99   }
100 };
101 
102 class SuperRegionLivenessAction: public ASTFrontendAction {
103 public:
104   SuperRegionLivenessAction() {}
105   std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
106                                                  StringRef File) override {
107     return llvm::make_unique<SuperRegionLivenessConsumer>(Compiler);
108   }
109 };
110 
111 // Test that marking s.x as live would also make s live.
112 TEST(SymbolReaper, SuperRegionLiveness) {
113   EXPECT_TRUE(tooling::runToolOnCode(new SuperRegionLivenessAction,
114                                      "void foo() { struct S { int x; } s; }"));
115 }
116 
117 } // namespace
118 } // namespace ento
119 } // namespace clang
120