1 //===--- Annotations.cpp - Annotated source code for unit tests --*- C++-*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "llvm/Testing/Support/Annotations.h" 10 11 #include "llvm/ADT/StringExtras.h" 12 #include "llvm/Support/FormatVariadic.h" 13 #include "llvm/Support/raw_ostream.h" 14 15 using namespace llvm; 16 17 // Crash if the assertion fails, printing the message and testcase. 18 // More elegant error handling isn't needed for unit tests. 19 static void require(bool Assertion, const char *Msg, llvm::StringRef Code) { 20 if (!Assertion) { 21 llvm::errs() << "Annotated testcase: " << Msg << "\n" << Code << "\n"; 22 llvm_unreachable("Annotated testcase assertion failed!"); 23 } 24 } 25 26 Annotations::Annotations(llvm::StringRef Text) { 27 auto Require = [Text](bool Assertion, const char *Msg) { 28 require(Assertion, Msg, Text); 29 }; 30 llvm::Optional<llvm::StringRef> Name; 31 llvm::SmallVector<std::pair<llvm::StringRef, size_t>, 8> OpenRanges; 32 33 Code.reserve(Text.size()); 34 while (!Text.empty()) { 35 if (Text.consume_front("^")) { 36 Points[Name.getValueOr("")].push_back(Code.size()); 37 Name = llvm::None; 38 continue; 39 } 40 if (Text.consume_front("[[")) { 41 OpenRanges.emplace_back(Name.getValueOr(""), Code.size()); 42 Name = llvm::None; 43 continue; 44 } 45 Require(!Name, "$name should be followed by ^ or [["); 46 if (Text.consume_front("]]")) { 47 Require(!OpenRanges.empty(), "unmatched ]]"); 48 Range R; 49 R.Begin = OpenRanges.back().second; 50 R.End = Code.size(); 51 Ranges[OpenRanges.back().first].push_back(R); 52 OpenRanges.pop_back(); 53 continue; 54 } 55 if (Text.consume_front("$")) { 56 Name = 57 Text.take_while([](char C) { return llvm::isAlnum(C) || C == '_'; }); 58 Text = Text.drop_front(Name->size()); 59 continue; 60 } 61 Code.push_back(Text.front()); 62 Text = Text.drop_front(); 63 } 64 Require(!Name, "unterminated $name"); 65 Require(OpenRanges.empty(), "unmatched [["); 66 } 67 68 size_t Annotations::point(llvm::StringRef Name) const { 69 auto I = Points.find(Name); 70 require(I != Points.end() && I->getValue().size() == 1, 71 "expected exactly one point", Code); 72 return I->getValue()[0]; 73 } 74 75 std::vector<size_t> Annotations::points(llvm::StringRef Name) const { 76 auto I = Points.find(Name); 77 if (I == Points.end()) 78 return {}; 79 return {I->getValue().begin(), I->getValue().end()}; 80 } 81 82 Annotations::Range Annotations::range(llvm::StringRef Name) const { 83 auto I = Ranges.find(Name); 84 require(I != Ranges.end() && I->getValue().size() == 1, 85 "expected exactly one range", Code); 86 return I->getValue()[0]; 87 } 88 89 std::vector<Annotations::Range> 90 Annotations::ranges(llvm::StringRef Name) const { 91 auto I = Ranges.find(Name); 92 if (I == Ranges.end()) 93 return {}; 94 return {I->getValue().begin(), I->getValue().end()}; 95 } 96 97 llvm::raw_ostream &llvm::operator<<(llvm::raw_ostream &O, 98 const llvm::Annotations::Range &R) { 99 return O << llvm::formatv("[{0}, {1})", R.Begin, R.End); 100 } 101