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