Skip to content

Commit b9f15c1

Browse files
author
git apple-llvm automerger
committed
Merge commit '3181eae561c8' from apple/main into swift/next
2 parents 7e54fc4 + 3181eae commit b9f15c1

File tree

3 files changed

+117
-0
lines changed

3 files changed

+117
-0
lines changed

llvm/include/llvm/Support/JSON.h

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -557,6 +557,75 @@ inline bool Object::erase(StringRef K) {
557557
return M.erase(ObjectKey(K));
558558
}
559559

560+
/// A "cursor" marking a position within a Value.
561+
/// The Value is a tree, and this is the path from the root to the current node.
562+
/// This is used to associate errors with particular subobjects.
563+
class Path {
564+
public:
565+
class Root;
566+
567+
/// Records that the value at the current path is invalid.
568+
/// Message is e.g. "expected number" and becomes part of the final error.
569+
/// This overwrites any previously written error message in the root.
570+
void report(llvm::StringLiteral Message);
571+
572+
/// The root may be treated as a Path.
573+
Path(Root &R) : Parent(nullptr), Seg(&R) {}
574+
/// Derives a path for an array element: this[Index]
575+
Path index(unsigned Index) const { return Path(this, Segment(Index)); }
576+
/// Derives a path for an object field: this.Field
577+
Path field(StringRef Field) const { return Path(this, Segment(Field)); }
578+
579+
private:
580+
/// One element in a JSON path: an object field (.foo) or array index [27].
581+
/// Exception: the root Path encodes a pointer to the Path::Root.
582+
class Segment {
583+
uintptr_t Pointer;
584+
unsigned Offset;
585+
586+
public:
587+
Segment() = default;
588+
Segment(Root *R) : Pointer(reinterpret_cast<uintptr_t>(R)) {}
589+
Segment(llvm::StringRef Field)
590+
: Pointer(reinterpret_cast<uintptr_t>(Field.data())),
591+
Offset(static_cast<unsigned>(Field.size())) {}
592+
Segment(unsigned Index) : Pointer(0), Offset(Index) {}
593+
594+
bool isField() const { return Pointer != 0; }
595+
StringRef field() const {
596+
return StringRef(reinterpret_cast<const char *>(Pointer), Offset);
597+
}
598+
unsigned index() const { return Offset; }
599+
Root *root() const { return reinterpret_cast<Root *>(Pointer); }
600+
};
601+
602+
const Path *Parent;
603+
Segment Seg;
604+
605+
Path(const Path *Parent, Segment S) : Parent(Parent), Seg(S) {}
606+
};
607+
608+
/// The root is the trivial Path to the root value.
609+
/// It also stores the latest reported error and the path where it occurred.
610+
class Path::Root {
611+
llvm::StringRef Name;
612+
llvm::StringLiteral ErrorMessage;
613+
std::vector<Path::Segment> ErrorPath; // Only valid in error state. Reversed.
614+
615+
friend void Path::report(llvm::StringLiteral Message);
616+
617+
public:
618+
Root(llvm::StringRef Name = "") : Name(Name), ErrorMessage("") {}
619+
// No copy/move allowed as there are incoming pointers.
620+
Root(Root &&) = delete;
621+
Root &operator=(Root &&) = delete;
622+
Root(const Root &) = delete;
623+
Root &operator=(const Root &) = delete;
624+
625+
/// Returns the last error reported, or else a generic error.
626+
Error getError() const;
627+
};
628+
560629
// Standard deserializers are provided for primitive types.
561630
// See comments on Value.
562631
inline bool fromJSON(const Value &E, std::string &Out) {

llvm/lib/Support/JSON.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
//===---------------------------------------------------------------------===//
88

99
#include "llvm/Support/JSON.h"
10+
#include "llvm/ADT/STLExtras.h"
1011
#include "llvm/Support/ConvertUTF.h"
12+
#include "llvm/Support/Error.h"
1113
#include "llvm/Support/Format.h"
1214
#include "llvm/Support/raw_ostream.h"
1315
#include <cctype>
@@ -199,6 +201,40 @@ bool operator==(const Value &L, const Value &R) {
199201
llvm_unreachable("Unknown value kind");
200202
}
201203

204+
void Path::report(llvm::StringLiteral Msg) {
205+
// Walk up to the root context, and count the number of segments.
206+
unsigned Count = 0;
207+
const Path *P;
208+
for (P = this; P->Parent != nullptr; P = P->Parent)
209+
++Count;
210+
Path::Root *R = P->Seg.root();
211+
// Fill in the error message and copy the path (in reverse order).
212+
R->ErrorMessage = Msg;
213+
R->ErrorPath.resize(Count);
214+
auto It = R->ErrorPath.begin();
215+
for (P = this; P->Parent != nullptr; P = P->Parent)
216+
*It++ = P->Seg;
217+
}
218+
219+
Error Path::Root::getError() const {
220+
std::string S;
221+
raw_string_ostream OS(S);
222+
OS << (ErrorMessage.empty() ? "invalid JSON contents" : ErrorMessage);
223+
if (ErrorPath.empty()) {
224+
if (!Name.empty())
225+
OS << " when parsing " << Name;
226+
} else {
227+
OS << " at " << (Name.empty() ? "(root)" : Name);
228+
for (const Path::Segment &S : llvm::reverse(ErrorPath)) {
229+
if (S.isField())
230+
OS << '.' << S.field();
231+
else
232+
OS << '[' << S.index() << ']';
233+
}
234+
}
235+
return createStringError(llvm::inconvertibleErrorCode(), OS.str());
236+
}
237+
202238
namespace {
203239
// Simple recursive-descent JSON parser.
204240
class Parser {

llvm/unittests/Support/JSONTest.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include "llvm/Support/JSON.h"
1010
#include "llvm/Support/raw_ostream.h"
11+
#include "llvm/Testing/Support/Error.h"
1112

1213
#include "gmock/gmock.h"
1314
#include "gtest/gtest.h"
@@ -461,6 +462,17 @@ TEST(JSONTest, Stream) {
461462
EXPECT_EQ(Pretty, StreamStuff(2));
462463
}
463464

465+
TEST(JSONTest, Path) {
466+
Path::Root R("foo");
467+
Path P = R, A = P.field("a"), B = P.field("b");
468+
A.index(1).field("c").index(2).report("boom");
469+
EXPECT_THAT_ERROR(R.getError(), FailedWithMessage("boom at foo.a[1].c[2]"));
470+
B.field("d").field("e").report("bam");
471+
EXPECT_THAT_ERROR(R.getError(), FailedWithMessage("bam at foo.b.d.e"));
472+
P.report("oh no");
473+
EXPECT_THAT_ERROR(R.getError(), FailedWithMessage("oh no when parsing foo"));
474+
}
475+
464476
} // namespace
465477
} // namespace json
466478
} // namespace llvm

0 commit comments

Comments
 (0)