Run DCE after a LoopFlatten test to reduce spurious output [nfc]
[llvm-project.git] / clang / unittests / Analysis / FlowSensitive / TestingSupport.cpp
blobe24ff25cb8292fb65e606f6e3625b11dc1566ce6
1 #include "TestingSupport.h"
2 #include "clang/AST/ASTContext.h"
3 #include "clang/AST/Decl.h"
4 #include "clang/AST/Stmt.h"
5 #include "clang/ASTMatchers/ASTMatchFinder.h"
6 #include "clang/ASTMatchers/ASTMatchers.h"
7 #include "clang/Basic/LLVM.h"
8 #include "clang/Basic/LangOptions.h"
9 #include "clang/Basic/SourceLocation.h"
10 #include "clang/Basic/SourceManager.h"
11 #include "clang/Basic/TokenKinds.h"
12 #include "clang/Lex/Lexer.h"
13 #include "llvm/ADT/DenseMap.h"
14 #include "llvm/ADT/StringRef.h"
15 #include "llvm/ADT/StringSet.h"
16 #include "llvm/Support/Error.h"
17 #include "llvm/Testing/Annotations/Annotations.h"
18 #include "gtest/gtest.h"
19 #include <cassert>
20 #include <functional>
21 #include <memory>
22 #include <string>
23 #include <system_error>
24 #include <utility>
25 #include <vector>
27 using namespace clang;
28 using namespace dataflow;
29 using namespace ast_matchers;
31 static bool
32 isAnnotationDirectlyAfterStatement(const Stmt *Stmt, unsigned AnnotationBegin,
33 const SourceManager &SourceManager,
34 const LangOptions &LangOptions) {
35 auto NextToken =
36 Lexer::findNextToken(Stmt->getEndLoc(), SourceManager, LangOptions);
38 while (NextToken && SourceManager.getFileOffset(NextToken->getLocation()) <
39 AnnotationBegin) {
40 if (NextToken->isNot(tok::semi))
41 return false;
43 NextToken = Lexer::findNextToken(NextToken->getEndLoc(), SourceManager,
44 LangOptions);
47 return true;
50 llvm::DenseMap<unsigned, std::string> test::buildLineToAnnotationMapping(
51 const SourceManager &SM, const LangOptions &LangOpts,
52 SourceRange BoundingRange, llvm::Annotations AnnotatedCode) {
53 CharSourceRange CharBoundingRange =
54 Lexer::getAsCharRange(BoundingRange, SM, LangOpts);
56 llvm::DenseMap<unsigned, std::string> LineNumberToContent;
57 auto Code = AnnotatedCode.code();
58 auto Annotations = AnnotatedCode.ranges();
59 for (auto &AnnotationRange : Annotations) {
60 SourceLocation Loc = SM.getLocForStartOfFile(SM.getMainFileID())
61 .getLocWithOffset(AnnotationRange.Begin);
62 if (SM.isPointWithin(Loc, CharBoundingRange.getBegin(),
63 CharBoundingRange.getEnd())) {
64 LineNumberToContent[SM.getPresumedLineNumber(Loc)] =
65 Code.slice(AnnotationRange.Begin, AnnotationRange.End).str();
68 return LineNumberToContent;
71 llvm::Expected<llvm::DenseMap<const Stmt *, std::string>>
72 test::buildStatementToAnnotationMapping(const FunctionDecl *Func,
73 llvm::Annotations AnnotatedCode) {
74 llvm::DenseMap<const Stmt *, std::string> Result;
75 llvm::StringSet<> ExistingAnnotations;
77 auto StmtMatcher =
78 findAll(stmt(unless(anyOf(hasParent(expr()), hasParent(returnStmt()))))
79 .bind("stmt"));
81 // This map should stay sorted because the binding algorithm relies on the
82 // ordering of statement offsets
83 std::map<unsigned, const Stmt *> Stmts;
84 auto &Context = Func->getASTContext();
85 auto &SourceManager = Context.getSourceManager();
87 for (auto &Match : match(StmtMatcher, *Func->getBody(), Context)) {
88 const auto *S = Match.getNodeAs<Stmt>("stmt");
89 unsigned Offset = SourceManager.getFileOffset(S->getEndLoc());
90 Stmts[Offset] = S;
93 unsigned FunctionBeginOffset =
94 SourceManager.getFileOffset(Func->getBeginLoc());
95 unsigned FunctionEndOffset = SourceManager.getFileOffset(Func->getEndLoc());
97 std::vector<llvm::Annotations::Range> Annotations = AnnotatedCode.ranges();
98 llvm::erase_if(Annotations, [=](llvm::Annotations::Range R) {
99 return R.Begin < FunctionBeginOffset || R.End >= FunctionEndOffset;
101 std::reverse(Annotations.begin(), Annotations.end());
102 auto Code = AnnotatedCode.code();
104 unsigned I = 0;
105 for (auto OffsetAndStmt = Stmts.rbegin(); OffsetAndStmt != Stmts.rend();
106 OffsetAndStmt++) {
107 unsigned Offset = OffsetAndStmt->first;
108 const Stmt *Stmt = OffsetAndStmt->second;
110 if (I < Annotations.size() && Annotations[I].Begin >= Offset) {
111 auto Range = Annotations[I];
113 if (!isAnnotationDirectlyAfterStatement(Stmt, Range.Begin, SourceManager,
114 Context.getLangOpts())) {
115 return llvm::createStringError(
116 std::make_error_code(std::errc::invalid_argument),
117 "Annotation is not placed after a statement: %s",
118 SourceManager.getLocForStartOfFile(SourceManager.getMainFileID())
119 .getLocWithOffset(Offset)
120 .printToString(SourceManager)
121 .data());
124 auto Annotation = Code.slice(Range.Begin, Range.End).str();
125 if (!ExistingAnnotations.insert(Annotation).second) {
126 return llvm::createStringError(
127 std::make_error_code(std::errc::invalid_argument),
128 "Repeated use of annotation: %s", Annotation.data());
130 Result[Stmt] = std::move(Annotation);
132 I++;
134 if (I < Annotations.size() && Annotations[I].Begin >= Offset) {
135 return llvm::createStringError(
136 std::make_error_code(std::errc::invalid_argument),
137 "Multiple annotations bound to the statement at the location: %s",
138 Stmt->getBeginLoc().printToString(SourceManager).data());
143 if (I < Annotations.size()) {
144 return llvm::createStringError(
145 std::make_error_code(std::errc::invalid_argument),
146 "Not all annotations were bound to statements. Unbound annotation at: "
147 "%s",
148 SourceManager.getLocForStartOfFile(SourceManager.getMainFileID())
149 .getLocWithOffset(Annotations[I].Begin)
150 .printToString(SourceManager)
151 .data());
154 return Result;
157 llvm::Error test::checkDataflowWithNoopAnalysis(
158 llvm::StringRef Code,
159 std::function<
160 void(const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,
161 ASTContext &)>
162 VerifyResults,
163 DataflowAnalysisOptions Options, LangStandard::Kind Std,
164 llvm::StringRef TargetFun) {
165 return checkDataflowWithNoopAnalysis(Code, ast_matchers::hasName(TargetFun),
166 VerifyResults, Options, Std);
169 llvm::Error test::checkDataflowWithNoopAnalysis(
170 llvm::StringRef Code,
171 ast_matchers::internal::Matcher<FunctionDecl> TargetFuncMatcher,
172 std::function<
173 void(const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,
174 ASTContext &)>
175 VerifyResults,
176 DataflowAnalysisOptions Options, LangStandard::Kind Std) {
177 llvm::SmallVector<std::string, 3> ASTBuildArgs = {
178 // -fnodelayed-template-parsing is the default everywhere but on Windows.
179 // Set it explicitly so that tests behave the same on Windows as on other
180 // platforms.
181 "-fsyntax-only", "-fno-delayed-template-parsing",
182 "-std=" +
183 std::string(LangStandard::getLangStandardForKind(Std).getName())};
184 AnalysisInputs<NoopAnalysis> AI(
185 Code, TargetFuncMatcher,
186 [UseBuiltinModel = Options.BuiltinOpts.has_value()](ASTContext &C,
187 Environment &Env) {
188 return NoopAnalysis(
190 DataflowAnalysisOptions{
191 UseBuiltinModel ? Env.getDataflowAnalysisContext().getOptions()
192 : std::optional<BuiltinOptions>()});
194 AI.ASTBuildArgs = ASTBuildArgs;
195 if (Options.BuiltinOpts)
196 AI.BuiltinOptions = *Options.BuiltinOpts;
197 return checkDataflow<NoopAnalysis>(
198 std::move(AI),
199 /*VerifyResults=*/
200 [&VerifyResults](
201 const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
202 const AnalysisOutputs &AO) { VerifyResults(Results, AO.ASTCtx); });
205 const ValueDecl *test::findValueDecl(ASTContext &ASTCtx, llvm::StringRef Name) {
206 auto TargetNodes = match(
207 valueDecl(unless(indirectFieldDecl()), hasName(Name)).bind("v"), ASTCtx);
208 assert(TargetNodes.size() == 1 && "Name must be unique");
209 auto *const Result = selectFirst<ValueDecl>("v", TargetNodes);
210 assert(Result != nullptr);
211 return Result;
214 const IndirectFieldDecl *test::findIndirectFieldDecl(ASTContext &ASTCtx,
215 llvm::StringRef Name) {
216 auto TargetNodes = match(indirectFieldDecl(hasName(Name)).bind("i"), ASTCtx);
217 assert(TargetNodes.size() == 1 && "Name must be unique");
218 const auto *Result = selectFirst<IndirectFieldDecl>("i", TargetNodes);
219 assert(Result != nullptr);
220 return Result;
223 std::vector<const Formula *> test::parseFormulas(Arena &A, StringRef Lines) {
224 std::vector<const Formula *> Result;
225 while (!Lines.empty()) {
226 auto [First, Rest] = Lines.split('\n');
227 Lines = Rest;
228 if (First.trim().empty())
229 continue;
230 if (auto F = A.parseFormula(First))
231 Result.push_back(&*F);
232 else
233 ADD_FAILURE() << llvm::toString(F.takeError());
235 return Result;