xref: /llvm-project/clang/unittests/Analysis/FlowSensitive/TestingSupport.cpp (revision 93fbaa46c82abd2d54bce9a7c3b39b01c30220d6)
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/CFG.h"
8 #include "clang/Analysis/FlowSensitive/DataflowAnalysis.h"
9 #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
10 #include "clang/Basic/LLVM.h"
11 #include "clang/Basic/LangOptions.h"
12 #include "clang/Basic/SourceManager.h"
13 #include "clang/Basic/TokenKinds.h"
14 #include "clang/Lex/Lexer.h"
15 #include "clang/Serialization/PCHContainerOperations.h"
16 #include "clang/Tooling/ArgumentsAdjusters.h"
17 #include "clang/Tooling/Tooling.h"
18 #include "llvm/ADT/ArrayRef.h"
19 #include "llvm/ADT/DenseMap.h"
20 #include "llvm/ADT/Optional.h"
21 #include "llvm/Support/Error.h"
22 #include "llvm/Testing/Support/Annotations.h"
23 #include "gtest/gtest.h"
24 #include <functional>
25 #include <memory>
26 #include <string>
27 #include <system_error>
28 #include <utility>
29 #include <vector>
30 
31 using namespace clang;
32 using namespace dataflow;
33 
34 namespace {
35 using ast_matchers::MatchFinder;
36 
37 class FindTranslationUnitCallback : public MatchFinder::MatchCallback {
38 public:
39   explicit FindTranslationUnitCallback(
40       std::function<void(ASTContext &)> Operation)
41       : Operation{Operation} {}
42 
43   void run(const MatchFinder::MatchResult &Result) override {
44     const auto *TU = Result.Nodes.getNodeAs<TranslationUnitDecl>("tu");
45     if (TU->getASTContext().getDiagnostics().getClient()->getNumErrors() != 0) {
46       FAIL() << "Source file has syntax or type errors, they were printed to "
47                 "the test log";
48     }
49     Operation(TU->getASTContext());
50   }
51 
52   std::function<void(ASTContext &)> Operation;
53 };
54 } // namespace
55 
56 static bool
57 isAnnotationDirectlyAfterStatement(const Stmt *Stmt, unsigned AnnotationBegin,
58                                    const SourceManager &SourceManager,
59                                    const LangOptions &LangOptions) {
60   auto NextToken =
61       Lexer::findNextToken(Stmt->getEndLoc(), SourceManager, LangOptions);
62 
63   while (NextToken.hasValue() &&
64          SourceManager.getFileOffset(NextToken->getLocation()) <
65              AnnotationBegin) {
66     if (NextToken->isNot(tok::semi))
67       return false;
68 
69     NextToken = Lexer::findNextToken(NextToken->getEndLoc(), SourceManager,
70                                      LangOptions);
71   }
72 
73   return true;
74 }
75 
76 llvm::Expected<llvm::DenseMap<const Stmt *, std::string>>
77 test::buildStatementToAnnotationMapping(const FunctionDecl *Func,
78                                         llvm::Annotations AnnotatedCode) {
79   llvm::DenseMap<const Stmt *, std::string> Result;
80 
81   using namespace ast_matchers; // NOLINT: Too many names
82   auto StmtMatcher =
83       findAll(stmt(unless(anyOf(hasParent(expr()), hasParent(returnStmt()))))
84                   .bind("stmt"));
85 
86   // This map should stay sorted because the binding algorithm relies on the
87   // ordering of statement offsets
88   std::map<unsigned, const Stmt *> Stmts;
89   auto &Context = Func->getASTContext();
90   auto &SourceManager = Context.getSourceManager();
91 
92   for (auto &Match : match(StmtMatcher, *Func->getBody(), Context)) {
93     const auto *S = Match.getNodeAs<Stmt>("stmt");
94     unsigned Offset = SourceManager.getFileOffset(S->getEndLoc());
95     Stmts[Offset] = S;
96   }
97 
98   unsigned I = 0;
99   auto Annotations = AnnotatedCode.ranges();
100   std::reverse(Annotations.begin(), Annotations.end());
101   auto Code = AnnotatedCode.code();
102 
103   for (auto OffsetAndStmt = Stmts.rbegin(); OffsetAndStmt != Stmts.rend();
104        OffsetAndStmt++) {
105     unsigned Offset = OffsetAndStmt->first;
106     const Stmt *Stmt = OffsetAndStmt->second;
107 
108     if (I < Annotations.size() && Annotations[I].Begin >= Offset) {
109       auto Range = Annotations[I];
110 
111       if (!isAnnotationDirectlyAfterStatement(Stmt, Range.Begin, SourceManager,
112                                               Context.getLangOpts())) {
113         return llvm::createStringError(
114             std::make_error_code(std::errc::invalid_argument),
115             "Annotation is not placed after a statement: %s",
116             SourceManager.getLocForStartOfFile(SourceManager.getMainFileID())
117                 .getLocWithOffset(Offset)
118                 .printToString(SourceManager)
119                 .data());
120       }
121 
122       Result[Stmt] = Code.slice(Range.Begin, Range.End).str();
123       I++;
124 
125       if (I < Annotations.size() && Annotations[I].Begin >= Offset) {
126         return llvm::createStringError(
127             std::make_error_code(std::errc::invalid_argument),
128             "Multiple annotations bound to the statement at the location: %s",
129             Stmt->getBeginLoc().printToString(SourceManager).data());
130       }
131     }
132   }
133 
134   if (I < Annotations.size()) {
135     return llvm::createStringError(
136         std::make_error_code(std::errc::invalid_argument),
137         "Not all annotations were bound to statements. Unbound annotation at: "
138         "%s",
139         SourceManager.getLocForStartOfFile(SourceManager.getMainFileID())
140             .getLocWithOffset(Annotations[I].Begin)
141             .printToString(SourceManager)
142             .data());
143   }
144 
145   return Result;
146 }
147 
148 std::pair<const FunctionDecl *, std::unique_ptr<CFG>>
149 test::buildCFG(ASTContext &Context,
150                ast_matchers::internal::Matcher<FunctionDecl> FuncMatcher) {
151   CFG::BuildOptions Options;
152   Options.PruneTriviallyFalseEdges = false;
153   Options.AddInitializers = true;
154   Options.AddImplicitDtors = true;
155   Options.AddTemporaryDtors = true;
156   Options.setAllAlwaysAdd();
157 
158   const FunctionDecl *F = ast_matchers::selectFirst<FunctionDecl>(
159       "target",
160       ast_matchers::match(
161           ast_matchers::functionDecl(ast_matchers::isDefinition(), FuncMatcher)
162               .bind("target"),
163           Context));
164   if (F == nullptr)
165     return std::make_pair(nullptr, nullptr);
166 
167   return std::make_pair(
168       F, clang::CFG::buildCFG(F, F->getBody(), &Context, Options));
169 }
170