Implement code generation

This commit is contained in:
Jonathan Müller 2017-03-29 19:51:02 +02:00
commit d18070a799
29 changed files with 2522 additions and 280 deletions

View 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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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
View 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
View 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;
}
}

View file

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

View file

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

View file

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

View file

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

View file

@ -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 {};
)";

View file

@ -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> {};
)";

View file

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

View file

@ -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() {}
)";

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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
}

View file

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

View file

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

View file

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

View file

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