xref: /llvm-project/clang/unittests/Analysis/FlowSensitive/TestingSupport.cpp (revision c8b31da1ef0a3f2a0ba5c39bb4281b1438e511fb)
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 <cassert>
19 #include <functional>
20 #include <memory>
21 #include <string>
22 #include <system_error>
23 #include <utility>
24 #include <vector>
25 
26 using namespace clang;
27 using namespace dataflow;
28 using namespace ast_matchers;
29 
30 static bool
31 isAnnotationDirectlyAfterStatement(const Stmt *Stmt, unsigned AnnotationBegin,
32                                    const SourceManager &SourceManager,
33                                    const LangOptions &LangOptions) {
34   auto NextToken =
35       Lexer::findNextToken(Stmt->getEndLoc(), SourceManager, LangOptions);
36 
37   while (NextToken && SourceManager.getFileOffset(NextToken->getLocation()) <
38                           AnnotationBegin) {
39     if (NextToken->isNot(tok::semi))
40       return false;
41 
42     NextToken = Lexer::findNextToken(NextToken->getEndLoc(), SourceManager,
43                                      LangOptions);
44   }
45 
46   return true;
47 }
48 
49 llvm::DenseMap<unsigned, std::string> test::buildLineToAnnotationMapping(
50     const SourceManager &SM, const LangOptions &LangOpts,
51     SourceRange BoundingRange, llvm::Annotations AnnotatedCode) {
52   CharSourceRange CharBoundingRange =
53       Lexer::getAsCharRange(BoundingRange, SM, LangOpts);
54 
55   llvm::DenseMap<unsigned, std::string> LineNumberToContent;
56   auto Code = AnnotatedCode.code();
57   auto Annotations = AnnotatedCode.ranges();
58   for (auto &AnnotationRange : Annotations) {
59     SourceLocation Loc = SM.getLocForStartOfFile(SM.getMainFileID())
60                              .getLocWithOffset(AnnotationRange.Begin);
61     if (SM.isPointWithin(Loc, CharBoundingRange.getBegin(),
62                          CharBoundingRange.getEnd())) {
63       LineNumberToContent[SM.getPresumedLineNumber(Loc)] =
64           Code.slice(AnnotationRange.Begin, AnnotationRange.End).str();
65     }
66   }
67   return LineNumberToContent;
68 }
69 
70 llvm::Expected<llvm::DenseMap<const Stmt *, std::string>>
71 test::buildStatementToAnnotationMapping(const FunctionDecl *Func,
72                                         llvm::Annotations AnnotatedCode) {
73   llvm::DenseMap<const Stmt *, std::string> Result;
74   llvm::StringSet<> ExistingAnnotations;
75 
76   auto StmtMatcher =
77       findAll(stmt(unless(anyOf(hasParent(expr()), hasParent(returnStmt()))))
78                   .bind("stmt"));
79 
80   // This map should stay sorted because the binding algorithm relies on the
81   // ordering of statement offsets
82   std::map<unsigned, const Stmt *> Stmts;
83   auto &Context = Func->getASTContext();
84   auto &SourceManager = Context.getSourceManager();
85 
86   for (auto &Match : match(StmtMatcher, *Func->getBody(), Context)) {
87     const auto *S = Match.getNodeAs<Stmt>("stmt");
88     unsigned Offset = SourceManager.getFileOffset(S->getEndLoc());
89     Stmts[Offset] = S;
90   }
91 
92   unsigned FunctionBeginOffset =
93       SourceManager.getFileOffset(Func->getBeginLoc());
94   unsigned FunctionEndOffset = SourceManager.getFileOffset(Func->getEndLoc());
95 
96   std::vector<llvm::Annotations::Range> Annotations = AnnotatedCode.ranges();
97   llvm::erase_if(Annotations, [=](llvm::Annotations::Range R) {
98     return R.Begin < FunctionBeginOffset || R.End >= FunctionEndOffset;
99   });
100   std::reverse(Annotations.begin(), Annotations.end());
101   auto Code = AnnotatedCode.code();
102 
103   unsigned I = 0;
104   for (auto OffsetAndStmt = Stmts.rbegin(); OffsetAndStmt != Stmts.rend();
105        OffsetAndStmt++) {
106     unsigned Offset = OffsetAndStmt->first;
107     const Stmt *Stmt = OffsetAndStmt->second;
108 
109     if (I < Annotations.size() && Annotations[I].Begin >= Offset) {
110       auto Range = Annotations[I];
111 
112       if (!isAnnotationDirectlyAfterStatement(Stmt, Range.Begin, SourceManager,
113                                               Context.getLangOpts())) {
114         return llvm::createStringError(
115             std::make_error_code(std::errc::invalid_argument),
116             "Annotation is not placed after a statement: %s",
117             SourceManager.getLocForStartOfFile(SourceManager.getMainFileID())
118                 .getLocWithOffset(Offset)
119                 .printToString(SourceManager)
120                 .data());
121       }
122 
123       auto Annotation = Code.slice(Range.Begin, Range.End).str();
124       if (!ExistingAnnotations.insert(Annotation).second) {
125         return llvm::createStringError(
126             std::make_error_code(std::errc::invalid_argument),
127             "Repeated use of annotation: %s", Annotation.data());
128       }
129       Result[Stmt] = std::move(Annotation);
130 
131       I++;
132 
133       if (I < Annotations.size() && Annotations[I].Begin >= Offset) {
134         return llvm::createStringError(
135             std::make_error_code(std::errc::invalid_argument),
136             "Multiple annotations bound to the statement at the location: %s",
137             Stmt->getBeginLoc().printToString(SourceManager).data());
138       }
139     }
140   }
141 
142   if (I < Annotations.size()) {
143     return llvm::createStringError(
144         std::make_error_code(std::errc::invalid_argument),
145         "Not all annotations were bound to statements. Unbound annotation at: "
146         "%s",
147         SourceManager.getLocForStartOfFile(SourceManager.getMainFileID())
148             .getLocWithOffset(Annotations[I].Begin)
149             .printToString(SourceManager)
150             .data());
151   }
152 
153   return Result;
154 }
155 
156 const ValueDecl *test::findValueDecl(ASTContext &ASTCtx, llvm::StringRef Name) {
157   auto TargetNodes = match(valueDecl(hasName(Name)).bind("v"), ASTCtx);
158   assert(TargetNodes.size() == 1 && "Name must be unique");
159   auto *const Result = selectFirst<ValueDecl>("v", TargetNodes);
160   assert(Result != nullptr);
161   return Result;
162 }
163