xref: /llvm-project/clang/unittests/Analysis/FlowSensitive/TestingSupport.cpp (revision 655c2233b6f81cd981d87e461ea202c563e90490)
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/Analysis/FlowSensitive/NoopAnalysis.h"
8 #include "clang/Basic/LLVM.h"
9 #include "clang/Basic/LangOptions.h"
10 #include "clang/Basic/SourceLocation.h"
11 #include "clang/Basic/SourceManager.h"
12 #include "clang/Basic/TokenKinds.h"
13 #include "clang/Lex/Lexer.h"
14 #include "llvm/ADT/DenseMap.h"
15 #include "llvm/ADT/StringRef.h"
16 #include "llvm/ADT/StringSet.h"
17 #include "llvm/Support/Error.h"
18 #include "llvm/Testing/Annotations/Annotations.h"
19 #include "gtest/gtest.h"
20 #include <cassert>
21 #include <functional>
22 #include <string>
23 #include <system_error>
24 #include <utility>
25 #include <vector>
26 
27 using namespace clang;
28 using namespace dataflow;
29 using namespace ast_matchers;
30 
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);
37 
38   while (NextToken && SourceManager.getFileOffset(NextToken->getLocation()) <
39                           AnnotationBegin) {
40     if (NextToken->isNot(tok::semi))
41       return false;
42 
43     NextToken = Lexer::findNextToken(NextToken->getEndLoc(), SourceManager,
44                                      LangOptions);
45   }
46 
47   return true;
48 }
49 
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);
55 
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();
66     }
67   }
68   return LineNumberToContent;
69 }
70 
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;
76 
77   auto StmtMatcher =
78       findAll(stmt(unless(anyOf(hasParent(expr()), hasParent(returnStmt()))))
79                   .bind("stmt"));
80 
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();
86 
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;
91   }
92 
93   unsigned FunctionBeginOffset =
94       SourceManager.getFileOffset(Func->getBeginLoc());
95   unsigned FunctionEndOffset = SourceManager.getFileOffset(Func->getEndLoc());
96 
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;
100   });
101   std::reverse(Annotations.begin(), Annotations.end());
102   auto Code = AnnotatedCode.code();
103 
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;
109 
110     if (I < Annotations.size() && Annotations[I].Begin >= Offset) {
111       auto Range = Annotations[I];
112 
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());
122       }
123 
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());
129       }
130       Result[Stmt] = std::move(Annotation);
131 
132       I++;
133 
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());
139       }
140     }
141   }
142 
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());
152   }
153 
154   return Result;
155 }
156 
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);
167 }
168 
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     std::function<llvm::StringMap<QualType>(QualType)> SyntheticFieldCallback) {
178   llvm::SmallVector<std::string, 3> ASTBuildArgs = {
179       "-fsyntax-only",
180       // -fnodelayed-template-parsing is the default everywhere but on Windows.
181       // Set it explicitly so that tests behave the same on Windows as on other
182       // platforms.
183       "-fno-delayed-template-parsing",
184       // Set -Wno-unused-value because it's often desirable in tests to write
185       // expressions with unused value, and we don't want the output to be
186       // cluttered with warnings about them.
187       "-Wno-unused-value",
188       // Some build environments don't have RTTI enabled by default.
189       // Enable it explicitly to make sure tests work in all environments.
190       "-frtti",
191       "-std=" +
192           std::string(LangStandard::getLangStandardForKind(Std).getName())};
193   AnalysisInputs<NoopAnalysis> AI(
194       Code, TargetFuncMatcher,
195       [UseBuiltinModel = Options.BuiltinOpts.has_value(),
196        &SyntheticFieldCallback](ASTContext &C, Environment &Env) {
197         Env.getDataflowAnalysisContext().setSyntheticFieldCallback(
198             std::move(SyntheticFieldCallback));
199         return NoopAnalysis(
200             C,
201             DataflowAnalysisOptions{
202                 UseBuiltinModel ? Env.getDataflowAnalysisContext().getOptions()
203                                 : std::optional<BuiltinOptions>()});
204       });
205   AI.ASTBuildArgs = ASTBuildArgs;
206   if (Options.BuiltinOpts)
207     AI.BuiltinOptions = *Options.BuiltinOpts;
208   return checkDataflow<NoopAnalysis>(
209       std::move(AI),
210       /*VerifyResults=*/
211       [&VerifyResults](
212           const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
213           const AnalysisOutputs &AO) { VerifyResults(Results, AO.ASTCtx); });
214 }
215 
216 const ValueDecl *test::findValueDecl(ASTContext &ASTCtx, llvm::StringRef Name) {
217   auto TargetNodes = match(
218       valueDecl(unless(indirectFieldDecl()), hasName(Name)).bind("v"), ASTCtx);
219   assert(TargetNodes.size() == 1 && "Name must be unique");
220   auto *const Result = selectFirst<ValueDecl>("v", TargetNodes);
221   assert(Result != nullptr);
222   return Result;
223 }
224 
225 const IndirectFieldDecl *test::findIndirectFieldDecl(ASTContext &ASTCtx,
226                                                      llvm::StringRef Name) {
227   auto TargetNodes = match(indirectFieldDecl(hasName(Name)).bind("i"), ASTCtx);
228   assert(TargetNodes.size() == 1 && "Name must be unique");
229   const auto *Result = selectFirst<IndirectFieldDecl>("i", TargetNodes);
230   assert(Result != nullptr);
231   return Result;
232 }
233 
234 std::vector<const Formula *> test::parseFormulas(Arena &A, StringRef Lines) {
235   std::vector<const Formula *> Result;
236   while (!Lines.empty()) {
237     auto [First, Rest] = Lines.split('\n');
238     Lines = Rest;
239     if (First.trim().empty())
240       continue;
241     if (auto F = A.parseFormula(First))
242       Result.push_back(&*F);
243     else
244       ADD_FAILURE() << llvm::toString(F.takeError());
245   }
246   return Result;
247 }
248