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 <functional>
24 #include <memory>
25 #include <string>
26 #include <system_error>
27 #include <utility>
28 #include <vector>
29 
30 using namespace clang;
31 using namespace dataflow;
32 
33 static bool
34 isAnnotationDirectlyAfterStatement(const Stmt *Stmt, unsigned AnnotationBegin,
35                                    const SourceManager &SourceManager,
36                                    const LangOptions &LangOptions) {
37   auto NextToken =
38       Lexer::findNextToken(Stmt->getEndLoc(), SourceManager, LangOptions);
39 
40   while (NextToken.hasValue() &&
41          SourceManager.getFileOffset(NextToken->getLocation()) <
42              AnnotationBegin) {
43     if (NextToken->isNot(tok::semi))
44       return false;
45 
46     NextToken = Lexer::findNextToken(NextToken->getEndLoc(), SourceManager,
47                                      LangOptions);
48   }
49 
50   return true;
51 }
52 
53 llvm::Expected<llvm::DenseMap<const Stmt *, std::string>>
54 test::buildStatementToAnnotationMapping(const FunctionDecl *Func,
55                                         llvm::Annotations AnnotatedCode) {
56   llvm::DenseMap<const Stmt *, std::string> Result;
57 
58   using namespace ast_matchers; // NOLINT: Too many names
59   auto StmtMatcher =
60       findAll(stmt(unless(anyOf(hasParent(expr()), hasParent(returnStmt()))))
61                   .bind("stmt"));
62 
63   // This map should stay sorted because the binding algorithm relies on the
64   // ordering of statement offsets
65   std::map<unsigned, const Stmt *> Stmts;
66   auto &Context = Func->getASTContext();
67   auto &SourceManager = Context.getSourceManager();
68 
69   for (auto &Match : match(StmtMatcher, *Func->getBody(), Context)) {
70     const auto *S = Match.getNodeAs<Stmt>("stmt");
71     unsigned Offset = SourceManager.getFileOffset(S->getEndLoc());
72     Stmts[Offset] = S;
73   }
74 
75   unsigned I = 0;
76   auto Annotations = AnnotatedCode.ranges();
77   std::reverse(Annotations.begin(), Annotations.end());
78   auto Code = AnnotatedCode.code();
79 
80   for (auto OffsetAndStmt = Stmts.rbegin(); OffsetAndStmt != Stmts.rend();
81        OffsetAndStmt++) {
82     unsigned Offset = OffsetAndStmt->first;
83     const Stmt *Stmt = OffsetAndStmt->second;
84 
85     if (I < Annotations.size() && Annotations[I].Begin >= Offset) {
86       auto Range = Annotations[I];
87 
88       if (!isAnnotationDirectlyAfterStatement(Stmt, Range.Begin, SourceManager,
89                                               Context.getLangOpts())) {
90         return llvm::createStringError(
91             std::make_error_code(std::errc::invalid_argument),
92             "Annotation is not placed after a statement: %s",
93             SourceManager.getLocForStartOfFile(SourceManager.getMainFileID())
94                 .getLocWithOffset(Offset)
95                 .printToString(SourceManager)
96                 .data());
97       }
98 
99       Result[Stmt] = Code.slice(Range.Begin, Range.End).str();
100       I++;
101 
102       if (I < Annotations.size() && Annotations[I].Begin >= Offset) {
103         return llvm::createStringError(
104             std::make_error_code(std::errc::invalid_argument),
105             "Multiple annotations bound to the statement at the location: %s",
106             Stmt->getBeginLoc().printToString(SourceManager).data());
107       }
108     }
109   }
110 
111   if (I < Annotations.size()) {
112     return llvm::createStringError(
113         std::make_error_code(std::errc::invalid_argument),
114         "Not all annotations were bound to statements. Unbound annotation at: "
115         "%s",
116         SourceManager.getLocForStartOfFile(SourceManager.getMainFileID())
117             .getLocWithOffset(Annotations[I].Begin)
118             .printToString(SourceManager)
119             .data());
120   }
121 
122   return Result;
123 }
124