diff options
-rw-r--r-- | docs/YamlIO.rst | 38 | ||||
-rw-r--r-- | include/llvm/Support/YAMLTraits.h | 49 | ||||
-rw-r--r-- | unittests/Support/YAMLIOTest.cpp | 46 |
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) { |