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 "-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(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 216 const 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 225 const 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 234 std::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 else 244 ADD_FAILURE() << llvm::toString(F.takeError()); 245 } 246 return Result; 247 } 248