diff options
| author | Sam McCall <sam.mccall@gmail.com> | 2017-11-28 09:25:09 +0000 |
|---|---|---|
| committer | Sam McCall <sam.mccall@gmail.com> | 2017-11-28 09:25:09 +0000 |
| commit | 1bb272bc747a11f1330caabf9c6262f66191ea0c (patch) | |
| tree | f96d52382f2eebe4d8d801b1e5dfc610df1d9631 | |
| parent | b4f28deda0b3e9a3bf2c092622d5fc3ecc846201 (diff) | |
| download | bcm5719-llvm-1bb272bc747a11f1330caabf9c6262f66191ea0c.tar.gz bcm5719-llvm-1bb272bc747a11f1330caabf9c6262f66191ea0c.zip | |
[clangd] Add missing (but documented!) JSONExpr typed accessors
Summary:
Noticed this when I tried to port the Protocol.h parsers.
And tests for the inspect API, which caught a small bug.
Reviewers: ioeric
Subscribers: ilya-biryukov
Differential Revision: https://reviews.llvm.org/D40399
llvm-svn: 319157
| -rw-r--r-- | clang-tools-extra/clangd/JSONExpr.cpp | 16 | ||||
| -rw-r--r-- | clang-tools-extra/clangd/JSONExpr.h | 103 | ||||
| -rw-r--r-- | clang-tools-extra/unittests/clangd/JSONExprTests.cpp | 44 |
3 files changed, 141 insertions, 22 deletions
diff --git a/clang-tools-extra/clangd/JSONExpr.cpp b/clang-tools-extra/clangd/JSONExpr.cpp index 323bde5ba5e..010fc0c4a78 100644 --- a/clang-tools-extra/clangd/JSONExpr.cpp +++ b/clang-tools-extra/clangd/JSONExpr.cpp @@ -161,7 +161,7 @@ bool Parser::parseExpr(Expr &Out) { } case '[': { Out = json::ary{}; - json::ary &A = *Out.array(); + json::ary &A = *Out.asArray(); eatWhitespace(); if (peek() == ']') { ++P; @@ -185,7 +185,7 @@ bool Parser::parseExpr(Expr &Out) { } case '{': { Out = json::obj{}; - json::obj &O = *Out.object(); + json::obj &O = *Out.asObject(); eatWhitespace(); if (peek() == '}') { ++P; @@ -507,17 +507,17 @@ bool operator==(const Expr &L, const Expr &R) { return false; switch (L.kind()) { case Expr::Null: - return L.null() == R.null(); + return *L.asNull() == *R.asNull(); case Expr::Boolean: - return L.boolean() == R.boolean(); + return *L.asBoolean() == *R.asBoolean(); case Expr::Number: - return L.boolean() == R.boolean(); + return *L.asNumber() == *R.asNumber(); case Expr::String: - return L.string() == R.string(); + return *L.asString() == *R.asString(); case Expr::Array: - return *L.array() == *R.array(); + return *L.asArray() == *R.asArray(); case Expr::Object: - return *L.object() == *R.object(); + return *L.asObject() == *R.asObject(); } llvm_unreachable("Unknown expression kind"); } diff --git a/clang-tools-extra/clangd/JSONExpr.h b/clang-tools-extra/clangd/JSONExpr.h index 33a1f0c7892..b6af3f1fe30 100644 --- a/clang-tools-extra/clangd/JSONExpr.h +++ b/clang-tools-extra/clangd/JSONExpr.h @@ -55,14 +55,14 @@ namespace json { // object (json::obj) // // The kind can be queried directly, or implicitly via the typed accessors: -// if (Optional<StringRef> S = E.string()) +// if (Optional<StringRef> S = E.asString() // assert(E.kind() == Expr::String); // // Array and Object also have typed indexing accessors for easy traversal: // Expected<Expr> E = parse(R"( {"options": {"font": "sans-serif"}} )"); -// if (json::obj* O = E->object()) -// if (json::obj* Opts = O->object("options")) -// if (Optional<StringRef> Font = Opts->string("font")) +// if (json::obj* O = E->asObject()) +// if (json::obj* Opts = O->getObject("options")) +// if (Optional<StringRef> Font = Opts->getString("font")) // assert(Opts->at("font").kind() == Expr::String); // // === Serialization === @@ -166,38 +166,38 @@ public: } // Typed accessors return None/nullptr if the Expr is not of this type. - llvm::Optional<std::nullptr_t> null() const { + llvm::Optional<std::nullptr_t> asNull() const { if (LLVM_LIKELY(Type == T_Null)) return nullptr; return llvm::None; } - llvm::Optional<bool> boolean() const { - if (LLVM_LIKELY(Type == T_Null)) + llvm::Optional<bool> asBoolean() const { + if (LLVM_LIKELY(Type == T_Boolean)) return as<bool>(); return llvm::None; } - llvm::Optional<double> number() const { + llvm::Optional<double> asNumber() const { if (LLVM_LIKELY(Type == T_Number)) return as<double>(); return llvm::None; } - llvm::Optional<llvm::StringRef> string() const { + llvm::Optional<llvm::StringRef> asString() const { if (Type == T_String) return llvm::StringRef(as<std::string>()); if (LLVM_LIKELY(Type == T_StringRef)) return as<llvm::StringRef>(); return llvm::None; } - const ObjectExpr *object() const { + const ObjectExpr *asObject() const { return LLVM_LIKELY(Type == T_Object) ? &as<ObjectExpr>() : nullptr; } - ObjectExpr *object() { + ObjectExpr *asObject() { return LLVM_LIKELY(Type == T_Object) ? &as<ObjectExpr>() : nullptr; } - const ArrayExpr *array() const { + const ArrayExpr *asArray() const { return LLVM_LIKELY(Type == T_Array) ? &as<ArrayExpr>() : nullptr; } - ArrayExpr *array() { + ArrayExpr *asArray() { return LLVM_LIKELY(Type == T_Array) ? &as<ArrayExpr>() : nullptr; } @@ -292,6 +292,63 @@ public: Expr &operator[](ObjectKey &&K) { return emplace(std::move(K), Expr(nullptr)).first->second; } + + // Look up a property, returning nullptr if it doesn't exist. + json::Expr *get(const ObjectKey &K) { + auto I = find(K); + if (I == end()) + return nullptr; + return &I->second; + } + const json::Expr *get(const ObjectKey &K) const { + auto I = find(K); + if (I == end()) + return nullptr; + return &I->second; + } + // Typed accessors return None/nullptr if + // - the property doesn't exist + // - or it has the wrong type + llvm::Optional<std::nullptr_t> getNull(const ObjectKey &K) const { + if (auto *V = get(K)) + return V->asNull(); + return llvm::None; + } + llvm::Optional<bool> getBoolean(const ObjectKey &K) const { + if (auto *V = get(K)) + return V->asBoolean(); + return llvm::None; + } + llvm::Optional<double> getNumber(const ObjectKey &K) const { + if (auto *V = get(K)) + return V->asNumber(); + return llvm::None; + } + llvm::Optional<llvm::StringRef> getString(const ObjectKey &K) const { + if (auto *V = get(K)) + return V->asString(); + return llvm::None; + } + const ObjectExpr *getObject(const ObjectKey &K) const { + if (auto *V = get(K)) + return V->asObject(); + return nullptr; + } + ObjectExpr *getObject(const ObjectKey &K) { + if (auto *V = get(K)) + return V->asObject(); + return nullptr; + } + const ArrayExpr *getArray(const ObjectKey &K) const { + if (auto *V = get(K)) + return V->asArray(); + return nullptr; + } + ArrayExpr *getArray(const ObjectKey &K) { + if (auto *V = get(K)) + return V->asArray(); + return nullptr; + } }; class ArrayExpr : public std::vector<Expr> { @@ -306,6 +363,26 @@ public: for (const auto &V : C) emplace_back(V); } + + // Typed accessors return None/nullptr if the element has the wrong type. + llvm::Optional<std::nullptr_t> getNull(size_t I) const { + return (*this)[I].asNull(); + } + llvm::Optional<bool> getBoolean(size_t I) const { + return (*this)[I].asBoolean(); + } + llvm::Optional<double> getNumber(size_t I) const { + return (*this)[I].asNumber(); + } + llvm::Optional<llvm::StringRef> getString(size_t I) const { + return (*this)[I].asString(); + } + const ObjectExpr *getObject(size_t I) const { + return (*this)[I].asObject(); + } + ObjectExpr *getObject(size_t I) { return (*this)[I].asObject(); } + const ArrayExpr *getArray(size_t I) const { return (*this)[I].asArray(); } + ArrayExpr *getArray(size_t I) { return (*this)[I].asArray(); } }; private: diff --git a/clang-tools-extra/unittests/clangd/JSONExprTests.cpp b/clang-tools-extra/unittests/clangd/JSONExprTests.cpp index d1bcf7a288b..4692aded6f9 100644 --- a/clang-tools-extra/unittests/clangd/JSONExprTests.cpp +++ b/clang-tools-extra/unittests/clangd/JSONExprTests.cpp @@ -167,7 +167,6 @@ TEST(JSONTest, ParseErrors) { ExpectErr("Unexpected EOF", ""); ExpectErr("Unexpected EOF", "["); ExpectErr("Text after end of document", "[][]"); - ExpectErr("Text after end of document", "[][]"); ExpectErr("Invalid bareword", "fuzzy"); ExpectErr("Expected , or ]", "[2?]"); ExpectErr("Expected object key", "{a:2}"); @@ -185,6 +184,49 @@ TEST(JSONTest, ParseErrors) { })"); } +TEST(JSONTest, Inspection) { + llvm::Expected<Expr> Doc = parse(R"( + { + "null": null, + "boolean": false, + "number": 2.78, + "string": "json", + "array": [null, true, 3.14, "hello", [1,2,3], {"time": "arrow"}], + "object": {"fruit": "banana"} + } + )"); + EXPECT_TRUE(!!Doc); + + obj *O = Doc->asObject(); + ASSERT_TRUE(O); + + EXPECT_FALSE(O->getNull("missing")); + EXPECT_FALSE(O->getNull("boolean")); + EXPECT_TRUE(O->getNull("null")); + + EXPECT_EQ(O->getNumber("number"), llvm::Optional<double>(2.78)); + EXPECT_EQ(O->getString("string"), llvm::Optional<llvm::StringRef>("json")); + ASSERT_FALSE(O->getObject("missing")); + ASSERT_FALSE(O->getObject("array")); + ASSERT_TRUE(O->getObject("object")); + EXPECT_EQ(*O->getObject("object"), (obj{{"fruit", "banana"}})); + + ary *A = O->getArray("array"); + ASSERT_TRUE(A); + EXPECT_EQ(A->getBoolean(1), llvm::Optional<bool>(true)); + ASSERT_TRUE(A->getArray(4)); + EXPECT_EQ(*A->getArray(4), (ary{1, 2, 3})); + int I = 0; + for (Expr &E : *A) { + if (I++ == 5) { + ASSERT_TRUE(E.asObject()); + EXPECT_EQ(E.asObject()->getString("time"), + llvm::Optional<llvm::StringRef>("arrow")); + } else + EXPECT_FALSE(E.asObject()); + } +} + } // namespace } // namespace json } // namespace clangd |

