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), Consumers(), |
63 | AMgr(C.getASTContext(), C.getPreprocessor(), Consumers, |
64 | CreateRegionStoreManager, CreateRangeConstraintManager, &ChkMgr, |
65 | C.getAnalyzerOpts()), |
66 | VisitedCallees(), FS(), |
67 | Eng(CTU, AMgr, &VisitedCallees, &FS, ExprEngine::Inline_Regular) {} |
68 | }; |
69 | |
70 | struct ExpectedLocationTy { |
71 | unsigned Line; |
72 | unsigned Column; |
73 | |
74 | void testEquality(SourceLocation L, SourceManager &SM) const { |
75 | EXPECT_EQ(SM.getSpellingLineNumber(L), Line); |
76 | EXPECT_EQ(SM.getSpellingColumnNumber(L), Column); |
77 | } |
78 | }; |
79 | |
80 | struct ExpectedRangeTy { |
81 | ExpectedLocationTy Begin; |
82 | ExpectedLocationTy End; |
83 | |
84 | void testEquality(SourceRange R, SourceManager &SM) const { |
85 | Begin.testEquality(L: R.getBegin(), SM); |
86 | End.testEquality(L: R.getEnd(), SM); |
87 | } |
88 | }; |
89 | |
90 | struct ExpectedPieceTy { |
91 | ExpectedLocationTy Loc; |
92 | std::string Text; |
93 | std::vector<ExpectedRangeTy> Ranges; |
94 | |
95 | void testEquality(const PathDiagnosticPiece &Piece, SourceManager &SM) { |
96 | Loc.testEquality(L: Piece.getLocation().asLocation(), SM); |
97 | EXPECT_EQ(Piece.getString(), Text); |
98 | EXPECT_EQ(Ranges.size(), Piece.getRanges().size()); |
99 | for (const auto &RangeItem : llvm::enumerate(First: Piece.getRanges())) |
100 | Ranges[RangeItem.index()].testEquality(R: RangeItem.value(), SM); |
101 | } |
102 | }; |
103 | |
104 | struct ExpectedDiagTy { |
105 | ExpectedLocationTy Loc; |
106 | std::string VerboseDescription; |
107 | std::string ShortDescription; |
108 | std::string CheckerName; |
109 | std::string BugType; |
110 | std::string Category; |
111 | std::vector<ExpectedPieceTy> Path; |
112 | |
113 | void testEquality(const PathDiagnostic &Diag, SourceManager &SM) { |
114 | Loc.testEquality(L: Diag.getLocation().asLocation(), SM); |
115 | EXPECT_EQ(Diag.getVerboseDescription(), VerboseDescription); |
116 | EXPECT_EQ(Diag.getShortDescription(), ShortDescription); |
117 | EXPECT_EQ(Diag.getCheckerName(), CheckerName); |
118 | EXPECT_EQ(Diag.getBugType(), BugType); |
119 | EXPECT_EQ(Diag.getCategory(), Category); |
120 | |
121 | EXPECT_EQ(Path.size(), Diag.path.size()); |
122 | for (const auto &PieceItem : llvm::enumerate(First: Diag.path)) { |
123 | if (PieceItem.index() < Path.size()) |
124 | Path[PieceItem.index()].testEquality(Piece: *PieceItem.value(), SM); |
125 | } |
126 | } |
127 | }; |
128 | |
129 | using ExpectedDiagsTy = std::vector<ExpectedDiagTy>; |
130 | |
131 | // A consumer to verify the generated diagnostics. |
132 | class VerifyPathDiagnosticConsumer : public PathDiagnosticConsumer { |
133 | ExpectedDiagsTy ExpectedDiags; |
134 | SourceManager &SM; |
135 | |
136 | public: |
137 | VerifyPathDiagnosticConsumer(ExpectedDiagsTy &&ExpectedDiags, |
138 | SourceManager &SM) |
139 | : ExpectedDiags(ExpectedDiags), SM(SM) {} |
140 | |
141 | StringRef getName() const override { return "verify test diagnostics" ; } |
142 | |
143 | void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags, |
144 | FilesMade *filesMade) override { |
145 | EXPECT_EQ(Diags.size(), ExpectedDiags.size()); |
146 | for (const auto &Item : llvm::enumerate(First&: Diags)) |
147 | if (Item.index() < ExpectedDiags.size()) |
148 | ExpectedDiags[Item.index()].testEquality(Diag: *Item.value(), SM); |
149 | } |
150 | }; |
151 | |
152 | } // namespace ento |
153 | } // namespace clang |
154 | |
155 | #endif |
156 | |