Implement code generation
This commit is contained in:
parent
c44e0b512e
commit
d18070a799
29 changed files with 2522 additions and 280 deletions
421
include/cppast/code_generator.hpp
Normal file
421
include/cppast/code_generator.hpp
Normal file
|
|
@ -0,0 +1,421 @@
|
|||
// Copyright (C) 2017 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#ifndef CPPAST_CODE_GENERATOR_HPP_INCLUDED
|
||||
#define CPPAST_CODE_GENERATOR_HPP_INCLUDED
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include <type_safe/index.hpp>
|
||||
|
||||
#include <cppast/cpp_entity.hpp>
|
||||
#include <cppast/cpp_entity_ref.hpp>
|
||||
|
||||
namespace cppast
|
||||
{
|
||||
/// A simple string view implementation, like [std::string_view]().
|
||||
///
|
||||
/// It "views" - stores a pointer to - some kind of string.
|
||||
class string_view
|
||||
{
|
||||
public:
|
||||
/// \effects Creates it viewing the [std::string]().
|
||||
string_view(const std::string& str) noexcept : str_(str.c_str()), length_(str.length())
|
||||
{
|
||||
}
|
||||
|
||||
/// \effects Creates it viewing the C string `str`.
|
||||
constexpr string_view(const char* str) noexcept : str_(str), length_(std::strlen(str))
|
||||
{
|
||||
}
|
||||
|
||||
/// \effects Creates it viewing the C string literal.
|
||||
template <std::size_t Size>
|
||||
constexpr string_view(const char (&str)[Size]) noexcept : str_(str), length_(Size - 1u)
|
||||
{
|
||||
}
|
||||
|
||||
/// \returns The number of characters.
|
||||
constexpr std::size_t length() const noexcept
|
||||
{
|
||||
return length_;
|
||||
}
|
||||
|
||||
/// \returns The C string.
|
||||
constexpr const char* c_str() const noexcept
|
||||
{
|
||||
return str_;
|
||||
}
|
||||
|
||||
/// \returns The character at the given index.
|
||||
/// \requires `i < length()`.
|
||||
char operator[](type_safe::index_t i) const noexcept
|
||||
{
|
||||
return type_safe::at(str_, i);
|
||||
}
|
||||
|
||||
private:
|
||||
const char* str_;
|
||||
std::size_t length_;
|
||||
};
|
||||
|
||||
/// \exclude
|
||||
namespace detail
|
||||
{
|
||||
template <typename Tag>
|
||||
class semantic_string_view
|
||||
{
|
||||
public:
|
||||
template <typename T>
|
||||
explicit semantic_string_view(T&& obj,
|
||||
decltype(string_view(std::forward<T>(obj)), 0) = 0)
|
||||
: str_(std::forward<T>(obj))
|
||||
{
|
||||
}
|
||||
|
||||
const string_view& str() const noexcept
|
||||
{
|
||||
return str_;
|
||||
}
|
||||
|
||||
private:
|
||||
string_view str_;
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
/// A special [cppast::string_view]() representing a C++ keyword token.
|
||||
using keyword = detail::semantic_string_view<struct keyword_tag>;
|
||||
|
||||
/// A special [cppast::string_view]() representing a C++ identifier token.
|
||||
using identifier = detail::semantic_string_view<struct identifier_tag>;
|
||||
|
||||
/// A special [cppast::string_view]() representing a C++ string or character literal token.
|
||||
using string_literal = detail::semantic_string_view<struct str_literal_tag>;
|
||||
|
||||
/// A special [cppast::string_view]() representing a C++ integer literal token.
|
||||
using int_literal = detail::semantic_string_view<struct int_literal_tag>;
|
||||
|
||||
/// A special [cppast::string_view]() representing a C++ floating point literal token.
|
||||
using float_literal = detail::semantic_string_view<struct float_literal_tag>;
|
||||
|
||||
/// A special [cppast::string_view]() representing a C++ punctuation token like `.` or `(`.
|
||||
using punctuation = detail::semantic_string_view<struct punctuation_tag>;
|
||||
|
||||
/// A special [cppast::string_view]() representing a C++ preprocessor token.
|
||||
using preprocessor_token = detail::semantic_string_view<struct preprocessor_tag>;
|
||||
|
||||
/// A special [cppast::string_view]() representing a sequence of unknown C++ tokens.
|
||||
using token_seq = detail::semantic_string_view<struct token_seq_tag>;
|
||||
|
||||
/// Tag type to represent an end-of-line character.
|
||||
constexpr struct newl_t
|
||||
{
|
||||
constexpr newl_t() noexcept
|
||||
{
|
||||
}
|
||||
} newl;
|
||||
|
||||
/// Tag type to represent a single space character.
|
||||
constexpr struct whitespace_t
|
||||
{
|
||||
constexpr whitespace_t() noexcept
|
||||
{
|
||||
}
|
||||
} whitespace;
|
||||
|
||||
/// Base class to control the code generation.
|
||||
///
|
||||
/// Inherit from it to customize how a [cppast::cpp_entity]() is printed
|
||||
/// by [cppast::generate_code]().
|
||||
class code_generator
|
||||
{
|
||||
public:
|
||||
code_generator(const code_generator&) = delete;
|
||||
code_generator& operator=(const code_generator&) = delete;
|
||||
virtual ~code_generator() noexcept = default;
|
||||
|
||||
/// Sentinel type used to output a given entity.
|
||||
class output
|
||||
{
|
||||
public:
|
||||
/// \effects Creates it giving the generator, the entity
|
||||
/// and whether or not the entity is a container.
|
||||
/// It is a container if while this object lives
|
||||
/// any other `output` objects are created.
|
||||
///
|
||||
/// It will call `on_container_begin()` or `on_leaf()`,
|
||||
/// respectively.
|
||||
explicit output(type_safe::object_ref<code_generator> gen,
|
||||
type_safe::object_ref<const cpp_entity> e, bool is_container)
|
||||
: gen_(gen), print_(is_container ? gen_->on_container_begin(*e) : gen_->on_leaf(*e))
|
||||
{
|
||||
if (is_container)
|
||||
e_ = e;
|
||||
}
|
||||
|
||||
/// \effects If the entity is a container
|
||||
/// and `on_container_begin()` returned `true`,
|
||||
/// calls `on_container_end()`,
|
||||
/// else does nothing.
|
||||
~output() noexcept
|
||||
{
|
||||
if (print_ && e_)
|
||||
gen_->on_container_end(e_.value());
|
||||
}
|
||||
|
||||
output(const output&) = delete;
|
||||
output& operator=(const output&) = delete;
|
||||
|
||||
/// \returns Whether or not the `on_XXX` function returned `true`.
|
||||
/// \notes If this returns `false`,
|
||||
/// the other functions have no effects.
|
||||
explicit operator bool() const noexcept
|
||||
{
|
||||
return print_;
|
||||
}
|
||||
|
||||
/// \returns A reference to the generator.
|
||||
type_safe::object_ref<code_generator> generator() const noexcept
|
||||
{
|
||||
return gen_;
|
||||
}
|
||||
|
||||
/// \effects Call `do_indent()` followed by `do_write_newline()` (if `print_newline` is `true`).
|
||||
void indent(bool print_newline = true) const noexcept
|
||||
{
|
||||
if (print_)
|
||||
{
|
||||
gen_->do_indent();
|
||||
if (print_newline)
|
||||
gen_->do_write_newline();
|
||||
}
|
||||
}
|
||||
|
||||
/// \effects Calls `do_unindent()`.
|
||||
void unindent() const noexcept
|
||||
{
|
||||
if (print_)
|
||||
gen_->do_unindent();
|
||||
}
|
||||
|
||||
/// \effects Calls `do_write_keyword()`.
|
||||
const output& operator<<(const keyword& k) const
|
||||
{
|
||||
if (print_)
|
||||
gen_->do_write_keyword(k.str());
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// \effects Calls `do_write_identifier()`.
|
||||
const output& operator<<(const identifier& ident) const
|
||||
{
|
||||
if (print_)
|
||||
gen_->do_write_identifier(ident.str());
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// \effects Calls `do_write_reference()`.
|
||||
template <typename T, class Predicate>
|
||||
const output& operator<<(const basic_cpp_entity_ref<T, Predicate>& ref) const
|
||||
{
|
||||
if (print_)
|
||||
gen_->do_write_reference(ref.id(), ref.name());
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// \effects Calls `do_write_punctuation()`.
|
||||
const output& operator<<(const punctuation& punct) const
|
||||
{
|
||||
if (print_)
|
||||
gen_->do_write_punctuation(punct.str());
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// \effects Calls `do_write_str_literal`.
|
||||
const output& operator<<(const string_literal& lit) const
|
||||
{
|
||||
if (print_)
|
||||
gen_->do_write_str_literal(lit.str());
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// \effects Calls `do_write_int_literal()`.
|
||||
const output& operator<<(const int_literal& lit) const
|
||||
{
|
||||
if (print_)
|
||||
gen_->do_write_int_literal(lit.str());
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// \effects Calls `do_write_float_literal()`.
|
||||
const output& operator<<(const float_literal& lit) const
|
||||
{
|
||||
if (print_)
|
||||
gen_->do_write_float_literal(lit.str());
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// \effects Calls `do_write_preprocessor()`.
|
||||
const output& operator<<(const preprocessor_token& tok) const
|
||||
{
|
||||
if (print_)
|
||||
gen_->do_write_preprocessor(tok.str());
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// \effects Calls `do_write_token_seq()`.
|
||||
const output& operator<<(const token_seq& seq) const
|
||||
{
|
||||
if (print_)
|
||||
gen_->do_write_token_seq(seq.str());
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// \effects Calls `do_write_newline()`.
|
||||
const output& operator<<(newl_t) const
|
||||
{
|
||||
if (print_)
|
||||
gen_->do_write_newline();
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// \effects Calls `do_write_whitespace()`.
|
||||
const output& operator<<(whitespace_t) const
|
||||
{
|
||||
if (print_)
|
||||
gen_->do_write_whitespace();
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
type_safe::object_ref<code_generator> gen_;
|
||||
type_safe::optional_ref<const cpp_entity> e_;
|
||||
bool print_;
|
||||
};
|
||||
|
||||
protected:
|
||||
code_generator() noexcept = default;
|
||||
|
||||
private:
|
||||
/// \effects Will be invoked before code of a container entity is generated.
|
||||
/// The base class version has no effect.
|
||||
/// \returns Whether or not that entity should actually be generated.
|
||||
/// The base class version returns `true`.
|
||||
virtual bool on_container_begin(const cpp_entity& e)
|
||||
{
|
||||
(void)e;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// \effects Will be invoked after all code of a container entity has been generated.
|
||||
/// The base class version has no effect.
|
||||
virtual void on_container_end(const cpp_entity& e)
|
||||
{
|
||||
(void)e;
|
||||
}
|
||||
|
||||
/// \effects Will be invoked before code of a non-container entity is generated.
|
||||
/// The base class version has no effect.
|
||||
/// \returns Whether or not that entity should actually be generated.
|
||||
/// The base class version returns `true`.
|
||||
virtual bool on_leaf(const cpp_entity& e)
|
||||
{
|
||||
(void)e;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// \effects Will be invoked when the indentation level should be increased by one.
|
||||
/// The level change must be applied on the next call to `do_write_newline()`.
|
||||
virtual void do_indent() = 0;
|
||||
|
||||
/// \effects Will be invoked when the indentation level should be decreased by one.
|
||||
/// The level change must be applied immediately if nothing else has been written on the current line.
|
||||
virtual void do_unindent() = 0;
|
||||
|
||||
/// \effects Writes the given token sequence.
|
||||
virtual void do_write_token_seq(string_view tokens) = 0;
|
||||
|
||||
/// \effects Writes the specified special token.
|
||||
/// The base class version simply forwards to `do_write_token_seq()`.
|
||||
/// \notes This is useful for syntax highlighting, for example.
|
||||
/// \group write
|
||||
virtual void do_write_keyword(string_view keyword)
|
||||
{
|
||||
do_write_token_seq(keyword);
|
||||
}
|
||||
/// \group write
|
||||
virtual void do_write_identifier(string_view identifier)
|
||||
{
|
||||
do_write_token_seq(identifier);
|
||||
}
|
||||
/// \group write
|
||||
virtual void do_write_reference(type_safe::array_ref<const cpp_entity_id> id,
|
||||
string_view name)
|
||||
{
|
||||
(void)id;
|
||||
do_write_token_seq(name);
|
||||
}
|
||||
/// \group write
|
||||
virtual void do_write_punctuation(string_view punct)
|
||||
{
|
||||
do_write_token_seq(punct);
|
||||
}
|
||||
/// \group write
|
||||
virtual void do_write_str_literal(string_view str)
|
||||
{
|
||||
do_write_token_seq(str);
|
||||
}
|
||||
/// \group write
|
||||
virtual void do_write_int_literal(string_view str)
|
||||
{
|
||||
do_write_token_seq(str);
|
||||
}
|
||||
/// \group write
|
||||
virtual void do_write_float_literal(string_view str)
|
||||
{
|
||||
do_write_token_seq(str);
|
||||
}
|
||||
/// \group write
|
||||
virtual void do_write_preprocessor(string_view punct)
|
||||
{
|
||||
do_write_token_seq(punct);
|
||||
}
|
||||
|
||||
/// \effects Writes a newline.
|
||||
/// It is guaranteed that this is the only way a newline will be printed.
|
||||
/// The base class forwards to `do_write_token_seq()`.
|
||||
virtual void do_write_newline()
|
||||
{
|
||||
do_write_token_seq("\n");
|
||||
}
|
||||
/// \effects Writes a whitespace character.
|
||||
/// It will be invoked only where a whitespace is truly needed,
|
||||
/// like between two keywords.
|
||||
/// The base class forwards to `do_write_token_seq()`.
|
||||
virtual void do_write_whitespace()
|
||||
{
|
||||
do_write_token_seq(" ");
|
||||
}
|
||||
};
|
||||
|
||||
/// Generates code for the given entity.
|
||||
///
|
||||
/// How the code is generated is customized by the generator.
|
||||
/// The implementation will write whitespace only where necessary,
|
||||
/// but a newline after each entity.
|
||||
/// This allows customization of formatting.
|
||||
void generate_code(code_generator& generator, const cpp_entity& e);
|
||||
|
||||
/// \exclude
|
||||
class cpp_template_argument;
|
||||
|
||||
/// \exclude
|
||||
namespace detail
|
||||
{
|
||||
void write_template_arguments(code_generator::output& output,
|
||||
type_safe::array_ref<const cpp_template_argument> arguments);
|
||||
} // namespace detail
|
||||
} // namespace cppast
|
||||
|
||||
#endif // CPPAST_CODE_GENERATOR_HPP_INCLUDED
|
||||
|
|
@ -44,7 +44,7 @@ namespace cppast
|
|||
|
||||
cpp_type_kind do_get_kind() const noexcept override
|
||||
{
|
||||
return cpp_type_kind::array;
|
||||
return cpp_type_kind::array_t;
|
||||
}
|
||||
|
||||
std::unique_ptr<cpp_type> type_;
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ namespace cppast
|
|||
|
||||
cpp_type_kind do_get_kind() const noexcept override
|
||||
{
|
||||
return cpp_type_kind::decltype_;
|
||||
return cpp_type_kind::decltype_t;
|
||||
}
|
||||
|
||||
std::unique_ptr<cpp_expression> expr_;
|
||||
|
|
@ -54,7 +54,7 @@ namespace cppast
|
|||
|
||||
cpp_type_kind do_get_kind() const noexcept override
|
||||
{
|
||||
return cpp_type_kind::decltype_auto;
|
||||
return cpp_type_kind::decltype_auto_t;
|
||||
}
|
||||
};
|
||||
} // namespace cppast
|
||||
|
|
|
|||
|
|
@ -14,9 +14,9 @@ namespace cppast
|
|||
/// The kind of a [cppast::cpp_expression]().
|
||||
enum class cpp_expression_kind
|
||||
{
|
||||
literal,
|
||||
literal_t,
|
||||
|
||||
unexposed,
|
||||
unexposed_t,
|
||||
};
|
||||
|
||||
/// Base class for all C++ expressions.
|
||||
|
|
@ -83,7 +83,7 @@ namespace cppast
|
|||
|
||||
cpp_expression_kind do_get_kind() const noexcept override
|
||||
{
|
||||
return cpp_expression_kind::unexposed;
|
||||
return cpp_expression_kind::unexposed_t;
|
||||
}
|
||||
|
||||
std::string str_;
|
||||
|
|
@ -115,11 +115,17 @@ namespace cppast
|
|||
|
||||
cpp_expression_kind do_get_kind() const noexcept override
|
||||
{
|
||||
return cpp_expression_kind::literal;
|
||||
return cpp_expression_kind::literal_t;
|
||||
}
|
||||
|
||||
std::string value_;
|
||||
};
|
||||
|
||||
/// \exclude
|
||||
namespace detail
|
||||
{
|
||||
void write_expression(code_generator::output& output, const cpp_expression& expr);
|
||||
} // namespace detail
|
||||
} // namespace cppast
|
||||
|
||||
#endif // CPPAST_CPP_EXPRESSION_HPP_INCLUDED
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ namespace cppast
|
|||
|
||||
cpp_type_kind do_get_kind() const noexcept override
|
||||
{
|
||||
return cpp_type_kind::function;
|
||||
return cpp_type_kind::function_t;
|
||||
}
|
||||
|
||||
std::unique_ptr<cpp_type> return_type_;
|
||||
|
|
@ -154,7 +154,7 @@ namespace cppast
|
|||
|
||||
cpp_type_kind do_get_kind() const noexcept override
|
||||
{
|
||||
return cpp_type_kind::member_function;
|
||||
return cpp_type_kind::member_function_t;
|
||||
}
|
||||
|
||||
std::unique_ptr<cpp_type> class_type_, return_type_;
|
||||
|
|
@ -197,7 +197,7 @@ namespace cppast
|
|||
|
||||
cpp_type_kind do_get_kind() const noexcept override
|
||||
{
|
||||
return cpp_type_kind::member_object;
|
||||
return cpp_type_kind::member_object_t;
|
||||
}
|
||||
|
||||
std::unique_ptr<cpp_type> class_type_, object_type_;
|
||||
|
|
|
|||
|
|
@ -165,7 +165,7 @@ namespace cppast
|
|||
|
||||
cpp_type_kind do_get_kind() const noexcept override
|
||||
{
|
||||
return cpp_type_kind::template_instantiation;
|
||||
return cpp_type_kind::template_instantiation_t;
|
||||
}
|
||||
|
||||
type_safe::variant<std::vector<cpp_template_argument>, std::string> arguments_;
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ namespace cppast
|
|||
|
||||
cpp_type_kind do_get_kind() const noexcept override
|
||||
{
|
||||
return cpp_type_kind::template_parameter;
|
||||
return cpp_type_kind::template_parameter_t;
|
||||
}
|
||||
|
||||
cpp_template_type_parameter_ref parameter_;
|
||||
|
|
|
|||
|
|
@ -9,34 +9,35 @@
|
|||
|
||||
#include <cppast/detail/intrusive_list.hpp>
|
||||
#include <cppast/cpp_entity_ref.hpp>
|
||||
#include <cppast/code_generator.hpp>
|
||||
|
||||
namespace cppast
|
||||
{
|
||||
/// The kinds of a [cppast::cpp_type]().
|
||||
enum class cpp_type_kind
|
||||
{
|
||||
builtin,
|
||||
user_defined,
|
||||
builtin_t,
|
||||
user_defined_t,
|
||||
|
||||
auto_,
|
||||
decltype_,
|
||||
decltype_auto,
|
||||
auto_t,
|
||||
decltype_t,
|
||||
decltype_auto_t,
|
||||
|
||||
cv_qualified,
|
||||
pointer,
|
||||
reference,
|
||||
cv_qualified_t,
|
||||
pointer_t,
|
||||
reference_t,
|
||||
|
||||
array,
|
||||
function,
|
||||
member_function,
|
||||
member_object,
|
||||
array_t,
|
||||
function_t,
|
||||
member_function_t,
|
||||
member_object_t,
|
||||
|
||||
template_parameter,
|
||||
template_instantiation,
|
||||
template_parameter_t,
|
||||
template_instantiation_t,
|
||||
|
||||
dependent,
|
||||
dependent_t,
|
||||
|
||||
unexposed,
|
||||
unexposed_t,
|
||||
};
|
||||
|
||||
/// Base class for all C++ types.
|
||||
|
|
@ -95,7 +96,7 @@ namespace cppast
|
|||
|
||||
cpp_type_kind do_get_kind() const noexcept override
|
||||
{
|
||||
return cpp_type_kind::unexposed;
|
||||
return cpp_type_kind::unexposed_t;
|
||||
}
|
||||
|
||||
std::string name_;
|
||||
|
|
@ -163,7 +164,7 @@ namespace cppast
|
|||
|
||||
cpp_type_kind do_get_kind() const noexcept override
|
||||
{
|
||||
return cpp_type_kind::builtin;
|
||||
return cpp_type_kind::builtin_t;
|
||||
}
|
||||
|
||||
cpp_builtin_type_kind kind_;
|
||||
|
|
@ -207,7 +208,7 @@ namespace cppast
|
|||
|
||||
cpp_type_kind do_get_kind() const noexcept override
|
||||
{
|
||||
return cpp_type_kind::user_defined;
|
||||
return cpp_type_kind::user_defined_t;
|
||||
}
|
||||
|
||||
cpp_type_ref entity_;
|
||||
|
|
@ -228,7 +229,7 @@ namespace cppast
|
|||
|
||||
cpp_type_kind do_get_kind() const noexcept override
|
||||
{
|
||||
return cpp_type_kind::auto_;
|
||||
return cpp_type_kind::auto_t;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -269,7 +270,7 @@ namespace cppast
|
|||
|
||||
cpp_type_kind do_get_kind() const noexcept override
|
||||
{
|
||||
return cpp_type_kind::dependent;
|
||||
return cpp_type_kind::dependent_t;
|
||||
}
|
||||
|
||||
std::string name_;
|
||||
|
|
@ -331,7 +332,7 @@ namespace cppast
|
|||
|
||||
cpp_type_kind do_get_kind() const noexcept override
|
||||
{
|
||||
return cpp_type_kind::cv_qualified;
|
||||
return cpp_type_kind::cv_qualified_t;
|
||||
}
|
||||
|
||||
std::unique_ptr<cpp_type> type_;
|
||||
|
|
@ -361,7 +362,7 @@ namespace cppast
|
|||
|
||||
cpp_type_kind do_get_kind() const noexcept override
|
||||
{
|
||||
return cpp_type_kind::pointer;
|
||||
return cpp_type_kind::pointer_t;
|
||||
}
|
||||
|
||||
std::unique_ptr<cpp_type> pointee_;
|
||||
|
|
@ -409,12 +410,31 @@ namespace cppast
|
|||
|
||||
cpp_type_kind do_get_kind() const noexcept override
|
||||
{
|
||||
return cpp_type_kind::reference;
|
||||
return cpp_type_kind::reference_t;
|
||||
}
|
||||
|
||||
std::unique_ptr<cpp_type> referee_;
|
||||
cpp_reference ref_;
|
||||
};
|
||||
|
||||
/// \exclude
|
||||
namespace detail
|
||||
{
|
||||
// whether or not it requires special treatment when printing it
|
||||
// i.e. function pointer types are complex as the identifier has to be put inside
|
||||
bool is_complex_type(const cpp_type& type) noexcept;
|
||||
|
||||
// write part of the type that comes before the variable name
|
||||
void write_type_prefix(code_generator::output& output, const cpp_type& type);
|
||||
|
||||
// write part of the type that comes after the name
|
||||
// for non-complex types, this does nothing
|
||||
void write_type_suffix(code_generator::output& output, const cpp_type& type);
|
||||
|
||||
// write prefix, variadic, name, suffix
|
||||
void write_type(code_generator::output& output, const cpp_type& type, std::string name,
|
||||
bool is_variadic = false);
|
||||
} // namespace detail
|
||||
} // namespace cppast
|
||||
|
||||
#endif // CPPAST_CPP_TYPE_HPP_INCLUDED
|
||||
|
|
|
|||
899
src/code_generator.cpp
Normal file
899
src/code_generator.cpp
Normal file
|
|
@ -0,0 +1,899 @@
|
|||
// Copyright (C) 2017 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#include <cppast/code_generator.hpp>
|
||||
|
||||
#include <cppast/cpp_alias_template.hpp>
|
||||
#include <cppast/cpp_class.hpp>
|
||||
#include <cppast/cpp_class_template.hpp>
|
||||
#include <cppast/cpp_entity_kind.hpp>
|
||||
#include <cppast/cpp_enum.hpp>
|
||||
#include <cppast/cpp_file.hpp>
|
||||
#include <cppast/cpp_function.hpp>
|
||||
#include <cppast/cpp_function_template.hpp>
|
||||
#include <cppast/cpp_language_linkage.hpp>
|
||||
#include <cppast/cpp_member_function.hpp>
|
||||
#include <cppast/cpp_member_variable.hpp>
|
||||
#include <cppast/cpp_namespace.hpp>
|
||||
#include <cppast/cpp_preprocessor.hpp>
|
||||
#include <cppast/cpp_template_parameter.hpp>
|
||||
#include <cppast/cpp_type_alias.hpp>
|
||||
#include <cppast/cpp_variable.hpp>
|
||||
#include <cppast/cpp_variable_template.hpp>
|
||||
|
||||
using namespace cppast;
|
||||
|
||||
namespace
|
||||
{
|
||||
template <typename Sep>
|
||||
auto write_sep(code_generator::output& output, Sep s) -> decltype(output << s)
|
||||
{
|
||||
return output << s;
|
||||
}
|
||||
|
||||
template <typename Sep>
|
||||
auto write_sep(code_generator::output& output, Sep s) -> decltype(s(output))
|
||||
{
|
||||
return s(output);
|
||||
}
|
||||
|
||||
template <class Container, typename Sep>
|
||||
bool write_container(code_generator::output& output, const Container& cont, Sep s)
|
||||
{
|
||||
auto need_sep = false;
|
||||
for (auto& child : cont)
|
||||
{
|
||||
if (need_sep)
|
||||
write_sep(output, s);
|
||||
else
|
||||
need_sep = true;
|
||||
generate_code(*output.generator(), child);
|
||||
}
|
||||
return need_sep;
|
||||
}
|
||||
|
||||
void generate_file(code_generator& generator, const cpp_file& f)
|
||||
{
|
||||
code_generator::output output(type_safe::ref(generator), type_safe::ref(f), true);
|
||||
if (output)
|
||||
{
|
||||
write_container(output, f, newl);
|
||||
output << newl;
|
||||
}
|
||||
}
|
||||
|
||||
void generate_macro_definition(code_generator& generator, const cpp_macro_definition& def)
|
||||
{
|
||||
code_generator::output output(type_safe::ref(generator), type_safe::ref(def), false);
|
||||
if (output)
|
||||
{
|
||||
output << preprocessor_token("#define") << whitespace << identifier(def.name());
|
||||
if (def.is_function_like())
|
||||
output << preprocessor_token("(") << preprocessor_token(def.parameters().value())
|
||||
<< preprocessor_token(")");
|
||||
if (!def.replacement().empty())
|
||||
output << whitespace << preprocessor_token(def.replacement()) << newl;
|
||||
else
|
||||
output << newl;
|
||||
}
|
||||
}
|
||||
|
||||
void generate_include_directive(code_generator& generator, const cpp_include_directive& include)
|
||||
{
|
||||
code_generator::output output(type_safe::ref(generator), type_safe::ref(include), false);
|
||||
if (output)
|
||||
{
|
||||
output << preprocessor_token("#include") << whitespace;
|
||||
if (include.include_kind() == cpp_include_kind::system)
|
||||
output << preprocessor_token("<");
|
||||
else
|
||||
output << preprocessor_token("\"");
|
||||
output << include.target();
|
||||
if (include.include_kind() == cpp_include_kind::system)
|
||||
output << preprocessor_token(">");
|
||||
else
|
||||
output << preprocessor_token("\"");
|
||||
output << newl;
|
||||
}
|
||||
}
|
||||
|
||||
void generate_language_linkage(code_generator& generator, const cpp_language_linkage& linkage)
|
||||
{
|
||||
code_generator::output output(type_safe::ref(generator), type_safe::ref(linkage), true);
|
||||
if (output)
|
||||
{
|
||||
output << keyword("extern") << whitespace << string_literal(linkage.name());
|
||||
if (linkage.is_block())
|
||||
{
|
||||
output << punctuation("{");
|
||||
output.indent();
|
||||
|
||||
write_container(output, linkage, newl);
|
||||
|
||||
output.unindent();
|
||||
output << punctuation("}") << newl;
|
||||
}
|
||||
else
|
||||
{
|
||||
output << whitespace;
|
||||
generate_code(generator, *linkage.begin());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void generate_namespace(code_generator& generator, const cpp_namespace& ns)
|
||||
{
|
||||
code_generator::output output(type_safe::ref(generator), type_safe::ref(ns), true);
|
||||
if (output)
|
||||
{
|
||||
if (ns.is_inline())
|
||||
output << keyword("inline") << whitespace;
|
||||
output << keyword("namespace") << whitespace << identifier(ns.name());
|
||||
output << punctuation("{");
|
||||
output.indent();
|
||||
|
||||
write_container(output, ns, newl);
|
||||
|
||||
output.unindent();
|
||||
output << punctuation("}") << newl;
|
||||
}
|
||||
}
|
||||
|
||||
void generate_namespace_alias(code_generator& generator, const cpp_namespace_alias& alias)
|
||||
{
|
||||
code_generator::output output(type_safe::ref(generator), type_safe::ref(alias), false);
|
||||
if (output)
|
||||
output << keyword("namespace") << whitespace << identifier(alias.name())
|
||||
<< punctuation("=") << alias.target() << punctuation(";") << newl;
|
||||
}
|
||||
|
||||
void generate_using_directive(code_generator& generator, const cpp_using_directive& directive)
|
||||
{
|
||||
code_generator::output output(type_safe::ref(generator), type_safe::ref(directive), false);
|
||||
if (output)
|
||||
output << keyword("using") << whitespace << keyword("namespace") << whitespace
|
||||
<< directive.target() << punctuation(";") << newl;
|
||||
}
|
||||
|
||||
void generate_using_declaration(code_generator& generator,
|
||||
const cpp_using_declaration& declaration)
|
||||
{
|
||||
code_generator::output output(type_safe::ref(generator), type_safe::ref(declaration),
|
||||
false);
|
||||
if (output)
|
||||
output << keyword("using") << whitespace << declaration.target() << punctuation(";")
|
||||
<< newl;
|
||||
}
|
||||
|
||||
void generate_type_alias(code_generator& generator, const cpp_type_alias& alias)
|
||||
{
|
||||
code_generator::output output(type_safe::ref(generator), type_safe::ref(alias), false);
|
||||
if (output)
|
||||
{
|
||||
output << keyword("using") << whitespace << identifier(alias.name())
|
||||
<< punctuation("=");
|
||||
detail::write_type(output, alias.underlying_type(), "");
|
||||
output << punctuation(";") << newl;
|
||||
}
|
||||
}
|
||||
|
||||
void generate_enum_value(code_generator& generator, const cpp_enum_value& value)
|
||||
{
|
||||
code_generator::output output(type_safe::ref(generator), type_safe::ref(value), false);
|
||||
if (output)
|
||||
{
|
||||
output << identifier(value.name());
|
||||
if (value.value())
|
||||
{
|
||||
output << punctuation("=");
|
||||
detail::
|
||||
write_expression(output,
|
||||
value.value()
|
||||
.value()); // should have named something differently...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void generate_enum(code_generator& generator, const cpp_enum& e)
|
||||
{
|
||||
code_generator::output output(type_safe::ref(generator), type_safe::ref(e), true);
|
||||
if (output)
|
||||
{
|
||||
output << keyword("enum");
|
||||
if (e.is_scoped())
|
||||
output << whitespace << keyword("class");
|
||||
output << whitespace << identifier(e.name());
|
||||
if (e.underlying_type())
|
||||
{
|
||||
output << newl << punctuation(":");
|
||||
detail::write_type(output, e.underlying_type().value(), "");
|
||||
}
|
||||
|
||||
if (e.is_definition())
|
||||
{
|
||||
output << punctuation("{");
|
||||
output.indent();
|
||||
|
||||
auto need_sep = write_container(output, e, [](code_generator::output& out) {
|
||||
out << punctuation(",") << newl;
|
||||
});
|
||||
if (need_sep)
|
||||
output << newl;
|
||||
|
||||
output.unindent();
|
||||
output << punctuation("};") << newl;
|
||||
}
|
||||
else
|
||||
output << punctuation(";") << newl;
|
||||
}
|
||||
}
|
||||
|
||||
void generate_access_specifier(code_generator& generator, const cpp_access_specifier& access)
|
||||
{
|
||||
code_generator::output output(type_safe::ref(generator), type_safe::ref(access), false);
|
||||
if (output)
|
||||
{
|
||||
output.unindent();
|
||||
output << keyword(to_string(access.access_specifier())) << punctuation(":");
|
||||
output.indent(false);
|
||||
}
|
||||
}
|
||||
|
||||
void generate_base_class(code_generator& generator, const cpp_base_class& base)
|
||||
{
|
||||
DEBUG_ASSERT(base.parent() && base.parent().value().kind() == cpp_entity_kind::class_t,
|
||||
detail::assert_handler{});
|
||||
auto parent_kind = static_cast<const cpp_class&>(base.parent().value()).class_kind();
|
||||
|
||||
code_generator::output output(type_safe::ref(generator), type_safe::ref(base), false);
|
||||
if (output)
|
||||
{
|
||||
if (base.is_virtual())
|
||||
output << keyword("virtual") << whitespace;
|
||||
|
||||
auto access = base.access_specifier();
|
||||
if (access == cpp_protected)
|
||||
output << keyword("protected") << whitespace;
|
||||
else if (access == cpp_private && parent_kind != cpp_class_kind::class_t)
|
||||
output << keyword("private") << whitespace;
|
||||
else if (access == cpp_public && parent_kind == cpp_class_kind::class_t)
|
||||
output << keyword("public") << whitespace;
|
||||
|
||||
output << identifier(base.name());
|
||||
}
|
||||
}
|
||||
|
||||
void write_specialization_arguments(code_generator::output& output,
|
||||
const cpp_template_specialization& spec)
|
||||
{
|
||||
if (spec.arguments_exposed())
|
||||
detail::write_template_arguments(output, spec.arguments());
|
||||
else if (!spec.unexposed_arguments().empty())
|
||||
output << punctuation("<") << token_seq(spec.unexposed_arguments()) << punctuation(">");
|
||||
}
|
||||
|
||||
void generate_class(code_generator& generator, const cpp_class& c,
|
||||
type_safe::optional_ref<const cpp_template_specialization> spec = nullptr)
|
||||
{
|
||||
code_generator::output output(type_safe::ref(generator), type_safe::ref(c), true);
|
||||
if (output)
|
||||
{
|
||||
output << keyword(to_string(c.class_kind())) << whitespace;
|
||||
|
||||
if (spec)
|
||||
{
|
||||
output << spec.value().primary_template();
|
||||
write_specialization_arguments(output, spec.value());
|
||||
}
|
||||
else
|
||||
output << identifier(c.name());
|
||||
|
||||
if (c.is_final())
|
||||
output << whitespace << keyword("final");
|
||||
|
||||
if (c.is_declaration())
|
||||
output << punctuation(";") << newl;
|
||||
else
|
||||
{
|
||||
if (!c.bases().empty())
|
||||
{
|
||||
output << newl << punctuation(":");
|
||||
|
||||
auto need_sep = false;
|
||||
for (auto& base : c.bases())
|
||||
{
|
||||
if (need_sep)
|
||||
output << punctuation(",");
|
||||
else
|
||||
need_sep = true;
|
||||
generate_base_class(generator, base);
|
||||
}
|
||||
}
|
||||
output << punctuation("{");
|
||||
output.indent();
|
||||
|
||||
auto need_sep = false;
|
||||
auto last_access =
|
||||
c.class_kind() == cpp_class_kind::class_t ? cpp_private : cpp_public;
|
||||
for (auto& member : c)
|
||||
{
|
||||
if (member.kind() == cpp_entity_kind::access_specifier_t)
|
||||
{
|
||||
auto& access = static_cast<const cpp_access_specifier&>(member);
|
||||
if (access.access_specifier() != last_access)
|
||||
{
|
||||
if (need_sep)
|
||||
output << newl;
|
||||
else
|
||||
need_sep = true;
|
||||
generate_access_specifier(generator, access);
|
||||
last_access = access.access_specifier();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (need_sep)
|
||||
output << newl;
|
||||
else
|
||||
need_sep = true;
|
||||
generate_code(generator, member);
|
||||
}
|
||||
}
|
||||
|
||||
output.unindent();
|
||||
output << punctuation("};") << newl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void write_variable_base(code_generator::output& output, const cpp_variable_base& var,
|
||||
const std::string& name)
|
||||
{
|
||||
detail::write_type(output, var.type(), name);
|
||||
|
||||
if (var.default_value())
|
||||
{
|
||||
output << punctuation("=");
|
||||
detail::write_expression(output, var.default_value().value());
|
||||
}
|
||||
}
|
||||
|
||||
void write_storage_class(code_generator::output& output, cpp_storage_class_specifiers storage,
|
||||
bool is_constexpr)
|
||||
{
|
||||
if (is_static(storage))
|
||||
output << keyword("static") << whitespace;
|
||||
if (is_extern(storage))
|
||||
output << keyword("extern") << whitespace;
|
||||
if (is_thread_local(storage))
|
||||
output << keyword("thread_local") << whitespace;
|
||||
if (is_constexpr)
|
||||
output << keyword("constexpr") << whitespace;
|
||||
}
|
||||
|
||||
void generate_variable(code_generator& generator, const cpp_variable& var)
|
||||
{
|
||||
code_generator::output output(type_safe::ref(generator), type_safe::ref(var), false);
|
||||
if (output)
|
||||
{
|
||||
write_storage_class(output, var.storage_class(), var.is_constexpr());
|
||||
|
||||
write_variable_base(output, var, var.name());
|
||||
output << punctuation(";") << newl;
|
||||
}
|
||||
}
|
||||
|
||||
void generate_member_variable(code_generator& generator, const cpp_member_variable& var)
|
||||
{
|
||||
code_generator::output output(type_safe::ref(generator), type_safe::ref(var), false);
|
||||
if (output)
|
||||
{
|
||||
if (var.is_mutable())
|
||||
output << keyword("mutable") << whitespace;
|
||||
write_variable_base(output, var, var.name());
|
||||
output << punctuation(";") << newl;
|
||||
}
|
||||
}
|
||||
|
||||
void generate_bitfield(code_generator& generator, const cpp_bitfield& var)
|
||||
{
|
||||
code_generator::output output(type_safe::ref(generator), type_safe::ref(var), false);
|
||||
if (output)
|
||||
{
|
||||
if (var.is_mutable())
|
||||
output << keyword("mutable") << whitespace;
|
||||
write_variable_base(output, var, var.name());
|
||||
output << punctuation(":") << int_literal(std::to_string(var.no_bits()));
|
||||
output << punctuation(";") << newl;
|
||||
}
|
||||
}
|
||||
|
||||
void generate_function_parameter(code_generator& generator, const cpp_function_parameter& param)
|
||||
{
|
||||
code_generator::output output(type_safe::ref(generator), type_safe::ref(param), false);
|
||||
if (output)
|
||||
write_variable_base(output, param, param.name());
|
||||
}
|
||||
|
||||
void write_function_parameters(code_generator::output& output, const cpp_function_base& base)
|
||||
{
|
||||
output << punctuation("(");
|
||||
auto need_sep = write_container(output, base, punctuation(","));
|
||||
if (base.is_variadic())
|
||||
{
|
||||
if (need_sep)
|
||||
output << punctuation(",");
|
||||
output << punctuation("...");
|
||||
}
|
||||
output << punctuation(")");
|
||||
}
|
||||
|
||||
void write_noexcept(code_generator::output& output, const cpp_function_base& base, bool need_ws)
|
||||
{
|
||||
if (!base.noexcept_condition())
|
||||
return;
|
||||
else if (need_ws)
|
||||
output << whitespace;
|
||||
|
||||
auto& cond = base.noexcept_condition().value();
|
||||
if (cond.kind() == cpp_expression_kind::literal_t
|
||||
&& static_cast<const cpp_literal_expression&>(cond).value() == "true")
|
||||
output << keyword("noexcept");
|
||||
else
|
||||
{
|
||||
output << keyword("noexcept") << punctuation("(");
|
||||
detail::write_expression(output, cond);
|
||||
output << punctuation(")");
|
||||
}
|
||||
}
|
||||
|
||||
void write_function_body(code_generator::output& output, const cpp_function_base& base,
|
||||
bool is_pure_virtual)
|
||||
{
|
||||
switch (base.body_kind())
|
||||
{
|
||||
case cpp_function_declaration:
|
||||
case cpp_function_definition:
|
||||
if (is_pure_virtual)
|
||||
output << punctuation("=") << int_literal("0");
|
||||
output << punctuation(";") << newl;
|
||||
break;
|
||||
|
||||
case cpp_function_defaulted:
|
||||
output << punctuation("=") << keyword("default") << punctuation(";") << newl;
|
||||
break;
|
||||
case cpp_function_deleted:
|
||||
output << punctuation("=") << keyword("delete") << punctuation(";") << newl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void generate_function(
|
||||
code_generator& generator, const cpp_function& func,
|
||||
type_safe::optional_ref<const cpp_template_specialization> spec = nullptr)
|
||||
{
|
||||
code_generator::output output(type_safe::ref(generator), type_safe::ref(func), true);
|
||||
if (output)
|
||||
{
|
||||
if (func.is_friend())
|
||||
output << keyword("friend") << whitespace;
|
||||
write_storage_class(output, func.storage_class(), func.is_constexpr());
|
||||
|
||||
if (detail::is_complex_type(func.return_type()))
|
||||
output << keyword("auto") << whitespace;
|
||||
else
|
||||
{
|
||||
detail::write_type(output, func.return_type(), "");
|
||||
output << whitespace;
|
||||
}
|
||||
|
||||
if (spec)
|
||||
{
|
||||
output << spec.value().primary_template();
|
||||
write_specialization_arguments(output, spec.value());
|
||||
}
|
||||
else
|
||||
output << identifier(func.name());
|
||||
write_function_parameters(output, func);
|
||||
write_noexcept(output, func, false);
|
||||
|
||||
if (detail::is_complex_type(func.return_type()))
|
||||
{
|
||||
output << punctuation("->");
|
||||
detail::write_type(output, func.return_type(), "");
|
||||
}
|
||||
write_function_body(output, func, false);
|
||||
}
|
||||
}
|
||||
|
||||
void write_prefix_virtual(code_generator::output& output, const cpp_virtual& virt)
|
||||
{
|
||||
if (is_virtual(virt))
|
||||
output << keyword("virtual") << whitespace;
|
||||
}
|
||||
|
||||
void write_suffix_virtual(code_generator::output& output, const cpp_virtual& virt)
|
||||
{
|
||||
if (is_overriding(virt))
|
||||
output << whitespace << keyword("override");
|
||||
if (is_final(virt))
|
||||
output << whitespace << keyword("final");
|
||||
}
|
||||
|
||||
bool write_cv_ref(code_generator::output& output, const cpp_member_function_base& base)
|
||||
{
|
||||
auto need_ws = false;
|
||||
switch (base.cv_qualifier())
|
||||
{
|
||||
case cpp_cv_none:
|
||||
break;
|
||||
case cpp_cv_const:
|
||||
output << keyword("const");
|
||||
need_ws = true;
|
||||
break;
|
||||
case cpp_cv_volatile:
|
||||
output << keyword("volatile");
|
||||
need_ws = true;
|
||||
break;
|
||||
case cpp_cv_const_volatile:
|
||||
output << keyword("const") << whitespace << keyword("volatile");
|
||||
need_ws = true;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (base.ref_qualifier())
|
||||
{
|
||||
case cpp_ref_none:
|
||||
break;
|
||||
case cpp_ref_lvalue:
|
||||
output << punctuation("&");
|
||||
need_ws = false;
|
||||
break;
|
||||
case cpp_ref_rvalue:
|
||||
output << punctuation("&&");
|
||||
need_ws = false;
|
||||
break;
|
||||
}
|
||||
|
||||
return need_ws;
|
||||
}
|
||||
|
||||
void generate_member_function(
|
||||
code_generator& generator, const cpp_member_function& func,
|
||||
type_safe::optional_ref<const cpp_template_specialization> spec = nullptr)
|
||||
{
|
||||
code_generator::output output(type_safe::ref(generator), type_safe::ref(func), true);
|
||||
if (output)
|
||||
{
|
||||
if (func.is_constexpr())
|
||||
output << keyword("constexpr") << whitespace;
|
||||
else
|
||||
write_prefix_virtual(output, func.virtual_info());
|
||||
|
||||
if (detail::is_complex_type(func.return_type()))
|
||||
output << keyword("auto") << whitespace;
|
||||
else
|
||||
{
|
||||
detail::write_type(output, func.return_type(), "");
|
||||
output << whitespace;
|
||||
}
|
||||
|
||||
if (spec)
|
||||
{
|
||||
output << spec.value().primary_template();
|
||||
write_specialization_arguments(output, spec.value());
|
||||
}
|
||||
else
|
||||
output << identifier(func.name());
|
||||
write_function_parameters(output, func);
|
||||
auto need_ws = write_cv_ref(output, func);
|
||||
write_noexcept(output, func, need_ws);
|
||||
|
||||
if (detail::is_complex_type(func.return_type()))
|
||||
{
|
||||
output << punctuation("->");
|
||||
detail::write_type(output, func.return_type(), "");
|
||||
}
|
||||
|
||||
write_suffix_virtual(output, func.virtual_info());
|
||||
write_function_body(output, func, is_pure(func.virtual_info()));
|
||||
}
|
||||
}
|
||||
|
||||
void generate_conversion_op(code_generator& generator, const cpp_conversion_op& op)
|
||||
{
|
||||
code_generator::output output(type_safe::ref(generator), type_safe::ref(op), true);
|
||||
if (output)
|
||||
{
|
||||
if (op.is_explicit())
|
||||
output << keyword("explicit") << whitespace;
|
||||
if (op.is_constexpr())
|
||||
output << keyword("constexpr") << whitespace;
|
||||
else
|
||||
write_prefix_virtual(output, op.virtual_info());
|
||||
|
||||
auto pos = op.name().find("operator");
|
||||
output << identifier(op.name().substr(0u, pos)) << keyword("operator") << whitespace;
|
||||
detail::write_type(output, op.return_type(), "");
|
||||
output << punctuation("(") << punctuation(")");
|
||||
auto need_ws = write_cv_ref(output, op);
|
||||
write_noexcept(output, op, need_ws);
|
||||
|
||||
write_suffix_virtual(output, op.virtual_info());
|
||||
write_function_body(output, op, is_pure(op.virtual_info()));
|
||||
}
|
||||
}
|
||||
|
||||
void generate_constructor(code_generator& generator, const cpp_constructor& ctor)
|
||||
{
|
||||
code_generator::output output(type_safe::ref(generator), type_safe::ref(ctor), true);
|
||||
if (output)
|
||||
{
|
||||
if (ctor.is_explicit())
|
||||
output << keyword("explicit") << whitespace;
|
||||
if (ctor.is_constexpr())
|
||||
output << keyword("constexpr") << whitespace;
|
||||
|
||||
output << identifier(ctor.name());
|
||||
write_function_parameters(output, ctor);
|
||||
write_noexcept(output, ctor, false);
|
||||
|
||||
write_function_body(output, ctor, false);
|
||||
}
|
||||
}
|
||||
|
||||
void generate_destructor(code_generator& generator, const cpp_destructor& dtor)
|
||||
{
|
||||
code_generator::output output(type_safe::ref(generator), type_safe::ref(dtor), true);
|
||||
if (output)
|
||||
{
|
||||
write_prefix_virtual(output, dtor.virtual_info());
|
||||
output << identifier(dtor.name()) << punctuation("(") << punctuation(")");
|
||||
write_noexcept(output, dtor, false);
|
||||
|
||||
write_suffix_virtual(output, dtor.virtual_info());
|
||||
write_function_body(output, dtor, is_pure(dtor.virtual_info()));
|
||||
}
|
||||
}
|
||||
|
||||
void generate_function_base(code_generator& generator, const cpp_function_base& base,
|
||||
const cpp_template_specialization& spec)
|
||||
{
|
||||
switch (base.kind())
|
||||
{
|
||||
case cpp_entity_kind::function_t:
|
||||
generate_function(generator, static_cast<const cpp_function&>(base),
|
||||
type_safe::ref(spec));
|
||||
break;
|
||||
case cpp_entity_kind::member_function_t:
|
||||
generate_member_function(generator, static_cast<const cpp_member_function&>(base),
|
||||
type_safe::ref(spec));
|
||||
break;
|
||||
case cpp_entity_kind::conversion_op_t:
|
||||
generate_conversion_op(generator, static_cast<const cpp_conversion_op&>(base));
|
||||
break;
|
||||
case cpp_entity_kind::constructor_t:
|
||||
generate_constructor(generator, static_cast<const cpp_constructor&>(base));
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG_UNREACHABLE(detail::assert_handler{});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void generate_template_type_parameter(code_generator& generator,
|
||||
const cpp_template_type_parameter& param)
|
||||
{
|
||||
code_generator::output output(type_safe::ref(generator), type_safe::ref(param), false);
|
||||
if (output)
|
||||
{
|
||||
output << keyword(to_string(param.keyword()));
|
||||
if (param.is_variadic())
|
||||
output << punctuation("...");
|
||||
if (!param.name().empty())
|
||||
output << whitespace << identifier(param.name());
|
||||
if (param.default_type())
|
||||
{
|
||||
output << punctuation("=");
|
||||
detail::write_type(output, param.default_type().value(), "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void generate_non_type_template_parameter(code_generator& generator,
|
||||
const cpp_non_type_template_parameter& param)
|
||||
{
|
||||
code_generator::output output(type_safe::ref(generator), type_safe::ref(param), false);
|
||||
if (output)
|
||||
{
|
||||
detail::write_type(output, param.type(), param.name(), param.is_variadic());
|
||||
if (param.default_value())
|
||||
{
|
||||
output << punctuation("=");
|
||||
detail::write_expression(output, param.default_value().value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void generate_template_template_parameter(code_generator& generator,
|
||||
const cpp_template_template_parameter& param)
|
||||
{
|
||||
code_generator::output output(type_safe::ref(generator), type_safe::ref(param), true);
|
||||
if (output)
|
||||
{
|
||||
output << keyword("template") << punctuation("<");
|
||||
write_container(output, param, punctuation(","));
|
||||
output << punctuation(">") << keyword(to_string(param.keyword())) << whitespace;
|
||||
if (param.is_variadic())
|
||||
output << punctuation("...");
|
||||
output << identifier(param.name());
|
||||
if (param.default_template())
|
||||
output << punctuation("=") << param.default_template().value();
|
||||
}
|
||||
}
|
||||
|
||||
void write_template_parameters(code_generator::output& output, const cpp_template& templ)
|
||||
{
|
||||
output << keyword("template") << punctuation("<");
|
||||
auto need_sep = false;
|
||||
for (auto& param : templ.parameters())
|
||||
{
|
||||
if (need_sep)
|
||||
output << punctuation(",");
|
||||
else
|
||||
need_sep = true;
|
||||
generate_code(*output.generator(), param);
|
||||
}
|
||||
output << punctuation(">") << newl;
|
||||
}
|
||||
|
||||
void generate_alias_template(code_generator& generator, const cpp_alias_template& alias)
|
||||
{
|
||||
code_generator::output output(type_safe::ref(generator), type_safe::ref(alias), true);
|
||||
if (output)
|
||||
{
|
||||
write_template_parameters(output, alias);
|
||||
generate_code(generator, alias.type_alias());
|
||||
}
|
||||
}
|
||||
|
||||
void generate_variable_template(code_generator& generator, const cpp_variable_template& var)
|
||||
{
|
||||
code_generator::output output(type_safe::ref(generator), type_safe::ref(var), true);
|
||||
if (output)
|
||||
{
|
||||
write_template_parameters(output, var);
|
||||
generate_code(generator, var.variable());
|
||||
}
|
||||
}
|
||||
|
||||
void generate_function_template(code_generator& generator, const cpp_function_template& func)
|
||||
{
|
||||
code_generator::output output(type_safe::ref(generator), type_safe::ref(func), true);
|
||||
if (output)
|
||||
{
|
||||
write_template_parameters(output, func);
|
||||
generate_code(generator, func.function());
|
||||
}
|
||||
}
|
||||
|
||||
void generate_function_template_specialization(code_generator& generator,
|
||||
const cpp_function_template_specialization& func)
|
||||
{
|
||||
code_generator::output output(type_safe::ref(generator), type_safe::ref(func), true);
|
||||
if (output)
|
||||
{
|
||||
write_template_parameters(output, func);
|
||||
generate_function_base(generator, func.function(), func);
|
||||
}
|
||||
}
|
||||
|
||||
void generate_class_template(code_generator& generator, const cpp_class_template& templ)
|
||||
{
|
||||
code_generator::output output(type_safe::ref(generator), type_safe::ref(templ), true);
|
||||
if (output)
|
||||
{
|
||||
write_template_parameters(output, templ);
|
||||
generate_class(generator, templ.class_());
|
||||
}
|
||||
}
|
||||
|
||||
void generate_class_template_specialization(code_generator& generator,
|
||||
const cpp_class_template_specialization& templ)
|
||||
{
|
||||
code_generator::output output(type_safe::ref(generator), type_safe::ref(templ), true);
|
||||
if (output)
|
||||
{
|
||||
write_template_parameters(output, templ);
|
||||
generate_class(generator, templ.class_(), type_safe::ref(templ));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cppast::generate_code(code_generator& generator, const cpp_entity& e)
|
||||
{
|
||||
switch (e.kind())
|
||||
{
|
||||
#define CPPAST_DETAIL_HANDLE(Name) \
|
||||
case cpp_entity_kind::Name##_t: \
|
||||
generate_##Name(generator, static_cast<const cpp_##Name&>(e)); \
|
||||
break;
|
||||
|
||||
CPPAST_DETAIL_HANDLE(file)
|
||||
|
||||
CPPAST_DETAIL_HANDLE(macro_definition)
|
||||
CPPAST_DETAIL_HANDLE(include_directive)
|
||||
|
||||
CPPAST_DETAIL_HANDLE(language_linkage)
|
||||
CPPAST_DETAIL_HANDLE(namespace)
|
||||
CPPAST_DETAIL_HANDLE(namespace_alias)
|
||||
CPPAST_DETAIL_HANDLE(using_directive)
|
||||
CPPAST_DETAIL_HANDLE(using_declaration)
|
||||
|
||||
CPPAST_DETAIL_HANDLE(type_alias)
|
||||
|
||||
CPPAST_DETAIL_HANDLE(enum)
|
||||
CPPAST_DETAIL_HANDLE(enum_value)
|
||||
|
||||
CPPAST_DETAIL_HANDLE(class)
|
||||
CPPAST_DETAIL_HANDLE(access_specifier)
|
||||
CPPAST_DETAIL_HANDLE(base_class)
|
||||
|
||||
CPPAST_DETAIL_HANDLE(variable)
|
||||
CPPAST_DETAIL_HANDLE(member_variable)
|
||||
CPPAST_DETAIL_HANDLE(bitfield)
|
||||
|
||||
CPPAST_DETAIL_HANDLE(function_parameter)
|
||||
CPPAST_DETAIL_HANDLE(function)
|
||||
CPPAST_DETAIL_HANDLE(member_function)
|
||||
CPPAST_DETAIL_HANDLE(conversion_op)
|
||||
CPPAST_DETAIL_HANDLE(constructor)
|
||||
CPPAST_DETAIL_HANDLE(destructor)
|
||||
|
||||
CPPAST_DETAIL_HANDLE(template_type_parameter)
|
||||
CPPAST_DETAIL_HANDLE(non_type_template_parameter)
|
||||
CPPAST_DETAIL_HANDLE(template_template_parameter)
|
||||
|
||||
CPPAST_DETAIL_HANDLE(alias_template)
|
||||
CPPAST_DETAIL_HANDLE(variable_template)
|
||||
CPPAST_DETAIL_HANDLE(function_template)
|
||||
CPPAST_DETAIL_HANDLE(function_template_specialization)
|
||||
CPPAST_DETAIL_HANDLE(class_template)
|
||||
CPPAST_DETAIL_HANDLE(class_template_specialization)
|
||||
|
||||
#undef CPPAST_DETAIL_HANDLE
|
||||
|
||||
case cpp_entity_kind::count:
|
||||
DEBUG_UNREACHABLE(detail::assert_handler{});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void detail::write_template_arguments(code_generator::output& output,
|
||||
type_safe::array_ref<const cpp_template_argument> arguments)
|
||||
{
|
||||
if (arguments.size() == 0u)
|
||||
return;
|
||||
|
||||
output << punctuation("<");
|
||||
auto need_sep = false;
|
||||
for (auto& arg : arguments)
|
||||
{
|
||||
if (need_sep)
|
||||
output << punctuation(",");
|
||||
else
|
||||
need_sep = true;
|
||||
|
||||
if (auto type = arg.type())
|
||||
detail::write_type(output, type.value(), "");
|
||||
else if (auto expr = arg.expression())
|
||||
detail::write_expression(output, expr.value());
|
||||
else if (auto templ = arg.template_ref())
|
||||
output << templ.value();
|
||||
else
|
||||
DEBUG_UNREACHABLE(detail::assert_handler{});
|
||||
}
|
||||
output << punctuation(">");
|
||||
}
|
||||
93
src/cpp_expression.cpp
Normal file
93
src/cpp_expression.cpp
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
// Copyright (C) 2017 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#include <cppast/cpp_expression.hpp>
|
||||
|
||||
using namespace cppast;
|
||||
|
||||
namespace
|
||||
{
|
||||
void write_literal(code_generator::output& output, const cpp_literal_expression& expr)
|
||||
{
|
||||
auto type_kind = cpp_void;
|
||||
if (expr.type().kind() == cpp_type_kind::builtin_t)
|
||||
type_kind = static_cast<const cpp_builtin_type&>(expr.type()).builtin_type_kind();
|
||||
else if (expr.type().kind() == cpp_type_kind::pointer_t)
|
||||
{
|
||||
auto& pointee = static_cast<const cpp_pointer_type&>(expr.type()).pointee();
|
||||
if (pointee.kind() == cpp_type_kind::builtin_t)
|
||||
{
|
||||
auto& builtin_pointee = static_cast<const cpp_builtin_type&>(pointee);
|
||||
if (builtin_pointee.builtin_type_kind() == cpp_char
|
||||
|| builtin_pointee.builtin_type_kind() == cpp_wchar
|
||||
|| builtin_pointee.builtin_type_kind() == cpp_char16
|
||||
|| builtin_pointee.builtin_type_kind() == cpp_char32)
|
||||
// pointer to char aka string
|
||||
type_kind = builtin_pointee.builtin_type_kind();
|
||||
}
|
||||
}
|
||||
|
||||
switch (type_kind)
|
||||
{
|
||||
case cpp_void:
|
||||
output << token_seq(expr.value());
|
||||
break;
|
||||
|
||||
case cpp_bool:
|
||||
output << keyword(expr.value());
|
||||
break;
|
||||
|
||||
case cpp_uchar:
|
||||
case cpp_ushort:
|
||||
case cpp_uint:
|
||||
case cpp_ulong:
|
||||
case cpp_ulonglong:
|
||||
case cpp_uint128:
|
||||
case cpp_schar:
|
||||
case cpp_short:
|
||||
case cpp_int:
|
||||
case cpp_long:
|
||||
case cpp_longlong:
|
||||
case cpp_int128:
|
||||
output << int_literal(expr.value());
|
||||
break;
|
||||
|
||||
case cpp_float:
|
||||
case cpp_double:
|
||||
case cpp_longdouble:
|
||||
case cpp_float128:
|
||||
output << float_literal(expr.value());
|
||||
break;
|
||||
|
||||
case cpp_char:
|
||||
case cpp_wchar:
|
||||
case cpp_char16:
|
||||
case cpp_char32:
|
||||
output << string_literal(expr.value());
|
||||
break;
|
||||
|
||||
case cpp_nullptr:
|
||||
output << keyword(expr.value());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void write_unexposed(code_generator::output& output, const cpp_unexposed_expression& expr)
|
||||
{
|
||||
output << token_seq(expr.expression());
|
||||
}
|
||||
}
|
||||
|
||||
void detail::write_expression(code_generator::output& output, const cpp_expression& expr)
|
||||
{
|
||||
switch (expr.kind())
|
||||
{
|
||||
case cpp_expression_kind::literal_t:
|
||||
write_literal(output, static_cast<const cpp_literal_expression&>(expr));
|
||||
break;
|
||||
case cpp_expression_kind::unexposed_t:
|
||||
write_unexposed(output, static_cast<const cpp_unexposed_expression&>(expr));
|
||||
break;
|
||||
}
|
||||
}
|
||||
380
src/cpp_type.cpp
380
src/cpp_type.cpp
|
|
@ -5,6 +5,7 @@
|
|||
#include <cppast/cpp_type.hpp>
|
||||
|
||||
#include <cppast/cpp_array_type.hpp>
|
||||
#include <cppast/cpp_decltype_type.hpp>
|
||||
#include <cppast/cpp_entity.hpp>
|
||||
#include <cppast/cpp_entity_kind.hpp>
|
||||
#include <cppast/cpp_function_type.hpp>
|
||||
|
|
@ -91,3 +92,382 @@ std::unique_ptr<cpp_dependent_type> cpp_dependent_type::build(
|
|||
return std::unique_ptr<cpp_dependent_type>(
|
||||
new cpp_dependent_type(std::move(name), std::move(dependee)));
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
// is directly a complex type
|
||||
// is_complex_type also checks for children
|
||||
bool is_direct_complex(const cpp_type& type) noexcept
|
||||
{
|
||||
switch (type.kind())
|
||||
{
|
||||
case cpp_type_kind::builtin_t:
|
||||
case cpp_type_kind::user_defined_t:
|
||||
case cpp_type_kind::auto_t:
|
||||
case cpp_type_kind::decltype_t:
|
||||
case cpp_type_kind::decltype_auto_t:
|
||||
case cpp_type_kind::cv_qualified_t:
|
||||
case cpp_type_kind::pointer_t:
|
||||
case cpp_type_kind::reference_t:
|
||||
case cpp_type_kind::template_parameter_t:
|
||||
case cpp_type_kind::template_instantiation_t:
|
||||
case cpp_type_kind::dependent_t:
|
||||
case cpp_type_kind::unexposed_t:
|
||||
return false;
|
||||
|
||||
case cpp_type_kind::array_t:
|
||||
case cpp_type_kind::function_t:
|
||||
case cpp_type_kind::member_function_t:
|
||||
case cpp_type_kind::member_object_t:
|
||||
return true;
|
||||
}
|
||||
|
||||
DEBUG_UNREACHABLE(detail::assert_handler{});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool detail::is_complex_type(const cpp_type& type) noexcept
|
||||
{
|
||||
switch (type.kind())
|
||||
{
|
||||
case cpp_type_kind::cv_qualified_t:
|
||||
return is_complex_type(static_cast<const cpp_cv_qualified_type&>(type).type());
|
||||
case cpp_type_kind::pointer_t:
|
||||
return is_complex_type(static_cast<const cpp_pointer_type&>(type).pointee());
|
||||
case cpp_type_kind::reference_t:
|
||||
return is_complex_type(static_cast<const cpp_reference_type&>(type).referee());
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return is_direct_complex(type);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
void write_builtin(code_generator::output& output, const cpp_builtin_type& type)
|
||||
{
|
||||
output << keyword(to_string(type.builtin_type_kind()));
|
||||
}
|
||||
|
||||
void write_user_defined(code_generator::output& output, const cpp_user_defined_type& type)
|
||||
{
|
||||
output << type.entity();
|
||||
}
|
||||
|
||||
void write_auto(code_generator::output& output, const cpp_auto_type&)
|
||||
{
|
||||
output << keyword("auto");
|
||||
}
|
||||
|
||||
void write_decltype(code_generator::output& output, const cpp_decltype_type& type)
|
||||
{
|
||||
output << keyword("decltype") << punctuation("(");
|
||||
detail::write_expression(output, type.expression());
|
||||
output << punctuation(")");
|
||||
}
|
||||
|
||||
void write_decltype_auto(code_generator::output& output, const cpp_decltype_auto_type&)
|
||||
{
|
||||
output << keyword("decltype") << punctuation("(") << keyword("auto") << punctuation(")");
|
||||
}
|
||||
|
||||
void write_cv_qualified_prefix(code_generator::output& output,
|
||||
const cpp_cv_qualified_type& type)
|
||||
{
|
||||
detail::write_type_prefix(output, type.type());
|
||||
|
||||
if (is_direct_complex(type.type()))
|
||||
output << punctuation("(");
|
||||
|
||||
if (is_const(type.cv_qualifier()))
|
||||
output << whitespace << keyword("const");
|
||||
if (is_volatile(type.cv_qualifier()))
|
||||
output << whitespace << keyword("volatile");
|
||||
}
|
||||
|
||||
void write_cv_qualified_suffix(code_generator::output& output,
|
||||
const cpp_cv_qualified_type& type)
|
||||
{
|
||||
if (is_direct_complex(type.type()))
|
||||
output << punctuation(")");
|
||||
detail::write_type_suffix(output, type.type());
|
||||
}
|
||||
|
||||
bool pointer_requires_paren(const cpp_pointer_type& type)
|
||||
{
|
||||
auto kind = type.pointee().kind();
|
||||
return kind == cpp_type_kind::function_t || kind == cpp_type_kind::array_t;
|
||||
}
|
||||
|
||||
void write_pointer_prefix(code_generator::output& output, const cpp_pointer_type& type)
|
||||
{
|
||||
detail::write_type_prefix(output, type.pointee());
|
||||
if (pointer_requires_paren(type))
|
||||
output << punctuation("(");
|
||||
|
||||
output << punctuation("*");
|
||||
}
|
||||
|
||||
void write_pointer_suffix(code_generator::output& output, const cpp_pointer_type& type)
|
||||
{
|
||||
if (pointer_requires_paren(type))
|
||||
output << punctuation(")");
|
||||
detail::write_type_suffix(output, type.pointee());
|
||||
}
|
||||
|
||||
void write_reference_prefix(code_generator::output& output, const cpp_reference_type& type)
|
||||
{
|
||||
detail::write_type_prefix(output, type.referee());
|
||||
if (is_direct_complex(type.referee()))
|
||||
output << punctuation("(");
|
||||
|
||||
if (type.reference_kind() == cpp_ref_lvalue)
|
||||
output << punctuation("&");
|
||||
else if (type.reference_kind() == cpp_ref_rvalue)
|
||||
output << punctuation("&&");
|
||||
else
|
||||
DEBUG_UNREACHABLE(detail::assert_handler{});
|
||||
}
|
||||
|
||||
void write_reference_suffix(code_generator::output& output, const cpp_reference_type& type)
|
||||
{
|
||||
if (is_direct_complex(type.referee()))
|
||||
output << punctuation(")");
|
||||
detail::write_type_suffix(output, type.referee());
|
||||
}
|
||||
|
||||
void write_array_prefix(code_generator::output& output, const cpp_array_type& type)
|
||||
{
|
||||
detail::write_type_prefix(output, type.value_type());
|
||||
}
|
||||
|
||||
void write_array_suffix(code_generator::output& output, const cpp_array_type& type)
|
||||
{
|
||||
output << punctuation("[");
|
||||
if (type.size())
|
||||
detail::write_expression(output, type.size().value());
|
||||
output << punctuation("]");
|
||||
detail::write_type_suffix(output, type.value_type());
|
||||
}
|
||||
|
||||
void write_function_prefix(code_generator::output& output, const cpp_function_type& type)
|
||||
{
|
||||
detail::write_type_prefix(output, type.return_type());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void write_parameters(code_generator::output& output, const T& type)
|
||||
{
|
||||
output << punctuation("(");
|
||||
|
||||
auto need_sep = false;
|
||||
for (auto& param : type.parameter_types())
|
||||
{
|
||||
if (need_sep)
|
||||
output << punctuation(",");
|
||||
else
|
||||
need_sep = true;
|
||||
detail::write_type_prefix(output, param);
|
||||
detail::write_type_suffix(output, param);
|
||||
}
|
||||
if (type.is_variadic())
|
||||
{
|
||||
if (need_sep)
|
||||
output << punctuation(",");
|
||||
output << punctuation("...");
|
||||
}
|
||||
|
||||
output << punctuation(")");
|
||||
}
|
||||
|
||||
void write_function_suffix(code_generator::output& output, const cpp_function_type& type)
|
||||
{
|
||||
write_parameters(output, type);
|
||||
|
||||
detail::write_type_suffix(output, type.return_type());
|
||||
}
|
||||
|
||||
const cpp_type& strip_class_type(const cpp_type& type, cpp_cv* cv, cpp_reference* ref)
|
||||
{
|
||||
if (type.kind() == cpp_type_kind::cv_qualified_t)
|
||||
{
|
||||
auto& cv_qual = static_cast<const cpp_cv_qualified_type&>(type);
|
||||
if (cv)
|
||||
*cv = cv_qual.cv_qualifier();
|
||||
return strip_class_type(cv_qual.type(), cv, ref);
|
||||
}
|
||||
else if (type.kind() == cpp_type_kind::reference_t)
|
||||
{
|
||||
auto& ref_type = static_cast<const cpp_reference_type&>(type);
|
||||
if (ref)
|
||||
*ref = ref_type.reference_kind();
|
||||
return strip_class_type(ref_type.referee(), cv, ref);
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_ASSERT(!detail::is_complex_type(type), detail::assert_handler{});
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
void write_member_function_prefix(code_generator::output& output,
|
||||
const cpp_member_function_type& type)
|
||||
{
|
||||
detail::write_type_prefix(output, type.return_type());
|
||||
|
||||
output << punctuation("(");
|
||||
detail::write_type_prefix(output, strip_class_type(type.class_type(), nullptr, nullptr));
|
||||
output << punctuation("::");
|
||||
}
|
||||
|
||||
void write_member_function_suffix(code_generator::output& output,
|
||||
const cpp_member_function_type& type)
|
||||
{
|
||||
output << punctuation(")");
|
||||
write_parameters(output, type);
|
||||
|
||||
auto cv = cpp_cv_none;
|
||||
auto ref = cpp_ref_none;
|
||||
strip_class_type(type.class_type(), &cv, &ref);
|
||||
|
||||
if (cv == cpp_cv_const_volatile)
|
||||
output << keyword("const") << whitespace << keyword("volatile");
|
||||
else if (is_const(cv))
|
||||
output << keyword("const");
|
||||
else if (is_volatile(cv))
|
||||
output << keyword("volatile");
|
||||
|
||||
if (ref == cpp_ref_lvalue)
|
||||
output << punctuation("&");
|
||||
else if (ref == cpp_ref_rvalue)
|
||||
output << punctuation("&&");
|
||||
|
||||
detail::write_type_suffix(output, type.return_type());
|
||||
}
|
||||
|
||||
void write_member_object_prefix(code_generator::output& output,
|
||||
const cpp_member_object_type& type)
|
||||
{
|
||||
detail::write_type_prefix(output, type.object_type());
|
||||
output << punctuation("(");
|
||||
DEBUG_ASSERT(!detail::is_complex_type(type.class_type()), detail::assert_handler{});
|
||||
detail::write_type_prefix(output, type.class_type());
|
||||
output << punctuation("::");
|
||||
}
|
||||
|
||||
void write_member_object_suffix(code_generator::output& output, const cpp_member_object_type&)
|
||||
{
|
||||
output << punctuation(")");
|
||||
}
|
||||
|
||||
void write_template_parameter(code_generator::output& output,
|
||||
const cpp_template_parameter_type& type)
|
||||
{
|
||||
output << type.entity();
|
||||
}
|
||||
|
||||
void write_template_instantiation(code_generator::output& output,
|
||||
const cpp_template_instantiation_type& type)
|
||||
{
|
||||
output << type.primary_template();
|
||||
if (type.arguments_exposed())
|
||||
detail::write_template_arguments(output, type.arguments());
|
||||
else
|
||||
output << punctuation("<") << token_seq(type.unexposed_arguments()) << punctuation(">");
|
||||
}
|
||||
|
||||
void write_dependent(code_generator::output& output, const cpp_dependent_type& type)
|
||||
{
|
||||
output << token_seq(type.name());
|
||||
}
|
||||
|
||||
void write_unexposed(code_generator::output& output, const cpp_unexposed_type& type)
|
||||
{
|
||||
output << token_seq(type.name());
|
||||
}
|
||||
}
|
||||
|
||||
void detail::write_type_prefix(code_generator::output& output, const cpp_type& type)
|
||||
{
|
||||
switch (type.kind())
|
||||
{
|
||||
#define CPPAST_DETAIL_HANDLE(Name) \
|
||||
case cpp_type_kind::Name##_t: \
|
||||
write_##Name(output, static_cast<const cpp_##Name##_type&>(type)); \
|
||||
break;
|
||||
|
||||
#define CPPAST_DETAIL_HANDLE_COMPLEX(Name) \
|
||||
case cpp_type_kind::Name##_t: \
|
||||
write_##Name##_prefix(output, static_cast<const cpp_##Name##_type&>(type)); \
|
||||
break;
|
||||
|
||||
CPPAST_DETAIL_HANDLE(builtin)
|
||||
CPPAST_DETAIL_HANDLE(user_defined)
|
||||
CPPAST_DETAIL_HANDLE(auto)
|
||||
CPPAST_DETAIL_HANDLE(decltype)
|
||||
CPPAST_DETAIL_HANDLE(decltype_auto)
|
||||
CPPAST_DETAIL_HANDLE_COMPLEX(cv_qualified)
|
||||
CPPAST_DETAIL_HANDLE_COMPLEX(pointer)
|
||||
CPPAST_DETAIL_HANDLE_COMPLEX(reference)
|
||||
CPPAST_DETAIL_HANDLE_COMPLEX(array)
|
||||
CPPAST_DETAIL_HANDLE_COMPLEX(function)
|
||||
CPPAST_DETAIL_HANDLE_COMPLEX(member_function)
|
||||
CPPAST_DETAIL_HANDLE_COMPLEX(member_object)
|
||||
CPPAST_DETAIL_HANDLE(template_parameter)
|
||||
CPPAST_DETAIL_HANDLE(template_instantiation)
|
||||
CPPAST_DETAIL_HANDLE(dependent)
|
||||
CPPAST_DETAIL_HANDLE(unexposed)
|
||||
}
|
||||
|
||||
#undef CPPAST_DETAIL_HANDLE
|
||||
#undef CPPAST_DETAIL_HANDLE_COMPLEX
|
||||
}
|
||||
|
||||
void detail::write_type_suffix(code_generator::output& output, const cpp_type& type)
|
||||
{
|
||||
switch (type.kind())
|
||||
{
|
||||
#define CPPAST_DETAIL_HANDLE(Name) \
|
||||
case cpp_type_kind::Name##_t: \
|
||||
break;
|
||||
|
||||
#define CPPAST_DETAIL_HANDLE_COMPLEX(Name) \
|
||||
case cpp_type_kind::Name##_t: \
|
||||
write_##Name##_suffix(output, static_cast<const cpp_##Name##_type&>(type)); \
|
||||
break;
|
||||
|
||||
CPPAST_DETAIL_HANDLE(builtin)
|
||||
CPPAST_DETAIL_HANDLE(user_defined)
|
||||
CPPAST_DETAIL_HANDLE(auto)
|
||||
CPPAST_DETAIL_HANDLE(decltype)
|
||||
CPPAST_DETAIL_HANDLE(decltype_auto)
|
||||
CPPAST_DETAIL_HANDLE_COMPLEX(cv_qualified)
|
||||
CPPAST_DETAIL_HANDLE_COMPLEX(pointer)
|
||||
CPPAST_DETAIL_HANDLE_COMPLEX(reference)
|
||||
CPPAST_DETAIL_HANDLE_COMPLEX(array)
|
||||
CPPAST_DETAIL_HANDLE_COMPLEX(function)
|
||||
CPPAST_DETAIL_HANDLE_COMPLEX(member_function)
|
||||
CPPAST_DETAIL_HANDLE_COMPLEX(member_object)
|
||||
CPPAST_DETAIL_HANDLE(template_parameter)
|
||||
CPPAST_DETAIL_HANDLE(template_instantiation)
|
||||
CPPAST_DETAIL_HANDLE(dependent)
|
||||
CPPAST_DETAIL_HANDLE(unexposed)
|
||||
}
|
||||
|
||||
#undef CPPAST_DETAIL_HANDLE
|
||||
#undef CPPAST_DETAIL_HANDLE_COMPLEX
|
||||
}
|
||||
|
||||
void detail::write_type(code_generator::output& output, const cpp_type& type, std::string name,
|
||||
bool is_variadic)
|
||||
{
|
||||
write_type_prefix(output, type);
|
||||
if (!name.empty())
|
||||
output << whitespace << identifier(name);
|
||||
if (is_variadic)
|
||||
output << punctuation("...");
|
||||
write_type_suffix(output, type);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ namespace
|
|||
{
|
||||
auto name = detail::get_cursor_name(cur);
|
||||
auto type = detail::parse_type(context, cur, clang_getCursorType(cur));
|
||||
auto is_decltype = type->kind() == cpp_type_kind::decltype_;
|
||||
auto is_decltype = type->kind() == cpp_type_kind::decltype_t;
|
||||
|
||||
std::unique_ptr<cpp_expression> default_value;
|
||||
detail::visit_children(cur, [&](const CXCursor& child) {
|
||||
|
|
|
|||
|
|
@ -299,13 +299,13 @@ namespace
|
|||
return result;
|
||||
}
|
||||
|
||||
bool bump_c_comment(position& p, detail::preprocessor_output& output)
|
||||
bool bump_c_comment(position& p, detail::preprocessor_output& output, bool in_main_file)
|
||||
{
|
||||
if (!starts_with(p, "/*"))
|
||||
return false;
|
||||
p.bump(2u);
|
||||
|
||||
if (starts_with(p, "*"))
|
||||
if (in_main_file && starts_with(p, "*"))
|
||||
{
|
||||
// doc comment
|
||||
p.bump();
|
||||
|
|
@ -371,20 +371,20 @@ namespace
|
|||
}
|
||||
}
|
||||
|
||||
bool bump_cpp_comment(position& p, detail::preprocessor_output& output)
|
||||
bool bump_cpp_comment(position& p, detail::preprocessor_output& output, bool in_main_file)
|
||||
{
|
||||
if (!starts_with(p, "//"))
|
||||
return false;
|
||||
p.bump(2u);
|
||||
|
||||
if (starts_with(p, "/") || starts_with(p, "!"))
|
||||
if (in_main_file && (starts_with(p, "/") || starts_with(p, "!")))
|
||||
{
|
||||
// C++ style doc comment
|
||||
p.bump();
|
||||
auto comment = parse_cpp_doc_comment(p, false);
|
||||
merge_or_add(output, std::move(comment));
|
||||
}
|
||||
else if (starts_with(p, "<"))
|
||||
else if (in_main_file && starts_with(p, "<"))
|
||||
{
|
||||
// end of line doc comment
|
||||
p.bump();
|
||||
|
|
@ -408,7 +408,8 @@ namespace
|
|||
}
|
||||
|
||||
std::unique_ptr<cpp_macro_definition> parse_macro(position& p,
|
||||
detail::preprocessor_output& output)
|
||||
detail::preprocessor_output& output,
|
||||
bool in_main_file)
|
||||
{
|
||||
// format (at new line): #define <name> [replacement]
|
||||
// or: #define <name>(<args>) [replacement]
|
||||
|
|
@ -446,6 +447,9 @@ namespace
|
|||
}
|
||||
// don't skip newline
|
||||
|
||||
if (!in_main_file)
|
||||
return nullptr;
|
||||
|
||||
auto result = cpp_macro_definition::build(std::move(name), std::move(args), std::move(rep));
|
||||
// match comment directly
|
||||
if (!output.comments.empty() && output.comments.back().matches(*result, p.cur_line()))
|
||||
|
|
@ -472,7 +476,8 @@ namespace
|
|||
}
|
||||
|
||||
std::unique_ptr<cpp_include_directive> parse_include(position& p,
|
||||
detail::preprocessor_output& output)
|
||||
detail::preprocessor_output& output,
|
||||
bool in_main_file)
|
||||
{
|
||||
// format (at new line, literal <>): #include <filename>
|
||||
// or: #include "filename"
|
||||
|
|
@ -505,6 +510,9 @@ namespace
|
|||
DEBUG_ASSERT(starts_with(p, "\n"), detail::assert_handler{});
|
||||
// don't skip newline
|
||||
|
||||
if (!in_main_file)
|
||||
return nullptr;
|
||||
|
||||
auto result = cpp_include_directive::build(cpp_file_ref(cpp_entity_id(filename), filename),
|
||||
include_kind);
|
||||
if (!output.comments.empty() && output.comments.back().matches(*result, p.cur_line()))
|
||||
|
|
@ -614,10 +622,9 @@ detail::preprocessor_output detail::preprocess(const libclang_compile_config& co
|
|||
std::size_t file_depth = 0u;
|
||||
while (p)
|
||||
{
|
||||
if (auto macro = parse_macro(p, result))
|
||||
if (auto macro = parse_macro(p, result, file_depth == 0u))
|
||||
{
|
||||
if (file_depth == 0u)
|
||||
result.entities.push_back({std::move(macro), p.cur_line()});
|
||||
result.entities.push_back({std::move(macro), p.cur_line()});
|
||||
}
|
||||
else if (auto undef = parse_undef(p))
|
||||
{
|
||||
|
|
@ -631,7 +638,7 @@ detail::preprocessor_output detail::preprocess(const libclang_compile_config& co
|
|||
}),
|
||||
result.entities.end());
|
||||
}
|
||||
else if (auto include = parse_include(p, result))
|
||||
else if (auto include = parse_include(p, result, file_depth == 0u))
|
||||
{
|
||||
if (file_depth == 0u)
|
||||
result.entities.push_back({std::move(include), p.cur_line()});
|
||||
|
|
@ -669,9 +676,9 @@ detail::preprocessor_output detail::preprocess(const libclang_compile_config& co
|
|||
break;
|
||||
}
|
||||
}
|
||||
else if (bump_c_comment(p, result))
|
||||
else if (bump_c_comment(p, result, file_depth == 0u))
|
||||
continue;
|
||||
else if (bump_cpp_comment(p, result))
|
||||
else if (bump_cpp_comment(p, result, file_depth == 0u))
|
||||
continue;
|
||||
else
|
||||
p.bump();
|
||||
|
|
|
|||
39
test/code_generator.cpp
Normal file
39
test/code_generator.cpp
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
// Copyright (C) 2017 Jonathan Müller <jonathanmueller.dev@gmail.com>
|
||||
// This file is subject to the license terms in the LICENSE file
|
||||
// found in the top-level directory of this distribution.
|
||||
|
||||
#include <cppast/code_generator.hpp>
|
||||
|
||||
#include "test_parser.hpp"
|
||||
|
||||
TEST_CASE("code_generator")
|
||||
{
|
||||
// no need to check much here, as each entity check separately
|
||||
// only write some file with equivalent code and synopsis
|
||||
auto code = R"(using type=int;
|
||||
|
||||
struct foo{
|
||||
int a;
|
||||
|
||||
auto func(int)->int(*)[];
|
||||
|
||||
private:
|
||||
int const b=42;
|
||||
};
|
||||
|
||||
int(*(foo::* mptr)(int))[];
|
||||
|
||||
enum class bar
|
||||
:int{
|
||||
a,
|
||||
b=42
|
||||
};
|
||||
|
||||
void func(int(*)(int));
|
||||
|
||||
extern void(* ptr)(int(*)(int))=&func;
|
||||
)";
|
||||
|
||||
auto file = parse({}, "code_generator.cpp", code);
|
||||
REQUIRE(get_code(*file) == code);
|
||||
}
|
||||
|
|
@ -12,27 +12,43 @@ TEST_CASE("cpp_alias_template")
|
|||
{
|
||||
// no need to check advanced types here nor template parameters
|
||||
auto code = R"(
|
||||
/// template<typename T>
|
||||
/// using a=int;
|
||||
template <typename T>
|
||||
using a = int;
|
||||
|
||||
/// template<int I,typename T=void>
|
||||
/// using b=T;
|
||||
template <int I, typename T = void>
|
||||
using b = T;
|
||||
|
||||
/// template<typename T>
|
||||
/// using c=T const*;
|
||||
template <typename T>
|
||||
using c = const T*;
|
||||
|
||||
/// template<typename T>
|
||||
/// using d=a<void>;
|
||||
template <typename T>
|
||||
using d = a<void>;
|
||||
|
||||
/// template<int I>
|
||||
/// using e=b<I> const;
|
||||
template <int I>
|
||||
using e = const b<I>;
|
||||
|
||||
/// template<int I>
|
||||
/// using f=b<I < a<int>{(0 , 1)}, int>;
|
||||
template <int I>
|
||||
using f = b<I < a<int>{(0,1)}, int>;
|
||||
|
||||
/// template<typename T,template<typename>class Templ>
|
||||
/// using g=Templ<T>;
|
||||
template <typename T, template <typename> class Templ>
|
||||
using g = Templ<T>;
|
||||
|
||||
/// template<typename T>
|
||||
/// using h=g<T, a>;
|
||||
template <typename T>
|
||||
using h = g<T, a>;
|
||||
)";
|
||||
|
|
|
|||
|
|
@ -12,16 +12,40 @@ TEST_CASE("cpp_class")
|
|||
{
|
||||
auto code = R"(
|
||||
// forward declarations
|
||||
/// struct a;
|
||||
struct a;
|
||||
/// class b;
|
||||
class b;
|
||||
/// struct unresolved;
|
||||
struct unresolved;
|
||||
|
||||
// basic
|
||||
/// struct a{
|
||||
/// };
|
||||
struct a {};
|
||||
/// class b final{
|
||||
/// };
|
||||
class b final {};
|
||||
/// union c{
|
||||
/// };
|
||||
union c {};
|
||||
|
||||
// members
|
||||
/// struct d{
|
||||
/// enum m1{
|
||||
/// };
|
||||
///
|
||||
/// enum m2{
|
||||
/// };
|
||||
///
|
||||
/// private:
|
||||
/// enum m3{
|
||||
/// };
|
||||
///
|
||||
/// protected:
|
||||
/// enum m4{
|
||||
/// };
|
||||
/// };
|
||||
struct d
|
||||
{
|
||||
enum m1 {};
|
||||
|
|
@ -38,20 +62,31 @@ protected:
|
|||
};
|
||||
|
||||
// bases
|
||||
/// class e
|
||||
/// :a,d{
|
||||
/// };
|
||||
class e
|
||||
: a, private d {};
|
||||
|
||||
namespace ns
|
||||
{
|
||||
/// struct base{
|
||||
/// };
|
||||
struct base {};
|
||||
}
|
||||
|
||||
/// struct f
|
||||
/// :ns::base,virtual protected e{
|
||||
/// };
|
||||
struct f
|
||||
: public ns::base, virtual protected e
|
||||
{};
|
||||
|
||||
using namespace ns;
|
||||
|
||||
/// struct g
|
||||
/// :base{
|
||||
/// };
|
||||
struct g
|
||||
: base {};
|
||||
)";
|
||||
|
|
|
|||
|
|
@ -16,20 +16,45 @@ TEST_CASE("cpp_class_template")
|
|||
{
|
||||
auto code = R"(
|
||||
// check everything not related to members first
|
||||
/// template<typename T>
|
||||
/// class a{
|
||||
/// };
|
||||
template <typename T>
|
||||
class a {};
|
||||
|
||||
/// template<int I,typename T>
|
||||
/// struct b{
|
||||
/// };
|
||||
template <int I, typename T>
|
||||
struct b {};
|
||||
|
||||
/// template<template<typename>class T>
|
||||
/// union c;
|
||||
template <template <typename> class T>
|
||||
union c;
|
||||
|
||||
// bases
|
||||
/// template<typename T>
|
||||
/// struct d
|
||||
/// :T,a<T>,T::type,a<T>::type{
|
||||
/// };
|
||||
template <typename T>
|
||||
struct d : T, a<T>, T::type, a<T>::type {};
|
||||
|
||||
// members
|
||||
/// template<typename T>
|
||||
/// struct e{
|
||||
/// T var_a;
|
||||
///
|
||||
/// a<T> var_b;
|
||||
///
|
||||
/// typename T::type var_c;
|
||||
///
|
||||
/// typename a<T>::type var_d;
|
||||
///
|
||||
/// template<typename U>
|
||||
/// T func(U);
|
||||
/// };
|
||||
template <typename T>
|
||||
struct e
|
||||
{
|
||||
|
|
@ -43,16 +68,28 @@ struct e
|
|||
};
|
||||
|
||||
// full specialization
|
||||
/// template<>
|
||||
/// class a<int>{
|
||||
/// };
|
||||
template <>
|
||||
class a<int> {};
|
||||
|
||||
/// template<>
|
||||
/// struct b<0,int>{
|
||||
/// };
|
||||
template <>
|
||||
struct b<0, int> {};
|
||||
|
||||
// partial specialization
|
||||
/// template<typename T>
|
||||
/// class a<T*>{
|
||||
/// };
|
||||
template <typename T>
|
||||
class a<T*> {};
|
||||
|
||||
/// template<typename T>
|
||||
/// struct b<0,T>{
|
||||
/// };
|
||||
template <typename T>
|
||||
struct b<0, T> {};
|
||||
)";
|
||||
|
|
|
|||
|
|
@ -11,6 +11,12 @@ using namespace cppast;
|
|||
TEST_CASE("cpp_enum")
|
||||
{
|
||||
auto code = R"(
|
||||
/// enum a{
|
||||
/// a_a,
|
||||
/// a_b=42,
|
||||
/// a_c,
|
||||
/// a_d=a_a+2
|
||||
/// };
|
||||
enum a
|
||||
{
|
||||
a_a,
|
||||
|
|
@ -19,8 +25,15 @@ enum a
|
|||
a_d = a_a + 2,
|
||||
};
|
||||
|
||||
/// enum class b;
|
||||
enum class b; // forward declaration
|
||||
|
||||
/// enum class b
|
||||
/// :int{
|
||||
/// b_a,
|
||||
/// b_b=42,
|
||||
/// b_c
|
||||
/// };
|
||||
enum class b : int
|
||||
{
|
||||
b_a,
|
||||
|
|
@ -28,6 +41,8 @@ enum class b : int
|
|||
b_c
|
||||
};
|
||||
|
||||
/// enum c
|
||||
/// :int;
|
||||
enum c : int;
|
||||
)";
|
||||
|
||||
|
|
@ -54,7 +69,7 @@ enum c : int;
|
|||
++no_vals;
|
||||
REQUIRE(val.value());
|
||||
auto& expr = val.value().value();
|
||||
REQUIRE(expr.kind() == cpp_expression_kind::unexposed);
|
||||
REQUIRE(expr.kind() == cpp_expression_kind::unexposed_t);
|
||||
REQUIRE(static_cast<const cpp_unexposed_expression&>(expr).expression()
|
||||
== "42");
|
||||
REQUIRE(equal_types(idx, expr.type(), *cpp_builtin_type::build(cpp_uint)));
|
||||
|
|
@ -65,7 +80,7 @@ enum c : int;
|
|||
REQUIRE(val.value());
|
||||
auto& expr = val.value().value();
|
||||
// this is unexposed for some reason
|
||||
REQUIRE(expr.kind() == cpp_expression_kind::unexposed);
|
||||
REQUIRE(expr.kind() == cpp_expression_kind::unexposed_t);
|
||||
REQUIRE(static_cast<const cpp_unexposed_expression&>(expr).expression()
|
||||
== "a_a+2");
|
||||
REQUIRE(equal_types(idx, expr.type(), *cpp_builtin_type::build(cpp_uint)));
|
||||
|
|
@ -99,7 +114,7 @@ enum c : int;
|
|||
++no_vals;
|
||||
REQUIRE(val.value());
|
||||
auto& expr = val.value().value();
|
||||
REQUIRE(expr.kind() == cpp_expression_kind::literal);
|
||||
REQUIRE(expr.kind() == cpp_expression_kind::literal_t);
|
||||
REQUIRE(static_cast<const cpp_literal_expression&>(expr).value() == "42");
|
||||
REQUIRE(equal_types(idx, expr.type(), *cpp_builtin_type::build(cpp_int)));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,29 +15,42 @@ TEST_CASE("cpp_function")
|
|||
{
|
||||
auto code = R"(
|
||||
// parameters and return type are only tested here
|
||||
/// void a();
|
||||
void a();
|
||||
/// int b(int a,float* b=nullptr);
|
||||
int b(int a, float* b = nullptr);
|
||||
/// auto c(decltype(42) a,...)->int(&)[10];
|
||||
int (&c(decltype(42) a, ...))[10];
|
||||
|
||||
// noexcept conditions
|
||||
/// void d()noexcept;
|
||||
void d() noexcept;
|
||||
/// void e()noexcept(false);
|
||||
void e() noexcept(false);
|
||||
/// void f()noexcept(noexcept(d()));
|
||||
void f() noexcept(noexcept(d()));
|
||||
|
||||
// storage class + constexpr
|
||||
/// extern void g();
|
||||
extern void g();
|
||||
/// static void h();
|
||||
static void h();
|
||||
/// constexpr void i();
|
||||
constexpr void i();
|
||||
/// static constexpr void j();
|
||||
static constexpr void j();
|
||||
|
||||
// body
|
||||
namespace ns
|
||||
{
|
||||
/// void k()=delete;
|
||||
void k() = delete;
|
||||
|
||||
/// void l();
|
||||
void l();
|
||||
}
|
||||
|
||||
/// void ns::l();
|
||||
void ns::l()
|
||||
{
|
||||
// might confuse parser
|
||||
|
|
@ -215,13 +228,17 @@ TEST_CASE("static cpp_function")
|
|||
// no need to test anything special
|
||||
struct foo
|
||||
{
|
||||
/// static void a();
|
||||
static void a();
|
||||
|
||||
/// static int b()noexcept;
|
||||
static int b() noexcept { return 0; }
|
||||
|
||||
/// static constexpr char c()=delete;
|
||||
static constexpr char c() = delete;
|
||||
};
|
||||
|
||||
/// static void foo::a();
|
||||
void foo::a() {}
|
||||
)";
|
||||
|
||||
|
|
|
|||
|
|
@ -17,36 +17,56 @@ TEST_CASE("cpp_function_template")
|
|||
template <int I>
|
||||
using type = int;
|
||||
|
||||
/// template<typename T>
|
||||
/// T a(T const& t);
|
||||
template <typename T>
|
||||
T a(const T& t);
|
||||
|
||||
struct d
|
||||
{
|
||||
/// template<int I,typename T>
|
||||
/// static type<I> b(T);
|
||||
template <int I, typename T>
|
||||
static type<I> b(T);
|
||||
|
||||
/// template<typename T=const int>
|
||||
/// T c();
|
||||
template <typename T = const int>
|
||||
auto c() -> T;
|
||||
|
||||
/// template<typename T>
|
||||
/// operator T()const;
|
||||
template <typename T>
|
||||
operator T() const;
|
||||
|
||||
/// template<typename T>
|
||||
/// d(T const&);
|
||||
template <typename T>
|
||||
d(const T&);
|
||||
};
|
||||
|
||||
/// template<>
|
||||
/// int a(int const& t);
|
||||
template <>
|
||||
int a(const int& t);
|
||||
|
||||
/// template<>
|
||||
/// static type<0> d::b<0,int>(int);
|
||||
template <>
|
||||
type<0> d::b<0, int>(int);
|
||||
|
||||
/// template<>
|
||||
/// int d::c();
|
||||
template <>
|
||||
auto d::c() -> int;
|
||||
|
||||
/// template<>
|
||||
/// d::operator int()const;
|
||||
template <>
|
||||
d::operator int() const;
|
||||
|
||||
/// template<>
|
||||
/// d::d(int const&);
|
||||
template <>
|
||||
d::d(const int&);
|
||||
)";
|
||||
|
|
|
|||
|
|
@ -13,10 +13,22 @@ using namespace cppast;
|
|||
TEST_CASE("cpp_language_linkage")
|
||||
{
|
||||
auto code = R"(
|
||||
/// extern "C" enum a{
|
||||
/// };
|
||||
extern "C" enum a {};
|
||||
|
||||
enum b {};
|
||||
|
||||
/// extern "C++"{
|
||||
/// enum c{
|
||||
/// };
|
||||
///
|
||||
/// enum d{
|
||||
/// };
|
||||
///
|
||||
/// enum e{
|
||||
/// };
|
||||
/// }
|
||||
extern "C++" // yup
|
||||
{
|
||||
enum c {};
|
||||
|
|
@ -41,21 +53,23 @@ enum f {};
|
|||
REQUIRE(count == 2u);
|
||||
|
||||
// check enums for their correct parent
|
||||
count = test_visit<cpp_enum>(*file, [&](const cpp_enum& e) {
|
||||
if (e.name() == "a")
|
||||
check_parent(e, "\"C\"", "a");
|
||||
else if (e.name() == "b")
|
||||
check_parent(e, "cpp_language_linkage.cpp", "b");
|
||||
else if (e.name() == "c")
|
||||
check_parent(e, "\"C++\"", "c");
|
||||
else if (e.name() == "d")
|
||||
check_parent(e, "\"C++\"", "d");
|
||||
else if (e.name() == "e")
|
||||
check_parent(e, "\"C++\"", "e");
|
||||
else if (e.name() == "f")
|
||||
check_parent(e, "cpp_language_linkage.cpp", "f");
|
||||
else
|
||||
REQUIRE(false);
|
||||
});
|
||||
count = test_visit<cpp_enum>(*file,
|
||||
[&](const cpp_enum& e) {
|
||||
if (e.name() == "a")
|
||||
check_parent(e, "\"C\"", "a");
|
||||
else if (e.name() == "b")
|
||||
check_parent(e, "cpp_language_linkage.cpp", "b");
|
||||
else if (e.name() == "c")
|
||||
check_parent(e, "\"C++\"", "c");
|
||||
else if (e.name() == "d")
|
||||
check_parent(e, "\"C++\"", "d");
|
||||
else if (e.name() == "e")
|
||||
check_parent(e, "\"C++\"", "e");
|
||||
else if (e.name() == "f")
|
||||
check_parent(e, "cpp_language_linkage.cpp", "f");
|
||||
else
|
||||
REQUIRE(false);
|
||||
},
|
||||
false); // don't check code generation here
|
||||
REQUIRE(count == 6u);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,24 +14,36 @@ TEST_CASE("cpp_member_function")
|
|||
// no need to test parameters/return types
|
||||
struct foo
|
||||
{
|
||||
/// void a();
|
||||
void a();
|
||||
/// void b()noexcept;
|
||||
void b() noexcept;
|
||||
|
||||
/// void c()const;
|
||||
void c() const;
|
||||
auto d() const volatile -> void;
|
||||
/// void d()const volatile noexcept;
|
||||
auto d() const volatile noexcept -> void;
|
||||
/// void e()&;
|
||||
void e() &;
|
||||
/// void f()const volatile&&;
|
||||
void f() const volatile &&;
|
||||
|
||||
/// virtual void g();
|
||||
virtual void g();
|
||||
/// virtual void h()=0;
|
||||
virtual void h() = 0;
|
||||
|
||||
/// void i();
|
||||
void i() {}
|
||||
/// void j()=delete;
|
||||
void j() = delete;
|
||||
};
|
||||
|
||||
struct bar : foo
|
||||
{
|
||||
/// virtual void g() override;
|
||||
void g();
|
||||
/// virtual void h() override final;
|
||||
virtual auto h() -> void override final;
|
||||
};
|
||||
)";
|
||||
|
|
@ -43,7 +55,7 @@ struct bar : foo
|
|||
REQUIRE(!func.is_variadic());
|
||||
REQUIRE(!func.is_constexpr());
|
||||
REQUIRE(equal_types(idx, func.return_type(), *cpp_builtin_type::build(cpp_void)));
|
||||
if (func.name() != "b")
|
||||
if (func.name() != "b" && func.name() != "d")
|
||||
REQUIRE(!func.noexcept_condition());
|
||||
if (func.name() != "g" && func.name() != "h")
|
||||
REQUIRE(!func.virtual_info());
|
||||
|
|
@ -142,8 +154,11 @@ namespace ns
|
|||
// most of it only need to be check in member function
|
||||
struct foo
|
||||
{
|
||||
/// operator int&();
|
||||
operator int&();
|
||||
/// explicit operator bool()const;
|
||||
explicit operator bool() const;
|
||||
/// constexpr operator ns::type();
|
||||
constexpr operator ns::type();
|
||||
};
|
||||
)";
|
||||
|
|
@ -192,8 +207,11 @@ TEST_CASE("cpp_constructor")
|
|||
// only test constructor specific stuff
|
||||
struct foo
|
||||
{
|
||||
/// foo()noexcept=default;
|
||||
foo() noexcept = default;
|
||||
/// explicit foo(int);
|
||||
explicit foo(int);
|
||||
/// constexpr foo(int,char)=delete;
|
||||
constexpr foo(int, char) = delete;
|
||||
};
|
||||
)";
|
||||
|
|
@ -240,21 +258,25 @@ TEST_CASE("cpp_destructor")
|
|||
auto code = R"(
|
||||
struct a
|
||||
{
|
||||
/// ~a();
|
||||
~a();
|
||||
};
|
||||
|
||||
struct b
|
||||
{
|
||||
/// ~b()noexcept(false);
|
||||
~b() noexcept(false) {}
|
||||
};
|
||||
|
||||
struct c
|
||||
{
|
||||
/// virtual ~c()=default;
|
||||
virtual ~c() = default;
|
||||
};
|
||||
|
||||
struct d : c
|
||||
{
|
||||
/// virtual ~d() override final;
|
||||
~d() final;
|
||||
};
|
||||
)";
|
||||
|
|
|
|||
|
|
@ -13,8 +13,11 @@ TEST_CASE("cpp_member_variable")
|
|||
auto code = R"(
|
||||
struct foo
|
||||
{
|
||||
/// int a;
|
||||
int a;
|
||||
/// float b=3.14f;
|
||||
float b = 3.14f;
|
||||
/// mutable char c;
|
||||
mutable char c;
|
||||
};
|
||||
)";
|
||||
|
|
@ -59,10 +62,15 @@ TEST_CASE("cpp_bitfield")
|
|||
auto code = R"(
|
||||
struct foo
|
||||
{
|
||||
/// char a:3;
|
||||
char a : 3;
|
||||
/// mutable char b:2;
|
||||
mutable char b : 2;
|
||||
/// char:0;
|
||||
char : 0;
|
||||
/// char c:3;
|
||||
char c : 3;
|
||||
/// char:4;
|
||||
char : 4;
|
||||
};
|
||||
)";
|
||||
|
|
|
|||
|
|
@ -11,12 +11,22 @@ using namespace cppast;
|
|||
TEST_CASE("cpp_namespace")
|
||||
{
|
||||
auto code = R"(
|
||||
/// namespace a{
|
||||
/// }
|
||||
namespace a {}
|
||||
|
||||
/// inline namespace b{
|
||||
/// }
|
||||
inline namespace b {}
|
||||
|
||||
/// namespace c{
|
||||
/// namespace d{
|
||||
/// }
|
||||
/// }
|
||||
namespace c
|
||||
{
|
||||
/// namespace d{
|
||||
/// }
|
||||
namespace d {}
|
||||
}
|
||||
)";
|
||||
|
|
@ -57,18 +67,24 @@ TEST_CASE("cpp_namespace_alias")
|
|||
namespace outer {}
|
||||
namespace ns {}
|
||||
|
||||
/// namespace a=outer;
|
||||
namespace a = outer;
|
||||
/// namespace b=ns;
|
||||
namespace b = ns;
|
||||
|
||||
namespace outer
|
||||
{
|
||||
namespace ns {}
|
||||
|
||||
/// namespace c=ns;
|
||||
namespace c = ns;
|
||||
/// namespace d=::outer;
|
||||
namespace d = ::outer;
|
||||
}
|
||||
|
||||
/// namespace e=outer::ns;
|
||||
namespace e = outer::ns;
|
||||
/// namespace f=outer::c;
|
||||
namespace f = outer::c;
|
||||
)";
|
||||
|
||||
|
|
@ -117,17 +133,22 @@ TEST_CASE("cpp_using_directive")
|
|||
namespace ns1 {}
|
||||
namespace ns2 {}
|
||||
|
||||
/// using namespace ns1;
|
||||
using namespace ns1;
|
||||
/// using namespace ns2;
|
||||
using namespace ns2;
|
||||
|
||||
namespace outer
|
||||
{
|
||||
namespace ns {}
|
||||
|
||||
/// using namespace ns;
|
||||
using namespace ns;
|
||||
/// using namespace ::ns1;
|
||||
using namespace ::ns1;
|
||||
}
|
||||
|
||||
/// using namespace outer::ns;
|
||||
using namespace outer::ns;
|
||||
)";
|
||||
|
||||
|
|
@ -180,7 +201,9 @@ namespace ns2
|
|||
enum b {};
|
||||
}
|
||||
|
||||
/// using ns1::a;
|
||||
using ns1::a;
|
||||
/// using ns2::b;
|
||||
using ns2::b;
|
||||
|
||||
namespace outer
|
||||
|
|
@ -190,10 +213,13 @@ namespace outer
|
|||
enum c {};
|
||||
}
|
||||
|
||||
/// using ns::c;
|
||||
using ns::c;
|
||||
}
|
||||
|
||||
/// using outer::ns::c;
|
||||
using outer::ns::c;
|
||||
/// using outer::c;
|
||||
using outer::c;
|
||||
|
||||
namespace ns1
|
||||
|
|
@ -202,6 +228,7 @@ namespace ns1
|
|||
void d(float);
|
||||
}
|
||||
|
||||
/// using ns1::d;
|
||||
using ns1::d;
|
||||
)";
|
||||
|
||||
|
|
|
|||
|
|
@ -13,15 +13,21 @@ TEST_CASE("cpp_macro_definition")
|
|||
auto code = R"(
|
||||
#include <iostream>
|
||||
#define G
|
||||
/// #define A
|
||||
#define A
|
||||
/// #define B hello
|
||||
#define B hello
|
||||
namespace ns {}
|
||||
/// #define C(x,y) x##_name
|
||||
#define C(x, y) x##_name
|
||||
/// #define D(...) __VA_ARGS__
|
||||
#define D(...) __VA_ARGS__
|
||||
/// #define E() barbaz
|
||||
#define E() bar\
|
||||
baz
|
||||
namespace ns2
|
||||
{
|
||||
/// #define F () bar
|
||||
#define F () bar
|
||||
#undef G
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,60 +35,82 @@ using e = void;
|
|||
|
||||
cpp_entity_index idx;
|
||||
auto file = parse(idx, "cpp_template_type_parameter.cpp", code);
|
||||
auto count = test_visit<cpp_alias_template>(*file, [&](const cpp_alias_template& alias) {
|
||||
REQUIRE(equal_types(idx, alias.type_alias().underlying_type(),
|
||||
*cpp_builtin_type::build(cpp_void)));
|
||||
auto count =
|
||||
test_visit<cpp_alias_template>(*file,
|
||||
[&](const cpp_alias_template& alias) {
|
||||
REQUIRE(equal_types(idx,
|
||||
alias.type_alias().underlying_type(),
|
||||
*cpp_builtin_type::build(cpp_void)));
|
||||
|
||||
for (auto& p : alias.parameters())
|
||||
{
|
||||
REQUIRE(p.kind() == cpp_entity_kind::template_type_parameter_t);
|
||||
for (auto& p : alias.parameters())
|
||||
{
|
||||
REQUIRE(
|
||||
p.kind()
|
||||
== cpp_entity_kind::template_type_parameter_t);
|
||||
|
||||
auto& param = static_cast<const cpp_template_type_parameter&>(p);
|
||||
if (param.name() == "A")
|
||||
{
|
||||
REQUIRE(alias.name() == "a");
|
||||
REQUIRE(param.keyword() == cpp_template_keyword::keyword_typename);
|
||||
REQUIRE(!param.is_variadic());
|
||||
REQUIRE(!param.default_type());
|
||||
}
|
||||
else if (param.name() == "B")
|
||||
{
|
||||
REQUIRE(alias.name() == "b");
|
||||
REQUIRE(param.keyword() == cpp_template_keyword::keyword_class);
|
||||
REQUIRE(param.is_variadic());
|
||||
REQUIRE(!param.default_type());
|
||||
}
|
||||
else if (param.name() == "")
|
||||
{
|
||||
REQUIRE(alias.name() == "c");
|
||||
REQUIRE(param.keyword() == cpp_template_keyword::keyword_typename);
|
||||
REQUIRE(!param.is_variadic());
|
||||
REQUIRE(param.default_type().has_value());
|
||||
REQUIRE(equal_types(idx, param.default_type().value(),
|
||||
*cpp_unexposed_type::build("const int*")));
|
||||
}
|
||||
else if (param.name() == "D")
|
||||
{
|
||||
REQUIRE(alias.name() == "d");
|
||||
REQUIRE(param.keyword() == cpp_template_keyword::keyword_class);
|
||||
REQUIRE(!param.is_variadic());
|
||||
REQUIRE(param.default_type().has_value());
|
||||
REQUIRE(equal_types(idx, param.default_type().value(),
|
||||
*cpp_unexposed_type::build("decltype(1+3)")));
|
||||
}
|
||||
else if (param.name() == "E")
|
||||
{
|
||||
REQUIRE(alias.name() == "e");
|
||||
REQUIRE(param.keyword() == cpp_template_keyword::keyword_typename);
|
||||
REQUIRE(!param.is_variadic());
|
||||
REQUIRE(param.default_type().has_value());
|
||||
REQUIRE(equal_types(idx, param.default_type().value(),
|
||||
*cpp_unexposed_type::build("a<void>")));
|
||||
}
|
||||
else
|
||||
REQUIRE(false);
|
||||
}
|
||||
});
|
||||
auto& param =
|
||||
static_cast<const cpp_template_type_parameter&>(
|
||||
p);
|
||||
if (param.name() == "A")
|
||||
{
|
||||
REQUIRE(alias.name() == "a");
|
||||
REQUIRE(
|
||||
param.keyword()
|
||||
== cpp_template_keyword::keyword_typename);
|
||||
REQUIRE(!param.is_variadic());
|
||||
REQUIRE(!param.default_type());
|
||||
}
|
||||
else if (param.name() == "B")
|
||||
{
|
||||
REQUIRE(alias.name() == "b");
|
||||
REQUIRE(param.keyword()
|
||||
== cpp_template_keyword::keyword_class);
|
||||
REQUIRE(param.is_variadic());
|
||||
REQUIRE(!param.default_type());
|
||||
}
|
||||
else if (param.name() == "")
|
||||
{
|
||||
REQUIRE(alias.name() == "c");
|
||||
REQUIRE(
|
||||
param.keyword()
|
||||
== cpp_template_keyword::keyword_typename);
|
||||
REQUIRE(!param.is_variadic());
|
||||
REQUIRE(param.default_type().has_value());
|
||||
REQUIRE(equal_types(idx,
|
||||
param.default_type().value(),
|
||||
*cpp_unexposed_type::build(
|
||||
"const int*")));
|
||||
}
|
||||
else if (param.name() == "D")
|
||||
{
|
||||
REQUIRE(alias.name() == "d");
|
||||
REQUIRE(param.keyword()
|
||||
== cpp_template_keyword::keyword_class);
|
||||
REQUIRE(!param.is_variadic());
|
||||
REQUIRE(param.default_type().has_value());
|
||||
REQUIRE(equal_types(idx,
|
||||
param.default_type().value(),
|
||||
*cpp_unexposed_type::build(
|
||||
"decltype(1+3)")));
|
||||
}
|
||||
else if (param.name() == "E")
|
||||
{
|
||||
REQUIRE(alias.name() == "e");
|
||||
REQUIRE(
|
||||
param.keyword()
|
||||
== cpp_template_keyword::keyword_typename);
|
||||
REQUIRE(!param.is_variadic());
|
||||
REQUIRE(param.default_type().has_value());
|
||||
REQUIRE(equal_types(idx,
|
||||
param.default_type().value(),
|
||||
*cpp_unexposed_type::build(
|
||||
"a<void>")));
|
||||
}
|
||||
else
|
||||
REQUIRE(false);
|
||||
}
|
||||
},
|
||||
false); // can't check synopsis with comments
|
||||
REQUIRE(count == 5u);
|
||||
}
|
||||
|
||||
|
|
@ -109,57 +131,62 @@ using d = void;
|
|||
)";
|
||||
|
||||
cpp_entity_index idx;
|
||||
auto file = parse(idx, "cpp_non_type_template_parameter.cpp", code);
|
||||
auto count = test_visit<cpp_alias_template>(*file, [&](const cpp_alias_template& alias) {
|
||||
REQUIRE(equal_types(idx, alias.type_alias().underlying_type(),
|
||||
*cpp_builtin_type::build(cpp_void)));
|
||||
auto file = parse(idx, "cpp_non_type_template_parameter.cpp", code);
|
||||
auto count = test_visit<cpp_alias_template>(
|
||||
*file,
|
||||
[&](const cpp_alias_template& alias) {
|
||||
REQUIRE(equal_types(idx, alias.type_alias().underlying_type(),
|
||||
*cpp_builtin_type::build(cpp_void)));
|
||||
|
||||
for (auto& p : alias.parameters())
|
||||
{
|
||||
REQUIRE(p.kind() == cpp_entity_kind::non_type_template_parameter_t);
|
||||
for (auto& p : alias.parameters())
|
||||
{
|
||||
REQUIRE(p.kind() == cpp_entity_kind::non_type_template_parameter_t);
|
||||
|
||||
auto& param = static_cast<const cpp_non_type_template_parameter&>(p);
|
||||
if (param.name() == "A")
|
||||
{
|
||||
REQUIRE(alias.name() == "a");
|
||||
REQUIRE(equal_types(idx, param.type(), *cpp_builtin_type::build(cpp_int)));
|
||||
REQUIRE(!param.is_variadic());
|
||||
REQUIRE(!param.default_value());
|
||||
}
|
||||
else if (param.name() == "")
|
||||
{
|
||||
REQUIRE(alias.name() == "b");
|
||||
REQUIRE(equal_types(idx, param.type(),
|
||||
*cpp_pointer_type::build(cpp_builtin_type::build(cpp_char))));
|
||||
REQUIRE(!param.is_variadic());
|
||||
REQUIRE(param.default_value());
|
||||
REQUIRE(equal_expressions(param.default_value().value(),
|
||||
auto& param = static_cast<const cpp_non_type_template_parameter&>(p);
|
||||
if (param.name() == "A")
|
||||
{
|
||||
REQUIRE(alias.name() == "a");
|
||||
REQUIRE(equal_types(idx, param.type(), *cpp_builtin_type::build(cpp_int)));
|
||||
REQUIRE(!param.is_variadic());
|
||||
REQUIRE(!param.default_value());
|
||||
}
|
||||
else if (param.name() == "")
|
||||
{
|
||||
REQUIRE(alias.name() == "b");
|
||||
REQUIRE(equal_types(idx, param.type(), *cpp_pointer_type::build(
|
||||
cpp_builtin_type::build(cpp_char))));
|
||||
REQUIRE(!param.is_variadic());
|
||||
REQUIRE(param.default_value());
|
||||
REQUIRE(
|
||||
equal_expressions(param.default_value().value(),
|
||||
*cpp_literal_expression::build(cpp_builtin_type::build(
|
||||
cpp_nullptr),
|
||||
"nullptr")));
|
||||
}
|
||||
else if (param.name() == "C")
|
||||
{
|
||||
REQUIRE(alias.name() == "c");
|
||||
REQUIRE(equal_types(idx, param.type(), *cpp_builtin_type::build(cpp_int)));
|
||||
REQUIRE(param.is_variadic());
|
||||
REQUIRE(!param.default_value());
|
||||
}
|
||||
else if (param.name() == "D")
|
||||
{
|
||||
REQUIRE(alias.name() == "d");
|
||||
}
|
||||
else if (param.name() == "C")
|
||||
{
|
||||
REQUIRE(alias.name() == "c");
|
||||
REQUIRE(equal_types(idx, param.type(), *cpp_builtin_type::build(cpp_int)));
|
||||
REQUIRE(param.is_variadic());
|
||||
REQUIRE(!param.default_value());
|
||||
}
|
||||
else if (param.name() == "D")
|
||||
{
|
||||
REQUIRE(alias.name() == "d");
|
||||
|
||||
cpp_function_type::builder builder(cpp_builtin_type::build(cpp_void));
|
||||
builder.is_variadic();
|
||||
REQUIRE(equal_types(idx, param.type(), *cpp_pointer_type::build(builder.finish())));
|
||||
cpp_function_type::builder builder(cpp_builtin_type::build(cpp_void));
|
||||
builder.is_variadic();
|
||||
REQUIRE(
|
||||
equal_types(idx, param.type(), *cpp_pointer_type::build(builder.finish())));
|
||||
|
||||
REQUIRE(!param.is_variadic());
|
||||
REQUIRE(!param.default_value());
|
||||
REQUIRE(!param.is_variadic());
|
||||
REQUIRE(!param.default_value());
|
||||
}
|
||||
else
|
||||
REQUIRE(false);
|
||||
}
|
||||
else
|
||||
REQUIRE(false);
|
||||
}
|
||||
});
|
||||
},
|
||||
false); // can't check synopsis with comments
|
||||
REQUIRE(count == 4u);
|
||||
}
|
||||
|
||||
|
|
@ -187,105 +214,108 @@ using d = void;
|
|||
)";
|
||||
|
||||
cpp_entity_index idx;
|
||||
auto file = parse(idx, "cpp_template_template_parameter.cpp", code);
|
||||
auto count = test_visit<cpp_alias_template>(*file, [&](const cpp_alias_template& alias) {
|
||||
REQUIRE(equal_types(idx, alias.type_alias().underlying_type(),
|
||||
*cpp_builtin_type::build(cpp_void)));
|
||||
if (alias.name() == "def")
|
||||
return;
|
||||
auto file = parse(idx, "cpp_template_template_parameter.cpp", code);
|
||||
auto count = test_visit<cpp_alias_template>(
|
||||
*file,
|
||||
[&](const cpp_alias_template& alias) {
|
||||
REQUIRE(equal_types(idx, alias.type_alias().underlying_type(),
|
||||
*cpp_builtin_type::build(cpp_void)));
|
||||
if (alias.name() == "def")
|
||||
return;
|
||||
|
||||
for (auto& p : alias.parameters())
|
||||
{
|
||||
REQUIRE(p.kind() == cpp_entity_kind::template_template_parameter_t);
|
||||
|
||||
auto& param = static_cast<const cpp_template_template_parameter&>(p);
|
||||
REQUIRE(param.keyword() == cpp_template_keyword::keyword_class);
|
||||
if (param.name() == "A")
|
||||
for (auto& p : alias.parameters())
|
||||
{
|
||||
REQUIRE(alias.name() == "a");
|
||||
REQUIRE(!param.is_variadic());
|
||||
REQUIRE(!param.default_template());
|
||||
REQUIRE(p.kind() == cpp_entity_kind::template_template_parameter_t);
|
||||
|
||||
auto no = 0u;
|
||||
for (auto& p_param : param)
|
||||
auto& param = static_cast<const cpp_template_template_parameter&>(p);
|
||||
REQUIRE(param.keyword() == cpp_template_keyword::keyword_class);
|
||||
if (param.name() == "A")
|
||||
{
|
||||
++no;
|
||||
REQUIRE(p_param.name() == "T");
|
||||
REQUIRE(p_param.kind() == cpp_entity_kind::template_type_parameter_t);
|
||||
}
|
||||
REQUIRE(no == 1u);
|
||||
}
|
||||
else if (param.name() == "B")
|
||||
{
|
||||
REQUIRE(alias.name() == "b");
|
||||
REQUIRE(param.is_variadic());
|
||||
REQUIRE(!param.default_template());
|
||||
REQUIRE(alias.name() == "a");
|
||||
REQUIRE(!param.is_variadic());
|
||||
REQUIRE(!param.default_template());
|
||||
|
||||
auto cur = param.begin();
|
||||
REQUIRE(cur != param.end());
|
||||
REQUIRE(cur->name().empty());
|
||||
REQUIRE(cur->kind() == cpp_entity_kind::non_type_template_parameter_t);
|
||||
|
||||
++cur;
|
||||
REQUIRE(cur != param.end());
|
||||
REQUIRE(cur->name().empty());
|
||||
REQUIRE(cur->kind() == cpp_entity_kind::template_type_parameter_t);
|
||||
|
||||
++cur;
|
||||
REQUIRE(cur == param.end());
|
||||
}
|
||||
else if (param.name() == "C")
|
||||
{
|
||||
REQUIRE(alias.name() == "c");
|
||||
REQUIRE(!param.is_variadic());
|
||||
REQUIRE(param.default_template());
|
||||
|
||||
auto def = param.default_template().value();
|
||||
REQUIRE(def.name() == "ns::def");
|
||||
auto entities = def.get(idx);
|
||||
REQUIRE(entities.size() == 1u);
|
||||
REQUIRE(entities[0]->name() == "def");
|
||||
|
||||
auto no = 0u;
|
||||
for (auto& p_param : param)
|
||||
{
|
||||
++no;
|
||||
REQUIRE(p_param.name() == "");
|
||||
REQUIRE(p_param.kind() == cpp_entity_kind::non_type_template_parameter_t);
|
||||
}
|
||||
REQUIRE(no == 1u);
|
||||
}
|
||||
else if (param.name() == "D")
|
||||
{
|
||||
REQUIRE(alias.name() == "d");
|
||||
REQUIRE(!param.is_variadic());
|
||||
REQUIRE(param.default_template());
|
||||
|
||||
auto def = param.default_template().value();
|
||||
REQUIRE(def.name() == "a");
|
||||
auto entities = def.get(idx);
|
||||
REQUIRE(entities.size() == 1u);
|
||||
REQUIRE(entities[0]->name() == "a");
|
||||
|
||||
auto no = 0u;
|
||||
for (auto& p_param : param)
|
||||
{
|
||||
++no;
|
||||
REQUIRE(p_param.name() == "");
|
||||
REQUIRE(p_param.kind() == cpp_entity_kind::template_template_parameter_t);
|
||||
for (auto& p_p_param :
|
||||
static_cast<const cpp_template_template_parameter&>(p_param))
|
||||
auto no = 0u;
|
||||
for (auto& p_param : param)
|
||||
{
|
||||
++no;
|
||||
REQUIRE(p_p_param.name() == "");
|
||||
REQUIRE(p_p_param.kind() == cpp_entity_kind::template_type_parameter_t);
|
||||
REQUIRE(p_param.name() == "T");
|
||||
REQUIRE(p_param.kind() == cpp_entity_kind::template_type_parameter_t);
|
||||
}
|
||||
REQUIRE(no == 1u);
|
||||
}
|
||||
REQUIRE(no == 2u);
|
||||
else if (param.name() == "B")
|
||||
{
|
||||
REQUIRE(alias.name() == "b");
|
||||
REQUIRE(param.is_variadic());
|
||||
REQUIRE(!param.default_template());
|
||||
|
||||
auto cur = param.begin();
|
||||
REQUIRE(cur != param.end());
|
||||
REQUIRE(cur->name().empty());
|
||||
REQUIRE(cur->kind() == cpp_entity_kind::non_type_template_parameter_t);
|
||||
|
||||
++cur;
|
||||
REQUIRE(cur != param.end());
|
||||
REQUIRE(cur->name().empty());
|
||||
REQUIRE(cur->kind() == cpp_entity_kind::template_type_parameter_t);
|
||||
|
||||
++cur;
|
||||
REQUIRE(cur == param.end());
|
||||
}
|
||||
else if (param.name() == "C")
|
||||
{
|
||||
REQUIRE(alias.name() == "c");
|
||||
REQUIRE(!param.is_variadic());
|
||||
REQUIRE(param.default_template());
|
||||
|
||||
auto def = param.default_template().value();
|
||||
REQUIRE(def.name() == "ns::def");
|
||||
auto entities = def.get(idx);
|
||||
REQUIRE(entities.size() == 1u);
|
||||
REQUIRE(entities[0]->name() == "def");
|
||||
|
||||
auto no = 0u;
|
||||
for (auto& p_param : param)
|
||||
{
|
||||
++no;
|
||||
REQUIRE(p_param.name() == "");
|
||||
REQUIRE(p_param.kind() == cpp_entity_kind::non_type_template_parameter_t);
|
||||
}
|
||||
REQUIRE(no == 1u);
|
||||
}
|
||||
else if (param.name() == "D")
|
||||
{
|
||||
REQUIRE(alias.name() == "d");
|
||||
REQUIRE(!param.is_variadic());
|
||||
REQUIRE(param.default_template());
|
||||
|
||||
auto def = param.default_template().value();
|
||||
REQUIRE(def.name() == "a");
|
||||
auto entities = def.get(idx);
|
||||
REQUIRE(entities.size() == 1u);
|
||||
REQUIRE(entities[0]->name() == "a");
|
||||
|
||||
auto no = 0u;
|
||||
for (auto& p_param : param)
|
||||
{
|
||||
++no;
|
||||
REQUIRE(p_param.name() == "");
|
||||
REQUIRE(p_param.kind() == cpp_entity_kind::template_template_parameter_t);
|
||||
for (auto& p_p_param :
|
||||
static_cast<const cpp_template_template_parameter&>(p_param))
|
||||
{
|
||||
++no;
|
||||
REQUIRE(p_p_param.name() == "");
|
||||
REQUIRE(p_p_param.kind() == cpp_entity_kind::template_type_parameter_t);
|
||||
}
|
||||
}
|
||||
REQUIRE(no == 2u);
|
||||
}
|
||||
else
|
||||
REQUIRE(false);
|
||||
}
|
||||
else
|
||||
REQUIRE(false);
|
||||
}
|
||||
});
|
||||
},
|
||||
false); // can't check synopsis with comments
|
||||
REQUIRE(count == 5u);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,26 +21,26 @@ bool equal_types(const cpp_entity_index& idx, const cpp_type& parsed, const cpp_
|
|||
|
||||
switch (parsed.kind())
|
||||
{
|
||||
case cpp_type_kind::builtin:
|
||||
case cpp_type_kind::builtin_t:
|
||||
return static_cast<const cpp_builtin_type&>(parsed).builtin_type_kind()
|
||||
== static_cast<const cpp_builtin_type&>(synthesized).builtin_type_kind();
|
||||
|
||||
case cpp_type_kind::user_defined:
|
||||
case cpp_type_kind::user_defined_t:
|
||||
{
|
||||
auto& user_parsed = static_cast<const cpp_user_defined_type&>(parsed);
|
||||
auto& user_synthesized = static_cast<const cpp_user_defined_type&>(synthesized);
|
||||
return equal_ref(idx, user_parsed.entity(), user_synthesized.entity());
|
||||
}
|
||||
|
||||
case cpp_type_kind::auto_:
|
||||
case cpp_type_kind::auto_t:
|
||||
return true;
|
||||
case cpp_type_kind::decltype_:
|
||||
case cpp_type_kind::decltype_t:
|
||||
return equal_expressions(static_cast<const cpp_decltype_type&>(parsed).expression(),
|
||||
static_cast<const cpp_decltype_type&>(synthesized).expression());
|
||||
case cpp_type_kind::decltype_auto:
|
||||
case cpp_type_kind::decltype_auto_t:
|
||||
return true;
|
||||
|
||||
case cpp_type_kind::cv_qualified:
|
||||
case cpp_type_kind::cv_qualified_t:
|
||||
{
|
||||
auto& cv_a = static_cast<const cpp_cv_qualified_type&>(parsed);
|
||||
auto& cv_b = static_cast<const cpp_cv_qualified_type&>(synthesized);
|
||||
|
|
@ -48,10 +48,10 @@ bool equal_types(const cpp_entity_index& idx, const cpp_type& parsed, const cpp_
|
|||
&& equal_types(idx, cv_a.type(), cv_b.type());
|
||||
}
|
||||
|
||||
case cpp_type_kind::pointer:
|
||||
case cpp_type_kind::pointer_t:
|
||||
return equal_types(idx, static_cast<const cpp_pointer_type&>(parsed).pointee(),
|
||||
static_cast<const cpp_pointer_type&>(synthesized).pointee());
|
||||
case cpp_type_kind::reference:
|
||||
case cpp_type_kind::reference_t:
|
||||
{
|
||||
auto& ref_a = static_cast<const cpp_reference_type&>(parsed);
|
||||
auto& ref_b = static_cast<const cpp_reference_type&>(synthesized);
|
||||
|
|
@ -59,7 +59,7 @@ bool equal_types(const cpp_entity_index& idx, const cpp_type& parsed, const cpp_
|
|||
&& equal_types(idx, ref_a.referee(), ref_b.referee());
|
||||
}
|
||||
|
||||
case cpp_type_kind::array:
|
||||
case cpp_type_kind::array_t:
|
||||
{
|
||||
auto& array_a = static_cast<const cpp_array_type&>(parsed);
|
||||
auto& array_b = static_cast<const cpp_array_type&>(synthesized);
|
||||
|
|
@ -77,7 +77,7 @@ bool equal_types(const cpp_entity_index& idx, const cpp_type& parsed, const cpp_
|
|||
return equal_expressions(size_a, size_b);
|
||||
}
|
||||
|
||||
case cpp_type_kind::function:
|
||||
case cpp_type_kind::function_t:
|
||||
{
|
||||
auto& func_a = static_cast<const cpp_function_type&>(parsed);
|
||||
auto& func_b = static_cast<const cpp_function_type&>(synthesized);
|
||||
|
|
@ -98,7 +98,7 @@ bool equal_types(const cpp_entity_index& idx, const cpp_type& parsed, const cpp_
|
|||
}
|
||||
return iter_a == func_a.parameter_types().end() && iter_b == func_b.parameter_types().end();
|
||||
}
|
||||
case cpp_type_kind::member_function:
|
||||
case cpp_type_kind::member_function_t:
|
||||
{
|
||||
auto& func_a = static_cast<const cpp_member_function_type&>(parsed);
|
||||
auto& func_b = static_cast<const cpp_member_function_type&>(synthesized);
|
||||
|
|
@ -121,7 +121,7 @@ bool equal_types(const cpp_entity_index& idx, const cpp_type& parsed, const cpp_
|
|||
}
|
||||
return iter_a == func_a.parameter_types().end() && iter_b == func_b.parameter_types().end();
|
||||
}
|
||||
case cpp_type_kind::member_object:
|
||||
case cpp_type_kind::member_object_t:
|
||||
{
|
||||
auto& obj_a = static_cast<const cpp_member_object_type&>(parsed);
|
||||
auto& obj_b = static_cast<const cpp_member_object_type&>(synthesized);
|
||||
|
|
@ -131,14 +131,14 @@ bool equal_types(const cpp_entity_index& idx, const cpp_type& parsed, const cpp_
|
|||
return equal_types(idx, obj_a.object_type(), obj_b.object_type());
|
||||
}
|
||||
|
||||
case cpp_type_kind::template_parameter:
|
||||
case cpp_type_kind::template_parameter_t:
|
||||
{
|
||||
auto& entity_parsed = static_cast<const cpp_template_parameter_type&>(parsed).entity();
|
||||
auto& entity_synthesized =
|
||||
static_cast<const cpp_template_parameter_type&>(synthesized).entity();
|
||||
return equal_ref(idx, entity_parsed, entity_synthesized);
|
||||
}
|
||||
case cpp_type_kind::template_instantiation:
|
||||
case cpp_type_kind::template_instantiation_t:
|
||||
{
|
||||
auto& inst_parsed = static_cast<const cpp_template_instantiation_type&>(parsed);
|
||||
auto& inst_synthesized = static_cast<const cpp_template_instantiation_type&>(synthesized);
|
||||
|
|
@ -179,10 +179,10 @@ bool equal_types(const cpp_entity_index& idx, const cpp_type& parsed, const cpp_
|
|||
&& iter_b == inst_synthesized.arguments().end();
|
||||
}
|
||||
// TODO: implement equality when those can be parsed
|
||||
case cpp_type_kind::dependent:
|
||||
case cpp_type_kind::dependent_t:
|
||||
break;
|
||||
|
||||
case cpp_type_kind::unexposed:
|
||||
case cpp_type_kind::unexposed_t:
|
||||
return static_cast<const cpp_unexposed_type&>(parsed).name()
|
||||
== static_cast<const cpp_unexposed_type&>(synthesized).name();
|
||||
}
|
||||
|
|
@ -194,45 +194,66 @@ bool equal_types(const cpp_entity_index& idx, const cpp_type& parsed, const cpp_
|
|||
// other test cases don't need that anymore
|
||||
TEST_CASE("cpp_type_alias")
|
||||
{
|
||||
const char* code = nullptr;
|
||||
const char* code = nullptr;
|
||||
auto check_code = false;
|
||||
SECTION("using")
|
||||
{
|
||||
code = R"(
|
||||
check_code = true;
|
||||
code = R"(
|
||||
// basic
|
||||
/// using a=int;
|
||||
using a = int;
|
||||
/// using b=long double const volatile;
|
||||
using b = const long double volatile;
|
||||
|
||||
// pointers
|
||||
/// using c=int*;
|
||||
using c = int*;
|
||||
/// using d=unsigned int const*;
|
||||
using d = const unsigned int*;
|
||||
/// using e=unsigned int const* volatile;
|
||||
using e = unsigned const * volatile;
|
||||
|
||||
// references
|
||||
/// using f=int&;
|
||||
using f = int&;
|
||||
/// using g=int const&&;
|
||||
using g = const int&&;
|
||||
|
||||
// user-defined types
|
||||
/// using h=c;
|
||||
using h = c;
|
||||
/// using i=d const;
|
||||
using i = const d;
|
||||
/// using j=e*;
|
||||
using j = e*;
|
||||
|
||||
// arrays
|
||||
/// using k=int[42];
|
||||
using k = int[42];
|
||||
/// using l=float*[];
|
||||
using l = float*[];
|
||||
/// using m=char[42];
|
||||
using m = char[3 * 2 + 4 ? 42 : 43];
|
||||
|
||||
// function pointers
|
||||
/// using n=void(*)(int);
|
||||
using n = void(*)(int);
|
||||
/// using o=char*(&)(int const&,...);
|
||||
using o = char*(&)(const int&,...);
|
||||
/// using p=n(*)(int,o);
|
||||
using p = n(*)(int, o);
|
||||
|
||||
struct foo {};
|
||||
|
||||
// member function pointers
|
||||
/// using q=void(foo::*)(int);
|
||||
using q = void(foo::*)(int);
|
||||
/// using r=void(foo::*)(int,...)const&;
|
||||
using r = void(foo::*)(int,...) const &;
|
||||
|
||||
// member data pointers
|
||||
/// using s=int(foo::*);
|
||||
using s = int(foo::*);
|
||||
|
||||
// user-defined types inline definition
|
||||
|
|
@ -241,12 +262,14 @@ using u = const struct u_ {}*;
|
|||
using v = struct {};
|
||||
|
||||
// decltype
|
||||
/// using w=decltype(0);
|
||||
using w = decltype(0);
|
||||
)";
|
||||
}
|
||||
SECTION("typedef")
|
||||
{
|
||||
code = R"(
|
||||
check_code = false; // will always generate using
|
||||
code = R"(
|
||||
// basic
|
||||
typedef int a;
|
||||
typedef const long double volatile b;
|
||||
|
|
@ -455,6 +478,8 @@ typedef decltype(0) w;
|
|||
{
|
||||
auto type = cpp_user_defined_type::build(cpp_type_ref(cpp_entity_id(""), "t_"));
|
||||
REQUIRE(equal_types(idx, alias.underlying_type(), *type));
|
||||
return false; // inhibit comment check for next three entities
|
||||
// as they can't be documented (will always apply to the inline type)
|
||||
}
|
||||
else if (alias.name() == "u")
|
||||
{
|
||||
|
|
@ -462,11 +487,13 @@ typedef decltype(0) w;
|
|||
add_cv(cpp_user_defined_type::build(cpp_type_ref(cpp_entity_id(""), "u_")),
|
||||
cpp_cv_const));
|
||||
REQUIRE(equal_types(idx, alias.underlying_type(), *type));
|
||||
return false;
|
||||
}
|
||||
else if (alias.name() == "v")
|
||||
{
|
||||
auto type = cpp_user_defined_type::build(cpp_type_ref(cpp_entity_id(""), "v"));
|
||||
REQUIRE(equal_types(idx, alias.underlying_type(), *type));
|
||||
return false;
|
||||
}
|
||||
else if (alias.name() == "w")
|
||||
{
|
||||
|
|
@ -476,6 +503,8 @@ typedef decltype(0) w;
|
|||
}
|
||||
else
|
||||
REQUIRE(false);
|
||||
|
||||
return check_code;
|
||||
});
|
||||
REQUIRE(count == 23u);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,17 +14,25 @@ TEST_CASE("cpp_variable")
|
|||
{
|
||||
auto code = R"(
|
||||
// basic
|
||||
/// int a;
|
||||
int a;
|
||||
/// unsigned long long b=42;
|
||||
unsigned long long b = 42;
|
||||
/// float c=3.f+0.14f;
|
||||
float c = 3.f + 0.14f;
|
||||
|
||||
// with storage class specifiers
|
||||
/// extern int d;
|
||||
extern int d; // actually declaration
|
||||
/// static int e;
|
||||
static int e;
|
||||
/// thread_local int f;
|
||||
thread_local int f;
|
||||
/// static thread_local int g;
|
||||
thread_local static int g;
|
||||
|
||||
// constexpr
|
||||
/// constexpr int const h=12;
|
||||
constexpr int h = 12;
|
||||
|
||||
// inline definition
|
||||
|
|
@ -34,11 +42,15 @@ struct baz {} k{};
|
|||
static struct {} l;
|
||||
|
||||
// auto
|
||||
/// auto m=128;
|
||||
auto m = 128;
|
||||
/// auto const& n=m;
|
||||
const auto& n = m;
|
||||
|
||||
// decltype
|
||||
/// decltype(0) o;
|
||||
decltype(0) o;
|
||||
/// decltype(o) const& p=o;
|
||||
const decltype(o)& p = o;
|
||||
)";
|
||||
|
||||
|
|
@ -106,10 +118,14 @@ const decltype(o)& p = o;
|
|||
"12")),
|
||||
cpp_storage_class_none, true, false);
|
||||
else if (var.name() == "i")
|
||||
{
|
||||
check_variable(var,
|
||||
*cpp_user_defined_type::build(cpp_type_ref(cpp_entity_id(""), "foo")),
|
||||
nullptr, cpp_storage_class_none, false, false);
|
||||
return false; // can't check code here
|
||||
}
|
||||
else if (var.name() == "j")
|
||||
{
|
||||
check_variable(var, *cpp_cv_qualified_type::build(cpp_user_defined_type::build(
|
||||
cpp_type_ref(cpp_entity_id(""),
|
||||
"bar")),
|
||||
|
|
@ -120,7 +136,10 @@ const decltype(o)& p = o;
|
|||
"bar")),
|
||||
"bar()")),
|
||||
cpp_storage_class_none, false, false);
|
||||
return false;
|
||||
}
|
||||
else if (var.name() == "k")
|
||||
{
|
||||
check_variable(var,
|
||||
*cpp_user_defined_type::build(cpp_type_ref(cpp_entity_id(""), "baz")),
|
||||
type_safe::ref(
|
||||
|
|
@ -129,9 +148,14 @@ const decltype(o)& p = o;
|
|||
"baz")),
|
||||
"{}")),
|
||||
cpp_storage_class_none, false, false);
|
||||
return false;
|
||||
}
|
||||
else if (var.name() == "l")
|
||||
{
|
||||
check_variable(var, *cpp_user_defined_type::build(cpp_type_ref(cpp_entity_id(""), "")),
|
||||
nullptr, cpp_storage_class_static, false, false);
|
||||
return false;
|
||||
}
|
||||
else if (var.name() == "m")
|
||||
check_variable(var, *cpp_auto_type::build(),
|
||||
type_safe::ref(
|
||||
|
|
@ -167,6 +191,8 @@ const decltype(o)& p = o;
|
|||
cpp_storage_class_none, false, false);
|
||||
else
|
||||
REQUIRE(false);
|
||||
|
||||
return true;
|
||||
});
|
||||
REQUIRE(count == 16u);
|
||||
}
|
||||
|
|
@ -176,8 +202,11 @@ TEST_CASE("static cpp_variable")
|
|||
auto code = R"(
|
||||
struct test
|
||||
{
|
||||
/// static int a;
|
||||
static int a;
|
||||
/// static int const b;
|
||||
static const int b;
|
||||
/// static thread_local int c;
|
||||
static thread_local int c;
|
||||
};
|
||||
)";
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include <catch.hpp>
|
||||
|
||||
#include <cppast/code_generator.hpp>
|
||||
#include <cppast/cpp_entity_kind.hpp>
|
||||
#include <cppast/cpp_expression.hpp>
|
||||
#include <cppast/cpp_type.hpp>
|
||||
|
|
@ -39,8 +40,72 @@ inline std::unique_ptr<cppast::cpp_file> parse(const cppast::cpp_entity_index& i
|
|||
return result;
|
||||
}
|
||||
|
||||
class test_generator : public cppast::code_generator
|
||||
{
|
||||
public:
|
||||
const std::string& str() const noexcept
|
||||
{
|
||||
return str_;
|
||||
}
|
||||
|
||||
private:
|
||||
void do_indent() override
|
||||
{
|
||||
++indent_;
|
||||
}
|
||||
|
||||
void do_unindent() override
|
||||
{
|
||||
if (indent_)
|
||||
--indent_;
|
||||
}
|
||||
|
||||
void do_write_token_seq(cppast::string_view tokens)
|
||||
{
|
||||
if (was_newline_)
|
||||
{
|
||||
str_ += std::string(indent_ * 2u, ' ');
|
||||
was_newline_ = false;
|
||||
}
|
||||
str_ += tokens.c_str();
|
||||
}
|
||||
|
||||
void do_write_newline() override
|
||||
{
|
||||
str_ += "\n";
|
||||
was_newline_ = true;
|
||||
}
|
||||
|
||||
std::string str_;
|
||||
unsigned indent_ = 0;
|
||||
bool was_newline_ = false;
|
||||
};
|
||||
|
||||
inline std::string get_code(const cppast::cpp_entity& e)
|
||||
{
|
||||
test_generator generator;
|
||||
cppast::generate_code(generator, e);
|
||||
auto str = generator.str();
|
||||
if (!str.empty() && str.back() == '\n')
|
||||
str.pop_back();
|
||||
return str;
|
||||
}
|
||||
|
||||
template <typename Func, typename T>
|
||||
auto visit_callback(bool, Func f, const T& t) -> decltype(f(t) == true)
|
||||
{
|
||||
return f(t);
|
||||
}
|
||||
|
||||
template <typename Func, typename T>
|
||||
bool visit_callback(int check, Func f, const T& t)
|
||||
{
|
||||
f(t);
|
||||
return check == 1;
|
||||
}
|
||||
|
||||
template <typename T, typename Func>
|
||||
unsigned test_visit(const cppast::cpp_file& file, Func f)
|
||||
unsigned test_visit(const cppast::cpp_file& file, Func f, bool check_code = true)
|
||||
{
|
||||
auto count = 0u;
|
||||
cppast::visit(file, [&](const cppast::cpp_entity& e, cppast::visitor_info info) {
|
||||
|
|
@ -49,9 +114,16 @@ unsigned test_visit(const cppast::cpp_file& file, Func f)
|
|||
|
||||
if (e.kind() == T::kind())
|
||||
{
|
||||
auto& obj = static_cast<const T&>(e);
|
||||
f(obj);
|
||||
auto& obj = static_cast<const T&>(e);
|
||||
auto check_cur = visit_callback(check_code, f, obj);
|
||||
++count;
|
||||
|
||||
if (check_cur)
|
||||
{
|
||||
INFO(e.name());
|
||||
REQUIRE(e.comment());
|
||||
REQUIRE(e.comment().value() == get_code(e));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
@ -88,11 +160,11 @@ inline bool equal_expressions(const cppast::cpp_expression& parsed,
|
|||
return false;
|
||||
switch (parsed.kind())
|
||||
{
|
||||
case cpp_expression_kind::unexposed:
|
||||
case cpp_expression_kind::unexposed_t:
|
||||
return static_cast<const cpp_unexposed_expression&>(parsed).expression()
|
||||
== static_cast<const cpp_unexposed_expression&>(synthesized).expression();
|
||||
|
||||
case cpp_expression_kind::literal:
|
||||
case cpp_expression_kind::literal_t:
|
||||
return static_cast<const cpp_literal_expression&>(parsed).value()
|
||||
== static_cast<const cpp_literal_expression&>(synthesized).value();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue