forked from OSchip/llvm-project
[Testing] Move clangd::Annotations to llvm testing support
Summary: Annotations allow writing nice-looking unit test code when one needs access to locations from the source code, e.g. running code completion at particular offsets in a file. See comments in Annotations.cpp for more details on the API. Also got rid of a duplicate annotations parsing code in clang's code complete tests. Reviewers: gribozavr, sammccall Reviewed By: gribozavr Subscribers: mgorny, hiraditya, ioeric, MaskRay, jkorous, arphaman, kadircet, jdoerfert, cfe-commits, llvm-commits Tags: #clang, #llvm Differential Revision: https://reviews.llvm.org/D59814 llvm-svn: 359179
This commit is contained in:
parent
45d042ed96
commit
6fae38ec91
|
@ -12,74 +12,41 @@
|
|||
namespace clang {
|
||||
namespace clangd {
|
||||
|
||||
// Crash if the assertion fails, printing the message and testcase.
|
||||
// More elegant error handling isn't needed for unit tests.
|
||||
static void require(bool Assertion, const char *Msg, llvm::StringRef Code) {
|
||||
if (!Assertion) {
|
||||
llvm::errs() << "Annotated testcase: " << Msg << "\n" << Code << "\n";
|
||||
llvm_unreachable("Annotated testcase assertion failed!");
|
||||
}
|
||||
}
|
||||
|
||||
Annotations::Annotations(llvm::StringRef Text) {
|
||||
auto Here = [this] { return offsetToPosition(Code, Code.size()); };
|
||||
auto Require = [Text](bool Assertion, const char *Msg) {
|
||||
require(Assertion, Msg, Text);
|
||||
};
|
||||
llvm::Optional<llvm::StringRef> Name;
|
||||
llvm::SmallVector<std::pair<llvm::StringRef, Position>, 8> OpenRanges;
|
||||
|
||||
Code.reserve(Text.size());
|
||||
while (!Text.empty()) {
|
||||
if (Text.consume_front("^")) {
|
||||
Points[Name.getValueOr("")].push_back(Here());
|
||||
Name = None;
|
||||
continue;
|
||||
}
|
||||
if (Text.consume_front("[[")) {
|
||||
OpenRanges.emplace_back(Name.getValueOr(""), Here());
|
||||
Name = None;
|
||||
continue;
|
||||
}
|
||||
Require(!Name, "$name should be followed by ^ or [[");
|
||||
if (Text.consume_front("]]")) {
|
||||
Require(!OpenRanges.empty(), "unmatched ]]");
|
||||
Ranges[OpenRanges.back().first].push_back(
|
||||
{OpenRanges.back().second, Here()});
|
||||
OpenRanges.pop_back();
|
||||
continue;
|
||||
}
|
||||
if (Text.consume_front("$")) {
|
||||
Name = Text.take_while(llvm::isAlnum);
|
||||
Text = Text.drop_front(Name->size());
|
||||
continue;
|
||||
}
|
||||
Code.push_back(Text.front());
|
||||
Text = Text.drop_front();
|
||||
}
|
||||
Require(!Name, "unterminated $name");
|
||||
Require(OpenRanges.empty(), "unmatched [[");
|
||||
}
|
||||
|
||||
Position Annotations::point(llvm::StringRef Name) const {
|
||||
auto I = Points.find(Name);
|
||||
require(I != Points.end() && I->getValue().size() == 1,
|
||||
"expected exactly one point", Code);
|
||||
return I->getValue()[0];
|
||||
return offsetToPosition(code(), Base::point(Name));
|
||||
}
|
||||
|
||||
std::vector<Position> Annotations::points(llvm::StringRef Name) const {
|
||||
auto P = Points.lookup(Name);
|
||||
return {P.begin(), P.end()};
|
||||
auto Offsets = Base::points(Name);
|
||||
|
||||
std::vector<Position> Ps;
|
||||
Ps.reserve(Offsets.size());
|
||||
for (size_t O : Offsets)
|
||||
Ps.push_back(offsetToPosition(code(), O));
|
||||
|
||||
return Ps;
|
||||
}
|
||||
Range Annotations::range(llvm::StringRef Name) const {
|
||||
auto I = Ranges.find(Name);
|
||||
require(I != Ranges.end() && I->getValue().size() == 1,
|
||||
"expected exactly one range", Code);
|
||||
return I->getValue()[0];
|
||||
|
||||
static clangd::Range toLSPRange(llvm::StringRef Code, Annotations::Range R) {
|
||||
clangd::Range LSPRange;
|
||||
LSPRange.start = offsetToPosition(Code, R.Begin);
|
||||
LSPRange.end = offsetToPosition(Code, R.End);
|
||||
return LSPRange;
|
||||
}
|
||||
std::vector<Range> Annotations::ranges(llvm::StringRef Name) const {
|
||||
auto R = Ranges.lookup(Name);
|
||||
return {R.begin(), R.end()};
|
||||
|
||||
clangd::Range Annotations::range(llvm::StringRef Name) const {
|
||||
return toLSPRange(code(), Base::range(Name));
|
||||
}
|
||||
|
||||
std::vector<clangd::Range> Annotations::ranges(llvm::StringRef Name) const {
|
||||
auto OffsetRanges = Base::ranges(Name);
|
||||
|
||||
std::vector<clangd::Range> Rs;
|
||||
Rs.reserve(OffsetRanges.size());
|
||||
for (Annotations::Range R : OffsetRanges)
|
||||
Rs.push_back(toLSPRange(code(), R));
|
||||
|
||||
return Rs;
|
||||
}
|
||||
|
||||
} // namespace clangd
|
||||
|
|
|
@ -5,64 +5,32 @@
|
|||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Annotations lets you mark points and ranges inside source code, for tests:
|
||||
//
|
||||
// Annotations Example(R"cpp(
|
||||
// int complete() { x.pri^ } // ^ indicates a point
|
||||
// void err() { [["hello" == 42]]; } // [[this is a range]]
|
||||
// $definition^class Foo{}; // points can be named: "definition"
|
||||
// $fail[[static_assert(false, "")]] // ranges can be named too: "fail"
|
||||
// )cpp");
|
||||
//
|
||||
// StringRef Code = Example.code(); // annotations stripped.
|
||||
// std::vector<Position> PP = Example.points(); // all unnamed points
|
||||
// Position P = Example.point(); // there must be exactly one
|
||||
// Range R = Example.range("fail"); // find named ranges
|
||||
//
|
||||
// Points/ranges are coordinates into `code()` which is stripped of annotations.
|
||||
//
|
||||
// Ranges may be nested (and points can be inside ranges), but there's no way
|
||||
// to define general overlapping ranges.
|
||||
//
|
||||
// A clangd-specific version of llvm/Testing/Support/Annotations.h, replaces
|
||||
// offsets and offset-based ranges with types from the LSP protocol.
|
||||
//===---------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANGD_ANNOTATIONS_H
|
||||
#define LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANGD_ANNOTATIONS_H
|
||||
|
||||
#include "Protocol.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/StringMap.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Testing/Support/Annotations.h"
|
||||
|
||||
namespace clang {
|
||||
namespace clangd {
|
||||
|
||||
class Annotations {
|
||||
/// Same as llvm::Annotations, but adjusts functions to LSP-specific types for
|
||||
/// positions and ranges.
|
||||
class Annotations : public llvm::Annotations {
|
||||
using Base = llvm::Annotations;
|
||||
|
||||
public:
|
||||
// Parses the annotations from Text. Crashes if it's malformed.
|
||||
Annotations(llvm::StringRef Text);
|
||||
using llvm::Annotations::Annotations;
|
||||
|
||||
// The input text with all annotations stripped.
|
||||
// All points and ranges are relative to this stripped text.
|
||||
llvm::StringRef code() const { return Code; }
|
||||
|
||||
// Returns the position of the point marked by ^ (or $name^) in the text.
|
||||
// Crashes if there isn't exactly one.
|
||||
Position point(llvm::StringRef Name = "") const;
|
||||
// Returns the position of all points marked by ^ (or $name^) in the text.
|
||||
std::vector<Position> points(llvm::StringRef Name = "") const;
|
||||
|
||||
// Returns the location of the range marked by [[ ]] (or $name[[ ]]).
|
||||
// Crashes if there isn't exactly one.
|
||||
Range range(llvm::StringRef Name = "") const;
|
||||
// Returns the location of all ranges marked by [[ ]] (or $name[[ ]]).
|
||||
std::vector<Range> ranges(llvm::StringRef Name = "") const;
|
||||
|
||||
private:
|
||||
std::string Code;
|
||||
llvm::StringMap<llvm::SmallVector<Position, 1>> Points;
|
||||
llvm::StringMap<llvm::SmallVector<Range, 1>> Ranges;
|
||||
clangd::Range range(llvm::StringRef Name = "") const;
|
||||
std::vector<clangd::Range> ranges(llvm::StringRef Name = "") const;
|
||||
};
|
||||
|
||||
} // namespace clangd
|
||||
|
|
|
@ -16,4 +16,5 @@ target_link_libraries(SemaTests
|
|||
clangSema
|
||||
clangSerialization
|
||||
clangTooling
|
||||
LLVMTestingSupport
|
||||
)
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "clang/Sema/Sema.h"
|
||||
#include "clang/Sema/SemaDiagnostic.h"
|
||||
#include "clang/Tooling/Tooling.h"
|
||||
#include "llvm/Testing/Support/Annotations.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include <cstddef>
|
||||
|
@ -107,41 +108,18 @@ CompletionContext runCompletion(StringRef Code, size_t Offset) {
|
|||
return ResultCtx;
|
||||
}
|
||||
|
||||
struct ParsedAnnotations {
|
||||
std::vector<size_t> Points;
|
||||
std::string Code;
|
||||
};
|
||||
|
||||
ParsedAnnotations parseAnnotations(StringRef AnnotatedCode) {
|
||||
ParsedAnnotations R;
|
||||
while (!AnnotatedCode.empty()) {
|
||||
size_t NextPoint = AnnotatedCode.find('^');
|
||||
if (NextPoint == StringRef::npos) {
|
||||
R.Code += AnnotatedCode;
|
||||
AnnotatedCode = "";
|
||||
break;
|
||||
}
|
||||
R.Code += AnnotatedCode.substr(0, NextPoint);
|
||||
R.Points.push_back(R.Code.size());
|
||||
|
||||
AnnotatedCode = AnnotatedCode.substr(NextPoint + 1);
|
||||
}
|
||||
return R;
|
||||
}
|
||||
|
||||
CompletionContext runCodeCompleteOnCode(StringRef AnnotatedCode) {
|
||||
ParsedAnnotations P = parseAnnotations(AnnotatedCode);
|
||||
assert(P.Points.size() == 1 && "expected exactly one annotation point");
|
||||
return runCompletion(P.Code, P.Points.front());
|
||||
llvm::Annotations A(AnnotatedCode);
|
||||
return runCompletion(A.code(), A.point());
|
||||
}
|
||||
|
||||
std::vector<std::string>
|
||||
collectPreferredTypes(StringRef AnnotatedCode,
|
||||
std::string *PtrDiffType = nullptr) {
|
||||
ParsedAnnotations P = parseAnnotations(AnnotatedCode);
|
||||
llvm::Annotations A(AnnotatedCode);
|
||||
std::vector<std::string> Types;
|
||||
for (size_t Point : P.Points) {
|
||||
auto Results = runCompletion(P.Code, Point);
|
||||
for (size_t Point : A.points()) {
|
||||
auto Results = runCompletion(A.code(), Point);
|
||||
if (PtrDiffType) {
|
||||
assert(PtrDiffType->empty() || *PtrDiffType == Results.PtrDiffType);
|
||||
*PtrDiffType = Results.PtrDiffType;
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
//===--- Annotations.h - Annotated source code for tests ---------*- C++-*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef LLVM_TESTING_SUPPORT_ANNOTATIONS_H
|
||||
#define LLVM_TESTING_SUPPORT_ANNOTATIONS_H
|
||||
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/StringMap.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
namespace llvm {
|
||||
|
||||
/// Annotations lets you mark points and ranges inside source code, for tests:
|
||||
///
|
||||
/// Annotations Example(R"cpp(
|
||||
/// int complete() { x.pri^ } // ^ indicates a point
|
||||
/// void err() { [["hello" == 42]]; } // [[this is a range]]
|
||||
/// $definition^class Foo{}; // points can be named: "definition"
|
||||
/// $fail[[static_assert(false, "")]] // ranges can be named too: "fail"
|
||||
/// )cpp");
|
||||
///
|
||||
/// StringRef Code = Example.code(); // annotations stripped.
|
||||
/// std::vector<size_t> PP = Example.points(); // all unnamed points
|
||||
/// size_t P = Example.point(); // there must be exactly one
|
||||
/// llvm::Range R = Example.range("fail"); // find named ranges
|
||||
///
|
||||
/// Points/ranges are coordinated into `code()` which is stripped of
|
||||
/// annotations.
|
||||
///
|
||||
/// Ranges may be nested (and points can be inside ranges), but there's no way
|
||||
/// to define general overlapping ranges.
|
||||
///
|
||||
/// FIXME: the choice of the marking syntax makes it impossible to represent
|
||||
/// some of the C++ and Objective C constructs (including common ones
|
||||
/// like C++ attributes). We can fix this by:
|
||||
/// 1. introducing an escaping mechanism for the special characters,
|
||||
/// 2. making characters for marking points and ranges configurable,
|
||||
/// 3. changing the syntax to something less commonly used,
|
||||
/// 4. ...
|
||||
class Annotations {
|
||||
public:
|
||||
/// Two offsets pointing to a continuous substring. End is not included, i.e.
|
||||
/// represents a half-open range.
|
||||
struct Range {
|
||||
size_t Begin = 0;
|
||||
size_t End = 0;
|
||||
|
||||
friend bool operator==(const Range &L, const Range &R) {
|
||||
return std::tie(L.Begin, L.End) == std::tie(R.Begin, R.End);
|
||||
}
|
||||
friend bool operator!=(const Range &L, const Range &R) { return !(L == R); }
|
||||
};
|
||||
|
||||
/// Parses the annotations from Text. Crashes if it's malformed.
|
||||
Annotations(llvm::StringRef Text);
|
||||
|
||||
/// The input text with all annotations stripped.
|
||||
/// All points and ranges are relative to this stripped text.
|
||||
llvm::StringRef code() const { return Code; }
|
||||
|
||||
/// Returns the position of the point marked by ^ (or $name^) in the text.
|
||||
/// Crashes if there isn't exactly one.
|
||||
size_t point(llvm::StringRef Name = "") const;
|
||||
/// Returns the position of all points marked by ^ (or $name^) in the text.
|
||||
std::vector<size_t> points(llvm::StringRef Name = "") const;
|
||||
|
||||
/// Returns the location of the range marked by [[ ]] (or $name[[ ]]).
|
||||
/// Crashes if there isn't exactly one.
|
||||
Range range(llvm::StringRef Name = "") const;
|
||||
/// Returns the location of all ranges marked by [[ ]] (or $name[[ ]]).
|
||||
std::vector<Range> ranges(llvm::StringRef Name = "") const;
|
||||
|
||||
private:
|
||||
std::string Code;
|
||||
llvm::StringMap<llvm::SmallVector<size_t, 1>> Points;
|
||||
llvm::StringMap<llvm::SmallVector<Range, 1>> Ranges;
|
||||
};
|
||||
|
||||
llvm::raw_ostream &operator<<(llvm::raw_ostream &O,
|
||||
const llvm::Annotations::Range &R);
|
||||
|
||||
} // namespace llvm
|
||||
|
||||
#endif
|
|
@ -0,0 +1,95 @@
|
|||
//===--- Annotations.cpp - Annotated source code for unit tests --*- C++-*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "llvm/Testing/Support/Annotations.h"
|
||||
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
#include "llvm/Support/FormatVariadic.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
// Crash if the assertion fails, printing the message and testcase.
|
||||
// More elegant error handling isn't needed for unit tests.
|
||||
static void require(bool Assertion, const char *Msg, llvm::StringRef Code) {
|
||||
if (!Assertion) {
|
||||
llvm::errs() << "Annotated testcase: " << Msg << "\n" << Code << "\n";
|
||||
llvm_unreachable("Annotated testcase assertion failed!");
|
||||
}
|
||||
}
|
||||
|
||||
Annotations::Annotations(llvm::StringRef Text) {
|
||||
auto Require = [Text](bool Assertion, const char *Msg) {
|
||||
require(Assertion, Msg, Text);
|
||||
};
|
||||
llvm::Optional<llvm::StringRef> Name;
|
||||
llvm::SmallVector<std::pair<llvm::StringRef, size_t>, 8> OpenRanges;
|
||||
|
||||
Code.reserve(Text.size());
|
||||
while (!Text.empty()) {
|
||||
if (Text.consume_front("^")) {
|
||||
Points[Name.getValueOr("")].push_back(Code.size());
|
||||
Name = llvm::None;
|
||||
continue;
|
||||
}
|
||||
if (Text.consume_front("[[")) {
|
||||
OpenRanges.emplace_back(Name.getValueOr(""), Code.size());
|
||||
Name = llvm::None;
|
||||
continue;
|
||||
}
|
||||
Require(!Name, "$name should be followed by ^ or [[");
|
||||
if (Text.consume_front("]]")) {
|
||||
Require(!OpenRanges.empty(), "unmatched ]]");
|
||||
Range R;
|
||||
R.Begin = OpenRanges.back().second;
|
||||
R.End = Code.size();
|
||||
Ranges[OpenRanges.back().first].push_back(R);
|
||||
OpenRanges.pop_back();
|
||||
continue;
|
||||
}
|
||||
if (Text.consume_front("$")) {
|
||||
Name = Text.take_while(llvm::isAlnum);
|
||||
Text = Text.drop_front(Name->size());
|
||||
continue;
|
||||
}
|
||||
Code.push_back(Text.front());
|
||||
Text = Text.drop_front();
|
||||
}
|
||||
Require(!Name, "unterminated $name");
|
||||
Require(OpenRanges.empty(), "unmatched [[");
|
||||
}
|
||||
|
||||
size_t Annotations::point(llvm::StringRef Name) const {
|
||||
auto I = Points.find(Name);
|
||||
require(I != Points.end() && I->getValue().size() == 1,
|
||||
"expected exactly one point", Code);
|
||||
return I->getValue()[0];
|
||||
}
|
||||
|
||||
std::vector<size_t> Annotations::points(llvm::StringRef Name) const {
|
||||
auto P = Points.lookup(Name);
|
||||
return {P.begin(), P.end()};
|
||||
}
|
||||
|
||||
Annotations::Range Annotations::range(llvm::StringRef Name) const {
|
||||
auto I = Ranges.find(Name);
|
||||
require(I != Ranges.end() && I->getValue().size() == 1,
|
||||
"expected exactly one range", Code);
|
||||
return I->getValue()[0];
|
||||
}
|
||||
|
||||
std::vector<Annotations::Range>
|
||||
Annotations::ranges(llvm::StringRef Name) const {
|
||||
auto R = Ranges.lookup(Name);
|
||||
return {R.begin(), R.end()};
|
||||
}
|
||||
|
||||
llvm::raw_ostream &llvm::operator<<(llvm::raw_ostream &O,
|
||||
const llvm::Annotations::Range &R) {
|
||||
return O << llvm::formatv("[{0}, {1})", R.Begin, R.End);
|
||||
}
|
|
@ -2,6 +2,7 @@ add_definitions(-DGTEST_LANG_CXX11=1)
|
|||
add_definitions(-DGTEST_HAS_TR1_TUPLE=0)
|
||||
|
||||
add_llvm_library(LLVMTestingSupport
|
||||
Annotations.cpp
|
||||
Error.cpp
|
||||
SupportHelpers.cpp
|
||||
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
//===----- unittests/AnnotationsTest.cpp ----------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "llvm/Testing/Support/Annotations.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using ::testing::ElementsAre;
|
||||
using ::testing::IsEmpty;
|
||||
|
||||
namespace {
|
||||
llvm::Annotations::Range range(size_t Begin, size_t End) {
|
||||
llvm::Annotations::Range R;
|
||||
R.Begin = Begin;
|
||||
R.End = End;
|
||||
return R;
|
||||
}
|
||||
|
||||
TEST(AnnotationsTest, CleanedCode) {
|
||||
EXPECT_EQ(llvm::Annotations("foo^bar$nnn[[baz$^[[qux]]]]").code(),
|
||||
"foobarbazqux");
|
||||
}
|
||||
|
||||
TEST(AnnotationsTest, Points) {
|
||||
// A single point.
|
||||
EXPECT_EQ(llvm::Annotations("^ab").point(), 0u);
|
||||
EXPECT_EQ(llvm::Annotations("a^b").point(), 1u);
|
||||
EXPECT_EQ(llvm::Annotations("ab^").point(), 2u);
|
||||
|
||||
// Multiple points.
|
||||
EXPECT_THAT(llvm::Annotations("^a^bc^d^").points(),
|
||||
ElementsAre(0u, 1u, 3u, 4u));
|
||||
|
||||
// No points.
|
||||
EXPECT_THAT(llvm::Annotations("ab[[cd]]").points(), IsEmpty());
|
||||
|
||||
// Consecutive points.
|
||||
EXPECT_THAT(llvm::Annotations("ab^^^cd").points(), ElementsAre(2u, 2u, 2u));
|
||||
}
|
||||
|
||||
TEST(AnnotationsTest, Ranges) {
|
||||
// A single range.
|
||||
EXPECT_EQ(llvm::Annotations("[[a]]bc").range(), range(0, 1));
|
||||
EXPECT_EQ(llvm::Annotations("a[[bc]]d").range(), range(1, 3));
|
||||
EXPECT_EQ(llvm::Annotations("ab[[cd]]").range(), range(2, 4));
|
||||
|
||||
// Empty range.
|
||||
EXPECT_EQ(llvm::Annotations("[[]]ab").range(), range(0, 0));
|
||||
EXPECT_EQ(llvm::Annotations("a[[]]b").range(), range(1, 1));
|
||||
EXPECT_EQ(llvm::Annotations("ab[[]]").range(), range(2, 2));
|
||||
|
||||
// Multiple ranges.
|
||||
EXPECT_THAT(llvm::Annotations("[[a]][[b]]cd[[ef]]ef").ranges(),
|
||||
ElementsAre(range(0, 1), range(1, 2), range(4, 6)));
|
||||
|
||||
// No ranges.
|
||||
EXPECT_THAT(llvm::Annotations("ab^c^defef").ranges(), IsEmpty());
|
||||
}
|
||||
|
||||
TEST(AnnotationsTest, Nested) {
|
||||
llvm::Annotations Annotated("a[[f^oo^bar[[b[[a]]z]]]]bcdef");
|
||||
EXPECT_THAT(Annotated.points(), ElementsAre(2u, 4u));
|
||||
EXPECT_THAT(Annotated.ranges(),
|
||||
ElementsAre(range(8, 9), range(7, 10), range(1, 10)));
|
||||
}
|
||||
|
||||
TEST(AnnotationsTest, Named) {
|
||||
// A single named point or range.
|
||||
EXPECT_EQ(llvm::Annotations("a$foo^b").point("foo"), 1u);
|
||||
EXPECT_EQ(llvm::Annotations("a$foo[[b]]cdef").range("foo"), range(1, 2));
|
||||
|
||||
// Empty names should also work.
|
||||
EXPECT_EQ(llvm::Annotations("a$^b").point(""), 1u);
|
||||
EXPECT_EQ(llvm::Annotations("a$[[b]]cdef").range(""), range(1, 2));
|
||||
|
||||
// Multiple named points.
|
||||
llvm::Annotations Annotated("a$p1^bcd$p2^123$p1^345");
|
||||
EXPECT_THAT(Annotated.points(), IsEmpty());
|
||||
EXPECT_THAT(Annotated.points("p1"), ElementsAre(1u, 7u));
|
||||
EXPECT_EQ(Annotated.point("p2"), 4u);
|
||||
}
|
||||
|
||||
TEST(AnnotationsTest, Errors) {
|
||||
// Annotations use llvm_unreachable, it will only crash in debug mode.
|
||||
#ifndef NDEBUG
|
||||
// point() and range() crash on zero or multiple ranges.
|
||||
EXPECT_DEATH(llvm::Annotations("ab[[c]]def").point(),
|
||||
"expected exactly one point");
|
||||
EXPECT_DEATH(llvm::Annotations("a^b^cdef").point(),
|
||||
"expected exactly one point");
|
||||
|
||||
EXPECT_DEATH(llvm::Annotations("a^bcdef").range(),
|
||||
"expected exactly one range");
|
||||
EXPECT_DEATH(llvm::Annotations("a[[b]]c[[d]]ef").range(),
|
||||
"expected exactly one range");
|
||||
|
||||
EXPECT_DEATH(llvm::Annotations("$foo^a$foo^a").point("foo"),
|
||||
"expected exactly one point");
|
||||
EXPECT_DEATH(llvm::Annotations("$foo[[a]]bc$foo[[a]]").range("foo"),
|
||||
"expected exactly one range");
|
||||
|
||||
// Parsing failures.
|
||||
EXPECT_DEATH(llvm::Annotations("ff[[fdfd"), "unmatched \\[\\[");
|
||||
EXPECT_DEATH(llvm::Annotations("ff[[fdjsfjd]]xxx]]"), "unmatched ]]");
|
||||
EXPECT_DEATH(llvm::Annotations("ff$fdsfd"), "unterminated \\$name");
|
||||
#endif
|
||||
}
|
||||
} // namespace
|
|
@ -5,6 +5,7 @@ set(LLVM_LINK_COMPONENTS
|
|||
add_llvm_unittest(SupportTests
|
||||
AlignOfTest.cpp
|
||||
AllocatorTest.cpp
|
||||
AnnotationsTest.cpp
|
||||
ARMAttributeParser.cpp
|
||||
ArrayRecyclerTest.cpp
|
||||
BinaryStreamTest.cpp
|
||||
|
|
Loading…
Reference in New Issue