1 | //===- unittests/StaticAnalyzer/Reusables.h -------------------------------===// |
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 | #ifndef LLVM_CLANG_UNITTESTS_STATICANALYZER_REUSABLES_H |
10 | #define LLVM_CLANG_UNITTESTS_STATICANALYZER_REUSABLES_H |
11 | |
12 | #include "clang/ASTMatchers/ASTMatchFinder.h" |
13 | #include "clang/CrossTU/CrossTranslationUnit.h" |
14 | #include "clang/Frontend/CompilerInstance.h" |
15 | #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" |
16 | #include "gtest/gtest.h" |
17 | |
18 | namespace clang { |
19 | namespace ento { |
20 | |
21 | // Find a node in the current AST that matches a matcher. |
22 | template <typename T, typename MatcherT> |
23 | const T *findNode(const Decl *Where, MatcherT What) { |
24 | using namespace ast_matchers; |
25 | auto Matches = match(decl(hasDescendant(What.bind("root" ))), |
26 | *Where, Where->getASTContext()); |
27 | assert(Matches.size() <= 1 && "Ambiguous match!" ); |
28 | assert(Matches.size() >= 1 && "Match not found!" ); |
29 | const T *Node = selectFirst<T>("root" , Matches); |
30 | assert(Node && "Type mismatch!" ); |
31 | return Node; |
32 | } |
33 | |
34 | // Find a declaration in the current AST by name. |
35 | template <typename T> |
36 | const T *findDeclByName(const Decl *Where, StringRef Name) { |
37 | using namespace ast_matchers; |
38 | return findNode<T>(Where, namedDecl(hasName(Name))); |
39 | } |
40 | |
41 | // A re-usable consumer that constructs ExprEngine out of CompilerInvocation. |
42 | class ExprEngineConsumer : public ASTConsumer { |
43 | protected: |
44 | CompilerInstance &C; |
45 | |
46 | private: |
47 | // We need to construct all of these in order to construct ExprEngine. |
48 | CheckerManager ChkMgr; |
49 | cross_tu::CrossTranslationUnitContext CTU; |
50 | PathDiagnosticConsumers Consumers; |
51 | AnalysisManager AMgr; |
52 | SetOfConstDecls VisitedCallees; |
53 | FunctionSummariesTy FS; |
54 | |
55 | protected: |
56 | ExprEngine Eng; |
57 | |
58 | public: |
59 | ExprEngineConsumer(CompilerInstance &C) |
60 | : C(C), |
61 | ChkMgr(C.getASTContext(), C.getAnalyzerOpts(), C.getPreprocessor()), |
62 | CTU(C), AMgr(C.getASTContext(), C.getPreprocessor(), {}, |
63 | CreateRegionStoreManager, CreateRangeConstraintManager, |
64 | &ChkMgr, C.getAnalyzerOpts()), |
65 | VisitedCallees(), FS(), |
66 | Eng(CTU, AMgr, &VisitedCallees, &FS, ExprEngine::Inline_Regular) {} |
67 | }; |
68 | |
69 | struct ExpectedLocationTy { |
70 | unsigned Line; |
71 | unsigned Column; |
72 | |
73 | void testEquality(SourceLocation L, SourceManager &SM) const { |
74 | EXPECT_EQ(SM.getSpellingLineNumber(L), Line); |
75 | EXPECT_EQ(SM.getSpellingColumnNumber(L), Column); |
76 | } |
77 | }; |
78 | |
79 | struct ExpectedRangeTy { |
80 | ExpectedLocationTy Begin; |
81 | ExpectedLocationTy End; |
82 | |
83 | void testEquality(SourceRange R, SourceManager &SM) const { |
84 | Begin.testEquality(L: R.getBegin(), SM); |
85 | End.testEquality(L: R.getEnd(), SM); |
86 | } |
87 | }; |
88 | |
89 | struct ExpectedPieceTy { |
90 | ExpectedLocationTy Loc; |
91 | std::string Text; |
92 | std::vector<ExpectedRangeTy> Ranges; |
93 | |
94 | void testEquality(const PathDiagnosticPiece &Piece, SourceManager &SM) { |
95 | Loc.testEquality(L: Piece.getLocation().asLocation(), SM); |
96 | EXPECT_EQ(Piece.getString(), Text); |
97 | EXPECT_EQ(Ranges.size(), Piece.getRanges().size()); |
98 | for (const auto &RangeItem : llvm::enumerate(First: Piece.getRanges())) |
99 | Ranges[RangeItem.index()].testEquality(R: RangeItem.value(), SM); |
100 | } |
101 | }; |
102 | |
103 | struct ExpectedDiagTy { |
104 | ExpectedLocationTy Loc; |
105 | std::string VerboseDescription; |
106 | std::string ShortDescription; |
107 | std::string CheckerName; |
108 | std::string BugType; |
109 | std::string Category; |
110 | std::vector<ExpectedPieceTy> Path; |
111 | |
112 | void testEquality(const PathDiagnostic &Diag, SourceManager &SM) { |
113 | Loc.testEquality(L: Diag.getLocation().asLocation(), SM); |
114 | EXPECT_EQ(Diag.getVerboseDescription(), VerboseDescription); |
115 | EXPECT_EQ(Diag.getShortDescription(), ShortDescription); |
116 | EXPECT_EQ(Diag.getCheckerName(), CheckerName); |
117 | EXPECT_EQ(Diag.getBugType(), BugType); |
118 | EXPECT_EQ(Diag.getCategory(), Category); |
119 | |
120 | EXPECT_EQ(Path.size(), Diag.path.size()); |
121 | for (const auto &PieceItem : llvm::enumerate(First: Diag.path)) { |
122 | if (PieceItem.index() < Path.size()) |
123 | Path[PieceItem.index()].testEquality(Piece: *PieceItem.value(), SM); |
124 | } |
125 | } |
126 | }; |
127 | |
128 | using ExpectedDiagsTy = std::vector<ExpectedDiagTy>; |
129 | |
130 | // A consumer to verify the generated diagnostics. |
131 | class VerifyPathDiagnosticConsumer : public PathDiagnosticConsumer { |
132 | ExpectedDiagsTy ExpectedDiags; |
133 | SourceManager &SM; |
134 | |
135 | public: |
136 | VerifyPathDiagnosticConsumer(ExpectedDiagsTy &&ExpectedDiags, |
137 | SourceManager &SM) |
138 | : ExpectedDiags(ExpectedDiags), SM(SM) {} |
139 | |
140 | StringRef getName() const override { return "verify test diagnostics" ; } |
141 | |
142 | void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags, |
143 | FilesMade *filesMade) override { |
144 | EXPECT_EQ(Diags.size(), ExpectedDiags.size()); |
145 | for (const auto &Item : llvm::enumerate(First&: Diags)) |
146 | if (Item.index() < ExpectedDiags.size()) |
147 | ExpectedDiags[Item.index()].testEquality(Diag: *Item.value(), SM); |
148 | } |
149 | }; |
150 | |
151 | } // namespace ento |
152 | } // namespace clang |
153 | |
154 | #endif |
155 | |