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 15using 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. 19static 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 26Annotations::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 = Text.take_while(llvm::isAlnum); 57 Text = Text.drop_front(Name->size()); 58 continue; 59 } 60 Code.push_back(Text.front()); 61 Text = Text.drop_front(); 62 } 63 Require(!Name, "unterminated $name"); 64 Require(OpenRanges.empty(), "unmatched [["); 65} 66 67size_t Annotations::point(llvm::StringRef Name) const { 68 auto I = Points.find(Name); 69 require(I != Points.end() && I->getValue().size() == 1, 70 "expected exactly one point", Code); 71 return I->getValue()[0]; 72} 73 74std::vector<size_t> Annotations::points(llvm::StringRef Name) const { 75 auto P = Points.lookup(Name); 76 return {P.begin(), P.end()}; 77} 78 79Annotations::Range Annotations::range(llvm::StringRef Name) const { 80 auto I = Ranges.find(Name); 81 require(I != Ranges.end() && I->getValue().size() == 1, 82 "expected exactly one range", Code); 83 return I->getValue()[0]; 84} 85 86std::vector<Annotations::Range> 87Annotations::ranges(llvm::StringRef Name) const { 88 auto R = Ranges.lookup(Name); 89 return {R.begin(), R.end()}; 90} 91 92llvm::raw_ostream &llvm::operator<<(llvm::raw_ostream &O, 93 const llvm::Annotations::Range &R) { 94 return O << llvm::formatv("[{0}, {1})", R.Begin, R.End); 95} 96