YAML I/O add support for validate()

MappingTrait template specializations can now have a validate() method which 
performs semantic checking. For details, see <http://llvm.org/docs/YamlIO.html>.



git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@195286 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Nick Kledzik
2013-11-21 00:28:07 +00:00
parent 25f01786ea
commit 9fd7416b3b
3 changed files with 132 additions and 7 deletions

View File

@@ -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 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. 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 Sequence
======== ========

View File

@@ -44,6 +44,8 @@ template<class T>
struct MappingTraits { struct MappingTraits {
// Must provide: // Must provide:
// static void mapping(IO &io, T &fields); // 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); 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. // Test if SequenceTraits<T> is defined on type T.
template <class T> template <class T>
@@ -309,7 +328,15 @@ struct missingTraits : public llvm::integral_constant<bool,
&& !has_SequenceTraits<T>::value && !has_SequenceTraits<T>::value
&& !has_DocumentListTraits<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. // Base class for Input and Output.
class IO { class IO {
public: public:
@@ -483,7 +510,27 @@ yamlize(IO &io, T &Val, bool) {
template<typename T> 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) { yamlize(IO &io, T &Val, bool) {
io.beginMapping(); io.beginMapping();
MappingTraits<T>::mapping(io, Val); MappingTraits<T>::mapping(io, Val);

View File

@@ -27,6 +27,13 @@ using llvm::yaml::Hex32;
using llvm::yaml::Hex64; using llvm::yaml::Hex64;
static void suppressErrorMessages(const llvm::SMDiagnostic &, void *) {
}
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
// Test MappingTraits // Test MappingTraits
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
@@ -1115,17 +1122,50 @@ TEST(YAMLIO, TestTaggedDocumentsWriteAndRead) {
} }
//===----------------------------------------------------------------------===//
// Test mapping validation
//===----------------------------------------------------------------------===//
struct MyValidation {
double value;
};
LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(MyValidation)
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
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
static void suppressErrorMessages(const llvm::SMDiagnostic &, void *) {
}
// //
// Test error handling of unknown enumerated scalar // Test error handling of unknown enumerated scalar
// //