1351278Sdim//===--- Annotations.cpp - Annotated source code for unit tests --*- C++-*-===// 2351278Sdim// 3351278Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4351278Sdim// See https://llvm.org/LICENSE.txt for license information. 5351278Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6351278Sdim// 7351278Sdim//===----------------------------------------------------------------------===// 8351278Sdim 9351278Sdim#include "llvm/Testing/Support/Annotations.h" 10351278Sdim 11351278Sdim#include "llvm/ADT/StringExtras.h" 12351278Sdim#include "llvm/Support/FormatVariadic.h" 13351278Sdim#include "llvm/Support/raw_ostream.h" 14351278Sdim 15351278Sdimusing namespace llvm; 16351278Sdim 17351278Sdim// Crash if the assertion fails, printing the message and testcase. 18351278Sdim// More elegant error handling isn't needed for unit tests. 19351278Sdimstatic void require(bool Assertion, const char *Msg, llvm::StringRef Code) { 20351278Sdim if (!Assertion) { 21351278Sdim llvm::errs() << "Annotated testcase: " << Msg << "\n" << Code << "\n"; 22351278Sdim llvm_unreachable("Annotated testcase assertion failed!"); 23351278Sdim } 24351278Sdim} 25351278Sdim 26351278SdimAnnotations::Annotations(llvm::StringRef Text) { 27351278Sdim auto Require = [Text](bool Assertion, const char *Msg) { 28351278Sdim require(Assertion, Msg, Text); 29351278Sdim }; 30351278Sdim llvm::Optional<llvm::StringRef> Name; 31351278Sdim llvm::SmallVector<std::pair<llvm::StringRef, size_t>, 8> OpenRanges; 32351278Sdim 33351278Sdim Code.reserve(Text.size()); 34351278Sdim while (!Text.empty()) { 35351278Sdim if (Text.consume_front("^")) { 36351278Sdim Points[Name.getValueOr("")].push_back(Code.size()); 37351278Sdim Name = llvm::None; 38351278Sdim continue; 39351278Sdim } 40351278Sdim if (Text.consume_front("[[")) { 41351278Sdim OpenRanges.emplace_back(Name.getValueOr(""), Code.size()); 42351278Sdim Name = llvm::None; 43351278Sdim continue; 44351278Sdim } 45351278Sdim Require(!Name, "$name should be followed by ^ or [["); 46351278Sdim if (Text.consume_front("]]")) { 47351278Sdim Require(!OpenRanges.empty(), "unmatched ]]"); 48351278Sdim Range R; 49351278Sdim R.Begin = OpenRanges.back().second; 50351278Sdim R.End = Code.size(); 51351278Sdim Ranges[OpenRanges.back().first].push_back(R); 52351278Sdim OpenRanges.pop_back(); 53351278Sdim continue; 54351278Sdim } 55351278Sdim if (Text.consume_front("$")) { 56351278Sdim Name = Text.take_while(llvm::isAlnum); 57351278Sdim Text = Text.drop_front(Name->size()); 58351278Sdim continue; 59351278Sdim } 60351278Sdim Code.push_back(Text.front()); 61351278Sdim Text = Text.drop_front(); 62351278Sdim } 63351278Sdim Require(!Name, "unterminated $name"); 64351278Sdim Require(OpenRanges.empty(), "unmatched [["); 65351278Sdim} 66351278Sdim 67351278Sdimsize_t Annotations::point(llvm::StringRef Name) const { 68351278Sdim auto I = Points.find(Name); 69351278Sdim require(I != Points.end() && I->getValue().size() == 1, 70351278Sdim "expected exactly one point", Code); 71351278Sdim return I->getValue()[0]; 72351278Sdim} 73351278Sdim 74351278Sdimstd::vector<size_t> Annotations::points(llvm::StringRef Name) const { 75351278Sdim auto P = Points.lookup(Name); 76351278Sdim return {P.begin(), P.end()}; 77351278Sdim} 78351278Sdim 79351278SdimAnnotations::Range Annotations::range(llvm::StringRef Name) const { 80351278Sdim auto I = Ranges.find(Name); 81351278Sdim require(I != Ranges.end() && I->getValue().size() == 1, 82351278Sdim "expected exactly one range", Code); 83351278Sdim return I->getValue()[0]; 84351278Sdim} 85351278Sdim 86351278Sdimstd::vector<Annotations::Range> 87351278SdimAnnotations::ranges(llvm::StringRef Name) const { 88351278Sdim auto R = Ranges.lookup(Name); 89351278Sdim return {R.begin(), R.end()}; 90351278Sdim} 91351278Sdim 92351278Sdimllvm::raw_ostream &llvm::operator<<(llvm::raw_ostream &O, 93351278Sdim const llvm::Annotations::Range &R) { 94351278Sdim return O << llvm::formatv("[{0}, {1})", R.Begin, R.End); 95351278Sdim} 96