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
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
========

View File

@ -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);

View File

@ -27,6 +27,13 @@ using llvm::yaml::Hex32;
using llvm::yaml::Hex64;
static void suppressErrorMessages(const llvm::SMDiagnostic &, void *) {
}
//===----------------------------------------------------------------------===//
// 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
//===----------------------------------------------------------------------===//
static void suppressErrorMessages(const llvm::SMDiagnostic &, void *) {
}
//
// Test error handling of unknown enumerated scalar
//