summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/YamlIO.rst38
-rw-r--r--include/llvm/Support/YAMLTraits.h49
-rw-r--r--unittests/Support/YAMLIOTest.cpp46
3 files changed, 129 insertions, 4 deletions
diff --git a/docs/YamlIO.rst b/docs/YamlIO.rst
index 5ec0a01f8d..46609a3acd 100644
--- a/docs/YamlIO.rst
+++ b/docs/YamlIO.rst
@@ -647,6 +647,44 @@ mappings, as long as they are convertable.
To check a tag, inside your mapping() method you can use io.mapTag() to specify
what the tag should be. This will also add that tag when writing yaml.
+Validation
+----------
+
+Sometimes in a yaml map, each key/value pair is valid, but the combination is
+not. This is similar to something having no syntax errors, but still having
+semantic errors. To support semantic level checking, YAML I/O allows
+an optional ``validate()`` method in a MappingTraits template specialization.
+
+When parsing yaml, the ``validate()`` method is call *after* all key/values in
+the map have been processed. Any error message returned by the ``validate()``
+method during input will be printed just a like a syntax error would be printed.
+When writing yaml, the ``validate()`` method is called *before* the yaml
+key/values are written. Any error during output will trigger an ``assert()``
+because it is a programming error to have invalid struct values.
+
+
+.. code-block:: c++
+
+ using llvm::yaml::MappingTraits;
+ using llvm::yaml::IO;
+
+ struct Stuff {
+ ...
+ };
+
+ template <>
+ struct MappingTraits<Stuff> {
+ static void mapping(IO &io, Stuff &stuff) {
+ ...
+ }
+ static StringRef validate(IO &io, Stuff &stuff) {
+ // Look at all fields in 'stuff' and if there
+ // are any bad values return a string describing
+ // the error. Otherwise return an empty string.
+ return StringRef();
+ }
+ };
+
Sequence
========
diff --git a/include/llvm/Support/YAMLTraits.h b/include/llvm/Support/YAMLTraits.h
index 27c1393b3a..1716a9d36e 100644
--- a/include/llvm/Support/YAMLTraits.h
+++ b/include/llvm/Support/YAMLTraits.h
@@ -44,6 +44,8 @@ template<class T>
struct MappingTraits {
// Must provide:
// static void mapping(IO &io, T &fields);
+ // Optionally may provide:
+ // static StringRef validate(IO &io, T &fields);
};
@@ -226,6 +228,23 @@ public:
static bool const value = (sizeof(test<MappingTraits<T> >(0)) == 1);
};
+// Test if MappingTraits<T>::validate() is defined on type T.
+template <class T>
+struct has_MappingValidateTraits
+{
+ typedef StringRef (*Signature_validate)(class IO&, T&);
+
+ template <typename U>
+ static char test(SameType<Signature_validate, &U::validate>*);
+
+ template <typename U>
+ static double test(...);
+
+public:
+ static bool const value = (sizeof(test<MappingTraits<T> >(0)) == 1);
+};
+
+
// Test if SequenceTraits<T> is defined on type T.
template <class T>
@@ -309,7 +328,15 @@ struct missingTraits : public llvm::integral_constant<bool,
&& !has_SequenceTraits<T>::value
&& !has_DocumentListTraits<T>::value > {};
+template<typename T>
+struct validatedMappingTraits : public llvm::integral_constant<bool,
+ has_MappingTraits<T>::value
+ && has_MappingValidateTraits<T>::value> {};
+template<typename T>
+struct unvalidatedMappingTraits : public llvm::integral_constant<bool,
+ has_MappingTraits<T>::value
+ && !has_MappingValidateTraits<T>::value> {};
// Base class for Input and Output.
class IO {
public:
@@ -483,7 +510,27 @@ yamlize(IO &io, T &Val, bool) {
template<typename T>
-typename llvm::enable_if_c<has_MappingTraits<T>::value, void>::type
+typename llvm::enable_if_c<validatedMappingTraits<T>::value, void>::type
+yamlize(IO &io, T &Val, bool) {
+ io.beginMapping();
+ if (io.outputting()) {
+ StringRef Err = MappingTraits<T>::validate(io, Val);
+ if (!Err.empty()) {
+ llvm::errs() << Err << "\n";
+ assert(Err.empty() && "invalid struct trying to be written as yaml");
+ }
+ }
+ MappingTraits<T>::mapping(io, Val);
+ if (!io.outputting()) {
+ StringRef Err = MappingTraits<T>::validate(io, Val);
+ if (!Err.empty())
+ io.setError(Err);
+ }
+ io.endMapping();
+}
+
+template<typename T>
+typename llvm::enable_if_c<unvalidatedMappingTraits<T>::value, void>::type
yamlize(IO &io, T &Val, bool) {
io.beginMapping();
MappingTraits<T>::mapping(io, Val);
diff --git a/unittests/Support/YAMLIOTest.cpp b/unittests/Support/YAMLIOTest.cpp
index 07d70459fb..52a8f6b88c 100644
--- a/unittests/Support/YAMLIOTest.cpp
+++ b/unittests/Support/YAMLIOTest.cpp
@@ -27,6 +27,13 @@ using llvm::yaml::Hex32;
using llvm::yaml::Hex64;
+
+
+static void suppressErrorMessages(const llvm::SMDiagnostic &, void *) {
+}
+
+
+
//===----------------------------------------------------------------------===//
// Test MappingTraits
//===----------------------------------------------------------------------===//
@@ -1115,18 +1122,51 @@ TEST(YAMLIO, TestTaggedDocumentsWriteAndRead) {
}
-
//===----------------------------------------------------------------------===//
-// Test error handling
+// Test mapping validation
//===----------------------------------------------------------------------===//
+struct MyValidation {
+ double value;
+};
+LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(MyValidation)
-static void suppressErrorMessages(const llvm::SMDiagnostic &, void *) {
+namespace llvm {
+namespace yaml {
+ template <>
+ struct MappingTraits<MyValidation> {
+ static void mapping(IO &io, MyValidation &d) {
+ io.mapRequired("value", d.value);
+ }
+ static StringRef validate(IO &io, MyValidation &d) {
+ if (d.value < 0)
+ return "negative value";
+ return StringRef();
+ }
+ };
+ }
}
//
+// Test that validate() is called and complains about the negative value.
+//
+TEST(YAMLIO, TestValidatingInput) {
+ std::vector<MyValidation> docList;
+ Input yin("--- \nvalue: 3.0\n"
+ "--- \nvalue: -1.0\n...\n",
+ NULL, suppressErrorMessages);
+ yin >> docList;
+ EXPECT_TRUE(yin.error());
+}
+
+
+//===----------------------------------------------------------------------===//
+// Test error handling
+//===----------------------------------------------------------------------===//
+
+//
// Test error handling of unknown enumerated scalar
//
TEST(YAMLIO, TestColorsReadError) {