llvm-project

Форк
0
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

27
using namespace clang;
28
using namespace dataflow;
29
using namespace ast_matchers;
30

31
static bool
32
isAnnotationDirectlyAfterStatement(const Stmt *Stmt, unsigned AnnotationBegin,
33
                                   const SourceManager &SourceManager,
34
                                   const LangOptions &LangOptions) {
35
  auto NextToken =
36
      Lexer::findNextToken(Stmt->getEndLoc(), SourceManager, LangOptions);
37

38
  while (NextToken && SourceManager.getFileOffset(NextToken->getLocation()) <
39
                          AnnotationBegin) {
40
    if (NextToken->isNot(tok::semi))
41
      return false;
42

43
    NextToken = Lexer::findNextToken(NextToken->getEndLoc(), SourceManager,
44
                                     LangOptions);
45
  }
46

47
  return true;
48
}
49

50
llvm::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(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(SM.getMainFileID())
61
                             .getLocWithOffset(AnnotationRange.Begin);
62
    if (SM.isPointWithin(Loc, CharBoundingRange.getBegin(),
63
                         CharBoundingRange.getEnd())) {
64
      LineNumberToContent[SM.getPresumedLineNumber(Loc)] =
65
          Code.slice(AnnotationRange.Begin, AnnotationRange.End).str();
66
    }
67
  }
68
  return LineNumberToContent;
69
}
70

71
llvm::Expected<llvm::DenseMap<const Stmt *, std::string>>
72
test::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(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
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(Annotations, [=](llvm::Annotations::Range R) {
99
    return R.Begin < FunctionBeginOffset || R.End >= FunctionEndOffset;
100
  });
101
  std::reverse(Annotations.begin(), 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(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(Range.Begin, Range.End).str();
125
      if (!ExistingAnnotations.insert(Annotation).second) {
126
        return llvm::createStringError(
127
            std::make_error_code(std::errc::invalid_argument),
128
            "Repeated use of annotation: %s", 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(std::errc::invalid_argument),
137
            "Multiple annotations bound to the statement at the location: %s",
138
            Stmt->getBeginLoc().printToString(SourceManager).data());
139
      }
140
    }
141
  }
142

143
  if (I < Annotations.size()) {
144
    return llvm::createStringError(
145
        std::make_error_code(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

157
llvm::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, ast_matchers::hasName(TargetFun),
166
                                       VerifyResults, Options, Std);
167
}
168

169
llvm::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
      // -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=" +
187
          std::string(LangStandard::getLangStandardForKind(Std).getName())};
188
  AnalysisInputs<NoopAnalysis> AI(
189
      Code, TargetFuncMatcher,
190
      [UseBuiltinModel = Options.BuiltinOpts.has_value(),
191
       &SyntheticFieldCallback](ASTContext &C, Environment &Env) {
192
        Env.getDataflowAnalysisContext().setSyntheticFieldCallback(
193
            std::move(SyntheticFieldCallback));
194
        return NoopAnalysis(
195
            C,
196
            DataflowAnalysisOptions{
197
                UseBuiltinModel ? Env.getDataflowAnalysisContext().getOptions()
198
                                : std::optional<BuiltinOptions>()});
199
      });
200
  AI.ASTBuildArgs = ASTBuildArgs;
201
  if (Options.BuiltinOpts)
202
    AI.BuiltinOptions = *Options.BuiltinOpts;
203
  return checkDataflow<NoopAnalysis>(
204
      std::move(AI),
205
      /*VerifyResults=*/
206
      [&VerifyResults](
207
          const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
208
          const AnalysisOutputs &AO) { VerifyResults(Results, AO.ASTCtx); });
209
}
210

211
const ValueDecl *test::findValueDecl(ASTContext &ASTCtx, llvm::StringRef Name) {
212
  auto TargetNodes = match(
213
      valueDecl(unless(indirectFieldDecl()), hasName(Name)).bind("v"), ASTCtx);
214
  assert(TargetNodes.size() == 1 && "Name must be unique");
215
  auto *const Result = selectFirst<ValueDecl>("v", TargetNodes);
216
  assert(Result != nullptr);
217
  return Result;
218
}
219

220
const IndirectFieldDecl *test::findIndirectFieldDecl(ASTContext &ASTCtx,
221
                                                     llvm::StringRef Name) {
222
  auto TargetNodes = match(indirectFieldDecl(hasName(Name)).bind("i"), ASTCtx);
223
  assert(TargetNodes.size() == 1 && "Name must be unique");
224
  const auto *Result = selectFirst<IndirectFieldDecl>("i", TargetNodes);
225
  assert(Result != nullptr);
226
  return Result;
227
}
228

229
std::vector<const Formula *> test::parseFormulas(Arena &A, StringRef Lines) {
230
  std::vector<const Formula *> Result;
231
  while (!Lines.empty()) {
232
    auto [First, Rest] = Lines.split('\n');
233
    Lines = Rest;
234
    if (First.trim().empty())
235
      continue;
236
    if (auto F = A.parseFormula(First))
237
      Result.push_back(&*F);
238
    else
239
      ADD_FAILURE() << llvm::toString(F.takeError());
240
  }
241
  return Result;
242
}
243

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.