diff --git a/llvm/include/llvm/Support/YAMLTraits.h b/llvm/include/llvm/Support/YAMLTraits.h --- a/llvm/include/llvm/Support/YAMLTraits.h +++ b/llvm/include/llvm/Support/YAMLTraits.h @@ -1539,8 +1539,22 @@ std::vector> Entries; }; + class AliasHNode : public HNode { + void anchor() override; + + public: + AliasHNode(Node *N, HNode *Aliased) : HNode(N), Aliased(Aliased) {} + + static bool classof(const HNode *n) { return AliasNode::classof(n->_node); } + + static bool classof(const AliasNode *) { return true; } + + HNode *Aliased; + }; + std::unique_ptr createHNodes(Node *node); void setError(HNode *hnode, const Twine &message); + void setErrorWithExpansion(HNode *Node, const Twine &Message); void setError(Node *node, const Twine &message); void setError(const SMRange &Range, const Twine &message); @@ -1548,6 +1562,11 @@ void reportWarning(Node *hnode, const Twine &message); void reportWarning(const SMRange &Range, const Twine &message); + HNode *ignoreAlias(HNode *N); + HNode *getCurrentIgnoringAlias() { return ignoreAlias(CurrentNode); } + + void diagnoseAliasExpansion(HNode *StartingNode); + public: // These are only used by operator>>. They could be private // if those templated things could be made friends. @@ -1563,6 +1582,7 @@ SourceMgr SrcMgr; // must be before Strm std::unique_ptr Strm; std::unique_ptr TopNode; + llvm::StringMap AnchorMap; std::error_code EC; BumpPtrAllocator StringAllocator; document_iterator DocIterator; diff --git a/llvm/lib/Support/YAMLParser.cpp b/llvm/lib/Support/YAMLParser.cpp --- a/llvm/lib/Support/YAMLParser.cpp +++ b/llvm/lib/Support/YAMLParser.cpp @@ -2455,7 +2455,13 @@ switch (T.Kind) { case Token::TK_Alias: getNext(); - return new (NodeAllocator) AliasNode(stream.CurrentDoc, T.Range.substr(1)); + { + auto *Result = + new (NodeAllocator) AliasNode(stream.CurrentDoc, T.Range.substr(1)); + Result->setSourceRange(SMRange(SMLoc::getFromPointer(T.Range.begin()), + SMLoc::getFromPointer(T.Range.end()))); + return Result; + } case Token::TK_Anchor: if (AnchorInfo.Kind == Token::TK_Anchor) { setError("Already encountered an anchor for this node!", T); diff --git a/llvm/lib/Support/YAMLTraits.cpp b/llvm/lib/Support/YAMLTraits.cpp --- a/llvm/lib/Support/YAMLTraits.cpp +++ b/llvm/lib/Support/YAMLTraits.cpp @@ -81,6 +81,7 @@ void Input::ScalarHNode::anchor() {} void Input::MapHNode::anchor() {} void Input::SequenceHNode::anchor() {} +void Input::AliasHNode::anchor() {} bool Input::outputting() const { return false; @@ -107,6 +108,8 @@ } bool Input::nextDocument() { + // Anchors shouldn't persist between documents. + AnchorMap.clear(); return ++DocIterator != Strm->end(); } @@ -133,17 +136,17 @@ if (EC) return; // CurrentNode can be null if the document is empty. - MapHNode *MN = dyn_cast_or_null(CurrentNode); + MapHNode *MN = dyn_cast_or_null(getCurrentIgnoringAlias()); if (MN) { MN->ValidKeys.clear(); } } std::vector Input::keys() { - MapHNode *MN = dyn_cast(CurrentNode); + MapHNode *MN = dyn_cast(getCurrentIgnoringAlias()); std::vector Ret; if (!MN) { - setError(CurrentNode, "not a mapping"); + setErrorWithExpansion(CurrentNode, "not a mapping"); return Ret; } for (auto &P : MN->Mapping) @@ -165,20 +168,21 @@ return false; } - MapHNode *MN = dyn_cast(CurrentNode); + MapHNode *MN = dyn_cast(getCurrentIgnoringAlias()); if (!MN) { - if (Required || !isa(CurrentNode)) - setError(CurrentNode, "not a mapping"); - else + if (Required || !isa(getCurrentIgnoringAlias())) { + setErrorWithExpansion(CurrentNode, "not a mapping"); + } else UseDefault = true; return false; } MN->ValidKeys.push_back(Key); HNode *Value = MN->Mapping[Key].first.get(); if (!Value) { - if (Required) - setError(CurrentNode, Twine("missing required key '") + Key + "'"); - else + if (Required) { + setErrorWithExpansion(CurrentNode, + Twine("missing required key '") + Key + "'"); + } else UseDefault = true; return false; } @@ -195,7 +199,7 @@ if (EC) return; // CurrentNode can be null if the document is empty. - MapHNode *MN = dyn_cast_or_null(CurrentNode); + MapHNode *MN = dyn_cast_or_null(getCurrentIgnoringAlias()); if (!MN) return; for (const auto &NN : MN->Mapping) { @@ -215,17 +219,17 @@ void Input::endFlowMapping() { endMapping(); } unsigned Input::beginSequence() { - if (SequenceHNode *SQ = dyn_cast(CurrentNode)) + if (SequenceHNode *SQ = dyn_cast(getCurrentIgnoringAlias())) return SQ->Entries.size(); - if (isa(CurrentNode)) + if (isa(getCurrentIgnoringAlias())) return 0; // Treat case where there's a scalar "null" value as an empty sequence. - if (ScalarHNode *SN = dyn_cast(CurrentNode)) { + if (ScalarHNode *SN = dyn_cast(getCurrentIgnoringAlias())) { if (isNull(SN->value())) return 0; } // Any other type of HNode is an error. - setError(CurrentNode, "not a sequence"); + setErrorWithExpansion(CurrentNode, "not a sequence"); return 0; } @@ -235,7 +239,7 @@ bool Input::preflightElement(unsigned Index, void *&SaveInfo) { if (EC) return false; - if (SequenceHNode *SQ = dyn_cast(CurrentNode)) { + if (SequenceHNode *SQ = dyn_cast(getCurrentIgnoringAlias())) { SaveInfo = CurrentNode; CurrentNode = SQ->Entries[Index].get(); return true; @@ -252,7 +256,7 @@ bool Input::preflightFlowElement(unsigned index, void *&SaveInfo) { if (EC) return false; - if (SequenceHNode *SQ = dyn_cast(CurrentNode)) { + if (SequenceHNode *SQ = dyn_cast(getCurrentIgnoringAlias())) { SaveInfo = CurrentNode; CurrentNode = SQ->Entries[index].get(); return true; @@ -274,7 +278,7 @@ bool Input::matchEnumScalar(const char *Str, bool) { if (ScalarMatchFound) return false; - if (ScalarHNode *SN = dyn_cast(CurrentNode)) { + if (ScalarHNode *SN = dyn_cast(getCurrentIgnoringAlias())) { if (SN->value().equals(Str)) { ScalarMatchFound = true; return true; @@ -292,16 +296,16 @@ void Input::endEnumScalar() { if (!ScalarMatchFound) { - setError(CurrentNode, "unknown enumerated scalar"); + setErrorWithExpansion(CurrentNode, "unknown enumerated scalar"); } } bool Input::beginBitSetScalar(bool &DoClear) { BitValuesUsed.clear(); - if (SequenceHNode *SQ = dyn_cast(CurrentNode)) { + if (SequenceHNode *SQ = dyn_cast(getCurrentIgnoringAlias())) { BitValuesUsed.resize(SQ->Entries.size()); } else { - setError(CurrentNode, "expected sequence of bit values"); + setErrorWithExpansion(CurrentNode, "expected sequence of bit values"); } DoClear = true; return true; @@ -310,7 +314,7 @@ bool Input::bitSetMatch(const char *Str, bool) { if (EC) return false; - if (SequenceHNode *SQ = dyn_cast(CurrentNode)) { + if (SequenceHNode *SQ = dyn_cast(getCurrentIgnoringAlias())) { unsigned Index = 0; for (auto &N : SQ->Entries) { if (ScalarHNode *SN = dyn_cast(N.get())) { @@ -319,12 +323,13 @@ return true; } } else { - setError(CurrentNode, "unexpected scalar in sequence of bit values"); + setErrorWithExpansion(CurrentNode, + "unexpected scalar in sequence of bit values"); } ++Index; } } else { - setError(CurrentNode, "expected sequence of bit values"); + setErrorWithExpansion(CurrentNode, "expected sequence of bit values"); } return false; } @@ -332,7 +337,7 @@ void Input::endBitSetScalar() { if (EC) return; - if (SequenceHNode *SQ = dyn_cast(CurrentNode)) { + if (SequenceHNode *SQ = dyn_cast(getCurrentIgnoringAlias())) { assert(BitValuesUsed.size() == SQ->Entries.size()); for (unsigned i = 0; i < SQ->Entries.size(); ++i) { if (!BitValuesUsed[i]) { @@ -344,10 +349,10 @@ } void Input::scalarString(StringRef &S, QuotingType) { - if (ScalarHNode *SN = dyn_cast(CurrentNode)) { + if (ScalarHNode *SN = dyn_cast(getCurrentIgnoringAlias())) { S = SN->value(); } else { - setError(CurrentNode, "unexpected scalar"); + setErrorWithExpansion(CurrentNode, "unexpected scalar"); } } @@ -362,12 +367,18 @@ setError(hnode->_node, message); } +void Input::setErrorWithExpansion(HNode *Node, const Twine &Message) { + setError(Node, Message); + diagnoseAliasExpansion(Node); +} + NodeKind Input::getNodeKind() { - if (isa(CurrentNode)) + auto *IgAlias = getCurrentIgnoringAlias(); + if (isa(IgAlias)) return NodeKind::Scalar; - else if (isa(CurrentNode)) + else if (isa(IgAlias)) return NodeKind::Map; - else if (isa(CurrentNode)) + else if (isa(IgAlias)) return NodeKind::Sequence; llvm_unreachable("Unsupported node kind"); } @@ -395,7 +406,24 @@ Strm->printError(range, message, SourceMgr::DK_Warning); } +void Input::diagnoseAliasExpansion(HNode *StartingNode) { + auto *Alias = ignoreAlias(StartingNode); + if (Alias != StartingNode) + Strm->printError(Alias->_node, "Expanded from alias", SourceMgr::DK_Note); +} + +Input::HNode *Input::ignoreAlias(HNode *N) { + if (auto *Alias = dyn_cast_or_null(N)) + return Alias->Aliased; + return N; +} + std::unique_ptr Input::createHNodes(Node *N) { + auto WrapAnchor = [this, N](std::unique_ptr X) { + if (!N->getAnchor().empty()) + AnchorMap[N->getAnchor()] = X.get(); + return X; + }; SmallString<128> StringStorage; if (ScalarNode *SN = dyn_cast(N)) { StringRef KeyStr = SN->getValue(StringStorage); @@ -403,10 +431,10 @@ // Copy string to permanent storage KeyStr = StringStorage.str().copy(StringAllocator); } - return std::make_unique(N, KeyStr); + return WrapAnchor(std::make_unique(N, KeyStr)); } else if (BlockScalarNode *BSN = dyn_cast(N)) { StringRef ValueCopy = BSN->getValue().copy(StringAllocator); - return std::make_unique(N, ValueCopy); + return WrapAnchor(std::make_unique(N, ValueCopy)); } else if (SequenceNode *SQ = dyn_cast(N)) { auto SQHNode = std::make_unique(N); for (Node &SN : *SQ) { @@ -415,7 +443,7 @@ break; SQHNode->Entries.push_back(std::move(Entry)); } - return std::move(SQHNode); + return WrapAnchor(std::move(SQHNode)); } else if (MappingNode *Map = dyn_cast(N)) { auto mapHNode = std::make_unique(N); for (KeyValueNode &KVN : *Map) { @@ -441,9 +469,17 @@ mapHNode->Mapping[KeyStr] = std::make_pair(std::move(ValueHNode), KeyNode->getSourceRange()); } - return std::move(mapHNode); + return WrapAnchor(std::move(mapHNode)); } else if (isa(N)) { - return std::make_unique(N); + return WrapAnchor(std::make_unique(N)); + } else if (auto *Alias = dyn_cast(N)) { + auto Iter = AnchorMap.find(Alias->getName()); + if (Iter == AnchorMap.end()) { + setError(Alias, "No anchor found named '" + Alias->getName() + "'"); + return nullptr; + } + // Don't wrap anchor here as Alias nodes cannot be anchored. + return std::make_unique(Alias, Iter->getValue()); } else { setError(N, "unknown node kind"); return nullptr; diff --git a/llvm/unittests/Support/YAMLIOTest.cpp b/llvm/unittests/Support/YAMLIOTest.cpp --- a/llvm/unittests/Support/YAMLIOTest.cpp +++ b/llvm/unittests/Support/YAMLIOTest.cpp @@ -49,6 +49,10 @@ }; typedef std::vector FooBarSequence; +static constexpr bool operator==(const FooBar &L, const FooBar &R) { + return L.foo == R.foo && L.bar == R.bar; +} + LLVM_YAML_IS_SEQUENCE_VECTOR(FooBar) struct FooBarContainer { @@ -201,6 +205,44 @@ } } +TEST(YAMLIO, TestAnchorRead) { + FooBarContainer cont; + Input yin(R"(--- +fbs: + - &Anchor # Anchors can be applied to maps. + foo: &Foo 2 # Anchors can be applied to scalars. + bar: 3 + - bar: *Foo + foo: *Foo + - *Anchor + - &Anchor # Anchors can be redefined + foo: &Foo 6 + bar: *Foo + - *Anchor + - &Foo # And redefined to a different type + foo: &Anchor 10 + bar: *Anchor + - *Foo +... +)", + nullptr); + yin >> cont; + + EXPECT_FALSE(yin.error()); + ASSERT_EQ(cont.fbs.size(), 7UL); + EXPECT_EQ(cont.fbs[0].foo, 2); + EXPECT_EQ(cont.fbs[0].bar, 3); + EXPECT_EQ(cont.fbs[1].foo, cont.fbs[1].foo); + EXPECT_EQ(cont.fbs[1].bar, cont.fbs[1].foo); + EXPECT_EQ(cont.fbs[2], cont.fbs[0]); + EXPECT_EQ(cont.fbs[3].foo, 6); + EXPECT_EQ(cont.fbs[3].bar, cont.fbs[3].foo); + EXPECT_EQ(cont.fbs[4], cont.fbs[3]); + EXPECT_EQ(cont.fbs[5].foo, 10); + EXPECT_EQ(cont.fbs[5].bar, cont.fbs[5].foo); + EXPECT_EQ(cont.fbs[6], cont.fbs[5]); +} + // // Test writing then reading back a sequence of mappings //