diff --git a/include/cppast/cpp_type.hpp b/include/cppast/cpp_type.hpp index c9bae35..6540d68 100644 --- a/include/cppast/cpp_type.hpp +++ b/include/cppast/cpp_type.hpp @@ -61,6 +61,10 @@ namespace cppast friend detail::intrusive_list_node; }; + /// \returns `true` if the given [cppast::cpp_type]() is valid, `false` otherwise. + /// Invalid types are, for example, references to references. + bool is_valid(const cpp_type& type) noexcept; + /// An unexposed [cppast::cpp_type](). /// /// This is one where no further information besides a name is available. diff --git a/src/cpp_type.cpp b/src/cpp_type.cpp index ee6f603..66c44f2 100644 --- a/src/cpp_type.cpp +++ b/src/cpp_type.cpp @@ -4,8 +4,10 @@ #include +#include #include #include +#include using namespace cppast; @@ -13,3 +15,96 @@ bool detail::cpp_type_ref_predicate::operator()(const cpp_entity& e) { return is_type(e.type()); } + +namespace +{ + bool can_compose(const cpp_type& type) + { + return type.kind() != cpp_type_kind::function + && type.kind() != cpp_type_kind::member_function + && type.kind() != cpp_type_kind::member_object; + } +} + +bool cppast::is_valid(const cpp_type& type) noexcept +{ + switch (type.kind()) + { + case cpp_type_kind::cv_qualified: + { + auto& qual = static_cast(type); + if (qual.type().kind() == cpp_type_kind::reference) + return false; // not allowed + return is_valid(qual.type()); + } + case cpp_type_kind::pointer: + { + auto& ptr = static_cast(type); + if (ptr.pointee().kind() == cpp_type_kind::reference) + return false; // not allowed + return is_valid(ptr.pointee()); + } + case cpp_type_kind::reference: + { + auto& ref = static_cast(type); + if (ref.referee().kind() == cpp_type_kind::member_function + || ref.referee().kind() == cpp_type_kind::member_object) + return false; // not allowed + return is_valid(ref.referee()); + } + + case cpp_type_kind::array: + { + auto& array = static_cast(type); + if (array.value_type().kind() == cpp_type_kind::reference + || !can_compose(array.value_type())) + return false; + return is_valid(array.value_type()); + } + + case cpp_type_kind::function: + { + auto& func = static_cast(type); + + if (!can_compose(func.return_type()) || !is_valid(func.return_type())) + return false; + + for (auto& arg : func.argument_types()) + if (!can_compose(arg) || !is_valid(arg)) + return false; + + return true; + } + case cpp_type_kind::member_function: + { + auto& func = static_cast(type); + + if (func.class_type().kind() != cpp_type_kind::user_defined) + return false; + else if (!can_compose(func.return_type()) || !is_valid(func.return_type())) + return false; + + for (auto& arg : func.argument_types()) + if (!can_compose(arg) || !is_valid(arg)) + return false; + + return true; + } + case cpp_type_kind::member_object: + { + auto& obj = static_cast(type); + + if (obj.class_type().kind() != cpp_type_kind::user_defined) + return false; + return is_valid(obj.object_type()); + } + + case cpp_type_kind::builtin: + case cpp_type_kind::user_defined: + case cpp_type_kind::unexposed: + // no further check required/possible + break; + } + + return true; +}