Add exclude_return and exclude_target code generation options

This commit is contained in:
Jonathan Müller 2017-05-25 21:04:50 +02:00
commit c86e950847
5 changed files with 151 additions and 29 deletions

View file

@ -8,6 +8,7 @@
#include <cstring>
#include <type_safe/index.hpp>
#include <type_safe/flag_set.hpp>
#include <cppast/cpp_entity.hpp>
#include <cppast/cpp_entity_ref.hpp>
@ -129,14 +130,19 @@ namespace cppast
code_generator& operator=(const code_generator&) = delete;
virtual ~code_generator() noexcept = default;
/// Options that control the synopsis.
enum synopsis_options
/// Flags that control the synopsis.
enum synopsis_flags
{
exclude, //< Exclude the entire entity.
declaration, //< Only write declaration.
definition, //< Also write definition.
exclude, //< Exclude the entire entity.
exclude_return, //< Exclude the return type of a function entity.
exclude_target, //< Exclude the underlying entity of an alias (e.g. typedef).
declaration, //< Only write declaration.
_flag_set_size, //< \exclude
};
/// Options that control the synopsis.
using synopsis_options = type_safe::flag_set<synopsis_flags>;
/// Sentinel type used to output a given entity.
class output
{
@ -177,10 +183,16 @@ namespace cppast
return options_ != exclude;
}
/// \returns The synopsis options.
synopsis_options options() const noexcept
{
return options_;
}
/// \returns Whether or not the definition should be generated as well.
bool generate_definition() const noexcept
{
return options_ == definition;
return !(options_ & declaration);
}
/// \returns A reference to the generator.
@ -280,6 +292,14 @@ namespace cppast
return *this;
}
/// \effects Calls `do_write_excluded()`.
const output& excluded(const cpp_entity& e) const
{
if (*this)
gen_->do_write_excluded(e);
return *this;
}
/// \effects Calls `do_write_newline()`.
const output& operator<<(newl_t) const
{
@ -309,11 +329,11 @@ namespace cppast
/// \effects Will be invoked before code of a container entity is generated.
/// The base class version has no effect.
/// \returns The synopsis options for that entity,
/// the base class version always returns `definition`.
/// the base class version always returns no special options.
virtual synopsis_options on_container_begin(const cpp_entity& e)
{
(void)e;
return definition;
return {};
}
/// \effects Will be invoked after all code of a container entity has been generated.
@ -326,11 +346,11 @@ namespace cppast
/// \effects Will be invoked before code of a non-container entity is generated.
/// The base class version has no effect.
/// \returns The synopsis options for that entity,
/// the base class version always returns `definition`.
/// the base class version always returns no special options.
virtual synopsis_options on_leaf(const cpp_entity& e)
{
(void)e;
return definition;
return {};
}
/// \effects Will be invoked when the indentation level should be increased by one.
@ -390,6 +410,14 @@ namespace cppast
do_write_token_seq(punct);
}
/// \effects Writes a string for an excluded target or return type for the given entity.
/// The base class version writes the identifier `excluded`.
virtual void do_write_excluded(const cpp_entity& e)
{
(void)e;
do_write_identifier("excluded");
}
/// \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()`.

View file

