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