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