@ -146,8 +146,15 @@ namespace
{
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;
<< punctuation("=");
if (output.options() & code_generator::exclude_target)
output.excluded(alias);
else
output << alias.target();
output << punctuation(";") << newl;
}
}
void generate_using_directive(code_generator& generator, const cpp_using_directive& directive)
@ -175,7 +182,10 @@ namespace
{
output << keyword("using") << whitespace << identifier(alias.name())
<< punctuation("=");
detail::write_type(output, alias.underlying_type(), "");
if (output.options() & code_generator::exclude_target)
output.excluded(alias);
else
detail::write_type(output, alias.underlying_type(), "");
output << punctuation(";") << newl;
}
}
@ -485,7 +495,9 @@ namespace
output << keyword("friend") << whitespace;
write_storage_class(output, func.storage_class(), func.is_constexpr());
if (detail::is_complex_type(func.return_type()))
if (output.options() & code_generator::exclude_return)
output.excluded(func) << whitespace;
else if (detail::is_complex_type(func.return_type()))
output << keyword("auto") << whitespace;
else
{
@ -504,7 +516,8 @@ namespace
write_function_parameters(output, func);
write_noexcept(output, func, false);
if (detail::is_complex_type(func.return_type()))
if (!(output.options() & code_generator::exclude_return)
&& detail::is_complex_type(func.return_type()))
{
output << punctuation("->");
detail::write_type(output, func.return_type(), "");
@ -579,7 +592,9 @@ namespace
else
write_prefix_virtual(output, func.virtual_info());
if (detail::is_complex_type(func.return_type()))
if (output.options() & code_generator::exclude_return)
output.excluded(func) << whitespace;
else if (detail::is_complex_type(func.return_type()))
output << keyword("auto") << whitespace;
else
{
@ -599,7 +614,8 @@ namespace
auto need_ws = write_cv_ref(output, func);
write_noexcept(output, func, need_ws);
if (detail::is_complex_type(func.return_type()))
if (!(output.options() & code_generator::exclude_return)
&& detail::is_complex_type(func.return_type()))
{
output << punctuation("->");
detail::write_type(output, func.return_type(), "");
@ -628,7 +644,11 @@ namespace
auto pos = op.name().find("operator");
output << identifier(op.name().substr(0u, pos)) << keyword("operator") << whitespace;
detail::write_type(output, op.return_type(), "");
if (output.options() & code_generator::exclude_return)
output.excluded(op);
else
detail::write_type(output, op.return_type(), "");
output << punctuation("(") << punctuation(")");
auto need_ws = write_cv_ref(output, op);
write_noexcept(output, op, need_ws);

View file

@ -6,11 +6,15 @@
#include "test_parser.hpp"
using namespace cppast;
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;
SECTION("basic")
{
// 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;
@ -34,6 +38,60 @@ void func(int(*)(int));
extern void(* ptr)(int(*)(int))=&func;
)";
auto file = parse({}, "code_generator.cpp", code);
REQUIRE(get_code(*file) == code);
auto file = parse({}, "code_generator.cpp", code);
REQUIRE(get_code(*file) == code);
}
SECTION("exclude target")
{
auto code = R"(
namespace a {}
namespace b = a;
using c = int*;
typedef int d;
)";
auto synopsis = R"(namespace a{
}
namespace b=excluded;
using c=excluded;
using d=excluded;
)";
auto file = parse({}, "code_generator_exclude_target.cpp", code);
REQUIRE(get_code(*file, code_generator::exclude_target) == synopsis);
}
SECTION("exclude return")
{
auto code = R"(
void a();
template <typename T>
auto b() -> int*;
struct foo
{
int c() const&;
operator const int ();
};
)";
auto synopsis = R"(excluded a();
template<typename T>
excluded b();
struct foo{
excluded c()const&;
operator excluded();
};
)";
auto file = parse({}, "code_generator_exclude_return.cpp", code);
REQUIRE(get_code(*file, code_generator::exclude_return) == synopsis);
}
}

View file

@ -45,12 +45,26 @@ inline std::unique_ptr<cppast::cpp_file> parse(const cppast::cpp_entity_index& i
class test_generator : public cppast::code_generator
{
public:
test_generator(synopsis_options options) : options_(std::move(options))
{
}
const std::string& str() const noexcept
{
return str_;
}
private:
synopsis_options on_container_begin(const cppast::cpp_entity&) override
{
return options_;
}
synopsis_options on_leaf(const cppast::cpp_entity&) override
{
return options_;
}
void do_indent() override
{
++indent_;
@ -78,14 +92,16 @@ private:
was_newline_ = true;
}
std::string str_;
unsigned indent_ = 0;
bool was_newline_ = false;
std::string str_;
synopsis_options options_;
unsigned indent_ = 0;
bool was_newline_ = false;
};
inline std::string get_code(const cppast::cpp_entity& e)
inline std::string get_code(const cppast::cpp_entity& e,
cppast::code_generator::synopsis_options options = {})
{
test_generator generator;
test_generator generator(options);
cppast::generate_code(generator, e);
auto str = generator.str();
if (!str.empty() && str.back() == '\n')

View file

@ -82,14 +82,14 @@ void print_entity(std::ostream& out, const cppast::cpp_entity& e)
synopsis_options on_container_begin(const cppast::cpp_entity&) override
{
// generate declaration only
return synopsis_options::declaration;
return code_generator::declaration;
}
// called before code generation of a leaf entity
synopsis_options on_leaf(const cppast::cpp_entity&) override
{
// generate declaration only
return synopsis_options::declaration;
return code_generator::declaration;
}
// no need to handle indentation, as only a single line is used