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