brintos

brintos / llvm-project-archived public Read only

0
0
Text · 9.3 KiB · 622de63 Raw
248 lines · cpp
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 bool32isAnnotationDirectlyAfterStatement(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 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(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 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(stmt(unless(anyOf(hasParent(expr()), hasParent(returnStmt()))))79                  .bind("stmt"));80 81  // This map should stay sorted because the binding algorithm relies on the82  // ordering of statement offsets83  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 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, ast_matchers::hasName(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 other182      // platforms.183      "-fno-delayed-template-parsing",184      // Set -Wno-unused-value because it's often desirable in tests to write185      // expressions with unused value, and we don't want the output to be186      // 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(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                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      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      valueDecl(unless(indirectFieldDecl()), hasName(Name)).bind("v"), ASTCtx);219  assert(TargetNodes.size() == 1 && "Name must be unique");220  auto *const Result = selectFirst<ValueDecl>("v", TargetNodes);221  assert(Result != nullptr);222  return Result;223}224 225const IndirectFieldDecl *test::findIndirectFieldDecl(ASTContext &ASTCtx,226                                                     llvm::StringRef Name) {227  auto TargetNodes = match(indirectFieldDecl(hasName(Name)).bind("i"), ASTCtx);228  assert(TargetNodes.size() == 1 && "Name must be unique");229  const auto *Result = selectFirst<IndirectFieldDecl>("i", 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('\n');238    Lines = Rest;239    if (First.trim().empty())240      continue;241    if (auto F = A.parseFormula(First))242      Result.push_back(&*F);243    else244      ADD_FAILURE() << llvm::toString(F.takeError());245  }246  return Result;247}248