16fae38ecSIlya Biryukov //===--- Annotations.cpp - Annotated source code for unit tests --*- C++-*-===//
26fae38ecSIlya Biryukov //
36fae38ecSIlya Biryukov // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
46fae38ecSIlya Biryukov // See https://llvm.org/LICENSE.txt for license information.
56fae38ecSIlya Biryukov // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
66fae38ecSIlya Biryukov //
76fae38ecSIlya Biryukov //===----------------------------------------------------------------------===//
86fae38ecSIlya Biryukov 
96fae38ecSIlya Biryukov #include "llvm/Testing/Support/Annotations.h"
106fae38ecSIlya Biryukov 
116fae38ecSIlya Biryukov #include "llvm/ADT/StringExtras.h"
126fae38ecSIlya Biryukov #include "llvm/Support/FormatVariadic.h"
136fae38ecSIlya Biryukov #include "llvm/Support/raw_ostream.h"
146fae38ecSIlya Biryukov 
156fae38ecSIlya Biryukov using namespace llvm;
166fae38ecSIlya Biryukov 
176fae38ecSIlya Biryukov // Crash if the assertion fails, printing the message and testcase.
186fae38ecSIlya Biryukov // More elegant error handling isn't needed for unit tests.
require(bool Assertion,const char * Msg,llvm::StringRef Code)196fae38ecSIlya Biryukov static void require(bool Assertion, const char *Msg, llvm::StringRef Code) {
206fae38ecSIlya Biryukov   if (!Assertion) {
216fae38ecSIlya Biryukov     llvm::errs() << "Annotated testcase: " << Msg << "\n" << Code << "\n";
226fae38ecSIlya Biryukov     llvm_unreachable("Annotated testcase assertion failed!");
236fae38ecSIlya Biryukov   }
246fae38ecSIlya Biryukov }
256fae38ecSIlya Biryukov 
Annotations(llvm::StringRef Text)266fae38ecSIlya Biryukov Annotations::Annotations(llvm::StringRef Text) {
276fae38ecSIlya Biryukov   auto Require = [Text](bool Assertion, const char *Msg) {
286fae38ecSIlya Biryukov     require(Assertion, Msg, Text);
296fae38ecSIlya Biryukov   };
306fae38ecSIlya Biryukov   llvm::Optional<llvm::StringRef> Name;
316fae38ecSIlya Biryukov   llvm::SmallVector<std::pair<llvm::StringRef, size_t>, 8> OpenRanges;
326fae38ecSIlya Biryukov 
336fae38ecSIlya Biryukov   Code.reserve(Text.size());
346fae38ecSIlya Biryukov   while (!Text.empty()) {
356fae38ecSIlya Biryukov     if (Text.consume_front("^")) {
36*129b531cSKazu Hirata       Points[Name.value_or("")].push_back(Code.size());
376fae38ecSIlya Biryukov       Name = llvm::None;
386fae38ecSIlya Biryukov       continue;
396fae38ecSIlya Biryukov     }
406fae38ecSIlya Biryukov     if (Text.consume_front("[[")) {
41*129b531cSKazu Hirata       OpenRanges.emplace_back(Name.value_or(""), Code.size());
426fae38ecSIlya Biryukov       Name = llvm::None;
436fae38ecSIlya Biryukov       continue;
446fae38ecSIlya Biryukov     }
456fae38ecSIlya Biryukov     Require(!Name, "$name should be followed by ^ or [[");
466fae38ecSIlya Biryukov     if (Text.consume_front("]]")) {
476fae38ecSIlya Biryukov       Require(!OpenRanges.empty(), "unmatched ]]");
486fae38ecSIlya Biryukov       Range R;
496fae38ecSIlya Biryukov       R.Begin = OpenRanges.back().second;
506fae38ecSIlya Biryukov       R.End = Code.size();
516fae38ecSIlya Biryukov       Ranges[OpenRanges.back().first].push_back(R);
526fae38ecSIlya Biryukov       OpenRanges.pop_back();
536fae38ecSIlya Biryukov       continue;
546fae38ecSIlya Biryukov     }
556fae38ecSIlya Biryukov     if (Text.consume_front("$")) {
56dd8fb212SSam McCall       Name =
57dd8fb212SSam McCall           Text.take_while([](char C) { return llvm::isAlnum(C) || C == '_'; });
586fae38ecSIlya Biryukov       Text = Text.drop_front(Name->size());
596fae38ecSIlya Biryukov       continue;
606fae38ecSIlya Biryukov     }
616fae38ecSIlya Biryukov     Code.push_back(Text.front());
626fae38ecSIlya Biryukov     Text = Text.drop_front();
636fae38ecSIlya Biryukov   }
646fae38ecSIlya Biryukov   Require(!Name, "unterminated $name");
656fae38ecSIlya Biryukov   Require(OpenRanges.empty(), "unmatched [[");
666fae38ecSIlya Biryukov }
676fae38ecSIlya Biryukov 
point(llvm::StringRef Name) const686fae38ecSIlya Biryukov size_t Annotations::point(llvm::StringRef Name) const {
696fae38ecSIlya Biryukov   auto I = Points.find(Name);
706fae38ecSIlya Biryukov   require(I != Points.end() && I->getValue().size() == 1,
716fae38ecSIlya Biryukov           "expected exactly one point", Code);
726fae38ecSIlya Biryukov   return I->getValue()[0];
736fae38ecSIlya Biryukov }
746fae38ecSIlya Biryukov 
points(llvm::StringRef Name) const756fae38ecSIlya Biryukov std::vector<size_t> Annotations::points(llvm::StringRef Name) const {
765a2febb3SNathan James   auto I = Points.find(Name);
775a2febb3SNathan James   if (I == Points.end())
785a2febb3SNathan James     return {};
795a2febb3SNathan James   return {I->getValue().begin(), I->getValue().end()};
806fae38ecSIlya Biryukov }
816fae38ecSIlya Biryukov 
range(llvm::StringRef Name) const826fae38ecSIlya Biryukov Annotations::Range Annotations::range(llvm::StringRef Name) const {
836fae38ecSIlya Biryukov   auto I = Ranges.find(Name);
846fae38ecSIlya Biryukov   require(I != Ranges.end() && I->getValue().size() == 1,
856fae38ecSIlya Biryukov           "expected exactly one range", Code);
866fae38ecSIlya Biryukov   return I->getValue()[0];
876fae38ecSIlya Biryukov }
886fae38ecSIlya Biryukov 
896fae38ecSIlya Biryukov std::vector<Annotations::Range>
ranges(llvm::StringRef Name) const906fae38ecSIlya Biryukov Annotations::ranges(llvm::StringRef Name) const {
915a2febb3SNathan James   auto I = Ranges.find(Name);
925a2febb3SNathan James   if (I == Ranges.end())
935a2febb3SNathan James     return {};
945a2febb3SNathan James   return {I->getValue().begin(), I->getValue().end()};
956fae38ecSIlya Biryukov }
966fae38ecSIlya Biryukov 
operator <<(llvm::raw_ostream & O,const llvm::Annotations::Range & R)976fae38ecSIlya Biryukov llvm::raw_ostream &llvm::operator<<(llvm::raw_ostream &O,
986fae38ecSIlya Biryukov                                     const llvm::Annotations::Range &R) {
996fae38ecSIlya Biryukov   return O << llvm::formatv("[{0}, {1})", R.Begin, R.End);
1006fae38ecSIlya Biryukov }
101