13dd7877bSStanislav Gatev #include "TestingSupport.h" 23dd7877bSStanislav Gatev #include "clang/AST/ASTContext.h" 33dd7877bSStanislav Gatev #include "clang/AST/Decl.h" 43dd7877bSStanislav Gatev #include "clang/AST/Stmt.h" 53dd7877bSStanislav Gatev #include "clang/ASTMatchers/ASTMatchFinder.h" 63dd7877bSStanislav Gatev #include "clang/ASTMatchers/ASTMatchers.h" 7f3dd8f10SYitzhak Mandelbaum #include "clang/Analysis/FlowSensitive/NoopAnalysis.h" 83dd7877bSStanislav Gatev #include "clang/Basic/LLVM.h" 93dd7877bSStanislav Gatev #include "clang/Basic/LangOptions.h" 10c8b31da1SDmitri Gribenko #include "clang/Basic/SourceLocation.h" 113dd7877bSStanislav Gatev #include "clang/Basic/SourceManager.h" 123dd7877bSStanislav Gatev #include "clang/Basic/TokenKinds.h" 133dd7877bSStanislav Gatev #include "clang/Lex/Lexer.h" 143dd7877bSStanislav Gatev #include "llvm/ADT/DenseMap.h" 153dd7877bSStanislav Gatev #include "llvm/ADT/StringRef.h" 168dd14c42SWei Yi Tee #include "llvm/ADT/StringSet.h" 173dd7877bSStanislav Gatev #include "llvm/Support/Error.h" 183432f4bfSJordan Rupprecht #include "llvm/Testing/Annotations/Annotations.h" 19bbd61d80Smartinboehme #include "gtest/gtest.h" 203dd7877bSStanislav Gatev #include <cassert> 213dd7877bSStanislav Gatev #include <functional> 223dd7877bSStanislav Gatev #include <string> 233dd7877bSStanislav Gatev #include <system_error> 243dd7877bSStanislav Gatev #include <utility> 253dd7877bSStanislav Gatev #include <vector> 263dd7877bSStanislav Gatev 273dd7877bSStanislav Gatev using namespace clang; 283dd7877bSStanislav Gatev using namespace dataflow; 293dd7877bSStanislav Gatev using namespace ast_matchers; 303dd7877bSStanislav Gatev 313dd7877bSStanislav Gatev static bool 323dd7877bSStanislav Gatev isAnnotationDirectlyAfterStatement(const Stmt *Stmt, unsigned AnnotationBegin, 333dd7877bSStanislav Gatev const SourceManager &SourceManager, 343dd7877bSStanislav Gatev const LangOptions &LangOptions) { 353dd7877bSStanislav Gatev auto NextToken = 363dd7877bSStanislav Gatev Lexer::findNextToken(Stmt->getEndLoc(), SourceManager, LangOptions); 373dd7877bSStanislav Gatev 38ca05cc20SKazu Hirata while (NextToken && SourceManager.getFileOffset(NextToken->getLocation()) < 393dd7877bSStanislav Gatev AnnotationBegin) { 403dd7877bSStanislav Gatev if (NextToken->isNot(tok::semi)) 413dd7877bSStanislav Gatev return false; 423dd7877bSStanislav Gatev 433dd7877bSStanislav Gatev NextToken = Lexer::findNextToken(NextToken->getEndLoc(), SourceManager, 443dd7877bSStanislav Gatev LangOptions); 453dd7877bSStanislav Gatev } 463dd7877bSStanislav Gatev 473dd7877bSStanislav Gatev return true; 483dd7877bSStanislav Gatev } 493dd7877bSStanislav Gatev 50c8b31da1SDmitri Gribenko llvm::DenseMap<unsigned, std::string> test::buildLineToAnnotationMapping( 51c8b31da1SDmitri Gribenko const SourceManager &SM, const LangOptions &LangOpts, 52c8b31da1SDmitri Gribenko SourceRange BoundingRange, llvm::Annotations AnnotatedCode) { 53c8b31da1SDmitri Gribenko CharSourceRange CharBoundingRange = 54c8b31da1SDmitri Gribenko Lexer::getAsCharRange(BoundingRange, SM, LangOpts); 55c8b31da1SDmitri Gribenko 56db898d43SWei Yi Tee llvm::DenseMap<unsigned, std::string> LineNumberToContent; 575a4aece7SWei Yi Tee auto Code = AnnotatedCode.code(); 585a4aece7SWei Yi Tee auto Annotations = AnnotatedCode.ranges(); 59db898d43SWei Yi Tee for (auto &AnnotationRange : Annotations) { 60c8b31da1SDmitri Gribenko SourceLocation Loc = SM.getLocForStartOfFile(SM.getMainFileID()) 61c8b31da1SDmitri Gribenko .getLocWithOffset(AnnotationRange.Begin); 62c8b31da1SDmitri Gribenko if (SM.isPointWithin(Loc, CharBoundingRange.getBegin(), 63c8b31da1SDmitri Gribenko CharBoundingRange.getEnd())) { 64c8b31da1SDmitri Gribenko LineNumberToContent[SM.getPresumedLineNumber(Loc)] = 65c8b31da1SDmitri Gribenko Code.slice(AnnotationRange.Begin, AnnotationRange.End).str(); 66c8b31da1SDmitri Gribenko } 67db898d43SWei Yi Tee } 68db898d43SWei Yi Tee return LineNumberToContent; 69db898d43SWei Yi Tee } 70db898d43SWei Yi Tee 713dd7877bSStanislav Gatev llvm::Expected<llvm::DenseMap<const Stmt *, std::string>> 723dd7877bSStanislav Gatev test::buildStatementToAnnotationMapping(const FunctionDecl *Func, 733dd7877bSStanislav Gatev llvm::Annotations AnnotatedCode) { 743dd7877bSStanislav Gatev llvm::DenseMap<const Stmt *, std::string> Result; 758dd14c42SWei Yi Tee llvm::StringSet<> ExistingAnnotations; 763dd7877bSStanislav Gatev 773dd7877bSStanislav Gatev auto StmtMatcher = 783dd7877bSStanislav Gatev findAll(stmt(unless(anyOf(hasParent(expr()), hasParent(returnStmt())))) 793dd7877bSStanislav Gatev .bind("stmt")); 803dd7877bSStanislav Gatev 813dd7877bSStanislav Gatev // This map should stay sorted because the binding algorithm relies on the 823dd7877bSStanislav Gatev // ordering of statement offsets 833dd7877bSStanislav Gatev std::map<unsigned, const Stmt *> Stmts; 843dd7877bSStanislav Gatev auto &Context = Func->getASTContext(); 853dd7877bSStanislav Gatev auto &SourceManager = Context.getSourceManager(); 863dd7877bSStanislav Gatev 873dd7877bSStanislav Gatev for (auto &Match : match(StmtMatcher, *Func->getBody(), Context)) { 883dd7877bSStanislav Gatev const auto *S = Match.getNodeAs<Stmt>("stmt"); 893dd7877bSStanislav Gatev unsigned Offset = SourceManager.getFileOffset(S->getEndLoc()); 903dd7877bSStanislav Gatev Stmts[Offset] = S; 913dd7877bSStanislav Gatev } 923dd7877bSStanislav Gatev 93c8b31da1SDmitri Gribenko unsigned FunctionBeginOffset = 94c8b31da1SDmitri Gribenko SourceManager.getFileOffset(Func->getBeginLoc()); 95c8b31da1SDmitri Gribenko unsigned FunctionEndOffset = SourceManager.getFileOffset(Func->getEndLoc()); 96c8b31da1SDmitri Gribenko 97c8b31da1SDmitri Gribenko std::vector<llvm::Annotations::Range> Annotations = AnnotatedCode.ranges(); 98c8b31da1SDmitri Gribenko llvm::erase_if(Annotations, [=](llvm::Annotations::Range R) { 99c8b31da1SDmitri Gribenko return R.Begin < FunctionBeginOffset || R.End >= FunctionEndOffset; 100c8b31da1SDmitri Gribenko }); 1013dd7877bSStanislav Gatev std::reverse(Annotations.begin(), Annotations.end()); 1023dd7877bSStanislav Gatev auto Code = AnnotatedCode.code(); 1033dd7877bSStanislav Gatev 104c8b31da1SDmitri Gribenko unsigned I = 0; 1053dd7877bSStanislav Gatev for (auto OffsetAndStmt = Stmts.rbegin(); OffsetAndStmt != Stmts.rend(); 1063dd7877bSStanislav Gatev OffsetAndStmt++) { 1073dd7877bSStanislav Gatev unsigned Offset = OffsetAndStmt->first; 1083dd7877bSStanislav Gatev const Stmt *Stmt = OffsetAndStmt->second; 1093dd7877bSStanislav Gatev 1103dd7877bSStanislav Gatev if (I < Annotations.size() && Annotations[I].Begin >= Offset) { 1113dd7877bSStanislav Gatev auto Range = Annotations[I]; 1123dd7877bSStanislav Gatev 1133dd7877bSStanislav Gatev if (!isAnnotationDirectlyAfterStatement(Stmt, Range.Begin, SourceManager, 1143dd7877bSStanislav Gatev Context.getLangOpts())) { 1153dd7877bSStanislav Gatev return llvm::createStringError( 1163dd7877bSStanislav Gatev std::make_error_code(std::errc::invalid_argument), 1173dd7877bSStanislav Gatev "Annotation is not placed after a statement: %s", 1183dd7877bSStanislav Gatev SourceManager.getLocForStartOfFile(SourceManager.getMainFileID()) 1193dd7877bSStanislav Gatev .getLocWithOffset(Offset) 1203dd7877bSStanislav Gatev .printToString(SourceManager) 1213dd7877bSStanislav Gatev .data()); 1223dd7877bSStanislav Gatev } 1233dd7877bSStanislav Gatev 1248dd14c42SWei Yi Tee auto Annotation = Code.slice(Range.Begin, Range.End).str(); 1258dd14c42SWei Yi Tee if (!ExistingAnnotations.insert(Annotation).second) { 1268dd14c42SWei Yi Tee return llvm::createStringError( 1278dd14c42SWei Yi Tee std::make_error_code(std::errc::invalid_argument), 1288dd14c42SWei Yi Tee "Repeated use of annotation: %s", Annotation.data()); 1298dd14c42SWei Yi Tee } 1308dd14c42SWei Yi Tee Result[Stmt] = std::move(Annotation); 1318dd14c42SWei Yi Tee 1323dd7877bSStanislav Gatev I++; 1333dd7877bSStanislav Gatev 1343dd7877bSStanislav Gatev if (I < Annotations.size() && Annotations[I].Begin >= Offset) { 1353dd7877bSStanislav Gatev return llvm::createStringError( 1363dd7877bSStanislav Gatev std::make_error_code(std::errc::invalid_argument), 1373dd7877bSStanislav Gatev "Multiple annotations bound to the statement at the location: %s", 1383dd7877bSStanislav Gatev Stmt->getBeginLoc().printToString(SourceManager).data()); 1393dd7877bSStanislav Gatev } 1403dd7877bSStanislav Gatev } 1413dd7877bSStanislav Gatev } 1423dd7877bSStanislav Gatev 1433dd7877bSStanislav Gatev if (I < Annotations.size()) { 1443dd7877bSStanislav Gatev return llvm::createStringError( 1453dd7877bSStanislav Gatev std::make_error_code(std::errc::invalid_argument), 1463dd7877bSStanislav Gatev "Not all annotations were bound to statements. Unbound annotation at: " 1473dd7877bSStanislav Gatev "%s", 1483dd7877bSStanislav Gatev SourceManager.getLocForStartOfFile(SourceManager.getMainFileID()) 1493dd7877bSStanislav Gatev .getLocWithOffset(Annotations[I].Begin) 1503dd7877bSStanislav Gatev .printToString(SourceManager) 1513dd7877bSStanislav Gatev .data()); 1523dd7877bSStanislav Gatev } 1533dd7877bSStanislav Gatev 1543dd7877bSStanislav Gatev return Result; 1553dd7877bSStanislav Gatev } 1563dd7877bSStanislav Gatev 157d0be47c5SMartin Braenne llvm::Error test::checkDataflowWithNoopAnalysis( 158d0be47c5SMartin Braenne llvm::StringRef Code, 159d0be47c5SMartin Braenne std::function< 160d0be47c5SMartin Braenne void(const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &, 161d0be47c5SMartin Braenne ASTContext &)> 162d0be47c5SMartin Braenne VerifyResults, 163d0be47c5SMartin Braenne DataflowAnalysisOptions Options, LangStandard::Kind Std, 164d0be47c5SMartin Braenne llvm::StringRef TargetFun) { 16552d06963SStanislav Gatev return checkDataflowWithNoopAnalysis(Code, ast_matchers::hasName(TargetFun), 16652d06963SStanislav Gatev VerifyResults, Options, Std); 16752d06963SStanislav Gatev } 16852d06963SStanislav Gatev 16952d06963SStanislav Gatev llvm::Error test::checkDataflowWithNoopAnalysis( 17052d06963SStanislav Gatev llvm::StringRef Code, 17152d06963SStanislav Gatev ast_matchers::internal::Matcher<FunctionDecl> TargetFuncMatcher, 17252d06963SStanislav Gatev std::function< 17352d06963SStanislav Gatev void(const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &, 17452d06963SStanislav Gatev ASTContext &)> 17552d06963SStanislav Gatev VerifyResults, 17671f2ec2dSmartinboehme DataflowAnalysisOptions Options, LangStandard::Kind Std, 17771f2ec2dSmartinboehme std::function<llvm::StringMap<QualType>(QualType)> SyntheticFieldCallback) { 178d0be47c5SMartin Braenne llvm::SmallVector<std::string, 3> ASTBuildArgs = { 179*655c2233Smartinboehme "-fsyntax-only", 180d0be47c5SMartin Braenne // -fnodelayed-template-parsing is the default everywhere but on Windows. 181d0be47c5SMartin Braenne // Set it explicitly so that tests behave the same on Windows as on other 182d0be47c5SMartin Braenne // platforms. 183*655c2233Smartinboehme "-fno-delayed-template-parsing", 1842d539db2Smartinboehme // Set -Wno-unused-value because it's often desirable in tests to write 1852d539db2Smartinboehme // expressions with unused value, and we don't want the output to be 1862d539db2Smartinboehme // cluttered with warnings about them. 187*655c2233Smartinboehme "-Wno-unused-value", 188*655c2233Smartinboehme // Some build environments don't have RTTI enabled by default. 189*655c2233Smartinboehme // Enable it explicitly to make sure tests work in all environments. 190*655c2233Smartinboehme "-frtti", 191d0be47c5SMartin Braenne "-std=" + 192d0be47c5SMartin Braenne std::string(LangStandard::getLangStandardForKind(Std).getName())}; 193d0be47c5SMartin Braenne AnalysisInputs<NoopAnalysis> AI( 19452d06963SStanislav Gatev Code, TargetFuncMatcher, 19571f2ec2dSmartinboehme [UseBuiltinModel = Options.BuiltinOpts.has_value(), 19671f2ec2dSmartinboehme &SyntheticFieldCallback](ASTContext &C, Environment &Env) { 19771f2ec2dSmartinboehme Env.getDataflowAnalysisContext().setSyntheticFieldCallback( 19871f2ec2dSmartinboehme std::move(SyntheticFieldCallback)); 199d0be47c5SMartin Braenne return NoopAnalysis( 200d0be47c5SMartin Braenne C, 201d0be47c5SMartin Braenne DataflowAnalysisOptions{ 202d0be47c5SMartin Braenne UseBuiltinModel ? Env.getDataflowAnalysisContext().getOptions() 203d0be47c5SMartin Braenne : std::optional<BuiltinOptions>()}); 204d0be47c5SMartin Braenne }); 205d0be47c5SMartin Braenne AI.ASTBuildArgs = ASTBuildArgs; 206d0be47c5SMartin Braenne if (Options.BuiltinOpts) 207d0be47c5SMartin Braenne AI.BuiltinOptions = *Options.BuiltinOpts; 208d0be47c5SMartin Braenne return checkDataflow<NoopAnalysis>( 209d0be47c5SMartin Braenne std::move(AI), 210d0be47c5SMartin Braenne /*VerifyResults=*/ 211d0be47c5SMartin Braenne [&VerifyResults]( 212d0be47c5SMartin Braenne const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results, 213d0be47c5SMartin Braenne const AnalysisOutputs &AO) { VerifyResults(Results, AO.ASTCtx); }); 214d0be47c5SMartin Braenne } 215d0be47c5SMartin Braenne 2163dd7877bSStanislav Gatev const ValueDecl *test::findValueDecl(ASTContext &ASTCtx, llvm::StringRef Name) { 217d04b1989SMartin Braenne auto TargetNodes = match( 218d04b1989SMartin Braenne valueDecl(unless(indirectFieldDecl()), hasName(Name)).bind("v"), ASTCtx); 2193dd7877bSStanislav Gatev assert(TargetNodes.size() == 1 && "Name must be unique"); 2203dd7877bSStanislav Gatev auto *const Result = selectFirst<ValueDecl>("v", TargetNodes); 2213dd7877bSStanislav Gatev assert(Result != nullptr); 2223dd7877bSStanislav Gatev return Result; 2233dd7877bSStanislav Gatev } 224d04b1989SMartin Braenne 225d04b1989SMartin Braenne const IndirectFieldDecl *test::findIndirectFieldDecl(ASTContext &ASTCtx, 226d04b1989SMartin Braenne llvm::StringRef Name) { 227d04b1989SMartin Braenne auto TargetNodes = match(indirectFieldDecl(hasName(Name)).bind("i"), ASTCtx); 228d04b1989SMartin Braenne assert(TargetNodes.size() == 1 && "Name must be unique"); 229d04b1989SMartin Braenne const auto *Result = selectFirst<IndirectFieldDecl>("i", TargetNodes); 230d04b1989SMartin Braenne assert(Result != nullptr); 231d04b1989SMartin Braenne return Result; 232d04b1989SMartin Braenne } 233bbd61d80Smartinboehme 234bbd61d80Smartinboehme std::vector<const Formula *> test::parseFormulas(Arena &A, StringRef Lines) { 235bbd61d80Smartinboehme std::vector<const Formula *> Result; 236bbd61d80Smartinboehme while (!Lines.empty()) { 237bbd61d80Smartinboehme auto [First, Rest] = Lines.split('\n'); 238bbd61d80Smartinboehme Lines = Rest; 239bbd61d80Smartinboehme if (First.trim().empty()) 240bbd61d80Smartinboehme continue; 241bbd61d80Smartinboehme if (auto F = A.parseFormula(First)) 242bbd61d80Smartinboehme Result.push_back(&*F); 243bbd61d80Smartinboehme else 244bbd61d80Smartinboehme ADD_FAILURE() << llvm::toString(F.takeError()); 245bbd61d80Smartinboehme } 246bbd61d80Smartinboehme return Result; 247bbd61d80Smartinboehme } 248