xref: /llvm-project/clang/unittests/Analysis/FlowSensitive/TestingSupport.cpp (revision 655c2233b6f81cd981d87e461ea202c563e90490)
13dd7877bSStanislav Gatev #include "TestingSupport.h"
23dd7877bSStanislav Gatev #include "clang/AST/ASTContext.h"
33dd7877bSStanislav Gatev #include "clang/AST/Decl.h"
43dd7877bSStanislav Gatev #include "clang/AST/Stmt.h"
53dd7877bSStanislav Gatev #include "clang/ASTMatchers/ASTMatchFinder.h"
63dd7877bSStanislav Gatev #include "clang/ASTMatchers/ASTMatchers.h"
7f3dd8f10SYitzhak Mandelbaum #include "clang/Analysis/FlowSensitive/NoopAnalysis.h"
83dd7877bSStanislav Gatev #include "clang/Basic/LLVM.h"
93dd7877bSStanislav Gatev #include "clang/Basic/LangOptions.h"
10c8b31da1SDmitri Gribenko #include "clang/Basic/SourceLocation.h"
113dd7877bSStanislav Gatev #include "clang/Basic/SourceManager.h"
123dd7877bSStanislav Gatev #include "clang/Basic/TokenKinds.h"
133dd7877bSStanislav Gatev #include "clang/Lex/Lexer.h"
143dd7877bSStanislav Gatev #include "llvm/ADT/DenseMap.h"
153dd7877bSStanislav Gatev #include "llvm/ADT/StringRef.h"
168dd14c42SWei Yi Tee #include "llvm/ADT/StringSet.h"
173dd7877bSStanislav Gatev #include "llvm/Support/Error.h"
183432f4bfSJordan Rupprecht #include "llvm/Testing/Annotations/Annotations.h"
19bbd61d80Smartinboehme #include "gtest/gtest.h"
203dd7877bSStanislav Gatev #include <cassert>
213dd7877bSStanislav Gatev #include <functional>
223dd7877bSStanislav Gatev #include <string>
233dd7877bSStanislav Gatev #include <system_error>
243dd7877bSStanislav Gatev #include <utility>
253dd7877bSStanislav Gatev #include <vector>
263dd7877bSStanislav Gatev 
273dd7877bSStanislav Gatev using namespace clang;
283dd7877bSStanislav Gatev using namespace dataflow;
293dd7877bSStanislav Gatev using namespace ast_matchers;
303dd7877bSStanislav Gatev 
313dd7877bSStanislav Gatev static bool
323dd7877bSStanislav Gatev isAnnotationDirectlyAfterStatement(const Stmt *Stmt, unsigned AnnotationBegin,
333dd7877bSStanislav Gatev                                    const SourceManager &SourceManager,
343dd7877bSStanislav Gatev                                    const LangOptions &LangOptions) {
353dd7877bSStanislav Gatev   auto NextToken =
363dd7877bSStanislav Gatev       Lexer::findNextToken(Stmt->getEndLoc(), SourceManager, LangOptions);
373dd7877bSStanislav Gatev 
38ca05cc20SKazu Hirata   while (NextToken && SourceManager.getFileOffset(NextToken->getLocation()) <
393dd7877bSStanislav Gatev                           AnnotationBegin) {
403dd7877bSStanislav Gatev     if (NextToken->isNot(tok::semi))
413dd7877bSStanislav Gatev       return false;
423dd7877bSStanislav Gatev 
433dd7877bSStanislav Gatev     NextToken = Lexer::findNextToken(NextToken->getEndLoc(), SourceManager,
443dd7877bSStanislav Gatev                                      LangOptions);
453dd7877bSStanislav Gatev   }
463dd7877bSStanislav Gatev 
473dd7877bSStanislav Gatev   return true;
483dd7877bSStanislav Gatev }
493dd7877bSStanislav Gatev 
50c8b31da1SDmitri Gribenko llvm::DenseMap<unsigned, std::string> test::buildLineToAnnotationMapping(
51c8b31da1SDmitri Gribenko     const SourceManager &SM, const LangOptions &LangOpts,
52c8b31da1SDmitri Gribenko     SourceRange BoundingRange, llvm::Annotations AnnotatedCode) {
53c8b31da1SDmitri Gribenko   CharSourceRange CharBoundingRange =
54c8b31da1SDmitri Gribenko       Lexer::getAsCharRange(BoundingRange, SM, LangOpts);
55c8b31da1SDmitri Gribenko 
56db898d43SWei Yi Tee   llvm::DenseMap<unsigned, std::string> LineNumberToContent;
575a4aece7SWei Yi Tee   auto Code = AnnotatedCode.code();
585a4aece7SWei Yi Tee   auto Annotations = AnnotatedCode.ranges();
59db898d43SWei Yi Tee   for (auto &AnnotationRange : Annotations) {
60c8b31da1SDmitri Gribenko     SourceLocation Loc = SM.getLocForStartOfFile(SM.getMainFileID())
61c8b31da1SDmitri Gribenko                              .getLocWithOffset(AnnotationRange.Begin);
62c8b31da1SDmitri Gribenko     if (SM.isPointWithin(Loc, CharBoundingRange.getBegin(),
63c8b31da1SDmitri Gribenko                          CharBoundingRange.getEnd())) {
64c8b31da1SDmitri Gribenko       LineNumberToContent[SM.getPresumedLineNumber(Loc)] =
65c8b31da1SDmitri Gribenko           Code.slice(AnnotationRange.Begin, AnnotationRange.End).str();
66c8b31da1SDmitri Gribenko     }
67db898d43SWei Yi Tee   }
68db898d43SWei Yi Tee   return LineNumberToContent;
69db898d43SWei Yi Tee }
70db898d43SWei Yi Tee 
713dd7877bSStanislav Gatev llvm::Expected<llvm::DenseMap<const Stmt *, std::string>>
723dd7877bSStanislav Gatev test::buildStatementToAnnotationMapping(const FunctionDecl *Func,
733dd7877bSStanislav Gatev                                         llvm::Annotations AnnotatedCode) {
743dd7877bSStanislav Gatev   llvm::DenseMap<const Stmt *, std::string> Result;
758dd14c42SWei Yi Tee   llvm::StringSet<> ExistingAnnotations;
763dd7877bSStanislav Gatev 
773dd7877bSStanislav Gatev   auto StmtMatcher =
783dd7877bSStanislav Gatev       findAll(stmt(unless(anyOf(hasParent(expr()), hasParent(returnStmt()))))
793dd7877bSStanislav Gatev                   .bind("stmt"));
803dd7877bSStanislav Gatev 
813dd7877bSStanislav Gatev   // This map should stay sorted because the binding algorithm relies on the
823dd7877bSStanislav Gatev   // ordering of statement offsets
833dd7877bSStanislav Gatev   std::map<unsigned, const Stmt *> Stmts;
843dd7877bSStanislav Gatev   auto &Context = Func->getASTContext();
853dd7877bSStanislav Gatev   auto &SourceManager = Context.getSourceManager();
863dd7877bSStanislav Gatev 
873dd7877bSStanislav Gatev   for (auto &Match : match(StmtMatcher, *Func->getBody(), Context)) {
883dd7877bSStanislav Gatev     const auto *S = Match.getNodeAs<Stmt>("stmt");
893dd7877bSStanislav Gatev     unsigned Offset = SourceManager.getFileOffset(S->getEndLoc());
903dd7877bSStanislav Gatev     Stmts[Offset] = S;
913dd7877bSStanislav Gatev   }
923dd7877bSStanislav Gatev 
93c8b31da1SDmitri Gribenko   unsigned FunctionBeginOffset =
94c8b31da1SDmitri Gribenko       SourceManager.getFileOffset(Func->getBeginLoc());
95c8b31da1SDmitri Gribenko   unsigned FunctionEndOffset = SourceManager.getFileOffset(Func->getEndLoc());
96c8b31da1SDmitri Gribenko 
97c8b31da1SDmitri Gribenko   std::vector<llvm::Annotations::Range> Annotations = AnnotatedCode.ranges();
98c8b31da1SDmitri Gribenko   llvm::erase_if(Annotations, [=](llvm::Annotations::Range R) {
99c8b31da1SDmitri Gribenko     return R.Begin < FunctionBeginOffset || R.End >= FunctionEndOffset;
100c8b31da1SDmitri Gribenko   });
1013dd7877bSStanislav Gatev   std::reverse(Annotations.begin(), Annotations.end());
1023dd7877bSStanislav Gatev   auto Code = AnnotatedCode.code();
1033dd7877bSStanislav Gatev 
104c8b31da1SDmitri Gribenko   unsigned I = 0;
1053dd7877bSStanislav Gatev   for (auto OffsetAndStmt = Stmts.rbegin(); OffsetAndStmt != Stmts.rend();
1063dd7877bSStanislav Gatev        OffsetAndStmt++) {
1073dd7877bSStanislav Gatev     unsigned Offset = OffsetAndStmt->first;
1083dd7877bSStanislav Gatev     const Stmt *Stmt = OffsetAndStmt->second;
1093dd7877bSStanislav Gatev 
1103dd7877bSStanislav Gatev     if (I < Annotations.size() && Annotations[I].Begin >= Offset) {
1113dd7877bSStanislav Gatev       auto Range = Annotations[I];
1123dd7877bSStanislav Gatev 
1133dd7877bSStanislav Gatev       if (!isAnnotationDirectlyAfterStatement(Stmt, Range.Begin, SourceManager,
1143dd7877bSStanislav Gatev                                               Context.getLangOpts())) {
1153dd7877bSStanislav Gatev         return llvm::createStringError(
1163dd7877bSStanislav Gatev             std::make_error_code(std::errc::invalid_argument),
1173dd7877bSStanislav Gatev             "Annotation is not placed after a statement: %s",
1183dd7877bSStanislav Gatev             SourceManager.getLocForStartOfFile(SourceManager.getMainFileID())
1193dd7877bSStanislav Gatev                 .getLocWithOffset(Offset)
1203dd7877bSStanislav Gatev                 .printToString(SourceManager)
1213dd7877bSStanislav Gatev                 .data());
1223dd7877bSStanislav Gatev       }
1233dd7877bSStanislav Gatev 
1248dd14c42SWei Yi Tee       auto Annotation = Code.slice(Range.Begin, Range.End).str();
1258dd14c42SWei Yi Tee       if (!ExistingAnnotations.insert(Annotation).second) {
1268dd14c42SWei Yi Tee         return llvm::createStringError(
1278dd14c42SWei Yi Tee             std::make_error_code(std::errc::invalid_argument),
1288dd14c42SWei Yi Tee             "Repeated use of annotation: %s", Annotation.data());
1298dd14c42SWei Yi Tee       }
1308dd14c42SWei Yi Tee       Result[Stmt] = std::move(Annotation);
1318dd14c42SWei Yi Tee 
1323dd7877bSStanislav Gatev       I++;
1333dd7877bSStanislav Gatev 
1343dd7877bSStanislav Gatev       if (I < Annotations.size() && Annotations[I].Begin >= Offset) {
1353dd7877bSStanislav Gatev         return llvm::createStringError(
1363dd7877bSStanislav Gatev             std::make_error_code(std::errc::invalid_argument),
1373dd7877bSStanislav Gatev             "Multiple annotations bound to the statement at the location: %s",
1383dd7877bSStanislav Gatev             Stmt->getBeginLoc().printToString(SourceManager).data());
1393dd7877bSStanislav Gatev       }
1403dd7877bSStanislav Gatev     }
1413dd7877bSStanislav Gatev   }
1423dd7877bSStanislav Gatev 
1433dd7877bSStanislav Gatev   if (I < Annotations.size()) {
1443dd7877bSStanislav Gatev     return llvm::createStringError(
1453dd7877bSStanislav Gatev         std::make_error_code(std::errc::invalid_argument),
1463dd7877bSStanislav Gatev         "Not all annotations were bound to statements. Unbound annotation at: "
1473dd7877bSStanislav Gatev         "%s",
1483dd7877bSStanislav Gatev         SourceManager.getLocForStartOfFile(SourceManager.getMainFileID())
1493dd7877bSStanislav Gatev             .getLocWithOffset(Annotations[I].Begin)
1503dd7877bSStanislav Gatev             .printToString(SourceManager)
1513dd7877bSStanislav Gatev             .data());
1523dd7877bSStanislav Gatev   }
1533dd7877bSStanislav Gatev 
1543dd7877bSStanislav Gatev   return Result;
1553dd7877bSStanislav Gatev }
1563dd7877bSStanislav Gatev 
157d0be47c5SMartin Braenne llvm::Error test::checkDataflowWithNoopAnalysis(
158d0be47c5SMartin Braenne     llvm::StringRef Code,
159d0be47c5SMartin Braenne     std::function<
160d0be47c5SMartin Braenne         void(const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,
161d0be47c5SMartin Braenne              ASTContext &)>
162d0be47c5SMartin Braenne         VerifyResults,
163d0be47c5SMartin Braenne     DataflowAnalysisOptions Options, LangStandard::Kind Std,
164d0be47c5SMartin Braenne     llvm::StringRef TargetFun) {
16552d06963SStanislav Gatev   return checkDataflowWithNoopAnalysis(Code, ast_matchers::hasName(TargetFun),
16652d06963SStanislav Gatev                                        VerifyResults, Options, Std);
16752d06963SStanislav Gatev }
16852d06963SStanislav Gatev 
16952d06963SStanislav Gatev llvm::Error test::checkDataflowWithNoopAnalysis(
17052d06963SStanislav Gatev     llvm::StringRef Code,
17152d06963SStanislav Gatev     ast_matchers::internal::Matcher<FunctionDecl> TargetFuncMatcher,
17252d06963SStanislav Gatev     std::function<
17352d06963SStanislav Gatev         void(const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,
17452d06963SStanislav Gatev              ASTContext &)>
17552d06963SStanislav Gatev         VerifyResults,
17671f2ec2dSmartinboehme     DataflowAnalysisOptions Options, LangStandard::Kind Std,
17771f2ec2dSmartinboehme     std::function<llvm::StringMap<QualType>(QualType)> SyntheticFieldCallback) {
178d0be47c5SMartin Braenne   llvm::SmallVector<std::string, 3> ASTBuildArgs = {
179*655c2233Smartinboehme       "-fsyntax-only",
180d0be47c5SMartin Braenne       // -fnodelayed-template-parsing is the default everywhere but on Windows.
181d0be47c5SMartin Braenne       // Set it explicitly so that tests behave the same on Windows as on other
182d0be47c5SMartin Braenne       // platforms.
183*655c2233Smartinboehme       "-fno-delayed-template-parsing",
1842d539db2Smartinboehme       // Set -Wno-unused-value because it's often desirable in tests to write
1852d539db2Smartinboehme       // expressions with unused value, and we don't want the output to be
1862d539db2Smartinboehme       // cluttered with warnings about them.
187*655c2233Smartinboehme       "-Wno-unused-value",
188*655c2233Smartinboehme       // Some build environments don't have RTTI enabled by default.
189*655c2233Smartinboehme       // Enable it explicitly to make sure tests work in all environments.
190*655c2233Smartinboehme       "-frtti",
191d0be47c5SMartin Braenne       "-std=" +
192d0be47c5SMartin Braenne           std::string(LangStandard::getLangStandardForKind(Std).getName())};
193d0be47c5SMartin Braenne   AnalysisInputs<NoopAnalysis> AI(
19452d06963SStanislav Gatev       Code, TargetFuncMatcher,
19571f2ec2dSmartinboehme       [UseBuiltinModel = Options.BuiltinOpts.has_value(),
19671f2ec2dSmartinboehme        &SyntheticFieldCallback](ASTContext &C, Environment &Env) {
19771f2ec2dSmartinboehme         Env.getDataflowAnalysisContext().setSyntheticFieldCallback(
19871f2ec2dSmartinboehme             std::move(SyntheticFieldCallback));
199d0be47c5SMartin Braenne         return NoopAnalysis(
200d0be47c5SMartin Braenne             C,
201d0be47c5SMartin Braenne             DataflowAnalysisOptions{
202d0be47c5SMartin Braenne                 UseBuiltinModel ? Env.getDataflowAnalysisContext().getOptions()
203d0be47c5SMartin Braenne                                 : std::optional<BuiltinOptions>()});
204d0be47c5SMartin Braenne       });
205d0be47c5SMartin Braenne   AI.ASTBuildArgs = ASTBuildArgs;
206d0be47c5SMartin Braenne   if (Options.BuiltinOpts)
207d0be47c5SMartin Braenne     AI.BuiltinOptions = *Options.BuiltinOpts;
208d0be47c5SMartin Braenne   return checkDataflow<NoopAnalysis>(
209d0be47c5SMartin Braenne       std::move(AI),
210d0be47c5SMartin Braenne       /*VerifyResults=*/
211d0be47c5SMartin Braenne       [&VerifyResults](
212d0be47c5SMartin Braenne           const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
213d0be47c5SMartin Braenne           const AnalysisOutputs &AO) { VerifyResults(Results, AO.ASTCtx); });
214d0be47c5SMartin Braenne }
215d0be47c5SMartin Braenne 
2163dd7877bSStanislav Gatev const ValueDecl *test::findValueDecl(ASTContext &ASTCtx, llvm::StringRef Name) {
217d04b1989SMartin Braenne   auto TargetNodes = match(
218d04b1989SMartin Braenne       valueDecl(unless(indirectFieldDecl()), hasName(Name)).bind("v"), ASTCtx);
2193dd7877bSStanislav Gatev   assert(TargetNodes.size() == 1 && "Name must be unique");
2203dd7877bSStanislav Gatev   auto *const Result = selectFirst<ValueDecl>("v", TargetNodes);
2213dd7877bSStanislav Gatev   assert(Result != nullptr);
2223dd7877bSStanislav Gatev   return Result;
2233dd7877bSStanislav Gatev }
224d04b1989SMartin Braenne 
225d04b1989SMartin Braenne const IndirectFieldDecl *test::findIndirectFieldDecl(ASTContext &ASTCtx,
226d04b1989SMartin Braenne                                                      llvm::StringRef Name) {
227d04b1989SMartin Braenne   auto TargetNodes = match(indirectFieldDecl(hasName(Name)).bind("i"), ASTCtx);
228d04b1989SMartin Braenne   assert(TargetNodes.size() == 1 && "Name must be unique");
229d04b1989SMartin Braenne   const auto *Result = selectFirst<IndirectFieldDecl>("i", TargetNodes);
230d04b1989SMartin Braenne   assert(Result != nullptr);
231d04b1989SMartin Braenne   return Result;
232d04b1989SMartin Braenne }
233bbd61d80Smartinboehme 
234bbd61d80Smartinboehme std::vector<const Formula *> test::parseFormulas(Arena &A, StringRef Lines) {
235bbd61d80Smartinboehme   std::vector<const Formula *> Result;
236bbd61d80Smartinboehme   while (!Lines.empty()) {
237bbd61d80Smartinboehme     auto [First, Rest] = Lines.split('\n');
238bbd61d80Smartinboehme     Lines = Rest;
239bbd61d80Smartinboehme     if (First.trim().empty())
240bbd61d80Smartinboehme       continue;
241bbd61d80Smartinboehme     if (auto F = A.parseFormula(First))
242bbd61d80Smartinboehme       Result.push_back(&*F);
243bbd61d80Smartinboehme     else
244bbd61d80Smartinboehme       ADD_FAILURE() << llvm::toString(F.takeError());
245bbd61d80Smartinboehme   }
246bbd61d80Smartinboehme   return Result;
247bbd61d80Smartinboehme }
248