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/FlowSensitive/NoopAnalysis.h"
8#include "clang/Basic/LLVM.h"
9#include "clang/Basic/LangOptions.h"
10#include "clang/Basic/SourceLocation.h"
11#include "clang/Basic/SourceManager.h"
12#include "clang/Basic/TokenKinds.h"
13#include "clang/Lex/Lexer.h"
14#include "llvm/ADT/DenseMap.h"
15#include "llvm/ADT/StringRef.h"
16#include "llvm/ADT/StringSet.h"
17#include "llvm/Support/Error.h"
18#include "llvm/Testing/Annotations/Annotations.h"
19#include "gtest/gtest.h"
20#include <cassert>
21#include <functional>
22#include <string>
23#include <system_error>
24#include <utility>
25#include <vector>
26
27using namespace clang;
28using namespace dataflow;
29using namespace ast_matchers;
30
31static bool
32isAnnotationDirectlyAfterStatement(const Stmt *Stmt, unsigned AnnotationBegin,
33 const SourceManager &SourceManager,
34 const LangOptions &LangOptions) {
35 auto NextToken =
36 Lexer::findNextToken(Loc: Stmt->getEndLoc(), SM: SourceManager, LangOpts: LangOptions);
37
38 while (NextToken && SourceManager.getFileOffset(SpellingLoc: NextToken->getLocation()) <
39 AnnotationBegin) {
40 if (NextToken->isNot(K: tok::semi))
41 return false;
42
43 NextToken = Lexer::findNextToken(Loc: NextToken->getEndLoc(), SM: SourceManager,
44 LangOpts: LangOptions);
45 }
46
47 return true;
48}
49
50llvm::DenseMap<unsigned, std::string> test::buildLineToAnnotationMapping(
51 const SourceManager &SM, const LangOptions &LangOpts,
52 SourceRange BoundingRange, llvm::Annotations AnnotatedCode) {
53 CharSourceRange CharBoundingRange =
54 Lexer::getAsCharRange(Range: BoundingRange, SM, LangOpts);
55
56 llvm::DenseMap<unsigned, std::string> LineNumberToContent;
57 auto Code = AnnotatedCode.code();
58 auto Annotations = AnnotatedCode.ranges();
59 for (auto &AnnotationRange : Annotations) {
60 SourceLocation Loc = SM.getLocForStartOfFile(FID: SM.getMainFileID())
61 .getLocWithOffset(Offset: AnnotationRange.Begin);
62 if (SM.isPointWithin(Location: Loc, Start: CharBoundingRange.getBegin(),
63 End: CharBoundingRange.getEnd())) {
64 LineNumberToContent[SM.getPresumedLineNumber(Loc)] =
65 Code.slice(Start: AnnotationRange.Begin, End: AnnotationRange.End).str();
66 }
67 }
68 return LineNumberToContent;
69}
70
71llvm::Expected<llvm::DenseMap<const Stmt *, std::string>>
72test::buildStatementToAnnotationMapping(const FunctionDecl *Func,
73 llvm::Annotations AnnotatedCode) {
74 llvm::DenseMap<const Stmt *, std::string> Result;
75 llvm::StringSet<> ExistingAnnotations;
76
77 auto StmtMatcher =
78 findAll(Matcher: stmt(unless(anyOf(hasParent(expr()), hasParent(returnStmt()))))
79 .bind(ID: "stmt"));
80
81 // This map should stay sorted because the binding algorithm relies on the
82 // ordering of statement offsets
83 std::map<unsigned, const Stmt *> Stmts;
84 auto &Context = Func->getASTContext();
85 auto &SourceManager = Context.getSourceManager();
86
87 for (auto &Match : match(StmtMatcher, *Func->getBody(), Context)) {
88 const auto *S = Match.getNodeAs<Stmt>("stmt");
89 unsigned Offset = SourceManager.getFileOffset(S->getEndLoc());
90 Stmts[Offset] = S;
91 }
92
93 unsigned FunctionBeginOffset =
94 SourceManager.getFileOffset(Func->getBeginLoc());
95 unsigned FunctionEndOffset = SourceManager.getFileOffset(Func->getEndLoc());
96
97 std::vector<llvm::Annotations::Range> Annotations = AnnotatedCode.ranges();
98 llvm::erase_if(C&: Annotations, P: [=](llvm::Annotations::Range R) {
99 return R.Begin < FunctionBeginOffset || R.End >= FunctionEndOffset;
100 });
101 std::reverse(first: Annotations.begin(), last: Annotations.end());
102 auto Code = AnnotatedCode.code();
103
104 unsigned I = 0;
105 for (auto OffsetAndStmt = Stmts.rbegin(); OffsetAndStmt != Stmts.rend();
106 OffsetAndStmt++) {
107 unsigned Offset = OffsetAndStmt->first;
108 const Stmt *Stmt = OffsetAndStmt->second;
109
110 if (I < Annotations.size() && Annotations[I].Begin >= Offset) {
111 auto Range = Annotations[I];
112
113 if (!isAnnotationDirectlyAfterStatement(Stmt, Range.Begin, SourceManager,
114 Context.getLangOpts())) {
115 return llvm::createStringError(
116 std::make_error_code(e: std::errc::invalid_argument),
117 "Annotation is not placed after a statement: %s",
118 SourceManager.getLocForStartOfFile(SourceManager.getMainFileID())
119 .getLocWithOffset(Offset)
120 .printToString(SourceManager)
121 .data());
122 }
123
124 auto Annotation = Code.slice(Start: Range.Begin, End: Range.End).str();
125 if (!ExistingAnnotations.insert(key: Annotation).second) {
126 return llvm::createStringError(
127 EC: std::make_error_code(e: std::errc::invalid_argument),
128 Fmt: "Repeated use of annotation: %s", Vals: Annotation.data());
129 }
130 Result[Stmt] = std::move(Annotation);
131
132 I++;
133
134 if (I < Annotations.size() && Annotations[I].Begin >= Offset) {
135 return llvm::createStringError(
136 std::make_error_code(e: std::errc::invalid_argument),
137 "Multiple annotations bound to the statement at the location: %s",
138 Stmt->getBeginLoc().printToString(SM: SourceManager).data());
139 }
140 }
141 }
142
143 if (I < Annotations.size()) {
144 return llvm::createStringError(
145 std::make_error_code(e: std::errc::invalid_argument),
146 "Not all annotations were bound to statements. Unbound annotation at: "
147 "%s",
148 SourceManager.getLocForStartOfFile(SourceManager.getMainFileID())
149 .getLocWithOffset(Annotations[I].Begin)
150 .printToString(SourceManager)
151 .data());
152 }
153
154 return Result;
155}
156
157llvm::Error test::checkDataflowWithNoopAnalysis(
158 llvm::StringRef Code,
159 std::function<
160 void(const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,
161 ASTContext &)>
162 VerifyResults,
163 DataflowAnalysisOptions Options, LangStandard::Kind Std,
164 llvm::StringRef TargetFun) {
165 return checkDataflowWithNoopAnalysis(Code, TargetFuncMatcher: ast_matchers::hasName(Name: TargetFun),
166 VerifyResults, Options, Std);
167}
168
169llvm::Error test::checkDataflowWithNoopAnalysis(
170 llvm::StringRef Code,
171 ast_matchers::internal::Matcher<FunctionDecl> TargetFuncMatcher,
172 std::function<
173 void(const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,
174 ASTContext &)>
175 VerifyResults,
176 DataflowAnalysisOptions Options, LangStandard::Kind Std,
177 std::function<llvm::StringMap<QualType>(QualType)> SyntheticFieldCallback) {
178 llvm::SmallVector<std::string, 3> ASTBuildArgs = {
179 "-fsyntax-only",
180 // -fnodelayed-template-parsing is the default everywhere but on Windows.
181 // Set it explicitly so that tests behave the same on Windows as on other
182 // platforms.
183 "-fno-delayed-template-parsing",
184 // Set -Wno-unused-value because it's often desirable in tests to write
185 // expressions with unused value, and we don't want the output to be
186 // cluttered with warnings about them.
187 "-Wno-unused-value",
188 // Some build environments don't have RTTI enabled by default.
189 // Enable it explicitly to make sure tests work in all environments.
190 "-frtti",
191 "-std=" +
192 std::string(LangStandard::getLangStandardForKind(K: Std).getName())};
193 AnalysisInputs<NoopAnalysis> AI(
194 Code, TargetFuncMatcher,
195 [UseBuiltinModel = Options.BuiltinOpts.has_value(),
196 &SyntheticFieldCallback](ASTContext &C, Environment &Env) {
197 Env.getDataflowAnalysisContext().setSyntheticFieldCallback(
198 std::move(SyntheticFieldCallback));
199 return NoopAnalysis(
200 C,
201 DataflowAnalysisOptions{
202 .BuiltinOpts: UseBuiltinModel ? Env.getDataflowAnalysisContext().getOptions()
203 : std::optional<BuiltinOptions>()});
204 });
205 AI.ASTBuildArgs = ASTBuildArgs;
206 if (Options.BuiltinOpts)
207 AI.BuiltinOptions = *Options.BuiltinOpts;
208 return checkDataflow<NoopAnalysis>(
209 AI: std::move(AI),
210 /*VerifyResults=*/
211 [&VerifyResults](
212 const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
213 const AnalysisOutputs &AO) { VerifyResults(Results, AO.ASTCtx); });
214}
215
216const ValueDecl *test::findValueDecl(ASTContext &ASTCtx, llvm::StringRef Name) {
217 auto TargetNodes = match(
218 Matcher: valueDecl(unless(indirectFieldDecl()), hasName(Name)).bind(ID: "v"), Context&: ASTCtx);
219 assert(TargetNodes.size() == 1 && "Name must be unique");
220 auto *const Result = selectFirst<ValueDecl>(BoundTo: "v", Results: TargetNodes);
221 assert(Result != nullptr);
222 return Result;
223}
224
225const IndirectFieldDecl *test::findIndirectFieldDecl(ASTContext &ASTCtx,
226 llvm::StringRef Name) {
227 auto TargetNodes = match(Matcher: indirectFieldDecl(hasName(Name)).bind(ID: "i"), Context&: ASTCtx);
228 assert(TargetNodes.size() == 1 && "Name must be unique");
229 const auto *Result = selectFirst<IndirectFieldDecl>(BoundTo: "i", Results: TargetNodes);
230 assert(Result != nullptr);
231 return Result;
232}
233
234std::vector<const Formula *> test::parseFormulas(Arena &A, StringRef Lines) {
235 std::vector<const Formula *> Result;
236 while (!Lines.empty()) {
237 auto [First, Rest] = Lines.split(Separator: '\n');
238 Lines = Rest;
239 if (First.trim().empty())
240 continue;
241 if (auto F = A.parseFormula(First))
242 Result.push_back(x: &*F);
243 else
244 ADD_FAILURE() << llvm::toString(E: F.takeError());
245 }
246 return Result;
247}
248

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

source code of clang/unittests/Analysis/FlowSensitive/TestingSupport.cpp