Update clang-format

This commit is contained in:
Jonathan Müller 2018-09-20 19:57:03 +02:00
commit 9f18b7f4d8
89 changed files with 11377 additions and 11546 deletions

View file

@ -2,10 +2,10 @@ AccessModifierOffset: -4
AlignAfterOpenBracket: Align AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: true AlignConsecutiveAssignments: true
AlignConsecutiveDeclarations: true AlignConsecutiveDeclarations: true
AlignEscapedNewlinesLeft: false AlignEscapedNewlinesLeft: Right
AlignOperands: true AlignOperands: true
AlignTrailingComments: true AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: true AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: false AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Empty AllowShortFunctionsOnASingleLine: Empty
@ -16,28 +16,50 @@ AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: true AlwaysBreakTemplateDeclarations: true
BinPackArguments: true BinPackArguments: true
BinPackParameters: true BinPackParameters: true
BreakBeforeBinaryOperators: NonAssignment BreakBeforeBraces: Custom
BreakBeforeBraces: Allman BraceWrapping:
BreakBeforeTernaryOperators: false AfterClass: true
BreakConstructorInitializersBeforeComma: false AfterControlStatement: true
ConstructorInitializerAllOnOneLineOrOnePerLine: true AfterEnum: true
AfterFunction: true
AfterNamespace: true
AfterStruct: true
AfterUnion: true
AfterExternBlock: true
BeforeCatch: true
BeforeElse: true
SplitEmptyFunction: false
SplitEmptyRecord: false
SplitEmptyNamespace: false
BreakBeforeBinaryOperators: All
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeColon
BreakStringLiterals: false
ColumnLimit: 100
CompactNamespaces: true
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 0 ConstructorInitializerIndentWidth: 0
ContinuationIndentWidth: 4 ContinuationIndentWidth: 4
ColumnLimit: 100
Cpp11BracedListStyle: true Cpp11BracedListStyle: true
DerivePointerAlignment: false
FixNamespaceComments: true
IncludeBlocks: Preserve
IndentCaseLabels: false IndentCaseLabels: false
IndentPPDirectives: AfterHash
IndentWidth: 4 IndentWidth: 4
IndentWrappedFunctionNames: true IndentWrappedFunctionNames: true
Language: Cpp
KeepEmptyLinesAtTheStartOfBlocks: false KeepEmptyLinesAtTheStartOfBlocks: false
Language: Cpp
MaxEmptyLinesToKeep: 1 MaxEmptyLinesToKeep: 1
NamespaceIndentation: All NamespaceIndentation: Inner
PenaltyBreakBeforeFirstCallParameter: 19937 PenaltyBreakBeforeFirstCallParameter: 19937
PenaltyReturnTypeOnItsOwnLine: 19937 PenaltyReturnTypeOnItsOwnLine: 19937
PointerAlignment: Left PointerAlignment: Left
ReflowComments: false ReflowComments: true
SortIncludes: false SortIncludes: true
SortUsingDeclarations: true
SpaceAfterCStyleCast: false SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false SpaceInEmptyParentheses: false

View file

@ -5,7 +5,8 @@
/// \file /// \file
/// Generate equality comparisons. /// Generate equality comparisons.
/// ///
/// Given an input file, it will generate comparison operators for each class that has the [[generate::comparison]] attribute. /// Given an input file, it will generate comparison operators for each class that has the
/// [[generate::comparison]] attribute.
#include <algorithm> #include <algorithm>
#include <iostream> #include <iostream>
@ -94,8 +95,8 @@ void generate_comparison(const cppast::cpp_file& file)
{ {
// it is a new class // it is a new class
auto& class_ = static_cast<const cppast::cpp_class&>(e); auto& class_ = static_cast<const cppast::cpp_class&>(e);
auto& attribute = auto& attribute
cppast::has_attribute(e, "generate::comparison").value(); = cppast::has_attribute(e, "generate::comparison").value();
// generate requested operators // generate requested operators
if (attribute.arguments()) if (attribute.arguments())

View file

@ -5,8 +5,9 @@
/// \file /// \file
/// Generates enum category functions. /// Generates enum category functions.
/// ///
/// Given an input file, it will generate definitions for functions marked with [[generate::enum_category(name)]]. /// Given an input file, it will generate definitions for functions marked with
/// The function takes an enumerator and will return true if it is marked with the same category. /// [[generate::enum_category(name)]]. The function takes an enumerator and will return true if it
/// is marked with the same category.
#include <algorithm> #include <algorithm>
#include <cassert> #include <cassert>
@ -24,10 +25,10 @@ bool is_category(const cppast::cpp_enum_value& e, const std::string& name)
if (auto attr = cppast::has_attribute(e, "generate::enum_category")) if (auto attr = cppast::has_attribute(e, "generate::enum_category"))
{ {
// ... by looking for the token // ... by looking for the token
auto iter = auto iter
std::find_if(attr.value().arguments().value().begin(), = std::find_if(attr.value().arguments().value().begin(),
attr.value().arguments().value().end(), attr.value().arguments().value().end(),
[&](const cppast::cpp_token& tok) { return tok.spelling == name; }); [&](const cppast::cpp_token& tok) { return tok.spelling == name; });
return iter != attr.value().arguments().value().end(); return iter != attr.value().arguments().value().end();
} }
else else
@ -42,8 +43,10 @@ const cppast::cpp_enum& get_enum(const cppast::cpp_entity_index& index,
// it is an enum // it is an enum
assert(param_type.kind() == cppast::cpp_type_kind::user_defined_t); assert(param_type.kind() == cppast::cpp_type_kind::user_defined_t);
// lookup definition // lookup definition
auto& definition = auto& definition = static_cast<const cppast::cpp_user_defined_type&>(param_type)
static_cast<const cppast::cpp_user_defined_type&>(param_type).entity().get(index)[0u].get(); .entity()
.get(index)[0u]
.get();
assert(definition.kind() == cppast::cpp_entity_kind::enum_t); assert(definition.kind() == cppast::cpp_entity_kind::enum_t);
return static_cast<const cppast::cpp_enum&>(definition); return static_cast<const cppast::cpp_enum&>(definition);

View file

@ -5,7 +5,8 @@
/// \file /// \file
/// Generates enum `to_string()` code. /// Generates enum `to_string()` code.
/// ///
/// Given an input file, it will generate a to_string() function for all enums marked with [[generate::to_string]]. /// Given an input file, it will generate a to_string() function for all enums marked with
/// [[generate::to_string]].
#include <iostream> #include <iostream>
@ -43,8 +44,8 @@ void generate_to_string(const cppast::cpp_file& file)
<< ":\n"; << ":\n";
// attribute can be used to override the string // attribute can be used to override the string
if (auto attr = if (auto attr
cppast::has_attribute(enumerator, "generate::to_string")) = cppast::has_attribute(enumerator, "generate::to_string"))
std::cout << " return " std::cout << " return "
<< attr.value().arguments().value().as_string() << attr.value().arguments().value().as_string()
<< ";\n"; << ";\n";

View file

@ -5,7 +5,8 @@
/// \file /// \file
/// Serialization code generation. /// Serialization code generation.
/// ///
/// Given an input file, it will generate a serialize() function for each class marked with [[generate::serialize]]. /// Given an input file, it will generate a serialize() function for each class marked with
/// [[generate::serialize]].
#include <iostream> #include <iostream>

File diff suppressed because it is too large Load diff

View file

@ -8,134 +8,134 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <type_safe/reference.hpp>
#include <type_safe/flag_set.hpp> #include <type_safe/flag_set.hpp>
#include <type_safe/reference.hpp>
#include <cppast/detail/assert.hpp> #include <cppast/detail/assert.hpp>
namespace cppast namespace cppast
{ {
/// The C++ standard that should be used. /// The C++ standard that should be used.
enum class cpp_standard enum class cpp_standard
{
cpp_98,
cpp_03,
cpp_11,
cpp_14,
cpp_1z, //< Upcoming C++17 (experimental).
cpp_latest = cpp_standard::cpp_14, //< The latest supported C++ standard.
};
/// \returns A human readable string representing the option,
/// it is e.g. `c++14` for `cpp_14`.
inline const char* to_string(cpp_standard standard) noexcept
{
switch (standard)
{ {
cpp_98, case cpp_standard::cpp_98:
cpp_03, return "c++98";
cpp_11, case cpp_standard::cpp_03:
cpp_14, return "c++03";
case cpp_standard::cpp_11:
cpp_1z, //< Upcoming C++17 (experimental). return "c++11";
case cpp_standard::cpp_14:
cpp_latest = cpp_standard::cpp_14, //< The latest supported C++ standard. return "c++14";
}; case cpp_standard::cpp_1z:
return "c++1z";
/// \returns A human readable string representing the option,
/// it is e.g. `c++14` for `cpp_14`.
inline const char* to_string(cpp_standard standard) noexcept
{
switch (standard)
{
case cpp_standard::cpp_98:
return "c++98";
case cpp_standard::cpp_03:
return "c++03";
case cpp_standard::cpp_11:
return "c++11";
case cpp_standard::cpp_14:
return "c++14";
case cpp_standard::cpp_1z:
return "c++1z";
}
DEBUG_UNREACHABLE(detail::assert_handler{});
return "ups";
} }
/// Other special compilation flags. DEBUG_UNREACHABLE(detail::assert_handler{});
enum class compile_flag return "ups";
}
/// Other special compilation flags.
enum class compile_flag
{
gnu_extensions, //< Enable GCC extensions.
ms_extensions, //< Enable MSVC extensions.
ms_compatibility, //< Enable MSVC compatibility.
_flag_set_size, //< \exclude
};
/// A [ts::flag_set]() of [cppast::compile_flag]().
using compile_flags = type_safe::flag_set<compile_flag>;
/// Base class for the configuration of a [cppast::parser]().
class compile_config
{
public:
/// \effects Sets the given C++ standard and compilation flags.
void set_flags(cpp_standard standard, compile_flags flags = {})
{ {
gnu_extensions, //< Enable GCC extensions. do_set_flags(standard, flags);
}
ms_extensions, //< Enable MSVC extensions. /// \effects Adds the given path to the set of include directories.
ms_compatibility, //< Enable MSVC compatibility. void add_include_dir(std::string path)
_flag_set_size, //< \exclude
};
/// A [ts::flag_set]() of [cppast::compile_flag]().
using compile_flags = type_safe::flag_set<compile_flag>;
/// Base class for the configuration of a [cppast::parser]().
class compile_config
{ {
public: do_add_include_dir(std::move(path));
/// \effects Sets the given C++ standard and compilation flags. }
void set_flags(cpp_standard standard, compile_flags flags = {})
{
do_set_flags(standard, flags);
}
/// \effects Adds the given path to the set of include directories. /// \effects Defines the given macro.
void add_include_dir(std::string path) void define_macro(std::string name, std::string definition)
{ {
do_add_include_dir(std::move(path)); do_add_macro_definition(std::move(name), std::move(definition));
} }
/// \effects Defines the given macro. /// \effects Undefines the given macro.
void define_macro(std::string name, std::string definition) void undefine_macro(std::string name)
{ {
do_add_macro_definition(std::move(name), std::move(definition)); do_remove_macro_definition(std::move(name));
} }
/// \effects Undefines the given macro. /// \returns A unique name of the configuration.
void undefine_macro(std::string name) /// \notes This allows detecting mismatches of configurations and parsers.
{ const char* name() const noexcept
do_remove_macro_definition(std::move(name)); {
} return do_get_name();
}
/// \returns A unique name of the configuration. protected:
/// \notes This allows detecting mismatches of configurations and parsers. compile_config(std::vector<std::string> def_flags) : flags_(std::move(def_flags)) {}
const char* name() const noexcept
{
return do_get_name();
}
protected: compile_config(const compile_config&) = default;
compile_config(std::vector<std::string> def_flags) : flags_(std::move(def_flags)) {} compile_config& operator=(const compile_config&) = default;
compile_config(const compile_config&) = default; ~compile_config() noexcept = default;
compile_config& operator=(const compile_config&) = default;
~compile_config() noexcept = default; void add_flag(std::string flag)
{
flags_.push_back(std::move(flag));
}
void add_flag(std::string flag) const std::vector<std::string>& get_flags() const noexcept
{ {
flags_.push_back(std::move(flag)); return flags_;
} }
const std::vector<std::string>& get_flags() const noexcept private:
{ /// \effects Sets the given C++ standard and compilation flags.
return flags_; virtual void do_set_flags(cpp_standard standard, compile_flags flags) = 0;
}
private: /// \effects Adds the given path to the set of include directories.
/// \effects Sets the given C++ standard and compilation flags. virtual void do_add_include_dir(std::string path) = 0;
virtual void do_set_flags(cpp_standard standard, compile_flags flags) = 0;
/// \effects Adds the given path to the set of include directories. /// \effects Defines the given macro.
virtual void do_add_include_dir(std::string path) = 0; virtual void do_add_macro_definition(std::string name, std::string definition) = 0;
/// \effects Defines the given macro. /// \effects Undefines the given macro.
virtual void do_add_macro_definition(std::string name, std::string definition) = 0; virtual void do_remove_macro_definition(std::string name) = 0;
/// \effects Undefines the given macro. /// \returns A unique name of the configuration.
virtual void do_remove_macro_definition(std::string name) = 0; /// \notes This allows detecting mismatches of configurations and parsers.
virtual const char* do_get_name() const noexcept = 0;
/// \returns A unique name of the configuration. std::vector<std::string> flags_;
/// \notes This allows detecting mismatches of configurations and parsers. };
virtual const char* do_get_name() const noexcept = 0;
std::vector<std::string> flags_;
};
} // namespace cppast } // namespace cppast
#endif // CPPAST_COMPILE_CONFIG_HPP_INCLUDED #endif // CPPAST_COMPILE_CONFIG_HPP_INCLUDED

View file

@ -10,35 +10,34 @@
namespace cppast namespace cppast
{ {
/// A [cppast::cpp_entity]() modelling a C++ alias template. /// A [cppast::cpp_entity]() modelling a C++ alias template.
class cpp_alias_template final : public cpp_template class cpp_alias_template final : public cpp_template
{
public:
static cpp_entity_kind kind() noexcept;
/// Builder for [cppast::cpp_alias_template]().
class builder : public basic_builder<cpp_alias_template, cpp_type_alias>
{ {
public: public:
static cpp_entity_kind kind() noexcept; using basic_builder::basic_builder;
/// Builder for [cppast::cpp_alias_template]().
class builder : public basic_builder<cpp_alias_template, cpp_type_alias>
{
public:
using basic_builder::basic_builder;
};
/// \returns A reference to the type alias that is being templated.
const cpp_type_alias& type_alias() const noexcept
{
return static_cast<const cpp_type_alias&>(*begin());
}
private:
cpp_alias_template(std::unique_ptr<cpp_type_alias> alias)
: cpp_template(std::unique_ptr<cpp_entity>(alias.release()))
{
}
cpp_entity_kind do_get_entity_kind() const noexcept override;
friend basic_builder<cpp_alias_template, cpp_type_alias>;
}; };
/// \returns A reference to the type alias that is being templated.
const cpp_type_alias& type_alias() const noexcept
{
return static_cast<const cpp_type_alias&>(*begin());
}
private:
cpp_alias_template(std::unique_ptr<cpp_type_alias> alias)
: cpp_template(std::unique_ptr<cpp_entity>(alias.release()))
{}
cpp_entity_kind do_get_entity_kind() const noexcept override;
friend basic_builder<cpp_alias_template, cpp_type_alias>;
};
} // namespace cppast } // namespace cppast
#endif // CPPAST_CPP_ALIAS_TEMPLATE_HPP_INCLUDED #endif // CPPAST_CPP_ALIAS_TEMPLATE_HPP_INCLUDED

View file

@ -10,46 +10,45 @@
namespace cppast namespace cppast
{ {
/// An array of a [cppast::cpp_type](). /// An array of a [cppast::cpp_type]().
class cpp_array_type final : public cpp_type class cpp_array_type final : public cpp_type
{
public:
/// \returns A newly created array.
/// \notes `size` may be `nullptr`.
static std::unique_ptr<cpp_array_type> build(std::unique_ptr<cpp_type> type,
std::unique_ptr<cpp_expression> size)
{ {
public: return std::unique_ptr<cpp_array_type>(
/// \returns A newly created array. new cpp_array_type(std::move(type), std::move(size)));
/// \notes `size` may be `nullptr`. }
static std::unique_ptr<cpp_array_type> build(std::unique_ptr<cpp_type> type,
std::unique_ptr<cpp_expression> size)
{
return std::unique_ptr<cpp_array_type>(
new cpp_array_type(std::move(type), std::move(size)));
}
/// \returns A reference to the value [cppast::cpp_type](). /// \returns A reference to the value [cppast::cpp_type]().
const cpp_type& value_type() const noexcept const cpp_type& value_type() const noexcept
{ {
return *type_; return *type_;
} }
/// \returns An optional reference to the [cppast::cpp_expression]() that is the size of the array. /// \returns An optional reference to the [cppast::cpp_expression]() that is the size of the
/// \notes An unsized array - `T[]` - does not have a size. /// array. \notes An unsized array - `T[]` - does not have a size.
type_safe::optional_ref<const cpp_expression> size() const noexcept type_safe::optional_ref<const cpp_expression> size() const noexcept
{ {
return type_safe::opt_cref(size_.get()); return type_safe::opt_cref(size_.get());
} }
private: private:
cpp_array_type(std::unique_ptr<cpp_type> type, std::unique_ptr<cpp_expression> size) cpp_array_type(std::unique_ptr<cpp_type> type, std::unique_ptr<cpp_expression> size)
: type_(std::move(type)), size_(std::move(size)) : type_(std::move(type)), size_(std::move(size))
{ {}
}
cpp_type_kind do_get_kind() const noexcept override cpp_type_kind do_get_kind() const noexcept override
{ {
return cpp_type_kind::array_t; return cpp_type_kind::array_t;
} }
std::unique_ptr<cpp_type> type_; std::unique_ptr<cpp_type> type_;
std::unique_ptr<cpp_expression> size_; std::unique_ptr<cpp_expression> size_;
}; };
} // namespace cppast } // namespace cppast
#endif // CPPAST_CPP_ARRAY_TYPE_HPP_INCLUDED #endif // CPPAST_CPP_ARRAY_TYPE_HPP_INCLUDED

View file

@ -15,109 +15,107 @@
namespace cppast namespace cppast
{ {
/// The known C++ attributes. /// The known C++ attributes.
enum class cpp_attribute_kind enum class cpp_attribute_kind
{
// update get_attribute_kind() in tokenizer, when updating this
alignas_,
carries_dependency,
deprecated,
fallthrough,
maybe_unused,
nodiscard,
noreturn,
unknown, //< An unknown attribute.
};
/// A C++ attribute, including `alignas` specifiers.
///
/// It consists of a name, an optional namespace scope and optional arguments.
/// The scope is just a single identifier and doesn't include the `::` and can be given explicitly
/// or via using. The arguments are as specified in the source code but do not include the
/// outer-most `(` and `)`. It can also be variadic or not.
///
/// An attribute can be known or unknown.
/// A known attribute will have the [cppast::cpp_attribute_kind]() set properly.
class cpp_attribute
{
public:
/// \effects Creates a known attribute, potentially with arguments.
cpp_attribute(cpp_attribute_kind kind, type_safe::optional<cpp_token_string> arguments);
/// \effects Creates an unknown attribute giving it the optional scope, names, arguments and
/// whether it is variadic.
cpp_attribute(type_safe::optional<std::string> scope, std::string name,
type_safe::optional<cpp_token_string> arguments, bool is_variadic)
: scope_(std::move(scope)), arguments_(std::move(arguments)), name_(std::move(name)),
variadic_(is_variadic)
{}
/// \returns The kind of attribute, if it is known.
const cpp_attribute_kind& kind() const noexcept
{ {
// update get_attribute_kind() in tokenizer, when updating this return kind_;
}
alignas_, /// \returns The name of the attribute.
carries_dependency, const std::string& name() const noexcept
deprecated,
fallthrough,
maybe_unused,
nodiscard,
noreturn,
unknown, //< An unknown attribute.
};
/// A C++ attribute, including `alignas` specifiers.
///
/// It consists of a name, an optional namespace scope and optional arguments.
/// The scope is just a single identifier and doesn't include the `::` and can be given explicitly or via using.
/// The arguments are as specified in the source code but do not include the outer-most `(` and `)`.
/// It can also be variadic or not.
///
/// An attribute can be known or unknown.
/// A known attribute will have the [cppast::cpp_attribute_kind]() set properly.
class cpp_attribute
{ {
public: return name_;
/// \effects Creates a known attribute, potentially with arguments. }
cpp_attribute(cpp_attribute_kind kind, type_safe::optional<cpp_token_string> arguments);
/// \effects Creates an unknown attribute giving it the optional scope, names, arguments and whether it is variadic. /// \returns The scope of the attribute, if there is any.
cpp_attribute(type_safe::optional<std::string> scope, std::string name, const type_safe::optional<std::string>& scope() const noexcept
type_safe::optional<cpp_token_string> arguments, bool is_variadic) {
: scope_(std::move(scope)), return scope_;
arguments_(std::move(arguments)), }
name_(std::move(name)),
variadic_(is_variadic)
{
}
/// \returns The kind of attribute, if it is known. /// \returns Whether or not the attribute is variadic.
const cpp_attribute_kind& kind() const noexcept bool is_variadic() const noexcept
{ {
return kind_; return variadic_;
} }
/// \returns The name of the attribute. /// \returns The arguments of the attribute, if they are any.
const std::string& name() const noexcept const type_safe::optional<cpp_token_string>& arguments() const noexcept
{ {
return name_; return arguments_;
} }
/// \returns The scope of the attribute, if there is any. private:
const type_safe::optional<std::string>& scope() const noexcept type_safe::optional<std::string> scope_;
{ type_safe::optional<cpp_token_string> arguments_;
return scope_; std::string name_;
} cpp_attribute_kind kind_ = cpp_attribute_kind::unknown;
bool variadic_;
};
/// \returns Whether or not the attribute is variadic. /// A list of C++ attributes.
bool is_variadic() const noexcept using cpp_attribute_list = std::vector<cpp_attribute>;
{
return variadic_;
}
/// \returns The arguments of the attribute, if they are any. /// Checks whether an attribute is given.
const type_safe::optional<cpp_token_string>& arguments() const noexcept /// \returns `true` if the given attribute list (1-2) / entity (3-4) contain
{ /// an attribute of the given name (1+3) / kind (2+4).
return arguments_; /// `false` otherwise.
} /// \group has_attribute
type_safe::optional_ref<const cpp_attribute> has_attribute(const cpp_attribute_list& attributes,
const std::string& name);
private: /// \group has_attribute
type_safe::optional<std::string> scope_; type_safe::optional_ref<const cpp_attribute> has_attribute(const cpp_attribute_list& attributes,
type_safe::optional<cpp_token_string> arguments_; cpp_attribute_kind kind);
std::string name_;
cpp_attribute_kind kind_ = cpp_attribute_kind::unknown;
bool variadic_;
};
/// A list of C++ attributes. class cpp_entity;
using cpp_attribute_list = std::vector<cpp_attribute>;
/// Checks whether an attribute is given. /// \group has_attribute
/// \returns `true` if the given attribute list (1-2) / entity (3-4) contain type_safe::optional_ref<const cpp_attribute> has_attribute(const cpp_entity& e,
/// an attribute of the given name (1+3) / kind (2+4). const std::string& name);
/// `false` otherwise.
/// \group has_attribute
type_safe::optional_ref<const cpp_attribute> has_attribute(const cpp_attribute_list& attributes,
const std::string& name);
/// \group has_attribute /// \group has_attribute
type_safe::optional_ref<const cpp_attribute> has_attribute(const cpp_attribute_list& attributes, type_safe::optional_ref<const cpp_attribute> has_attribute(const cpp_entity& e,
cpp_attribute_kind kind); cpp_attribute_kind kind);
class cpp_entity;
/// \group has_attribute
type_safe::optional_ref<const cpp_attribute> has_attribute(const cpp_entity& e,
const std::string& name);
/// \group has_attribute
type_safe::optional_ref<const cpp_attribute> has_attribute(const cpp_entity& e,
cpp_attribute_kind kind);
} // namespace cppast } // namespace cppast
#endif // CPPAST_CPP_ATTRIBUTE_HPP_INCLUDED #endif // CPPAST_CPP_ATTRIBUTE_HPP_INCLUDED

View file

@ -12,239 +12,233 @@
namespace cppast namespace cppast
{ {
/// The keyword used on the declaration of a [cppast::cpp_class](). /// The keyword used on the declaration of a [cppast::cpp_class]().
enum class cpp_class_kind enum class cpp_class_kind
{
class_t,
struct_t,
union_t,
};
/// \returns The keyword as a string.
const char* to_string(cpp_class_kind kind) noexcept;
/// The C++ access specifiers.
enum cpp_access_specifier_kind : int
{
cpp_public,
cpp_protected,
cpp_private
};
/// \returns The access specifier keyword as a string.
const char* to_string(cpp_access_specifier_kind access) noexcept;
/// A [cppast::cpp_entity]() modelling a C++ access specifier.
class cpp_access_specifier final : public cpp_entity
{
public:
static cpp_entity_kind kind() noexcept;
/// \returns A newly created access specifier.
/// \notes It is not meant to be registered at the [cppast::cpp_entity_index](),
/// as nothing can refer to it.
static std::unique_ptr<cpp_access_specifier> build(cpp_access_specifier_kind kind)
{ {
class_t, return std::unique_ptr<cpp_access_specifier>(new cpp_access_specifier(kind));
struct_t, }
union_t,
};
/// \returns The keyword as a string. /// \returns The kind of access specifier.
const char* to_string(cpp_class_kind kind) noexcept; cpp_access_specifier_kind access_specifier() const noexcept
/// The C++ access specifiers.
enum cpp_access_specifier_kind : int
{ {
cpp_public, return access_;
cpp_protected, }
cpp_private
};
/// \returns The access specifier keyword as a string. private:
const char* to_string(cpp_access_specifier_kind access) noexcept; cpp_access_specifier(cpp_access_specifier_kind access)
: cpp_entity(to_string(access)), access_(access)
{}
/// A [cppast::cpp_entity]() modelling a C++ access specifier. cpp_entity_kind do_get_entity_kind() const noexcept override;
class cpp_access_specifier final : public cpp_entity
cpp_access_specifier_kind access_;
};
/// A [cppast::cpp_entity]() modelling a base class specifier.
class cpp_base_class final : public cpp_entity
{
public:
static cpp_entity_kind kind() noexcept;
/// \returns A newly created base class specifier.
/// \notes It is not meant to be registered at the [cppast::cpp_entity_index](),
/// as nothing can refer to the specifier itself.
static std::unique_ptr<cpp_base_class> build(std::string name, std::unique_ptr<cpp_type> base,
cpp_access_specifier_kind access, bool is_virtual)
{
return std::unique_ptr<cpp_base_class>(
new cpp_base_class(std::move(name), std::move(base), access, is_virtual));
}
/// \returns The type of the base class.
const cpp_type& type() const
{
return *type_;
}
/// \returns The access specifier of the base class.
cpp_access_specifier_kind access_specifier() const noexcept
{
return access_;
}
/// \returns Whether or not it is a `virtual` base class.
bool is_virtual() const noexcept
{
return virtual_;
}
private:
cpp_base_class(std::string name, std::unique_ptr<cpp_type> base,
cpp_access_specifier_kind access, bool is_virtual)
: cpp_entity(std::move(name)), type_(std::move(base)), access_(access), virtual_(is_virtual)
{}
cpp_entity_kind do_get_entity_kind() const noexcept override;
std::unique_ptr<cpp_type> type_;
cpp_access_specifier_kind access_;
bool virtual_;
};
/// A [cppast::cpp_entity]() modelling a C++ class.
///
/// This can either be a definition or just a forward declaration.
/// If it is just a forward declaration,
/// everything except the class type will not be available.
class cpp_class final : public cpp_entity,
public cpp_entity_container<cpp_class, cpp_entity>,
public cpp_forward_declarable
{
public:
static cpp_entity_kind kind() noexcept;
/// Builds a [cppast::cpp_class]().
class builder
{ {
public: public:
static cpp_entity_kind kind() noexcept; /// \effects Sets the name and kind and whether it is `final`.
explicit builder(std::string name, cpp_class_kind kind, bool is_final = false)
: class_(new cpp_class(std::move(name), kind, is_final))
{}
/// \returns A newly created access specifier. /// \effects Marks the class as final.
/// \notes It is not meant to be registered at the [cppast::cpp_entity_index](), void is_final() noexcept
/// as nothing can refer to it.
static std::unique_ptr<cpp_access_specifier> build(cpp_access_specifier_kind kind)
{ {
return std::unique_ptr<cpp_access_specifier>(new cpp_access_specifier(kind)); class_->final_ = true;
} }
/// \returns The kind of access specifier. /// \effects Builds a [cppast::cpp_base_class]() and adds it.
cpp_access_specifier_kind access_specifier() const noexcept cpp_base_class& base_class(std::string name, std::unique_ptr<cpp_type> type,
cpp_access_specifier_kind access, bool is_virtual)
{ {
return access_; return add_base_class(
cpp_base_class::build(std::move(name), std::move(type), access, is_virtual));
} }
/// \effects Adds a new base class.
cpp_base_class& add_base_class(std::unique_ptr<cpp_base_class> base) noexcept
{
auto bptr = base.get();
class_->bases_.push_back(*class_, std::move(base));
return *bptr;
}
/// \effects Builds a [cppast::cpp_access_specifier]() and adds it.
void access_specifier(cpp_access_specifier_kind access)
{
add_child(cpp_access_specifier::build(access));
}
/// \effects Adds an entity.
void add_child(std::unique_ptr<cpp_entity> child) noexcept
{
class_->add_child(std::move(child));
}
/// \returns The not yet finished class.
cpp_class& get() noexcept
{
return *class_;
}
/// \effects Registers the class in the [cppast::cpp_entity_index](),
/// using the given [cppast::cpp_entity_id]().
/// \returns The finished class.
std::unique_ptr<cpp_class> finish(const cpp_entity_index& idx, cpp_entity_id id,
type_safe::optional<cpp_entity_ref> semantic_parent);
/// \effects Marks the class as forward declaration.
/// \returns The finished class.
std::unique_ptr<cpp_class> finish_declaration(const cpp_entity_index& idx,
cpp_entity_id definition_id);
/// \effects Returns the finished class without registering it.
/// \notes This is intended for templated classes only.
std::unique_ptr<cpp_class> finish(type_safe::optional<cpp_entity_ref> semantic_parent);
/// \effects Returns the finish class without registering it and marks it as forward
/// declaration. \notes This is intended for templated classes only.
std::unique_ptr<cpp_class> finish_declaration(cpp_entity_id definition_id);
private: private:
cpp_access_specifier(cpp_access_specifier_kind access) std::unique_ptr<cpp_class> class_;
: cpp_entity(to_string(access)), access_(access)
{
}
cpp_entity_kind do_get_entity_kind() const noexcept override;
cpp_access_specifier_kind access_;
}; };
/// A [cppast::cpp_entity]() modelling a base class specifier. /// \returns The keyword used in the declaration of the class.
class cpp_base_class final : public cpp_entity cpp_class_kind class_kind() const noexcept
{ {
public: return kind_;
static cpp_entity_kind kind() noexcept; }
/// \returns A newly created base class specifier. /// \returns Whether or not the class was declared `final`.
/// \notes It is not meant to be registered at the [cppast::cpp_entity_index](), bool is_final() const noexcept
/// as nothing can refer to the specifier itself.
static std::unique_ptr<cpp_base_class> build(std::string name,
std::unique_ptr<cpp_type> base,
cpp_access_specifier_kind access,
bool is_virtual)
{
return std::unique_ptr<cpp_base_class>(
new cpp_base_class(std::move(name), std::move(base), access, is_virtual));
}
/// \returns The type of the base class.
const cpp_type& type() const
{
return *type_;
}
/// \returns The access specifier of the base class.
cpp_access_specifier_kind access_specifier() const noexcept
{
return access_;
}
/// \returns Whether or not it is a `virtual` base class.
bool is_virtual() const noexcept
{
return virtual_;
}
private:
cpp_base_class(std::string name, std::unique_ptr<cpp_type> base,
cpp_access_specifier_kind access, bool is_virtual)
: cpp_entity(std::move(name)), type_(std::move(base)), access_(access), virtual_(is_virtual)
{
}
cpp_entity_kind do_get_entity_kind() const noexcept override;
std::unique_ptr<cpp_type> type_;
cpp_access_specifier_kind access_;
bool virtual_;
};
/// A [cppast::cpp_entity]() modelling a C++ class.
///
/// This can either be a definition or just a forward declaration.
/// If it is just a forward declaration,
/// everything except the class type will not be available.
class cpp_class final : public cpp_entity,
public cpp_entity_container<cpp_class, cpp_entity>,
public cpp_forward_declarable
{ {
public: return final_;
static cpp_entity_kind kind() noexcept; }
/// Builds a [cppast::cpp_class](). /// \returns An iteratable object iterating over the [cppast::cpp_base_class]() specifiers.
class builder detail::iteratable_intrusive_list<cpp_base_class> bases() const noexcept
{ {
public: return type_safe::ref(bases_);
/// \effects Sets the name and kind and whether it is `final`. }
explicit builder(std::string name, cpp_class_kind kind, bool is_final = false)
: class_(new cpp_class(std::move(name), kind, is_final))
{
}
/// \effects Marks the class as final. private:
void is_final() noexcept cpp_class(std::string name, cpp_class_kind kind, bool final)
{ : cpp_entity(std::move(name)), kind_(kind), final_(final)
class_->final_ = true; {}
}
/// \effects Builds a [cppast::cpp_base_class]() and adds it. cpp_entity_kind do_get_entity_kind() const noexcept override;
cpp_base_class& base_class(std::string name, std::unique_ptr<cpp_type> type,
cpp_access_specifier_kind access, bool is_virtual)
{
return add_base_class(
cpp_base_class::build(std::move(name), std::move(type), access, is_virtual));
}
/// \effects Adds a new base class. type_safe::optional<cpp_scope_name> do_get_scope_name() const override
cpp_base_class& add_base_class(std::unique_ptr<cpp_base_class> base) noexcept {
{ return type_safe::ref(*this);
auto bptr = base.get(); }
class_->bases_.push_back(*class_, std::move(base));
return *bptr;
}
/// \effects Builds a [cppast::cpp_access_specifier]() and adds it. detail::intrusive_list<cpp_base_class> bases_;
void access_specifier(cpp_access_specifier_kind access) cpp_class_kind kind_;
{ bool final_;
add_child(cpp_access_specifier::build(access)); };
}
/// \effects Adds an entity. /// \returns The type the base class refers to.
void add_child(std::unique_ptr<cpp_entity> child) noexcept /// It is either a class or some form of typedef.
{ type_safe::optional_ref<const cpp_entity> get_class_or_typedef(const cpp_entity_index& index,
class_->add_child(std::move(child)); const cpp_base_class& base);
}
/// \returns The not yet finished class. /// \returns The type the base class refers to.
cpp_class& get() noexcept /// Typedefs are unwrapped.
{ type_safe::optional_ref<const cpp_class> get_class(const cpp_entity_index& index,
return *class_; const cpp_base_class& base);
}
/// \effects Registers the class in the [cppast::cpp_entity_index](),
/// using the given [cppast::cpp_entity_id]().
/// \returns The finished class.
std::unique_ptr<cpp_class> finish(const cpp_entity_index& idx, cpp_entity_id id,
type_safe::optional<cpp_entity_ref> semantic_parent);
/// \effects Marks the class as forward declaration.
/// \returns The finished class.
std::unique_ptr<cpp_class> finish_declaration(const cpp_entity_index& idx,
cpp_entity_id definition_id);
/// \effects Returns the finished class without registering it.
/// \notes This is intended for templated classes only.
std::unique_ptr<cpp_class> finish(type_safe::optional<cpp_entity_ref> semantic_parent);
/// \effects Returns the finish class without registering it and marks it as forward declaration.
/// \notes This is intended for templated classes only.
std::unique_ptr<cpp_class> finish_declaration(cpp_entity_id definition_id);
private:
std::unique_ptr<cpp_class> class_;
};
/// \returns The keyword used in the declaration of the class.
cpp_class_kind class_kind() const noexcept
{
return kind_;
}
/// \returns Whether or not the class was declared `final`.
bool is_final() const noexcept
{
return final_;
}
/// \returns An iteratable object iterating over the [cppast::cpp_base_class]() specifiers.
detail::iteratable_intrusive_list<cpp_base_class> bases() const noexcept
{
return type_safe::ref(bases_);
}
private:
cpp_class(std::string name, cpp_class_kind kind, bool final)
: cpp_entity(std::move(name)), kind_(kind), final_(final)
{
}
cpp_entity_kind do_get_entity_kind() const noexcept override;
type_safe::optional<cpp_scope_name> do_get_scope_name() const override
{
return type_safe::ref(*this);
}
detail::intrusive_list<cpp_base_class> bases_;
cpp_class_kind kind_;
bool final_;
};
/// \returns The type the base class refers to.
/// It is either a class or some form of typedef.
type_safe::optional_ref<const cpp_entity> get_class_or_typedef(const cpp_entity_index& index,
const cpp_base_class& base);
/// \returns The type the base class refers to.
/// Typedefs are unwrapped.
type_safe::optional_ref<const cpp_class> get_class(const cpp_entity_index& index,
const cpp_base_class& base);
} // namespace cppast } // namespace cppast
#endif // CPPAST_CPP_CLASS_HPP_INCLUDED #endif // CPPAST_CPP_CLASS_HPP_INCLUDED

View file

@ -10,65 +10,63 @@
namespace cppast namespace cppast
{ {
/// A [cppast::cpp_entity]() modelling a class template. /// A [cppast::cpp_entity]() modelling a class template.
class cpp_class_template final : public cpp_template class cpp_class_template final : public cpp_template
{
public:
static cpp_entity_kind kind() noexcept;
/// Builder for [cppast::cpp_class_template]().
class builder : public basic_builder<cpp_class_template, cpp_class>
{ {
public: public:
static cpp_entity_kind kind() noexcept; using basic_builder::basic_builder;
/// Builder for [cppast::cpp_class_template]().
class builder : public basic_builder<cpp_class_template, cpp_class>
{
public:
using basic_builder::basic_builder;
};
/// A reference to the class that is being templated.
const cpp_class& class_() const noexcept
{
return static_cast<const cpp_class&>(*begin());
}
private:
cpp_class_template(std::unique_ptr<cpp_class> func)
: cpp_template(std::unique_ptr<cpp_entity>(func.release()))
{
}
cpp_entity_kind do_get_entity_kind() const noexcept override;
friend basic_builder<cpp_class_template, cpp_class>;
}; };
/// A [cppast::cpp_entity]() modelling a class template specialization. /// A reference to the class that is being templated.
class cpp_class_template_specialization final : public cpp_template_specialization const cpp_class& class_() const noexcept
{
return static_cast<const cpp_class&>(*begin());
}
private:
cpp_class_template(std::unique_ptr<cpp_class> func)
: cpp_template(std::unique_ptr<cpp_entity>(func.release()))
{}
cpp_entity_kind do_get_entity_kind() const noexcept override;
friend basic_builder<cpp_class_template, cpp_class>;
};
/// A [cppast::cpp_entity]() modelling a class template specialization.
class cpp_class_template_specialization final : public cpp_template_specialization
{
public:
static cpp_entity_kind kind() noexcept;
/// Builder for [cppast::cpp_class_template_specialization]().
class builder : public specialization_builder<cpp_class_template_specialization, cpp_class>
{ {
public: public:
static cpp_entity_kind kind() noexcept; using specialization_builder::specialization_builder;
/// Builder for [cppast::cpp_class_template_specialization]().
class builder : public specialization_builder<cpp_class_template_specialization, cpp_class>
{
public:
using specialization_builder::specialization_builder;
};
/// A reference to the class that is being specialized.
const cpp_class& class_() const noexcept
{
return static_cast<const cpp_class&>(*begin());
}
private:
cpp_class_template_specialization(std::unique_ptr<cpp_class> func, cpp_template_ref primary)
: cpp_template_specialization(std::unique_ptr<cpp_entity>(func.release()), primary)
{
}
cpp_entity_kind do_get_entity_kind() const noexcept override;
friend specialization_builder<cpp_class_template_specialization, cpp_class>;
}; };
/// A reference to the class that is being specialized.
const cpp_class& class_() const noexcept
{
return static_cast<const cpp_class&>(*begin());
}
private:
cpp_class_template_specialization(std::unique_ptr<cpp_class> func, cpp_template_ref primary)
: cpp_template_specialization(std::unique_ptr<cpp_entity>(func.release()), primary)
{}
cpp_entity_kind do_get_entity_kind() const noexcept override;
friend specialization_builder<cpp_class_template_specialization, cpp_class>;
};
} // namespace cppast } // namespace cppast
#endif // CPPAST_CPP_CLASS_TEMPLATE_HPP_INCLUDED #endif // CPPAST_CPP_CLASS_TEMPLATE_HPP_INCLUDED

View file

@ -10,51 +10,51 @@
namespace cppast namespace cppast
{ {
/// A [cppast::cpp_type]() that isn't given but taken from an expression. /// A [cppast::cpp_type]() that isn't given but taken from an expression.
class cpp_decltype_type final : public cpp_type class cpp_decltype_type final : public cpp_type
{
public:
/// \returns A newly created `decltype` type.
static std::unique_ptr<cpp_decltype_type> build(std::unique_ptr<cpp_expression> expr)
{ {
public: return std::unique_ptr<cpp_decltype_type>(new cpp_decltype_type(std::move(expr)));
/// \returns A newly created `decltype` type. }
static std::unique_ptr<cpp_decltype_type> build(std::unique_ptr<cpp_expression> expr)
{
return std::unique_ptr<cpp_decltype_type>(new cpp_decltype_type(std::move(expr)));
}
/// \returns A reference to the expression given. /// \returns A reference to the expression given.
const cpp_expression& expression() const noexcept const cpp_expression& expression() const noexcept
{
return *expr_;
}
private:
cpp_decltype_type(std::unique_ptr<cpp_expression> expr) : expr_(std::move(expr)) {}
cpp_type_kind do_get_kind() const noexcept override
{
return cpp_type_kind::decltype_t;
}
std::unique_ptr<cpp_expression> expr_;
};
/// A [cppast::cpp_type]() that isn't given but deduced using the `decltype` rules.
class cpp_decltype_auto_type final : public cpp_type
{ {
public: return *expr_;
/// \returns A newly created `auto` type. }
static std::unique_ptr<cpp_decltype_auto_type> build()
{
return std::unique_ptr<cpp_decltype_auto_type>(new cpp_decltype_auto_type);
}
private: private:
cpp_decltype_auto_type() = default; cpp_decltype_type(std::unique_ptr<cpp_expression> expr) : expr_(std::move(expr)) {}
cpp_type_kind do_get_kind() const noexcept override cpp_type_kind do_get_kind() const noexcept override
{ {
return cpp_type_kind::decltype_auto_t; return cpp_type_kind::decltype_t;
} }
};
std::unique_ptr<cpp_expression> expr_;
};
/// A [cppast::cpp_type]() that isn't given but deduced using the `decltype` rules.
class cpp_decltype_auto_type final : public cpp_type
{
public:
/// \returns A newly created `auto` type.
static std::unique_ptr<cpp_decltype_auto_type> build()
{
return std::unique_ptr<cpp_decltype_auto_type>(new cpp_decltype_auto_type);
}
private:
cpp_decltype_auto_type() = default;
cpp_type_kind do_get_kind() const noexcept override
{
return cpp_type_kind::decltype_auto_t;
}
};
} // namespace cppast } // namespace cppast
#endif // CPPAST_CPP_DECLTYPE_TYPE_HPP_INCLUDED #endif // CPPAST_CPP_DECLTYPE_TYPE_HPP_INCLUDED

View file

@ -10,229 +10,227 @@
#include <type_safe/optional_ref.hpp> #include <type_safe/optional_ref.hpp>
#include <cppast/detail/intrusive_list.hpp>
#include <cppast/cpp_attribute.hpp> #include <cppast/cpp_attribute.hpp>
#include <cppast/cpp_token.hpp> #include <cppast/cpp_token.hpp>
#include <cppast/detail/intrusive_list.hpp>
namespace cppast namespace cppast
{ {
class cpp_entity; class cpp_entity;
enum class cpp_entity_kind; enum class cpp_entity_kind;
class cpp_entity_index; class cpp_entity_index;
struct cpp_entity_id; struct cpp_entity_id;
class cpp_template_parameter; class cpp_template_parameter;
class cpp_template; class cpp_template;
/// The name of a scope. /// The name of a scope.
///
/// It is a combination of a name and optional template parameters.
class cpp_scope_name
{
public:
/// \effects Creates a scope out of a given entity.
cpp_scope_name(type_safe::object_ref<const cpp_entity> entity);
/// \returns The name of the scope.
const std::string& name() const noexcept;
/// \returns Whether or not the scope is templated.
bool is_templated() const noexcept
{
return templ_.has_value();
}
/// \returns An iteratable object iterating over the [cppast::cpp_template_parameter]() entities
/// of the scope. \requires The scope is templated.
detail::iteratable_intrusive_list<cpp_template_parameter> template_parameters() const noexcept;
private:
type_safe::object_ref<const cpp_entity> entity_;
type_safe::optional_ref<const cpp_template> templ_;
};
/// The base class for all entities in the C++ AST.
class cpp_entity : detail::intrusive_list_node<cpp_entity>
{
public:
cpp_entity(const cpp_entity&) = delete;
cpp_entity& operator=(const cpp_entity&) = delete;
virtual ~cpp_entity() noexcept = default;
/// \returns The kind of the entity.
cpp_entity_kind kind() const noexcept
{
return do_get_entity_kind();
}
/// \returns The name of the entity.
/// The name is the string associated with the entity's declaration.
const std::string& name() const noexcept
{
return name_;
}
/// \returns The name of the new scope created by the entity,
/// if there is any.
type_safe::optional<cpp_scope_name> scope_name() const
{
return do_get_scope_name();
}
/// \returns A [ts::optional_ref]() to the parent entity in the AST.
type_safe::optional_ref<const cpp_entity> parent() const noexcept
{
return parent_;
}
/// \returns The documentation comment associated with that entity, if any.
/// \notes A documentation comment can have three forms:
/// ///
/// It is a combination of a name and optional template parameters. /// * A C style doc comment. It is a C style comment starting with an additional `*`, i.e.
class cpp_scope_name /// `/**`. One space after the leading sequence will be skipped. It ends either with `*/` or
{ /// `**/`. After a newline all whitespace is skipped, as well as an optional `*` followed by
public: /// another optional space, as well as trailing whitespace on each line. I.e. `/** a\n * b
/// \effects Creates a scope out of a given entity. /// */` yields the text `a\nb`.
cpp_scope_name(type_safe::object_ref<const cpp_entity> entity); /// * A C++ style doc comment. It is a C++ style comment starting with an additional `/` or '!`,
/// i.e. `///` or `//!`.
/// \returns The name of the scope. /// One space character after the leading sequence will be skipped,
const std::string& name() const noexcept; /// as well as any trailing whitespace.
/// Two C++ style doc comments on two adjacent lines will be merged.
/// \returns Whether or not the scope is templated. /// * An end of line doc comment. It is a C++ style comment starting with an '<', i.e. `//<`.
bool is_templated() const noexcept /// One space character after the leading sequence will be skipped,
{ /// as well as any trailing whitespace.
return templ_.has_value(); /// If the next line is a C++ style doc comment, it will be merged with that one.
}
/// \returns An iteratable object iterating over the [cppast::cpp_template_parameter]() entities of the scope.
/// \requires The scope is templated.
detail::iteratable_intrusive_list<cpp_template_parameter> template_parameters() const
noexcept;
private:
type_safe::object_ref<const cpp_entity> entity_;
type_safe::optional_ref<const cpp_template> templ_;
};
/// The base class for all entities in the C++ AST.
class cpp_entity : detail::intrusive_list_node<cpp_entity>
{
public:
cpp_entity(const cpp_entity&) = delete;
cpp_entity& operator=(const cpp_entity&) = delete;
virtual ~cpp_entity() noexcept = default;
/// \returns The kind of the entity.
cpp_entity_kind kind() const noexcept
{
return do_get_entity_kind();
}
/// \returns The name of the entity.
/// The name is the string associated with the entity's declaration.
const std::string& name() const noexcept
{
return name_;
}
/// \returns The name of the new scope created by the entity,
/// if there is any.
type_safe::optional<cpp_scope_name> scope_name() const
{
return do_get_scope_name();
}
/// \returns A [ts::optional_ref]() to the parent entity in the AST.
type_safe::optional_ref<const cpp_entity> parent() const noexcept
{
return parent_;
}
/// \returns The documentation comment associated with that entity, if any.
/// \notes A documentation comment can have three forms:
///
/// * A C style doc comment. It is a C style comment starting with an additional `*`, i.e. `/**`.
/// One space after the leading sequence will be skipped.
/// It ends either with `*/` or `**/`.
/// After a newline all whitespace is skipped, as well as an optional `*` followed by another optional space,
/// as well as trailing whitespace on each line.
/// I.e. `/** a\n * b */` yields the text `a\nb`.
/// * A C++ style doc comment. It is a C++ style comment starting with an additional `/` or '!`,
/// i.e. `///` or `//!`.
/// One space character after the leading sequence will be skipped,
/// as well as any trailing whitespace.
/// Two C++ style doc comments on two adjacent lines will be merged.
/// * An end of line doc comment. It is a C++ style comment starting with an '<', i.e. `//<`.
/// One space character after the leading sequence will be skipped,
/// as well as any trailing whitespace.
/// If the next line is a C++ style doc comment, it will be merged with that one.
///
/// A documentation comment is associated with an entity,
/// if for C and C++ style doc comments, the entity declaration begins
/// on the line after the last line of the comment,
/// and if for an end of line comment, the entity declaration ends
/// on the same line as the end of line comment.
///
/// This comment system is also used by [standardese](https://standardese.foonathan.net).
type_safe::optional_ref<const std::string> comment() const noexcept
{
return comment_.empty() ? nullptr : type_safe::opt_ref(&comment_);
}
/// \effects Sets the associated comment.
/// \requires The comment must not be empty, if there is one.
void set_comment(type_safe::optional<std::string> comment) noexcept
{
comment_ = comment.value_or("");
}
/// \returns The list of attributes that are specified for that entity.
const cpp_attribute_list& attributes() const noexcept
{
return attributes_;
}
/// \effects Adds an attribute for that entity.
void add_attribute(cpp_attribute attr) noexcept
{
attributes_.push_back(std::move(attr));
}
/// \effects Adds multiple arguments for that entity.
void add_attribute(const cpp_attribute_list& list) noexcept
{
attributes_.insert(attributes_.end(), list.begin(), list.end());
}
/// \returns The specified user data.
void* user_data() const noexcept
{
return user_data_.load();
}
/// \effects Sets some kind of user data.
///
/// User data is just some kind of pointer, there are no requirements.
/// The class will do no lifetime management.
///
/// User data is useful if you need to store additional data for an entity without the need to maintain a registry.
void set_user_data(void* data) const noexcept
{
user_data_ = data;
}
protected:
/// \effects Creates it giving it the the name.
cpp_entity(std::string name) : name_(std::move(name)), user_data_(nullptr) {}
private:
/// \returns The kind of the entity.
virtual cpp_entity_kind do_get_entity_kind() const noexcept = 0;
/// \returns The name of the new scope created by the entity, if any.
/// By default, there is no scope created.
virtual type_safe::optional<cpp_scope_name> do_get_scope_name() const
{
return type_safe::nullopt;
}
void on_insert(const cpp_entity& parent) noexcept
{
parent_ = type_safe::ref(parent);
}
std::string name_;
std::string comment_;
cpp_attribute_list attributes_;
type_safe::optional_ref<const cpp_entity> parent_;
mutable std::atomic<void*> user_data_;
template <typename T>
friend struct detail::intrusive_list_access;
friend detail::intrusive_list_node<cpp_entity>;
};
/// A [cppast::cpp_entity]() that isn't exposed directly.
/// ///
/// The only information available is the raw source code. /// A documentation comment is associated with an entity,
class cpp_unexposed_entity final : public cpp_entity /// if for C and C++ style doc comments, the entity declaration begins
/// on the line after the last line of the comment,
/// and if for an end of line comment, the entity declaration ends
/// on the same line as the end of line comment.
///
/// This comment system is also used by [standardese](https://standardese.foonathan.net).
type_safe::optional_ref<const std::string> comment() const noexcept
{ {
public: return comment_.empty() ? nullptr : type_safe::opt_ref(&comment_);
static cpp_entity_kind kind() noexcept; }
/// \returns A newly built and registered unexposed entity. /// \effects Sets the associated comment.
/// \notes It will be registered as a declaration. /// \requires The comment must not be empty, if there is one.
static std::unique_ptr<cpp_entity> build(const cpp_entity_index& index, cpp_entity_id id, void set_comment(type_safe::optional<std::string> comment) noexcept
std::string name, cpp_token_string spelling); {
comment_ = comment.value_or("");
}
/// \returns A newly built unnamed unexposed entity. /// \returns The list of attributes that are specified for that entity.
/// It will not be registered. const cpp_attribute_list& attributes() const noexcept
static std::unique_ptr<cpp_entity> build(cpp_token_string spelling); {
return attributes_;
}
/// \returns The spelling of that entity. /// \effects Adds an attribute for that entity.
const cpp_token_string& spelling() const noexcept void add_attribute(cpp_attribute attr) noexcept
{ {
return spelling_; attributes_.push_back(std::move(attr));
} }
private: /// \effects Adds multiple arguments for that entity.
cpp_unexposed_entity(std::string name, cpp_token_string spelling) void add_attribute(const cpp_attribute_list& list) noexcept
: cpp_entity(std::move(name)), spelling_(std::move(spelling)) {
{ attributes_.insert(attributes_.end(), list.begin(), list.end());
} }
cpp_entity_kind do_get_entity_kind() const noexcept override; /// \returns The specified user data.
void* user_data() const noexcept
{
return user_data_.load();
}
cpp_token_string spelling_; /// \effects Sets some kind of user data.
}; ///
/// User data is just some kind of pointer, there are no requirements.
/// The class will do no lifetime management.
///
/// User data is useful if you need to store additional data for an entity without the need to
/// maintain a registry.
void set_user_data(void* data) const noexcept
{
user_data_ = data;
}
/// \returns Whether or not the entity is templated. protected:
/// If this function returns `true` that means the entity is not the "real" entity, /// \effects Creates it giving it the the name.
/// but contains just the information for the template which is the parent entity. cpp_entity(std::string name) : name_(std::move(name)), user_data_(nullptr) {}
/// \notes Do not use this entity other to read information from the template entity.
bool is_templated(const cpp_entity& e) noexcept;
/// \returns Whether or not the given entity is "friended", private:
/// that is, its declaration exists as part of a [cppast::cpp_friend]() declaration. /// \returns The kind of the entity.
bool is_friended(const cpp_entity& e) noexcept; virtual cpp_entity_kind do_get_entity_kind() const noexcept = 0;
/// \returns The name of the new scope created by the entity, if any.
/// By default, there is no scope created.
virtual type_safe::optional<cpp_scope_name> do_get_scope_name() const
{
return type_safe::nullopt;
}
void on_insert(const cpp_entity& parent) noexcept
{
parent_ = type_safe::ref(parent);
}
std::string name_;
std::string comment_;
cpp_attribute_list attributes_;
type_safe::optional_ref<const cpp_entity> parent_;
mutable std::atomic<void*> user_data_;
template <typename T>
friend struct detail::intrusive_list_access;
friend detail::intrusive_list_node<cpp_entity>;
};
/// A [cppast::cpp_entity]() that isn't exposed directly.
///
/// The only information available is the raw source code.
class cpp_unexposed_entity final : public cpp_entity
{
public:
static cpp_entity_kind kind() noexcept;
/// \returns A newly built and registered unexposed entity.
/// \notes It will be registered as a declaration.
static std::unique_ptr<cpp_entity> build(const cpp_entity_index& index, cpp_entity_id id,
std::string name, cpp_token_string spelling);
/// \returns A newly built unnamed unexposed entity.
/// It will not be registered.
static std::unique_ptr<cpp_entity> build(cpp_token_string spelling);
/// \returns The spelling of that entity.
const cpp_token_string& spelling() const noexcept
{
return spelling_;
}
private:
cpp_unexposed_entity(std::string name, cpp_token_string spelling)
: cpp_entity(std::move(name)), spelling_(std::move(spelling))
{}
cpp_entity_kind do_get_entity_kind() const noexcept override;
cpp_token_string spelling_;
};
/// \returns Whether or not the entity is templated.
/// If this function returns `true` that means the entity is not the "real" entity,
/// but contains just the information for the template which is the parent entity.
/// \notes Do not use this entity other to read information from the template entity.
bool is_templated(const cpp_entity& e) noexcept;
/// \returns Whether or not the given entity is "friended",
/// that is, its declaration exists as part of a [cppast::cpp_friend]() declaration.
bool is_friended(const cpp_entity& e) noexcept;
} // namespace cppast } // namespace cppast
#endif // CPPAST_CPP_ENTITY_HPP_INCLUDED #endif // CPPAST_CPP_ENTITY_HPP_INCLUDED

View file

@ -9,51 +9,51 @@
namespace cppast namespace cppast
{ {
/// Helper class for entities that are containers. /// Helper class for entities that are containers.
/// ///
/// Inherit from it to generate container access. /// Inherit from it to generate container access.
template <class Derived, typename T> template <class Derived, typename T>
class cpp_entity_container class cpp_entity_container
{
public:
using iterator = typename detail::intrusive_list<T>::const_iterator;
/// \returns A const iterator to the first child.
iterator begin() const noexcept
{ {
public: return children_.begin();
using iterator = typename detail::intrusive_list<T>::const_iterator; }
/// \returns A const iterator to the first child. /// \returns A const iterator to the last child.
iterator begin() const noexcept iterator end() const noexcept
{ {
return children_.begin(); return children_.end();
} }
/// \returns A const iterator to the last child. protected:
iterator end() const noexcept /// \effects Adds a new child to the container.
{ void add_child(std::unique_ptr<T> ptr) noexcept
return children_.end(); {
} children_.push_back(static_cast<Derived&>(*this), std::move(ptr));
}
protected: /// \returns A non-const iterator to the first child.
/// \effects Adds a new child to the container. typename detail::intrusive_list<T>::iterator mutable_begin() noexcept
void add_child(std::unique_ptr<T> ptr) noexcept {
{ return children_.begin();
children_.push_back(static_cast<Derived&>(*this), std::move(ptr)); }
}
/// \returns A non-const iterator to the first child. /// \returns A non-const iterator one past the last child.
typename detail::intrusive_list<T>::iterator mutable_begin() noexcept typename detail::intrusive_list<T>::iterator mutable_end() noexcept
{ {
return children_.begin(); return children_.begin();
} }
/// \returns A non-const iterator one past the last child. ~cpp_entity_container() noexcept = default;
typename detail::intrusive_list<T>::iterator mutable_end() noexcept
{
return children_.begin();
}
~cpp_entity_container() noexcept = default; private:
detail::intrusive_list<T> children_;
private: };
detail::intrusive_list<T> children_;
};
} // namespace cppast } // namespace cppast
#endif // CPPAST_CPP_ENTITY_CONTAINER_HPP_INCLUDED #endif // CPPAST_CPP_ENTITY_CONTAINER_HPP_INCLUDED

View file

@ -16,130 +16,128 @@
namespace cppast namespace cppast
{ {
class cpp_entity; class cpp_entity;
class cpp_file; class cpp_file;
class cpp_namespace; class cpp_namespace;
/// \exclude /// \exclude
namespace detail namespace detail
{
constexpr std::size_t fnv_basis = 14695981039346656037ull;
constexpr std::size_t fnv_prime = 1099511628211ull;
// FNV-1a 64 bit hash
constexpr std::size_t id_hash(const char* str, std::size_t hash = fnv_basis)
{ {
constexpr std::size_t fnv_basis = 14695981039346656037ull; return *str ? id_hash(str + 1, (hash ^ std::size_t(*str)) * fnv_prime) : hash;
constexpr std::size_t fnv_prime = 1099511628211ull;
// FNV-1a 64 bit hash
constexpr std::size_t id_hash(const char* str, std::size_t hash = fnv_basis)
{
return *str ? id_hash(str + 1, (hash ^ std::size_t(*str)) * fnv_prime) : hash;
}
} // namespace detail
/// A [ts::strong_typedef]() representing the unique id of a [cppast::cpp_entity]().
///
/// It is comparable for equality.
struct cpp_entity_id : type_safe::strong_typedef<cpp_entity_id, std::size_t>,
type_safe::strong_typedef_op::equality_comparison<cpp_entity_id>
{
explicit cpp_entity_id(const std::string& str) : cpp_entity_id(str.c_str()) {}
explicit cpp_entity_id(const char* str) : strong_typedef(detail::id_hash(str)) {}
};
inline namespace literals
{
/// \returns A new [cppast::cpp_entity_id]() created from the given string.
inline cpp_entity_id operator"" _id(const char* str, std::size_t)
{
return cpp_entity_id(str);
}
} }
} // namespace detail
/// An index of all [cppast::cpp_entity]() objects created. /// A [ts::strong_typedef]() representing the unique id of a [cppast::cpp_entity]().
/// ///
/// It maps [cppast::cpp_entity_id]() to references to the [cppast::cpp_entity]() objects. /// It is comparable for equality.
class cpp_entity_index struct cpp_entity_id : type_safe::strong_typedef<cpp_entity_id, std::size_t>,
type_safe::strong_typedef_op::equality_comparison<cpp_entity_id>
{
explicit cpp_entity_id(const std::string& str) : cpp_entity_id(str.c_str()) {}
explicit cpp_entity_id(const char* str) : strong_typedef(detail::id_hash(str)) {}
};
inline namespace literals
{
/// \returns A new [cppast::cpp_entity_id]() created from the given string.
inline cpp_entity_id operator"" _id(const char* str, std::size_t)
{
return cpp_entity_id(str);
}
} // namespace literals
/// An index of all [cppast::cpp_entity]() objects created.
///
/// It maps [cppast::cpp_entity_id]() to references to the [cppast::cpp_entity]() objects.
class cpp_entity_index
{
public:
/// Exception thrown on duplicate entity definition.
class duplicate_definition_error : public std::logic_error
{ {
public: public:
/// Exception thrown on duplicate entity definition. duplicate_definition_error();
class duplicate_definition_error : public std::logic_error
{
public:
duplicate_definition_error();
};
/// \effects Registers a new [cppast::cpp_entity]() which is a definition.
/// It will override any previously registered declarations of the same entity.
/// \throws duplicate_defintion_error if the entity has been registered as definition before.
/// \requires The entity must live as long as the index lives,
/// and it must not be a namespace.
/// \notes This operation is thread safe.
void register_definition(cpp_entity_id id,
type_safe::object_ref<const cpp_entity> entity) const;
/// \effects Registers a new [cppast::cpp_file]().
/// \returns `true` if the file was not registered before.
/// If it returns `false`, the file was registered before and nothing was changed.
/// \requires The entity must live as long as the index lives.
/// \notes This operation is thread safe.
bool register_file(cpp_entity_id id, type_safe::object_ref<const cpp_file> file) const;
/// \effects Registers a new [cppast::cpp_entity]() which is a declaration.
/// Only the first declaration will be registered.
/// \requires The entity must live as long as the index lives.
/// \requires The entity must be forward declarable.
/// \notes This operation is thread safe.
void register_forward_declaration(cpp_entity_id id,
type_safe::object_ref<const cpp_entity> entity) const;
/// \effects Registers a new [cppast::cpp_namespace]().
/// \notes The namespace object must live as long as the index lives.
/// \notes This operation is thread safe.
void register_namespace(cpp_entity_id id,
type_safe::object_ref<const cpp_namespace> ns) const;
/// \returns A [ts::optional_ref]() corresponding to the entity(/ies) of the given [cppast::cpp_entity_id]().
/// If no definition has been registered, it return the first declaration that was registered.
/// If the id resolves to a namespaces, returns an empty optional.
/// \notes This operation is thread safe.
type_safe::optional_ref<const cpp_entity> lookup(const cpp_entity_id& id) const noexcept;
/// \returns A [ts::optional_ref]() corresponding to the entity of the given [cppast::cpp_entity_id]().
/// If no definition has been registered, it returns an empty optional.
/// \notes This operation is thread safe.
type_safe::optional_ref<const cpp_entity> lookup_definition(const cpp_entity_id& id) const
noexcept;
/// \returns A [ts::array_ref]() of references to all namespaces matching the given [cppast::cpp_entity_id]().
/// If no namespace is found, it returns an empty array reference.
/// \notes This operation is thread safe.
auto lookup_namespace(const cpp_entity_id& id) const noexcept
-> type_safe::array_ref<type_safe::object_ref<const cpp_namespace>>;
private:
struct hash
{
std::size_t operator()(const cpp_entity_id& id) const noexcept
{
return static_cast<std::size_t>(id);
}
};
struct value
{
type_safe::object_ref<const cpp_entity> entity;
bool is_definition;
value(type_safe::object_ref<const cpp_entity> e, bool def)
: entity(std::move(e)), is_definition(def)
{
}
};
mutable std::mutex mutex_;
mutable std::unordered_map<cpp_entity_id, value, hash> map_;
mutable std::unordered_map<cpp_entity_id,
std::vector<type_safe::object_ref<const cpp_namespace>>, hash>
ns_;
}; };
/// \effects Registers a new [cppast::cpp_entity]() which is a definition.
/// It will override any previously registered declarations of the same entity.
/// \throws duplicate_defintion_error if the entity has been registered as definition before.
/// \requires The entity must live as long as the index lives,
/// and it must not be a namespace.
/// \notes This operation is thread safe.
void register_definition(cpp_entity_id id,
type_safe::object_ref<const cpp_entity> entity) const;
/// \effects Registers a new [cppast::cpp_file]().
/// \returns `true` if the file was not registered before.
/// If it returns `false`, the file was registered before and nothing was changed.
/// \requires The entity must live as long as the index lives.
/// \notes This operation is thread safe.
bool register_file(cpp_entity_id id, type_safe::object_ref<const cpp_file> file) const;
/// \effects Registers a new [cppast::cpp_entity]() which is a declaration.
/// Only the first declaration will be registered.
/// \requires The entity must live as long as the index lives.
/// \requires The entity must be forward declarable.
/// \notes This operation is thread safe.
void register_forward_declaration(cpp_entity_id id,
type_safe::object_ref<const cpp_entity> entity) const;
/// \effects Registers a new [cppast::cpp_namespace]().
/// \notes The namespace object must live as long as the index lives.
/// \notes This operation is thread safe.
void register_namespace(cpp_entity_id id, type_safe::object_ref<const cpp_namespace> ns) const;
/// \returns A [ts::optional_ref]() corresponding to the entity(/ies) of the given
/// [cppast::cpp_entity_id](). If no definition has been registered, it return the first
/// declaration that was registered. If the id resolves to a namespaces, returns an empty
/// optional. \notes This operation is thread safe.
type_safe::optional_ref<const cpp_entity> lookup(const cpp_entity_id& id) const noexcept;
/// \returns A [ts::optional_ref]() corresponding to the entity of the given
/// [cppast::cpp_entity_id](). If no definition has been registered, it returns an empty
/// optional. \notes This operation is thread safe.
type_safe::optional_ref<const cpp_entity> lookup_definition(const cpp_entity_id& id) const
noexcept;
/// \returns A [ts::array_ref]() of references to all namespaces matching the given
/// [cppast::cpp_entity_id](). If no namespace is found, it returns an empty array reference.
/// \notes This operation is thread safe.
auto lookup_namespace(const cpp_entity_id& id) const noexcept
-> type_safe::array_ref<type_safe::object_ref<const cpp_namespace>>;
private:
struct hash
{
std::size_t operator()(const cpp_entity_id& id) const noexcept
{
return static_cast<std::size_t>(id);
}
};
struct value
{
type_safe::object_ref<const cpp_entity> entity;
bool is_definition;
value(type_safe::object_ref<const cpp_entity> e, bool def)
: entity(std::move(e)), is_definition(def)
{}
};
mutable std::mutex mutex_;
mutable std::unordered_map<cpp_entity_id, value, hash> map_;
mutable std::unordered_map<cpp_entity_id,
std::vector<type_safe::object_ref<const cpp_namespace>>, hash>
ns_;
};
} // namespace cppast } // namespace cppast
#endif // CPPAST_CPP_ENTITY_INDEX_HPP_INCLUDED #endif // CPPAST_CPP_ENTITY_INDEX_HPP_INCLUDED

View file

@ -9,81 +9,81 @@
namespace cppast namespace cppast
{ {
/// All possible kinds of C++ entities. /// All possible kinds of C++ entities.
enum class cpp_entity_kind enum class cpp_entity_kind
{ {
file_t, file_t,
macro_parameter_t, macro_parameter_t,
macro_definition_t, macro_definition_t,
include_directive_t, include_directive_t,
language_linkage_t, language_linkage_t,
namespace_t, namespace_t,
namespace_alias_t, namespace_alias_t,
using_directive_t, using_directive_t,
using_declaration_t, using_declaration_t,
type_alias_t, type_alias_t,
enum_t, enum_t,
enum_value_t, enum_value_t,
class_t, class_t,
access_specifier_t, access_specifier_t,
base_class_t, base_class_t,
variable_t, variable_t,
member_variable_t, member_variable_t,
bitfield_t, bitfield_t,
function_parameter_t, function_parameter_t,
function_t, function_t,
member_function_t, member_function_t,
conversion_op_t, conversion_op_t,
constructor_t, constructor_t,
destructor_t, destructor_t,
friend_t, friend_t,
template_type_parameter_t, template_type_parameter_t,
non_type_template_parameter_t, non_type_template_parameter_t,
template_template_parameter_t, template_template_parameter_t,
alias_template_t, alias_template_t,
variable_template_t, variable_template_t,
function_template_t, function_template_t,
function_template_specialization_t, function_template_specialization_t,
class_template_t, class_template_t,
class_template_specialization_t, class_template_specialization_t,
static_assert_t, static_assert_t,
unexposed_t, unexposed_t,
count, count,
}; };
/// \returns A human readable string describing the entity kind. /// \returns A human readable string describing the entity kind.
const char* to_string(cpp_entity_kind kind) noexcept; const char* to_string(cpp_entity_kind kind) noexcept;
/// \returns Whether or not a given entity kind is a C++ function, /// \returns Whether or not a given entity kind is a C++ function,
/// that is, it dervies from [cppast::cpp_function_base](). /// that is, it dervies from [cppast::cpp_function_base]().
bool is_function(cpp_entity_kind kind) noexcept; bool is_function(cpp_entity_kind kind) noexcept;
/// \returns Whether or not a given entity kind is a C++ (template) parameter. /// \returns Whether or not a given entity kind is a C++ (template) parameter.
bool is_parameter(cpp_entity_kind kind) noexcept; bool is_parameter(cpp_entity_kind kind) noexcept;
/// \returns Whether or not a given entity kind is a C++ template, /// \returns Whether or not a given entity kind is a C++ template,
/// that is, it dervies from [cppast::cpp_template](). /// that is, it dervies from [cppast::cpp_template]().
/// \notes A template template parameter is not considered a template for this function. /// \notes A template template parameter is not considered a template for this function.
/// \notes Template specializations are also considered templates here. /// \notes Template specializations are also considered templates here.
bool is_template(cpp_entity_kind kind) noexcept; bool is_template(cpp_entity_kind kind) noexcept;
/// \returns Whether or not a given entity kind is a specialization of a C++ template, /// \returns Whether or not a given entity kind is a specialization of a C++ template,
/// that is, it derives from [cppast::cpp_template_specialization](). /// that is, it derives from [cppast::cpp_template_specialization]().
bool is_template_specialization(cpp_entity_kind kind) noexcept; bool is_template_specialization(cpp_entity_kind kind) noexcept;
} // namespace cppast } // namespace cppast
#endif // CPPAST_CPP_ENTITY_KIND_HPP_INCLUDED #endif // CPPAST_CPP_ENTITY_KIND_HPP_INCLUDED

View file

@ -9,123 +9,121 @@
#include <type_safe/variant.hpp> #include <type_safe/variant.hpp>
#include <cppast/detail/assert.hpp>
#include <cppast/cpp_entity_index.hpp> #include <cppast/cpp_entity_index.hpp>
#include <cppast/detail/assert.hpp>
namespace cppast namespace cppast
{ {
enum class cpp_entity_kind; enum class cpp_entity_kind;
/// A basic reference to some kind of [cppast::cpp_entity](). /// A basic reference to some kind of [cppast::cpp_entity]().
/// ///
/// It can either refer to a single [cppast::cpp_entity]() /// It can either refer to a single [cppast::cpp_entity]()
/// or multiple. /// or multiple.
/// In the later case it is *overloaded*. /// In the later case it is *overloaded*.
template <typename T, typename Predicate> template <typename T, typename Predicate>
class basic_cpp_entity_ref class basic_cpp_entity_ref
{
public:
/// \effects Creates it giving it the target id and name.
basic_cpp_entity_ref(cpp_entity_id target_id, std::string target_name)
: target_(std::move(target_id)), name_(std::move(target_name))
{}
/// \effects Creates it giving it multiple target ids and name.
/// \notes This is to refer to an overloaded function.
basic_cpp_entity_ref(std::vector<cpp_entity_id> target_ids, std::string target_name)
: target_(std::move(target_ids)), name_(std::move(target_name))
{}
/// \returns The name of the reference, as spelled in the source code.
const std::string& name() const noexcept
{ {
public: return name_;
/// \effects Creates it giving it the target id and name.
basic_cpp_entity_ref(cpp_entity_id target_id, std::string target_name)
: target_(std::move(target_id)), name_(std::move(target_name))
{
}
/// \effects Creates it giving it multiple target ids and name.
/// \notes This is to refer to an overloaded function.
basic_cpp_entity_ref(std::vector<cpp_entity_id> target_ids, std::string target_name)
: target_(std::move(target_ids)), name_(std::move(target_name))
{
}
/// \returns The name of the reference, as spelled in the source code.
const std::string& name() const noexcept
{
return name_;
}
/// \returns Whether or not it refers to multiple entities.
bool is_overloaded() const noexcept
{
return target_.has_value(type_safe::variant_type<std::vector<cpp_entity_id>>{});
}
/// \returns The number of entities it refers to.
type_safe::size_t no_overloaded() const noexcept
{
return id().size();
}
/// \returns An array reference to the id or ids it refers to.
type_safe::array_ref<const cpp_entity_id> id() const noexcept
{
if (is_overloaded())
{
auto& vec = target_.value(type_safe::variant_type<std::vector<cpp_entity_id>>{});
return type_safe::ref(vec.data(), vec.size());
}
else
{
auto& id = target_.value(type_safe::variant_type<cpp_entity_id>{});
return type_safe::ref(&id, 1u);
}
}
/// \returns An array reference to the entities it refers to.
/// The return type provides `operator[]` + `size()`,
/// as well as `begin()` and `end()` returning forward iterators.
/// \exclude return
std::vector<type_safe::object_ref<const T>> get(const cpp_entity_index& idx) const
{
std::vector<type_safe::object_ref<const T>> result;
get_impl(std::is_convertible<cpp_namespace&, T&>{}, result, idx);
return result;
}
private:
void get_impl(std::true_type, std::vector<type_safe::object_ref<const T>>& result,
const cpp_entity_index& idx) const
{
for (auto& cur : id())
for (auto& ns : idx.lookup_namespace(cur))
result.push_back(ns);
if (!std::is_same<T, cpp_namespace>::value)
get_impl(std::false_type{}, result, idx);
}
void get_impl(std::false_type, std::vector<type_safe::object_ref<const T>>& result,
const cpp_entity_index& idx) const
{
for (auto& cur : id())
{
auto entity = idx.lookup(cur).map([](const cpp_entity& e) {
DEBUG_ASSERT(Predicate{}(e), detail::precondition_error_handler{},
"invalid entity type");
return type_safe::ref(static_cast<const T&>(e));
});
if (entity)
result.push_back(type_safe::ref(entity.value()));
}
}
type_safe::variant<cpp_entity_id, std::vector<cpp_entity_id>> target_;
std::string name_;
};
/// \exclude
namespace detail
{
struct cpp_entity_ref_predicate
{
bool operator()(const cpp_entity&)
{
return true;
}
};
} }
/// A [cppast::basic_cpp_entity_ref]() to any [cppast::cpp_entity](). /// \returns Whether or not it refers to multiple entities.
using cpp_entity_ref = basic_cpp_entity_ref<cpp_entity, detail::cpp_entity_ref_predicate>; bool is_overloaded() const noexcept
{
return target_.has_value(type_safe::variant_type<std::vector<cpp_entity_id>>{});
}
/// \returns The number of entities it refers to.
type_safe::size_t no_overloaded() const noexcept
{
return id().size();
}
/// \returns An array reference to the id or ids it refers to.
type_safe::array_ref<const cpp_entity_id> id() const noexcept
{
if (is_overloaded())
{
auto& vec = target_.value(type_safe::variant_type<std::vector<cpp_entity_id>>{});
return type_safe::ref(vec.data(), vec.size());
}
else
{
auto& id = target_.value(type_safe::variant_type<cpp_entity_id>{});
return type_safe::ref(&id, 1u);
}
}
/// \returns An array reference to the entities it refers to.
/// The return type provides `operator[]` + `size()`,
/// as well as `begin()` and `end()` returning forward iterators.
/// \exclude return
std::vector<type_safe::object_ref<const T>> get(const cpp_entity_index& idx) const
{
std::vector<type_safe::object_ref<const T>> result;
get_impl(std::is_convertible<cpp_namespace&, T&>{}, result, idx);
return result;
}
private:
void get_impl(std::true_type, std::vector<type_safe::object_ref<const T>>& result,
const cpp_entity_index& idx) const
{
for (auto& cur : id())
for (auto& ns : idx.lookup_namespace(cur))
result.push_back(ns);
if (!std::is_same<T, cpp_namespace>::value)
get_impl(std::false_type{}, result, idx);
}
void get_impl(std::false_type, std::vector<type_safe::object_ref<const T>>& result,
const cpp_entity_index& idx) const
{
for (auto& cur : id())
{
auto entity = idx.lookup(cur).map([](const cpp_entity& e) {
DEBUG_ASSERT(Predicate{}(e), detail::precondition_error_handler{},
"invalid entity type");
return type_safe::ref(static_cast<const T&>(e));
});
if (entity)
result.push_back(type_safe::ref(entity.value()));
}
}
type_safe::variant<cpp_entity_id, std::vector<cpp_entity_id>> target_;
std::string name_;
};
/// \exclude
namespace detail
{
struct cpp_entity_ref_predicate
{
bool operator()(const cpp_entity&)
{
return true;
}
};
} // namespace detail
/// A [cppast::basic_cpp_entity_ref]() to any [cppast::cpp_entity]().
using cpp_entity_ref = basic_cpp_entity_ref<cpp_entity, detail::cpp_entity_ref_predicate>;
} // namespace cppast } // namespace cppast
#endif // CPPAST_CPP_ENTITY_REF_HPP_INCLUDED #endif // CPPAST_CPP_ENTITY_REF_HPP_INCLUDED

View file

@ -9,139 +9,132 @@
#include <type_safe/optional_ref.hpp> #include <type_safe/optional_ref.hpp>
#include <cppast/cpp_entity.hpp>
#include <cppast/cpp_entity_container.hpp> #include <cppast/cpp_entity_container.hpp>
#include <cppast/cpp_entity_index.hpp> #include <cppast/cpp_entity_index.hpp>
#include <cppast/cpp_entity.hpp>
#include <cppast/cpp_expression.hpp> #include <cppast/cpp_expression.hpp>
#include <cppast/cpp_forward_declarable.hpp> #include <cppast/cpp_forward_declarable.hpp>
#include <cppast/cpp_type.hpp> #include <cppast/cpp_type.hpp>
namespace cppast namespace cppast
{ {
/// A [cppast::cpp_entity]() modelling the value of an [cppast::cpp_enum](). /// A [cppast::cpp_entity]() modelling the value of an [cppast::cpp_enum]().
class cpp_enum_value final : public cpp_entity class cpp_enum_value final : public cpp_entity
{
public:
static cpp_entity_kind kind() noexcept;
/// \returns A newly created and registered enum value.
/// \notes `value` may be `nullptr`, in which case the enum has an implicit value.
static std::unique_ptr<cpp_enum_value> build(const cpp_entity_index& idx, cpp_entity_id id,
std::string name,
std::unique_ptr<cpp_expression> value = nullptr);
/// \returns A [ts::optional_ref]() to the [cppast::cpp_expression]() that is the enum value.
/// \notes It only has an associated expression if the value is explictly given.
type_safe::optional_ref<const cpp_expression> value() const noexcept
{
return type_safe::opt_cref(value_.get());
}
private:
cpp_enum_value(std::string name, std::unique_ptr<cpp_expression> value)
: cpp_entity(std::move(name)), value_(std::move(value))
{}
cpp_entity_kind do_get_entity_kind() const noexcept override;
std::unique_ptr<cpp_expression> value_;
};
/// A [cppast::cpp_entity]() modelling a C++ enumeration.
///
/// This can either be a definition or just a forward declaration.
/// If it is just forward declared, it will not have any children.
class cpp_enum final : public cpp_entity,
public cpp_entity_container<cpp_enum, cpp_enum_value>,
public cpp_forward_declarable
{
public:
static cpp_entity_kind kind() noexcept;
/// Builds a [cppast::cpp_enum]().
class builder
{ {
public: public:
static cpp_entity_kind kind() noexcept; /// \effects Sets the name, underlying type and whether it is scoped.
builder(std::string name, bool scoped, std::unique_ptr<cpp_type> type, bool explicit_type)
: enum_(new cpp_enum(std::move(name), std::move(type), explicit_type, scoped))
{}
/// \returns A newly created and registered enum value. /// \effects Adds a [cppast::cpp_enum_value]().
/// \notes `value` may be `nullptr`, in which case the enum has an implicit value. void add_value(std::unique_ptr<cpp_enum_value> value)
static std::unique_ptr<cpp_enum_value> build(
const cpp_entity_index& idx, cpp_entity_id id, std::string name,
std::unique_ptr<cpp_expression> value = nullptr);
/// \returns A [ts::optional_ref]() to the [cppast::cpp_expression]() that is the enum value.
/// \notes It only has an associated expression if the value is explictly given.
type_safe::optional_ref<const cpp_expression> value() const noexcept
{ {
return type_safe::opt_cref(value_.get()); enum_->add_child(std::move(value));
}
/// \returns The not yet finished enumeration.
cpp_enum& get() noexcept
{
return *enum_;
}
/// \effects Registers the enum in the [cppast::cpp_entity_index](),
/// using the given [cppast::cpp_entity_id]().
/// \returns The finished enum.
std::unique_ptr<cpp_enum> finish(
const cpp_entity_index& idx, cpp_entity_id id,
type_safe::optional<cpp_entity_ref> semantic_parent) noexcept
{
enum_->set_semantic_parent(std::move(semantic_parent));
idx.register_definition(std::move(id), type_safe::ref(*enum_));
return std::move(enum_);
}
/// \effects Marks the enum as forward declaration.
/// \returns The finished enum.
std::unique_ptr<cpp_enum> finish_declaration(const cpp_entity_index& idx,
cpp_entity_id definition_id) noexcept
{
enum_->mark_declaration(definition_id);
idx.register_forward_declaration(std::move(definition_id), type_safe::ref(*enum_));
return std::move(enum_);
} }
private: private:
cpp_enum_value(std::string name, std::unique_ptr<cpp_expression> value) std::unique_ptr<cpp_enum> enum_;
: cpp_entity(std::move(name)), value_(std::move(value))
{
}
cpp_entity_kind do_get_entity_kind() const noexcept override;
std::unique_ptr<cpp_expression> value_;
}; };
/// A [cppast::cpp_entity]() modelling a C++ enumeration. /// \returns A reference to the underlying [cppast::cpp_type]() of the enum.
/// const cpp_type& underlying_type() const noexcept
/// This can either be a definition or just a forward declaration.
/// If it is just forward declared, it will not have any children.
class cpp_enum final : public cpp_entity,
public cpp_entity_container<cpp_enum, cpp_enum_value>,
public cpp_forward_declarable
{ {
public: return *type_;
static cpp_entity_kind kind() noexcept; }
/// Builds a [cppast::cpp_enum](). /// \returns Whether or not the underlying type is explictly given.
class builder bool has_explicit_type() const noexcept
{ {
public: return type_given_;
/// \effects Sets the name, underlying type and whether it is scoped. }
builder(std::string name, bool scoped, std::unique_ptr<cpp_type> type,
bool explicit_type)
: enum_(new cpp_enum(std::move(name), std::move(type), explicit_type, scoped))
{
}
/// \effects Adds a [cppast::cpp_enum_value](). /// \returns Whether or not it is a scoped enumeration (i.e. an `enum class`).
void add_value(std::unique_ptr<cpp_enum_value> value) bool is_scoped() const noexcept
{ {
enum_->add_child(std::move(value)); return scoped_;
} }
/// \returns The not yet finished enumeration. private:
cpp_enum& get() noexcept cpp_enum(std::string name, std::unique_ptr<cpp_type> type, bool type_given, bool scoped)
{ : cpp_entity(std::move(name)), type_(std::move(type)), scoped_(scoped), type_given_(type_given)
return *enum_; {}
}
/// \effects Registers the enum in the [cppast::cpp_entity_index](), cpp_entity_kind do_get_entity_kind() const noexcept override;
/// using the given [cppast::cpp_entity_id]().
/// \returns The finished enum.
std::unique_ptr<cpp_enum> finish(
const cpp_entity_index& idx, cpp_entity_id id,
type_safe::optional<cpp_entity_ref> semantic_parent) noexcept
{
enum_->set_semantic_parent(std::move(semantic_parent));
idx.register_definition(std::move(id), type_safe::ref(*enum_));
return std::move(enum_);
}
/// \effects Marks the enum as forward declaration. type_safe::optional<cpp_scope_name> do_get_scope_name() const override;
/// \returns The finished enum.
std::unique_ptr<cpp_enum> finish_declaration(const cpp_entity_index& idx,
cpp_entity_id definition_id) noexcept
{
enum_->mark_declaration(definition_id);
idx.register_forward_declaration(std::move(definition_id), type_safe::ref(*enum_));
return std::move(enum_);
}
private: std::unique_ptr<cpp_type> type_;
std::unique_ptr<cpp_enum> enum_; bool scoped_, type_given_;
}; };
/// \returns A reference to the underlying [cppast::cpp_type]() of the enum.
const cpp_type& underlying_type() const noexcept
{
return *type_;
}
/// \returns Whether or not the underlying type is explictly given.
bool has_explicit_type() const noexcept
{
return type_given_;
}
/// \returns Whether or not it is a scoped enumeration (i.e. an `enum class`).
bool is_scoped() const noexcept
{
return scoped_;
}
private:
cpp_enum(std::string name, std::unique_ptr<cpp_type> type, bool type_given, bool scoped)
: cpp_entity(std::move(name)),
type_(std::move(type)),
scoped_(scoped),
type_given_(type_given)
{
}
cpp_entity_kind do_get_entity_kind() const noexcept override;
type_safe::optional<cpp_scope_name> do_get_scope_name() const override;
std::unique_ptr<cpp_type> type_;
bool scoped_, type_given_;
};
} // namespace cppast } // namespace cppast
#endif // CPPAST_CPP_ENUM_HPP_INCLUDED #endif // CPPAST_CPP_ENUM_HPP_INCLUDED

View file

@ -13,139 +13,138 @@
namespace cppast 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_t,
unexposed_t,
};
/// Base class for all C++ expressions.
class cpp_expression
{
public:
cpp_expression(const cpp_expression&) = delete;
cpp_expression& operator=(const cpp_expression&) = delete;
virtual ~cpp_expression() noexcept = default;
/// \returns The [cppast::cpp_expression_kind]().
cpp_expression_kind kind() const noexcept
{ {
literal_t, return do_get_kind();
}
unexposed_t, /// \returns The type of the expression.
}; const cpp_type& type() const noexcept
/// Base class for all C++ expressions.
class cpp_expression
{ {
public: return *type_;
cpp_expression(const cpp_expression&) = delete; }
cpp_expression& operator=(const cpp_expression&) = delete;
virtual ~cpp_expression() noexcept = default; /// \returns The specified user data.
void* user_data() const noexcept
{
return user_data_.load();
}
/// \returns The [cppast::cpp_expression_kind](). /// \effects Sets some kind of user data.
cpp_expression_kind kind() const noexcept
{
return do_get_kind();
}
/// \returns The type of the expression.
const cpp_type& type() const noexcept
{
return *type_;
}
/// \returns The specified user data.
void* user_data() const noexcept
{
return user_data_.load();
}
/// \effects Sets some kind of user data.
///
/// User data is just some kind of pointer, there are no requirements.
/// The class will do no lifetime management.
///
/// User data is useful if you need to store additional data for an entity without the need to maintain a registry.
void set_user_data(void* data) const noexcept
{
user_data_ = data;
}
protected:
/// \effects Creates it given the type.
/// \requires The type must not be `nullptr`.
cpp_expression(std::unique_ptr<cpp_type> type) : type_(std::move(type)), user_data_(nullptr)
{
DEBUG_ASSERT(type_ != nullptr, detail::precondition_error_handler{});
}
private:
/// \returns The [cppast::cpp_expression_kind]().
virtual cpp_expression_kind do_get_kind() const noexcept = 0;
std::unique_ptr<cpp_type> type_;
mutable std::atomic<void*> user_data_;
};
/// An unexposed [cppast::cpp_expression]().
/// ///
/// There is no further information than a string available. /// User data is just some kind of pointer, there are no requirements.
class cpp_unexposed_expression final : public cpp_expression /// The class will do no lifetime management.
///
/// User data is useful if you need to store additional data for an entity without the need to
/// maintain a registry.
void set_user_data(void* data) const noexcept
{ {
public: user_data_ = data;
/// \returns A newly created unexposed expression. }
static std::unique_ptr<cpp_unexposed_expression> build(std::unique_ptr<cpp_type> type,
cpp_token_string str)
{
return std::unique_ptr<cpp_unexposed_expression>(
new cpp_unexposed_expression(std::move(type), std::move(str)));
}
/// \returns The expression as a string. protected:
const cpp_token_string& expression() const noexcept /// \effects Creates it given the type.
{ /// \requires The type must not be `nullptr`.
return str_; cpp_expression(std::unique_ptr<cpp_type> type) : type_(std::move(type)), user_data_(nullptr)
}
private:
cpp_unexposed_expression(std::unique_ptr<cpp_type> type, cpp_token_string str)
: cpp_expression(std::move(type)), str_(std::move(str))
{
}
cpp_expression_kind do_get_kind() const noexcept override
{
return cpp_expression_kind::unexposed_t;
}
cpp_token_string str_;
};
/// A [cppast::cpp_expression]() that is a literal.
class cpp_literal_expression final : public cpp_expression
{ {
public: DEBUG_ASSERT(type_ != nullptr, detail::precondition_error_handler{});
/// \returns A newly created literal expression. }
static std::unique_ptr<cpp_literal_expression> build(std::unique_ptr<cpp_type> type,
std::string value)
{
return std::unique_ptr<cpp_literal_expression>(
new cpp_literal_expression(std::move(type), std::move(value)));
}
/// \returns The value of the literal, as string. private:
const std::string& value() const noexcept /// \returns The [cppast::cpp_expression_kind]().
{ virtual cpp_expression_kind do_get_kind() const noexcept = 0;
return value_;
}
private: std::unique_ptr<cpp_type> type_;
cpp_literal_expression(std::unique_ptr<cpp_type> type, std::string value) mutable std::atomic<void*> user_data_;
: cpp_expression(std::move(type)), value_(std::move(value)) };
{
}
cpp_expression_kind do_get_kind() const noexcept override /// An unexposed [cppast::cpp_expression]().
{ ///
return cpp_expression_kind::literal_t; /// There is no further information than a string available.
} class cpp_unexposed_expression final : public cpp_expression
{
std::string value_; public:
}; /// \returns A newly created unexposed expression.
static std::unique_ptr<cpp_unexposed_expression> build(std::unique_ptr<cpp_type> type,
/// \exclude cpp_token_string str)
namespace detail
{ {
void write_expression(code_generator::output& output, const cpp_expression& expr); return std::unique_ptr<cpp_unexposed_expression>(
} // namespace detail new cpp_unexposed_expression(std::move(type), std::move(str)));
}
/// \returns The expression as a string.
const cpp_token_string& expression() const noexcept
{
return str_;
}
private:
cpp_unexposed_expression(std::unique_ptr<cpp_type> type, cpp_token_string str)
: cpp_expression(std::move(type)), str_(std::move(str))
{}
cpp_expression_kind do_get_kind() const noexcept override
{
return cpp_expression_kind::unexposed_t;
}
cpp_token_string str_;
};
/// A [cppast::cpp_expression]() that is a literal.
class cpp_literal_expression final : public cpp_expression
{
public:
/// \returns A newly created literal expression.
static std::unique_ptr<cpp_literal_expression> build(std::unique_ptr<cpp_type> type,
std::string value)
{
return std::unique_ptr<cpp_literal_expression>(
new cpp_literal_expression(std::move(type), std::move(value)));
}
/// \returns The value of the literal, as string.
const std::string& value() const noexcept
{
return value_;
}
private:
cpp_literal_expression(std::unique_ptr<cpp_type> type, std::string value)
: cpp_expression(std::move(type)), value_(std::move(value))
{}
cpp_expression_kind do_get_kind() const noexcept override
{
return cpp_expression_kind::literal_t;
}
std::string value_;
};
/// \exclude
namespace detail
{
void write_expression(code_generator::output& output, const cpp_expression& expr);
} // namespace detail
} // namespace cppast } // namespace cppast
#endif // CPPAST_CPP_EXPRESSION_HPP_INCLUDED #endif // CPPAST_CPP_EXPRESSION_HPP_INCLUDED

View file

@ -7,96 +7,93 @@
#include <vector> #include <vector>
#include <cppast/cpp_entity_index.hpp>
#include <cppast/cpp_entity_container.hpp> #include <cppast/cpp_entity_container.hpp>
#include <cppast/cpp_entity_index.hpp>
#include <cppast/cpp_entity_ref.hpp> #include <cppast/cpp_entity_ref.hpp>
namespace cppast namespace cppast
{ {
/// An unmatched documentation comment. /// An unmatched documentation comment.
struct cpp_doc_comment struct cpp_doc_comment
{ {
std::string content; std::string content;
unsigned line; unsigned line;
cpp_doc_comment(std::string content, unsigned line) cpp_doc_comment(std::string content, unsigned line) : content(std::move(content)), line(line) {}
: content(std::move(content)), line(line) };
{
}
};
/// A [cppast::cpp_entity]() modelling a file. /// A [cppast::cpp_entity]() modelling a file.
/// ///
/// This is the top-level entity of the AST. /// This is the top-level entity of the AST.
class cpp_file final : public cpp_entity, public cpp_entity_container<cpp_file, cpp_entity> class cpp_file final : public cpp_entity, public cpp_entity_container<cpp_file, cpp_entity>
{
public:
static cpp_entity_kind kind() noexcept;
/// Builds a [cppast::cpp_file]().
class builder
{ {
public: public:
static cpp_entity_kind kind() noexcept; /// \effects Sets the file name.
explicit builder(std::string name) : file_(new cpp_file(std::move(name))) {}
/// Builds a [cppast::cpp_file](). /// \effects Adds an entity.
class builder void add_child(std::unique_ptr<cpp_entity> child) noexcept
{ {
public: file_->add_child(std::move(child));
/// \effects Sets the file name. }
explicit builder(std::string name) : file_(new cpp_file(std::move(name))) {}
/// \effects Adds an entity. /// \effects Adds an unmatched documentation comment.
void add_child(std::unique_ptr<cpp_entity> child) noexcept void add_unmatched_comment(cpp_doc_comment comment)
{
file_->add_child(std::move(child));
}
/// \effects Adds an unmatched documentation comment.
void add_unmatched_comment(cpp_doc_comment comment)
{
file_->comments_.push_back(std::move(comment));
}
/// \returns The not yet finished file.
cpp_file& get() noexcept
{
return *file_;
}
/// \effects Registers the file in the [cppast::cpp_entity_index]().
/// It will use the file name as identifier.
/// \returns The finished file, or `nullptr`, if that file was already registered.
std::unique_ptr<cpp_file> finish(const cpp_entity_index& idx) noexcept
{
auto res = idx.register_file(cpp_entity_id(file_->name()), type_safe::ref(*file_));
return res ? std::move(file_) : nullptr;
}
private:
std::unique_ptr<cpp_file> file_;
};
/// \returns The unmatched documentation comments.
type_safe::array_ref<const cpp_doc_comment> unmatched_comments() const noexcept
{ {
return type_safe::ref(comments_.data(), comments_.size()); file_->comments_.push_back(std::move(comment));
}
/// \returns The not yet finished file.
cpp_file& get() noexcept
{
return *file_;
}
/// \effects Registers the file in the [cppast::cpp_entity_index]().
/// It will use the file name as identifier.
/// \returns The finished file, or `nullptr`, if that file was already registered.
std::unique_ptr<cpp_file> finish(const cpp_entity_index& idx) noexcept
{
auto res = idx.register_file(cpp_entity_id(file_->name()), type_safe::ref(*file_));
return res ? std::move(file_) : nullptr;
} }
private: private:
cpp_file(std::string name) : cpp_entity(std::move(name)) {} std::unique_ptr<cpp_file> file_;
/// \returns [cpp_entity_type::file_t]().
cpp_entity_kind do_get_entity_kind() const noexcept override;
std::vector<cpp_doc_comment> comments_;
}; };
/// \exclude /// \returns The unmatched documentation comments.
namespace detail type_safe::array_ref<const cpp_doc_comment> unmatched_comments() const noexcept
{ {
struct cpp_file_ref_predicate return type_safe::ref(comments_.data(), comments_.size());
{ }
bool operator()(const cpp_entity& e);
};
} // namespace detail
/// A reference to a [cppast::cpp_file](). private:
using cpp_file_ref = basic_cpp_entity_ref<cpp_file, detail::cpp_file_ref_predicate>; cpp_file(std::string name) : cpp_entity(std::move(name)) {}
/// \returns [cpp_entity_type::file_t]().
cpp_entity_kind do_get_entity_kind() const noexcept override;
std::vector<cpp_doc_comment> comments_;
};
/// \exclude
namespace detail
{
struct cpp_file_ref_predicate
{
bool operator()(const cpp_entity& e);
};
} // namespace detail
/// A reference to a [cppast::cpp_file]().
using cpp_file_ref = basic_cpp_entity_ref<cpp_file, detail::cpp_file_ref_predicate>;
} // namespace cppast } // namespace cppast
#endif // CPPAST_CPP_FILE_HPP_INCLUDED #endif // CPPAST_CPP_FILE_HPP_INCLUDED

View file

@ -14,104 +14,103 @@
namespace cppast namespace cppast
{ {
/// Mixin base class for all entities that can have a forward declaration. /// Mixin base class for all entities that can have a forward declaration.
/// ///
/// Examples are [cppast::cpp_enum]() or [cppast::cpp_class](), /// Examples are [cppast::cpp_enum]() or [cppast::cpp_class](),
/// but also [cppast::cpp_function_base](). /// but also [cppast::cpp_function_base]().
/// Those entities can have multiple declarations and one definition. /// Those entities can have multiple declarations and one definition.
class cpp_forward_declarable class cpp_forward_declarable
{
public:
/// \returns Whether or not the entity is the definition.
bool is_definition() const noexcept
{ {
public: return !definition_.has_value();
/// \returns Whether or not the entity is the definition. }
bool is_definition() const noexcept
{
return !definition_.has_value();
}
/// \returns Whether or not the entity is "just" a declaration. /// \returns Whether or not the entity is "just" a declaration.
bool is_declaration() const noexcept bool is_declaration() const noexcept
{ {
return definition_.has_value(); return definition_.has_value();
} }
/// \returns The [cppast::cpp_entity_id]() of the definition, /// \returns The [cppast::cpp_entity_id]() of the definition,
/// if the current entity is not the definition. /// if the current entity is not the definition.
const type_safe::optional<cpp_entity_id>& definition() const noexcept const type_safe::optional<cpp_entity_id>& definition() const noexcept
{ {
return definition_; return definition_;
} }
/// \returns A reference to the semantic parent of the entity. /// \returns A reference to the semantic parent of the entity.
/// This applies only to out-of-line definitions /// This applies only to out-of-line definitions
/// and is the entity which owns the declaration. /// and is the entity which owns the declaration.
const type_safe::optional<cpp_entity_ref>& semantic_parent() const noexcept const type_safe::optional<cpp_entity_ref>& semantic_parent() const noexcept
{ {
return semantic_parent_; return semantic_parent_;
} }
/// \returns The name of the semantic parent, if it has one, /// \returns The name of the semantic parent, if it has one,
/// else the empty string. /// else the empty string.
/// \notes This may include template parameters. /// \notes This may include template parameters.
std::string semantic_scope() const noexcept std::string semantic_scope() const noexcept
{ {
return type_safe::copy(semantic_parent_.map(&cpp_entity_ref::name)).value_or(""); return type_safe::copy(semantic_parent_.map(&cpp_entity_ref::name)).value_or("");
} }
protected: protected:
/// \effects Marks the entity as definition. /// \effects Marks the entity as definition.
/// \notes If it is not a definition, /// \notes If it is not a definition,
/// [*set_definition]() must be called. /// [*set_definition]() must be called.
cpp_forward_declarable() noexcept = default; cpp_forward_declarable() noexcept = default;
~cpp_forward_declarable() noexcept = default; ~cpp_forward_declarable() noexcept = default;
/// \effects Sets the definition entity, /// \effects Sets the definition entity,
/// marking it as a forward declaration. /// marking it as a forward declaration.
void mark_declaration(cpp_entity_id def) noexcept void mark_declaration(cpp_entity_id def) noexcept
{ {
definition_ = std::move(def); definition_ = std::move(def);
} }
/// \effects Sets the semantic parent of the entity. /// \effects Sets the semantic parent of the entity.
void set_semantic_parent(type_safe::optional<cpp_entity_ref> semantic_parent) noexcept void set_semantic_parent(type_safe::optional<cpp_entity_ref> semantic_parent) noexcept
{ {
semantic_parent_ = std::move(semantic_parent); semantic_parent_ = std::move(semantic_parent);
} }
private: private:
type_safe::optional<cpp_entity_ref> semantic_parent_; type_safe::optional<cpp_entity_ref> semantic_parent_;
type_safe::optional<cpp_entity_id> definition_; type_safe::optional<cpp_entity_id> definition_;
}; };
/// \returns Whether or not the given entity is a definition. /// \returns Whether or not the given entity is a definition.
bool is_definition(const cpp_entity& e) noexcept; bool is_definition(const cpp_entity& e) noexcept;
class cpp_enum; class cpp_enum;
class cpp_class; class cpp_class;
class cpp_variable; class cpp_variable;
class cpp_function_base; class cpp_function_base;
/// Gets the definition of an entity. /// Gets the definition of an entity.
/// \returns A [ts::optional_ref]() to the entity that is the definition. /// \returns A [ts::optional_ref]() to the entity that is the definition.
/// If the entity is a definition or not derived from [cppast::cpp_forward_declarable]() (only valid for the generic entity overload), /// If the entity is a definition or not derived from [cppast::cpp_forward_declarable]() (only valid
/// returns a reference to the entity itself. /// for the generic entity overload), returns a reference to the entity itself. Otherwise lookups
/// Otherwise lookups the definition id and returns it. /// the definition id and returns it. \notes The return value will only be `nullptr`, if the
/// \notes The return value will only be `nullptr`, if the definition is not registered. /// definition is not registered. \group get_definition
/// \group get_definition type_safe::optional_ref<const cpp_entity> get_definition(const cpp_entity_index& idx,
type_safe::optional_ref<const cpp_entity> get_definition(const cpp_entity_index& idx, const cpp_entity& e);
const cpp_entity& e); /// \group get_definition
/// \group get_definition type_safe::optional_ref<const cpp_enum> get_definition(const cpp_entity_index& idx,
type_safe::optional_ref<const cpp_enum> get_definition(const cpp_entity_index& idx, const cpp_enum& e);
const cpp_enum& e); /// \group get_definition
/// \group get_definition type_safe::optional_ref<const cpp_class> get_definition(const cpp_entity_index& idx,
type_safe::optional_ref<const cpp_class> get_definition(const cpp_entity_index& idx, const cpp_class& e);
const cpp_class& e); /// \group get_definition
/// \group get_definition type_safe::optional_ref<const cpp_variable> get_definition(const cpp_entity_index& idx,
type_safe::optional_ref<const cpp_variable> get_definition(const cpp_entity_index& idx, const cpp_variable& e);
const cpp_variable& e); /// \group get_definition
/// \group get_definition type_safe::optional_ref<const cpp_function_base> get_definition(const cpp_entity_index& idx,
type_safe::optional_ref<const cpp_function_base> get_definition(const cpp_entity_index& idx, const cpp_function_base& e);
const cpp_function_base& e);
} // namespace cppast } // namespace cppast

View file

@ -13,58 +13,58 @@
namespace cppast namespace cppast
{ {
/// A [cppast::cpp_entity]() representing a friend declaration. /// A [cppast::cpp_entity]() representing a friend declaration.
/// ///
/// It can either declare or define a `friend` function (template), declare a `friend` class, /// It can either declare or define a `friend` function (template), declare a `friend` class,
/// or refer to an existing type. /// or refer to an existing type.
class cpp_friend : public cpp_entity, private cpp_entity_container<cpp_friend, cpp_entity> class cpp_friend : public cpp_entity, private cpp_entity_container<cpp_friend, cpp_entity>
{
public:
static cpp_entity_kind kind() noexcept;
/// \returns A newly created friend declaring the given entity as `friend`.
/// \notes The friend declaration itself will not be registered,
/// but the referring entity is.
static std::unique_ptr<cpp_friend> build(std::unique_ptr<cpp_entity> e)
{ {
public: return std::unique_ptr<cpp_friend>(new cpp_friend(std::move(e)));
static cpp_entity_kind kind() noexcept; }
/// \returns A newly created friend declaring the given entity as `friend`. /// \returns A newly created friend declaring the given type as `friend`.
/// \notes The friend declaration itself will not be registered, /// \notes It will not be registered.
/// but the referring entity is. static std::unique_ptr<cpp_friend> build(std::unique_ptr<cpp_type> type)
static std::unique_ptr<cpp_friend> build(std::unique_ptr<cpp_entity> e) {
{ return std::unique_ptr<cpp_friend>(new cpp_friend(std::move(type)));
return std::unique_ptr<cpp_friend>(new cpp_friend(std::move(e))); }
}
/// \returns A newly created friend declaring the given type as `friend`. /// \returns An optional reference to the entity it declares as friend, or `nullptr`.
/// \notes It will not be registered. type_safe::optional_ref<const cpp_entity> entity() const noexcept
static std::unique_ptr<cpp_friend> build(std::unique_ptr<cpp_type> type) {
{ if (begin() == end())
return std::unique_ptr<cpp_friend>(new cpp_friend(std::move(type))); return nullptr;
} return type_safe::ref(*begin());
}
/// \returns An optional reference to the entity it declares as friend, or `nullptr`. /// \returns An optional reference to the type it declares as friend, or `nullptr`.
type_safe::optional_ref<const cpp_entity> entity() const noexcept type_safe::optional_ref<const cpp_type> type() const noexcept
{ {
if (begin() == end()) return type_safe::opt_ref(type_.get());
return nullptr; }
return type_safe::ref(*begin());
}
/// \returns An optional reference to the type it declares as friend, or `nullptr`. private:
type_safe::optional_ref<const cpp_type> type() const noexcept cpp_friend(std::unique_ptr<cpp_entity> e) : cpp_entity("")
{ {
return type_safe::opt_ref(type_.get()); add_child(std::move(e));
} }
private: cpp_friend(std::unique_ptr<cpp_type> type) : cpp_entity(""), type_(std::move(type)) {}
cpp_friend(std::unique_ptr<cpp_entity> e) : cpp_entity("")
{
add_child(std::move(e));
}
cpp_friend(std::unique_ptr<cpp_type> type) : cpp_entity(""), type_(std::move(type)) {} cpp_entity_kind do_get_entity_kind() const noexcept override;
cpp_entity_kind do_get_entity_kind() const noexcept override; std::unique_ptr<cpp_type> type_;
std::unique_ptr<cpp_type> type_; friend cpp_entity_container<cpp_friend, cpp_entity>;
};
friend cpp_entity_container<cpp_friend, cpp_entity>;
};
} // namespace cppast } // namespace cppast
#endif // CPPAST_CPP_FRIEND_HPP_INCLUDED #endif // CPPAST_CPP_FRIEND_HPP_INCLUDED

View file

@ -5,262 +5,261 @@
#ifndef CPPAST_CPP_FUNCTION_HPP_INCLUDED #ifndef CPPAST_CPP_FUNCTION_HPP_INCLUDED
#define CPPAST_CPP_FUNCTION_HPP_INCLUDED #define CPPAST_CPP_FUNCTION_HPP_INCLUDED
#include <cppast/detail/intrusive_list.hpp>
#include <cppast/cpp_entity.hpp> #include <cppast/cpp_entity.hpp>
#include <cppast/cpp_forward_declarable.hpp> #include <cppast/cpp_forward_declarable.hpp>
#include <cppast/cpp_storage_class_specifiers.hpp> #include <cppast/cpp_storage_class_specifiers.hpp>
#include <cppast/cpp_variable_base.hpp> #include <cppast/cpp_variable_base.hpp>
#include <cppast/detail/intrusive_list.hpp>
namespace cppast namespace cppast
{ {
/// A [cppast::cpp_entity]() modelling a function parameter. /// A [cppast::cpp_entity]() modelling a function parameter.
class cpp_function_parameter final : public cpp_entity, public cpp_variable_base class cpp_function_parameter final : public cpp_entity, public cpp_variable_base
{
public:
static cpp_entity_kind kind() noexcept;
/// \returns A newly created and registered function parameter.
static std::unique_ptr<cpp_function_parameter> build(const cpp_entity_index& idx,
cpp_entity_id id, std::string name,
std::unique_ptr<cpp_type> type,
std::unique_ptr<cpp_expression> def
= nullptr);
/// \returns A newly created unnamed function parameter.
/// \notes It will not be registered, as nothing can refer to it.
static std::unique_ptr<cpp_function_parameter> build(std::unique_ptr<cpp_type> type,
std::unique_ptr<cpp_expression> def
= nullptr);
private:
cpp_function_parameter(std::string name, std::unique_ptr<cpp_type> type,
std::unique_ptr<cpp_expression> def)
: cpp_entity(std::move(name)), cpp_variable_base(std::move(type), std::move(def))
{}
cpp_entity_kind do_get_entity_kind() const noexcept override;
};
/// The kinds of function bodies of a [cppast::cpp_function_base]().
enum cpp_function_body_kind
{
cpp_function_declaration, //< Just a declaration.
cpp_function_definition, //< Regular definition.
cpp_function_defaulted, //< Defaulted definition.
cpp_function_deleted, //< Deleted definition.
};
/// \returns Whether or not the function body is a declaration,
/// without a definition.
inline bool is_declaration(cpp_function_body_kind body) noexcept
{
return body == cpp_function_declaration;
}
/// \returns Whether or not the function body is a definition.
inline bool is_definition(cpp_function_body_kind body) noexcept
{
return !is_declaration(body);
}
/// Base class for all entities that are functions.
///
/// It contains arguments and common flags.
class cpp_function_base : public cpp_entity, public cpp_forward_declarable
{
public:
/// \returns An iteratable object iterating over the [cppast::cpp_function__parameter]()
/// entities.
detail::iteratable_intrusive_list<cpp_function_parameter> parameters() const noexcept
{ {
public: return type_safe::ref(parameters_);
static cpp_entity_kind kind() noexcept;
/// \returns A newly created and registered function parameter.
static std::unique_ptr<cpp_function_parameter> build(
const cpp_entity_index& idx, cpp_entity_id id, std::string name,
std::unique_ptr<cpp_type> type, std::unique_ptr<cpp_expression> def = nullptr);
/// \returns A newly created unnamed function parameter.
/// \notes It will not be registered, as nothing can refer to it.
static std::unique_ptr<cpp_function_parameter> build(
std::unique_ptr<cpp_type> type, std::unique_ptr<cpp_expression> def = nullptr);
private:
cpp_function_parameter(std::string name, std::unique_ptr<cpp_type> type,
std::unique_ptr<cpp_expression> def)
: cpp_entity(std::move(name)), cpp_variable_base(std::move(type), std::move(def))
{
}
cpp_entity_kind do_get_entity_kind() const noexcept override;
};
/// The kinds of function bodies of a [cppast::cpp_function_base]().
enum cpp_function_body_kind
{
cpp_function_declaration, //< Just a declaration.
cpp_function_definition, //< Regular definition.
cpp_function_defaulted, //< Defaulted definition.
cpp_function_deleted, //< Deleted definition.
};
/// \returns Whether or not the function body is a declaration,
/// without a definition.
inline bool is_declaration(cpp_function_body_kind body) noexcept
{
return body == cpp_function_declaration;
} }
/// \returns Whether or not the function body is a definition. /// \returns The [cppast::cpp_function_body_kind]().
inline bool is_definition(cpp_function_body_kind body) noexcept /// \notes This matches the [cppast::cpp_forward_declarable]() queries.
cpp_function_body_kind body_kind() const noexcept
{ {
return !is_declaration(body); return body_;
} }
/// Base class for all entities that are functions. /// \returns A [ts::optional_ref]() to the [cppast::cpp_expression]() that is the given
/// `noexcept` condition. \notes If this returns `nullptr`, the function has the implicit
/// noexcept value (i.e. none, unless it is a destructor). \notes There is no way to distinguish
/// between `noexcept` and `noexcept(true)`.
type_safe::optional_ref<const cpp_expression> noexcept_condition() const noexcept
{
return type_safe::opt_cref(noexcept_expr_.get());
}
/// \returns Whether the function has an ellipsis.
bool is_variadic() const noexcept
{
return variadic_;
}
/// \returns The signature of the function,
/// i.e. parameters and cv/ref-qualifiers if a member function.
/// It has the form `(int,char,...) const`.
std::string signature() const
{
return do_get_signature();
}
protected:
/// Builder class for functions.
/// ///
/// It contains arguments and common flags. /// Inherit from it to provide additional setter.
class cpp_function_base : public cpp_entity, public cpp_forward_declarable template <typename T>
class basic_builder
{ {
public: public:
/// \returns An iteratable object iterating over the [cppast::cpp_function__parameter]() entities. /// \effects Sets the name.
detail::iteratable_intrusive_list<cpp_function_parameter> parameters() const noexcept basic_builder(std::string name) : function(new T(name)) {}
/// \effects Adds a parameter.
void add_parameter(std::unique_ptr<cpp_function_parameter> parameter)
{ {
return type_safe::ref(parameters_); static_cast<cpp_function_base&>(*function).parameters_.push_back(*function,
std::move(parameter));
} }
/// \returns The [cppast::cpp_function_body_kind](). /// \effects Marks the function as variadic.
/// \notes This matches the [cppast::cpp_forward_declarable]() queries. void is_variadic()
cpp_function_body_kind body_kind() const noexcept
{ {
return body_; static_cast<cpp_function_base&>(*function).variadic_ = true;
} }
/// \returns A [ts::optional_ref]() to the [cppast::cpp_expression]() that is the given `noexcept` condition. /// \effects Sets the noexcept condition expression.
/// \notes If this returns `nullptr`, the function has the implicit noexcept value (i.e. none, unless it is a destructor). void noexcept_condition(std::unique_ptr<cpp_expression> cond)
/// \notes There is no way to distinguish between `noexcept` and `noexcept(true)`.
type_safe::optional_ref<const cpp_expression> noexcept_condition() const noexcept
{ {
return type_safe::opt_cref(noexcept_expr_.get()); static_cast<cpp_function_base&>(*function).noexcept_expr_ = std::move(cond);
} }
/// \returns Whether the function has an ellipsis. /// \returns The not yet finished function.
bool is_variadic() const noexcept T& get() noexcept
{ {
return variadic_; return *function;
} }
/// \returns The signature of the function, /// \effects If the body is a definition, registers it.
/// i.e. parameters and cv/ref-qualifiers if a member function. /// Else marks it as a declaration.
/// It has the form `(int,char,...) const`. /// \returns The finished function.
std::string signature() const std::unique_ptr<T> finish(const cpp_entity_index& idx, cpp_entity_id id,
cpp_function_body_kind body_kind,
type_safe::optional<cpp_entity_ref> semantic_parent)
{ {
return do_get_signature(); function->body_ = body_kind;
function->set_semantic_parent(std::move(semantic_parent));
if (cppast::is_definition(body_kind))
idx.register_definition(std::move(id), type_safe::ref(*function));
else
{
function->mark_declaration(id);
idx.register_forward_declaration(std::move(id), type_safe::ref(*function));
}
return std::move(function);
}
/// \returns The finished function without registering it.
/// \notes This is intended for templated functions only.
std::unique_ptr<T> finish(cpp_entity_id id, cpp_function_body_kind body_kind,
type_safe::optional<cpp_entity_ref> semantic_parent)
{
function->body_ = body_kind;
function->set_semantic_parent(std::move(semantic_parent));
if (!cppast::is_definition(body_kind))
function->mark_declaration(id);
return std::move(function);
} }
protected: protected:
/// Builder class for functions. basic_builder() = default;
/// ~basic_builder() noexcept = default;
/// Inherit from it to provide additional setter.
template <typename T>
class basic_builder
{
public:
/// \effects Sets the name.
basic_builder(std::string name) : function(new T(name)) {}
/// \effects Adds a parameter. std::unique_ptr<T> function;
void add_parameter(std::unique_ptr<cpp_function_parameter> parameter)
{
static_cast<cpp_function_base&>(*function).parameters_.push_back(*function,
std::move(
parameter));
}
/// \effects Marks the function as variadic.
void is_variadic()
{
static_cast<cpp_function_base&>(*function).variadic_ = true;
}
/// \effects Sets the noexcept condition expression.
void noexcept_condition(std::unique_ptr<cpp_expression> cond)
{
static_cast<cpp_function_base&>(*function).noexcept_expr_ = std::move(cond);
}
/// \returns The not yet finished function.
T& get() noexcept
{
return *function;
}
/// \effects If the body is a definition, registers it.
/// Else marks it as a declaration.
/// \returns The finished function.
std::unique_ptr<T> finish(const cpp_entity_index& idx, cpp_entity_id id,
cpp_function_body_kind body_kind,
type_safe::optional<cpp_entity_ref> semantic_parent)
{
function->body_ = body_kind;
function->set_semantic_parent(std::move(semantic_parent));
if (cppast::is_definition(body_kind))
idx.register_definition(std::move(id), type_safe::ref(*function));
else
{
function->mark_declaration(id);
idx.register_forward_declaration(std::move(id), type_safe::ref(*function));
}
return std::move(function);
}
/// \returns The finished function without registering it.
/// \notes This is intended for templated functions only.
std::unique_ptr<T> finish(cpp_entity_id id, cpp_function_body_kind body_kind,
type_safe::optional<cpp_entity_ref> semantic_parent)
{
function->body_ = body_kind;
function->set_semantic_parent(std::move(semantic_parent));
if (!cppast::is_definition(body_kind))
function->mark_declaration(id);
return std::move(function);
}
protected:
basic_builder() = default;
~basic_builder() noexcept = default;
std::unique_ptr<T> function;
};
cpp_function_base(std::string name)
: cpp_entity(std::move(name)), body_(cpp_function_declaration), variadic_(false)
{
}
protected:
/// \returns The signature, it is called by [*signature()]().
virtual std::string do_get_signature() const;
private:
detail::intrusive_list<cpp_function_parameter> parameters_;
std::unique_ptr<cpp_expression> noexcept_expr_;
cpp_function_body_kind body_;
bool variadic_;
}; };
/// A [cppast::cpp_entity]() modelling a C++ function. cpp_function_base(std::string name)
/// \notes This is not a member function, : cpp_entity(std::move(name)), body_(cpp_function_declaration), variadic_(false)
/// use [cppast::cpp_member_function]() for that. {}
/// It can be a `static` function of a class, however.
class cpp_function final : public cpp_function_base protected:
/// \returns The signature, it is called by [*signature()]().
virtual std::string do_get_signature() const;
private:
detail::intrusive_list<cpp_function_parameter> parameters_;
std::unique_ptr<cpp_expression> noexcept_expr_;
cpp_function_body_kind body_;
bool variadic_;
};
/// A [cppast::cpp_entity]() modelling a C++ function.
/// \notes This is not a member function,
/// use [cppast::cpp_member_function]() for that.
/// It can be a `static` function of a class, however.
class cpp_function final : public cpp_function_base
{
public:
static cpp_entity_kind kind() noexcept;
/// Builds a [cppast::cpp_function]().
class builder : public cpp_function_base::basic_builder<cpp_function>
{ {
public: public:
static cpp_entity_kind kind() noexcept; /// \effects Sets the name and return type.
builder(std::string name, std::unique_ptr<cpp_type> return_type)
/// Builds a [cppast::cpp_function]().
class builder : public cpp_function_base::basic_builder<cpp_function>
{ {
public: function = std::unique_ptr<cpp_function>(
/// \effects Sets the name and return type. new cpp_function(std::move(name), std::move(return_type)));
builder(std::string name, std::unique_ptr<cpp_type> return_type)
{
function = std::unique_ptr<cpp_function>(
new cpp_function(std::move(name), std::move(return_type)));
}
/// \effects Sets the storage class.
void storage_class(cpp_storage_class_specifiers storage)
{
function->storage_ = storage;
}
/// \effects Marks the function as `constexpr`.
void is_constexpr()
{
function->constexpr_ = true;
}
};
/// \returns A reference to the return [cppast::cpp_type]().
const cpp_type& return_type() const noexcept
{
return *return_type_;
} }
/// \returns The [cppast::cpp_storage_specifiers]() of the function. /// \effects Sets the storage class.
/// \notes If it is `cpp_storage_class_static` and inside a [cppast::cpp_class](), void storage_class(cpp_storage_class_specifiers storage)
/// it is a `static` class function.
cpp_storage_class_specifiers storage_class() const noexcept
{ {
return storage_; function->storage_ = storage;
} }
/// \returns Whether the function is marked `constexpr`. /// \effects Marks the function as `constexpr`.
bool is_constexpr() const noexcept void is_constexpr()
{ {
return constexpr_; function->constexpr_ = true;
} }
private:
cpp_entity_kind do_get_entity_kind() const noexcept override;
cpp_function(std::string name, std::unique_ptr<cpp_type> ret)
: cpp_function_base(std::move(name)),
return_type_(std::move(ret)),
storage_(cpp_storage_class_auto),
constexpr_(false)
{
}
std::unique_ptr<cpp_type> return_type_;
cpp_storage_class_specifiers storage_;
bool constexpr_;
}; };
/// \returns A reference to the return [cppast::cpp_type]().
const cpp_type& return_type() const noexcept
{
return *return_type_;
}
/// \returns The [cppast::cpp_storage_specifiers]() of the function.
/// \notes If it is `cpp_storage_class_static` and inside a [cppast::cpp_class](),
/// it is a `static` class function.
cpp_storage_class_specifiers storage_class() const noexcept
{
return storage_;
}
/// \returns Whether the function is marked `constexpr`.
bool is_constexpr() const noexcept
{
return constexpr_;
}
private:
cpp_entity_kind do_get_entity_kind() const noexcept override;
cpp_function(std::string name, std::unique_ptr<cpp_type> ret)
: cpp_function_base(std::move(name)), return_type_(std::move(ret)),
storage_(cpp_storage_class_auto), constexpr_(false)
{}
std::unique_ptr<cpp_type> return_type_;
cpp_storage_class_specifiers storage_;
bool constexpr_;
};
} // namespace cppast } // namespace cppast
#endif // CPPAST_CPP_FUNCTION_HPP_INCLUDED #endif // CPPAST_CPP_FUNCTION_HPP_INCLUDED

View file

@ -10,70 +10,68 @@
namespace cppast namespace cppast
{ {
/// A [cppast::cpp_entity]() modelling a function template. /// A [cppast::cpp_entity]() modelling a function template.
class cpp_function_template final : public cpp_template class cpp_function_template final : public cpp_template
{
public:
static cpp_entity_kind kind() noexcept;
/// Builder for [cppast::cpp_function_template]().
class builder : public basic_builder<cpp_function_template, cpp_function_base>
{ {
public: public:
static cpp_entity_kind kind() noexcept; using basic_builder::basic_builder;
/// Builder for [cppast::cpp_function_template]().
class builder : public basic_builder<cpp_function_template, cpp_function_base>
{
public:
using basic_builder::basic_builder;
};
/// A reference to the function that is being templated.
const cpp_function_base& function() const noexcept
{
return static_cast<const cpp_function_base&>(*begin());
}
private:
cpp_function_template(std::unique_ptr<cpp_function_base> func)
: cpp_template(std::unique_ptr<cpp_entity>(func.release()))
{
}
cpp_entity_kind do_get_entity_kind() const noexcept override;
friend basic_builder<cpp_function_template, cpp_function_base>;
}; };
/// A [cppast::cpp_entity]() modelling a function template specialization. /// A reference to the function that is being templated.
class cpp_function_template_specialization final : public cpp_template_specialization const cpp_function_base& function() const noexcept
{
return static_cast<const cpp_function_base&>(*begin());
}
private:
cpp_function_template(std::unique_ptr<cpp_function_base> func)
: cpp_template(std::unique_ptr<cpp_entity>(func.release()))
{}
cpp_entity_kind do_get_entity_kind() const noexcept override;
friend basic_builder<cpp_function_template, cpp_function_base>;
};
/// A [cppast::cpp_entity]() modelling a function template specialization.
class cpp_function_template_specialization final : public cpp_template_specialization
{
public:
static cpp_entity_kind kind() noexcept;
/// Builder for [cppast::cpp_function_template_specialization]().
class builder
: public specialization_builder<cpp_function_template_specialization, cpp_function_base>
{ {
public: public:
static cpp_entity_kind kind() noexcept; using specialization_builder::specialization_builder;
/// Builder for [cppast::cpp_function_template_specialization]().
class builder
: public specialization_builder<cpp_function_template_specialization, cpp_function_base>
{
public:
using specialization_builder::specialization_builder;
private:
using specialization_builder::add_parameter;
};
/// A reference to the function that is being specialized.
const cpp_function_base& function() const noexcept
{
return static_cast<const cpp_function_base&>(*begin());
}
private: private:
cpp_function_template_specialization(std::unique_ptr<cpp_function_base> func, using specialization_builder::add_parameter;
cpp_template_ref primary)
: cpp_template_specialization(std::unique_ptr<cpp_entity>(func.release()), primary)
{
}
cpp_entity_kind do_get_entity_kind() const noexcept override;
friend specialization_builder<cpp_function_template_specialization, cpp_function_base>;
}; };
/// A reference to the function that is being specialized.
const cpp_function_base& function() const noexcept
{
return static_cast<const cpp_function_base&>(*begin());
}
private:
cpp_function_template_specialization(std::unique_ptr<cpp_function_base> func,
cpp_template_ref primary)
: cpp_template_specialization(std::unique_ptr<cpp_entity>(func.release()), primary)
{}
cpp_entity_kind do_get_entity_kind() const noexcept override;
friend specialization_builder<cpp_function_template_specialization, cpp_function_base>;
};
} // namespace cppast } // namespace cppast
#endif // CPPAST_CPP_FUNCTION_TEMPLATE_HPP_INCLUDED #endif // CPPAST_CPP_FUNCTION_TEMPLATE_HPP_INCLUDED

View file

@ -9,199 +9,195 @@
namespace cppast namespace cppast
{ {
/// A [cppast::cpp_type]() that is a function. /// A [cppast::cpp_type]() that is a function.
/// ///
/// A function pointer is created by wrapping it in [cppast::cpp_pointer_type](). /// A function pointer is created by wrapping it in [cppast::cpp_pointer_type]().
class cpp_function_type final : public cpp_type class cpp_function_type final : public cpp_type
{
public:
/// Builds a [cppast::cpp_function_type]().
class builder
{ {
public: public:
/// Builds a [cppast::cpp_function_type](). /// \effects Sets the return type.
class builder explicit builder(std::unique_ptr<cpp_type> return_type)
: func_(new cpp_function_type(std::move(return_type)))
{}
/// \effects Adds an parameter type.
void add_parameter(std::unique_ptr<cpp_type> arg)
{ {
public: func_->parameters_.push_back(*func_, std::move(arg));
/// \effects Sets the return type.
explicit builder(std::unique_ptr<cpp_type> return_type)
: func_(new cpp_function_type(std::move(return_type)))
{
}
/// \effects Adds an parameter type.
void add_parameter(std::unique_ptr<cpp_type> arg)
{
func_->parameters_.push_back(*func_, std::move(arg));
}
/// \effects Adds an ellipsis, marking it as variadic.
void is_variadic()
{
func_->variadic_ = true;
}
/// \returns The finished [cppast::cpp_function_type]().
std::unique_ptr<cpp_function_type> finish()
{
return std::move(func_);
}
private:
std::unique_ptr<cpp_function_type> func_;
};
/// \returns A reference to the return [cppast::cpp_type]().
const cpp_type& return_type() const noexcept
{
return *return_type_;
} }
/// \returns An iteratable object iterating over the parameter types. /// \effects Adds an ellipsis, marking it as variadic.
detail::iteratable_intrusive_list<cpp_type> parameter_types() const noexcept void is_variadic()
{ {
return type_safe::ref(parameters_); func_->variadic_ = true;
} }
/// \returns Whether or not the function is variadic (C-style ellipsis). /// \returns The finished [cppast::cpp_function_type]().
bool is_variadic() const noexcept std::unique_ptr<cpp_function_type> finish()
{ {
return variadic_; return std::move(func_);
} }
private: private:
cpp_function_type(std::unique_ptr<cpp_type> return_type) std::unique_ptr<cpp_function_type> func_;
: return_type_(std::move(return_type)), variadic_(false)
{
}
cpp_type_kind do_get_kind() const noexcept override
{
return cpp_type_kind::function_t;
}
std::unique_ptr<cpp_type> return_type_;
detail::intrusive_list<cpp_type> parameters_;
bool variadic_;
}; };
/// A [cppast::cpp_type]() that is a member function. /// \returns A reference to the return [cppast::cpp_type]().
/// const cpp_type& return_type() const noexcept
/// A member function with cv qualifier is created by wrapping it in [cppast::cpp_cv_qualified_type](). {
/// A member function with reference qualifier is created by wrapping it in [cppast::cpp_reference_type](). return *return_type_;
/// A member function pointer is created by wrapping it in [cppast::cpp_pointer_type](). }
class cpp_member_function_type final : public cpp_type
/// \returns An iteratable object iterating over the parameter types.
detail::iteratable_intrusive_list<cpp_type> parameter_types() const noexcept
{
return type_safe::ref(parameters_);
}
/// \returns Whether or not the function is variadic (C-style ellipsis).
bool is_variadic() const noexcept
{
return variadic_;
}
private:
cpp_function_type(std::unique_ptr<cpp_type> return_type)
: return_type_(std::move(return_type)), variadic_(false)
{}
cpp_type_kind do_get_kind() const noexcept override
{
return cpp_type_kind::function_t;
}
std::unique_ptr<cpp_type> return_type_;
detail::intrusive_list<cpp_type> parameters_;
bool variadic_;
};
/// A [cppast::cpp_type]() that is a member function.
///
/// A member function with cv qualifier is created by wrapping it in
/// [cppast::cpp_cv_qualified_type](). A member function with reference qualifier is created by
/// wrapping it in [cppast::cpp_reference_type](). A member function pointer is created by wrapping
/// it in [cppast::cpp_pointer_type]().
class cpp_member_function_type final : public cpp_type
{
public:
/// Builds a [cppast::cpp_member_function_type]().
class builder
{ {
public: public:
/// Builds a [cppast::cpp_member_function_type](). /// \effects Sets the class and return type.
class builder builder(std::unique_ptr<cpp_type> class_type, std::unique_ptr<cpp_type> return_type)
: func_(new cpp_member_function_type(std::move(class_type), std::move(return_type)))
{}
/// \effects Adds a parameter type.
void add_parameter(std::unique_ptr<cpp_type> arg)
{ {
public: func_->parameters_.push_back(*func_, std::move(arg));
/// \effects Sets the class and return type.
builder(std::unique_ptr<cpp_type> class_type, std::unique_ptr<cpp_type> return_type)
: func_(new cpp_member_function_type(std::move(class_type), std::move(return_type)))
{
}
/// \effects Adds a parameter type.
void add_parameter(std::unique_ptr<cpp_type> arg)
{
func_->parameters_.push_back(*func_, std::move(arg));
}
/// \effects Adds an ellipsis, marking it as variadic.
void is_variadic()
{
func_->variadic_ = true;
}
/// \returns The finished [cppast::cpp_member_function_type]().
std::unique_ptr<cpp_member_function_type> finish()
{
return std::move(func_);
}
private:
std::unique_ptr<cpp_member_function_type> func_;
};
/// \returns A reference to the class [cppast::cpp_type]().
const cpp_type& class_type() const noexcept
{
return *class_type_;
} }
/// \returns A reference to the return [cppast::cpp_type](). /// \effects Adds an ellipsis, marking it as variadic.
const cpp_type& return_type() const noexcept void is_variadic()
{ {
return *return_type_; func_->variadic_ = true;
} }
/// \returns An iteratable object iterating over the parameter types. /// \returns The finished [cppast::cpp_member_function_type]().
detail::iteratable_intrusive_list<cpp_type> parameter_types() const noexcept std::unique_ptr<cpp_member_function_type> finish()
{ {
return type_safe::ref(parameters_); return std::move(func_);
}
/// \returns Whether or not the function is variadic (C-style ellipsis).
bool is_variadic() const noexcept
{
return variadic_;
} }
private: private:
cpp_member_function_type(std::unique_ptr<cpp_type> class_type, std::unique_ptr<cpp_member_function_type> func_;
std::unique_ptr<cpp_type> return_type)
: class_type_(std::move(class_type)), return_type_(std::move(return_type)), variadic_(false)
{
}
cpp_type_kind do_get_kind() const noexcept override
{
return cpp_type_kind::member_function_t;
}
std::unique_ptr<cpp_type> class_type_, return_type_;
detail::intrusive_list<cpp_type> parameters_;
bool variadic_;
}; };
/// A [cppast::cpp_type]() that is a member object. /// \returns A reference to the class [cppast::cpp_type]().
/// const cpp_type& class_type() const noexcept
/// A member object pointer is created by wrapping it in [cppast::cpp_pointer_type]().
class cpp_member_object_type final : public cpp_type
{ {
public: return *class_type_;
/// \returns A newly created member object type. }
static std::unique_ptr<cpp_member_object_type> build(std::unique_ptr<cpp_type> class_type,
std::unique_ptr<cpp_type> object_type)
{
return std::unique_ptr<cpp_member_object_type>(
new cpp_member_object_type(std::move(class_type), std::move(object_type)));
}
/// \returns A reference to the class [cppast::cpp_type](). /// \returns A reference to the return [cppast::cpp_type]().
const cpp_type& class_type() const noexcept const cpp_type& return_type() const noexcept
{ {
return *class_type_; return *return_type_;
} }
/// \returns A reference to the object [cppast::cpp_type](). /// \returns An iteratable object iterating over the parameter types.
const cpp_type& object_type() const noexcept detail::iteratable_intrusive_list<cpp_type> parameter_types() const noexcept
{ {
return *object_type_; return type_safe::ref(parameters_);
} }
private: /// \returns Whether or not the function is variadic (C-style ellipsis).
cpp_member_object_type(std::unique_ptr<cpp_type> class_type, bool is_variadic() const noexcept
std::unique_ptr<cpp_type> object_type) {
: class_type_(std::move(class_type)), object_type_(std::move(object_type)) return variadic_;
{ }
}
cpp_type_kind do_get_kind() const noexcept override private:
{ cpp_member_function_type(std::unique_ptr<cpp_type> class_type,
return cpp_type_kind::member_object_t; std::unique_ptr<cpp_type> return_type)
} : class_type_(std::move(class_type)), return_type_(std::move(return_type)), variadic_(false)
{}
std::unique_ptr<cpp_type> class_type_, object_type_; cpp_type_kind do_get_kind() const noexcept override
}; {
return cpp_type_kind::member_function_t;
}
std::unique_ptr<cpp_type> class_type_, return_type_;
detail::intrusive_list<cpp_type> parameters_;
bool variadic_;
};
/// A [cppast::cpp_type]() that is a member object.
///
/// A member object pointer is created by wrapping it in [cppast::cpp_pointer_type]().
class cpp_member_object_type final : public cpp_type
{
public:
/// \returns A newly created member object type.
static std::unique_ptr<cpp_member_object_type> build(std::unique_ptr<cpp_type> class_type,
std::unique_ptr<cpp_type> object_type)
{
return std::unique_ptr<cpp_member_object_type>(
new cpp_member_object_type(std::move(class_type), std::move(object_type)));
}
/// \returns A reference to the class [cppast::cpp_type]().
const cpp_type& class_type() const noexcept
{
return *class_type_;
}
/// \returns A reference to the object [cppast::cpp_type]().
const cpp_type& object_type() const noexcept
{
return *object_type_;
}
private:
cpp_member_object_type(std::unique_ptr<cpp_type> class_type,
std::unique_ptr<cpp_type> object_type)
: class_type_(std::move(class_type)), object_type_(std::move(object_type))
{}
cpp_type_kind do_get_kind() const noexcept override
{
return cpp_type_kind::member_object_t;
}
std::unique_ptr<cpp_type> class_type_, object_type_;
};
} // namespace cppast } // namespace cppast
#endif // CPPAST_CPP_FUNCTION_TYPE_HPP_INCLUDED #endif // CPPAST_CPP_FUNCTION_TYPE_HPP_INCLUDED

View file

@ -10,53 +10,51 @@
namespace cppast namespace cppast
{ {
/// A [cppast::cpp_entity]() modelling a language linkage. /// A [cppast::cpp_entity]() modelling a language linkage.
class cpp_language_linkage final : public cpp_entity, class cpp_language_linkage final : public cpp_entity,
public cpp_entity_container<cpp_language_linkage, cpp_entity> public cpp_entity_container<cpp_language_linkage, cpp_entity>
{
public:
static cpp_entity_kind kind() noexcept;
/// Builds a [cppast::cpp_language_linkage]().
class builder
{ {
public: public:
static cpp_entity_kind kind() noexcept; /// \effects Sets the name, that is the kind of language linkage.
explicit builder(std::string name) : linkage_(new cpp_language_linkage(std::move(name))) {}
/// Builds a [cppast::cpp_language_linkage](). /// \effects Adds an entity to the language linkage.
class builder void add_child(std::unique_ptr<cpp_entity> child)
{ {
public: linkage_->add_child(std::move(child));
/// \effects Sets the name, that is the kind of language linkage. }
explicit builder(std::string name) : linkage_(new cpp_language_linkage(std::move(name)))
{
}
/// \effects Adds an entity to the language linkage. /// \returns The not yet finished language linkage.
void add_child(std::unique_ptr<cpp_entity> child) cpp_language_linkage& get() const noexcept
{ {
linkage_->add_child(std::move(child)); return *linkage_;
} }
/// \returns The not yet finished language linkage. /// \returns The finalized language linkage.
cpp_language_linkage& get() const noexcept /// \notes It is not registered on purpose as nothing can refer to it.
{ std::unique_ptr<cpp_language_linkage> finish()
return *linkage_; {
} return std::move(linkage_);
}
/// \returns The finalized language linkage.
/// \notes It is not registered on purpose as nothing can refer to it.
std::unique_ptr<cpp_language_linkage> finish()
{
return std::move(linkage_);
}
private:
std::unique_ptr<cpp_language_linkage> linkage_;
};
/// \returns `true` if the linkage is a block, `false` otherwise.
bool is_block() const noexcept;
private: private:
using cpp_entity::cpp_entity; std::unique_ptr<cpp_language_linkage> linkage_;
cpp_entity_kind do_get_entity_kind() const noexcept override;
}; };
/// \returns `true` if the linkage is a block, `false` otherwise.
bool is_block() const noexcept;
private:
using cpp_entity::cpp_entity;
cpp_entity_kind do_get_entity_kind() const noexcept override;
};
} // namespace cppast } // namespace cppast
#endif // CPPAST_CPP_LANGUAGE_LINKAGE_HPP_INCLUDED #endif // CPPAST_CPP_LANGUAGE_LINKAGE_HPP_INCLUDED

View file

@ -11,312 +11,305 @@
namespace cppast namespace cppast
{ {
/// The `virtual`-ness of a member function. /// The `virtual`-ness of a member function.
/// ///
/// This is a [ts::flag_set]() `enum`. /// This is a [ts::flag_set]() `enum`.
/// \notes It does not specify whether a member function is `virtual` or not, /// \notes It does not specify whether a member function is `virtual` or not,
/// only the kind of `virtual`. /// only the kind of `virtual`.
/// \notes As surprising as it may be, any of these can be used in combination, /// \notes As surprising as it may be, any of these can be used in combination,
/// i.e. you can have a `final` non-overriding function or an overriding pure `virtual` function. /// i.e. you can have a `final` non-overriding function or an overriding pure `virtual` function.
enum class cpp_virtual_flags enum class cpp_virtual_flags
{
pure, //< Set if the function is pure.
override, //< Set if the function overrides a base class function.
final, //< Set if the function is marked `final`.
_flag_set_size, //< \exclude
};
/// The `virtual` information of a member function.
///
/// This is an optional of the combination of the [cppast::cpp_virtual_flags]().
/// If the optional has a value, the member function is `virtual`,
/// and the [ts::flag_set]() describes additional information.
using cpp_virtual = type_safe::optional<type_safe::flag_set<cpp_virtual_flags>>;
/// \returns Whether or not a member function is `virtual`.
inline bool is_virtual(const cpp_virtual& virt) noexcept
{
return virt.has_value();
}
/// \returns Whether or not a member function is pure.
inline bool is_pure(const cpp_virtual& virt) noexcept
{
return static_cast<bool>(virt.value_or(cpp_virtual_flags::final) & cpp_virtual_flags::pure);
}
/// \returns Whether or not a member function overrides another one.
inline bool is_overriding(const cpp_virtual& virt) noexcept
{
return static_cast<bool>(virt.value_or(cpp_virtual_flags::pure) & cpp_virtual_flags::override);
}
/// \returns Whether or not a member function is `final`.
inline bool is_final(const cpp_virtual& virt) noexcept
{
return static_cast<bool>(virt.value_or(cpp_virtual_flags::pure) & cpp_virtual_flags::final);
}
/// Base classes for all regular member function.
///
/// The two derived classes are [cppast::cpp_member_function]() and [cppast::cpp_conversion_op]().
class cpp_member_function_base : public cpp_function_base
{
public:
/// \returns The return type of the member function.
const cpp_type& return_type() const noexcept
{ {
pure, //< Set if the function is pure. return *return_type_;
override, //< Set if the function overrides a base class function.
final, //< Set if the function is marked `final`.
_flag_set_size, //< \exclude
};
/// The `virtual` information of a member function.
///
/// This is an optional of the combination of the [cppast::cpp_virtual_flags]().
/// If the optional has a value, the member function is `virtual`,
/// and the [ts::flag_set]() describes additional information.
using cpp_virtual = type_safe::optional<type_safe::flag_set<cpp_virtual_flags>>;
/// \returns Whether or not a member function is `virtual`.
inline bool is_virtual(const cpp_virtual& virt) noexcept
{
return virt.has_value();
} }
/// \returns Whether or not a member function is pure. /// \returns Whether or not it is `virtual`.
inline bool is_pure(const cpp_virtual& virt) noexcept bool is_virtual() const noexcept
{ {
return static_cast<bool>(virt.value_or(cpp_virtual_flags::final) & cpp_virtual_flags::pure); return virtual_info().has_value();
} }
/// \returns Whether or not a member function overrides another one. /// \returns The `virtual`-ness of the member function.
inline bool is_overriding(const cpp_virtual& virt) noexcept const cpp_virtual& virtual_info() const noexcept
{ {
return static_cast<bool>(virt.value_or(cpp_virtual_flags::pure) return virtual_;
& cpp_virtual_flags::override);
} }
/// \returns Whether or not a member function is `final`. /// \returns The cv-qualifier on the member function.
inline bool is_final(const cpp_virtual& virt) noexcept cpp_cv cv_qualifier() const noexcept
{ {
return static_cast<bool>(virt.value_or(cpp_virtual_flags::pure) & cpp_virtual_flags::final); return cv_;
} }
/// Base classes for all regular member function. /// \returns The ref-qualifier on the member function.
/// cpp_reference ref_qualifier() const noexcept
/// The two derived classes are [cppast::cpp_member_function]() and [cppast::cpp_conversion_op](). {
class cpp_member_function_base : public cpp_function_base return ref_;
}
/// \returns Whether or not the member function is `constexpr`.
bool is_constexpr() const noexcept
{
return constexpr_;
}
protected:
/// Builder class for member functions.
template <typename T>
class basic_member_builder : public basic_builder<T>
{ {
public: public:
/// \returns The return type of the member function. /// \effects Sets the name and return type.
const cpp_type& return_type() const noexcept basic_member_builder(std::string name, std::unique_ptr<cpp_type> return_type)
{ {
return *return_type_; this->function = std::unique_ptr<T>(new T(std::move(name), std::move(return_type)));
} }
/// \returns Whether or not it is `virtual`. /// \effects Sets the cv- and ref-qualifier.
bool is_virtual() const noexcept void cv_ref_qualifier(cpp_cv cv, cpp_reference ref) noexcept
{ {
return virtual_info().has_value(); auto& base = static_cast<cpp_member_function_base&>(*this->function);
base.cv_ = cv;
base.ref_ = ref;
} }
/// \returns The `virtual`-ness of the member function. /// \effects Sets the `virtual`-ness of the function.
const cpp_virtual& virtual_info() const noexcept void virtual_info(type_safe::flag_set<cpp_virtual_flags> virt) noexcept
{ {
return virtual_; static_cast<cpp_member_function_base&>(*this->function).virtual_ = virt;
} }
/// \returns The cv-qualifier on the member function. /// \effects Marks the function as `constexpr`.
cpp_cv cv_qualifier() const noexcept void is_constexpr() noexcept
{ {
return cv_; static_cast<cpp_member_function_base&>(*this->function).constexpr_ = true;
}
/// \returns The ref-qualifier on the member function.
cpp_reference ref_qualifier() const noexcept
{
return ref_;
}
/// \returns Whether or not the member function is `constexpr`.
bool is_constexpr() const noexcept
{
return constexpr_;
} }
protected: protected:
/// Builder class for member functions. basic_member_builder() noexcept = default;
template <typename T>
class basic_member_builder : public basic_builder<T>
{
public:
/// \effects Sets the name and return type.
basic_member_builder(std::string name, std::unique_ptr<cpp_type> return_type)
{
this->function = std::unique_ptr<T>(new T(std::move(name), std::move(return_type)));
}
/// \effects Sets the cv- and ref-qualifier.
void cv_ref_qualifier(cpp_cv cv, cpp_reference ref) noexcept
{
auto& base = static_cast<cpp_member_function_base&>(*this->function);
base.cv_ = cv;
base.ref_ = ref;
}
/// \effects Sets the `virtual`-ness of the function.
void virtual_info(type_safe::flag_set<cpp_virtual_flags> virt) noexcept
{
static_cast<cpp_member_function_base&>(*this->function).virtual_ = virt;
}
/// \effects Marks the function as `constexpr`.
void is_constexpr() noexcept
{
static_cast<cpp_member_function_base&>(*this->function).constexpr_ = true;
}
protected:
basic_member_builder() noexcept = default;
};
/// \effects Sets name and return type, as well as the rest to defaults.
cpp_member_function_base(std::string name, std::unique_ptr<cpp_type> return_type)
: cpp_function_base(std::move(name)),
return_type_(std::move(return_type)),
cv_(cpp_cv_none),
ref_(cpp_ref_none),
constexpr_(false)
{
}
protected:
std::string do_get_signature() const override;
private:
std::unique_ptr<cpp_type> return_type_;
cpp_virtual virtual_;
cpp_cv cv_;
cpp_reference ref_;
bool constexpr_;
}; };
/// A [cppast::cpp_entity]() modelling a member function. /// \effects Sets name and return type, as well as the rest to defaults.
class cpp_member_function final : public cpp_member_function_base cpp_member_function_base(std::string name, std::unique_ptr<cpp_type> return_type)
: cpp_function_base(std::move(name)), return_type_(std::move(return_type)), cv_(cpp_cv_none),
ref_(cpp_ref_none), constexpr_(false)
{}
protected:
std::string do_get_signature() const override;
private:
std::unique_ptr<cpp_type> return_type_;
cpp_virtual virtual_;
cpp_cv cv_;
cpp_reference ref_;
bool constexpr_;
};
/// A [cppast::cpp_entity]() modelling a member function.
class cpp_member_function final : public cpp_member_function_base
{
public:
static cpp_entity_kind kind() noexcept;
/// Builder for [cppast::cpp_member_function]().
class builder : public cpp_member_function_base::basic_member_builder<cpp_member_function>
{ {
public: public:
static cpp_entity_kind kind() noexcept; using cpp_member_function_base::basic_member_builder<
cpp_member_function>::basic_member_builder;
/// Builder for [cppast::cpp_member_function]().
class builder : public cpp_member_function_base::basic_member_builder<cpp_member_function>
{
public:
using cpp_member_function_base::basic_member_builder<
cpp_member_function>::basic_member_builder;
};
private:
using cpp_member_function_base::cpp_member_function_base;
cpp_entity_kind do_get_entity_kind() const noexcept override;
friend basic_member_builder<cpp_member_function>;
}; };
/// A [cppast::cpp_entity]() modelling a C++ conversion operator. private:
class cpp_conversion_op final : public cpp_member_function_base using cpp_member_function_base::cpp_member_function_base;
cpp_entity_kind do_get_entity_kind() const noexcept override;
friend basic_member_builder<cpp_member_function>;
};
/// A [cppast::cpp_entity]() modelling a C++ conversion operator.
class cpp_conversion_op final : public cpp_member_function_base
{
public:
static cpp_entity_kind kind() noexcept;
/// Builder for [cppast::cpp_conversion_op]().
class builder : public basic_member_builder<cpp_conversion_op>
{ {
public: public:
static cpp_entity_kind kind() noexcept; using basic_member_builder::basic_member_builder;
/// Builder for [cppast::cpp_conversion_op](). /// \effects Marks the conversion operator `explicit`.
class builder : public basic_member_builder<cpp_conversion_op> void is_explicit() noexcept
{ {
public: function->explicit_ = true;
using basic_member_builder::basic_member_builder;
/// \effects Marks the conversion operator `explicit`.
void is_explicit() noexcept
{
function->explicit_ = true;
}
private:
using basic_member_builder::add_parameter;
using basic_member_builder::is_variadic;
};
/// \returns Whether or not the conversion is `explicit`.
bool is_explicit() const noexcept
{
return explicit_;
} }
private: private:
cpp_conversion_op(std::string name, std::unique_ptr<cpp_type> return_t) using basic_member_builder::add_parameter;
: cpp_member_function_base(std::move(name), std::move(return_t)), explicit_(false) using basic_member_builder::is_variadic;
{
}
cpp_entity_kind do_get_entity_kind() const noexcept override;
bool explicit_;
friend basic_member_builder<cpp_conversion_op>;
}; };
/// A [cppast::cpp_entity]() modelling a C++ constructor. /// \returns Whether or not the conversion is `explicit`.
class cpp_constructor final : public cpp_function_base bool is_explicit() const noexcept
{
return explicit_;
}
private:
cpp_conversion_op(std::string name, std::unique_ptr<cpp_type> return_t)
: cpp_member_function_base(std::move(name), std::move(return_t)), explicit_(false)
{}
cpp_entity_kind do_get_entity_kind() const noexcept override;
bool explicit_;
friend basic_member_builder<cpp_conversion_op>;
};
/// A [cppast::cpp_entity]() modelling a C++ constructor.
class cpp_constructor final : public cpp_function_base
{
public:
static cpp_entity_kind kind() noexcept;
/// Builder for [cppast::cpp_constructor]().
class builder : public basic_builder<cpp_constructor>
{ {
public: public:
static cpp_entity_kind kind() noexcept; using basic_builder::basic_builder;
/// Builder for [cppast::cpp_constructor](). /// \effects Marks the constructor `explicit`.
class builder : public basic_builder<cpp_constructor> void is_explicit() noexcept
{ {
public: function->explicit_ = true;
using basic_builder::basic_builder;
/// \effects Marks the constructor `explicit`.
void is_explicit() noexcept
{
function->explicit_ = true;
}
/// \effects Marks the constructor `constexpr`.
void is_constexpr() noexcept
{
function->constexpr_ = true;
}
};
/// \returns Whether or not the constructor is `explicit`.
bool is_explicit() const noexcept
{
return explicit_;
} }
/// \returns Whether or not the constructor is `constexpr`. /// \effects Marks the constructor `constexpr`.
bool is_constexpr() const noexcept void is_constexpr() noexcept
{ {
return constexpr_; function->constexpr_ = true;
} }
private:
cpp_constructor(std::string name)
: cpp_function_base(std::move(name)), explicit_(false), constexpr_(false)
{
}
cpp_entity_kind do_get_entity_kind() const noexcept override;
bool explicit_;
bool constexpr_;
friend basic_builder<cpp_constructor>;
}; };
/// A [cppast::cpp_entity]() modelling a C++ destructor. /// \returns Whether or not the constructor is `explicit`.
class cpp_destructor final : public cpp_function_base bool is_explicit() const noexcept
{
return explicit_;
}
/// \returns Whether or not the constructor is `constexpr`.
bool is_constexpr() const noexcept
{
return constexpr_;
}
private:
cpp_constructor(std::string name)
: cpp_function_base(std::move(name)), explicit_(false), constexpr_(false)
{}
cpp_entity_kind do_get_entity_kind() const noexcept override;
bool explicit_;
bool constexpr_;
friend basic_builder<cpp_constructor>;
};
/// A [cppast::cpp_entity]() modelling a C++ destructor.
class cpp_destructor final : public cpp_function_base
{
public:
static cpp_entity_kind kind() noexcept;
/// Builds a [cppast::cpp_destructor]().
class builder : public basic_builder<cpp_destructor>
{ {
public: public:
static cpp_entity_kind kind() noexcept; using basic_builder::basic_builder;
/// Builds a [cppast::cpp_destructor](). /// \effects Sets the `virtual`-ness of the destructor.
class builder : public basic_builder<cpp_destructor> void virtual_info(cpp_virtual virt) noexcept
{ {
public: function->virtual_ = virt;
using basic_builder::basic_builder;
/// \effects Sets the `virtual`-ness of the destructor.
void virtual_info(cpp_virtual virt) noexcept
{
function->virtual_ = virt;
}
private:
using basic_builder::add_parameter;
using basic_builder::is_variadic;
};
/// \returns Whether or not it is `virtual`.
bool is_virtual() const noexcept
{
return virtual_info().has_value();
}
/// \returns The `virtual`-ness of the constructor.
cpp_virtual virtual_info() const noexcept
{
return virtual_;
} }
private: private:
cpp_destructor(std::string name) : cpp_function_base(std::move(name)) {} using basic_builder::add_parameter;
using basic_builder::is_variadic;
cpp_entity_kind do_get_entity_kind() const noexcept override;
cpp_virtual virtual_;
friend basic_builder<cpp_destructor>;
}; };
/// \returns Whether or not it is `virtual`.
bool is_virtual() const noexcept
{
return virtual_info().has_value();
}
/// \returns The `virtual`-ness of the constructor.
cpp_virtual virtual_info() const noexcept
{
return virtual_;
}
private:
cpp_destructor(std::string name) : cpp_function_base(std::move(name)) {}
cpp_entity_kind do_get_entity_kind() const noexcept override;
cpp_virtual virtual_;
friend basic_builder<cpp_destructor>;
};
} // namespace cppast } // namespace cppast
#endif // CPPAST_CPP_MEMBER_FUNCTION_HPP_INCLUDED #endif // CPPAST_CPP_MEMBER_FUNCTION_HPP_INCLUDED

View file

@ -10,84 +10,81 @@
namespace cppast namespace cppast
{ {
/// Base class for all kinds of member variables. /// Base class for all kinds of member variables.
class cpp_member_variable_base : public cpp_entity, public cpp_variable_base class cpp_member_variable_base : public cpp_entity, public cpp_variable_base
{
public:
/// \returns Whether or not the member variable is declared `mutable`.
bool is_mutable() const noexcept
{ {
public: return mutable_;
/// \returns Whether or not the member variable is declared `mutable`. }
bool is_mutable() const noexcept
{
return mutable_;
}
protected: protected:
cpp_member_variable_base(std::string name, std::unique_ptr<cpp_type> type, cpp_member_variable_base(std::string name, std::unique_ptr<cpp_type> type,
std::unique_ptr<cpp_expression> def, bool is_mutable) std::unique_ptr<cpp_expression> def, bool is_mutable)
: cpp_entity(std::move(name)), : cpp_entity(std::move(name)), cpp_variable_base(std::move(type), std::move(def)),
cpp_variable_base(std::move(type), std::move(def)), mutable_(is_mutable)
mutable_(is_mutable) {}
{
}
private: private:
bool mutable_; bool mutable_;
}; };
/// A [cppast::cpp_entity]() modelling a C++ member variable. /// A [cppast::cpp_entity]() modelling a C++ member variable.
class cpp_member_variable final : public cpp_member_variable_base class cpp_member_variable final : public cpp_member_variable_base
{
public:
static cpp_entity_kind kind() noexcept;
/// \returns A newly created and registered member variable.
/// \notes `def` may be `nullptr` in which case there is no member initializer provided.
static std::unique_ptr<cpp_member_variable> build(const cpp_entity_index& idx, cpp_entity_id id,
std::string name,
std::unique_ptr<cpp_type> type,
std::unique_ptr<cpp_expression> def,
bool is_mutable);
private:
using cpp_member_variable_base::cpp_member_variable_base;
cpp_entity_kind do_get_entity_kind() const noexcept override;
};
/// A [cppast::cpp_entity]() modelling a C++ bitfield.
class cpp_bitfield final : public cpp_member_variable_base
{
public:
static cpp_entity_kind kind() noexcept;
/// \returns A newly created and registered bitfield.
/// \notes It cannot have a member initializer, i.e. default value.
static std::unique_ptr<cpp_bitfield> build(const cpp_entity_index& idx, cpp_entity_id id,
std::string name, std::unique_ptr<cpp_type> type,
unsigned no_bits, bool is_mutable);
/// \returns A newly created unnamed bitfield.
/// \notes It will not be registered, as it is unnamed.
static std::unique_ptr<cpp_bitfield> build(std::unique_ptr<cpp_type> type, unsigned no_bits,
bool is_mutable);
/// \returns The number of bits of the bitfield.
unsigned no_bits() const noexcept
{ {
public: return bits_;
static cpp_entity_kind kind() noexcept; }
/// \returns A newly created and registered member variable. private:
/// \notes `def` may be `nullptr` in which case there is no member initializer provided. cpp_bitfield(std::string name, std::unique_ptr<cpp_type> type, unsigned no_bits,
static std::unique_ptr<cpp_member_variable> build(const cpp_entity_index& idx, bool is_mutable)
cpp_entity_id id, std::string name, : cpp_member_variable_base(std::move(name), std::move(type), nullptr, is_mutable),
std::unique_ptr<cpp_type> type, bits_(no_bits)
std::unique_ptr<cpp_expression> def, {}
bool is_mutable);
private: cpp_entity_kind do_get_entity_kind() const noexcept override;
using cpp_member_variable_base::cpp_member_variable_base;
cpp_entity_kind do_get_entity_kind() const noexcept override; unsigned bits_;
}; };
/// A [cppast::cpp_entity]() modelling a C++ bitfield.
class cpp_bitfield final : public cpp_member_variable_base
{
public:
static cpp_entity_kind kind() noexcept;
/// \returns A newly created and registered bitfield.
/// \notes It cannot have a member initializer, i.e. default value.
static std::unique_ptr<cpp_bitfield> build(const cpp_entity_index& idx, cpp_entity_id id,
std::string name, std::unique_ptr<cpp_type> type,
unsigned no_bits, bool is_mutable);
/// \returns A newly created unnamed bitfield.
/// \notes It will not be registered, as it is unnamed.
static std::unique_ptr<cpp_bitfield> build(std::unique_ptr<cpp_type> type, unsigned no_bits,
bool is_mutable);
/// \returns The number of bits of the bitfield.
unsigned no_bits() const noexcept
{
return bits_;
}
private:
cpp_bitfield(std::string name, std::unique_ptr<cpp_type> type, unsigned no_bits,
bool is_mutable)
: cpp_member_variable_base(std::move(name), std::move(type), nullptr, is_mutable),
bits_(no_bits)
{
}
cpp_entity_kind do_get_entity_kind() const noexcept override;
unsigned bits_;
};
} // namespace cppast } // namespace cppast
#endif // CPPAST_CPP_MEMBER_VARIABLE_HPP_INCLUDED #endif // CPPAST_CPP_MEMBER_VARIABLE_HPP_INCLUDED

View file

@ -7,196 +7,188 @@
#include <cppast/cpp_entity_container.hpp> #include <cppast/cpp_entity_container.hpp>
#include <cppast/cpp_entity_index.hpp> #include <cppast/cpp_entity_index.hpp>
#include <cppast/cpp_entity_ref.hpp>
#include <cppast/cpp_entity_kind.hpp> #include <cppast/cpp_entity_kind.hpp>
#include <cppast/cpp_entity_ref.hpp>
namespace cppast namespace cppast
{ {
/// A [cppast::cpp_entity]() modelling a namespace. /// A [cppast::cpp_entity]() modelling a namespace.
class cpp_namespace final : public cpp_entity, class cpp_namespace final : public cpp_entity,
public cpp_entity_container<cpp_namespace, cpp_entity> public cpp_entity_container<cpp_namespace, cpp_entity>
{
public:
static cpp_entity_kind kind() noexcept;
/// Builds a [cppast::cpp_namespace]().
class builder
{ {
public: public:
static cpp_entity_kind kind() noexcept; /// \effects Sets the namespace name and whether it is inline and nested.
explicit builder(std::string name, bool is_inline, bool is_nested)
: namespace_(new cpp_namespace(std::move(name), is_inline, is_nested))
{}
/// Builds a [cppast::cpp_namespace](). /// \effects Adds an entity.
class builder void add_child(std::unique_ptr<cpp_entity> child) noexcept
{ {
public: namespace_->add_child(std::move(child));
/// \effects Sets the namespace name and whether it is inline and nested.
explicit builder(std::string name, bool is_inline, bool is_nested)
: namespace_(new cpp_namespace(std::move(name), is_inline, is_nested))
{
}
/// \effects Adds an entity.
void add_child(std::unique_ptr<cpp_entity> child) noexcept
{
namespace_->add_child(std::move(child));
}
/// \returns The not yet finished namespace.
cpp_namespace& get() const noexcept
{
return *namespace_;
}
/// \effects Registers the namespace in the [cppast::cpp_entity_index](),
/// using the given [cppast::cpp_entity_id]().
/// \returns The finished namespace.
std::unique_ptr<cpp_namespace> finish(const cpp_entity_index& idx, cpp_entity_id id)
{
idx.register_namespace(std::move(id), type_safe::ref(*namespace_));
return std::move(namespace_);
}
private:
std::unique_ptr<cpp_namespace> namespace_;
};
/// \returns Whether or not the namespace is an `inline namespace`.
bool is_inline() const noexcept
{
return inline_;
} }
/// \returns Whether or not the namespace is part of a C++17 nested namespace. /// \returns The not yet finished namespace.
bool is_nested() const noexcept cpp_namespace& get() const noexcept
{ {
return nested_; return *namespace_;
} }
/// \returns Whether or not the namespace is anonymous. /// \effects Registers the namespace in the [cppast::cpp_entity_index](),
bool is_anonymous() const noexcept /// using the given [cppast::cpp_entity_id]().
/// \returns The finished namespace.
std::unique_ptr<cpp_namespace> finish(const cpp_entity_index& idx, cpp_entity_id id)
{ {
return name().empty(); idx.register_namespace(std::move(id), type_safe::ref(*namespace_));
return std::move(namespace_);
} }
private: private:
cpp_namespace(std::string name, bool is_inline, bool is_nested) std::unique_ptr<cpp_namespace> namespace_;
: cpp_entity(std::move(name)), inline_(is_inline), nested_(is_nested)
{
}
cpp_entity_kind do_get_entity_kind() const noexcept override;
type_safe::optional<cpp_scope_name> do_get_scope_name() const override
{
return type_safe::ref(*this);
}
bool inline_;
bool nested_;
}; };
/// \exclude /// \returns Whether or not the namespace is an `inline namespace`.
namespace detail bool is_inline() const noexcept
{ {
struct cpp_namespace_ref_predicate return inline_;
{ }
bool operator()(const cpp_entity& e);
};
} // namespace detail
/// A reference to a [cppast::cpp_namespace](). /// \returns Whether or not the namespace is part of a C++17 nested namespace.
using cpp_namespace_ref = bool is_nested() const noexcept
basic_cpp_entity_ref<cpp_namespace, detail::cpp_namespace_ref_predicate>;
/// A [cppast::cpp_entity]() modelling a namespace alias.
class cpp_namespace_alias final : public cpp_entity
{ {
public: return nested_;
static cpp_entity_kind kind() noexcept; }
/// \returns A newly created and registered namespace alias. /// \returns Whether or not the namespace is anonymous.
static std::unique_ptr<cpp_namespace_alias> build(const cpp_entity_index& idx, bool is_anonymous() const noexcept
cpp_entity_id id, std::string name, {
cpp_namespace_ref target); return name().empty();
}
/// \returns The [cppast::cpp_namespace_ref]() to the aliased namespace. private:
/// \notes If the namespace aliases aliases another namespace alias, cpp_namespace(std::string name, bool is_inline, bool is_nested)
/// the target entity will still be the namespace, not the alias. : cpp_entity(std::move(name)), inline_(is_inline), nested_(is_nested)
const cpp_namespace_ref& target() const noexcept {}
{
return target_;
}
private: cpp_entity_kind do_get_entity_kind() const noexcept override;
cpp_namespace_alias(std::string name, cpp_namespace_ref target)
: cpp_entity(std::move(name)), target_(std::move(target))
{
}
cpp_entity_kind do_get_entity_kind() const noexcept override; type_safe::optional<cpp_scope_name> do_get_scope_name() const override
{
return type_safe::ref(*this);
}
cpp_namespace_ref target_; bool inline_;
bool nested_;
};
/// \exclude
namespace detail
{
struct cpp_namespace_ref_predicate
{
bool operator()(const cpp_entity& e);
}; };
} // namespace detail
/// A [cppast::cpp_entity]() modelling a using directive. /// A reference to a [cppast::cpp_namespace]().
/// using cpp_namespace_ref = basic_cpp_entity_ref<cpp_namespace, detail::cpp_namespace_ref_predicate>;
/// A using directive is `using namespace std`, for example.
/// \notes It does not have a name. /// A [cppast::cpp_entity]() modelling a namespace alias.
class cpp_using_directive final : public cpp_entity class cpp_namespace_alias final : public cpp_entity
{
public:
static cpp_entity_kind kind() noexcept;
/// \returns A newly created and registered namespace alias.
static std::unique_ptr<cpp_namespace_alias> build(const cpp_entity_index& idx, cpp_entity_id id,
std::string name, cpp_namespace_ref target);
/// \returns The [cppast::cpp_namespace_ref]() to the aliased namespace.
/// \notes If the namespace aliases aliases another namespace alias,
/// the target entity will still be the namespace, not the alias.
const cpp_namespace_ref& target() const noexcept
{ {
public: return target_;
static cpp_entity_kind kind() noexcept; }
/// \returns A newly created using directive. private:
/// \notes It is not meant to be registered at the [cppast::cpp_entity_index](), cpp_namespace_alias(std::string name, cpp_namespace_ref target)
/// as nothing can refer to it. : cpp_entity(std::move(name)), target_(std::move(target))
static std::unique_ptr<cpp_using_directive> build(cpp_namespace_ref target) {}
{
return std::unique_ptr<cpp_using_directive>(new cpp_using_directive(std::move(target)));
}
/// \returns The [cppast::cpp_namespace_ref]() that is being used. cpp_entity_kind do_get_entity_kind() const noexcept override;
const cpp_namespace_ref& target() const
{
return target_;
}
private: cpp_namespace_ref target_;
cpp_using_directive(cpp_namespace_ref target) : cpp_entity(""), target_(std::move(target)) };
{
}
cpp_entity_kind do_get_entity_kind() const noexcept override; /// A [cppast::cpp_entity]() modelling a using directive.
///
/// A using directive is `using namespace std`, for example.
/// \notes It does not have a name.
class cpp_using_directive final : public cpp_entity
{
public:
static cpp_entity_kind kind() noexcept;
cpp_namespace_ref target_; /// \returns A newly created using directive.
}; /// \notes It is not meant to be registered at the [cppast::cpp_entity_index](),
/// as nothing can refer to it.
/// A [cppast::cpp_entity]() modelling a using declaration. static std::unique_ptr<cpp_using_directive> build(cpp_namespace_ref target)
///
/// A using declaration is `using std::vector`, for example.
/// \notes It does not have a name.
class cpp_using_declaration final : public cpp_entity
{ {
public: return std::unique_ptr<cpp_using_directive>(new cpp_using_directive(std::move(target)));
static cpp_entity_kind kind() noexcept; }
/// \returns A newly created using declaration. /// \returns The [cppast::cpp_namespace_ref]() that is being used.
/// \notes It is not meant to be registered at the [cppast::cpp_entity_index](), const cpp_namespace_ref& target() const
/// as nothing can refer to it. {
static std::unique_ptr<cpp_using_declaration> build(cpp_entity_ref target) return target_;
{ }
return std::unique_ptr<cpp_using_declaration>(
new cpp_using_declaration(std::move(target)));
}
/// \returns The [cppast::cpp_entity_ref]() that is being used. private:
/// \notes The name of the reference is the same as the name of this entity. cpp_using_directive(cpp_namespace_ref target) : cpp_entity(""), target_(std::move(target)) {}
const cpp_entity_ref& target() const noexcept
{
return target_;
}
private: cpp_entity_kind do_get_entity_kind() const noexcept override;
cpp_using_declaration(cpp_entity_ref target) : cpp_entity(""), target_(std::move(target)) {}
cpp_entity_kind do_get_entity_kind() const noexcept override; cpp_namespace_ref target_;
};
cpp_entity_ref target_; /// A [cppast::cpp_entity]() modelling a using declaration.
}; ///
/// A using declaration is `using std::vector`, for example.
/// \notes It does not have a name.
class cpp_using_declaration final : public cpp_entity
{
public:
static cpp_entity_kind kind() noexcept;
/// \returns A newly created using declaration.
/// \notes It is not meant to be registered at the [cppast::cpp_entity_index](),
/// as nothing can refer to it.
static std::unique_ptr<cpp_using_declaration> build(cpp_entity_ref target)
{
return std::unique_ptr<cpp_using_declaration>(new cpp_using_declaration(std::move(target)));
}
/// \returns The [cppast::cpp_entity_ref]() that is being used.
/// \notes The name of the reference is the same as the name of this entity.
const cpp_entity_ref& target() const noexcept
{
return target_;
}
private:
cpp_using_declaration(cpp_entity_ref target) : cpp_entity(""), target_(std::move(target)) {}
cpp_entity_kind do_get_entity_kind() const noexcept override;
cpp_entity_ref target_;
};
} // namespace cppast } // namespace cppast
#endif // CPPAST_CPP_NAMESPACE_HPP_INCLUDED #endif // CPPAST_CPP_NAMESPACE_HPP_INCLUDED

View file

@ -12,197 +12,194 @@
namespace cppast namespace cppast
{ {
/// A [cppast::cpp_entity]() modelling a macro parameter. /// A [cppast::cpp_entity]() modelling a macro parameter.
class cpp_macro_parameter final : public cpp_entity class cpp_macro_parameter final : public cpp_entity
{
public:
static cpp_entity_kind kind() noexcept;
/// \returns A newly built macro parameter.
/// \notes It is not meant to be registered in the [cppast::cpp_entity_index]() as no other
/// [cppast::cpp_entity]() can refer to it.
static std::unique_ptr<cpp_macro_parameter> build(std::string name)
{
return std::unique_ptr<cpp_macro_parameter>(new cpp_macro_parameter(std::move(name)));
}
private:
cpp_macro_parameter(std::string name) : cpp_entity(std::move(name)) {}
cpp_entity_kind do_get_entity_kind() const noexcept override;
};
/// A [cppast::cpp_entity]() modelling a macro definition.
class cpp_macro_definition final : public cpp_entity
{
public:
static cpp_entity_kind kind() noexcept;
/// \returns A newly built object like macro.
/// \notes It is not meant to be registered in the [cppast::cpp_entity_index](),
/// as no other [cppast::cpp_entity]() can refer to it.
static std::unique_ptr<cpp_macro_definition> build_object_like(std::string name,
std::string replacement)
{
std::unique_ptr<cpp_macro_definition> result{new cpp_macro_definition(std::move(name))};
result->replacement_ = std::move(replacement);
return result;
}
/// Builds a function like macro.
class function_like_builder
{ {
public: public:
static cpp_entity_kind kind() noexcept; /// \effects Sets the name of the function like macro.
function_like_builder(std::string name) : result_(new cpp_macro_definition(std::move(name)))
/// \returns A newly built macro parameter.
/// \notes It is not meant to be registered in the [cppast::cpp_entity_index]() as no other [cppast::cpp_entity]() can refer to it.
static std::unique_ptr<cpp_macro_parameter> build(std::string name)
{ {
return std::unique_ptr<cpp_macro_parameter>(new cpp_macro_parameter(std::move(name))); result_->kind_ = function_like;
} }
private: /// \effects Sets the replacement text.
cpp_macro_parameter(std::string name) : cpp_entity(std::move(name)) {} void replacement(std::string replacement)
{
result_->replacement_ = std::move(replacement);
}
cpp_entity_kind do_get_entity_kind() const noexcept override; /// \effects Marks the macro as variadic.
}; void is_variadic()
{
result_->kind_ = variadic_function;
}
/// A [cppast::cpp_entity]() modelling a macro definition. /// \effects Adds a parameter.
class cpp_macro_definition final : public cpp_entity /// \group param
{ void parameter(std::unique_ptr<cpp_macro_parameter> param)
public: {
static cpp_entity_kind kind() noexcept; result_->parameters_.push_back(*result_, std::move(param));
}
/// \group param
void parameter(std::string name)
{
parameter(cpp_macro_parameter::build(std::move(name)));
}
/// \returns A newly built object like macro. /// \returns The finished macro.
/// \notes It is not meant to be registered in the [cppast::cpp_entity_index](), /// \notes It is not meant to be registered in the [cppast::cpp_entity_index](),
/// as no other [cppast::cpp_entity]() can refer to it. /// as no other [cppast::cpp_entity]() can refer to it.
static std::unique_ptr<cpp_macro_definition> build_object_like(std::string name, std::unique_ptr<cpp_macro_definition> finish()
std::string replacement)
{ {
std::unique_ptr<cpp_macro_definition> result{new cpp_macro_definition(std::move(name))}; return std::move(result_);
result->replacement_ = std::move(replacement);
return result;
}
/// Builds a function like macro.
class function_like_builder
{
public:
/// \effects Sets the name of the function like macro.
function_like_builder(std::string name)
: result_(new cpp_macro_definition(std::move(name)))
{
result_->kind_ = function_like;
}
/// \effects Sets the replacement text.
void replacement(std::string replacement)
{
result_->replacement_ = std::move(replacement);
}
/// \effects Marks the macro as variadic.
void is_variadic()
{
result_->kind_ = variadic_function;
}
/// \effects Adds a parameter.
/// \group param
void parameter(std::unique_ptr<cpp_macro_parameter> param)
{
result_->parameters_.push_back(*result_, std::move(param));
}
/// \group param
void parameter(std::string name)
{
parameter(cpp_macro_parameter::build(std::move(name)));
}
/// \returns The finished macro.
/// \notes It is not meant to be registered in the [cppast::cpp_entity_index](),
/// as no other [cppast::cpp_entity]() can refer to it.
std::unique_ptr<cpp_macro_definition> finish()
{
return std::move(result_);
}
private:
std::unique_ptr<cpp_macro_definition> result_;
};
/// \returns The replacement text of the macro.
const std::string& replacement() const noexcept
{
return replacement_;
}
/// \returns Whether or not it is an object like macro.
bool is_object_like() const noexcept
{
return kind_ == object_like;
}
/// \returns Whether or not it is a function like macro.
bool is_function_like() const noexcept
{
return kind_ != object_like;
}
/// \returns Whether or not it is a variadic macro.
bool is_variadic() const noexcept
{
return kind_ == variadic_function;
}
/// \returns The parameters of the macro.
/// \notes It has none if it is not a function like macro.
detail::iteratable_intrusive_list<cpp_macro_parameter> parameters() const noexcept
{
return type_safe::ref(parameters_);
} }
private: private:
cpp_entity_kind do_get_entity_kind() const noexcept override; std::unique_ptr<cpp_macro_definition> result_;
cpp_macro_definition(std::string name) : cpp_entity(std::move(name)), kind_(object_like) {}
detail::intrusive_list<cpp_macro_parameter> parameters_;
std::string replacement_;
enum : char
{
object_like,
function_like,
variadic_function,
} kind_;
friend function_like_builder;
}; };
/// The kind of [cppast::cpp_include_directive](). /// \returns The replacement text of the macro.
enum class cpp_include_kind const std::string& replacement() const noexcept
{ {
system, //< An `#include <...>`. return replacement_;
local, //< An `#include "..."`. }
};
/// A [cppast::cpp_entity]() modelling an `#include`. /// \returns Whether or not it is an object like macro.
class cpp_include_directive final : public cpp_entity bool is_object_like() const noexcept
{ {
public: return kind_ == object_like;
static cpp_entity_kind kind() noexcept; }
/// \returns A newly built include directive. /// \returns Whether or not it is a function like macro.
/// \notes It is not meant to be registered in the [cppast::cpp_entity_index](), bool is_function_like() const noexcept
/// as no other [cppast::cpp_entity]() can refer to it. {
static std::unique_ptr<cpp_include_directive> build(const cpp_file_ref& target, return kind_ != object_like;
cpp_include_kind kind, }
std::string full_path)
{
return std::unique_ptr<cpp_include_directive>(
new cpp_include_directive(target, kind, std::move(full_path)));
}
/// \returns A reference to the [cppast::cpp_file]() it includes. /// \returns Whether or not it is a variadic macro.
cpp_file_ref target() const noexcept bool is_variadic() const noexcept
{ {
return cpp_file_ref(target_, name()); return kind_ == variadic_function;
} }
/// \returns The kind of include it is. /// \returns The parameters of the macro.
cpp_include_kind include_kind() const noexcept /// \notes It has none if it is not a function like macro.
{ detail::iteratable_intrusive_list<cpp_macro_parameter> parameters() const noexcept
return kind_; {
} return type_safe::ref(parameters_);
}
/// \returns The full path of the included file. private:
const std::string& full_path() const noexcept cpp_entity_kind do_get_entity_kind() const noexcept override;
{
return full_path_;
}
private: cpp_macro_definition(std::string name) : cpp_entity(std::move(name)), kind_(object_like) {}
cpp_entity_kind do_get_entity_kind() const noexcept override;
cpp_include_directive(const cpp_file_ref& target, cpp_include_kind kind, detail::intrusive_list<cpp_macro_parameter> parameters_;
std::string full_path) std::string replacement_;
: cpp_entity(target.name()),
target_(target.id()[0u]),
kind_(kind),
full_path_(std::move(full_path))
{
DEBUG_ASSERT(!target.is_overloaded(), detail::precondition_error_handler{});
}
cpp_entity_id target_; enum : char
cpp_include_kind kind_; {
std::string full_path_; object_like,
}; function_like,
variadic_function,
} kind_;
friend function_like_builder;
};
/// The kind of [cppast::cpp_include_directive]().
enum class cpp_include_kind
{
system, //< An `#include <...>`.
local, //< An `#include "..."`.
};
/// A [cppast::cpp_entity]() modelling an `#include`.
class cpp_include_directive final : public cpp_entity
{
public:
static cpp_entity_kind kind() noexcept;
/// \returns A newly built include directive.
/// \notes It is not meant to be registered in the [cppast::cpp_entity_index](),
/// as no other [cppast::cpp_entity]() can refer to it.
static std::unique_ptr<cpp_include_directive> build(const cpp_file_ref& target,
cpp_include_kind kind,
std::string full_path)
{
return std::unique_ptr<cpp_include_directive>(
new cpp_include_directive(target, kind, std::move(full_path)));
}
/// \returns A reference to the [cppast::cpp_file]() it includes.
cpp_file_ref target() const noexcept
{
return cpp_file_ref(target_, name());
}
/// \returns The kind of include it is.
cpp_include_kind include_kind() const noexcept
{
return kind_;
}
/// \returns The full path of the included file.
const std::string& full_path() const noexcept
{
return full_path_;
}
private:
cpp_entity_kind do_get_entity_kind() const noexcept override;
cpp_include_directive(const cpp_file_ref& target, cpp_include_kind kind, std::string full_path)
: cpp_entity(target.name()), target_(target.id()[0u]), kind_(kind),
full_path_(std::move(full_path))
{
DEBUG_ASSERT(!target.is_overloaded(), detail::precondition_error_handler{});
}
cpp_entity_id target_;
cpp_include_kind kind_;
std::string full_path_;
};
} // namespace cppast } // namespace cppast
#endif // CPPAST_CPP_PREPROCESSOR_HPP_INCLUDED #endif // CPPAST_CPP_PREPROCESSOR_HPP_INCLUDED

View file

@ -10,43 +10,42 @@
namespace cppast namespace cppast
{ {
class cpp_static_assert : public cpp_entity class cpp_static_assert : public cpp_entity
{
public:
static cpp_entity_kind kind() noexcept;
/// \returns A newly created `static_assert()` entity.
/// \notes It will not be registered as nothing can refer to it.
static std::unique_ptr<cpp_static_assert> build(std::unique_ptr<cpp_expression> expr,
std::string msg)
{ {
public: return std::unique_ptr<cpp_static_assert>(
static cpp_entity_kind kind() noexcept; new cpp_static_assert(std::move(expr), std::move(msg)));
}
/// \returns A newly created `static_assert()` entity. /// \returns A reference to the [cppast::cpp_expression]() that is being asserted.
/// \notes It will not be registered as nothing can refer to it. const cpp_expression& expression() const noexcept
static std::unique_ptr<cpp_static_assert> build(std::unique_ptr<cpp_expression> expr, {
std::string msg) return *expr_;
{ }
return std::unique_ptr<cpp_static_assert>(
new cpp_static_assert(std::move(expr), std::move(msg)));
}
/// \returns A reference to the [cppast::cpp_expression]() that is being asserted. /// \returns A reference to the message of the assertion.
const cpp_expression& expression() const noexcept const std::string& message() const noexcept
{ {
return *expr_; return msg_;
} }
/// \returns A reference to the message of the assertion. private:
const std::string& message() const noexcept cpp_static_assert(std::unique_ptr<cpp_expression> expr, std::string msg)
{ : cpp_entity(""), expr_(std::move(expr)), msg_(std::move(msg))
return msg_; {}
}
private: cpp_entity_kind do_get_entity_kind() const noexcept override;
cpp_static_assert(std::unique_ptr<cpp_expression> expr, std::string msg)
: cpp_entity(""), expr_(std::move(expr)), msg_(std::move(msg))
{
}
cpp_entity_kind do_get_entity_kind() const noexcept override; std::unique_ptr<cpp_expression> expr_;
std::string msg_;
std::unique_ptr<cpp_expression> expr_; };
std::string msg_;
};
} // namespace cppast } // namespace cppast
#endif // CPPAST_CPP_STATIC_ASSERT_HPP_INCLUDED #endif // CPPAST_CPP_STATIC_ASSERT_HPP_INCLUDED

View file

@ -7,49 +7,47 @@
namespace cppast namespace cppast
{ {
/// C++ storage class specifiers. /// C++ storage class specifiers.
/// ///
/// See http://en.cppreference.com/w/cpp/language/storage_duration, for example. /// See http://en.cppreference.com/w/cpp/language/storage_duration, for example.
/// \notes These are just all the possible *keywords* used in a variable declaration, /// \notes These are just all the possible *keywords* used in a variable declaration,
/// not necessarily their *semantic* meaning. /// not necessarily their *semantic* meaning.
enum cpp_storage_class_specifiers : int enum cpp_storage_class_specifiers : int
{ {
cpp_storage_class_none = 0, //< no storage class specifier given. cpp_storage_class_none = 0, //< no storage class specifier given.
cpp_storage_class_auto = 1, //< *automatic* storage duration. cpp_storage_class_auto = 1, //< *automatic* storage duration.
cpp_storage_class_static = cpp_storage_class_static = 2, //< *static* or *thread* storage duration and *internal* linkage.
2, //< *static* or *thread* storage duration and *internal* linkage. cpp_storage_class_extern = 4, //< *static* or *thread* storage duration and *external* linkage.
cpp_storage_class_extern =
4, //< *static* or *thread* storage duration and *external* linkage.
cpp_storage_class_thread_local = 8, //< *thread* storage duration. cpp_storage_class_thread_local = 8, //< *thread* storage duration.
/// \notes This is the only one that can be combined with the others. /// \notes This is the only one that can be combined with the others.
}; };
/// \returns Whether the [cppast::cpp_storage_class_specifiers]() contain `thread_local`. /// \returns Whether the [cppast::cpp_storage_class_specifiers]() contain `thread_local`.
inline bool is_thread_local(cpp_storage_class_specifiers spec) noexcept inline bool is_thread_local(cpp_storage_class_specifiers spec) noexcept
{ {
return (spec & cpp_storage_class_thread_local) != 0; return (spec & cpp_storage_class_thread_local) != 0;
} }
/// \returns Whether the [cppast::cpp_storage_class_speicifers]() contain `auto`. /// \returns Whether the [cppast::cpp_storage_class_speicifers]() contain `auto`.
inline bool is_auto(cpp_storage_class_specifiers spec) noexcept inline bool is_auto(cpp_storage_class_specifiers spec) noexcept
{ {
return (spec & cpp_storage_class_auto) != 0; return (spec & cpp_storage_class_auto) != 0;
} }
/// \returns Whether the [cppast::cpp_storage_class_specifiers]() contain `static`. /// \returns Whether the [cppast::cpp_storage_class_specifiers]() contain `static`.
inline bool is_static(cpp_storage_class_specifiers spec) noexcept inline bool is_static(cpp_storage_class_specifiers spec) noexcept
{ {
return (spec & cpp_storage_class_static) != 0; return (spec & cpp_storage_class_static) != 0;
} }
/// \returns Whether the [cppast::cpp_storage_class_specifiers]() contain `extern`. /// \returns Whether the [cppast::cpp_storage_class_specifiers]() contain `extern`.
inline bool is_extern(cpp_storage_class_specifiers spec) noexcept inline bool is_extern(cpp_storage_class_specifiers spec) noexcept
{ {
return (spec & cpp_storage_class_extern) != 0; return (spec & cpp_storage_class_extern) != 0;
} }
} // namespace cppast } // namespace cppast
#endif // CPPAST_CPP_STORAGE_CLASS_SPECIFIERS_HPP_INCLUDED #endif // CPPAST_CPP_STORAGE_CLASS_SPECIFIERS_HPP_INCLUDED

View file

@ -11,275 +11,263 @@
#include <cppast/cpp_entity.hpp> #include <cppast/cpp_entity.hpp>
#include <cppast/cpp_entity_container.hpp> #include <cppast/cpp_entity_container.hpp>
#include <cppast/cpp_token.hpp>
#include <cppast/cpp_template_parameter.hpp> #include <cppast/cpp_template_parameter.hpp>
#include <cppast/cpp_token.hpp>
namespace cppast namespace cppast
{ {
/// Base class for all entities modelling a C++ template of some kind. /// Base class for all entities modelling a C++ template of some kind.
///
/// It is a container of a single [cppast::cpp_entity]() that is the entity being templated.
class cpp_template : public cpp_entity, public cpp_entity_container<cpp_template, cpp_entity>
{
public:
/// \returns An iteratable object iterating over the [cppast::cpp_template_parameter]()
/// entities. \notes These may be empty for a full specialization.
detail::iteratable_intrusive_list<cpp_template_parameter> parameters() const noexcept
{
return type_safe::ref(parameters_);
}
protected:
/// Builder class for templates.
/// ///
/// It is a container of a single [cppast::cpp_entity]() that is the entity being templated. /// Inherit from it to provide additional setter.
class cpp_template : public cpp_entity, public cpp_entity_container<cpp_template, cpp_entity> template <class T, class EntityT>
class basic_builder
{ {
public: public:
/// \returns An iteratable object iterating over the [cppast::cpp_template_parameter]() entities. /// \effects Sets the entity that is begin templated.
/// \notes These may be empty for a full specialization. basic_builder(std::unique_ptr<EntityT> templ) : template_entity(new T(std::move(templ))) {}
detail::iteratable_intrusive_list<cpp_template_parameter> parameters() const noexcept
basic_builder(basic_builder&&) = default;
/// \effects Adds a parameter.
void add_parameter(std::unique_ptr<cpp_template_parameter> parameter)
{ {
return type_safe::ref(parameters_); static_cast<cpp_template&>(*template_entity)
.parameters_.push_back(*template_entity, std::move(parameter));
}
/// \returns The not yet finished template.
T& get() const noexcept
{
return *template_entity;
}
/// \effects Registers the template.
/// \returns The finished template.
std::unique_ptr<T> finish(const cpp_entity_index& idx, cpp_entity_id id, bool is_definition)
{
if (is_definition)
idx.register_definition(std::move(id), type_safe::cref(*template_entity));
else
idx.register_forward_declaration(std::move(id), type_safe::cref(*template_entity));
return std::move(template_entity);
} }
protected: protected:
/// Builder class for templates. basic_builder() = default;
/// ~basic_builder() noexcept = default;
/// Inherit from it to provide additional setter.
template <class T, class EntityT> std::unique_ptr<T> template_entity;
class basic_builder };
/// \effects Sets the entity to be templated.
cpp_template(std::unique_ptr<cpp_entity> entity) : cpp_entity(entity->name())
{
add_child(std::move(entity));
}
private:
type_safe::optional<cppast::cpp_scope_name> do_get_scope_name() const override
{
return begin()->scope_name()
? type_safe::make_optional(cppast::cpp_scope_name(type_safe::ref(*this)))
: type_safe::nullopt;
}
detail::intrusive_list<cpp_template_parameter> parameters_;
};
/// A [cppast::cpp_type]() representing an instantiation of a [cppast::cpp_template]().
class cpp_template_instantiation_type final : public cpp_type
{
public:
/// Builds a [cppast::cpp_template_instantiation]().
class builder
{
public:
/// \effects Sets the primary template being instantiated.
builder(cpp_template_ref templ)
: result_(new cpp_template_instantiation_type(std::move(templ)))
{}
/// \effects Adds the next argument.
/// \requires No call to `add_unexposed_arguments()` has happened before.
void add_argument(cpp_template_argument arg)
{ {
public: result_->arguments_.value(type_safe::variant_type<std::vector<cpp_template_argument>>{})
/// \effects Sets the entity that is begin templated. .push_back(std::move(arg));
basic_builder(std::unique_ptr<EntityT> templ) : template_entity(new T(std::move(templ))) }
{
}
basic_builder(basic_builder&&) = default; /// \effects Adds unexposed arguments as string.
void add_unexposed_arguments(std::string arg)
/// \effects Adds a parameter.
void add_parameter(std::unique_ptr<cpp_template_parameter> parameter)
{
static_cast<cpp_template&>(*template_entity)
.parameters_.push_back(*template_entity, std::move(parameter));
}
/// \returns The not yet finished template.
T& get() const noexcept
{
return *template_entity;
}
/// \effects Registers the template.
/// \returns The finished template.
std::unique_ptr<T> finish(const cpp_entity_index& idx, cpp_entity_id id,
bool is_definition)
{
if (is_definition)
idx.register_definition(std::move(id), type_safe::cref(*template_entity));
else
idx.register_forward_declaration(std::move(id),
type_safe::cref(*template_entity));
return std::move(template_entity);
}
protected:
basic_builder() = default;
~basic_builder() noexcept = default;
std::unique_ptr<T> template_entity;
};
/// \effects Sets the entity to be templated.
cpp_template(std::unique_ptr<cpp_entity> entity) : cpp_entity(entity->name())
{ {
add_child(std::move(entity)); result_->arguments_ = std::move(arg);
}
/// \returns The finished instantiation.
std::unique_ptr<cpp_template_instantiation_type> finish()
{
return std::move(result_);
} }
private: private:
type_safe::optional<cppast::cpp_scope_name> do_get_scope_name() const override std::unique_ptr<cpp_template_instantiation_type> result_;
{
return begin()->scope_name() ?
type_safe::make_optional(cppast::cpp_scope_name(type_safe::ref(*this))) :
type_safe::nullopt;
}
detail::intrusive_list<cpp_template_parameter> parameters_;
}; };
/// A [cppast::cpp_type]() representing an instantiation of a [cppast::cpp_template](). /// \returns A reference to the template that is being instantiated.
class cpp_template_instantiation_type final : public cpp_type /// \notes It could also point to a specialization,
/// this is just the *primary* template.
const cpp_template_ref& primary_template() const noexcept
{
return templ_;
}
/// \returns Whether or not the arguments are exposed.
bool arguments_exposed() const noexcept
{
return arguments_.has_value(type_safe::variant_type<std::vector<cpp_template_argument>>{});
}
/// \returns An array ref to the [cppast::cpp_template_argument](), if there are any.
/// \requires The arguments are exposed, i.e. `arguments_exposed()` returns `true`.
type_safe::optional<type_safe::array_ref<const cpp_template_argument>> arguments() const
noexcept
{
auto& vec = arguments_.value(type_safe::variant_type<std::vector<cpp_template_argument>>{});
if (vec.empty())
return type_safe::nullopt;
return type_safe::ref(vec.data(), vec.size());
}
/// \returns The unexposed arguments as string.
/// \requires The arguments are not exposed, i.e. `arguments_exposed()` returns `false`.
const std::string& unexposed_arguments() const noexcept
{
return arguments_.value(type_safe::variant_type<std::string>{});
}
private:
cpp_template_instantiation_type(cpp_template_ref ref)
: arguments_(type_safe::variant_type<std::vector<cpp_template_argument>>{}),
templ_(std::move(ref))
{}
cpp_type_kind do_get_kind() const noexcept override
{
return cpp_type_kind::template_instantiation_t;
}
type_safe::variant<std::vector<cpp_template_argument>, std::string> arguments_;
cpp_template_ref templ_;
};
/// Base class for all entities modelling a C++ template specialization.
class cpp_template_specialization : public cpp_template
{
public:
/// \returns A reference to the template that is being specialized.
cpp_template_ref primary_template() const noexcept
{
return cpp_template_ref(templ_, name());
}
/// \returns Whether or not the arguments are exposed.
bool arguments_exposed() const noexcept
{
return arguments_.has_value(type_safe::variant_type<std::vector<cpp_template_argument>>{});
}
/// \returns An iteratable object iterating over the [cppast::cpp_template_argument]()s.
/// \requires The arguments are exposed, i.e. `arguments_exposed()` returns `true`.
/// \notes For function template specializations it can be empty,
/// meaning that the arguments are not explictly given but deduced from the signature.
type_safe::array_ref<const cpp_template_argument> arguments() const noexcept
{
auto& vec = arguments_.value(type_safe::variant_type<std::vector<cpp_template_argument>>{});
return type_safe::ref(vec.data(), vec.size());
}
/// \returns The unexposed arguments as string.
/// \requires The arguments are not exposed, i.e. `arguments_exposed()` returns `false`.
/// \notes For function template specializations it can be empty,
/// meaning that the arguments are not explictly given but deduced from the signature.
const cpp_token_string& unexposed_arguments() const noexcept
{
return arguments_.value(type_safe::variant_type<cpp_token_string>{});
}
/// \returns Whether or not the specialization is a full specialization.
bool is_full_specialization() const noexcept
{
// if no template parameters are given, it is a full specialization
return parameters().empty();
}
protected:
/// Builder class for specializations.
///
/// Inherit from it to provide additional setter.
template <class T, class EntityT>
class specialization_builder : public basic_builder<T, EntityT>
{ {
public: public:
/// Builds a [cppast::cpp_template_instantiation]().
class builder
{
public:
/// \effects Sets the primary template being instantiated.
builder(cpp_template_ref templ)
: result_(new cpp_template_instantiation_type(std::move(templ)))
{
}
/// \effects Adds the next argument.
/// \requires No call to `add_unexposed_arguments()` has happened before.
void add_argument(cpp_template_argument arg)
{
result_->arguments_
.value(type_safe::variant_type<std::vector<cpp_template_argument>>{})
.push_back(std::move(arg));
}
/// \effects Adds unexposed arguments as string.
void add_unexposed_arguments(std::string arg)
{
result_->arguments_ = std::move(arg);
}
/// \returns The finished instantiation.
std::unique_ptr<cpp_template_instantiation_type> finish()
{
return std::move(result_);
}
private:
std::unique_ptr<cpp_template_instantiation_type> result_;
};
/// \returns A reference to the template that is being instantiated.
/// \notes It could also point to a specialization,
/// this is just the *primary* template.
const cpp_template_ref& primary_template() const noexcept
{
return templ_;
}
/// \returns Whether or not the arguments are exposed.
bool arguments_exposed() const noexcept
{
return arguments_.has_value(
type_safe::variant_type<std::vector<cpp_template_argument>>{});
}
/// \returns An array ref to the [cppast::cpp_template_argument](), if there are any.
/// \requires The arguments are exposed, i.e. `arguments_exposed()` returns `true`.
type_safe::optional<type_safe::array_ref<const cpp_template_argument>> arguments() const
noexcept
{
auto& vec =
arguments_.value(type_safe::variant_type<std::vector<cpp_template_argument>>{});
if (vec.empty())
return type_safe::nullopt;
return type_safe::ref(vec.data(), vec.size());
}
/// \returns The unexposed arguments as string.
/// \requires The arguments are not exposed, i.e. `arguments_exposed()` returns `false`.
const std::string& unexposed_arguments() const noexcept
{
return arguments_.value(type_safe::variant_type<std::string>{});
}
private:
cpp_template_instantiation_type(cpp_template_ref ref)
: arguments_(type_safe::variant_type<std::vector<cpp_template_argument>>{}),
templ_(std::move(ref))
{
}
cpp_type_kind do_get_kind() const noexcept override
{
return cpp_type_kind::template_instantiation_t;
}
type_safe::variant<std::vector<cpp_template_argument>, std::string> arguments_;
cpp_template_ref templ_;
};
/// Base class for all entities modelling a C++ template specialization.
class cpp_template_specialization : public cpp_template
{
public:
/// \returns A reference to the template that is being specialized.
cpp_template_ref primary_template() const noexcept
{
return cpp_template_ref(templ_, name());
}
/// \returns Whether or not the arguments are exposed.
bool arguments_exposed() const noexcept
{
return arguments_.has_value(
type_safe::variant_type<std::vector<cpp_template_argument>>{});
}
/// \returns An iteratable object iterating over the [cppast::cpp_template_argument]()s.
/// \requires The arguments are exposed, i.e. `arguments_exposed()` returns `true`.
/// \notes For function template specializations it can be empty,
/// meaning that the arguments are not explictly given but deduced from the signature.
type_safe::array_ref<const cpp_template_argument> arguments() const noexcept
{
auto& vec =
arguments_.value(type_safe::variant_type<std::vector<cpp_template_argument>>{});
return type_safe::ref(vec.data(), vec.size());
}
/// \returns The unexposed arguments as string.
/// \requires The arguments are not exposed, i.e. `arguments_exposed()` returns `false`.
/// \notes For function template specializations it can be empty,
/// meaning that the arguments are not explictly given but deduced from the signature.
const cpp_token_string& unexposed_arguments() const noexcept
{
return arguments_.value(type_safe::variant_type<cpp_token_string>{});
}
/// \returns Whether or not the specialization is a full specialization.
bool is_full_specialization() const noexcept
{
// if no template parameters are given, it is a full specialization
return parameters().empty();
}
protected:
/// Builder class for specializations.
///
/// Inherit from it to provide additional setter.
template <class T, class EntityT>
class specialization_builder : public basic_builder<T, EntityT>
{
public:
/// \effects Sets the entity that is being templated and the primary template.
specialization_builder(std::unique_ptr<EntityT> entity, const cpp_template_ref& templ)
{
this->template_entity = std::unique_ptr<T>(new T(std::move(entity), templ));
}
/// \effects Adds the next argument for the [cppast::cpp_template_parameter]() of the primary template.
/// \requires No call to `add_unexposed_arguments()` has happened before.
void add_argument(cpp_template_argument arg)
{
auto& specialization =
static_cast<cpp_template_specialization&>(*this->template_entity);
specialization.arguments_
.value(type_safe::variant_type<std::vector<cpp_template_argument>>{})
.push_back(std::move(arg));
}
/// \effects Adds unexposed arguments as string.
void add_unexposed_arguments(cpp_token_string arg)
{
auto& specialization =
static_cast<cpp_template_specialization&>(*this->template_entity);
specialization.arguments_ = std::move(arg);
}
protected:
specialization_builder() = default;
};
/// \effects Sets the entity that is being templated and the primary template. /// \effects Sets the entity that is being templated and the primary template.
cpp_template_specialization(std::unique_ptr<cpp_entity> entity, specialization_builder(std::unique_ptr<EntityT> entity, const cpp_template_ref& templ)
const cpp_template_ref& templ)
: cpp_template(std::move(entity)),
arguments_(type_safe::variant_type<std::vector<cpp_template_argument>>{}),
templ_(templ.id()[0u])
{ {
DEBUG_ASSERT(!templ.is_overloaded() this->template_entity = std::unique_ptr<T>(new T(std::move(entity), templ));
&& (templ.name().empty() || templ.name() == begin()->name()),
detail::precondition_error_handler{}, "invalid name of template ref");
} }
private: /// \effects Adds the next argument for the [cppast::cpp_template_parameter]() of the
type_safe::variant<std::vector<cpp_template_argument>, cpp_token_string> arguments_; /// primary template. \requires No call to `add_unexposed_arguments()` has happened before.
cpp_entity_id templ_; void add_argument(cpp_template_argument arg)
{
auto& specialization
= static_cast<cpp_template_specialization&>(*this->template_entity);
specialization.arguments_
.value(type_safe::variant_type<std::vector<cpp_template_argument>>{})
.push_back(std::move(arg));
}
/// \effects Adds unexposed arguments as string.
void add_unexposed_arguments(cpp_token_string arg)
{
auto& specialization
= static_cast<cpp_template_specialization&>(*this->template_entity);
specialization.arguments_ = std::move(arg);
}
protected:
specialization_builder() = default;
}; };
/// \effects Sets the entity that is being templated and the primary template.
cpp_template_specialization(std::unique_ptr<cpp_entity> entity, const cpp_template_ref& templ)
: cpp_template(std::move(entity)),
arguments_(type_safe::variant_type<std::vector<cpp_template_argument>>{}),
templ_(templ.id()[0u])
{
DEBUG_ASSERT(!templ.is_overloaded()
&& (templ.name().empty() || templ.name() == begin()->name()),
detail::precondition_error_handler{}, "invalid name of template ref");
}
private:
type_safe::variant<std::vector<cpp_template_argument>, cpp_token_string> arguments_;
cpp_entity_id templ_;
};
} // namespace cppast } // namespace cppast
#endif // CPPAST_CPP_TEMPLATE_HPP_INCLUDED #endif // CPPAST_CPP_TEMPLATE_HPP_INCLUDED

View file

@ -8,308 +8,295 @@
#include <type_safe/optional.hpp> #include <type_safe/optional.hpp>
#include <type_safe/variant.hpp> #include <type_safe/variant.hpp>
#include <cppast/detail/intrusive_list.hpp>
#include <cppast/cpp_entity.hpp> #include <cppast/cpp_entity.hpp>
#include <cppast/cpp_variable_base.hpp> #include <cppast/cpp_variable_base.hpp>
#include <cppast/detail/intrusive_list.hpp>
namespace cppast namespace cppast
{ {
/// Base class for all entities modelling a template parameter of some kind. /// Base class for all entities modelling a template parameter of some kind.
class cpp_template_parameter : public cpp_entity class cpp_template_parameter : public cpp_entity
{
public:
/// \returns Whether or not the parameter is variadic.
bool is_variadic() const noexcept
{
return variadic_;
}
protected:
cpp_template_parameter(std::string name, bool variadic)
: cpp_entity(std::move(name)), variadic_(variadic)
{}
private:
bool variadic_;
};
/// The kind of keyword used in a template parameter.
enum class cpp_template_keyword
{
keyword_class,
keyword_typename
};
/// \returns The string associated of the keyword.
const char* to_string(cpp_template_keyword kw) noexcept;
/// A [cppast::cpp_entity]() modelling a C++ template type parameter.
class cpp_template_type_parameter final : public cpp_template_parameter
{
public:
static cpp_entity_kind kind() noexcept;
/// \returns A newly created and registered template type parameter.
/// \notes The `default_type` may be `nullptr` in which case the parameter has no default.
static std::unique_ptr<cpp_template_type_parameter> build(
const cpp_entity_index& idx, cpp_entity_id id, std::string name, cpp_template_keyword kw,
bool variadic, std::unique_ptr<cpp_type> default_type = nullptr);
/// \returns A [ts::optional_ref]() to the default type.
type_safe::optional_ref<const cpp_type> default_type() const noexcept
{
return type_safe::opt_cref(default_type_.get());
}
/// \returns The keyword used in the template parameter.
cpp_template_keyword keyword() const noexcept
{
return keyword_;
}
private:
cpp_template_type_parameter(std::string name, cpp_template_keyword kw, bool variadic,
std::unique_ptr<cpp_type> default_type)
: cpp_template_parameter(std::move(name), variadic), default_type_(std::move(default_type)),
keyword_(kw)
{}
cpp_entity_kind do_get_entity_kind() const noexcept override;
std::unique_ptr<cpp_type> default_type_;
cpp_template_keyword keyword_;
};
/// \exclude
namespace detail
{
struct cpp_template_parameter_ref_predicate
{
bool operator()(const cpp_entity& e);
};
} // namespace detail
/// Reference to a [cppast::cpp_template_type_parameter]().
using cpp_template_type_parameter_ref
= basic_cpp_entity_ref<cpp_template_type_parameter,
detail::cpp_template_parameter_ref_predicate>;
/// A [cppast::cpp_type]() defined by a [cppast::cpp_template_type_parameter]().
class cpp_template_parameter_type final : public cpp_type
{
public:
/// \returns A newly created parameter type.
static std::unique_ptr<cpp_template_parameter_type> build(
cpp_template_type_parameter_ref parameter)
{
return std::unique_ptr<cpp_template_parameter_type>(
new cpp_template_parameter_type(std::move(parameter)));
}
/// \returns A reference to the [cppast::cpp_template_type_parameter]() this type refers to.
const cpp_template_type_parameter_ref& entity() const noexcept
{
return parameter_;
}
private:
cpp_template_parameter_type(cpp_template_type_parameter_ref parameter)
: parameter_(std::move(parameter))
{}
cpp_type_kind do_get_kind() const noexcept override
{
return cpp_type_kind::template_parameter_t;
}
cpp_template_type_parameter_ref parameter_;
};
/// A [cppast::cpp_entity]() modelling a C++ non-type template parameter.
class cpp_non_type_template_parameter final : public cpp_template_parameter,
public cpp_variable_base
{
public:
static cpp_entity_kind kind() noexcept;
/// \returns A newly created and registered non type template parameter.
/// \notes The `default_value` may be `nullptr` in which case the parameter has no default.
static std::unique_ptr<cpp_non_type_template_parameter> build(
const cpp_entity_index& idx, cpp_entity_id id, std::string name,
std::unique_ptr<cpp_type> type, bool is_variadic,
std::unique_ptr<cpp_expression> default_value = nullptr);
private:
cpp_non_type_template_parameter(std::string name, std::unique_ptr<cpp_type> type, bool variadic,
std::unique_ptr<cpp_expression> def)
: cpp_template_parameter(std::move(name), variadic),
cpp_variable_base(std::move(type), std::move(def))
{}
cpp_entity_kind do_get_entity_kind() const noexcept override;
};
/// \exclude
namespace detail
{
struct cpp_template_ref_predicate
{
bool operator()(const cpp_entity& e);
};
} // namespace detail
class cpp_template;
/// A reference to a [cppast::cpp_template]() or a [cppast::cpp_template_template_parameter]().
using cpp_template_ref = basic_cpp_entity_ref<cpp_entity, detail::cpp_template_ref_predicate>;
/// A [cppast::cpp_entity]() modelling a C++ template template parameter.
class cpp_template_template_parameter final : public cpp_template_parameter
{
public:
static cpp_entity_kind kind() noexcept;
/// Builds a [cppast::cpp_template_template_parameter]().
class builder
{ {
public: public:
/// \returns Whether or not the parameter is variadic. /// \effects Sets the name and whether it is variadic.
bool is_variadic() const noexcept builder(std::string name, bool variadic)
: parameter_(new cpp_template_template_parameter(std::move(name), variadic))
{}
/// \effects Sets the keyword,
/// default is [cpp_template_keyword::keyword_class]().
void keyword(cpp_template_keyword kw)
{ {
return variadic_; parameter_->keyword_ = kw;
} }
protected: /// \effects Adds a parameter to the template.
cpp_template_parameter(std::string name, bool variadic) void add_parameter(std::unique_ptr<cpp_template_parameter> param)
: cpp_entity(std::move(name)), variadic_(variadic)
{ {
parameter_->parameters_.push_back(*parameter_, std::move(param));
}
/// \effects Sets the default template.
void default_template(cpp_template_ref templ)
{
parameter_->default_ = std::move(templ);
}
/// \effects Registers the parameter in the [cppast::cpp_entity_index](),
/// using the given [cppast::cpp_entity_id]().
/// \returns The finished parameter.
std::unique_ptr<cpp_template_template_parameter> finish(const cpp_entity_index& idx,
cpp_entity_id id)
{
idx.register_definition(std::move(id), type_safe::ref(*parameter_));
return std::move(parameter_);
} }
private: private:
bool variadic_; std::unique_ptr<cpp_template_template_parameter> parameter_;
}; };
/// The kind of keyword used in a template parameter. /// \returns An iteratable object containing the template parameters of the template template
enum class cpp_template_keyword /// parameter.
detail::iteratable_intrusive_list<cpp_template_parameter> parameters() const noexcept
{ {
keyword_class, return type_safe::ref(parameters_);
keyword_typename }
};
/// \returns The string associated of the keyword. /// \returns The keyword used in the template parameter.
const char* to_string(cpp_template_keyword kw) noexcept; cpp_template_keyword keyword() const noexcept
/// A [cppast::cpp_entity]() modelling a C++ template type parameter.
class cpp_template_type_parameter final : public cpp_template_parameter
{ {
public: return keyword_;
static cpp_entity_kind kind() noexcept; }
/// \returns A newly created and registered template type parameter. /// \returns A [ts::optional]() that is the default template.
/// \notes The `default_type` may be `nullptr` in which case the parameter has no default. type_safe::optional<cpp_template_ref> default_template() const noexcept
static std::unique_ptr<cpp_template_type_parameter> build(
const cpp_entity_index& idx, cpp_entity_id id, std::string name,
cpp_template_keyword kw, bool variadic,
std::unique_ptr<cpp_type> default_type = nullptr);
/// \returns A [ts::optional_ref]() to the default type.
type_safe::optional_ref<const cpp_type> default_type() const noexcept
{
return type_safe::opt_cref(default_type_.get());
}
/// \returns The keyword used in the template parameter.
cpp_template_keyword keyword() const noexcept
{
return keyword_;
}
private:
cpp_template_type_parameter(std::string name, cpp_template_keyword kw, bool variadic,
std::unique_ptr<cpp_type> default_type)
: cpp_template_parameter(std::move(name), variadic),
default_type_(std::move(default_type)),
keyword_(kw)
{
}
cpp_entity_kind do_get_entity_kind() const noexcept override;
std::unique_ptr<cpp_type> default_type_;
cpp_template_keyword keyword_;
};
/// \exclude
namespace detail
{ {
struct cpp_template_parameter_ref_predicate return default_;
{ }
bool operator()(const cpp_entity& e);
};
} // namespace detail
/// Reference to a [cppast::cpp_template_type_parameter](). private:
using cpp_template_type_parameter_ref = cpp_template_template_parameter(std::string name, bool variadic)
basic_cpp_entity_ref<cpp_template_type_parameter, : cpp_template_parameter(std::move(name), variadic),
detail::cpp_template_parameter_ref_predicate>; keyword_(cpp_template_keyword::keyword_class)
{}
/// A [cppast::cpp_type]() defined by a [cppast::cpp_template_type_parameter](). cpp_entity_kind do_get_entity_kind() const noexcept override;
class cpp_template_parameter_type final : public cpp_type
detail::intrusive_list<cpp_template_parameter> parameters_;
type_safe::optional<cpp_template_ref> default_;
cpp_template_keyword keyword_;
};
/// An argument for a [cppast::cpp_template_parameter]().
///
/// It is based on a [ts::variant]() of [cppast::cpp_type]() (for
/// [cppast::cpp_template_type_parameter]()), [cppast::cpp_expression]() (for
/// [cppast::cpp_non_type_template_parameter]()) and [cppast::cpp_template_ref]() (for
/// [cppast::cpp_template_template_parameter]().
class cpp_template_argument
{
public:
/// \effects Initializes it passing a type as argument.
/// This corresponds to a [cppast::cpp_template_type_parameter]().
/// \notes This constructor only participates in overload resolution if `T` is dervied from
/// [cppast::cpp_type](). \param 1 \exclude
template <typename T,
typename std::enable_if<std::is_base_of<cpp_type, T>::value, int>::type = 0>
cpp_template_argument(std::unique_ptr<T> type)
: arg_(std::unique_ptr<cpp_type>(std::move(type)))
{}
/// \effects Initializes it passing an expression as argument.
/// This corresponds to a [cppast::cpp_non_type_template_parameter]().
/// \notes This constructor only participates in overload resolution if `T` is dervied from
/// [cppast::cpp_expression](). \param 1 \exclude
template <typename T,
typename = typename std::enable_if<std::is_base_of<cpp_expression, T>::value>::type>
cpp_template_argument(std::unique_ptr<T> expr)
: arg_(std::unique_ptr<cpp_expression>(std::move(expr)))
{}
/// \effects Initializes it passing a template as argument.
/// This corresponds to a [cppast::cpp_template_template_parameter]().
cpp_template_argument(cpp_template_ref templ) : arg_(std::move(templ)) {}
type_safe::optional_ref<const cpp_type> type() const noexcept
{ {
public: return arg_.optional_value(type_safe::variant_type<std::unique_ptr<cpp_type>>{})
/// \returns A newly created parameter type. .map([](const std::unique_ptr<cpp_type>& type) { return type_safe::ref(*type); });
static std::unique_ptr<cpp_template_parameter_type> build( }
cpp_template_type_parameter_ref parameter)
{
return std::unique_ptr<cpp_template_parameter_type>(
new cpp_template_parameter_type(std::move(parameter)));
}
/// \returns A reference to the [cppast::cpp_template_type_parameter]() this type refers to. type_safe::optional_ref<const cpp_expression> expression() const noexcept
const cpp_template_type_parameter_ref& entity() const noexcept
{
return parameter_;
}
private:
cpp_template_parameter_type(cpp_template_type_parameter_ref parameter)
: parameter_(std::move(parameter))
{
}
cpp_type_kind do_get_kind() const noexcept override
{
return cpp_type_kind::template_parameter_t;
}
cpp_template_type_parameter_ref parameter_;
};
/// A [cppast::cpp_entity]() modelling a C++ non-type template parameter.
class cpp_non_type_template_parameter final : public cpp_template_parameter,
public cpp_variable_base
{ {
public: return arg_.optional_value(type_safe::variant_type<std::unique_ptr<cpp_expression>>{})
static cpp_entity_kind kind() noexcept; .map([](const std::unique_ptr<cpp_expression>& expr) { return type_safe::ref(*expr); });
}
/// \returns A newly created and registered non type template parameter. type_safe::optional_ref<const cpp_template_ref> template_ref() const noexcept
/// \notes The `default_value` may be `nullptr` in which case the parameter has no default.
static std::unique_ptr<cpp_non_type_template_parameter> build(
const cpp_entity_index& idx, cpp_entity_id id, std::string name,
std::unique_ptr<cpp_type> type, bool is_variadic,
std::unique_ptr<cpp_expression> default_value = nullptr);
private:
cpp_non_type_template_parameter(std::string name, std::unique_ptr<cpp_type> type,
bool variadic, std::unique_ptr<cpp_expression> def)
: cpp_template_parameter(std::move(name), variadic),
cpp_variable_base(std::move(type), std::move(def))
{
}
cpp_entity_kind do_get_entity_kind() const noexcept override;
};
/// \exclude
namespace detail
{ {
struct cpp_template_ref_predicate return arg_.optional_value(type_safe::variant_type<cpp_template_ref>{});
{ }
bool operator()(const cpp_entity& e);
};
} // namespace detail
class cpp_template; private:
type_safe::variant<std::unique_ptr<cpp_type>, std::unique_ptr<cpp_expression>, cpp_template_ref>
/// A reference to a [cppast::cpp_template]() or a [cppast::cpp_template_template_parameter](). arg_;
using cpp_template_ref = basic_cpp_entity_ref<cpp_entity, detail::cpp_template_ref_predicate>; };
/// A [cppast::cpp_entity]() modelling a C++ template template parameter.
class cpp_template_template_parameter final : public cpp_template_parameter
{
public:
static cpp_entity_kind kind() noexcept;
/// Builds a [cppast::cpp_template_template_parameter]().
class builder
{
public:
/// \effects Sets the name and whether it is variadic.
builder(std::string name, bool variadic)
: parameter_(new cpp_template_template_parameter(std::move(name), variadic))
{
}
/// \effects Sets the keyword,
/// default is [cpp_template_keyword::keyword_class]().
void keyword(cpp_template_keyword kw)
{
parameter_->keyword_ = kw;
}
/// \effects Adds a parameter to the template.
void add_parameter(std::unique_ptr<cpp_template_parameter> param)
{
parameter_->parameters_.push_back(*parameter_, std::move(param));
}
/// \effects Sets the default template.
void default_template(cpp_template_ref templ)
{
parameter_->default_ = std::move(templ);
}
/// \effects Registers the parameter in the [cppast::cpp_entity_index](),
/// using the given [cppast::cpp_entity_id]().
/// \returns The finished parameter.
std::unique_ptr<cpp_template_template_parameter> finish(const cpp_entity_index& idx,
cpp_entity_id id)
{
idx.register_definition(std::move(id), type_safe::ref(*parameter_));
return std::move(parameter_);
}
private:
std::unique_ptr<cpp_template_template_parameter> parameter_;
};
/// \returns An iteratable object containing the template parameters of the template template parameter.
detail::iteratable_intrusive_list<cpp_template_parameter> parameters() const noexcept
{
return type_safe::ref(parameters_);
}
/// \returns The keyword used in the template parameter.
cpp_template_keyword keyword() const noexcept
{
return keyword_;
}
/// \returns A [ts::optional]() that is the default template.
type_safe::optional<cpp_template_ref> default_template() const noexcept
{
return default_;
}
private:
cpp_template_template_parameter(std::string name, bool variadic)
: cpp_template_parameter(std::move(name), variadic),
keyword_(cpp_template_keyword::keyword_class)
{
}
cpp_entity_kind do_get_entity_kind() const noexcept override;
detail::intrusive_list<cpp_template_parameter> parameters_;
type_safe::optional<cpp_template_ref> default_;
cpp_template_keyword keyword_;
};
/// An argument for a [cppast::cpp_template_parameter]().
///
/// It is based on a [ts::variant]() of [cppast::cpp_type]() (for [cppast::cpp_template_type_parameter]()),
/// [cppast::cpp_expression]() (for [cppast::cpp_non_type_template_parameter]()) and [cppast::cpp_template_ref]()
/// (for [cppast::cpp_template_template_parameter]().
class cpp_template_argument
{
public:
/// \effects Initializes it passing a type as argument.
/// This corresponds to a [cppast::cpp_template_type_parameter]().
/// \notes This constructor only participates in overload resolution if `T` is dervied from [cppast::cpp_type]().
/// \param 1
/// \exclude
template <typename T,
typename std::enable_if<std::is_base_of<cpp_type, T>::value, int>::type = 0>
cpp_template_argument(std::unique_ptr<T> type)
: arg_(std::unique_ptr<cpp_type>(std::move(type)))
{
}
/// \effects Initializes it passing an expression as argument.
/// This corresponds to a [cppast::cpp_non_type_template_parameter]().
/// \notes This constructor only participates in overload resolution if `T` is dervied from [cppast::cpp_expression]().
/// \param 1
/// \exclude
template <typename T, typename = typename std::enable_if<
std::is_base_of<cpp_expression, T>::value>::type>
cpp_template_argument(std::unique_ptr<T> expr)
: arg_(std::unique_ptr<cpp_expression>(std::move(expr)))
{
}
/// \effects Initializes it passing a template as argument.
/// This corresponds to a [cppast::cpp_template_template_parameter]().
cpp_template_argument(cpp_template_ref templ) : arg_(std::move(templ)) {}
type_safe::optional_ref<const cpp_type> type() const noexcept
{
return arg_.optional_value(type_safe::variant_type<std::unique_ptr<cpp_type>>{})
.map([](const std::unique_ptr<cpp_type>& type) { return type_safe::ref(*type); });
}
type_safe::optional_ref<const cpp_expression> expression() const noexcept
{
return arg_.optional_value(type_safe::variant_type<std::unique_ptr<cpp_expression>>{})
.map([](const std::unique_ptr<cpp_expression>& expr) {
return type_safe::ref(*expr);
});
}
type_safe::optional_ref<const cpp_template_ref> template_ref() const noexcept
{
return arg_.optional_value(type_safe::variant_type<cpp_template_ref>{});
}
private:
type_safe::variant<std::unique_ptr<cpp_type>, std::unique_ptr<cpp_expression>,
cpp_template_ref>
arg_;
};
} // namespace cppast } // namespace cppast
#endif // CPPAST_CPP_TEMPLATE_PARAMETER_HPP_INCLUDED #endif // CPPAST_CPP_TEMPLATE_PARAMETER_HPP_INCLUDED

View file

@ -12,126 +12,124 @@
namespace cppast namespace cppast
{ {
/// The kinds of C++ tokens. /// The kinds of C++ tokens.
enum class cpp_token_kind enum class cpp_token_kind
{
identifier, //< Any identifier.
keyword, //< Any keyword.
int_literal, //< An integer literal.
float_literal, //< A floating point literal.
char_literal, //< A character literal.
string_literal, //< A string literal.
punctuation //< Any other punctuation.
};
/// A C++ token.
struct cpp_token
{
std::string spelling;
cpp_token_kind kind;
cpp_token(cpp_token_kind kind, std::string spelling) : spelling(std::move(spelling)), kind(kind)
{}
friend bool operator==(const cpp_token& lhs, const cpp_token& rhs) noexcept
{ {
identifier, //< Any identifier. return lhs.spelling == rhs.spelling;
keyword, //< Any keyword. }
int_literal, //< An integer literal.
float_literal, //< A floating point literal.
char_literal, //< A character literal.
string_literal, //< A string literal.
punctuation //< Any other punctuation.
};
/// A C++ token. friend bool operator!=(const cpp_token& lhs, const cpp_token& rhs) noexcept
struct cpp_token
{ {
std::string spelling; return !(rhs == lhs);
cpp_token_kind kind; }
};
cpp_token(cpp_token_kind kind, std::string spelling) /// A combination of multiple C++ tokens.
: spelling(std::move(spelling)), kind(kind) class cpp_token_string
{ {
} public:
/// Builds a token string.
friend bool operator==(const cpp_token& lhs, const cpp_token& rhs) noexcept class builder
{
return lhs.spelling == rhs.spelling;
}
friend bool operator!=(const cpp_token& lhs, const cpp_token& rhs) noexcept
{
return !(rhs == lhs);
}
};
/// A combination of multiple C++ tokens.
class cpp_token_string
{ {
public: public:
/// Builds a token string. builder() = default;
class builder
/// \effects Adds a token.
void add_token(cpp_token tok)
{ {
public: tokens_.push_back(std::move(tok));
builder() = default;
/// \effects Adds a token.
void add_token(cpp_token tok)
{
tokens_.push_back(std::move(tok));
}
/// \effects Converts a trailing `>>` to `>` token.
void unmunch();
/// \returns The finished string.
cpp_token_string finish()
{
return cpp_token_string(std::move(tokens_));
}
private:
std::vector<cpp_token> tokens_;
};
/// Tokenizes a string.
/// \effects Splits the string into C++ tokens.
/// The string must contain valid tokens and must already be preprocessed (i.e. translation phase 6 is already done).
/// \returns The tokenized string.
static cpp_token_string tokenize(std::string str);
/// \effects Creates it from a sequence of tokens.
cpp_token_string(std::vector<cpp_token> tokens) : tokens_(std::move(tokens)) {}
/// \exclude target
using iterator = std::vector<cpp_token>::const_iterator;
/// \returns An iterator to the first token.
iterator begin() const noexcept
{
return tokens_.begin();
} }
/// \returns An iterator one past the last token. /// \effects Converts a trailing `>>` to `>` token.
iterator end() const noexcept void unmunch();
{
return tokens_.end();
}
/// \returns Whether or not the string is empty. /// \returns The finished string.
bool empty() const noexcept cpp_token_string finish()
{ {
return tokens_.empty(); return cpp_token_string(std::move(tokens_));
} }
/// \returns A reference to the first token.
const cpp_token& front() const noexcept
{
return tokens_.front();
}
/// \returns A reference to the last token.
const cpp_token& back() const noexcept
{
return tokens_.back();
}
/// \returns The string representation of the tokens, without any whitespace.
std::string as_string() const;
private: private:
std::vector<cpp_token> tokens_; std::vector<cpp_token> tokens_;
friend bool operator==(const cpp_token_string& lhs, const cpp_token_string& rhs);
}; };
bool operator==(const cpp_token_string& lhs, const cpp_token_string& rhs); /// Tokenizes a string.
/// \effects Splits the string into C++ tokens.
/// The string must contain valid tokens and must already be preprocessed (i.e. translation
/// phase 6 is already done). \returns The tokenized string.
static cpp_token_string tokenize(std::string str);
inline bool operator!=(const cpp_token_string& lhs, const cpp_token_string& rhs) /// \effects Creates it from a sequence of tokens.
cpp_token_string(std::vector<cpp_token> tokens) : tokens_(std::move(tokens)) {}
/// \exclude target
using iterator = std::vector<cpp_token>::const_iterator;
/// \returns An iterator to the first token.
iterator begin() const noexcept
{ {
return !(lhs == rhs); return tokens_.begin();
} }
/// \returns An iterator one past the last token.
iterator end() const noexcept
{
return tokens_.end();
}
/// \returns Whether or not the string is empty.
bool empty() const noexcept
{
return tokens_.empty();
}
/// \returns A reference to the first token.
const cpp_token& front() const noexcept
{
return tokens_.front();
}
/// \returns A reference to the last token.
const cpp_token& back() const noexcept
{
return tokens_.back();
}
/// \returns The string representation of the tokens, without any whitespace.
std::string as_string() const;
private:
std::vector<cpp_token> tokens_;
friend bool operator==(const cpp_token_string& lhs, const cpp_token_string& rhs);
};
bool operator==(const cpp_token_string& lhs, const cpp_token_string& rhs);
inline bool operator!=(const cpp_token_string& lhs, const cpp_token_string& rhs)
{
return !(lhs == rhs);
}
} // namespace cppast } // namespace cppast
#endif // CPPAST_CPP_TOKEN_HPP_INCLUDED #endif // CPPAST_CPP_TOKEN_HPP_INCLUDED

View file

@ -8,455 +8,452 @@
#include <atomic> #include <atomic>
#include <memory> #include <memory>
#include <cppast/detail/intrusive_list.hpp>
#include <cppast/cpp_entity_ref.hpp>
#include <cppast/code_generator.hpp> #include <cppast/code_generator.hpp>
#include <cppast/cpp_entity_ref.hpp>
#include <cppast/detail/intrusive_list.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_t,
user_defined_t,
auto_t,
decltype_t,
decltype_auto_t,
cv_qualified_t,
pointer_t,
reference_t,
array_t,
function_t,
member_function_t,
member_object_t,
template_parameter_t,
template_instantiation_t,
dependent_t,
unexposed_t,
};
/// Base class for all C++ types.
class cpp_type : detail::intrusive_list_node<cpp_type>
{
public:
cpp_type(const cpp_type&) = delete;
cpp_type& operator=(const cpp_type&) = delete;
virtual ~cpp_type() noexcept = default;
/// \returns The [cppast::cpp_type_kind]().
cpp_type_kind kind() const noexcept
{ {
builtin_t, return do_get_kind();
user_defined_t,
auto_t,
decltype_t,
decltype_auto_t,
cv_qualified_t,
pointer_t,
reference_t,
array_t,
function_t,
member_function_t,
member_object_t,
template_parameter_t,
template_instantiation_t,
dependent_t,
unexposed_t,
};
/// Base class for all C++ types.
class cpp_type : detail::intrusive_list_node<cpp_type>
{
public:
cpp_type(const cpp_type&) = delete;
cpp_type& operator=(const cpp_type&) = delete;
virtual ~cpp_type() noexcept = default;
/// \returns The [cppast::cpp_type_kind]().
cpp_type_kind kind() const noexcept
{
return do_get_kind();
}
/// \returns The specified user data.
void* user_data() const noexcept
{
return user_data_.load();
}
/// \effects Sets some kind of user data.
///
/// User data is just some kind of pointer, there are no requirements.
/// The class will do no lifetime management.
///
/// User data is useful if you need to store additional data for an entity without the need to maintain a registry.
void set_user_data(void* data) const noexcept
{
user_data_ = data;
}
protected:
cpp_type() noexcept : user_data_(nullptr) {}
private:
/// \returns The [cppast::cpp_type_kind]().
virtual cpp_type_kind do_get_kind() const noexcept = 0;
void on_insert(const cpp_type&) {}
mutable std::atomic<void*> user_data_;
template <typename T>
friend struct detail::intrusive_list_access;
friend detail::intrusive_list_node<cpp_type>;
};
/// An unexposed [cppast::cpp_type]().
///
/// This is one where no further information besides a name is available.
class cpp_unexposed_type final : public cpp_type
{
public:
/// \returns A newly created unexposed type.
static std::unique_ptr<cpp_unexposed_type> build(std::string name)
{
return std::unique_ptr<cpp_unexposed_type>(new cpp_unexposed_type(std::move(name)));
}
/// \returns The name of the type.
const std::string& name() const noexcept
{
return name_;
}
private:
cpp_unexposed_type(std::string name) : name_(std::move(name)) {}
cpp_type_kind do_get_kind() const noexcept override
{
return cpp_type_kind::unexposed_t;
}
std::string name_;
};
/// The C++ builtin types.
enum cpp_builtin_type_kind
{
cpp_void, //< `void`
cpp_bool, //< `bool`
cpp_uchar, //< `unsigned char`
cpp_ushort, //< `unsigned short`
cpp_uint, //< `unsigned int`
cpp_ulong, //< `unsigned long`
cpp_ulonglong, //< `unsigned long long`
cpp_uint128, //< `unsigned __int128`
cpp_schar, //< `signed char`
cpp_short, //< `short`
cpp_int, //< `int`
cpp_long, //< `long`
cpp_longlong, //< `long long`
cpp_int128, //< `__int128`
cpp_float, //< `float`
cpp_double, //< `double`
cpp_longdouble, //< `long double`
cpp_float128, //< `__float128`
cpp_char, //< `char`
cpp_wchar, //< `wchar_t`
cpp_char16, //< `char16_t`
cpp_char32, //< `char32_t`
cpp_nullptr, //< `decltype(nullptr)` aka `std::nullptr_t`
};
/// \returns The string representing the spelling of that type in the source code.
const char* to_string(cpp_builtin_type_kind kind) noexcept;
/// A builtin [cppast::cpp_type]().
///
/// This is one where there is no associated [cppast::cpp_entity]().
class cpp_builtin_type final : public cpp_type
{
public:
/// \returns A newly created builtin type.
static std::unique_ptr<cpp_builtin_type> build(cpp_builtin_type_kind kind)
{
return std::unique_ptr<cpp_builtin_type>(new cpp_builtin_type(kind));
}
/// \returns Which builtin type it is.
cpp_builtin_type_kind builtin_type_kind() const noexcept
{
return kind_;
}
private:
cpp_builtin_type(cpp_builtin_type_kind kind) : kind_(kind) {}
cpp_type_kind do_get_kind() const noexcept override
{
return cpp_type_kind::builtin_t;
}
cpp_builtin_type_kind kind_;
};
/// \exclude
namespace detail
{
struct cpp_type_ref_predicate
{
bool operator()(const cpp_entity& e);
};
} // namespace detail
/// Reference to a [cppast::cpp_entity]() representing a new type.
using cpp_type_ref = basic_cpp_entity_ref<cpp_entity, detail::cpp_type_ref_predicate>;
/// A user-defined [cppast::cpp_type]().
///
/// It has an associated [cppast::cpp_entity]().
class cpp_user_defined_type final : public cpp_type
{
public:
/// \returns A newly created user-defined type.
static std::unique_ptr<cpp_user_defined_type> build(cpp_type_ref entity)
{
return std::unique_ptr<cpp_user_defined_type>(
new cpp_user_defined_type(std::move(entity)));
}
/// \returns A [cppast::cpp_type_ref]() to the associated [cppast::cpp_entity]() that is the type.
const cpp_type_ref& entity() const noexcept
{
return entity_;
}
private:
cpp_user_defined_type(cpp_type_ref entity) : entity_(std::move(entity)) {}
cpp_type_kind do_get_kind() const noexcept override
{
return cpp_type_kind::user_defined_t;
}
cpp_type_ref entity_;
};
/// A [cppast::cpp_type]() that isn't given but deduced by `auto`.
class cpp_auto_type final : public cpp_type
{
public:
/// \returns A newly created `auto` type.
static std::unique_ptr<cpp_auto_type> build()
{
return std::unique_ptr<cpp_auto_type>(new cpp_auto_type);
}
private:
cpp_auto_type() = default;
cpp_type_kind do_get_kind() const noexcept override
{
return cpp_type_kind::auto_t;
}
};
class cpp_template_parameter_type;
class cpp_template_instantiation_type;
/// A [cppast::cpp_type]() that depends on another type.
class cpp_dependent_type final : public cpp_type
{
public:
/// \returns A newly created type dependent on a [cppast::cpp_template_parameter_type]().
static std::unique_ptr<cpp_dependent_type> build(
std::string name, std::unique_ptr<cpp_template_parameter_type> dependee);
/// \returns A newly created type dependent on a [cppast::cpp_template_instantiation_type]().
static std::unique_ptr<cpp_dependent_type> build(
std::string name, std::unique_ptr<cpp_template_instantiation_type> dependee);
/// \returns The name of the dependent type.
/// \notes It does not include a scope.
const std::string& name() const noexcept
{
return name_;
}
/// \returns A reference to the [cppast::cpp_type]() it depends one.
/// \notes This is either [cppast::cpp_template_parameter_type]() or [cppast:cpp_template_instantiation_type]().
const cpp_type& dependee() const noexcept
{
return *dependee_;
}
private:
cpp_dependent_type(std::string name, std::unique_ptr<cpp_type> dependee)
: name_(std::move(name)), dependee_(std::move(dependee))
{
}
cpp_type_kind do_get_kind() const noexcept override
{
return cpp_type_kind::dependent_t;
}
std::string name_;
std::unique_ptr<cpp_type> dependee_;
};
/// The kinds of C++ cv qualifiers.
enum cpp_cv
{
cpp_cv_none,
cpp_cv_const,
cpp_cv_volatile,
cpp_cv_const_volatile,
};
/// \returns `true` if the qualifier contains `const`.
inline bool is_const(cpp_cv cv) noexcept
{
return cv == cpp_cv_const || cv == cpp_cv_const_volatile;
} }
/// \returns `true` if the qualifier contains `volatile`. /// \returns The specified user data.
inline bool is_volatile(cpp_cv cv) noexcept void* user_data() const noexcept
{ {
return cv == cpp_cv_volatile || cv == cpp_cv_const_volatile; return user_data_.load();
} }
/// A [cppast::cpp_cv]() qualified [cppast::cpp_type](). /// \effects Sets some kind of user data.
class cpp_cv_qualified_type final : public cpp_type ///
/// User data is just some kind of pointer, there are no requirements.
/// The class will do no lifetime management.
///
/// User data is useful if you need to store additional data for an entity without the need to
/// maintain a registry.
void set_user_data(void* data) const noexcept
{ {
public: user_data_ = data;
/// \returns A newly created qualified type. }
/// \requires `cv` must not be [cppast::cpp_cv::cpp_cv_none]().
static std::unique_ptr<cpp_cv_qualified_type> build(std::unique_ptr<cpp_type> type,
cpp_cv cv)
{
DEBUG_ASSERT(cv != cpp_cv_none, detail::precondition_error_handler{});
return std::unique_ptr<cpp_cv_qualified_type>(
new cpp_cv_qualified_type(std::move(type), cv));
}
/// \returns A reference to the [cppast::cpp_type]() that is qualified. protected:
const cpp_type& type() const noexcept cpp_type() noexcept : user_data_(nullptr) {}
{
return *type_;
}
/// \returns The [cppast::cpp_cv]() qualifier. private:
cpp_cv cv_qualifier() const noexcept /// \returns The [cppast::cpp_type_kind]().
{ virtual cpp_type_kind do_get_kind() const noexcept = 0;
return cv_;
}
private: void on_insert(const cpp_type&) {}
cpp_cv_qualified_type(std::unique_ptr<cpp_type> type, cpp_cv cv)
: type_(std::move(type)), cv_(cv)
{
}
cpp_type_kind do_get_kind() const noexcept override mutable std::atomic<void*> user_data_;
{
return cpp_type_kind::cv_qualified_t;
}
std::unique_ptr<cpp_type> type_; template <typename T>
cpp_cv cv_; friend struct detail::intrusive_list_access;
friend detail::intrusive_list_node<cpp_type>;
};
/// An unexposed [cppast::cpp_type]().
///
/// This is one where no further information besides a name is available.
class cpp_unexposed_type final : public cpp_type
{
public:
/// \returns A newly created unexposed type.
static std::unique_ptr<cpp_unexposed_type> build(std::string name)
{
return std::unique_ptr<cpp_unexposed_type>(new cpp_unexposed_type(std::move(name)));
}
/// \returns The name of the type.
const std::string& name() const noexcept
{
return name_;
}
private:
cpp_unexposed_type(std::string name) : name_(std::move(name)) {}
cpp_type_kind do_get_kind() const noexcept override
{
return cpp_type_kind::unexposed_t;
}
std::string name_;
};
/// The C++ builtin types.
enum cpp_builtin_type_kind
{
cpp_void, //< `void`
cpp_bool, //< `bool`
cpp_uchar, //< `unsigned char`
cpp_ushort, //< `unsigned short`
cpp_uint, //< `unsigned int`
cpp_ulong, //< `unsigned long`
cpp_ulonglong, //< `unsigned long long`
cpp_uint128, //< `unsigned __int128`
cpp_schar, //< `signed char`
cpp_short, //< `short`
cpp_int, //< `int`
cpp_long, //< `long`
cpp_longlong, //< `long long`
cpp_int128, //< `__int128`
cpp_float, //< `float`
cpp_double, //< `double`
cpp_longdouble, //< `long double`
cpp_float128, //< `__float128`
cpp_char, //< `char`
cpp_wchar, //< `wchar_t`
cpp_char16, //< `char16_t`
cpp_char32, //< `char32_t`
cpp_nullptr, //< `decltype(nullptr)` aka `std::nullptr_t`
};
/// \returns The string representing the spelling of that type in the source code.
const char* to_string(cpp_builtin_type_kind kind) noexcept;
/// A builtin [cppast::cpp_type]().
///
/// This is one where there is no associated [cppast::cpp_entity]().
class cpp_builtin_type final : public cpp_type
{
public:
/// \returns A newly created builtin type.
static std::unique_ptr<cpp_builtin_type> build(cpp_builtin_type_kind kind)
{
return std::unique_ptr<cpp_builtin_type>(new cpp_builtin_type(kind));
}
/// \returns Which builtin type it is.
cpp_builtin_type_kind builtin_type_kind() const noexcept
{
return kind_;
}
private:
cpp_builtin_type(cpp_builtin_type_kind kind) : kind_(kind) {}
cpp_type_kind do_get_kind() const noexcept override
{
return cpp_type_kind::builtin_t;
}
cpp_builtin_type_kind kind_;
};
/// \exclude
namespace detail
{
struct cpp_type_ref_predicate
{
bool operator()(const cpp_entity& e);
}; };
} // namespace detail
/// \returns The type without top-level const/volatile qualifiers. /// Reference to a [cppast::cpp_entity]() representing a new type.
const cpp_type& remove_cv(const cpp_type& type) noexcept; using cpp_type_ref = basic_cpp_entity_ref<cpp_entity, detail::cpp_type_ref_predicate>;
/// \returns The type without top-level const qualifiers. /// A user-defined [cppast::cpp_type]().
const cpp_type& remove_const(const cpp_type& type) noexcept; ///
/// It has an associated [cppast::cpp_entity]().
/// \returns The type without top-level volatile qualifiers. class cpp_user_defined_type final : public cpp_type
const cpp_type& remove_volatile(const cpp_type& type) noexcept; {
public:
/// A pointer to a [cppast::cpp_type](). /// \returns A newly created user-defined type.
class cpp_pointer_type final : public cpp_type static std::unique_ptr<cpp_user_defined_type> build(cpp_type_ref entity)
{ {
public: return std::unique_ptr<cpp_user_defined_type>(new cpp_user_defined_type(std::move(entity)));
/// \returns A newly created pointer type. }
static std::unique_ptr<cpp_pointer_type> build(std::unique_ptr<cpp_type> pointee)
{
return std::unique_ptr<cpp_pointer_type>(new cpp_pointer_type(std::move(pointee)));
}
/// \returns A reference to the [cppast::cpp_type]() that is the pointee. /// \returns A [cppast::cpp_type_ref]() to the associated [cppast::cpp_entity]() that is the
const cpp_type& pointee() const noexcept /// type.
{ const cpp_type_ref& entity() const noexcept
return *pointee_;
}
private:
cpp_pointer_type(std::unique_ptr<cpp_type> pointee) : pointee_(std::move(pointee)) {}
cpp_type_kind do_get_kind() const noexcept override
{
return cpp_type_kind::pointer_t;
}
std::unique_ptr<cpp_type> pointee_;
};
/// The kinds of C++ references.
enum cpp_reference
{ {
cpp_ref_none, return entity_;
cpp_ref_lvalue, }
cpp_ref_rvalue,
};
/// A reference to a [cppast::cpp_type](). private:
class cpp_reference_type final : public cpp_type cpp_user_defined_type(cpp_type_ref entity) : entity_(std::move(entity)) {}
cpp_type_kind do_get_kind() const noexcept override
{ {
public: return cpp_type_kind::user_defined_t;
/// \returns A newly created qualified type. }
/// \requires `ref` must not be [cppast::cpp_reference::cpp_ref_none]().
static std::unique_ptr<cpp_reference_type> build(std::unique_ptr<cpp_type> type,
cpp_reference ref)
{
DEBUG_ASSERT(ref != cpp_ref_none, detail::precondition_error_handler{});
return std::unique_ptr<cpp_reference_type>(
new cpp_reference_type(std::move(type), ref));
}
/// \returns A reference to the [cppast::cpp_type]() that is referenced. cpp_type_ref entity_;
const cpp_type& referee() const noexcept };
{
return *referee_;
}
/// \returns The [cppast::cpp_reference]() type. /// A [cppast::cpp_type]() that isn't given but deduced by `auto`.
cpp_reference reference_kind() const noexcept class cpp_auto_type final : public cpp_type
{ {
return ref_; public:
} /// \returns A newly created `auto` type.
static std::unique_ptr<cpp_auto_type> build()
private:
cpp_reference_type(std::unique_ptr<cpp_type> referee, cpp_reference ref)
: referee_(std::move(referee)), ref_(ref)
{
}
cpp_type_kind do_get_kind() const noexcept override
{
return cpp_type_kind::reference_t;
}
std::unique_ptr<cpp_type> referee_;
cpp_reference ref_;
};
/// \returns The type as a string representation.
std::string to_string(const cpp_type& type);
/// \exclude
namespace detail
{ {
// whether or not it requires special treatment when printing it return std::unique_ptr<cpp_auto_type>(new cpp_auto_type);
// 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 private:
void write_type_prefix(code_generator::output& output, const cpp_type& type); cpp_auto_type() = default;
// write part of the type that comes after the name cpp_type_kind do_get_kind() const noexcept override
// for non-complex types, this does nothing {
void write_type_suffix(code_generator::output& output, const cpp_type& type); return cpp_type_kind::auto_t;
}
};
// write prefix, variadic, name, suffix class cpp_template_parameter_type;
void write_type(code_generator::output& output, const cpp_type& type, std::string name, class cpp_template_instantiation_type;
bool is_variadic = false);
} // namespace detail /// A [cppast::cpp_type]() that depends on another type.
class cpp_dependent_type final : public cpp_type
{
public:
/// \returns A newly created type dependent on a [cppast::cpp_template_parameter_type]().
static std::unique_ptr<cpp_dependent_type> build(
std::string name, std::unique_ptr<cpp_template_parameter_type> dependee);
/// \returns A newly created type dependent on a [cppast::cpp_template_instantiation_type]().
static std::unique_ptr<cpp_dependent_type> build(
std::string name, std::unique_ptr<cpp_template_instantiation_type> dependee);
/// \returns The name of the dependent type.
/// \notes It does not include a scope.
const std::string& name() const noexcept
{
return name_;
}
/// \returns A reference to the [cppast::cpp_type]() it depends one.
/// \notes This is either [cppast::cpp_template_parameter_type]() or
/// [cppast:cpp_template_instantiation_type]().
const cpp_type& dependee() const noexcept
{
return *dependee_;
}
private:
cpp_dependent_type(std::string name, std::unique_ptr<cpp_type> dependee)
: name_(std::move(name)), dependee_(std::move(dependee))
{}
cpp_type_kind do_get_kind() const noexcept override
{
return cpp_type_kind::dependent_t;
}
std::string name_;
std::unique_ptr<cpp_type> dependee_;
};
/// The kinds of C++ cv qualifiers.
enum cpp_cv
{
cpp_cv_none,
cpp_cv_const,
cpp_cv_volatile,
cpp_cv_const_volatile,
};
/// \returns `true` if the qualifier contains `const`.
inline bool is_const(cpp_cv cv) noexcept
{
return cv == cpp_cv_const || cv == cpp_cv_const_volatile;
}
/// \returns `true` if the qualifier contains `volatile`.
inline bool is_volatile(cpp_cv cv) noexcept
{
return cv == cpp_cv_volatile || cv == cpp_cv_const_volatile;
}
/// A [cppast::cpp_cv]() qualified [cppast::cpp_type]().
class cpp_cv_qualified_type final : public cpp_type
{
public:
/// \returns A newly created qualified type.
/// \requires `cv` must not be [cppast::cpp_cv::cpp_cv_none]().
static std::unique_ptr<cpp_cv_qualified_type> build(std::unique_ptr<cpp_type> type, cpp_cv cv)
{
DEBUG_ASSERT(cv != cpp_cv_none, detail::precondition_error_handler{});
return std::unique_ptr<cpp_cv_qualified_type>(
new cpp_cv_qualified_type(std::move(type), cv));
}
/// \returns A reference to the [cppast::cpp_type]() that is qualified.
const cpp_type& type() const noexcept
{
return *type_;
}
/// \returns The [cppast::cpp_cv]() qualifier.
cpp_cv cv_qualifier() const noexcept
{
return cv_;
}
private:
cpp_cv_qualified_type(std::unique_ptr<cpp_type> type, cpp_cv cv)
: type_(std::move(type)), cv_(cv)
{}
cpp_type_kind do_get_kind() const noexcept override
{
return cpp_type_kind::cv_qualified_t;
}
std::unique_ptr<cpp_type> type_;
cpp_cv cv_;
};
/// \returns The type without top-level const/volatile qualifiers.
const cpp_type& remove_cv(const cpp_type& type) noexcept;
/// \returns The type without top-level const qualifiers.
const cpp_type& remove_const(const cpp_type& type) noexcept;
/// \returns The type without top-level volatile qualifiers.
const cpp_type& remove_volatile(const cpp_type& type) noexcept;
/// A pointer to a [cppast::cpp_type]().
class cpp_pointer_type final : public cpp_type
{
public:
/// \returns A newly created pointer type.
static std::unique_ptr<cpp_pointer_type> build(std::unique_ptr<cpp_type> pointee)
{
return std::unique_ptr<cpp_pointer_type>(new cpp_pointer_type(std::move(pointee)));
}
/// \returns A reference to the [cppast::cpp_type]() that is the pointee.
const cpp_type& pointee() const noexcept
{
return *pointee_;
}
private:
cpp_pointer_type(std::unique_ptr<cpp_type> pointee) : pointee_(std::move(pointee)) {}
cpp_type_kind do_get_kind() const noexcept override
{
return cpp_type_kind::pointer_t;
}
std::unique_ptr<cpp_type> pointee_;
};
/// The kinds of C++ references.
enum cpp_reference
{
cpp_ref_none,
cpp_ref_lvalue,
cpp_ref_rvalue,
};
/// A reference to a [cppast::cpp_type]().
class cpp_reference_type final : public cpp_type
{
public:
/// \returns A newly created qualified type.
/// \requires `ref` must not be [cppast::cpp_reference::cpp_ref_none]().
static std::unique_ptr<cpp_reference_type> build(std::unique_ptr<cpp_type> type,
cpp_reference ref)
{
DEBUG_ASSERT(ref != cpp_ref_none, detail::precondition_error_handler{});
return std::unique_ptr<cpp_reference_type>(new cpp_reference_type(std::move(type), ref));
}
/// \returns A reference to the [cppast::cpp_type]() that is referenced.
const cpp_type& referee() const noexcept
{
return *referee_;
}
/// \returns The [cppast::cpp_reference]() type.
cpp_reference reference_kind() const noexcept
{
return ref_;
}
private:
cpp_reference_type(std::unique_ptr<cpp_type> referee, cpp_reference ref)
: referee_(std::move(referee)), ref_(ref)
{}
cpp_type_kind do_get_kind() const noexcept override
{
return cpp_type_kind::reference_t;
}
std::unique_ptr<cpp_type> referee_;
cpp_reference ref_;
};
/// \returns The type as a string representation.
std::string to_string(const cpp_type& type);
/// \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

View file

@ -10,39 +10,36 @@
namespace cppast namespace cppast
{ {
/// A [cppast::cpp_entity]() modelling a type alias/typedef. /// A [cppast::cpp_entity]() modelling a type alias/typedef.
/// \notes There is no distinction between `using` and `typedef` type aliases made in the AST. /// \notes There is no distinction between `using` and `typedef` type aliases made in the AST.
class cpp_type_alias final : public cpp_entity class cpp_type_alias final : public cpp_entity
{
public:
static cpp_entity_kind kind() noexcept;
/// \returns A newly created and registered type alias.
static std::unique_ptr<cpp_type_alias> build(const cpp_entity_index& idx, cpp_entity_id id,
std::string name, std::unique_ptr<cpp_type> type);
/// \returns A newly created type alias that isn't registered.
/// \notes This function is intendend for templated type aliases.
static std::unique_ptr<cpp_type_alias> build(std::string name, std::unique_ptr<cpp_type> type);
/// \returns A reference to the aliased [cppast::cpp_type]().
const cpp_type& underlying_type() const noexcept
{ {
public: return *type_;
static cpp_entity_kind kind() noexcept; }
/// \returns A newly created and registered type alias. private:
static std::unique_ptr<cpp_type_alias> build(const cpp_entity_index& idx, cpp_entity_id id, cpp_type_alias(std::string name, std::unique_ptr<cpp_type> type)
std::string name, : cpp_entity(std::move(name)), type_(std::move(type))
std::unique_ptr<cpp_type> type); {}
/// \returns A newly created type alias that isn't registered. cpp_entity_kind do_get_entity_kind() const noexcept override;
/// \notes This function is intendend for templated type aliases.
static std::unique_ptr<cpp_type_alias> build(std::string name,
std::unique_ptr<cpp_type> type);
/// \returns A reference to the aliased [cppast::cpp_type](). std::unique_ptr<cpp_type> type_;
const cpp_type& underlying_type() const noexcept };
{
return *type_;
}
private:
cpp_type_alias(std::string name, std::unique_ptr<cpp_type> type)
: cpp_entity(std::move(name)), type_(std::move(type))
{
}
cpp_entity_kind do_get_entity_kind() const noexcept override;
std::unique_ptr<cpp_type> type_;
};
} // namespace cppast } // namespace cppast
#endif // CPPAST_CPP_TYPE_ALIAS_HPP_INCLUDED #endif // CPPAST_CPP_TYPE_ALIAS_HPP_INCLUDED

View file

@ -12,61 +12,58 @@
namespace cppast namespace cppast
{ {
/// A [cppast::cpp_entity]() modelling a C++ variable. /// A [cppast::cpp_entity]() modelling a C++ variable.
/// \notes This is not a member variable, /// \notes This is not a member variable,
/// use [cppast::cpp_member_variable]() for that. /// use [cppast::cpp_member_variable]() for that.
/// But it can be `static` member variable. /// But it can be `static` member variable.
class cpp_variable final : public cpp_entity, class cpp_variable final : public cpp_entity,
public cpp_variable_base, public cpp_variable_base,
public cpp_forward_declarable public cpp_forward_declarable
{
public:
static cpp_entity_kind kind() noexcept;
/// \returns A newly created and registered variable.
/// \notes The default value may be `nullptr` indicating no default value.
static std::unique_ptr<cpp_variable> build(const cpp_entity_index& idx, cpp_entity_id id,
std::string name, std::unique_ptr<cpp_type> type,
std::unique_ptr<cpp_expression> def,
cpp_storage_class_specifiers spec,
bool is_constexpr);
/// \returns A newly created variable that is a declaration.
/// A declaration will not be registered and it does not have the default value.
static std::unique_ptr<cpp_variable> build_declaration(cpp_entity_id definition_id,
std::string name,
std::unique_ptr<cpp_type> type,
cpp_storage_class_specifiers spec,
bool is_constexpr);
/// \returns The [cppast::cpp_storage_specifiers]() on that variable.
cpp_storage_class_specifiers storage_class() const noexcept
{ {
public: return storage_;
static cpp_entity_kind kind() noexcept; }
/// \returns A newly created and registered variable. /// \returns Whether the variable is marked `constexpr`.
/// \notes The default value may be `nullptr` indicating no default value. bool is_constexpr() const noexcept
static std::unique_ptr<cpp_variable> build(const cpp_entity_index& idx, cpp_entity_id id, {
std::string name, std::unique_ptr<cpp_type> type, return is_constexpr_;
std::unique_ptr<cpp_expression> def, }
cpp_storage_class_specifiers spec,
bool is_constexpr);
/// \returns A newly created variable that is a declaration. private:
/// A declaration will not be registered and it does not have the default value. cpp_variable(std::string name, std::unique_ptr<cpp_type> type,
static std::unique_ptr<cpp_variable> build_declaration(cpp_entity_id definition_id, std::unique_ptr<cpp_expression> def, cpp_storage_class_specifiers spec,
std::string name, bool is_constexpr)
std::unique_ptr<cpp_type> type, : cpp_entity(std::move(name)), cpp_variable_base(std::move(type), std::move(def)),
cpp_storage_class_specifiers spec, storage_(spec), is_constexpr_(is_constexpr)
bool is_constexpr); {}
/// \returns The [cppast::cpp_storage_specifiers]() on that variable. cpp_entity_kind do_get_entity_kind() const noexcept override;
cpp_storage_class_specifiers storage_class() const noexcept
{
return storage_;
}
/// \returns Whether the variable is marked `constexpr`. cpp_storage_class_specifiers storage_;
bool is_constexpr() const noexcept bool is_constexpr_;
{ };
return is_constexpr_;
}
private:
cpp_variable(std::string name, std::unique_ptr<cpp_type> type,
std::unique_ptr<cpp_expression> def, cpp_storage_class_specifiers spec,
bool is_constexpr)
: cpp_entity(std::move(name)),
cpp_variable_base(std::move(type), std::move(def)),
storage_(spec),
is_constexpr_(is_constexpr)
{
}
cpp_entity_kind do_get_entity_kind() const noexcept override;
cpp_storage_class_specifiers storage_;
bool is_constexpr_;
};
} // namespace cppast } // namespace cppast
#endif // CPPAST_CPP_VARIABLE_HPP_INCLUDED #endif // CPPAST_CPP_VARIABLE_HPP_INCLUDED

View file

@ -10,37 +10,36 @@
namespace cppast namespace cppast
{ {
/// Additional base class for all [cppast::cpp_entity]() modelling some kind of variable. /// Additional base class for all [cppast::cpp_entity]() modelling some kind of variable.
/// ///
/// Examples are [cppast::cpp_variable]() or [cppast::cpp_function_parameter](), /// Examples are [cppast::cpp_variable]() or [cppast::cpp_function_parameter](),
/// or anything that is name/type/default-value triple. /// or anything that is name/type/default-value triple.
class cpp_variable_base class cpp_variable_base
{
public:
/// \returns A reference to the [cppast::cpp_type]() of the variable.
const cpp_type& type() const noexcept
{ {
public: return *type_;
/// \returns A reference to the [cppast::cpp_type]() of the variable. }
const cpp_type& type() const noexcept
{
return *type_;
}
/// \returns A [ts::optional_ref]() to the [cppast::cpp_expression]() that is the default value. /// \returns A [ts::optional_ref]() to the [cppast::cpp_expression]() that is the default value.
type_safe::optional_ref<const cpp_expression> default_value() const noexcept type_safe::optional_ref<const cpp_expression> default_value() const noexcept
{ {
return type_safe::opt_ref(default_.get()); return type_safe::opt_ref(default_.get());
} }
protected: protected:
cpp_variable_base(std::unique_ptr<cpp_type> type, std::unique_ptr<cpp_expression> def) cpp_variable_base(std::unique_ptr<cpp_type> type, std::unique_ptr<cpp_expression> def)
: type_(std::move(type)), default_(std::move(def)) : type_(std::move(type)), default_(std::move(def))
{ {}
}
~cpp_variable_base() noexcept = default; ~cpp_variable_base() noexcept = default;
private: private:
std::unique_ptr<cpp_type> type_; std::unique_ptr<cpp_type> type_;
std::unique_ptr<cpp_expression> default_; std::unique_ptr<cpp_expression> default_;
}; };
} // namespace cppast } // namespace cppast
#endif // CPPAST_CPP_VARIABLE_BASE_HPP_INCLUDED #endif // CPPAST_CPP_VARIABLE_BASE_HPP_INCLUDED

View file

@ -10,35 +10,34 @@
namespace cppast namespace cppast
{ {
/// A [cppast::cpp_entity]() modelling a C++ alias template. /// A [cppast::cpp_entity]() modelling a C++ alias template.
class cpp_variable_template final : public cpp_template class cpp_variable_template final : public cpp_template
{
public:
static cpp_entity_kind kind() noexcept;
/// Builder for [cppast::cpp_variable_template]().
class builder : public basic_builder<cpp_variable_template, cpp_variable>
{ {
public: public:
static cpp_entity_kind kind() noexcept; using basic_builder::basic_builder;
/// Builder for [cppast::cpp_variable_template]().
class builder : public basic_builder<cpp_variable_template, cpp_variable>
{
public:
using basic_builder::basic_builder;
};
/// \returns A reference to the type variable that is being templated.
const cpp_variable& variable() const noexcept
{
return static_cast<const cpp_variable&>(*begin());
}
private:
cpp_variable_template(std::unique_ptr<cpp_variable> variable)
: cpp_template(std::unique_ptr<cpp_entity>(variable.release()))
{
}
cpp_entity_kind do_get_entity_kind() const noexcept override;
friend basic_builder<cpp_variable_template, cpp_variable>;
}; };
/// \returns A reference to the type variable that is being templated.
const cpp_variable& variable() const noexcept
{
return static_cast<const cpp_variable&>(*begin());
}
private:
cpp_variable_template(std::unique_ptr<cpp_variable> variable)
: cpp_template(std::unique_ptr<cpp_entity>(variable.release()))
{}
cpp_entity_kind do_get_entity_kind() const noexcept override;
friend basic_builder<cpp_variable_template, cpp_variable>;
};
} // namespace cppast } // namespace cppast
#endif // CPPAST_CPP_VARIABLE_TEMPLATE_HPP_INCLUDED #endif // CPPAST_CPP_VARIABLE_TEMPLATE_HPP_INCLUDED

View file

@ -8,31 +8,29 @@
#include <debug_assert.hpp> #include <debug_assert.hpp>
#ifdef CPPAST_ENABLE_ASSERTIONS #ifdef CPPAST_ENABLE_ASSERTIONS
#define CPPAST_ASSERTION_LEVEL 1 # define CPPAST_ASSERTION_LEVEL 1
#else #else
#define CPPAST_ASSERTION_LEVEL 0 # define CPPAST_ASSERTION_LEVEL 0
#endif #endif
#ifdef CPPAST_ENABLE_PRECONDITION_CHECKS #ifdef CPPAST_ENABLE_PRECONDITION_CHECKS
#define CPPAST_PRECONDITION_LEVEL 1 # define CPPAST_PRECONDITION_LEVEL 1
#else #else
#define CPPAST_PRECONDITION_LEVEL 0 # define CPPAST_PRECONDITION_LEVEL 0
#endif #endif
namespace cppast namespace cppast
{ {
namespace detail namespace detail
{ {
struct assert_handler : debug_assert::set_level<CPPAST_ASSERTION_LEVEL>, struct assert_handler : debug_assert::set_level<CPPAST_ASSERTION_LEVEL>,
debug_assert::default_handler debug_assert::default_handler
{ {};
};
struct precondition_error_handler : debug_assert::set_level<CPPAST_PRECONDITION_LEVEL>, struct precondition_error_handler : debug_assert::set_level<CPPAST_PRECONDITION_LEVEL>,
debug_assert::default_handler debug_assert::default_handler
{ {};
}; } // namespace detail
} } // namespace cppast
} // namespace cppast::detail
#endif // CPPAST_ASSERT_HPP_INCLUDED #endif // CPPAST_ASSERT_HPP_INCLUDED

View file

@ -5,8 +5,8 @@
#ifndef CPPAST_INTRUSIVE_LIST_HPP_INCLUDED #ifndef CPPAST_INTRUSIVE_LIST_HPP_INCLUDED
#define CPPAST_INTRUSIVE_LIST_HPP_INCLUDED #define CPPAST_INTRUSIVE_LIST_HPP_INCLUDED
#include <memory>
#include <iterator> #include <iterator>
#include <memory>
#include <type_safe/optional_ref.hpp> #include <type_safe/optional_ref.hpp>
@ -14,228 +14,226 @@
namespace cppast namespace cppast
{ {
class cpp_file; class cpp_file;
namespace detail namespace detail
{
template <typename T>
class intrusive_list_node
{ {
template <typename T> std::unique_ptr<T> next_;
class intrusive_list_node
void do_on_insert(const T& parent) noexcept
{ {
std::unique_ptr<T> next_; static_cast<T&>(*this).on_insert(parent);
}
void do_on_insert(const T& parent) noexcept template <typename U>
{ friend struct intrusive_list_access;
static_cast<T&>(*this).on_insert(parent); };
}
template <typename U> template <typename T>
friend struct intrusive_list_access; struct intrusive_list_access
}; {
template <typename U>
template <typename T> static T* get_next(const U& obj)
struct intrusive_list_access
{ {
template <typename U> static_assert(std::is_base_of<U, T>::value, "must be a base");
static T* get_next(const U& obj) return static_cast<T*>(obj.next_.get());
{ }
static_assert(std::is_base_of<U, T>::value, "must be a base");
return static_cast<T*>(obj.next_.get());
}
template <typename U> template <typename U>
static T* set_next(U& obj, std::unique_ptr<T> node) static T* set_next(U& obj, std::unique_ptr<T> node)
{
static_assert(std::is_base_of<U, T>::value, "must be a base");
obj.next_ = std::move(node);
return static_cast<T*>(obj.next_.get());
}
template <typename U, typename V>
static void on_insert(U& obj, const V& parent)
{
obj.do_on_insert(parent);
}
};
template <typename T>
class intrusive_list_iterator
{ {
public: static_assert(std::is_base_of<U, T>::value, "must be a base");
using value_type = T; obj.next_ = std::move(node);
using reference = T&; return static_cast<T*>(obj.next_.get());
using pointer = T*; }
using difference_type = std::ptrdiff_t;
using iterator_category = std::forward_iterator_tag;
intrusive_list_iterator() noexcept : cur_(nullptr) {} template <typename U, typename V>
static void on_insert(U& obj, const V& parent)
reference operator*() const noexcept
{
return *cur_;
}
pointer operator->() const noexcept
{
return cur_;
}
intrusive_list_iterator& operator++() noexcept
{
cur_ = intrusive_list_access<T>::get_next(*cur_);
return *this;
}
intrusive_list_iterator operator++(int)noexcept
{
auto tmp = *this;
++(*this);
return tmp;
}
friend bool operator==(const intrusive_list_iterator& a,
const intrusive_list_iterator& b) noexcept
{
return a.cur_ == b.cur_;
}
friend bool operator!=(const intrusive_list_iterator& a,
const intrusive_list_iterator& b) noexcept
{
return !(a == b);
}
private:
intrusive_list_iterator(T* ptr) : cur_(ptr) {}
T* cur_;
template <typename U>
friend class intrusive_list;
};
template <typename T>
class intrusive_list
{ {
public: obj.do_on_insert(parent);
intrusive_list() = default; }
};
//=== modifiers ===// template <typename T>
template <typename Dummy = T, typename = typename std::enable_if< class intrusive_list_iterator
std::is_same<Dummy, cpp_file>::value>::type> {
void push_back(std::unique_ptr<T> obj) noexcept public:
{ using value_type = T;
push_back_impl(std::move(obj)); using reference = T&;
} using pointer = T*;
using difference_type = std::ptrdiff_t;
using iterator_category = std::forward_iterator_tag;
template <typename U, typename = typename std::enable_if< intrusive_list_iterator() noexcept : cur_(nullptr) {}
!std::is_same<T, cpp_file>::value, U>::type>
void push_back(const U& parent, std::unique_ptr<T> obj) noexcept
{
push_back_impl(std::move(obj));
intrusive_list_access<T>::on_insert(last_.value(), parent);
}
//=== accesors ===// reference operator*() const noexcept
bool empty() const noexcept
{
return first_ == nullptr;
}
type_safe::optional_ref<T> front() noexcept
{
return type_safe::opt_ref(first_.get());
}
type_safe::optional_ref<const T> front() const noexcept
{
return type_safe::opt_cref(first_.get());
}
type_safe::optional_ref<T> back() noexcept
{
return last_;
}
type_safe::optional_ref<const T> back() const noexcept
{
return last_;
}
//=== iterators ===//
using iterator = intrusive_list_iterator<T>;
using const_iterator = intrusive_list_iterator<const T>;
iterator begin() noexcept
{
return iterator(first_.get());
}
iterator end() noexcept
{
return {};
}
const_iterator begin() const noexcept
{
return const_iterator(first_.get());
}
const_iterator end() const noexcept
{
return {};
}
private:
void push_back_impl(std::unique_ptr<T> obj)
{
DEBUG_ASSERT(obj != nullptr, detail::assert_handler{});
if (last_)
{
auto ptr = intrusive_list_access<T>::set_next(last_.value(), std::move(obj));
last_ = type_safe::ref(*ptr);
}
else
{
first_ = std::move(obj);
last_ = type_safe::opt_ref(first_.get());
}
}
std::unique_ptr<T> first_;
type_safe::optional_ref<T> last_;
};
template <typename T>
class iteratable_intrusive_list
{ {
public: return *cur_;
iteratable_intrusive_list(type_safe::object_ref<const intrusive_list<T>> list) }
: list_(list)
pointer operator->() const noexcept
{
return cur_;
}
intrusive_list_iterator& operator++() noexcept
{
cur_ = intrusive_list_access<T>::get_next(*cur_);
return *this;
}
intrusive_list_iterator operator++(int) noexcept
{
auto tmp = *this;
++(*this);
return tmp;
}
friend bool operator==(const intrusive_list_iterator& a,
const intrusive_list_iterator& b) noexcept
{
return a.cur_ == b.cur_;
}
friend bool operator!=(const intrusive_list_iterator& a,
const intrusive_list_iterator& b) noexcept
{
return !(a == b);
}
private:
intrusive_list_iterator(T* ptr) : cur_(ptr) {}
T* cur_;
template <typename U>
friend class intrusive_list;
};
template <typename T>
class intrusive_list
{
public:
intrusive_list() = default;
//=== modifiers ===//
template <typename Dummy = T,
typename = typename std::enable_if<std::is_same<Dummy, cpp_file>::value>::type>
void push_back(std::unique_ptr<T> obj) noexcept
{
push_back_impl(std::move(obj));
}
template <typename U,
typename = typename std::enable_if<!std::is_same<T, cpp_file>::value, U>::type>
void push_back(const U& parent, std::unique_ptr<T> obj) noexcept
{
push_back_impl(std::move(obj));
intrusive_list_access<T>::on_insert(last_.value(), parent);
}
//=== accesors ===//
bool empty() const noexcept
{
return first_ == nullptr;
}
type_safe::optional_ref<T> front() noexcept
{
return type_safe::opt_ref(first_.get());
}
type_safe::optional_ref<const T> front() const noexcept
{
return type_safe::opt_cref(first_.get());
}
type_safe::optional_ref<T> back() noexcept
{
return last_;
}
type_safe::optional_ref<const T> back() const noexcept
{
return last_;
}
//=== iterators ===//
using iterator = intrusive_list_iterator<T>;
using const_iterator = intrusive_list_iterator<const T>;
iterator begin() noexcept
{
return iterator(first_.get());
}
iterator end() noexcept
{
return {};
}
const_iterator begin() const noexcept
{
return const_iterator(first_.get());
}
const_iterator end() const noexcept
{
return {};
}
private:
void push_back_impl(std::unique_ptr<T> obj)
{
DEBUG_ASSERT(obj != nullptr, detail::assert_handler{});
if (last_)
{ {
auto ptr = intrusive_list_access<T>::set_next(last_.value(), std::move(obj));
last_ = type_safe::ref(*ptr);
} }
else
bool empty() const noexcept
{ {
return list_->empty(); first_ = std::move(obj);
last_ = type_safe::opt_ref(first_.get());
} }
}
using iterator = typename intrusive_list<T>::const_iterator; std::unique_ptr<T> first_;
type_safe::optional_ref<T> last_;
};
iterator begin() const noexcept template <typename T>
{ class iteratable_intrusive_list
return list_->begin(); {
} public:
iteratable_intrusive_list(type_safe::object_ref<const intrusive_list<T>> list) : list_(list)
{}
iterator end() const noexcept bool empty() const noexcept
{ {
return list_->end(); return list_->empty();
} }
private: using iterator = typename intrusive_list<T>::const_iterator;
type_safe::object_ref<const intrusive_list<T>> list_;
}; iterator begin() const noexcept
} {
} // namespace cppast::detail return list_->begin();
}
iterator end() const noexcept
{
return list_->end();
}
private:
type_safe::object_ref<const intrusive_list<T>> list_;
};
} // namespace detail
} // namespace cppast
#endif // CPPAST_INTRUSIVE_LIST_HPP_INCLUDED #endif // CPPAST_INTRUSIVE_LIST_HPP_INCLUDED

View file

@ -12,137 +12,138 @@
namespace cppast namespace cppast
{ {
/// Describes a physical source location attached to a [cppast::diagnostic](). /// Describes a physical source location attached to a [cppast::diagnostic]().
/// \notes All information might be unavailable. /// \notes All information might be unavailable.
struct source_location struct source_location
{
type_safe::optional<std::string> entity;
type_safe::optional<std::string> file;
type_safe::optional<unsigned> line, column;
/// \returns A source location where all information is available.
static source_location make(std::string entity, std::string file, unsigned line,
unsigned column)
{ {
type_safe::optional<std::string> entity; return {std::move(entity), std::move(file), line, column};
type_safe::optional<std::string> file; }
type_safe::optional<unsigned> line, column;
/// \returns A source location where all information is available. /// \returns A source location where only file information is available.
static source_location make(std::string entity, std::string file, unsigned line, static source_location make_file(std::string file,
unsigned column) type_safe::optional<unsigned> line = type_safe::nullopt,
{ type_safe::optional<unsigned> column = type_safe::nullopt)
return {std::move(entity), std::move(file), line, column}; {
} return {type_safe::nullopt, std::move(file), line, column};
}
/// \returns A source location where only file information is available. /// \returns A source location where only the entity name is available.
static source_location make_file(std::string file, static source_location make_entity(std::string entity)
type_safe::optional<unsigned> line = type_safe::nullopt, {
type_safe::optional<unsigned> column = type_safe::nullopt) return {std::move(entity), type_safe::nullopt, type_safe::nullopt, type_safe::nullopt};
{ }
return {type_safe::nullopt, std::move(file), line, column};
}
/// \returns A source location where only the entity name is available. /// \returns A source location where no information is avilable.
static source_location make_entity(std::string entity) static source_location make_unknown()
{ {
return {std::move(entity), type_safe::nullopt, type_safe::nullopt, type_safe::nullopt}; return {type_safe::nullopt, type_safe::nullopt, type_safe::nullopt, type_safe::nullopt};
} }
/// \returns A source location where no information is avilable. /// \returns A source location where entity and file name is available.
static source_location make_unknown() static source_location make_entity(std::string entity, std::string file)
{ {
return {type_safe::nullopt, type_safe::nullopt, type_safe::nullopt, type_safe::nullopt}; return {std::move(entity), std::move(file), type_safe::nullopt, type_safe::nullopt};
} }
/// \returns A source location where entity and file name is available. /// \returns A possible string representation of the source location.
static source_location make_entity(std::string entity, std::string file) /// \notes It will include a separator, but no trailing whitespace.
std::string to_string() const
{
std::string result;
if (file)
{ {
return {std::move(entity), std::move(file), type_safe::nullopt, type_safe::nullopt}; result += file.value() + ":";
}
/// \returns A possible string representation of the source location. if (line)
/// \notes It will include a separator, but no trailing whitespace.
std::string to_string() const
{
std::string result;
if (file)
{ {
result += file.value() + ":"; result += std::to_string(line.value());
if (line) if (column)
{ result += "," + std::to_string(column.value());
result += std::to_string(line.value());
if (column) result += ":";
result += "," + std::to_string(column.value());
result += ":";
}
if (entity)
result += " (" + entity.value() + "):";
} }
else if (entity && !entity.value().empty())
result += entity.value() + ":";
return result; if (entity)
result += " (" + entity.value() + "):";
} }
}; else if (entity && !entity.value().empty())
result += entity.value() + ":";
/// The severity of a [cppast::diagnostic](). return result;
enum class severity }
};
/// The severity of a [cppast::diagnostic]().
enum class severity
{
debug, //< A debug diagnostic that is just for debugging purposes.
info, //< An informational message.
warning, //< A warning that doesn't impact AST generation.
error, //< A non-critical error that does impact AST generation but not critically.
critical, //< A critical error where AST generation isn't possible.
/// \notes This will usually result in an exception being thrown after the diagnostic has been
/// displayed.
};
/// \returns A human-readable string describing the severity.
inline const char* to_string(severity s) noexcept
{
switch (s)
{ {
debug, //< A debug diagnostic that is just for debugging purposes. case severity::debug:
info, //< An informational message. return "debug";
warning, //< A warning that doesn't impact AST generation. case severity::info:
error, //< A non-critical error that does impact AST generation but not critically. return "info";
critical, //< A critical error where AST generation isn't possible. case severity::warning:
/// \notes This will usually result in an exception being thrown after the diagnostic has been displayed. return "warning";
}; case severity::error:
return "error";
/// \returns A human-readable string describing the severity. case severity::critical:
inline const char* to_string(severity s) noexcept return "critical";
{
switch (s)
{
case severity::debug:
return "debug";
case severity::info:
return "info";
case severity::warning:
return "warning";
case severity::error:
return "error";
case severity::critical:
return "critical";
}
return "programmer error";
} }
/// A diagnostic. return "programmer error";
/// }
/// It represents an error message from a [cppast::parser]().
struct diagnostic
{
std::string message;
source_location location;
cppast::severity severity;
};
namespace detail /// A diagnostic.
{ ///
template <typename... Args> /// It represents an error message from a [cppast::parser]().
std::string format(Args&&... args) struct diagnostic
{ {
std::ostringstream stream; std::string message;
int dummy[] = {(stream << std::forward<Args>(args), 0)...}; source_location location;
(void)dummy; cppast::severity severity;
return stream.str(); };
}
} // namespace detail
/// Creates a diagnostic. namespace detail
/// \returns A diagnostic with the specified severity and location. {
/// The message is created by streaming each argument in order to a [std::ostringstream]().
template <typename... Args> template <typename... Args>
diagnostic format_diagnostic(severity sev, source_location loc, Args&&... args) std::string format(Args&&... args)
{ {
return {detail::format(std::forward<Args>(args)...), std::move(loc), sev}; std::ostringstream stream;
int dummy[] = {(stream << std::forward<Args>(args), 0)...};
(void)dummy;
return stream.str();
} }
} // namespace detail
/// Creates a diagnostic.
/// \returns A diagnostic with the specified severity and location.
/// The message is created by streaming each argument in order to a [std::ostringstream]().
template <typename... Args>
diagnostic format_diagnostic(severity sev, source_location loc, Args&&... args)
{
return {detail::format(std::forward<Args>(args)...), std::move(loc), sev};
}
} // namespace cppast } // namespace cppast
#endif // CPPAST_DIAGNOSTIC_HPP_INCLUDED #endif // CPPAST_DIAGNOSTIC_HPP_INCLUDED

View file

@ -11,59 +11,60 @@
namespace cppast namespace cppast
{ {
/// Base class for a [cppast::diagnostic]() logger. /// Base class for a [cppast::diagnostic]() logger.
/// ///
/// Its task is controlling how diagnostic are being displayed. /// Its task is controlling how diagnostic are being displayed.
class diagnostic_logger class diagnostic_logger
{
public:
/// \effects Creates it either as verbose or not.
explicit diagnostic_logger(bool is_verbose = false) noexcept : verbose_(is_verbose) {}
diagnostic_logger(const diagnostic_logger&) = delete;
diagnostic_logger& operator=(const diagnostic_logger&) = delete;
virtual ~diagnostic_logger() noexcept = default;
/// \effects Logs the diagnostic by invoking the `do_log()` member function.
/// \returns Whether or not the diagnostic was logged.
/// \notes `source` points to a string literal that gives additional context to what generates
/// the message.
bool log(const char* source, const diagnostic& d) const;
/// \effects Sets whether or not the logger prints debugging diagnostics.
void set_verbose(bool value) noexcept
{ {
public: verbose_ = value;
/// \effects Creates it either as verbose or not. }
explicit diagnostic_logger(bool is_verbose = false) noexcept : verbose_(is_verbose) {}
diagnostic_logger(const diagnostic_logger&) = delete; /// \returns Whether or not the logger prints debugging diagnostics.
diagnostic_logger& operator=(const diagnostic_logger&) = delete; bool is_verbose() const noexcept
virtual ~diagnostic_logger() noexcept = default;
/// \effects Logs the diagnostic by invoking the `do_log()` member function.
/// \returns Whether or not the diagnostic was logged.
/// \notes `source` points to a string literal that gives additional context to what generates the message.
bool log(const char* source, const diagnostic& d) const;
/// \effects Sets whether or not the logger prints debugging diagnostics.
void set_verbose(bool value) noexcept
{
verbose_ = value;
}
/// \returns Whether or not the logger prints debugging diagnostics.
bool is_verbose() const noexcept
{
return verbose_;
}
private:
virtual bool do_log(const char* source, const diagnostic& d) const = 0;
bool verbose_;
};
/// \returns The default logger object.
type_safe::object_ref<const diagnostic_logger> default_logger() noexcept;
/// \returns The default verbose logger object.
type_safe::object_ref<const diagnostic_logger> default_verbose_logger() noexcept;
/// A [cppast::diagnostic_logger]() that logs to `stderr`.
///
/// It prints all diagnostics in an implementation-defined format.
class stderr_diagnostic_logger final : public diagnostic_logger
{ {
public: return verbose_;
using diagnostic_logger::diagnostic_logger; }
private: private:
bool do_log(const char* source, const diagnostic& d) const override; virtual bool do_log(const char* source, const diagnostic& d) const = 0;
};
bool verbose_;
};
/// \returns The default logger object.
type_safe::object_ref<const diagnostic_logger> default_logger() noexcept;
/// \returns The default verbose logger object.
type_safe::object_ref<const diagnostic_logger> default_verbose_logger() noexcept;
/// A [cppast::diagnostic_logger]() that logs to `stderr`.
///
/// It prints all diagnostics in an implementation-defined format.
class stderr_diagnostic_logger final : public diagnostic_logger
{
public:
using diagnostic_logger::diagnostic_logger;
private:
bool do_log(const char* source, const diagnostic& d) const override;
};
} // namespace cppast } // namespace cppast
#endif // CPPAST_DIAGNOSTIC_LOGGER_HPP_INCLUDED #endif // CPPAST_DIAGNOSTIC_LOGGER_HPP_INCLUDED

View file

@ -11,252 +11,254 @@
namespace cppast namespace cppast
{ {
class libclang_compile_config; class libclang_compile_config;
class libclang_compilation_database; class libclang_compilation_database;
namespace detail namespace detail
{
struct libclang_compile_config_access
{ {
struct libclang_compile_config_access static const std::string& clang_binary(const libclang_compile_config& config);
{
static const std::string& clang_binary(const libclang_compile_config& config);
static int clang_version(const libclang_compile_config& config); static int clang_version(const libclang_compile_config& config);
static const std::vector<std::string>& flags(const libclang_compile_config& config); static const std::vector<std::string>& flags(const libclang_compile_config& config);
static bool write_preprocessed(const libclang_compile_config& config); static bool write_preprocessed(const libclang_compile_config& config);
static bool fast_preprocessing(const libclang_compile_config& config); static bool fast_preprocessing(const libclang_compile_config& config);
static bool remove_comments_in_macro(const libclang_compile_config& config); static bool remove_comments_in_macro(const libclang_compile_config& config);
};
void for_each_file(const libclang_compilation_database& database, void* user_data,
void (*callback)(void*, std::string));
} // namespace detail
/// The exception thrown when a fatal parse error occurs.
class libclang_error final : public std::runtime_error
{
public:
/// \effects Creates it with a message.
libclang_error(std::string msg) : std::runtime_error(std::move(msg)) {}
}; };
/// A compilation database. void for_each_file(const libclang_compilation_database& database, void* user_data,
/// void (*callback)(void*, std::string));
/// This represents a `compile_commands.json` file, } // namespace detail
/// which stores all the commands needed to compile a set of files.
/// It can be generated by CMake using the `CMAKE_EXPORT_COMPILE_COMMANDS` option. /// The exception thrown when a fatal parse error occurs.
class libclang_compilation_database class libclang_error final : public std::runtime_error
{
public:
/// \effects Creates it with a message.
libclang_error(std::string msg) : std::runtime_error(std::move(msg)) {}
};
/// A compilation database.
///
/// This represents a `compile_commands.json` file,
/// which stores all the commands needed to compile a set of files.
/// It can be generated by CMake using the `CMAKE_EXPORT_COMPILE_COMMANDS` option.
class libclang_compilation_database
{
public:
/// \effects Creates it giving the directory where the `compile_commands.json` file is located.
/// \throws `libclang_error` if the database could not be loaded or found.
libclang_compilation_database(const std::string& build_directory);
libclang_compilation_database(libclang_compilation_database&& other)
: database_(other.database_)
{ {
public: other.database_ = nullptr;
/// \effects Creates it giving the directory where the `compile_commands.json` file is located.
/// \throws `libclang_error` if the database could not be loaded or found.
libclang_compilation_database(const std::string& build_directory);
libclang_compilation_database(libclang_compilation_database&& other)
: database_(other.database_)
{
other.database_ = nullptr;
}
~libclang_compilation_database();
libclang_compilation_database& operator=(libclang_compilation_database&& other)
{
libclang_compilation_database tmp(std::move(other));
std::swap(tmp.database_, database_);
return *this;
}
/// \returns Whether or not the database contains information about the given file.
/// \group has_config
bool has_config(const char* file_name) const;
/// \group has_config
bool has_config(const std::string& file_name) const
{
return has_config(file_name.c_str());
}
private:
using database = void*;
database database_;
friend libclang_compile_config;
friend void detail::for_each_file(const libclang_compilation_database& database,
void* user_data, void (*callback)(void*, std::string));
};
/// Compilation config for the [cppast::libclang_parser]().
class libclang_compile_config final : public compile_config
{
public:
/// Creates the default configuration.
///
/// \effects It will set the clang binary determined by the build system,
/// as well as the libclang system include directory determined by the build system.
/// It will also define `__cppast__` with the value `"libclang"` as well as `__cppast_major__` and `__cppast_minor__`.
libclang_compile_config();
/// Creates the configuration stored in the database.
///
/// \effects It will use the options found in the database for the specified file.
/// This does not necessarily need to match the file that is going to be parsed,
/// but it should.
/// It will also add the default configuration options.
/// \notes Header files are not included in the compilation database,
/// you need to pass in the file name of the corresponding source file,
/// if you want to parse one.
/// \notes It will only consider options you could also set by the other functions.
/// \notes The file key will include the specified directory in the JSON, if it is not a full path.
libclang_compile_config(const libclang_compilation_database& database,
const std::string& file);
libclang_compile_config(const libclang_compile_config& other) = default;
libclang_compile_config& operator=(const libclang_compile_config& other) = default;
/// \effects Sets the path to the location of the `clang++` binary and the version of that binary.
/// \notes It will be used for preprocessing.
void set_clang_binary(std::string binary, int major, int minor, int patch)
{
clang_binary_ = std::move(binary);
clang_version_ = major * 10000 + minor * 100 + patch;
}
/// \effects Sets whether or not the preprocessed file will be written out.
/// Default value is `false`.
void write_preprocessed(bool b) noexcept
{
write_preprocessed_ = b;
}
/// \effects Sets whether or not the fast preprocessor is enabled.
/// Default value is `false`.
/// \notes The fast preprocessor gets a list of all macros that are defined in the translation unit,
/// then preprocesses it without resolving includes but manually defining the list of macros to ensure correctness.
/// Later stages will use the includes again.
/// This hack breaks if you define the same macro multiple times in the file being parsed (headers don't matter)
/// or you rely on the order of macro directives.
/// \notes If this option is `true`, the full file name of include directives is not available,
/// just the name as written in the source code.
void fast_preprocessing(bool b) noexcept
{
fast_preprocessing_ = b;
}
/// \effects Sets whether or not documentation comments generated by macros are removed.
/// Default value is `false`.
/// \notes If this leads to an error due to preprocessing and comments, you have to enable it.
/// \notes If this is `true`, `clang` will be invoked with `-CC`, otherwise `-C`.
void remove_comments_in_macro(bool b) noexcept
{
remove_comments_in_macro_ = b;
}
private:
void do_set_flags(cpp_standard standard, compile_flags flags) override;
void do_add_include_dir(std::string path) override;
void do_add_macro_definition(std::string name, std::string definition) override;
void do_remove_macro_definition(std::string name) override;
const char* do_get_name() const noexcept override
{
return "libclang";
}
std::string clang_binary_;
int clang_version_;
bool write_preprocessed_ : 1;
bool fast_preprocessing_ : 1;
bool remove_comments_in_macro_ : 1;
friend detail::libclang_compile_config_access;
};
/// Finds a configuration for a given file.
///
/// \returns If the database contains a configuration for the given file, returns that configuration.
/// Otherwise removes the file extension of the file and tries the same procedure
/// for common C++ header and source file extensions.
/// \notes This function is intended to be used as the basis for a `get_config` function of [cppast::parse_files](standardese://cppast::parse_files_basic/).
type_safe::optional<libclang_compile_config> find_config_for(
const libclang_compilation_database& database, std::string file_name);
/// A parser that uses libclang.
class libclang_parser final : public parser
{
public:
using config = libclang_compile_config;
/// \effects Creates a parser using the default logger.
libclang_parser();
/// \effects Creates a parser that will log error messages using the specified logger.
explicit libclang_parser(type_safe::object_ref<const diagnostic_logger> logger);
~libclang_parser() noexcept override;
private:
std::unique_ptr<cpp_file> do_parse(const cpp_entity_index& idx, std::string path,
const compile_config& config) const override;
struct impl;
std::unique_ptr<impl> pimpl_;
};
/// Parses multiple files using a [cppast::libclang_parser]() and a compilation database.
///
/// \effects Invokes [cppast::parse_files](standardese://parse_files_basic/) passing it the parser and file names,
/// and a `get_config` function using [cppast::find_config_for]().
///
/// \throws [cppast::libclang_error]() if no configuration for a given file could be found in the database.
///
/// \requires `FileParser` must use the libclang parser.
/// i.e. `FileParser::parser` must be an alias of [cppast::libclang_parser]().
template <class FileParser, class Range>
void parse_files(FileParser& parser, Range&& file_names,
const libclang_compilation_database& database)
{
static_assert(std::is_same<typename FileParser::parser, libclang_parser>::value,
"must use the libclang parser");
parse_files(parser, std::forward<Range>(file_names), [&](const std::string& file) {
auto config = find_config_for(database, file);
if (!config)
throw libclang_error("unable to find configuration for file '" + file + "'");
return config.value();
});
} }
/// Parses the files specified in a compilation database using a [cppast::libclang_parser](). ~libclang_compilation_database();
///
/// \effects For each file specified in a compilation database,
/// uses the `FileParser` to parse the file with the configuration specified in the database.
///
/// \requires `FileParser` must have the same requirements as for [cppast::parse_files](standardese://parse_files_basic/).
/// It must also use the libclang parser,
/// i.e. `FileParser::parser` must be an alias of [cppast::libclang_parser]().
template <class FileParser>
void parse_database(FileParser& parser, const libclang_compilation_database& database)
{
static_assert(std::is_same<typename FileParser::parser, libclang_parser>::value,
"must use the libclang parser");
struct data_t
{
FileParser& parser;
const libclang_compilation_database& database;
} data{parser, database};
detail::for_each_file(database, &data, [](void* ptr, std::string file) {
auto& data = *static_cast<data_t*>(ptr);
libclang_compile_config config(data.database, file); libclang_compilation_database& operator=(libclang_compilation_database&& other)
data.parser.parse(std::move(file), std::move(config)); {
}); libclang_compilation_database tmp(std::move(other));
std::swap(tmp.database_, database_);
return *this;
} }
/// \returns Whether or not the database contains information about the given file.
/// \group has_config
bool has_config(const char* file_name) const;
/// \group has_config
bool has_config(const std::string& file_name) const
{
return has_config(file_name.c_str());
}
private:
using database = void*;
database database_;
friend libclang_compile_config;
friend void detail::for_each_file(const libclang_compilation_database& database,
void* user_data, void (*callback)(void*, std::string));
};
/// Compilation config for the [cppast::libclang_parser]().
class libclang_compile_config final : public compile_config
{
public:
/// Creates the default configuration.
///
/// \effects It will set the clang binary determined by the build system,
/// as well as the libclang system include directory determined by the build system.
/// It will also define `__cppast__` with the value `"libclang"` as well as `__cppast_major__`
/// and `__cppast_minor__`.
libclang_compile_config();
/// Creates the configuration stored in the database.
///
/// \effects It will use the options found in the database for the specified file.
/// This does not necessarily need to match the file that is going to be parsed,
/// but it should.
/// It will also add the default configuration options.
/// \notes Header files are not included in the compilation database,
/// you need to pass in the file name of the corresponding source file,
/// if you want to parse one.
/// \notes It will only consider options you could also set by the other functions.
/// \notes The file key will include the specified directory in the JSON, if it is not a full
/// path.
libclang_compile_config(const libclang_compilation_database& database, const std::string& file);
libclang_compile_config(const libclang_compile_config& other) = default;
libclang_compile_config& operator=(const libclang_compile_config& other) = default;
/// \effects Sets the path to the location of the `clang++` binary and the version of that
/// binary. \notes It will be used for preprocessing.
void set_clang_binary(std::string binary, int major, int minor, int patch)
{
clang_binary_ = std::move(binary);
clang_version_ = major * 10000 + minor * 100 + patch;
}
/// \effects Sets whether or not the preprocessed file will be written out.
/// Default value is `false`.
void write_preprocessed(bool b) noexcept
{
write_preprocessed_ = b;
}
/// \effects Sets whether or not the fast preprocessor is enabled.
/// Default value is `false`.
/// \notes The fast preprocessor gets a list of all macros that are defined in the translation
/// unit, then preprocesses it without resolving includes but manually defining the list of
/// macros to ensure correctness. Later stages will use the includes again. This hack breaks if
/// you define the same macro multiple times in the file being parsed (headers don't matter) or
/// you rely on the order of macro directives. \notes If this option is `true`, the full file
/// name of include directives is not available, just the name as written in the source code.
void fast_preprocessing(bool b) noexcept
{
fast_preprocessing_ = b;
}
/// \effects Sets whether or not documentation comments generated by macros are removed.
/// Default value is `false`.
/// \notes If this leads to an error due to preprocessing and comments, you have to enable it.
/// \notes If this is `true`, `clang` will be invoked with `-CC`, otherwise `-C`.
void remove_comments_in_macro(bool b) noexcept
{
remove_comments_in_macro_ = b;
}
private:
void do_set_flags(cpp_standard standard, compile_flags flags) override;
void do_add_include_dir(std::string path) override;
void do_add_macro_definition(std::string name, std::string definition) override;
void do_remove_macro_definition(std::string name) override;
const char* do_get_name() const noexcept override
{
return "libclang";
}
std::string clang_binary_;
int clang_version_;
bool write_preprocessed_ : 1;
bool fast_preprocessing_ : 1;
bool remove_comments_in_macro_ : 1;
friend detail::libclang_compile_config_access;
};
/// Finds a configuration for a given file.
///
/// \returns If the database contains a configuration for the given file, returns that
/// configuration. Otherwise removes the file extension of the file and tries the same procedure for
/// common C++ header and source file extensions. \notes This function is intended to be used as the
/// basis for a `get_config` function of
/// [cppast::parse_files](standardese://cppast::parse_files_basic/).
type_safe::optional<libclang_compile_config> find_config_for(
const libclang_compilation_database& database, std::string file_name);
/// A parser that uses libclang.
class libclang_parser final : public parser
{
public:
using config = libclang_compile_config;
/// \effects Creates a parser using the default logger.
libclang_parser();
/// \effects Creates a parser that will log error messages using the specified logger.
explicit libclang_parser(type_safe::object_ref<const diagnostic_logger> logger);
~libclang_parser() noexcept override;
private:
std::unique_ptr<cpp_file> do_parse(const cpp_entity_index& idx, std::string path,
const compile_config& config) const override;
struct impl;
std::unique_ptr<impl> pimpl_;
};
/// Parses multiple files using a [cppast::libclang_parser]() and a compilation database.
///
/// \effects Invokes [cppast::parse_files](standardese://parse_files_basic/) passing it the parser
/// and file names, and a `get_config` function using [cppast::find_config_for]().
///
/// \throws [cppast::libclang_error]() if no configuration for a given file could be found in the
/// database.
///
/// \requires `FileParser` must use the libclang parser.
/// i.e. `FileParser::parser` must be an alias of [cppast::libclang_parser]().
template <class FileParser, class Range>
void parse_files(FileParser& parser, Range&& file_names,
const libclang_compilation_database& database)
{
static_assert(std::is_same<typename FileParser::parser, libclang_parser>::value,
"must use the libclang parser");
parse_files(parser, std::forward<Range>(file_names), [&](const std::string& file) {
auto config = find_config_for(database, file);
if (!config)
throw libclang_error("unable to find configuration for file '" + file + "'");
return config.value();
});
}
/// Parses the files specified in a compilation database using a [cppast::libclang_parser]().
///
/// \effects For each file specified in a compilation database,
/// uses the `FileParser` to parse the file with the configuration specified in the database.
///
/// \requires `FileParser` must have the same requirements as for
/// [cppast::parse_files](standardese://parse_files_basic/). It must also use the libclang parser,
/// i.e. `FileParser::parser` must be an alias of [cppast::libclang_parser]().
template <class FileParser>
void parse_database(FileParser& parser, const libclang_compilation_database& database)
{
static_assert(std::is_same<typename FileParser::parser, libclang_parser>::value,
"must use the libclang parser");
struct data_t
{
FileParser& parser;
const libclang_compilation_database& database;
} data{parser, database};
detail::for_each_file(database, &data, [](void* ptr, std::string file) {
auto& data = *static_cast<data_t*>(ptr);
libclang_compile_config config(data.database, file);
data.parser.parse(std::move(file), std::move(config));
});
}
} // namespace cppast } // namespace cppast
#endif // CPPAST_LIBCLANG_PARSER_HPP_INCLUDED #endif // CPPAST_LIBCLANG_PARSER_HPP_INCLUDED

View file

@ -10,232 +10,223 @@
#include <cppast/compile_config.hpp> #include <cppast/compile_config.hpp>
#include <cppast/cpp_file.hpp> #include <cppast/cpp_file.hpp>
#include <cppast/cpp_preprocessor.hpp> #include <cppast/cpp_preprocessor.hpp>
#include <cppast/diagnostic_logger.hpp>
#include <cppast/diagnostic.hpp> #include <cppast/diagnostic.hpp>
#include <cppast/diagnostic_logger.hpp>
namespace cppast namespace cppast
{ {
class cpp_entity_index; class cpp_entity_index;
/// Base class for a parser. /// Base class for a parser.
/// ///
/// It reads a C++ source file and creates the matching [cppast::cpp_file](). /// It reads a C++ source file and creates the matching [cppast::cpp_file]().
/// Derived classes can implement how the file is parsed. /// Derived classes can implement how the file is parsed.
/// ///
/// \requires A derived class must provide an alias `config` which is the corresponding derived class of the [cppast::compile_config](). /// \requires A derived class must provide an alias `config` which is the corresponding derived
class parser /// class of the [cppast::compile_config]().
class parser
{
public:
parser(const parser&) = delete;
parser& operator=(const parser&) = delete;
virtual ~parser() noexcept = default;
/// \effects Parses the given file.
/// \returns The [cppast::cpp_file]() object describing it.
/// It can be `nullptr`, if there was an error or the specified file already registered in the
/// index. \requires The dynamic type of `config` must match the required config type. \notes
/// This function is thread safe.
std::unique_ptr<cpp_file> parse(const cpp_entity_index& idx, std::string path,
const compile_config& config) const
{ {
public: return do_parse(idx, std::move(path), config);
parser(const parser&) = delete;
parser& operator=(const parser&) = delete;
virtual ~parser() noexcept = default;
/// \effects Parses the given file.
/// \returns The [cppast::cpp_file]() object describing it.
/// It can be `nullptr`, if there was an error or the specified file already registered in the index.
/// \requires The dynamic type of `config` must match the required config type.
/// \notes This function is thread safe.
std::unique_ptr<cpp_file> parse(const cpp_entity_index& idx, std::string path,
const compile_config& config) const
{
return do_parse(idx, std::move(path), config);
}
/// \returns Whether or not an error occurred during parsing.
/// If that happens, the AST might be incomplete.
bool error() const noexcept
{
return error_;
}
/// \effects Resets the error state.
void reset_error() noexcept
{
error_ = false;
}
/// \returns A reference to the logger used.
const diagnostic_logger& logger() const noexcept
{
return *logger_;
}
protected:
/// \effects Creates it giving it a reference to the logger it uses.
explicit parser(type_safe::object_ref<const diagnostic_logger> logger)
: logger_(logger), error_(false)
{
}
/// \effects Sets the error state.
/// This must be called when an error or critical diagnostic is logged and the AST is incomplete.
void set_error() const noexcept
{
error_ = true;
}
private:
/// \effects Parses the given file.
/// \returns The [cppast::cpp_file]() object describing it.
/// \requires The function must be thread safe.
virtual std::unique_ptr<cpp_file> do_parse(const cpp_entity_index& idx, std::string path,
const compile_config& config) const = 0;
type_safe::object_ref<const diagnostic_logger> logger_;
mutable std::atomic<bool> error_;
};
/// A simple `FileParser` that parses all files synchronously.
///
/// More advanced parsers could use a thread pool, for example.
template <class Parser>
class simple_file_parser
{
static_assert(std::is_base_of<cppast::parser, Parser>::value,
"Parser must be derived from cppast::parser");
public:
using parser = Parser;
using config = typename Parser::config;
/// \effects Creates a file parser populating the given index
/// and using the parser created by forwarding the given arguments.
template <typename... Args>
explicit simple_file_parser(type_safe::object_ref<const cpp_entity_index> idx,
Args&&... args)
: parser_(std::forward<Args>(args)...), idx_(idx)
{
}
/// \effects Parses the given file using the given configuration.
/// \returns The parsed file or an empty optional, if a fatal error occurred.
type_safe::optional_ref<const cpp_file> parse(std::string path, const config& c)
{
parser_.logger().log("simple file parser",
diagnostic{"parsing file '" + path + "'", source_location(),
severity::info});
auto file = parser_.parse(*idx_, std::move(path), c);
auto ptr = file.get();
if (file)
files_.push_back(std::move(file));
return type_safe::opt_ref(ptr);
}
/// \returns The result of [cppast::parser::error]().
bool error() const noexcept
{
return parser_.error();
}
/// \effects Calls [cppast::parser::reset_error]().
void reset_error() noexcept
{
parser_.reset_error();
}
/// \returns The index that is being populated.
const cpp_entity_index& index() const noexcept
{
return *idx_;
}
/// \returns An iteratable object iterating over all the files that have been parsed so far.
/// \exclude return
detail::iteratable_intrusive_list<cpp_file> files() const noexcept
{
return type_safe::ref(files_);
}
private:
Parser parser_;
detail::intrusive_list<cpp_file> files_;
type_safe::object_ref<const cpp_entity_index> idx_;
};
namespace detail
{
struct std_begin
{
};
struct adl_begin : std_begin
{
};
struct member_begin : adl_begin
{
};
template <class Range>
auto get_value_type_impl(member_begin, Range&& r)
-> decltype(std::forward<Range>(r).begin());
template <class Range>
auto get_value_type_impl(adl_begin, Range&& r) -> decltype(begin(std::forward<Range>(r)));
template <class Range>
auto get_value_type_impl(std_begin, Range&& r)
-> decltype(std::begin(std::forward<Range>(r)));
template <class Range>
using value_type = decltype(*get_value_type_impl(member_begin{}, std::declval<Range>()));
} // namespace detail
/// Parses multiple files using a given `FileParser`.
///
/// \effects Will call the `parse()` function for each path specified in the `file_names`,
/// using `get_confg` to determine the configuration.
///
/// \requires `FileParser` must be a class with the following members:
/// * `parser` - A typedef for the parser being used to do the parsing.
/// * `config` - The same as `parser::config`.
/// * `parse(path, config)` - Parses the given file with the configuration using its parser.
/// The parsing can be executed in a separated thread, but then a copy of the configuration and path must be created.
/// \requires `Range` must be some type that can be iterated in a range-based for loop.
/// \requires `Configuration` must be a function that returns a configuration of type `FileParser::config` when given a path.
/// \unique_name parse_files_basic
/// \synopsis_return void
template <class FileParser, class Range, class Configuration>
auto parse_files(FileParser& parser, Range&& file_names, const Configuration& get_config) ->
typename std::enable_if<std::is_same<typename std::decay<decltype(get_config(
std::declval<detail::value_type<Range>>()))>::type,
typename FileParser::config>::value>::type
{
for (auto&& file : std::forward<Range>(file_names))
{
auto&& config = get_config(file);
parser.parse(std::forward<decltype(file)>(file),
std::forward<decltype(config)>(config));
}
} }
/// Parses multiple files using a given `FileParser` and configuration. /// \returns Whether or not an error occurred during parsing.
/// \effects Invokes [cppast::parse_files](standardese://parse_files_basic/) passing it the parser and file names, /// If that happens, the AST might be incomplete.
/// and a function that returns the same configuration for each file. bool error() const noexcept
template <class FileParser, class Range>
void parse_files(FileParser& parser, Range&& file_names, typename FileParser::config config)
{ {
parse_files(parser, std::forward<Range>(file_names), return error_;
[&](const std::string&) { return config; });
} }
/// Parses all files included by `file`. /// \effects Resets the error state.
/// \effects For each [cppast::cpp_include_directive]() in file it will parse the included file. void reset_error() noexcept
template <class FileParser>
std::size_t resolve_includes(FileParser& parser, const cpp_file& file,
typename FileParser::config config)
{ {
auto count = 0u; error_ = false;
for (auto& entity : file)
{
if (entity.kind() == cpp_include_directive::kind())
{
auto& include = static_cast<const cpp_include_directive&>(entity);
parser.parse(include.full_path(), config);
++count;
}
}
return count;
} }
/// \returns A reference to the logger used.
const diagnostic_logger& logger() const noexcept
{
return *logger_;
}
protected:
/// \effects Creates it giving it a reference to the logger it uses.
explicit parser(type_safe::object_ref<const diagnostic_logger> logger)
: logger_(logger), error_(false)
{}
/// \effects Sets the error state.
/// This must be called when an error or critical diagnostic is logged and the AST is
/// incomplete.
void set_error() const noexcept
{
error_ = true;
}
private:
/// \effects Parses the given file.
/// \returns The [cppast::cpp_file]() object describing it.
/// \requires The function must be thread safe.
virtual std::unique_ptr<cpp_file> do_parse(const cpp_entity_index& idx, std::string path,
const compile_config& config) const = 0;
type_safe::object_ref<const diagnostic_logger> logger_;
mutable std::atomic<bool> error_;
};
/// A simple `FileParser` that parses all files synchronously.
///
/// More advanced parsers could use a thread pool, for example.
template <class Parser>
class simple_file_parser
{
static_assert(std::is_base_of<cppast::parser, Parser>::value,
"Parser must be derived from cppast::parser");
public:
using parser = Parser;
using config = typename Parser::config;
/// \effects Creates a file parser populating the given index
/// and using the parser created by forwarding the given arguments.
template <typename... Args>
explicit simple_file_parser(type_safe::object_ref<const cpp_entity_index> idx, Args&&... args)
: parser_(std::forward<Args>(args)...), idx_(idx)
{}
/// \effects Parses the given file using the given configuration.
/// \returns The parsed file or an empty optional, if a fatal error occurred.
type_safe::optional_ref<const cpp_file> parse(std::string path, const config& c)
{
parser_.logger().log("simple file parser", diagnostic{"parsing file '" + path + "'",
source_location(), severity::info});
auto file = parser_.parse(*idx_, std::move(path), c);
auto ptr = file.get();
if (file)
files_.push_back(std::move(file));
return type_safe::opt_ref(ptr);
}
/// \returns The result of [cppast::parser::error]().
bool error() const noexcept
{
return parser_.error();
}
/// \effects Calls [cppast::parser::reset_error]().
void reset_error() noexcept
{
parser_.reset_error();
}
/// \returns The index that is being populated.
const cpp_entity_index& index() const noexcept
{
return *idx_;
}
/// \returns An iteratable object iterating over all the files that have been parsed so far.
/// \exclude return
detail::iteratable_intrusive_list<cpp_file> files() const noexcept
{
return type_safe::ref(files_);
}
private:
Parser parser_;
detail::intrusive_list<cpp_file> files_;
type_safe::object_ref<const cpp_entity_index> idx_;
};
namespace detail
{
struct std_begin
{};
struct adl_begin : std_begin
{};
struct member_begin : adl_begin
{};
template <class Range>
auto get_value_type_impl(member_begin, Range&& r) -> decltype(std::forward<Range>(r).begin());
template <class Range>
auto get_value_type_impl(adl_begin, Range&& r) -> decltype(begin(std::forward<Range>(r)));
template <class Range>
auto get_value_type_impl(std_begin, Range&& r) -> decltype(std::begin(std::forward<Range>(r)));
template <class Range>
using value_type = decltype(*get_value_type_impl(member_begin{}, std::declval<Range>()));
} // namespace detail
/// Parses multiple files using a given `FileParser`.
///
/// \effects Will call the `parse()` function for each path specified in the `file_names`,
/// using `get_confg` to determine the configuration.
///
/// \requires `FileParser` must be a class with the following members:
/// * `parser` - A typedef for the parser being used to do the parsing.
/// * `config` - The same as `parser::config`.
/// * `parse(path, config)` - Parses the given file with the configuration using its parser.
/// The parsing can be executed in a separated thread, but then a copy of the configuration and path
/// must be created. \requires `Range` must be some type that can be iterated in a range-based for
/// loop. \requires `Configuration` must be a function that returns a configuration of type
/// `FileParser::config` when given a path. \unique_name parse_files_basic \synopsis_return void
template <class FileParser, class Range, class Configuration>
auto parse_files(FileParser& parser, Range&& file_names, const Configuration& get_config) ->
typename std::enable_if<std::is_same<
typename std::decay<decltype(get_config(std::declval<detail::value_type<Range>>()))>::type,
typename FileParser::config>::value>::type
{
for (auto&& file : std::forward<Range>(file_names))
{
auto&& config = get_config(file);
parser.parse(std::forward<decltype(file)>(file), std::forward<decltype(config)>(config));
}
}
/// Parses multiple files using a given `FileParser` and configuration.
/// \effects Invokes [cppast::parse_files](standardese://parse_files_basic/) passing it the parser
/// and file names, and a function that returns the same configuration for each file.
template <class FileParser, class Range>
void parse_files(FileParser& parser, Range&& file_names, typename FileParser::config config)
{
parse_files(parser, std::forward<Range>(file_names),
[&](const std::string&) { return config; });
}
/// Parses all files included by `file`.
/// \effects For each [cppast::cpp_include_directive]() in file it will parse the included file.
template <class FileParser>
std::size_t resolve_includes(FileParser& parser, const cpp_file& file,
typename FileParser::config config)
{
auto count = 0u;
for (auto& entity : file)
{
if (entity.kind() == cpp_include_directive::kind())
{
auto& include = static_cast<const cpp_include_directive&>(entity);
parser.parse(include.full_path(), config);
++count;
}
}
return count;
}
} // namespace cppast } // namespace cppast
#endif // CPPAST_PARSER_HPP_INCLUDED #endif // CPPAST_PARSER_HPP_INCLUDED

View file

@ -13,231 +13,230 @@
namespace cppast namespace cppast
{ {
/// Information about the state of a visit operation. /// Information about the state of a visit operation.
struct visitor_info struct visitor_info
{
enum event_type
{ {
enum event_type leaf_entity, //< Callback called for a leaf entity without children.
{ /// If callback returns `false`, visit operation will be aborted.
leaf_entity, //< Callback called for a leaf entity without children.
/// If callback returns `false`, visit operation will be aborted.
container_entity_enter, //< Callback called for a container entity before the children. container_entity_enter, //< Callback called for a container entity before the children.
/// If callback returns `false`, none of the children will be visited, /// If callback returns `false`, none of the children will be visited,
/// going immediately to the exit event. /// going immediately to the exit event.
container_entity_exit, //< Callback called for a container entity after the children. container_entity_exit, //< Callback called for a container entity after the children.
/// If callback returns `false`, visit operation will be aborted. /// If callback returns `false`, visit operation will be aborted.
} event; } event;
cpp_access_specifier_kind access; //< The current access specifier. cpp_access_specifier_kind access; //< The current access specifier.
/// True when the current entity is the last child of the visited parent entity. /// True when the current entity is the last child of the visited parent entity.
/// \notes It will always be `false` for the initial entity. /// \notes It will always be `false` for the initial entity.
bool last_child; bool last_child;
/// \returns `true` if the entity was not visited already. /// \returns `true` if the entity was not visited already.
bool is_new_entity() const noexcept bool is_new_entity() const noexcept
{
return event != container_entity_exit;
}
/// \returns `true` if the entity was visited already.
bool is_old_entity() const noexcept
{
return !is_new_entity();
}
};
/// A more expressive way to specify the return of a visit operation.
enum visitor_result : bool
{ {
/// Visit should continue. return event != container_entity_exit;
/// \group continue }
continue_visit = true,
/// \group continue /// \returns `true` if the entity was visited already.
continue_visit_children = true, bool is_old_entity() const noexcept
/// Visit should not visit the children.
/// \notes This only happens when the event is [cppast::visitor_info::container_entity_enter]().
continue_visit_no_children = false,
/// Visit should be aborted.
/// \notes This only happens when the event is not [cppast::visitor_info::container_entity_enter]().
abort_visit = false,
};
/// \exclude
namespace detail
{ {
using visitor_callback_t = bool (*)(void* mem, const cpp_entity&, visitor_info info); return !is_new_entity();
}
};
struct visitor_info_void /// A more expressive way to specify the return of a visit operation.
{ enum visitor_result : bool
}; {
struct visitor_info_bool : visitor_info_void /// Visit should continue.
{ /// \group continue
}; continue_visit = true,
template <typename Func> /// \group continue
visitor_callback_t get_visitor_callback( continue_visit_children = true,
visitor_info_bool, decltype(std::declval<Func>()(std::declval<const cpp_entity&>(),
visitor_info{})) = true)
{
return [](void* mem, const cpp_entity& e, visitor_info info) {
auto& func = *static_cast<Func*>(mem);
return func(e, info);
};
}
template <typename Func> /// Visit should not visit the children.
visitor_callback_t get_visitor_callback( /// \notes This only happens when the event is [cppast::visitor_info::container_entity_enter]().
visitor_info_void, continue_visit_no_children = false,
decltype(std::declval<Func>()(std::declval<const cpp_entity&>(), visitor_info{}),
0) = 0)
{
return [](void* mem, const cpp_entity& e, visitor_info info) {
auto& func = *static_cast<Func*>(mem);
func(e, info);
return true;
};
}
template <typename Func> /// Visit should be aborted.
visitor_callback_t get_visitor_callback() /// \notes This only happens when the event is not
{ /// [cppast::visitor_info::container_entity_enter]().
return get_visitor_callback<Func>(visitor_info_bool{}); abort_visit = false,
} };
bool visit(const cpp_entity& e, visitor_callback_t cb, void* functor, /// \exclude
cpp_access_specifier_kind cur_access, bool last_child); namespace detail
} // namespace detail {
using visitor_callback_t = bool (*)(void* mem, const cpp_entity&, visitor_info info);
struct visitor_info_void
{};
struct visitor_info_bool : visitor_info_void
{};
/// Visits a [cppast::cpp_entity]() and children.
///
/// \effects It will invoke the callback - the visitor - for the current entity and children,
/// as controlled by the [cppast::visitor_result]().
/// The visitor is given a reference to the currently visited entity and the [cppast::visitor_info]().
///
/// \requires The visitor must be callable as specified and must either return `bool` or nothing.
/// If it returns nothing, a return value [cppast::visitor_result::continue_visit]() is assumed.
template <typename Func> template <typename Func>
void visit(const cpp_entity& e, Func f) visitor_callback_t get_visitor_callback(
visitor_info_bool,
decltype(std::declval<Func>()(std::declval<const cpp_entity&>(), visitor_info{})) = true)
{ {
detail::visit(e, detail::get_visitor_callback<Func>(), &f, cpp_public, false); return [](void* mem, const cpp_entity& e, visitor_info info) {
auto& func = *static_cast<Func*>(mem);
return func(e, info);
};
} }
/// The result of a visitor filter operation. template <typename Func>
enum class visit_filter visitor_callback_t get_visitor_callback(
visitor_info_void,
decltype(std::declval<Func>()(std::declval<const cpp_entity&>(), visitor_info{}), 0) = 0)
{ {
include = true, //< The entity is included. return [](void* mem, const cpp_entity& e, visitor_info info) {
exclude = false, //< The entity is excluded and will not be visited. auto& func = *static_cast<Func*>(mem);
exclude_and_children = func(e, info);
2, //< The entity and all direct children are excluded and will not be visited. return true;
}; };
}
namespace detail template <typename Func>
visitor_callback_t get_visitor_callback()
{ {
using visitor_filter_t = visit_filter (*)(const cpp_entity&); return get_visitor_callback<Func>(visitor_info_bool{});
}
template <typename Filter> bool visit(const cpp_entity& e, visitor_callback_t cb, void* functor,
auto invoke_visit_filter(int, Filter f, const cpp_entity& e, cpp_access_specifier_kind cur_access, bool last_child);
cpp_access_specifier_kind access) } // namespace detail
-> decltype(static_cast<visit_filter>(f(e, access)))
/// Visits a [cppast::cpp_entity]() and children.
///
/// \effects It will invoke the callback - the visitor - for the current entity and children,
/// as controlled by the [cppast::visitor_result]().
/// The visitor is given a reference to the currently visited entity and the
/// [cppast::visitor_info]().
///
/// \requires The visitor must be callable as specified and must either return `bool` or nothing.
/// If it returns nothing, a return value [cppast::visitor_result::continue_visit]() is assumed.
template <typename Func>
void visit(const cpp_entity& e, Func f)
{
detail::visit(e, detail::get_visitor_callback<Func>(), &f, cpp_public, false);
}
/// The result of a visitor filter operation.
enum class visit_filter
{
include = true, //< The entity is included.
exclude = false, //< The entity is excluded and will not be visited.
exclude_and_children
= 2, //< The entity and all direct children are excluded and will not be visited.
};
namespace detail
{
using visitor_filter_t = visit_filter (*)(const cpp_entity&);
template <typename Filter>
auto invoke_visit_filter(int, Filter f, const cpp_entity& e, cpp_access_specifier_kind access)
-> decltype(static_cast<visit_filter>(f(e, access)))
{
return static_cast<visit_filter>(f(e, access));
}
template <typename Filter>
auto invoke_visit_filter(short, Filter f, const cpp_entity& e, cpp_access_specifier_kind)
-> decltype(static_cast<visit_filter>(f(e)))
{
return static_cast<visit_filter>(f(e));
}
} // namespace detail
/// Visits a [cppast::cpp_entity]() and children that pass a given filter.
///
/// \effects It behaves like the non-filtered visit except it will only invoke the visitor for
/// entities that match the filter. The filter is a predicate that will be invoked with the current
/// entity and access specifier first and the result converted to [cppast::visit_filter](). Visit
/// will behave accordingly.
///
/// \requires The visitor must be as specified for the other overload.
/// The filter must be a function taking a [cppast::cpp_entity]() as first parameter and optionally
/// a [cppast::cpp_access_specifier_kind]() as second. It must return a `bool` or a
/// [cppast::visit_filter]().
template <typename Func, typename Filter>
void visit(const cpp_entity& e, Filter filter, Func f)
{
visit(e, [&](const cpp_entity& e, const visitor_info& info) -> bool {
auto result = detail::invoke_visit_filter(0, filter, e, info.access);
switch (result)
{ {
return static_cast<visit_filter>(f(e, access)); case visit_filter::include:
} return detail::get_visitor_callback<Func>()(&f, e, info);
case visit_filter::exclude:
template <typename Filter>
auto invoke_visit_filter(short, Filter f, const cpp_entity& e, cpp_access_specifier_kind)
-> decltype(static_cast<visit_filter>(f(e)))
{
return static_cast<visit_filter>(f(e));
}
} // namespace detail
/// Visits a [cppast::cpp_entity]() and children that pass a given filter.
///
/// \effects It behaves like the non-filtered visit except it will only invoke the visitor for entities that match the filter.
/// The filter is a predicate that will be invoked with the current entity and access specifier first and the result converted to [cppast::visit_filter]().
/// Visit will behave accordingly.
///
/// \requires The visitor must be as specified for the other overload.
/// The filter must be a function taking a [cppast::cpp_entity]() as first parameter and optionally a [cppast::cpp_access_specifier_kind]() as second.
/// It must return a `bool` or a [cppast::visit_filter]().
template <typename Func, typename Filter>
void visit(const cpp_entity& e, Filter filter, Func f)
{
visit(e, [&](const cpp_entity& e, const visitor_info& info) -> bool {
auto result = detail::invoke_visit_filter(0, filter, e, info.access);
switch (result)
{
case visit_filter::include:
return detail::get_visitor_callback<Func>()(&f, e, info);
case visit_filter::exclude:
return continue_visit;
case visit_filter::exclude_and_children:
// exclude children if entering
if (info.event == visitor_info::container_entity_enter)
return continue_visit_no_children;
else
return continue_visit;
}
return continue_visit; return continue_visit;
}); case visit_filter::exclude_and_children:
} // exclude children if entering
if (info.event == visitor_info::container_entity_enter)
/// \exclude return continue_visit_no_children;
namespace detail else
{ return continue_visit;
template <cpp_entity_kind... K>
bool has_one_of_kind(const cpp_entity& e)
{
static_assert(sizeof...(K) > 0, "At least one entity kind must be specified");
bool result = false;
// poor men's fold
int dummy[]{(result |= (K == e.kind()), 0)...};
(void)dummy;
return result;
} }
} // namespace detail return continue_visit;
});
}
/// Generates a blacklist visitor filter. /// \exclude
/// namespace detail
/// \returns A visitor filter that excludes all entities having one of the specified kinds. {
template <cpp_entity_kind... Kinds> template <cpp_entity_kind... K>
detail::visitor_filter_t blacklist() bool has_one_of_kind(const cpp_entity& e)
{ {
return [](const cpp_entity& e) { static_assert(sizeof...(K) > 0, "At least one entity kind must be specified");
return detail::has_one_of_kind<Kinds...>(e) ? visit_filter::exclude : bool result = false;
visit_filter::include;
};
}
/// Generates a blacklist visitor filter. // poor men's fold
/// int dummy[]{(result |= (K == e.kind()), 0)...};
/// \returns A visitor filter that excludes all entities having one of the specified kinds and children of those entities. (void)dummy;
template <cpp_entity_kind... Kinds>
detail::visitor_filter_t blacklist_and_children()
{
return [](const cpp_entity& e) {
return detail::has_one_of_kind<Kinds...>(e) ? visit_filter::exclude_and_children :
visit_filter::include;
};
}
/// Generates a whitelist visitor filter. return result;
///
/// \returns A visitor filter that excludes all entities having not one of the specified kinds.
template <cpp_entity_kind... Kinds>
detail::visitor_filter_t whitelist()
{
return [](const cpp_entity& e) {
return detail::has_one_of_kind<Kinds...>(e) ? visit_filter::include :
visit_filter::exclude;
};
} }
} // namespace detail
/// Generates a blacklist visitor filter.
///
/// \returns A visitor filter that excludes all entities having one of the specified kinds.
template <cpp_entity_kind... Kinds>
detail::visitor_filter_t blacklist()
{
return [](const cpp_entity& e) {
return detail::has_one_of_kind<Kinds...>(e) ? visit_filter::exclude : visit_filter::include;
};
}
/// Generates a blacklist visitor filter.
///
/// \returns A visitor filter that excludes all entities having one of the specified kinds and
/// children of those entities.
template <cpp_entity_kind... Kinds>
detail::visitor_filter_t blacklist_and_children()
{
return [](const cpp_entity& e) {
return detail::has_one_of_kind<Kinds...>(e) ? visit_filter::exclude_and_children
: visit_filter::include;
};
}
/// Generates a whitelist visitor filter.
///
/// \returns A visitor filter that excludes all entities having not one of the specified kinds.
template <cpp_entity_kind... Kinds>
detail::visitor_filter_t whitelist()
{
return [](const cpp_entity& e) {
return detail::has_one_of_kind<Kinds...>(e) ? visit_filter::include : visit_filter::exclude;
};
}
} // namespace cppast } // namespace cppast
#endif // CPPAST_VISITOR_HPP_INCLUDED #endif // CPPAST_VISITOR_HPP_INCLUDED

File diff suppressed because it is too large Load diff

View file

@ -12,32 +12,32 @@ using namespace cppast;
namespace namespace
{ {
const char* get_attribute_name(cpp_attribute_kind kind) noexcept const char* get_attribute_name(cpp_attribute_kind kind) noexcept
{
switch (kind)
{ {
switch (kind) case cpp_attribute_kind::alignas_:
{ return "alignas";
case cpp_attribute_kind::alignas_: case cpp_attribute_kind::carries_dependency:
return "alignas"; return "carries_dependency";
case cpp_attribute_kind::carries_dependency: case cpp_attribute_kind::deprecated:
return "carries_dependency"; return "deprecated";
case cpp_attribute_kind::deprecated: case cpp_attribute_kind::fallthrough:
return "deprecated"; return "fallthrough";
case cpp_attribute_kind::fallthrough: case cpp_attribute_kind::maybe_unused:
return "fallthrough"; return "maybe_unused";
case cpp_attribute_kind::maybe_unused: case cpp_attribute_kind::nodiscard:
return "maybe_unused"; return "nodiscard";
case cpp_attribute_kind::nodiscard: case cpp_attribute_kind::noreturn:
return "nodiscard"; return "noreturn";
case cpp_attribute_kind::noreturn:
return "noreturn";
case cpp_attribute_kind::unknown: case cpp_attribute_kind::unknown:
return "unknown"; return "unknown";
}
return "<error>";
} }
return "<error>";
} }
} // namespace
cpp_attribute::cpp_attribute(cpp_attribute_kind kind, cpp_attribute::cpp_attribute(cpp_attribute_kind kind,
type_safe::optional<cpp_token_string> arguments) type_safe::optional<cpp_token_string> arguments)
@ -49,13 +49,13 @@ cpp_attribute::cpp_attribute(cpp_attribute_kind kind,
type_safe::optional_ref<const cpp_attribute> cppast::has_attribute( type_safe::optional_ref<const cpp_attribute> cppast::has_attribute(
const cpp_attribute_list& attributes, const std::string& name) const cpp_attribute_list& attributes, const std::string& name)
{ {
auto iter = auto iter
std::find_if(attributes.begin(), attributes.end(), [&](const cpp_attribute& attribute) { = std::find_if(attributes.begin(), attributes.end(), [&](const cpp_attribute& attribute) {
if (attribute.scope()) if (attribute.scope())
return attribute.scope().value() + "::" + attribute.name() == name; return attribute.scope().value() + "::" + attribute.name() == name;
else else
return attribute.name() == name; return attribute.name() == name;
}); });
if (iter == attributes.end()) if (iter == attributes.end())
return nullptr; return nullptr;
@ -66,9 +66,9 @@ type_safe::optional_ref<const cpp_attribute> cppast::has_attribute(
type_safe::optional_ref<const cpp_attribute> cppast::has_attribute( type_safe::optional_ref<const cpp_attribute> cppast::has_attribute(
const cpp_attribute_list& attributes, cpp_attribute_kind kind) const cpp_attribute_list& attributes, cpp_attribute_kind kind)
{ {
auto iter = auto iter
std::find_if(attributes.begin(), attributes.end(), = std::find_if(attributes.begin(), attributes.end(),
[&](const cpp_attribute& attribute) { return attribute.kind() == kind; }); [&](const cpp_attribute& attribute) { return attribute.kind() == kind; });
if (iter == attributes.end()) if (iter == attributes.end())
return nullptr; return nullptr;

View file

@ -4,10 +4,10 @@
#include <cppast/cpp_class.hpp> #include <cppast/cpp_class.hpp>
#include <cppast/cpp_entity_index.hpp>
#include <cppast/cpp_entity_kind.hpp>
#include <cppast/cpp_alias_template.hpp> #include <cppast/cpp_alias_template.hpp>
#include <cppast/cpp_class_template.hpp> #include <cppast/cpp_class_template.hpp>
#include <cppast/cpp_entity_index.hpp>
#include <cppast/cpp_entity_kind.hpp>
using namespace cppast; using namespace cppast;
@ -103,70 +103,69 @@ cpp_entity_kind cpp_class::do_get_entity_kind() const noexcept
namespace namespace
{ {
cpp_entity_ref get_type_ref(const cpp_type& type) cpp_entity_ref get_type_ref(const cpp_type& type)
{
if (type.kind() == cpp_type_kind::user_defined_t)
{ {
if (type.kind() == cpp_type_kind::user_defined_t) auto& ref = static_cast<const cpp_user_defined_type&>(type).entity();
{ return cpp_entity_ref(ref.id()[0u], ref.name());
auto& ref = static_cast<const cpp_user_defined_type&>(type).entity(); }
return cpp_entity_ref(ref.id()[0u], ref.name()); else if (type.kind() == cpp_type_kind::template_instantiation_t)
} {
else if (type.kind() == cpp_type_kind::template_instantiation_t) auto& ref = static_cast<const cpp_template_instantiation_type&>(type).primary_template();
{ return cpp_entity_ref(ref.id()[0u], ref.name());
auto& ref =
static_cast<const cpp_template_instantiation_type&>(type).primary_template();
return cpp_entity_ref(ref.id()[0u], ref.name());
}
DEBUG_ASSERT(type.kind() == cpp_type_kind::template_parameter_t
|| type.kind() == cpp_type_kind::decltype_t
|| type.kind() == cpp_type_kind::decltype_auto_t
|| type.kind() == cpp_type_kind::unexposed_t,
detail::assert_handler{});
return cpp_entity_ref(cpp_entity_id("<null id>"), "");
} }
type_safe::optional_ref<const cpp_entity> get_entity_impl(const cpp_entity_index& index, DEBUG_ASSERT(type.kind() == cpp_type_kind::template_parameter_t
const cpp_entity_ref& ref) || type.kind() == cpp_type_kind::decltype_t
{ || type.kind() == cpp_type_kind::decltype_auto_t
auto result = ref.get(index); || type.kind() == cpp_type_kind::unexposed_t,
if (result.empty()) detail::assert_handler{});
return nullptr; return cpp_entity_ref(cpp_entity_id("<null id>"), "");
DEBUG_ASSERT(result.size() == 1u, detail::assert_handler{}); }
auto entity = result.front(); type_safe::optional_ref<const cpp_entity> get_entity_impl(const cpp_entity_index& index,
if (entity->kind() == cpp_class_template::kind()) const cpp_entity_ref& ref)
return type_safe::ref(static_cast<const cpp_class_template&>(*entity).class_()); {
else if (entity->kind() == cpp_class_template_specialization::kind()) auto result = ref.get(index);
return type_safe::ref( if (result.empty())
static_cast<const cpp_class_template_specialization&>(*entity).class_()); return nullptr;
else DEBUG_ASSERT(result.size() == 1u, detail::assert_handler{});
return entity;
auto entity = result.front();
if (entity->kind() == cpp_class_template::kind())
return type_safe::ref(static_cast<const cpp_class_template&>(*entity).class_());
else if (entity->kind() == cpp_class_template_specialization::kind())
return type_safe::ref(
static_cast<const cpp_class_template_specialization&>(*entity).class_());
else
return entity;
}
type_safe::optional_ref<const cpp_class> get_class_impl(const cpp_entity_index& index,
const cpp_entity_ref& ref)
{
auto entity = get_entity_impl(index, ref);
if (!entity)
return nullptr;
if (entity.value().kind() == cpp_alias_template::kind())
{
auto& alias = static_cast<const cppast::cpp_alias_template&>(entity.value());
return get_class_impl(index, get_type_ref(alias.type_alias().underlying_type()));
} }
else if (entity.value().kind() == cpp_type_alias::kind())
type_safe::optional_ref<const cpp_class> get_class_impl(const cpp_entity_index& index,
const cpp_entity_ref& ref)
{ {
auto entity = get_entity_impl(index, ref); auto& alias = static_cast<const cppast::cpp_type_alias&>(entity.value());
if (!entity) return get_class_impl(index, get_type_ref(alias.underlying_type()));
return nullptr; }
else
if (entity.value().kind() == cpp_alias_template::kind()) {
{ DEBUG_ASSERT(entity.value().kind() == cpp_class::kind(), detail::assert_handler{});
auto& alias = static_cast<const cppast::cpp_alias_template&>(entity.value()); return type_safe::ref(static_cast<const cpp_class&>(entity.value()));
return get_class_impl(index, get_type_ref(alias.type_alias().underlying_type()));
}
else if (entity.value().kind() == cpp_type_alias::kind())
{
auto& alias = static_cast<const cppast::cpp_type_alias&>(entity.value());
return get_class_impl(index, get_type_ref(alias.underlying_type()));
}
else
{
DEBUG_ASSERT(entity.value().kind() == cpp_class::kind(), detail::assert_handler{});
return type_safe::ref(static_cast<const cpp_class&>(entity.value()));
}
} }
} }
} // namespace
type_safe::optional_ref<const cpp_class> cppast::get_class(const cpp_entity_index& index, type_safe::optional_ref<const cpp_class> cppast::get_class(const cpp_entity_index& index,
const cpp_base_class& base) const cpp_base_class& base)

View file

@ -4,17 +4,16 @@
#include <cppast/cpp_entity_index.hpp> #include <cppast/cpp_entity_index.hpp>
#include <cppast/detail/assert.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_file.hpp> #include <cppast/cpp_file.hpp>
#include <cppast/detail/assert.hpp>
using namespace cppast; using namespace cppast;
cpp_entity_index::duplicate_definition_error::duplicate_definition_error() cpp_entity_index::duplicate_definition_error::duplicate_definition_error()
: std::logic_error("duplicate registration of entity definition") : std::logic_error("duplicate registration of entity definition")
{ {}
}
void cpp_entity_index::register_definition(cpp_entity_id id, void cpp_entity_index::register_definition(cpp_entity_id id,
type_safe::object_ref<const cpp_entity> entity) const type_safe::object_ref<const cpp_entity> entity) const

View file

@ -17,8 +17,8 @@ std::unique_ptr<cpp_enum_value> cpp_enum_value::build(const cpp_entity_index& id
std::string name, std::string name,
std::unique_ptr<cpp_expression> value) std::unique_ptr<cpp_expression> value)
{ {
auto result = auto result
std::unique_ptr<cpp_enum_value>(new cpp_enum_value(std::move(name), std::move(value))); = std::unique_ptr<cpp_enum_value>(new cpp_enum_value(std::move(name), std::move(value)));
idx.register_definition(std::move(id), type_safe::ref(*result)); idx.register_definition(std::move(id), type_safe::ref(*result));
return result; return result;
} }

View file

@ -8,77 +8,77 @@ using namespace cppast;
namespace namespace
{ {
void write_literal(code_generator::output& output, const cpp_literal_expression& expr) 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 type_kind = cpp_void; auto& pointee = static_cast<const cpp_pointer_type&>(expr.type()).pointee();
if (expr.type().kind() == cpp_type_kind::builtin_t) if (pointee.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(); auto& builtin_pointee = static_cast<const cpp_builtin_type&>(pointee);
if (pointee.kind() == cpp_type_kind::builtin_t) if (builtin_pointee.builtin_type_kind() == cpp_char
{ || builtin_pointee.builtin_type_kind() == cpp_wchar
auto& builtin_pointee = static_cast<const cpp_builtin_type&>(pointee); || builtin_pointee.builtin_type_kind() == cpp_char16
if (builtin_pointee.builtin_type_kind() == cpp_char || builtin_pointee.builtin_type_kind() == cpp_char32)
|| builtin_pointee.builtin_type_kind() == cpp_wchar // pointer to char aka string
|| builtin_pointee.builtin_type_kind() == cpp_char16 type_kind = builtin_pointee.builtin_type_kind();
|| 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) switch (type_kind)
{ {
detail::write_token_string(output, expr.expression()); 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)
{
detail::write_token_string(output, expr.expression());
}
} // namespace
void detail::write_expression(code_generator::output& output, const cpp_expression& expr) void detail::write_expression(code_generator::output& output, const cpp_expression& expr)
{ {
switch (expr.kind()) switch (expr.kind())

View file

@ -15,73 +15,73 @@ using namespace cppast;
namespace namespace
{ {
type_safe::optional_ref<const cpp_forward_declarable> get_declarable(const cpp_entity& e) type_safe::optional_ref<const cpp_forward_declarable> get_declarable(const cpp_entity& e)
{
switch (e.kind())
{ {
switch (e.kind()) case cpp_entity_kind::enum_t:
{ return type_safe::ref(static_cast<const cpp_enum&>(e));
case cpp_entity_kind::enum_t: case cpp_entity_kind::class_t:
return type_safe::ref(static_cast<const cpp_enum&>(e)); return type_safe::ref(static_cast<const cpp_class&>(e));
case cpp_entity_kind::class_t: case cpp_entity_kind::variable_t:
return type_safe::ref(static_cast<const cpp_class&>(e)); return type_safe::ref(static_cast<const cpp_variable&>(e));
case cpp_entity_kind::variable_t: case cpp_entity_kind::function_t:
return type_safe::ref(static_cast<const cpp_variable&>(e)); case cpp_entity_kind::member_function_t:
case cpp_entity_kind::function_t: case cpp_entity_kind::conversion_op_t:
case cpp_entity_kind::member_function_t: case cpp_entity_kind::constructor_t:
case cpp_entity_kind::conversion_op_t: case cpp_entity_kind::destructor_t:
case cpp_entity_kind::constructor_t: return type_safe::ref(static_cast<const cpp_function_base&>(e));
case cpp_entity_kind::destructor_t: case cpp_entity_kind::function_template_t:
return type_safe::ref(static_cast<const cpp_function_base&>(e)); case cpp_entity_kind::function_template_specialization_t:
case cpp_entity_kind::function_template_t: case cpp_entity_kind::class_template_t:
case cpp_entity_kind::function_template_specialization_t: case cpp_entity_kind::class_template_specialization_t:
case cpp_entity_kind::class_template_t: return get_declarable(*static_cast<const cpp_template&>(e).begin());
case cpp_entity_kind::class_template_specialization_t:
return get_declarable(*static_cast<const cpp_template&>(e).begin());
case cpp_entity_kind::file_t: case cpp_entity_kind::file_t:
case cpp_entity_kind::macro_parameter_t: case cpp_entity_kind::macro_parameter_t:
case cpp_entity_kind::macro_definition_t: case cpp_entity_kind::macro_definition_t:
case cpp_entity_kind::include_directive_t: case cpp_entity_kind::include_directive_t:
case cpp_entity_kind::language_linkage_t: case cpp_entity_kind::language_linkage_t:
case cpp_entity_kind::namespace_t: case cpp_entity_kind::namespace_t:
case cpp_entity_kind::namespace_alias_t: case cpp_entity_kind::namespace_alias_t:
case cpp_entity_kind::using_directive_t: case cpp_entity_kind::using_directive_t:
case cpp_entity_kind::using_declaration_t: case cpp_entity_kind::using_declaration_t:
case cpp_entity_kind::type_alias_t: case cpp_entity_kind::type_alias_t:
case cpp_entity_kind::enum_value_t: case cpp_entity_kind::enum_value_t:
case cpp_entity_kind::access_specifier_t: case cpp_entity_kind::access_specifier_t:
case cpp_entity_kind::base_class_t: case cpp_entity_kind::base_class_t:
case cpp_entity_kind::member_variable_t: case cpp_entity_kind::member_variable_t:
case cpp_entity_kind::bitfield_t: case cpp_entity_kind::bitfield_t:
case cpp_entity_kind::function_parameter_t: case cpp_entity_kind::function_parameter_t:
case cpp_entity_kind::friend_t: case cpp_entity_kind::friend_t:
case cpp_entity_kind::template_type_parameter_t: case cpp_entity_kind::template_type_parameter_t:
case cpp_entity_kind::non_type_template_parameter_t: case cpp_entity_kind::non_type_template_parameter_t:
case cpp_entity_kind::template_template_parameter_t: case cpp_entity_kind::template_template_parameter_t:
case cpp_entity_kind::alias_template_t: case cpp_entity_kind::alias_template_t:
case cpp_entity_kind::variable_template_t: case cpp_entity_kind::variable_template_t:
case cpp_entity_kind::static_assert_t: case cpp_entity_kind::static_assert_t:
case cpp_entity_kind::unexposed_t: case cpp_entity_kind::unexposed_t:
return nullptr;
case cpp_entity_kind::count:
break;
}
DEBUG_UNREACHABLE(detail::assert_handler{});
return nullptr; return nullptr;
case cpp_entity_kind::count:
break;
} }
type_safe::optional_ref<const cpp_entity> get_definition_impl(const cpp_entity_index& idx, DEBUG_UNREACHABLE(detail::assert_handler{});
const cpp_entity& e) return nullptr;
{ }
auto declarable = get_declarable(e);
if (!declarable || declarable.value().is_definition()) type_safe::optional_ref<const cpp_entity> get_definition_impl(const cpp_entity_index& idx,
// not declarable or is a definition const cpp_entity& e)
// return reference to entity itself {
return type_safe::ref(e); auto declarable = get_declarable(e);
// else lookup definition if (!declarable || declarable.value().is_definition())
return idx.lookup_definition(declarable.value().definition().value()); // not declarable or is a definition
} // return reference to entity itself
return type_safe::ref(e);
// else lookup definition
return idx.lookup_definition(declarable.value().definition().value());
}
} // namespace } // namespace
bool cppast::is_definition(const cpp_entity& e) noexcept bool cppast::is_definition(const cpp_entity& e) noexcept

File diff suppressed because it is too large Load diff

View file

@ -10,8 +10,8 @@
#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>
#include <cppast/cpp_type_alias.hpp>
#include <cppast/cpp_template.hpp> #include <cppast/cpp_template.hpp>
#include <cppast/cpp_type_alias.hpp>
using namespace cppast; using namespace cppast;
@ -174,36 +174,36 @@ std::unique_ptr<cpp_dependent_type> cpp_dependent_type::build(
namespace namespace
{ {
// is directly a complex type // is directly a complex type
// is_complex_type also checks for children // is_complex_type also checks for children
bool is_direct_complex(const cpp_type& type) noexcept bool is_direct_complex(const cpp_type& type) noexcept
{
switch (type.kind())
{ {
switch (type.kind()) case cpp_type_kind::builtin_t:
{ case cpp_type_kind::user_defined_t:
case cpp_type_kind::builtin_t: case cpp_type_kind::auto_t:
case cpp_type_kind::user_defined_t: case cpp_type_kind::decltype_t:
case cpp_type_kind::auto_t: case cpp_type_kind::decltype_auto_t:
case cpp_type_kind::decltype_t: case cpp_type_kind::cv_qualified_t:
case cpp_type_kind::decltype_auto_t: case cpp_type_kind::pointer_t:
case cpp_type_kind::cv_qualified_t: case cpp_type_kind::reference_t:
case cpp_type_kind::pointer_t: case cpp_type_kind::template_parameter_t:
case cpp_type_kind::reference_t: case cpp_type_kind::template_instantiation_t:
case cpp_type_kind::template_parameter_t: case cpp_type_kind::dependent_t:
case cpp_type_kind::template_instantiation_t: case cpp_type_kind::unexposed_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; 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;
}
} // namespace } // namespace
bool detail::is_complex_type(const cpp_type& type) noexcept bool detail::is_complex_type(const cpp_type& type) noexcept
@ -226,281 +226,278 @@ bool detail::is_complex_type(const cpp_type& type) noexcept
namespace namespace
{ {
void comma(const code_generator::output& output) void comma(const code_generator::output& output)
{ {
output << punctuation(","); output << punctuation(",");
if (output.formatting().is_set(formatting_flags::comma_ws)) if (output.formatting().is_set(formatting_flags::comma_ws))
output << whitespace; output << whitespace;
} }
void bracket_ws(const code_generator::output& output) void bracket_ws(const code_generator::output& output)
{ {
if (output.formatting().is_set(formatting_flags::bracket_ws)) if (output.formatting().is_set(formatting_flags::bracket_ws))
output << whitespace; output << whitespace;
} }
void operator_ws(const code_generator::output& output) void operator_ws(const code_generator::output& output)
{ {
if (output.formatting().is_set(formatting_flags::operator_ws)) if (output.formatting().is_set(formatting_flags::operator_ws))
output << whitespace; output << whitespace;
} }
void write_builtin(code_generator::output& output, const cpp_builtin_type& type) void write_builtin(code_generator::output& output, const cpp_builtin_type& type)
{ {
output << keyword(to_string(type.builtin_type_kind())); output << keyword(to_string(type.builtin_type_kind()));
} }
void write_user_defined(code_generator::output& output, const cpp_user_defined_type& type) void write_user_defined(code_generator::output& output, const cpp_user_defined_type& type)
{ {
output << type.entity(); output << type.entity();
} }
void write_auto(code_generator::output& output, const cpp_auto_type&) void write_auto(code_generator::output& output, const cpp_auto_type&)
{ {
output << keyword("auto"); output << keyword("auto");
} }
void write_decltype(code_generator::output& output, const cpp_decltype_type& type) void write_decltype(code_generator::output& output, const cpp_decltype_type& type)
{ {
output << keyword("decltype") << punctuation("(") << bracket_ws; output << keyword("decltype") << punctuation("(") << bracket_ws;
detail::write_expression(output, type.expression()); detail::write_expression(output, type.expression());
output << bracket_ws << punctuation(")"); output << bracket_ws << punctuation(")");
} }
void write_decltype_auto(code_generator::output& output, const cpp_decltype_auto_type&) void write_decltype_auto(code_generator::output& output, const cpp_decltype_auto_type&)
{ {
output << keyword("decltype") << punctuation("(") << bracket_ws << keyword("auto") output << keyword("decltype") << punctuation("(") << bracket_ws << keyword("auto") << bracket_ws
<< bracket_ws << punctuation(")"); << punctuation(")");
} }
void write_cv_qualified_prefix(code_generator::output& output, void write_cv_qualified_prefix(code_generator::output& output, const cpp_cv_qualified_type& type)
const cpp_cv_qualified_type& type) {
{ detail::write_type_prefix(output, type.type());
detail::write_type_prefix(output, type.type());
if (is_direct_complex(type.type())) if (is_direct_complex(type.type()))
output << punctuation("(") << bracket_ws;
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 << bracket_ws << 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("(") << bracket_ws;
else if (output.formatting().is_set(formatting_flags::ptr_ref_var))
output << whitespace;
output << punctuation("*");
}
void write_pointer_suffix(code_generator::output& output, const cpp_pointer_type& type)
{
if (pointer_requires_paren(type))
output << bracket_ws << 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("(") << bracket_ws;
else if (output.formatting().is_set(formatting_flags::ptr_ref_var))
output << whitespace;
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 << bracket_ws << 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())
{
output << bracket_ws;
detail::write_expression(output, type.size().value());
output << bracket_ws;
}
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("(") << bracket_ws; output << punctuation("(") << bracket_ws;
auto need_sep = false; if (is_const(type.cv_qualifier()))
for (auto& param : type.parameter_types()) output << whitespace << keyword("const");
{ if (is_volatile(type.cv_qualifier()))
if (need_sep) output << whitespace << keyword("volatile");
output << comma; }
else
need_sep = true;
detail::write_type_prefix(output, param);
detail::write_type_suffix(output, param);
}
if (type.is_variadic())
{
if (need_sep)
output << comma;
output << punctuation("...");
}
void write_cv_qualified_suffix(code_generator::output& output, const cpp_cv_qualified_type& type)
{
if (is_direct_complex(type.type()))
output << bracket_ws << punctuation(")"); output << bracket_ws << punctuation(")");
} detail::write_type_suffix(output, type.type());
}
void write_function_suffix(code_generator::output& output, const cpp_function_type& type) bool pointer_requires_paren(const cpp_pointer_type& type)
{ {
write_parameters(output, type); auto kind = type.pointee().kind();
return kind == cpp_type_kind::function_t || kind == cpp_type_kind::array_t;
}
detail::write_type_suffix(output, type.return_type()); void write_pointer_prefix(code_generator::output& output, const cpp_pointer_type& type)
} {
detail::write_type_prefix(output, type.pointee());
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());
if (pointer_requires_paren(type))
output << punctuation("(") << bracket_ws; output << punctuation("(") << bracket_ws;
detail::write_type_prefix(output, strip_class_type(type.class_type(), nullptr, nullptr)); else if (output.formatting().is_set(formatting_flags::ptr_ref_var))
output << punctuation("::"); output << whitespace;
}
void write_member_function_suffix(code_generator::output& output, output << punctuation("*");
const cpp_member_function_type& type) }
{
void write_pointer_suffix(code_generator::output& output, const cpp_pointer_type& type)
{
if (pointer_requires_paren(type))
output << bracket_ws << punctuation(")"); output << bracket_ws << punctuation(")");
write_parameters(output, type); detail::write_type_suffix(output, type.pointee());
}
auto cv = cpp_cv_none; void write_reference_prefix(code_generator::output& output, const cpp_reference_type& type)
auto ref = cpp_ref_none; {
strip_class_type(type.class_type(), &cv, &ref); detail::write_type_prefix(output, type.referee());
if (cv == cpp_cv_const_volatile) if (is_direct_complex(type.referee()))
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 << operator_ws << punctuation("&") << operator_ws;
else if (ref == cpp_ref_rvalue)
output << operator_ws << punctuation("&&") << operator_ws;
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("(") << bracket_ws; output << punctuation("(") << bracket_ws;
DEBUG_ASSERT(!detail::is_complex_type(type.class_type()), detail::assert_handler{}); else if (output.formatting().is_set(formatting_flags::ptr_ref_var))
detail::write_type_prefix(output, type.class_type()); output << whitespace;
output << punctuation("::");
}
void write_member_object_suffix(code_generator::output& output, const cpp_member_object_type&) 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 << bracket_ws << punctuation(")"); output << bracket_ws << punctuation(")");
} detail::write_type_suffix(output, type.referee());
}
void write_template_parameter(code_generator::output& output, void write_array_prefix(code_generator::output& output, const cpp_array_type& type)
const cpp_template_parameter_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())
{ {
output << type.entity(); output << bracket_ws;
detail::write_expression(output, type.size().value());
output << bracket_ws;
} }
output << punctuation("]");
detail::write_type_suffix(output, type.value_type());
}
void write_template_instantiation(code_generator::output& output, void write_function_prefix(code_generator::output& output, const cpp_function_type& type)
const cpp_template_instantiation_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("(") << bracket_ws;
auto need_sep = false;
for (auto& param : type.parameter_types())
{ {
output << type.primary_template(); if (need_sep)
if (output.was_reference_excluded()) output << comma;
return;
if (type.arguments_exposed())
detail::write_template_arguments(output, type.arguments());
else else
output << punctuation("<") << bracket_ws << token_seq(type.unexposed_arguments()) need_sep = true;
<< bracket_ws << punctuation(">"); detail::write_type_prefix(output, param);
detail::write_type_suffix(output, param);
}
if (type.is_variadic())
{
if (need_sep)
output << comma;
output << punctuation("...");
} }
void write_dependent(code_generator::output& output, const cpp_dependent_type& type) output << bracket_ws << punctuation(")");
{ }
output << token_seq(type.name());
}
void write_unexposed(code_generator::output& output, const cpp_unexposed_type& type) 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)
{ {
output << token_seq(type.name()); 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("(") << bracket_ws;
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 << bracket_ws << 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 << operator_ws << punctuation("&") << operator_ws;
else if (ref == cpp_ref_rvalue)
output << operator_ws << punctuation("&&") << operator_ws;
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("(") << bracket_ws;
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 << bracket_ws << 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 (output.was_reference_excluded())
return;
if (type.arguments_exposed())
detail::write_template_arguments(output, type.arguments());
else
output << punctuation("<") << bracket_ws << token_seq(type.unexposed_arguments())
<< bracket_ws << 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());
}
} // namespace } // namespace
void detail::write_type_prefix(code_generator::output& output, const cpp_type& type) void detail::write_type_prefix(code_generator::output& output, const cpp_type& type)

View file

@ -2,8 +2,8 @@
// This file is subject to the license terms in the LICENSE file // This file is subject to the license terms in the LICENSE file
// found in the top-level directory of this distribution. // found in the top-level directory of this distribution.
#include <cppast/cpp_class.hpp>
#include <clang-c/Index.h> #include <clang-c/Index.h>
#include <cppast/cpp_class.hpp>
#include "libclang_visitor.hpp" #include "libclang_visitor.hpp"
#include "parse_functions.hpp" #include "parse_functions.hpp"
@ -12,104 +12,104 @@ using namespace cppast;
namespace namespace
{ {
cpp_class_kind parse_class_kind(detail::cxtoken_stream& stream) cpp_class_kind parse_class_kind(detail::cxtoken_stream& stream)
{
auto kind = clang_getTemplateCursorKind(stream.cursor());
if (kind == CXCursor_NoDeclFound)
kind = clang_getCursorKind(stream.cursor());
if (detail::skip_if(stream, "template"))
// skip template parameters
detail::skip_brackets(stream);
detail::skip_if(stream, "friend");
if (detail::skip_if(stream, "extern"))
// extern template
detail::skip(stream, "template");
switch (kind)
{ {
auto kind = clang_getTemplateCursorKind(stream.cursor()); case CXCursor_ClassDecl:
if (kind == CXCursor_NoDeclFound) detail::skip(stream, "class");
kind = clang_getCursorKind(stream.cursor());
if (detail::skip_if(stream, "template"))
// skip template parameters
detail::skip_brackets(stream);
detail::skip_if(stream, "friend");
if (detail::skip_if(stream, "extern"))
// extern template
detail::skip(stream, "template");
switch (kind)
{
case CXCursor_ClassDecl:
detail::skip(stream, "class");
return cpp_class_kind::class_t;
case CXCursor_StructDecl:
detail::skip(stream, "struct");
return cpp_class_kind::struct_t;
case CXCursor_UnionDecl:
detail::skip(stream, "union");
return cpp_class_kind::union_t;
default:
break;
}
DEBUG_UNREACHABLE(detail::assert_handler{});
return cpp_class_kind::class_t; return cpp_class_kind::class_t;
case CXCursor_StructDecl:
detail::skip(stream, "struct");
return cpp_class_kind::struct_t;
case CXCursor_UnionDecl:
detail::skip(stream, "union");
return cpp_class_kind::union_t;
default:
break;
} }
DEBUG_UNREACHABLE(detail::assert_handler{});
cpp_class::builder make_class_builder(const detail::parse_context& context, const CXCursor& cur) return cpp_class_kind::class_t;
{
detail::cxtokenizer tokenizer(context.tu, context.file, cur);
detail::cxtoken_stream stream(tokenizer, cur);
auto kind = parse_class_kind(stream);
auto attributes = detail::parse_attributes(stream);
auto name = detail::get_cursor_name(cur);
auto result = cpp_class::builder(name.c_str(), kind);
result.get().add_attribute(attributes);
return result;
}
cpp_access_specifier_kind convert_access(const CXCursor& cur)
{
switch (clang_getCXXAccessSpecifier(cur))
{
case CX_CXXInvalidAccessSpecifier:
break;
case CX_CXXPublic:
return cpp_public;
case CX_CXXProtected:
return cpp_protected;
case CX_CXXPrivate:
return cpp_private;
}
DEBUG_UNREACHABLE(detail::assert_handler{});
return cpp_public;
}
void add_access_specifier(cpp_class::builder& builder, const CXCursor& cur)
{
DEBUG_ASSERT(cur.kind == CXCursor_CXXAccessSpecifier, detail::assert_handler{});
builder.access_specifier(convert_access(cur));
}
void add_base_class(cpp_class::builder& builder, const detail::parse_context& context,
const CXCursor& cur, const CXCursor& class_cur)
{
DEBUG_ASSERT(cur.kind == CXCursor_CXXBaseSpecifier, detail::assert_handler{});
auto access = convert_access(cur);
auto is_virtual = clang_isVirtualBase(cur) != 0u;
detail::cxtokenizer tokenizer(context.tu, context.file, cur);
detail::cxtoken_stream stream(tokenizer, cur);
// [<attribute>] [virtual] [<access>] <name>
// can't use spelling to get the name
auto attributes = detail::parse_attributes(stream);
if (is_virtual)
detail::skip(stream, "virtual");
detail::skip_if(stream, to_string(access));
auto name = detail::to_string(stream, stream.end()).as_string();
auto type = detail::parse_type(context, class_cur, clang_getCursorType(cur));
auto& base = builder.base_class(std::move(name), std::move(type), access, is_virtual);
base.add_attribute(attributes);
}
} }
cpp_class::builder make_class_builder(const detail::parse_context& context, const CXCursor& cur)
{
detail::cxtokenizer tokenizer(context.tu, context.file, cur);
detail::cxtoken_stream stream(tokenizer, cur);
auto kind = parse_class_kind(stream);
auto attributes = detail::parse_attributes(stream);
auto name = detail::get_cursor_name(cur);
auto result = cpp_class::builder(name.c_str(), kind);
result.get().add_attribute(attributes);
return result;
}
cpp_access_specifier_kind convert_access(const CXCursor& cur)
{
switch (clang_getCXXAccessSpecifier(cur))
{
case CX_CXXInvalidAccessSpecifier:
break;
case CX_CXXPublic:
return cpp_public;
case CX_CXXProtected:
return cpp_protected;
case CX_CXXPrivate:
return cpp_private;
}
DEBUG_UNREACHABLE(detail::assert_handler{});
return cpp_public;
}
void add_access_specifier(cpp_class::builder& builder, const CXCursor& cur)
{
DEBUG_ASSERT(cur.kind == CXCursor_CXXAccessSpecifier, detail::assert_handler{});
builder.access_specifier(convert_access(cur));
}
void add_base_class(cpp_class::builder& builder, const detail::parse_context& context,
const CXCursor& cur, const CXCursor& class_cur)
{
DEBUG_ASSERT(cur.kind == CXCursor_CXXBaseSpecifier, detail::assert_handler{});
auto access = convert_access(cur);
auto is_virtual = clang_isVirtualBase(cur) != 0u;
detail::cxtokenizer tokenizer(context.tu, context.file, cur);
detail::cxtoken_stream stream(tokenizer, cur);
// [<attribute>] [virtual] [<access>] <name>
// can't use spelling to get the name
auto attributes = detail::parse_attributes(stream);
if (is_virtual)
detail::skip(stream, "virtual");
detail::skip_if(stream, to_string(access));
auto name = detail::to_string(stream, stream.end()).as_string();
auto type = detail::parse_type(context, class_cur, clang_getCursorType(cur));
auto& base = builder.base_class(std::move(name), std::move(type), access, is_virtual);
base.add_attribute(attributes);
}
} // namespace
std::unique_ptr<cpp_entity> detail::parse_cpp_class(const detail::parse_context& context, std::unique_ptr<cpp_entity> detail::parse_cpp_class(const detail::parse_context& context,
const CXCursor& cur, const CXCursor& parent_cur) const CXCursor& cur, const CXCursor& parent_cur)
{ {
@ -145,9 +145,9 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_class(const detail::parse_context&
stream.bump(); stream.bump();
} }
if (!scope.empty()) if (!scope.empty())
semantic_parent = semantic_parent
cpp_entity_ref(detail::get_entity_id(clang_getCursorSemanticParent(cur)), = cpp_entity_ref(detail::get_entity_id(clang_getCursorSemanticParent(cur)),
std::move(scope)); std::move(scope));
} }
context.comments.match(builder.get(), cur); context.comments.match(builder.get(), cur);
@ -159,12 +159,12 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_class(const detail::parse_context&
add_base_class(builder, context, child, cur); add_base_class(builder, context, child, cur);
else if (kind == CXCursor_CXXFinalAttr) else if (kind == CXCursor_CXXFinalAttr)
builder.is_final(); builder.is_final();
else if ( else if (kind == CXCursor_TemplateTypeParameter
kind == CXCursor_TemplateTypeParameter || kind == CXCursor_NonTypeTemplateParameter || kind == CXCursor_NonTypeTemplateParameter
|| kind == CXCursor_TemplateTemplateParameter || kind == CXCursor_ParmDecl || kind == CXCursor_TemplateTemplateParameter || kind == CXCursor_ParmDecl
|| clang_isExpression(kind) || clang_isReference(kind) || clang_isExpression(kind) || clang_isReference(kind)
|| kind || kind == CXCursor_UnexposedAttr) // I have no idea what this is, but happens
== CXCursor_UnexposedAttr) // I have no idea what this is, but happens on Windows // on Windows
// other children due to templates and stuff // other children due to templates and stuff
return; return;
else if (auto entity = parse_entity(context, &builder.get(), child)) else if (auto entity = parse_entity(context, &builder.get(), child))
@ -173,10 +173,10 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_class(const detail::parse_context&
} }
if (!is_friend && clang_isCursorDefinition(cur)) if (!is_friend && clang_isCursorDefinition(cur))
return is_templated ? return is_templated
builder.finish(std::move(semantic_parent)) : ? builder.finish(std::move(semantic_parent))
builder.finish(*context.idx, get_entity_id(cur), std::move(semantic_parent)); : builder.finish(*context.idx, get_entity_id(cur), std::move(semantic_parent));
else else
return is_templated ? builder.finish_declaration(detail::get_entity_id(cur)) : return is_templated ? builder.finish_declaration(detail::get_entity_id(cur))
builder.finish_declaration(*context.idx, get_entity_id(cur)); : builder.finish_declaration(*context.idx, get_entity_id(cur));
} }

View file

@ -13,290 +13,282 @@ using namespace cppast;
detail::cxtoken::cxtoken(const CXTranslationUnit& tu_unit, const CXToken& token) detail::cxtoken::cxtoken(const CXTranslationUnit& tu_unit, const CXToken& token)
: value_(clang_getTokenSpelling(tu_unit, token)), kind_(clang_getTokenKind(token)) : value_(clang_getTokenSpelling(tu_unit, token)), kind_(clang_getTokenKind(token))
{ {}
}
namespace namespace
{ {
bool cursor_is_function(CXCursorKind kind) bool cursor_is_function(CXCursorKind kind)
{
return kind == CXCursor_FunctionDecl || kind == CXCursor_CXXMethod
|| kind == CXCursor_Constructor || kind == CXCursor_Destructor
|| kind == CXCursor_ConversionFunction;
}
CXSourceLocation get_next_location(const CXTranslationUnit& tu, CXFile file,
const CXSourceLocation& loc, int inc = 1)
{
unsigned offset;
clang_getSpellingLocation(loc, nullptr, nullptr, nullptr, &offset);
if (inc >= 0)
offset += unsigned(inc);
else
offset -= unsigned(-inc);
return clang_getLocationForOffset(tu, file, offset);
}
class simple_tokenizer
{
public:
explicit simple_tokenizer(const CXTranslationUnit& tu, const CXSourceRange& range) : tu_(tu)
{ {
return kind == CXCursor_FunctionDecl || kind == CXCursor_CXXMethod clang_tokenize(tu, range, &tokens_, &no_);
|| kind == CXCursor_Constructor || kind == CXCursor_Destructor
|| kind == CXCursor_ConversionFunction;
} }
CXSourceLocation get_next_location(const CXTranslationUnit& tu, CXFile file, ~simple_tokenizer()
const CXSourceLocation& loc, int inc = 1)
{ {
unsigned offset; clang_disposeTokens(tu_, tokens_, no_);
clang_getSpellingLocation(loc, nullptr, nullptr, nullptr, &offset);
if (inc >= 0)
offset += unsigned(inc);
else
offset -= unsigned(-inc);
return clang_getLocationForOffset(tu, file, offset);
} }
class simple_tokenizer simple_tokenizer(const simple_tokenizer&) = delete;
simple_tokenizer& operator=(const simple_tokenizer&) = delete;
unsigned size() const noexcept
{ {
public: return no_;
explicit simple_tokenizer(const CXTranslationUnit& tu, const CXSourceRange& range) : tu_(tu)
{
clang_tokenize(tu, range, &tokens_, &no_);
}
~simple_tokenizer()
{
clang_disposeTokens(tu_, tokens_, no_);
}
simple_tokenizer(const simple_tokenizer&) = delete;
simple_tokenizer& operator=(const simple_tokenizer&) = delete;
unsigned size() const noexcept
{
return no_;
}
const CXToken& operator[](unsigned i) const noexcept
{
return tokens_[i];
}
private:
CXTranslationUnit tu_;
CXToken* tokens_;
unsigned no_;
};
bool token_after_is(const CXTranslationUnit& tu, const CXFile& file,
const CXSourceLocation& loc, const char* token_str, int inc)
{
auto loc_after = get_next_location(tu, file, loc, inc);
if (!clang_Location_isFromMainFile(loc_after))
return false;
simple_tokenizer tokenizer(tu, inc > 0 ? clang_getRange(loc, loc_after) :
clang_getRange(loc_after, loc));
if (tokenizer.size() == 0u)
return false;
detail::cxstring spelling(clang_getTokenSpelling(tu, tokenizer[0u]));
return spelling == token_str;
} }
// clang_getCursorExtent() is somehow broken in various ways const CXToken& operator[](unsigned i) const noexcept
// this function returns the actual CXSourceRange that covers all parts required for parsing
// might include more tokens
// this function is the reason you shouldn't use libclang
CXSourceRange get_extent(const CXTranslationUnit& tu, const CXFile& file, const CXCursor& cur,
bool& unmunch)
{ {
unmunch = false; return tokens_[i];
}
auto extent = clang_getCursorExtent(cur); private:
auto begin = clang_getRangeStart(extent); CXTranslationUnit tu_;
auto end = clang_getRangeEnd(extent); CXToken* tokens_;
unsigned no_;
};
auto kind = clang_getCursorKind(cur); bool token_after_is(const CXTranslationUnit& tu, const CXFile& file, const CXSourceLocation& loc,
if (cursor_is_function(kind) || cursor_is_function(clang_getTemplateCursorKind(cur)) const char* token_str, int inc)
|| kind == CXCursor_VarDecl || kind == CXCursor_FieldDecl || kind == CXCursor_ParmDecl {
|| kind == CXCursor_NonTypeTemplateParameter) auto loc_after = get_next_location(tu, file, loc, inc);
if (!clang_Location_isFromMainFile(loc_after))
return false;
simple_tokenizer tokenizer(tu, inc > 0 ? clang_getRange(loc, loc_after)
: clang_getRange(loc_after, loc));
if (tokenizer.size() == 0u)
return false;
detail::cxstring spelling(clang_getTokenSpelling(tu, tokenizer[0u]));
return spelling == token_str;
}
// clang_getCursorExtent() is somehow broken in various ways
// this function returns the actual CXSourceRange that covers all parts required for parsing
// might include more tokens
// this function is the reason you shouldn't use libclang
CXSourceRange get_extent(const CXTranslationUnit& tu, const CXFile& file, const CXCursor& cur,
bool& unmunch)
{
unmunch = false;
auto extent = clang_getCursorExtent(cur);
auto begin = clang_getRangeStart(extent);
auto end = clang_getRangeEnd(extent);
auto kind = clang_getCursorKind(cur);
if (cursor_is_function(kind) || cursor_is_function(clang_getTemplateCursorKind(cur))
|| kind == CXCursor_VarDecl || kind == CXCursor_FieldDecl || kind == CXCursor_ParmDecl
|| kind == CXCursor_NonTypeTemplateParameter)
{
if (token_after_is(tu, file, begin, "]", -2) && token_after_is(tu, file, begin, "]", -3))
{ {
if (token_after_is(tu, file, begin, "]", -2) while (!token_after_is(tu, file, begin, "[", -1)
&& token_after_is(tu, file, begin, "]", -3)) && !token_after_is(tu, file, begin, "[", -2))
{
while (!token_after_is(tu, file, begin, "[", -1)
&& !token_after_is(tu, file, begin, "[", -2))
begin = get_next_location(tu, file, begin, -1);
begin = get_next_location(tu, file, begin, -3);
DEBUG_ASSERT(token_after_is(tu, file, begin, "[", 0)
&& token_after_is(tu, file, get_next_location(tu, file, begin),
"[", 0),
detail::parse_error_handler{}, cur,
"error in pre-function attribute parsing");
}
else if (token_after_is(tu, file, begin, ")", -2))
{
// maybe alignas specifier
auto save_begin = begin;
auto paren_count = 1;
begin = get_next_location(tu, file, begin, -1);
for (auto last_begin = begin; paren_count != 0; last_begin = begin)
{
begin = get_next_location(tu, file, begin, -1);
if (token_after_is(tu, file, begin, "(", -1))
--paren_count;
else if (token_after_is(tu, file, begin, ")", -1))
++paren_count;
DEBUG_ASSERT(!clang_equalLocations(last_begin, begin),
detail::parse_error_handler{}, cur,
"infinite loop in alignas parsing");
}
begin = get_next_location(tu, file, begin, -(int(std::strlen("alignas")) + 1));
if (token_after_is(tu, file, begin, "alignas", 0))
begin = get_next_location(tu, file, begin, -1);
else
begin = save_begin;
}
}
if (cursor_is_function(kind) || cursor_is_function(clang_getTemplateCursorKind(cur)))
{
auto is_definition = false;
// if a function we need to remove the body
// it does not need to be parsed
detail::visit_children(cur, [&](const CXCursor& child) {
if (clang_getCursorKind(child) == CXCursor_CompoundStmt
|| clang_getCursorKind(child) == CXCursor_CXXTryStmt
|| clang_getCursorKind(child) == CXCursor_InitListExpr)
{
auto child_extent = clang_getCursorExtent(child);
end = clang_getRangeStart(child_extent);
is_definition = true;
}
});
if (!is_definition)
{
// i have no idea why this is necessary
is_definition = token_after_is(tu, file, end, "{", 0)
|| token_after_is(tu, file, end, "try", 0)
|| token_after_is(tu, file, end, ":", 0);
if (is_definition)
// need to extend range here to include the token
end = get_next_location(tu, file, end);
}
if (!is_definition && !token_after_is(tu, file, end, ";", 0))
{
// we do not have a body, but it is not a declaration either
do
{
end = get_next_location(tu, file, end);
} while (!token_after_is(tu, file, end, ";", 0));
}
else if (kind == CXCursor_CXXMethod)
// necessary for some reason
begin = get_next_location(tu, file, begin, -1); begin = get_next_location(tu, file, begin, -1);
else if (kind == CXCursor_Destructor && token_after_is(tu, file, end, ")", 0))
// necessary for some other reason begin = get_next_location(tu, file, begin, -3);
end = get_next_location(tu, file, end); DEBUG_ASSERT(token_after_is(tu, file, begin, "[", 0)
&& token_after_is(tu, file, get_next_location(tu, file, begin), "[",
0),
detail::parse_error_handler{}, cur,
"error in pre-function attribute parsing");
} }
else if (kind == CXCursor_TemplateTypeParameter && token_after_is(tu, file, end, "(", 0)) else if (token_after_is(tu, file, begin, ")", -2))
{ {
// if you have decltype as default argument for a type template parameter // maybe alignas specifier
// libclang doesn't include the parameters auto save_begin = begin;
auto next = get_next_location(tu, file, end);
auto prev = end; auto paren_count = 1;
for (auto paren_count = 1; paren_count != 0; next = get_next_location(tu, file, next)) begin = get_next_location(tu, file, begin, -1);
for (auto last_begin = begin; paren_count != 0; last_begin = begin)
{ {
if (token_after_is(tu, file, next, "(", 0)) begin = get_next_location(tu, file, begin, -1);
++paren_count; if (token_after_is(tu, file, begin, "(", -1))
else if (token_after_is(tu, file, next, ")", 0))
--paren_count; --paren_count;
prev = next; else if (token_after_is(tu, file, begin, ")", -1))
++paren_count;
DEBUG_ASSERT(!clang_equalLocations(last_begin, begin),
detail::parse_error_handler{}, cur,
"infinite loop in alignas parsing");
} }
#if CINDEX_VERSION_MINOR < 37 begin = get_next_location(tu, file, begin, -(int(std::strlen("alignas")) + 1));
end = prev;
#else
end = next;
#endif
}
else if (kind == CXCursor_TemplateTemplateParameter
&& token_after_is(tu, file, end, "<", 0))
{
// if you have a template template parameter in a template template parameter,
// the tokens are all messed up, only contain the `template`
// first: skip to closing angle bracket if (token_after_is(tu, file, begin, "alignas", 0))
// luckily no need to handle expressions here begin = get_next_location(tu, file, begin, -1);
auto next = get_next_location(tu, file, end, 2); else
for (auto angle_count = 1; angle_count != 0; next = get_next_location(tu, file, next)) begin = save_begin;
{
if (token_after_is(tu, file, next, ">", 0))
--angle_count;
else if (token_after_is(tu, file, next, ">>", 0))
angle_count -= 2;
else if (token_after_is(tu, file, next, "<", 0))
++angle_count;
}
// second: skip until end of parameter
// no need to handle default, so look for '>' or ','
while (!token_after_is(tu, file, next, ">", 0)
&& !token_after_is(tu, file, next, ",", 0))
next = get_next_location(tu, file, next);
// now we found the proper end of the token
end = get_next_location(tu, file, next, -1);
} }
else if ((kind == CXCursor_TemplateTypeParameter
|| kind == CXCursor_NonTypeTemplateParameter
|| kind == CXCursor_TemplateTemplateParameter)
&& token_after_is(tu, file, end, "...", 0))
{
// variadic tokens in unnamed parameter not included
end = get_next_location(tu, file, end, 3);
if (token_after_is(tu, file, end, ".", 0))
// extra whitespace, so bump again
// this should all go away once I redid the whole token thing...
end = get_next_location(tu, file, end, 1);
DEBUG_ASSERT(token_after_is(tu, file, end, ">", 0)
|| token_after_is(tu, file, end, ",", 0),
detail::parse_error_handler{}, cur,
"unexpected token in variadic parameter workaround");
}
else if ((kind == CXCursor_TemplateTypeParameter
|| kind == CXCursor_NonTypeTemplateParameter
|| kind == CXCursor_TemplateTemplateParameter)
&& !token_after_is(tu, file, end, ">", 0)
&& !token_after_is(tu, file, end, ",", 0))
{
DEBUG_ASSERT(token_after_is(tu, file, get_next_location(tu, file, end, -2), ">>", 0),
detail::parse_error_handler{}, cur,
"unexpected token in maximal munch workaround");
unmunch = true;
// need to shrink range anyway
end = get_next_location(tu, file, end, -1);
}
else if (kind == CXCursor_EnumDecl && !token_after_is(tu, file, end, ";", 0))
{
while (!token_after_is(tu, file, end, ";", 0))
end = get_next_location(tu, file, end);
}
else if (kind == CXCursor_EnumConstantDecl && !token_after_is(tu, file, end, ",", 0))
{
// need to support attributes
// just give up and extend the range to the range of the entire enum...
auto parent = clang_getCursorLexicalParent(cur);
end = clang_getRangeEnd(clang_getCursorExtent(parent));
}
else if (kind == CXCursor_ParmDecl && !token_after_is(tu, file, end, "]", -1))
// need to shrink range by one
end = get_next_location(tu, file, end, -1);
else if (kind == CXCursor_FieldDecl || kind == CXCursor_NonTypeTemplateParameter
|| kind == CXCursor_TemplateTemplateParameter
#if CINDEX_VERSION_MINOR < 37
|| clang_isExpression(kind) || kind == CXCursor_CXXBaseSpecifier
|| kind == CXCursor_TemplateTypeParameter
#endif
)
// need to shrink range by one
end = get_next_location(tu, file, end, -1);
else if (kind == CXCursor_UnexposedDecl)
{
// include semicolon, if necessary
if (token_after_is(tu, file, end, ";", 0))
end = get_next_location(tu, file, end);
}
return clang_getRange(begin, end);
} }
if (cursor_is_function(kind) || cursor_is_function(clang_getTemplateCursorKind(cur)))
{
auto is_definition = false;
// if a function we need to remove the body
// it does not need to be parsed
detail::visit_children(cur, [&](const CXCursor& child) {
if (clang_getCursorKind(child) == CXCursor_CompoundStmt
|| clang_getCursorKind(child) == CXCursor_CXXTryStmt
|| clang_getCursorKind(child) == CXCursor_InitListExpr)
{
auto child_extent = clang_getCursorExtent(child);
end = clang_getRangeStart(child_extent);
is_definition = true;
}
});
if (!is_definition)
{
// i have no idea why this is necessary
is_definition = token_after_is(tu, file, end, "{", 0)
|| token_after_is(tu, file, end, "try", 0)
|| token_after_is(tu, file, end, ":", 0);
if (is_definition)
// need to extend range here to include the token
end = get_next_location(tu, file, end);
}
if (!is_definition && !token_after_is(tu, file, end, ";", 0))
{
// we do not have a body, but it is not a declaration either
do
{
end = get_next_location(tu, file, end);
} while (!token_after_is(tu, file, end, ";", 0));
}
else if (kind == CXCursor_CXXMethod)
// necessary for some reason
begin = get_next_location(tu, file, begin, -1);
else if (kind == CXCursor_Destructor && token_after_is(tu, file, end, ")", 0))
// necessary for some other reason
end = get_next_location(tu, file, end);
}
else if (kind == CXCursor_TemplateTypeParameter && token_after_is(tu, file, end, "(", 0))
{
// if you have decltype as default argument for a type template parameter
// libclang doesn't include the parameters
auto next = get_next_location(tu, file, end);
auto prev = end;
for (auto paren_count = 1; paren_count != 0; next = get_next_location(tu, file, next))
{
if (token_after_is(tu, file, next, "(", 0))
++paren_count;
else if (token_after_is(tu, file, next, ")", 0))
--paren_count;
prev = next;
}
#if CINDEX_VERSION_MINOR < 37
end = prev;
#else
end = next;
#endif
}
else if (kind == CXCursor_TemplateTemplateParameter && token_after_is(tu, file, end, "<", 0))
{
// if you have a template template parameter in a template template parameter,
// the tokens are all messed up, only contain the `template`
// first: skip to closing angle bracket
// luckily no need to handle expressions here
auto next = get_next_location(tu, file, end, 2);
for (auto angle_count = 1; angle_count != 0; next = get_next_location(tu, file, next))
{
if (token_after_is(tu, file, next, ">", 0))
--angle_count;
else if (token_after_is(tu, file, next, ">>", 0))
angle_count -= 2;
else if (token_after_is(tu, file, next, "<", 0))
++angle_count;
}
// second: skip until end of parameter
// no need to handle default, so look for '>' or ','
while (!token_after_is(tu, file, next, ">", 0) && !token_after_is(tu, file, next, ",", 0))
next = get_next_location(tu, file, next);
// now we found the proper end of the token
end = get_next_location(tu, file, next, -1);
}
else if ((kind == CXCursor_TemplateTypeParameter || kind == CXCursor_NonTypeTemplateParameter
|| kind == CXCursor_TemplateTemplateParameter)
&& token_after_is(tu, file, end, "...", 0))
{
// variadic tokens in unnamed parameter not included
end = get_next_location(tu, file, end, 3);
if (token_after_is(tu, file, end, ".", 0))
// extra whitespace, so bump again
// this should all go away once I redid the whole token thing...
end = get_next_location(tu, file, end, 1);
DEBUG_ASSERT(token_after_is(tu, file, end, ">", 0) || token_after_is(tu, file, end, ",", 0),
detail::parse_error_handler{}, cur,
"unexpected token in variadic parameter workaround");
}
else if ((kind == CXCursor_TemplateTypeParameter || kind == CXCursor_NonTypeTemplateParameter
|| kind == CXCursor_TemplateTemplateParameter)
&& !token_after_is(tu, file, end, ">", 0) && !token_after_is(tu, file, end, ",", 0))
{
DEBUG_ASSERT(token_after_is(tu, file, get_next_location(tu, file, end, -2), ">>", 0),
detail::parse_error_handler{}, cur,
"unexpected token in maximal munch workaround");
unmunch = true;
// need to shrink range anyway
end = get_next_location(tu, file, end, -1);
}
else if (kind == CXCursor_EnumDecl && !token_after_is(tu, file, end, ";", 0))
{
while (!token_after_is(tu, file, end, ";", 0))
end = get_next_location(tu, file, end);
}
else if (kind == CXCursor_EnumConstantDecl && !token_after_is(tu, file, end, ",", 0))
{
// need to support attributes
// just give up and extend the range to the range of the entire enum...
auto parent = clang_getCursorLexicalParent(cur);
end = clang_getRangeEnd(clang_getCursorExtent(parent));
}
else if (kind == CXCursor_ParmDecl && !token_after_is(tu, file, end, "]", -1))
// need to shrink range by one
end = get_next_location(tu, file, end, -1);
else if (kind == CXCursor_FieldDecl || kind == CXCursor_NonTypeTemplateParameter
|| kind == CXCursor_TemplateTemplateParameter
#if CINDEX_VERSION_MINOR < 37
|| clang_isExpression(kind) || kind == CXCursor_CXXBaseSpecifier
|| kind == CXCursor_TemplateTypeParameter
#endif
)
// need to shrink range by one
end = get_next_location(tu, file, end, -1);
else if (kind == CXCursor_UnexposedDecl)
{
// include semicolon, if necessary
if (token_after_is(tu, file, end, ";", 0))
end = get_next_location(tu, file, end);
}
return clang_getRange(begin, end);
}
} // namespace } // namespace
detail::cxtokenizer::cxtokenizer(const CXTranslationUnit& tu, const CXFile& file, detail::cxtokenizer::cxtokenizer(const CXTranslationUnit& tu, const CXFile& file,
@ -326,15 +318,15 @@ void detail::skip(detail::cxtoken_stream& stream, const char* str)
namespace namespace
{ {
bool starts_with(const char*& str, const detail::cxtoken& t) bool starts_with(const char*& str, const detail::cxtoken& t)
{ {
if (std::strncmp(str, t.c_str(), t.value().length()) != 0) if (std::strncmp(str, t.c_str(), t.value().length()) != 0)
return false; return false;
str += t.value().length(); str += t.value().length();
while (*str == ' ' || *str == '\t') while (*str == ' ' || *str == '\t')
++str; ++str;
return true; return true;
} }
} // namespace } // namespace
bool detail::skip_if(detail::cxtoken_stream& stream, const char* str, bool multi_token) bool detail::skip_if(detail::cxtoken_stream& stream, const char* str, bool multi_token)
@ -359,16 +351,16 @@ bool detail::skip_if(detail::cxtoken_stream& stream, const char* str, bool multi
namespace namespace
{ {
// whether or not the current angle bracket can be a comparison // whether or not the current angle bracket can be a comparison
// note: this is a heuristic I hope works often enough // note: this is a heuristic I hope works often enough
bool is_comparison(CXTokenKind last_kind, const detail::cxtoken& cur, CXTokenKind next_kind) bool is_comparison(CXTokenKind last_kind, const detail::cxtoken& cur, CXTokenKind next_kind)
{ {
if (cur == "<") if (cur == "<")
return last_kind == CXToken_Literal; return last_kind == CXToken_Literal;
else if (cur == ">") else if (cur == ">")
return next_kind == CXToken_Literal; return next_kind == CXToken_Literal;
return false; return false;
} }
} // namespace } // namespace
detail::cxtoken_iterator detail::find_closing_bracket(detail::cxtoken_stream stream) detail::cxtoken_iterator detail::find_closing_bracket(detail::cxtoken_stream stream)
@ -430,161 +422,160 @@ void detail::skip_brackets(detail::cxtoken_stream& stream)
namespace namespace
{ {
type_safe::optional<std::string> parse_attribute_using(detail::cxtoken_stream& stream) type_safe::optional<std::string> parse_attribute_using(detail::cxtoken_stream& stream)
{
// using identifier :
if (skip_if(stream, "using"))
{ {
// using identifier : DEBUG_ASSERT(stream.peek().kind() == CXToken_Identifier, detail::parse_error_handler{},
if (skip_if(stream, "using")) stream.cursor(), "expected identifier");
{ auto scope = stream.get().value().std_str();
DEBUG_ASSERT(stream.peek().kind() == CXToken_Identifier, detail::parse_error_handler{}, skip(stream, ":");
stream.cursor(), "expected identifier");
auto scope = stream.get().value().std_str();
skip(stream, ":");
return scope; return scope;
}
else
return type_safe::nullopt;
} }
else
return type_safe::nullopt;
}
cpp_attribute_kind get_attribute_kind(const std::string& name) cpp_attribute_kind get_attribute_kind(const std::string& name)
{
if (name == "carries_dependency")
return cpp_attribute_kind::carries_dependency;
else if (name == "deprecated")
return cpp_attribute_kind::deprecated;
else if (name == "fallthrough")
return cpp_attribute_kind::fallthrough;
else if (name == "maybe_unused")
return cpp_attribute_kind::maybe_unused;
else if (name == "nodiscard")
return cpp_attribute_kind::nodiscard;
else if (name == "noreturn")
return cpp_attribute_kind::noreturn;
else
return cpp_attribute_kind::unknown;
}
cpp_token_string parse_attribute_arguments(detail::cxtoken_stream& stream)
{
auto end = find_closing_bracket(stream);
skip(stream, "(");
auto arguments = detail::to_string(stream, end);
stream.set_cur(end);
skip(stream, ")");
return arguments;
}
cpp_attribute parse_attribute_token(detail::cxtoken_stream& stream,
type_safe::optional<std::string> scope)
{
// (identifier ::)_opt identifier ( '(' some tokens ')' )_opt ..._opt
// parse name
DEBUG_ASSERT(stream.peek().kind() == CXToken_Identifier
|| stream.peek().kind() == CXToken_Keyword,
detail::parse_error_handler{}, stream.cursor(), "expected identifier");
auto name = stream.get().value().std_str();
if (skip_if(stream, "::"))
{ {
if (name == "carries_dependency") // name was actually a scope, so parse name again
return cpp_attribute_kind::carries_dependency; DEBUG_ASSERT(!scope, detail::parse_error_handler{}, stream.cursor(),
else if (name == "deprecated") "attribute using + scope not allowed");
return cpp_attribute_kind::deprecated; scope = std::move(name);
else if (name == "fallthrough")
return cpp_attribute_kind::fallthrough;
else if (name == "maybe_unused")
return cpp_attribute_kind::maybe_unused;
else if (name == "nodiscard")
return cpp_attribute_kind::nodiscard;
else if (name == "noreturn")
return cpp_attribute_kind::noreturn;
else
return cpp_attribute_kind::unknown;
}
cpp_token_string parse_attribute_arguments(detail::cxtoken_stream& stream)
{
auto end = find_closing_bracket(stream);
skip(stream, "(");
auto arguments = detail::to_string(stream, end);
stream.set_cur(end);
skip(stream, ")");
return arguments;
}
cpp_attribute parse_attribute_token(detail::cxtoken_stream& stream,
type_safe::optional<std::string> scope)
{
// (identifier ::)_opt identifier ( '(' some tokens ')' )_opt ..._opt
// parse name
DEBUG_ASSERT(stream.peek().kind() == CXToken_Identifier DEBUG_ASSERT(stream.peek().kind() == CXToken_Identifier
|| stream.peek().kind() == CXToken_Keyword, || stream.peek().kind() == CXToken_Keyword,
detail::parse_error_handler{}, stream.cursor(), "expected identifier"); detail::parse_error_handler{}, stream.cursor(), "expected identifier");
auto name = stream.get().value().std_str(); name = stream.get().value().std_str();
if (skip_if(stream, "::"))
{
// name was actually a scope, so parse name again
DEBUG_ASSERT(!scope, detail::parse_error_handler{}, stream.cursor(),
"attribute using + scope not allowed");
scope = std::move(name);
DEBUG_ASSERT(stream.peek().kind() == CXToken_Identifier
|| stream.peek().kind() == CXToken_Keyword,
detail::parse_error_handler{}, stream.cursor(), "expected identifier");
name = stream.get().value().std_str();
}
// parse arguments
type_safe::optional<cpp_token_string> arguments;
if (stream.peek() == "(")
arguments = parse_attribute_arguments(stream);
// parse variadic token
auto is_variadic = skip_if(stream, "...");
// get kind
auto kind = get_attribute_kind(name);
if (!scope && kind != cpp_attribute_kind::unknown)
return cpp_attribute(kind, std::move(arguments));
else
return cpp_attribute(std::move(scope), std::move(name), std::move(arguments),
is_variadic);
} }
bool parse_attribute_impl(cpp_attribute_list& result, detail::cxtoken_stream& stream) // parse arguments
type_safe::optional<cpp_token_string> arguments;
if (stream.peek() == "(")
arguments = parse_attribute_arguments(stream);
// parse variadic token
auto is_variadic = skip_if(stream, "...");
// get kind
auto kind = get_attribute_kind(name);
if (!scope && kind != cpp_attribute_kind::unknown)
return cpp_attribute(kind, std::move(arguments));
else
return cpp_attribute(std::move(scope), std::move(name), std::move(arguments), is_variadic);
}
bool parse_attribute_impl(cpp_attribute_list& result, detail::cxtoken_stream& stream)
{
if (skip_if(stream, "[") && stream.peek() == "[")
{ {
if (skip_if(stream, "[") && stream.peek() == "[") // C++11 attribute
// [[<attribute>]]
// ^
skip(stream, "[");
auto scope = parse_attribute_using(stream);
while (!skip_if(stream, "]"))
{ {
// C++11 attribute auto attribute = parse_attribute_token(stream, scope);
// [[<attribute>]] result.push_back(std::move(attribute));
// ^ detail::skip_if(stream, ",");
skip(stream, "[");
auto scope = parse_attribute_using(stream);
while (!skip_if(stream, "]"))
{
auto attribute = parse_attribute_token(stream, scope);
result.push_back(std::move(attribute));
detail::skip_if(stream, ",");
}
// [[<attribute>]]
// ^
skip(stream, "]");
return true;
}
else if (skip_if(stream, "alignas"))
{
// alignas specifier
// alignas(<some arguments>)
// ^
auto arguments = parse_attribute_arguments(stream);
result.push_back(cpp_attribute(cpp_attribute_kind::alignas_, std::move(arguments)));
}
else if (skip_if(stream, "__attribute__") && stream.peek() == "(")
{
// GCC/clang attributes
// __attribute__((<attribute>))
// ^^
skip(stream, "(");
skip(stream, "(");
auto scope = parse_attribute_using(stream);
while (!skip_if(stream, ")"))
{
auto attribute = parse_attribute_token(stream, scope);
result.push_back(std::move(attribute));
detail::skip_if(stream, ",");
}
skip(stream, ")");
return true;
}
else if (skip_if(stream, "__declspec"))
{
// MSVC declspec
// __declspec(<attribute>)
// ^
skip(stream, "(");
auto scope = parse_attribute_using(stream);
while (!skip_if(stream, ")"))
{
auto attribute = parse_attribute_token(stream, scope);
result.push_back(std::move(attribute));
detail::skip_if(stream, ",");
}
return true;
} }
return false; // [[<attribute>]]
// ^
skip(stream, "]");
return true;
} }
else if (skip_if(stream, "alignas"))
{
// alignas specifier
// alignas(<some arguments>)
// ^
auto arguments = parse_attribute_arguments(stream);
result.push_back(cpp_attribute(cpp_attribute_kind::alignas_, std::move(arguments)));
}
else if (skip_if(stream, "__attribute__") && stream.peek() == "(")
{
// GCC/clang attributes
// __attribute__((<attribute>))
// ^^
skip(stream, "(");
skip(stream, "(");
auto scope = parse_attribute_using(stream);
while (!skip_if(stream, ")"))
{
auto attribute = parse_attribute_token(stream, scope);
result.push_back(std::move(attribute));
detail::skip_if(stream, ",");
}
skip(stream, ")");
return true;
}
else if (skip_if(stream, "__declspec"))
{
// MSVC declspec
// __declspec(<attribute>)
// ^
skip(stream, "(");
auto scope = parse_attribute_using(stream);
while (!skip_if(stream, ")"))
{
auto attribute = parse_attribute_token(stream, scope);
result.push_back(std::move(attribute));
detail::skip_if(stream, ",");
}
return true;
}
return false;
}
} // namespace } // namespace
cpp_attribute_list detail::parse_attributes(detail::cxtoken_stream& stream, bool skip_anway) cpp_attribute_list detail::parse_attributes(detail::cxtoken_stream& stream, bool skip_anway)
@ -602,37 +593,37 @@ cpp_attribute_list detail::parse_attributes(detail::cxtoken_stream& stream, bool
namespace namespace
{ {
cpp_token_kind get_kind(const detail::cxtoken& token) cpp_token_kind get_kind(const detail::cxtoken& token)
{
switch (token.kind())
{ {
switch (token.kind()) case CXToken_Punctuation:
{
case CXToken_Punctuation:
return cpp_token_kind::punctuation;
case CXToken_Keyword:
return cpp_token_kind::keyword;
case CXToken_Identifier:
return cpp_token_kind::identifier;
case CXToken_Literal:
{
auto spelling = token.value().std_str();
if (spelling.find('.') != std::string::npos)
return cpp_token_kind::float_literal;
else if (std::isdigit(spelling.front()))
return cpp_token_kind::int_literal;
else if (spelling.back() == '\'')
return cpp_token_kind::char_literal;
else
return cpp_token_kind::string_literal;
}
case CXToken_Comment:
break;
}
DEBUG_UNREACHABLE(detail::assert_handler{});
return cpp_token_kind::punctuation; return cpp_token_kind::punctuation;
case CXToken_Keyword:
return cpp_token_kind::keyword;
case CXToken_Identifier:
return cpp_token_kind::identifier;
case CXToken_Literal:
{
auto spelling = token.value().std_str();
if (spelling.find('.') != std::string::npos)
return cpp_token_kind::float_literal;
else if (std::isdigit(spelling.front()))
return cpp_token_kind::int_literal;
else if (spelling.back() == '\'')
return cpp_token_kind::char_literal;
else
return cpp_token_kind::string_literal;
} }
case CXToken_Comment:
break;
}
DEBUG_UNREACHABLE(detail::assert_handler{});
return cpp_token_kind::punctuation;
}
} // namespace } // namespace
cpp_token_string detail::to_string(cxtoken_stream& stream, cxtoken_iterator end) cpp_token_string detail::to_string(cxtoken_stream& stream, cxtoken_iterator end)

View file

@ -15,194 +15,189 @@
namespace cppast namespace cppast
{ {
namespace detail namespace detail
{
class cxtoken
{ {
class cxtoken public:
explicit cxtoken(const CXTranslationUnit& tu_unit, const CXToken& token);
const cxstring& value() const noexcept
{ {
public: return value_;
explicit cxtoken(const CXTranslationUnit& tu_unit, const CXToken& token);
const cxstring& value() const noexcept
{
return value_;
}
const char* c_str() const noexcept
{
return value_.c_str();
}
CXTokenKind kind() const noexcept
{
return kind_;
}
private:
cxstring value_;
CXTokenKind kind_;
};
inline bool operator==(const cxtoken& tok, const char* str) noexcept
{
return tok.value() == str;
} }
inline bool operator==(const char* str, const cxtoken& tok) noexcept const char* c_str() const noexcept
{ {
return str == tok.value(); return value_.c_str();
} }
inline bool operator!=(const cxtoken& tok, const char* str) noexcept CXTokenKind kind() const noexcept
{ {
return !(tok == str); return kind_;
} }
inline bool operator!=(const char* str, const cxtoken& tok) noexcept private:
{ cxstring value_;
return !(str == tok); CXTokenKind kind_;
} };
using cxtoken_iterator = std::vector<cxtoken>::const_iterator; inline bool operator==(const cxtoken& tok, const char* str) noexcept
{
class cxtokenizer return tok.value() == str;
{
public:
explicit cxtokenizer(const CXTranslationUnit& tu, const CXFile& file,
const CXCursor& cur);
cxtoken_iterator begin() const noexcept
{
return tokens_.begin();
}
cxtoken_iterator end() const noexcept
{
return tokens_.end();
}
// if it returns true, the last token is ">>",
// but should haven been ">"
// only a problem for template parameters
bool unmunch() const noexcept
{
return unmunch_;
}
private:
std::vector<cxtoken> tokens_;
bool unmunch_;
};
class cxtoken_stream
{
public:
explicit cxtoken_stream(const cxtokenizer& tokenizer, const CXCursor& cur)
: cursor_(cur),
begin_(tokenizer.begin()),
cur_(begin_),
end_(tokenizer.end()),
unmunch_(tokenizer.unmunch())
{
}
const cxtoken& peek() const noexcept
{
if (done())
return *std::prev(end_);
return *cur_;
}
void bump() noexcept
{
if (cur_ != end_)
++cur_;
}
void bump_back() noexcept
{
if (cur_ != begin_)
--cur_;
}
const cxtoken& get() noexcept
{
auto& result = peek();
bump();
return result;
}
bool done() const noexcept
{
return cur_ == end_;
}
const CXCursor& cursor() const noexcept
{
return cursor_;
}
cxtoken_iterator begin() const noexcept
{
return begin_;
}
cxtoken_iterator cur() const noexcept
{
return cur_;
}
cxtoken_iterator end() const noexcept
{
return end_;
}
void set_cur(cxtoken_iterator iter) noexcept
{
cur_ = iter;
}
bool unmunch() const noexcept
{
return unmunch_;
}
private:
CXCursor cursor_;
cxtoken_iterator begin_, cur_, end_;
bool unmunch_;
};
// skips the next token
// asserts that it has the given string
void skip(cxtoken_stream& stream, const char* str);
// skips the next token if it has the given string
// if multi_token == true, str can consist of multiple tokens optionally separated by whitespace
bool skip_if(cxtoken_stream& stream, const char* str, bool multi_token = false);
// returns the location of the closing bracket
// the current token must be (,[,{ or <
// note: < might not work in the arguments of a template specialization
cxtoken_iterator find_closing_bracket(cxtoken_stream stream);
// skips brackets
// the current token must be (,[,{ or <
// note: < might not work in the arguments of a template specialization
void skip_brackets(cxtoken_stream& stream);
// parses attributes
// if skip_anyway is true it will bump even if no attributes have been parsed
cpp_attribute_list parse_attributes(cxtoken_stream& stream, bool skip_anyway = false);
// converts a token range to a string
cpp_token_string to_string(cxtoken_stream& stream, cxtoken_iterator end);
// appends token to scope, if it is still valid
// else clears it
// note: does not consume the token if it is not valid,
// returns false in that case
bool append_scope(cxtoken_stream& stream, std::string& scope);
} }
} // namespace cppast::detail
inline bool operator==(const char* str, const cxtoken& tok) noexcept
{
return str == tok.value();
}
inline bool operator!=(const cxtoken& tok, const char* str) noexcept
{
return !(tok == str);
}
inline bool operator!=(const char* str, const cxtoken& tok) noexcept
{
return !(str == tok);
}
using cxtoken_iterator = std::vector<cxtoken>::const_iterator;
class cxtokenizer
{
public:
explicit cxtokenizer(const CXTranslationUnit& tu, const CXFile& file, const CXCursor& cur);
cxtoken_iterator begin() const noexcept
{
return tokens_.begin();
}
cxtoken_iterator end() const noexcept
{
return tokens_.end();
}
// if it returns true, the last token is ">>",
// but should haven been ">"
// only a problem for template parameters
bool unmunch() const noexcept
{
return unmunch_;
}
private:
std::vector<cxtoken> tokens_;
bool unmunch_;
};
class cxtoken_stream
{
public:
explicit cxtoken_stream(const cxtokenizer& tokenizer, const CXCursor& cur)
: cursor_(cur), begin_(tokenizer.begin()), cur_(begin_), end_(tokenizer.end()),
unmunch_(tokenizer.unmunch())
{}
const cxtoken& peek() const noexcept
{
if (done())
return *std::prev(end_);
return *cur_;
}
void bump() noexcept
{
if (cur_ != end_)
++cur_;
}
void bump_back() noexcept
{
if (cur_ != begin_)
--cur_;
}
const cxtoken& get() noexcept
{
auto& result = peek();
bump();
return result;
}
bool done() const noexcept
{
return cur_ == end_;
}
const CXCursor& cursor() const noexcept
{
return cursor_;
}
cxtoken_iterator begin() const noexcept
{
return begin_;
}
cxtoken_iterator cur() const noexcept
{
return cur_;
}
cxtoken_iterator end() const noexcept
{
return end_;
}
void set_cur(cxtoken_iterator iter) noexcept
{
cur_ = iter;
}
bool unmunch() const noexcept
{
return unmunch_;
}
private:
CXCursor cursor_;
cxtoken_iterator begin_, cur_, end_;
bool unmunch_;
};
// skips the next token
// asserts that it has the given string
void skip(cxtoken_stream& stream, const char* str);
// skips the next token if it has the given string
// if multi_token == true, str can consist of multiple tokens optionally separated by whitespace
bool skip_if(cxtoken_stream& stream, const char* str, bool multi_token = false);
// returns the location of the closing bracket
// the current token must be (,[,{ or <
// note: < might not work in the arguments of a template specialization
cxtoken_iterator find_closing_bracket(cxtoken_stream stream);
// skips brackets
// the current token must be (,[,{ or <
// note: < might not work in the arguments of a template specialization
void skip_brackets(cxtoken_stream& stream);
// parses attributes
// if skip_anyway is true it will bump even if no attributes have been parsed
cpp_attribute_list parse_attributes(cxtoken_stream& stream, bool skip_anyway = false);
// converts a token range to a string
cpp_token_string to_string(cxtoken_stream& stream, cxtoken_iterator end);
// appends token to scope, if it is still valid
// else clears it
// note: does not consume the token if it is not valid,
// returns false in that case
bool append_scope(cxtoken_stream& stream, std::string& scope);
} // namespace detail
} // namespace cppast
#endif // CPPAST_CXTOKENIZER_HPP_INCLUDED #endif // CPPAST_CXTOKENIZER_HPP_INCLUDED

View file

@ -28,7 +28,7 @@ detail::cxstring detail::get_type_kind_spelling(const CXType& type) noexcept
namespace namespace
{ {
std::mutex mtx; std::mutex mtx;
} }
void detail::print_cursor_info(const CXCursor& cur) noexcept void detail::print_cursor_info(const CXCursor& cur) noexcept

View file

@ -11,21 +11,21 @@
namespace cppast namespace cppast
{ {
namespace detail namespace detail
{ {
cxstring get_display_name(const CXCursor& cur) noexcept; cxstring get_display_name(const CXCursor& cur) noexcept;
cxstring get_cursor_kind_spelling(const CXCursor& cur) noexcept; cxstring get_cursor_kind_spelling(const CXCursor& cur) noexcept;
cxstring get_type_kind_spelling(const CXType& type) noexcept; cxstring get_type_kind_spelling(const CXType& type) noexcept;
void print_cursor_info(const CXCursor& cur) noexcept; void print_cursor_info(const CXCursor& cur) noexcept;
void print_type_info(const CXType& type) noexcept; void print_type_info(const CXType& type) noexcept;
void print_tokens(const CXTranslationUnit& tu, const CXFile& file, void print_tokens(const CXTranslationUnit& tu, const CXFile& file,
const CXCursor& cur) noexcept; const CXCursor& cur) noexcept;
} } // namespace detail
} // namespace cppast::detail } // namespace cppast
#endif // CPPAST_DEBUG_HELPER_HPP_INCLUDED #endif // CPPAST_DEBUG_HELPER_HPP_INCLUDED

View file

@ -4,80 +4,77 @@
#include <cppast/cpp_enum.hpp> #include <cppast/cpp_enum.hpp>
#include "parse_functions.hpp"
#include "libclang_visitor.hpp" #include "libclang_visitor.hpp"
#include "parse_functions.hpp"
using namespace cppast; using namespace cppast;
namespace namespace
{ {
std::unique_ptr<cpp_enum_value> parse_enum_value(const detail::parse_context& context, std::unique_ptr<cpp_enum_value> parse_enum_value(const detail::parse_context& context,
const CXCursor& cur) const CXCursor& cur)
{
if (clang_isAttribute(clang_getCursorKind(cur)))
return nullptr;
DEBUG_ASSERT(cur.kind == CXCursor_EnumConstantDecl, detail::parse_error_handler{}, cur,
"unexpected child cursor of enum");
detail::cxtokenizer tokenizer(context.tu, context.file, cur);
detail::cxtoken_stream stream(tokenizer, cur);
// <identifier> [<attribute>],
// or: <identifier> [<attribute>] = <expression>,
auto& name = stream.get().value();
auto attributes = detail::parse_attributes(stream);
std::unique_ptr<cpp_expression> value;
if (detail::skip_if(stream, "="))
{ {
if (clang_isAttribute(clang_getCursorKind(cur))) detail::visit_children(cur, [&](const CXCursor& child) {
return nullptr; DEBUG_ASSERT(clang_isExpression(child.kind) && !value, detail::parse_error_handler{},
cur, "unexpected child cursor of enum value");
DEBUG_ASSERT(cur.kind == CXCursor_EnumConstantDecl, detail::parse_error_handler{}, cur, value = detail::parse_expression(context, child);
"unexpected child cursor of enum"); });
detail::cxtokenizer tokenizer(context.tu, context.file, cur);
detail::cxtoken_stream stream(tokenizer, cur);
// <identifier> [<attribute>],
// or: <identifier> [<attribute>] = <expression>,
auto& name = stream.get().value();
auto attributes = detail::parse_attributes(stream);
std::unique_ptr<cpp_expression> value;
if (detail::skip_if(stream, "="))
{
detail::visit_children(cur, [&](const CXCursor& child) {
DEBUG_ASSERT(clang_isExpression(child.kind) && !value,
detail::parse_error_handler{}, cur,
"unexpected child cursor of enum value");
value = detail::parse_expression(context, child);
});
}
auto result = cpp_enum_value::build(*context.idx, detail::get_entity_id(cur), name.c_str(),
std::move(value));
result->add_attribute(attributes);
return result;
} }
cpp_enum::builder make_enum_builder(const detail::parse_context& context, const CXCursor& cur, auto result = cpp_enum_value::build(*context.idx, detail::get_entity_id(cur), name.c_str(),
type_safe::optional<cpp_entity_ref>& semantic_parent) std::move(value));
{ result->add_attribute(attributes);
auto name = detail::get_cursor_name(cur); return result;
detail::cxtokenizer tokenizer(context.tu, context.file, cur);
detail::cxtoken_stream stream(tokenizer, cur);
// enum [class/struct] [<attribute>] name [: type] {
detail::skip(stream, "enum");
auto scoped = detail::skip_if(stream, "class") || detail::skip_if(stream, "struct");
auto attributes = detail::parse_attributes(stream);
std::string scope;
while (!detail::skip_if(stream, name.c_str()))
if (!detail::append_scope(stream, scope))
DEBUG_UNREACHABLE(detail::parse_error_handler{}, cur,
"unexpected tokens in enum name");
if (!scope.empty())
semantic_parent =
cpp_entity_ref(detail::get_entity_id(clang_getCursorSemanticParent(cur)),
std::move(scope));
// parse type
auto type = detail::parse_type(context, cur, clang_getEnumDeclIntegerType(cur));
auto type_given = detail::skip_if(stream, ":");
auto result = cpp_enum::builder(name.c_str(), scoped, std::move(type), type_given);
result.get().add_attribute(attributes);
return result;
}
} }
cpp_enum::builder make_enum_builder(const detail::parse_context& context, const CXCursor& cur,
type_safe::optional<cpp_entity_ref>& semantic_parent)
{
auto name = detail::get_cursor_name(cur);
detail::cxtokenizer tokenizer(context.tu, context.file, cur);
detail::cxtoken_stream stream(tokenizer, cur);
// enum [class/struct] [<attribute>] name [: type] {
detail::skip(stream, "enum");
auto scoped = detail::skip_if(stream, "class") || detail::skip_if(stream, "struct");
auto attributes = detail::parse_attributes(stream);
std::string scope;
while (!detail::skip_if(stream, name.c_str()))
if (!detail::append_scope(stream, scope))
DEBUG_UNREACHABLE(detail::parse_error_handler{}, cur, "unexpected tokens in enum name");
if (!scope.empty())
semantic_parent = cpp_entity_ref(detail::get_entity_id(clang_getCursorSemanticParent(cur)),
std::move(scope));
// parse type
auto type = detail::parse_type(context, cur, clang_getEnumDeclIntegerType(cur));
auto type_given = detail::skip_if(stream, ":");
auto result = cpp_enum::builder(name.c_str(), scoped, std::move(type), type_given);
result.get().add_attribute(attributes);
return result;
}
} // namespace
std::unique_ptr<cpp_entity> detail::parse_cpp_enum(const detail::parse_context& context, std::unique_ptr<cpp_entity> detail::parse_cpp_enum(const detail::parse_context& context,
const CXCursor& cur) const CXCursor& cur)
{ {

View file

@ -4,11 +4,11 @@
#include <cppast/cpp_friend.hpp> #include <cppast/cpp_friend.hpp>
#include <cppast/cpp_template_parameter.hpp>
#include <cppast/cpp_template.hpp> #include <cppast/cpp_template.hpp>
#include <cppast/cpp_template_parameter.hpp>
#include "parse_functions.hpp"
#include "libclang_visitor.hpp" #include "libclang_visitor.hpp"
#include "parse_functions.hpp"
using namespace cppast; using namespace cppast;
@ -45,9 +45,9 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_friend(const detail::parse_context
// we can't use the other branch here, // we can't use the other branch here,
// as then the class name would be wrong // as then the class name would be wrong
auto name = detail::get_cursor_name(referenced); auto name = detail::get_cursor_name(referenced);
type = type = cpp_user_defined_type::build(
cpp_user_defined_type::build(cpp_type_ref(detail::get_entity_id(referenced), cpp_type_ref(detail::get_entity_id(referenced),
namespace_str + "::" + name.c_str())); namespace_str + "::" + name.c_str()));
} }
else else
{ {

File diff suppressed because it is too large Load diff

View file

@ -4,8 +4,8 @@
#include "parse_functions.hpp" #include "parse_functions.hpp"
#include <cppast/cpp_language_linkage.hpp>
#include <clang-c/Index.h> #include <clang-c/Index.h>
#include <cppast/cpp_language_linkage.hpp>
#include "libclang_visitor.hpp" #include "libclang_visitor.hpp"

View file

@ -10,12 +10,12 @@
#include <clang-c/CXCompilationDatabase.h> #include <clang-c/CXCompilationDatabase.h>
#include "cxtokenizer.hpp"
#include "libclang_visitor.hpp" #include "libclang_visitor.hpp"
#include "raii_wrapper.hpp"
#include "parse_error.hpp" #include "parse_error.hpp"
#include "parse_functions.hpp" #include "parse_functions.hpp"
#include "preprocessor.hpp" #include "preprocessor.hpp"
#include "cxtokenizer.hpp" #include "raii_wrapper.hpp"
using namespace cppast; using namespace cppast;
@ -81,22 +81,20 @@ bool libclang_compilation_database::has_config(const char* file_name) const
namespace namespace
{ {
int parse_number(const char*& str) int parse_number(const char*& str)
{
auto result = 0;
for (; *str && *str != '.'; ++str)
{ {
auto result = 0; result *= 10;
for (; *str && *str != '.'; ++str) result += int(*str - '0');
{
result *= 10;
result += int(*str - '0');
}
return result;
} }
return result;
}
} // namespace } // namespace
libclang_compile_config::libclang_compile_config() libclang_compile_config::libclang_compile_config()
: compile_config({}), : compile_config({}), write_preprocessed_(false), fast_preprocessing_(false),
write_preprocessed_(false),
fast_preprocessing_(false),
remove_comments_in_macro_(false) remove_comments_in_macro_(false)
{ {
// set given clang binary // set given clang binary
@ -117,39 +115,38 @@ libclang_compile_config::libclang_compile_config()
namespace namespace
{ {
struct cxcompile_commands_deleter struct cxcompile_commands_deleter
{
void operator()(CXCompileCommands cmds)
{ {
void operator()(CXCompileCommands cmds) clang_CompileCommands_dispose(cmds);
{
clang_CompileCommands_dispose(cmds);
}
};
using cxcompile_commands = detail::raii_wrapper<CXCompileCommands, cxcompile_commands_deleter>;
bool has_drive_prefix(const std::string& file)
{
return file.size() > 2 && file[1] == ':';
} }
};
bool is_absolute(const std::string& file) using cxcompile_commands = detail::raii_wrapper<CXCompileCommands, cxcompile_commands_deleter>;
{
return !file.empty()
&& (has_drive_prefix(file) || file.front() == '/' || file.front() == '\\');
}
std::string get_full_path(const detail::cxstring& dir, const std::string& file) bool has_drive_prefix(const std::string& file)
{ {
if (is_absolute(file)) return file.size() > 2 && file[1] == ':';
// absolute file }
return file;
else if (dir[dir.length() - 1] != '/' && dir[dir.length() - 1] != '\\') bool is_absolute(const std::string& file)
// relative needing separator {
return dir.std_str() + '/' + file; return !file.empty() && (has_drive_prefix(file) || file.front() == '/' || file.front() == '\\');
else }
// relative w/o separator
return dir.std_str() + file; std::string get_full_path(const detail::cxstring& dir, const std::string& file)
} {
if (is_absolute(file))
// absolute file
return file;
else if (dir[dir.length() - 1] != '/' && dir[dir.length() - 1] != '\\')
// relative needing separator
return dir.std_str() + '/' + file;
else
// relative w/o separator
return dir.std_str() + file;
}
} // namespace } // namespace
void detail::for_each_file(const libclang_compilation_database& database, void* user_data, void detail::for_each_file(const libclang_compilation_database& database, void* user_data,
@ -170,70 +167,70 @@ void detail::for_each_file(const libclang_compilation_database& database, void*
namespace namespace
{ {
bool is_flag(const detail::cxstring& str) bool is_flag(const detail::cxstring& str)
{ {
return str.length() > 1u && str[0] == '-'; return str.length() > 1u && str[0] == '-';
} }
const char* find_flag_arg_sep(const std::string& last_flag) const char* find_flag_arg_sep(const std::string& last_flag)
{ {
if (last_flag[1] == 'D') if (last_flag[1] == 'D')
// no separator, equal is part of the arg // no separator, equal is part of the arg
return nullptr; return nullptr;
return std::strchr(last_flag.c_str(), '='); return std::strchr(last_flag.c_str(), '=');
} }
template <typename Func> template <typename Func>
void parse_flags(CXCompileCommand cmd, Func callback) void parse_flags(CXCompileCommand cmd, Func callback)
{
auto no_args = clang_CompileCommand_getNumArgs(cmd);
std::string last_flag;
for (auto i = 1u /* 0 is compiler executable */; i != no_args; ++i)
{ {
auto no_args = clang_CompileCommand_getNumArgs(cmd); detail::cxstring str(clang_CompileCommand_getArg(cmd, i));
std::string last_flag; if (is_flag(str))
for (auto i = 1u /* 0 is compiler executable */; i != no_args; ++i)
{ {
detail::cxstring str(clang_CompileCommand_getArg(cmd, i)); if (!last_flag.empty())
if (is_flag(str))
{ {
if (!last_flag.empty()) // process last flag
std::string args;
if (auto ptr = find_flag_arg_sep(last_flag))
{ {
// process last flag auto pos = std::size_t(ptr - last_flag.c_str());
std::string args; ++ptr;
if (auto ptr = find_flag_arg_sep(last_flag)) while (*ptr)
{ args += *ptr++;
auto pos = std::size_t(ptr - last_flag.c_str()); last_flag.erase(pos);
++ptr; }
while (*ptr) else if (last_flag.size() > 2u)
args += *ptr++; {
last_flag.erase(pos); // assume two character flag
} args = last_flag.substr(2u);
else if (last_flag.size() > 2u) last_flag.erase(2u);
{
// assume two character flag
args = last_flag.substr(2u);
last_flag.erase(2u);
}
callback(std::move(last_flag), std::move(args));
} }
last_flag = str.std_str(); callback(std::move(last_flag), std::move(args));
} }
else if (!last_flag.empty())
{ last_flag = str.std_str();
// we have flags + args
callback(std::move(last_flag), str.std_str());
last_flag.clear();
}
// else skip argument
} }
else if (!last_flag.empty())
{
// we have flags + args
callback(std::move(last_flag), str.std_str());
last_flag.clear();
}
// else skip argument
} }
}
} // namespace } // namespace
libclang_compile_config::libclang_compile_config(const libclang_compilation_database& database, libclang_compile_config::libclang_compile_config(const libclang_compilation_database& database,
const std::string& file) const std::string& file)
: libclang_compile_config() : libclang_compile_config()
{ {
auto cxcommands = auto cxcommands
clang_CompilationDatabase_getCompileCommands(database.database_, file.c_str()); = clang_CompilationDatabase_getCompileCommands(database.database_, file.c_str());
if (cxcommands == nullptr) if (cxcommands == nullptr)
throw libclang_error(detail::format("no compile commands specified for file '", file, "'")); throw libclang_error(detail::format("no compile commands specified for file '", file, "'"));
cxcompile_commands commands(cxcommands); cxcompile_commands commands(cxcommands);
@ -353,8 +350,8 @@ type_safe::optional<libclang_compile_config> cppast::find_config_for(
if (database.has_config(file_name)) if (database.has_config(file_name))
return libclang_compile_config(database, std::move(file_name)); return libclang_compile_config(database, std::move(file_name));
static const char* extensions[] = {".h", ".hpp", ".cpp", ".h++", ".c++", ".hxx", static const char* extensions[]
".cxx", ".hh", ".cc", ".H", ".C"}; = {".h", ".hpp", ".cpp", ".h++", ".c++", ".hxx", ".cxx", ".hh", ".cc", ".H", ".C"};
for (auto ext : extensions) for (auto ext : extensions)
{ {
auto name = file_name + ext; auto name = file_name + ext;
@ -370,125 +367,122 @@ struct libclang_parser::impl
detail::cxindex index; detail::cxindex index;
impl() : index(clang_createIndex(0, 0)) // no diagnostic, other one is irrelevant impl() : index(clang_createIndex(0, 0)) // no diagnostic, other one is irrelevant
{ {}
}
}; };
libclang_parser::libclang_parser() : libclang_parser(default_logger()) {} libclang_parser::libclang_parser() : libclang_parser(default_logger()) {}
libclang_parser::libclang_parser(type_safe::object_ref<const diagnostic_logger> logger) libclang_parser::libclang_parser(type_safe::object_ref<const diagnostic_logger> logger)
: parser(logger), pimpl_(new impl) : parser(logger), pimpl_(new impl)
{ {}
}
libclang_parser::~libclang_parser() noexcept {} libclang_parser::~libclang_parser() noexcept {}
namespace namespace
{ {
std::vector<const char*> get_arguments(const libclang_compile_config& config) std::vector<const char*> get_arguments(const libclang_compile_config& config)
{
std::vector<const char*> args
= {"-x", "c++", "-I."}; // force C++ and enable current directory for include search
for (auto& flag : detail::libclang_compile_config_access::flags(config))
args.push_back(flag.c_str());
return args;
}
type_safe::optional<severity> get_severity(const CXDiagnostic& diag)
{
switch (clang_getDiagnosticSeverity(diag))
{ {
std::vector<const char*> args = case CXDiagnostic_Ignored:
{"-x", "c++", "-I."}; // force C++ and enable current directory for include search case CXDiagnostic_Note:
for (auto& flag : detail::libclang_compile_config_access::flags(config)) case CXDiagnostic_Warning:
args.push_back(flag.c_str()); // ignore those diagnostics
return args;
}
type_safe::optional<severity> get_severity(const CXDiagnostic& diag)
{
switch (clang_getDiagnosticSeverity(diag))
{
case CXDiagnostic_Ignored:
case CXDiagnostic_Note:
case CXDiagnostic_Warning:
// ignore those diagnostics
return type_safe::nullopt;
case CXDiagnostic_Error:
return severity::error;
case CXDiagnostic_Fatal:
return severity::critical;
}
DEBUG_UNREACHABLE(detail::assert_handler{});
return type_safe::nullopt; return type_safe::nullopt;
case CXDiagnostic_Error:
return severity::error;
case CXDiagnostic_Fatal:
return severity::critical;
} }
void print_diagnostics(const diagnostic_logger& logger, const CXTranslationUnit& tu) DEBUG_UNREACHABLE(detail::assert_handler{});
{ return type_safe::nullopt;
auto no = clang_getNumDiagnostics(tu); }
for (auto i = 0u; i != no; ++i)
{
auto diag = clang_getDiagnostic(tu, i);
auto sev = get_severity(diag);
if (sev)
{
auto diag_loc = clang_getDiagnosticLocation(diag);
CXString diag_file;
unsigned line;
clang_getPresumedLocation(diag_loc, &diag_file, &line, nullptr);
auto loc = source_location::make_file(detail::cxstring(diag_file).c_str(), line); void print_diagnostics(const diagnostic_logger& logger, const CXTranslationUnit& tu)
auto text = detail::cxstring(clang_getDiagnosticSpelling(diag)); {
if (text != "too many errors emitted, stopping now") auto no = clang_getNumDiagnostics(tu);
logger.log("libclang", diagnostic{text.c_str(), loc, sev.value()}); for (auto i = 0u; i != no; ++i)
} {
auto diag = clang_getDiagnostic(tu, i);
auto sev = get_severity(diag);
if (sev)
{
auto diag_loc = clang_getDiagnosticLocation(diag);
CXString diag_file;
unsigned line;
clang_getPresumedLocation(diag_loc, &diag_file, &line, nullptr);
auto loc = source_location::make_file(detail::cxstring(diag_file).c_str(), line);
auto text = detail::cxstring(clang_getDiagnosticSpelling(diag));
if (text != "too many errors emitted, stopping now")
logger.log("libclang", diagnostic{text.c_str(), loc, sev.value()});
} }
} }
}
detail::cxtranslation_unit get_cxunit(const diagnostic_logger& logger, detail::cxtranslation_unit get_cxunit(const diagnostic_logger& logger, const detail::cxindex& idx,
const detail::cxindex& idx, const libclang_compile_config& config, const char* path,
const libclang_compile_config& config, const char* path, const std::string& source)
const std::string& source) {
CXUnsavedFile file;
file.Filename = path;
file.Contents = source.c_str();
file.Length = source.length();
auto args = get_arguments(config);
CXTranslationUnit tu;
auto flags = CXTranslationUnit_Incomplete | CXTranslationUnit_KeepGoing
| CXTranslationUnit_DetailedPreprocessingRecord;
auto error
= clang_parseTranslationUnit2(idx.get(), path, // index and path
args.data(),
static_cast<int>(args.size()), // arguments (ptr + size)
&file, 1, // unsaved files (ptr + size)
unsigned(flags), &tu);
if (error != CXError_Success)
{ {
CXUnsavedFile file; switch (error)
file.Filename = path;
file.Contents = source.c_str();
file.Length = source.length();
auto args = get_arguments(config);
CXTranslationUnit tu;
auto flags = CXTranslationUnit_Incomplete | CXTranslationUnit_KeepGoing
| CXTranslationUnit_DetailedPreprocessingRecord;
auto error =
clang_parseTranslationUnit2(idx.get(), path, // index and path
args.data(),
static_cast<int>(args.size()), // arguments (ptr + size)
&file, 1, // unsaved files (ptr + size)
unsigned(flags), &tu);
if (error != CXError_Success)
{ {
switch (error) case CXError_Success:
{ DEBUG_UNREACHABLE(detail::assert_handler{});
case CXError_Success: break;
DEBUG_UNREACHABLE(detail::assert_handler{});
break;
case CXError_Failure: case CXError_Failure:
throw libclang_error("clang_parseTranslationUnit: generic error"); throw libclang_error("clang_parseTranslationUnit: generic error");
case CXError_Crashed: case CXError_Crashed:
throw libclang_error("clang_parseTranslationUnit: libclang crashed :("); throw libclang_error("clang_parseTranslationUnit: libclang crashed :(");
case CXError_InvalidArguments: case CXError_InvalidArguments:
throw libclang_error("clang_parseTranslationUnit: you shouldn't see this message"); throw libclang_error("clang_parseTranslationUnit: you shouldn't see this message");
case CXError_ASTReadError: case CXError_ASTReadError:
throw libclang_error("clang_parseTranslationUnit: AST deserialization error"); throw libclang_error("clang_parseTranslationUnit: AST deserialization error");
}
} }
print_diagnostics(logger, tu);
return detail::cxtranslation_unit(tu);
} }
print_diagnostics(logger, tu);
unsigned get_line_no(const CXCursor& cursor) return detail::cxtranslation_unit(tu);
{ }
auto loc = clang_getCursorLocation(cursor);
unsigned line; unsigned get_line_no(const CXCursor& cursor)
clang_getPresumedLocation(loc, nullptr, &line, nullptr); {
return line; auto loc = clang_getCursorLocation(cursor);
}
unsigned line;
clang_getPresumedLocation(loc, nullptr, &line, nullptr);
return line;
}
} // namespace } // namespace
std::unique_ptr<cpp_file> libclang_parser::do_parse(const cpp_entity_index& idx, std::string path, std::unique_ptr<cpp_file> libclang_parser::do_parse(const cpp_entity_index& idx, std::string path,
@ -530,8 +524,8 @@ std::unique_ptr<cpp_file> libclang_parser::do_parse(const cpp_entity_index& idx,
&& get_line_no(cur) >= include_iter->line, && get_line_no(cur) >= include_iter->line,
detail::assert_handler{}); detail::assert_handler{});
auto full_path = include_iter->full_path.empty() ? include_iter->file_name : auto full_path = include_iter->full_path.empty() ? include_iter->file_name
include_iter->full_path; : include_iter->full_path;
// if we got an absolute file path for the current file, // if we got an absolute file path for the current file,
// also use an absolute file path for the id // also use an absolute file path for the id
@ -543,10 +537,10 @@ std::unique_ptr<cpp_file> libclang_parser::do_parse(const cpp_entity_index& idx,
else else
id = cpp_entity_id(include_iter->file_name.c_str()); id = cpp_entity_id(include_iter->file_name.c_str());
auto include = auto include
cpp_include_directive::build(cpp_file_ref(id, = cpp_include_directive::build(cpp_file_ref(id,
std::move(include_iter->file_name)), std::move(include_iter->file_name)),
include_iter->kind, std::move(full_path)); include_iter->kind, std::move(full_path));
context.comments.match(*include, include_iter->line, context.comments.match(*include, include_iter->line,
false); // must not skip comments, false); // must not skip comments,
// includes are not reported in order // includes are not reported in order

View file

@ -11,50 +11,50 @@
namespace cppast namespace cppast
{ {
namespace detail namespace detail
{
// visits direct children of an entity
template <typename Func>
void visit_children(CXCursor parent, Func f, bool recurse = false)
{ {
// visits direct children of an entity auto continue_lambda = [](CXCursor cur, CXCursor, CXClientData data) {
template <typename Func> auto& actual_cb = *static_cast<Func*>(data);
void visit_children(CXCursor parent, Func f, bool recurse = false) actual_cb(cur);
{ return CXChildVisit_Continue;
auto continue_lambda = [](CXCursor cur, CXCursor, CXClientData data) { };
auto& actual_cb = *static_cast<Func*>(data); auto recurse_lambda = [](CXCursor cur, CXCursor, CXClientData data) {
actual_cb(cur); auto& actual_cb = *static_cast<Func*>(data);
return CXChildVisit_Continue; actual_cb(cur);
}; return CXChildVisit_Recurse;
auto recurse_lambda = [](CXCursor cur, CXCursor, CXClientData data) { };
auto& actual_cb = *static_cast<Func*>(data);
actual_cb(cur);
return CXChildVisit_Recurse;
};
if (recurse) if (recurse)
clang_visitChildren(parent, recurse_lambda, &f); clang_visitChildren(parent, recurse_lambda, &f);
else else
clang_visitChildren(parent, continue_lambda, &f); clang_visitChildren(parent, continue_lambda, &f);
}
// visits a translation unit
// notes: only visits if directly defined in file, not included
template <typename Func>
void visit_tu(const cxtranslation_unit& tu, const char* path, Func f)
{
auto in_tu = [&](const CXCursor& cur) {
auto location = clang_getCursorLocation(cur);
CXString cx_file_name;
clang_getPresumedLocation(location, &cx_file_name, nullptr, nullptr);
cxstring file_name(cx_file_name);
return file_name == path;
};
visit_children(clang_getTranslationUnitCursor(tu.get()), [&](const CXCursor& cur) {
if (in_tu(cur))
f(cur);
});
}
} }
} // namespace cppast::detail
// visits a translation unit
// notes: only visits if directly defined in file, not included
template <typename Func>
void visit_tu(const cxtranslation_unit& tu, const char* path, Func f)
{
auto in_tu = [&](const CXCursor& cur) {
auto location = clang_getCursorLocation(cur);
CXString cx_file_name;
clang_getPresumedLocation(location, &cx_file_name, nullptr, nullptr);
cxstring file_name(cx_file_name);
return file_name == path;
};
visit_children(clang_getTranslationUnitCursor(tu.get()), [&](const CXCursor& cur) {
if (in_tu(cur))
f(cur);
});
}
} // namespace detail
} // namespace cppast
#endif // CPPAST_LIBCLANG_VISITOR_HPP_INCLUDED #endif // CPPAST_LIBCLANG_VISITOR_HPP_INCLUDED

View file

@ -13,49 +13,48 @@ using namespace cppast;
namespace namespace
{ {
cpp_namespace::builder make_ns_builder(const detail::parse_context& context, cpp_namespace::builder make_ns_builder(const detail::parse_context& context, const CXCursor& cur)
const CXCursor& cur) {
detail::cxtokenizer tokenizer(context.tu, context.file, cur);
detail::cxtoken_stream stream(tokenizer, cur);
// [inline] namespace|:: [<attribute>] <identifier> [{]
auto is_inline = false;
if (skip_if(stream, "inline"))
is_inline = true;
// C++17 nested namespace declarations get one cursor per nested name.
// The first cursor starts with the `namespace` keyword, and the
// following start with the `::` separator. Either way, it is skipped.
auto is_nested = false;
if (!detail::skip_if(stream, "namespace"))
{ {
detail::cxtokenizer tokenizer(context.tu, context.file, cur); is_nested = true;
detail::cxtoken_stream stream(tokenizer, cur); skip(stream, "::");
// [inline] namespace|:: [<attribute>] <identifier> [{]
auto is_inline = false;
if (skip_if(stream, "inline"))
is_inline = true;
// C++17 nested namespace declarations get one cursor per nested name.
// The first cursor starts with the `namespace` keyword, and the
// following start with the `::` separator. Either way, it is skipped.
auto is_nested = false;
if (!detail::skip_if(stream, "namespace"))
{
is_nested = true;
skip(stream, "::");
}
auto attributes = parse_attributes(stream);
// <identifier> {
// or when anonymous: {
if (detail::skip_if(stream, "{"))
return cpp_namespace::builder("", is_inline, false);
auto& name = stream.get().value();
auto other_attributes = parse_attributes(stream);
attributes.insert(attributes.end(), other_attributes.begin(), other_attributes.end());
// If the next token is not `::`, there are no more nested namespace
// names, and we expect to see an opening brace.
if (!detail::skip_if(stream, "::"))
skip(stream, "{");
auto result = cpp_namespace::builder(name.c_str(), is_inline, is_nested);
result.get().add_attribute(attributes);
return result;
} }
auto attributes = parse_attributes(stream);
// <identifier> {
// or when anonymous: {
if (detail::skip_if(stream, "{"))
return cpp_namespace::builder("", is_inline, false);
auto& name = stream.get().value();
auto other_attributes = parse_attributes(stream);
attributes.insert(attributes.end(), other_attributes.begin(), other_attributes.end());
// If the next token is not `::`, there are no more nested namespace
// names, and we expect to see an opening brace.
if (!detail::skip_if(stream, "::"))
skip(stream, "{");
auto result = cpp_namespace::builder(name.c_str(), is_inline, is_nested);
result.get().add_attribute(attributes);
return result;
} }
} // namespace
std::unique_ptr<cpp_entity> detail::parse_cpp_namespace(const detail::parse_context& context, std::unique_ptr<cpp_entity> detail::parse_cpp_namespace(const detail::parse_context& context,
cpp_entity& parent, const CXCursor& cur) cpp_entity& parent, const CXCursor& cur)
@ -83,27 +82,27 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_namespace(const detail::parse_cont
namespace namespace
{ {
cpp_entity_id parse_ns_target_cursor(const CXCursor& cur) cpp_entity_id parse_ns_target_cursor(const CXCursor& cur)
{ {
cpp_entity_id result(""); cpp_entity_id result("");
detail::visit_children(cur, detail::visit_children(cur,
[&](const CXCursor& child) { [&](const CXCursor& child) {
auto referenced = clang_getCursorReferenced(child); auto referenced = clang_getCursorReferenced(child);
auto kind = clang_getCursorKind(referenced); auto kind = clang_getCursorKind(referenced);
if (kind == CXCursor_Namespace) if (kind == CXCursor_Namespace)
result = detail::get_entity_id(referenced); result = detail::get_entity_id(referenced);
else if (kind == CXCursor_NamespaceAlias) else if (kind == CXCursor_NamespaceAlias)
// get target of namespace alias instead // get target of namespace alias instead
result = parse_ns_target_cursor(referenced); result = parse_ns_target_cursor(referenced);
else else
DEBUG_UNREACHABLE(detail::parse_error_handler{}, cur, DEBUG_UNREACHABLE(detail::parse_error_handler{}, cur,
"unexpected target for namespace " "unexpected target for namespace "
"alias/using directive"); "alias/using directive");
}, },
true); true);
return result; return result;
}
} }
} // namespace
std::unique_ptr<cpp_entity> detail::parse_cpp_namespace_alias(const detail::parse_context& context, std::unique_ptr<cpp_entity> detail::parse_cpp_namespace_alias(const detail::parse_context& context,
const CXCursor& cur) const CXCursor& cur)
@ -155,54 +154,53 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_using_directive(const detail::pars
namespace namespace
{ {
cpp_entity_ref parse_entity_target_cursor(const CXCursor& cur, std::string name) cpp_entity_ref parse_entity_target_cursor(const CXCursor& cur, std::string name)
{ {
type_safe::deferred_construction<cpp_entity_ref> result; type_safe::deferred_construction<cpp_entity_ref> result;
detail::visit_children(cur, detail::visit_children(cur,
[&](const CXCursor& child) { [&](const CXCursor& child) {
if (result) if (result)
return; return;
switch (clang_getCursorKind(child)) switch (clang_getCursorKind(child))
{ {
case CXCursor_TypeRef: case CXCursor_TypeRef:
case CXCursor_TemplateRef: case CXCursor_TemplateRef:
case CXCursor_MemberRef: case CXCursor_MemberRef:
case CXCursor_VariableRef: case CXCursor_VariableRef:
case CXCursor_DeclRefExpr: case CXCursor_DeclRefExpr:
{ {
auto referenced = clang_getCursorReferenced(child); auto referenced = clang_getCursorReferenced(child);
result = cpp_entity_ref(detail::get_entity_id(referenced), result = cpp_entity_ref(detail::get_entity_id(referenced),
std::move(name)); std::move(name));
break; break;
} }
case CXCursor_OverloadedDeclRef: case CXCursor_OverloadedDeclRef:
{ {
auto size = clang_getNumOverloadedDecls(child); auto size = clang_getNumOverloadedDecls(child);
DEBUG_ASSERT(size >= 1u, detail::parse_error_handler{}, cur, DEBUG_ASSERT(size >= 1u, detail::parse_error_handler{}, cur,
"no target for using declaration"); "no target for using declaration");
std::vector<cpp_entity_id> ids; std::vector<cpp_entity_id> ids;
for (auto i = 0u; i != size; ++i) for (auto i = 0u; i != size; ++i)
ids.push_back(detail::get_entity_id( ids.push_back(detail::get_entity_id(
clang_getOverloadedDecl(child, i))); clang_getOverloadedDecl(child, i)));
result = cpp_entity_ref(std::move(ids), std::move(name)); result = cpp_entity_ref(std::move(ids), std::move(name));
break; break;
} }
case CXCursor_NamespaceRef: case CXCursor_NamespaceRef:
break; // wait for children break; // wait for children
default: default:
DEBUG_UNREACHABLE(detail::parse_error_handler{}, cur, DEBUG_UNREACHABLE(detail::parse_error_handler{}, cur,
"unexpected target for using declaration"); "unexpected target for using declaration");
} }
},
}, true);
true); return result.value();
return result.value();
}
} }
} // namespace
std::unique_ptr<cpp_entity> detail::parse_cpp_using_declaration( std::unique_ptr<cpp_entity> detail::parse_cpp_using_declaration(
const detail::parse_context& context, const CXCursor& cur) const detail::parse_context& context, const CXCursor& cur)

View file

@ -7,89 +7,86 @@
#include <stdexcept> #include <stdexcept>
#include <debug_assert.hpp>
#include <cppast/diagnostic.hpp> #include <cppast/diagnostic.hpp>
#include <debug_assert.hpp>
#include "debug_helper.hpp" #include "debug_helper.hpp"
namespace cppast namespace cppast
{ {
namespace detail namespace detail
{
inline source_location make_location(const CXCursor& cur)
{ {
inline source_location make_location(const CXCursor& cur) auto loc = clang_getCursorLocation(cur);
{
auto loc = clang_getCursorLocation(cur);
CXString file; CXString file;
unsigned line; unsigned line;
clang_getPresumedLocation(loc, &file, &line, nullptr); clang_getPresumedLocation(loc, &file, &line, nullptr);
return source_location::make_file(cxstring(file).c_str(), line); return source_location::make_file(cxstring(file).c_str(), line);
}
inline source_location make_location(const CXFile& file, const CXCursor& cur)
{
return source_location::make_entity(get_display_name(cur).c_str(),
cxstring(clang_getFileName(file)).c_str());
}
inline source_location make_location(const CXType& type)
{
return source_location::make_entity(cxstring(clang_getTypeSpelling(type)).c_str());
}
// thrown on a parsing error
// not meant to escape to the user
class parse_error : public std::logic_error
{
public:
parse_error(source_location loc, std::string message)
: std::logic_error(std::move(message)), location_(std::move(loc))
{
}
parse_error(const CXCursor& cur, std::string message)
: parse_error(make_location(cur), std::move(message))
{
}
parse_error(const CXType& type, std::string message)
: parse_error(make_location(type), std::move(message))
{
}
diagnostic get_diagnostic(const CXFile& file)
{
return get_diagnostic(cxstring(clang_getFileName(file)).c_str());
}
diagnostic get_diagnostic(std::string file)
{
location_.file = std::move(file);
return diagnostic{what(), location_, severity::error};
}
private:
source_location location_;
};
// DEBUG_ASSERT handler for parse errors
// throws a parse_error exception
struct parse_error_handler : debug_assert::set_level<1>, debug_assert::allow_exception
{
static void handle(const debug_assert::source_location&, const char*,
const CXCursor& cur, std::string message)
{
throw parse_error(cur, std::move(message));
}
static void handle(const debug_assert::source_location&, const char*,
const CXType& type, std::string message)
{
throw parse_error(type, std::move(message));
}
};
} }
} // namespace cppast::detail
inline source_location make_location(const CXFile& file, const CXCursor& cur)
{
return source_location::make_entity(get_display_name(cur).c_str(),
cxstring(clang_getFileName(file)).c_str());
}
inline source_location make_location(const CXType& type)
{
return source_location::make_entity(cxstring(clang_getTypeSpelling(type)).c_str());
}
// thrown on a parsing error
// not meant to escape to the user
class parse_error : public std::logic_error
{
public:
parse_error(source_location loc, std::string message)
: std::logic_error(std::move(message)), location_(std::move(loc))
{}
parse_error(const CXCursor& cur, std::string message)
: parse_error(make_location(cur), std::move(message))
{}
parse_error(const CXType& type, std::string message)
: parse_error(make_location(type), std::move(message))
{}
diagnostic get_diagnostic(const CXFile& file)
{
return get_diagnostic(cxstring(clang_getFileName(file)).c_str());
}
diagnostic get_diagnostic(std::string file)
{
location_.file = std::move(file);
return diagnostic{what(), location_, severity::error};
}
private:
source_location location_;
};
// DEBUG_ASSERT handler for parse errors
// throws a parse_error exception
struct parse_error_handler : debug_assert::set_level<1>, debug_assert::allow_exception
{
static void handle(const debug_assert::source_location&, const char*, const CXCursor& cur,
std::string message)
{
throw parse_error(cur, std::move(message));
}
static void handle(const debug_assert::source_location&, const char*, const CXType& type,
std::string message)
{
throw parse_error(type, std::move(message));
}
};
} // namespace detail
} // namespace cppast
#endif // CPPAST_PARSE_ERROR_HPP_INCLUDED #endif // CPPAST_PARSE_ERROR_HPP_INCLUDED

View file

@ -4,8 +4,8 @@
#include "parse_functions.hpp" #include "parse_functions.hpp"
#include <cppast/cpp_storage_class_specifiers.hpp>
#include <cppast/cpp_static_assert.hpp> #include <cppast/cpp_static_assert.hpp>
#include <cppast/cpp_storage_class_specifiers.hpp>
#include "libclang_visitor.hpp" #include "libclang_visitor.hpp"
@ -100,16 +100,16 @@ void detail::comment_context::match(cpp_entity& e, unsigned line, bool skip_comm
namespace namespace
{ {
bool is_friend(const CXCursor& parent_cur) bool is_friend(const CXCursor& parent_cur)
{ {
#if CPPAST_CINDEX_HAS_FRIEND #if CPPAST_CINDEX_HAS_FRIEND
return clang_getCursorKind(parent_cur) == CXCursor_FriendDecl; return clang_getCursorKind(parent_cur) == CXCursor_FriendDecl;
#else #else
(void)parent_cur; (void)parent_cur;
return false; return false;
#endif #endif
}
} }
} // namespace
std::unique_ptr<cpp_entity> detail::parse_entity(const detail::parse_context& context, std::unique_ptr<cpp_entity> detail::parse_entity(const detail::parse_context& context,
cpp_entity* parent, const CXCursor& cur, cpp_entity* parent, const CXCursor& cur,
@ -165,25 +165,25 @@ std::unique_ptr<cpp_entity> detail::parse_entity(const detail::parse_context& co
return parse_cpp_member_variable(context, cur); return parse_cpp_member_variable(context, cur);
case CXCursor_FunctionDecl: case CXCursor_FunctionDecl:
if (auto tfunc = if (auto tfunc
try_parse_cpp_function_template_specialization(context, cur, is_friend(parent_cur))) = try_parse_cpp_function_template_specialization(context, cur, is_friend(parent_cur)))
return tfunc; return tfunc;
return parse_cpp_function(context, cur, is_friend(parent_cur)); return parse_cpp_function(context, cur, is_friend(parent_cur));
case CXCursor_CXXMethod: case CXCursor_CXXMethod:
if (auto tfunc = if (auto tfunc
try_parse_cpp_function_template_specialization(context, cur, is_friend(parent_cur))) = try_parse_cpp_function_template_specialization(context, cur, is_friend(parent_cur)))
return tfunc; return tfunc;
else if (auto func = try_parse_static_cpp_function(context, cur)) else if (auto func = try_parse_static_cpp_function(context, cur))
return func; return func;
return parse_cpp_member_function(context, cur, is_friend(parent_cur)); return parse_cpp_member_function(context, cur, is_friend(parent_cur));
case CXCursor_ConversionFunction: case CXCursor_ConversionFunction:
if (auto tfunc = if (auto tfunc
try_parse_cpp_function_template_specialization(context, cur, is_friend(parent_cur))) = try_parse_cpp_function_template_specialization(context, cur, is_friend(parent_cur)))
return tfunc; return tfunc;
return parse_cpp_conversion_op(context, cur, is_friend(parent_cur)); return parse_cpp_conversion_op(context, cur, is_friend(parent_cur));
case CXCursor_Constructor: case CXCursor_Constructor:
if (auto tfunc = if (auto tfunc
try_parse_cpp_function_template_specialization(context, cur, is_friend(parent_cur))) = try_parse_cpp_function_template_specialization(context, cur, is_friend(parent_cur)))
return tfunc; return tfunc;
return parse_cpp_constructor(context, cur, is_friend(parent_cur)); return parse_cpp_constructor(context, cur, is_friend(parent_cur));
case CXCursor_Destructor: case CXCursor_Destructor:

View file

@ -8,166 +8,161 @@
#include <cppast/cpp_entity.hpp> #include <cppast/cpp_entity.hpp>
#include <cppast/parser.hpp> #include <cppast/parser.hpp>
#include "raii_wrapper.hpp"
#include "cxtokenizer.hpp" // for convenience #include "cxtokenizer.hpp" // for convenience
#include "parse_error.hpp" // for convenience #include "parse_error.hpp" // for convenience
#include "preprocessor.hpp" #include "preprocessor.hpp"
#include "raii_wrapper.hpp"
#if CINDEX_VERSION_MINOR >= 36 #if CINDEX_VERSION_MINOR >= 36
#define CPPAST_CINDEX_HAS_FRIEND 1 # define CPPAST_CINDEX_HAS_FRIEND 1
#else #else
#define CPPAST_CINDEX_HAS_FRIEND 0 # define CPPAST_CINDEX_HAS_FRIEND 0
#endif #endif
namespace cppast namespace cppast
{ {
class cpp_expression; class cpp_expression;
class cpp_type; class cpp_type;
enum cpp_storage_class_specifiers : int; enum cpp_storage_class_specifiers : int;
namespace detail namespace detail
{
cpp_entity_id get_entity_id(const CXCursor& cur);
// only use this if the name is just a single token
// never where it is a reference to something (like base class name)
// as then you won't get it "as-is"
cxstring get_cursor_name(const CXCursor& cur);
// note: does not handle thread_local
cpp_storage_class_specifiers get_storage_class(const CXCursor& cur);
class comment_context
{ {
cpp_entity_id get_entity_id(const CXCursor& cur); public:
explicit comment_context(std::vector<pp_doc_comment>& comments)
: cur_(comments.data()), end_(comments.data() + comments.size())
{}
// only use this if the name is just a single token // must be called for entities that want an associated comment
// never where it is a reference to something (like base class name) // must be called *BEFORE* the children are added
// as then you won't get it "as-is" void match(cpp_entity& e, const CXCursor& cur) const;
cxstring get_cursor_name(const CXCursor& cur); void match(cpp_entity& e, unsigned line, bool skip_comments = true) const;
// note: does not handle thread_local private:
cpp_storage_class_specifiers get_storage_class(const CXCursor& cur); mutable pp_doc_comment* cur_;
pp_doc_comment* end_;
};
class comment_context struct parse_context
{ {
public: CXTranslationUnit tu;
explicit comment_context(std::vector<pp_doc_comment>& comments) CXFile file;
: cur_(comments.data()), end_(comments.data() + comments.size()) type_safe::object_ref<const diagnostic_logger> logger;
{ type_safe::object_ref<const cpp_entity_index> idx;
} comment_context comments;
mutable bool error;
};
// must be called for entities that want an associated comment // parse default value of variable, function parameter...
// must be called *BEFORE* the children are added std::unique_ptr<cpp_expression> parse_default_value(cpp_attribute_list& attributes,
void match(cpp_entity& e, const CXCursor& cur) const; const parse_context& context,
void match(cpp_entity& e, unsigned line, bool skip_comments = true) const; const CXCursor& cur, const char* name);
private: std::unique_ptr<cpp_type> parse_type(const parse_context& context, const CXCursor& cur,
mutable pp_doc_comment* cur_; const CXType& type);
pp_doc_comment* end_;
};
struct parse_context // parse the type starting at the current token stream
{ // and ends at the given iterator
CXTranslationUnit tu; // this is required for situations where there is no type exposed,
CXFile file; // like default type of a template type parameter
type_safe::object_ref<const diagnostic_logger> logger; std::unique_ptr<cpp_type> parse_raw_type(const parse_context& context, cxtoken_stream& stream,
type_safe::object_ref<const cpp_entity_index> idx; cxtoken_iterator end);
comment_context comments;
mutable bool error;
};
// parse default value of variable, function parameter... std::unique_ptr<cpp_expression> parse_expression(const parse_context& context,
std::unique_ptr<cpp_expression> parse_default_value(cpp_attribute_list& attributes,
const parse_context& context,
const CXCursor& cur, const char* name);
std::unique_ptr<cpp_type> parse_type(const parse_context& context, const CXCursor& cur,
const CXType& type);
// parse the type starting at the current token stream
// and ends at the given iterator
// this is required for situations where there is no type exposed,
// like default type of a template type parameter
std::unique_ptr<cpp_type> parse_raw_type(const parse_context& context,
cxtoken_stream& stream, cxtoken_iterator end);
std::unique_ptr<cpp_expression> parse_expression(const parse_context& context,
const CXCursor& cur);
// parse the expression starting at the current token in the stream
// and ends at the given iterator
// this is required for situations where there is no expression cursor exposed,
// like member initializers
std::unique_ptr<cpp_expression> parse_raw_expression(const parse_context& context,
cxtoken_stream& stream,
cxtoken_iterator end,
std::unique_ptr<cpp_type> type);
// parse_entity() dispatches on the cursor type
// it calls one of the other parse functions defined elsewhere
// try_parse_XXX are not exposed/differently exposed entities
// they are called on corresponding cursor and see whether they match
// unexposed
std::unique_ptr<cpp_entity> try_parse_cpp_language_linkage(const parse_context& context,
const CXCursor& cur);
// CXXMethod
std::unique_ptr<cpp_entity> try_parse_static_cpp_function(const parse_context& context,
const CXCursor& cur);
// on all function cursors except on destructor
std::unique_ptr<cpp_entity> try_parse_cpp_function_template_specialization(
const parse_context& context, const CXCursor& cur, bool is_friend);
// on class cursors
std::unique_ptr<cpp_entity> try_parse_full_cpp_class_template_specialization(
const parse_context& context, const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_namespace(const parse_context& context,
cpp_entity& parent, const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_namespace_alias(const parse_context& context,
const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_using_directive(const parse_context& context,
const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_using_declaration(const parse_context& context,
const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_type_alias(const parse_context& context,
const CXCursor& cur,
const CXCursor& template_cur);
std::unique_ptr<cpp_entity> parse_cpp_enum(const parse_context& context,
const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_class(const parse_context& context,
const CXCursor& cur,
const CXCursor& parent_cur);
std::unique_ptr<cpp_entity> parse_cpp_variable(const parse_context& context,
const CXCursor& cur);
// also parses bitfields
std::unique_ptr<cpp_entity> parse_cpp_member_variable(const parse_context& context,
const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_function(const parse_context& context,
const CXCursor& cur, bool is_friend);
std::unique_ptr<cpp_entity> parse_cpp_member_function(const parse_context& context,
const CXCursor& cur, bool is_friend);
std::unique_ptr<cpp_entity> parse_cpp_conversion_op(const parse_context& context,
const CXCursor& cur, bool is_friend);
std::unique_ptr<cpp_entity> parse_cpp_constructor(const parse_context& context,
const CXCursor& cur, bool is_friend);
std::unique_ptr<cpp_entity> parse_cpp_destructor(const parse_context& context,
const CXCursor& cur, bool is_friend);
std::unique_ptr<cpp_entity> parse_cpp_friend(const parse_context& context,
const CXCursor& cur); const CXCursor& cur);
// parse the expression starting at the current token in the stream
// and ends at the given iterator
// this is required for situations where there is no expression cursor exposed,
// like member initializers
std::unique_ptr<cpp_expression> parse_raw_expression(const parse_context& context,
cxtoken_stream& stream,
cxtoken_iterator end,
std::unique_ptr<cpp_type> type);
std::unique_ptr<cpp_entity> parse_cpp_alias_template(const parse_context& context, // parse_entity() dispatches on the cursor type
const CXCursor& cur); // it calls one of the other parse functions defined elsewhere
std::unique_ptr<cpp_entity> parse_cpp_function_template(const parse_context& context, // try_parse_XXX are not exposed/differently exposed entities
const CXCursor& cur, // they are called on corresponding cursor and see whether they match
bool is_friend);
std::unique_ptr<cpp_entity> parse_cpp_class_template(const parse_context& context,
const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_class_template_specialization(
const parse_context& context, const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_static_assert(const parse_context& context, // unexposed
std::unique_ptr<cpp_entity> try_parse_cpp_language_linkage(const parse_context& context,
const CXCursor& cur);
// CXXMethod
std::unique_ptr<cpp_entity> try_parse_static_cpp_function(const parse_context& context,
const CXCursor& cur);
// on all function cursors except on destructor
std::unique_ptr<cpp_entity> try_parse_cpp_function_template_specialization(
const parse_context& context, const CXCursor& cur, bool is_friend);
// on class cursors
std::unique_ptr<cpp_entity> try_parse_full_cpp_class_template_specialization(
const parse_context& context, const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_namespace(const parse_context& context,
cpp_entity& parent, const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_namespace_alias(const parse_context& context,
const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_using_directive(const parse_context& context,
const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_using_declaration(const parse_context& context,
const CXCursor& cur); const CXCursor& cur);
// parent: used for nested namespace, doesn't matter otherwise std::unique_ptr<cpp_entity> parse_cpp_type_alias(const parse_context& context,
// parent_cur: used when parsing templates or friends const CXCursor& cur,
std::unique_ptr<cpp_entity> parse_entity( const CXCursor& template_cur);
const parse_context& context, cpp_entity* parent, const CXCursor& cur, std::unique_ptr<cpp_entity> parse_cpp_enum(const parse_context& context, const CXCursor& cur);
const CXCursor& parent_cur = clang_getNullCursor()); std::unique_ptr<cpp_entity> parse_cpp_class(const parse_context& context, const CXCursor& cur,
} const CXCursor& parent_cur);
} // namespace cppast::detail
std::unique_ptr<cpp_entity> parse_cpp_variable(const parse_context& context,
const CXCursor& cur);
// also parses bitfields
std::unique_ptr<cpp_entity> parse_cpp_member_variable(const parse_context& context,
const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_function(const parse_context& context,
const CXCursor& cur, bool is_friend);
std::unique_ptr<cpp_entity> parse_cpp_member_function(const parse_context& context,
const CXCursor& cur, bool is_friend);
std::unique_ptr<cpp_entity> parse_cpp_conversion_op(const parse_context& context,
const CXCursor& cur, bool is_friend);
std::unique_ptr<cpp_entity> parse_cpp_constructor(const parse_context& context,
const CXCursor& cur, bool is_friend);
std::unique_ptr<cpp_entity> parse_cpp_destructor(const parse_context& context,
const CXCursor& cur, bool is_friend);
std::unique_ptr<cpp_entity> parse_cpp_friend(const parse_context& context, const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_alias_template(const parse_context& context,
const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_function_template(const parse_context& context,
const CXCursor& cur, bool is_friend);
std::unique_ptr<cpp_entity> parse_cpp_class_template(const parse_context& context,
const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_class_template_specialization(
const parse_context& context, const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_static_assert(const parse_context& context,
const CXCursor& cur);
// parent: used for nested namespace, doesn't matter otherwise
// parent_cur: used when parsing templates or friends
std::unique_ptr<cpp_entity> parse_entity(const parse_context& context, cpp_entity* parent,
const CXCursor& cur,
const CXCursor& parent_cur = clang_getNullCursor());
} // namespace detail
} // namespace cppast
#endif // CPPAST_PARSE_FUNCTIONS_HPP_INCLUDED #endif // CPPAST_PARSE_FUNCTIONS_HPP_INCLUDED

File diff suppressed because it is too large Load diff

View file

@ -10,46 +10,46 @@
namespace cppast namespace cppast
{ {
namespace detail namespace detail
{
struct pp_macro
{ {
struct pp_macro std::unique_ptr<cpp_macro_definition> macro;
unsigned line;
};
struct pp_include
{
std::string file_name, full_path;
cpp_include_kind kind;
unsigned line;
};
struct pp_doc_comment
{
std::string comment;
unsigned line;
enum
{ {
std::unique_ptr<cpp_macro_definition> macro; c,
unsigned line; cpp,
}; end_of_line,
} kind;
struct pp_include bool matches(const cpp_entity& e, unsigned line);
{ };
std::string file_name, full_path;
cpp_include_kind kind;
unsigned line;
};
struct pp_doc_comment struct preprocessor_output
{ {
std::string comment; std::string source;
unsigned line; std::vector<pp_include> includes;
enum std::vector<pp_macro> macros;
{ std::vector<pp_doc_comment> comments;
c, };
cpp,
end_of_line,
} kind;
bool matches(const cpp_entity& e, unsigned line); preprocessor_output preprocess(const libclang_compile_config& config, const char* path,
}; const diagnostic_logger& logger);
} // namespace detail
struct preprocessor_output } // namespace cppast
{
std::string source;
std::vector<pp_include> includes;
std::vector<pp_macro> macros;
std::vector<pp_doc_comment> comments;
};
preprocessor_output preprocess(const libclang_compile_config& config, const char* path,
const diagnostic_logger& logger);
}
} // namespace cppast::detail
#endif // CPPAST_PREPROCESSOR_HPP_INCLUDED #endif // CPPAST_PREPROCESSOR_HPP_INCLUDED

View file

@ -16,170 +16,169 @@
namespace cppast namespace cppast
{ {
namespace detail namespace detail
{
template <typename T, class Deleter>
class raii_wrapper : Deleter
{ {
template <typename T, class Deleter> static_assert(std::is_pointer<T>::value, "");
class raii_wrapper : Deleter
public:
raii_wrapper() noexcept : obj_(nullptr) {}
explicit raii_wrapper(T obj) noexcept : obj_(obj)
{ {
static_assert(std::is_pointer<T>::value, ""); DEBUG_ASSERT(obj_, detail::assert_handler{});
}
public: raii_wrapper(raii_wrapper&& other) noexcept : obj_(other.obj_)
raii_wrapper() noexcept : obj_(nullptr) {} {
DEBUG_ASSERT(obj_, detail::assert_handler{});
other.obj_ = nullptr;
}
explicit raii_wrapper(T obj) noexcept : obj_(obj) ~raii_wrapper() noexcept
{ {
DEBUG_ASSERT(obj_, detail::assert_handler{}); if (obj_)
} static_cast<Deleter&> (*this)(obj_);
}
raii_wrapper(raii_wrapper&& other) noexcept : obj_(other.obj_) raii_wrapper& operator=(raii_wrapper&& other) noexcept
{ {
DEBUG_ASSERT(obj_, detail::assert_handler{}); raii_wrapper tmp(std::move(other));
other.obj_ = nullptr; swap(*this, tmp);
} return *this;
}
~raii_wrapper() noexcept friend void swap(raii_wrapper& a, raii_wrapper& b) noexcept
{ {
if (obj_) std::swap(a.obj_, b.obj_);
static_cast<Deleter&> (*this)(obj_); }
}
raii_wrapper& operator=(raii_wrapper&& other) noexcept T get() const noexcept
{ {
raii_wrapper tmp(std::move(other)); DEBUG_ASSERT(obj_, detail::assert_handler{});
swap(*this, tmp); return obj_;
return *this; }
}
friend void swap(raii_wrapper& a, raii_wrapper& b) noexcept private:
{ T obj_;
std::swap(a.obj_, b.obj_); };
}
T get() const noexcept struct cxindex_deleter
{ {
DEBUG_ASSERT(obj_, detail::assert_handler{}); void operator()(CXIndex idx) noexcept
return obj_; {
} clang_disposeIndex(idx);
}
};
private: using cxindex = raii_wrapper<CXIndex, cxindex_deleter>;
T obj_;
struct cxtranslation_unit_deleter
{
void operator()(CXTranslationUnit unit) noexcept
{
clang_disposeTranslationUnit(unit);
}
};
using cxtranslation_unit = raii_wrapper<CXTranslationUnit, cxtranslation_unit_deleter>;
class cxstring
{
public:
explicit cxstring(CXString str) noexcept : str_(string(str)) {}
cxstring(cxstring&& other) noexcept : str_(other.str_)
{
other.str_.reset();
}
cxstring& operator=(cxstring&& other) noexcept
{
if (str_)
clang_disposeString(str_.value().str);
str_ = other.str_;
other.str_.reset();
return *this;
}
~cxstring() noexcept
{
if (str_)
clang_disposeString(str_.value().str);
}
const char* c_str() const noexcept
{
return str_ ? str_.value().c_str : "";
}
std::string std_str() const noexcept
{
return c_str();
}
char operator[](std::size_t i) const noexcept
{
return c_str()[i];
}
std::size_t length() const noexcept
{
return str_ ? str_.value().length : 0u;
}
bool empty() const noexcept
{
return length() == 0u;
}
private:
struct string
{
CXString str;
const char* c_str;
std::size_t length;
explicit string(CXString str)
: str(std::move(str)), c_str(clang_getCString(str)), length(std::strlen(c_str))
{}
}; };
type_safe::optional<string> str_;
};
struct cxindex_deleter inline bool operator==(const cxstring& a, const cxstring& b) noexcept
{ {
void operator()(CXIndex idx) noexcept return std::strcmp(a.c_str(), b.c_str()) == 0;
{
clang_disposeIndex(idx);
}
};
using cxindex = raii_wrapper<CXIndex, cxindex_deleter>;
struct cxtranslation_unit_deleter
{
void operator()(CXTranslationUnit unit) noexcept
{
clang_disposeTranslationUnit(unit);
}
};
using cxtranslation_unit = raii_wrapper<CXTranslationUnit, cxtranslation_unit_deleter>;
class cxstring
{
public:
explicit cxstring(CXString str) noexcept : str_(string(str)) {}
cxstring(cxstring&& other) noexcept : str_(other.str_)
{
other.str_.reset();
}
cxstring& operator=(cxstring&& other) noexcept
{
if (str_)
clang_disposeString(str_.value().str);
str_ = other.str_;
other.str_.reset();
return *this;
}
~cxstring() noexcept
{
if (str_)
clang_disposeString(str_.value().str);
}
const char* c_str() const noexcept
{
return str_ ? str_.value().c_str : "";
}
std::string std_str() const noexcept
{
return c_str();
}
char operator[](std::size_t i) const noexcept
{
return c_str()[i];
}
std::size_t length() const noexcept
{
return str_ ? str_.value().length : 0u;
}
bool empty() const noexcept
{
return length() == 0u;
}
private:
struct string
{
CXString str;
const char* c_str;
std::size_t length;
explicit string(CXString str)
: str(std::move(str)), c_str(clang_getCString(str)), length(std::strlen(c_str))
{
}
};
type_safe::optional<string> str_;
};
inline bool operator==(const cxstring& a, const cxstring& b) noexcept
{
return std::strcmp(a.c_str(), b.c_str()) == 0;
}
inline bool operator==(const cxstring& a, const char* str) noexcept
{
return std::strcmp(a.c_str(), str) == 0;
}
inline bool operator==(const char* str, const cxstring& b) noexcept
{
return std::strcmp(str, b.c_str()) == 0;
}
inline bool operator!=(const cxstring& a, const cxstring& b) noexcept
{
return !(a == b);
}
inline bool operator!=(const cxstring& a, const char* str) noexcept
{
return !(a == str);
}
inline bool operator!=(const char* str, const cxstring& b) noexcept
{
return !(str == b);
}
} }
} // namespace cppast::detail
inline bool operator==(const cxstring& a, const char* str) noexcept
{
return std::strcmp(a.c_str(), str) == 0;
}
inline bool operator==(const char* str, const cxstring& b) noexcept
{
return std::strcmp(str, b.c_str()) == 0;
}
inline bool operator!=(const cxstring& a, const cxstring& b) noexcept
{
return !(a == b);
}
inline bool operator!=(const cxstring& a, const char* str) noexcept
{
return !(a == str);
}
inline bool operator!=(const char* str, const cxstring& b) noexcept
{
return !(str == b);
}
} // namespace detail
} // namespace cppast
#endif // CPPAST_RAII_WRAPPER_HPP_INCLUDED #endif // CPPAST_RAII_WRAPPER_HPP_INCLUDED

View file

@ -15,214 +15,209 @@ using namespace cppast;
namespace namespace
{ {
template <typename TemplateT, typename EntityT, typename Predicate> template <typename TemplateT, typename EntityT, typename Predicate>
type_safe::optional<typename TemplateT::builder> get_builder( type_safe::optional<typename TemplateT::builder> get_builder(const detail::parse_context& context,
const detail::parse_context& context, const CXCursor& cur, Predicate p) const CXCursor& cur, Predicate p)
{ {
// we need the actual entity first, then the parameters // we need the actual entity first, then the parameters
// so two visit calls are required // so two visit calls are required
auto result = clang_getNullCursor(); auto result = clang_getNullCursor();
detail::visit_children(cur, [&](const CXCursor& child) { detail::visit_children(cur, [&](const CXCursor& child) {
auto kind = clang_getCursorKind(child); auto kind = clang_getCursorKind(child);
if (kind == CXCursor_TemplateTypeParameter || kind == CXCursor_NonTypeTemplateParameter if (kind == CXCursor_TemplateTypeParameter || kind == CXCursor_NonTypeTemplateParameter
|| kind == CXCursor_TemplateTemplateParameter) || kind == CXCursor_TemplateTemplateParameter)
return; return;
DEBUG_ASSERT(clang_Cursor_isNull(result), detail::parse_error_handler{}, cur, DEBUG_ASSERT(clang_Cursor_isNull(result), detail::parse_error_handler{}, cur,
"unexpected child of template"); "unexpected child of template");
result = child; result = child;
}); });
DEBUG_ASSERT(!clang_Cursor_isNull(result), detail::parse_error_handler{}, cur, DEBUG_ASSERT(!clang_Cursor_isNull(result), detail::parse_error_handler{}, cur,
"missing child of template"); "missing child of template");
auto entity = detail::parse_entity(context, nullptr, result, cur); auto entity = detail::parse_entity(context, nullptr, result, cur);
if (!entity) if (!entity)
return type_safe::nullopt; return type_safe::nullopt;
DEBUG_ASSERT(p(entity->kind()), detail::parse_error_handler{}, cur, DEBUG_ASSERT(p(entity->kind()), detail::parse_error_handler{}, cur, "wrong child of template");
"wrong child of template"); return typename TemplateT::builder(
return typename TemplateT::builder( std::unique_ptr<EntityT>(static_cast<EntityT*>(entity.release())));
std::unique_ptr<EntityT>(static_cast<EntityT*>(entity.release()))); }
}
std::unique_ptr<cpp_template_parameter> parse_type_parameter( std::unique_ptr<cpp_template_parameter> parse_type_parameter(const detail::parse_context& context,
const detail::parse_context& context, const CXCursor& cur) const CXCursor& cur)
{ {
DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_TemplateTypeParameter, DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_TemplateTypeParameter,
detail::assert_handler{}); detail::assert_handler{});
detail::cxtokenizer tokenizer(context.tu, context.file, cur); detail::cxtokenizer tokenizer(context.tu, context.file, cur);
detail::cxtoken_stream stream(tokenizer, cur); detail::cxtoken_stream stream(tokenizer, cur);
auto name = detail::get_cursor_name(cur); auto name = detail::get_cursor_name(cur);
// syntax: typename/class [...] name [= ...] // syntax: typename/class [...] name [= ...]
auto keyword = cpp_template_keyword::keyword_class; auto keyword = cpp_template_keyword::keyword_class;
if (detail::skip_if(stream, "typename")) if (detail::skip_if(stream, "typename"))
keyword = cpp_template_keyword::keyword_typename; keyword = cpp_template_keyword::keyword_typename;
else else
detail::skip(stream, "class"); detail::skip(stream, "class");
auto variadic = false; auto variadic = false;
if (detail::skip_if(stream, "...")) if (detail::skip_if(stream, "..."))
variadic = true; variadic = true;
if (stream.peek() != "=") if (stream.peek() != "=")
detail::skip(stream, name.c_str());
std::unique_ptr<cpp_type> def;
if (detail::skip_if(stream, "="))
// default type
def = detail::parse_raw_type(context, stream, stream.end());
return cpp_template_type_parameter::build(*context.idx, detail::get_entity_id(cur),
name.c_str(), keyword, variadic, std::move(def));
}
std::unique_ptr<cpp_template_parameter> parse_non_type_parameter(
const detail::parse_context& context, const CXCursor& cur)
{
DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_NonTypeTemplateParameter,
detail::assert_handler{});
auto name = detail::get_cursor_name(cur);
auto type = clang_getCursorType(cur);
cpp_attribute_list attributes;
auto def = detail::parse_default_value(attributes, context, cur, name.c_str());
detail::cxtokenizer tokenizer(context.tu, context.file, cur);
detail::cxtoken_stream stream(tokenizer, cur);
// see if it is variadic
// syntax a): some-tokens ... name some-tokens
// syntax b): some-tokens (some-tokens ... name) some-tokens-or-...
// name might be empty, so can't loop until it occurs
// some-tokens will not contain ... or parenthesis, however
auto is_variadic = false;
for (; !stream.done(); stream.bump())
{
if (stream.peek() == "...")
{
is_variadic = true;
break;
}
else if (stream.peek() == ")")
break;
}
auto result =
cpp_non_type_template_parameter::build(*context.idx, detail::get_entity_id(cur),
name.c_str(),
detail::parse_type(context, cur, type),
is_variadic, std::move(def));
result->add_attribute(attributes);
return std::move(result);
}
std::unique_ptr<cpp_template_template_parameter> parse_template_parameter(
const detail::parse_context& context, const CXCursor& cur)
{
DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_TemplateTemplateParameter,
detail::assert_handler{});
detail::cxtokenizer tokenizer(context.tu, context.file, cur);
detail::cxtoken_stream stream(tokenizer, cur);
auto name = detail::get_cursor_name(cur);
// syntax: template <…> class/typename [...] name [= …]
detail::skip(stream, "template");
detail::skip_brackets(stream);
auto keyword = cpp_template_keyword::keyword_class;
if (detail::skip_if(stream, "typename"))
keyword = cpp_template_keyword::keyword_typename;
else
detail::skip(stream, "class");
auto is_variadic = detail::skip_if(stream, "...");
detail::skip(stream, name.c_str()); detail::skip(stream, name.c_str());
// now we can create the builder std::unique_ptr<cpp_type> def;
cpp_template_template_parameter::builder builder(name.c_str(), is_variadic); if (detail::skip_if(stream, "="))
builder.keyword(keyword); // default type
def = detail::parse_raw_type(context, stream, stream.end());
// look for parameters and default return cpp_template_type_parameter::build(*context.idx, detail::get_entity_id(cur),
detail::visit_children(cur, [&](const CXCursor& child) { name.c_str(), keyword, variadic, std::move(def));
auto kind = clang_getCursorKind(child);
if (kind == CXCursor_TemplateTypeParameter)
builder.add_parameter(parse_type_parameter(context, child));
else if (kind == CXCursor_NonTypeTemplateParameter)
builder.add_parameter(parse_non_type_parameter(context, child));
else if (kind == CXCursor_TemplateTemplateParameter)
builder.add_parameter(parse_template_parameter(context, child));
else if (kind == CXCursor_TemplateRef)
{
auto target = clang_getCursorReferenced(child);
// stream is after the keyword
// syntax: = default
detail::skip(stream, "=");
std::string spelling;
while (!stream.done())
spelling += stream.get().c_str();
if (stream.unmunch())
{
DEBUG_ASSERT(!spelling.empty() && spelling.back() == '>',
detail::assert_handler{});
spelling.pop_back();
DEBUG_ASSERT(!spelling.empty() && spelling.back() == '>',
detail::assert_handler{});
}
builder.default_template(
cpp_template_ref(detail::get_entity_id(target), std::move(spelling)));
}
else
DEBUG_ASSERT(clang_isReference(kind), detail::parse_error_handler{}, cur,
"unexpected child of template template parameter");
});
return builder.finish(*context.idx, detail::get_entity_id(cur));
}
template <class Builder>
void parse_parameters(Builder& builder, const detail::parse_context& context,
const CXCursor& cur)
{
// now visit to get the parameters
detail::visit_children(cur, [&](const CXCursor& child) {
auto kind = clang_getCursorKind(child);
if (kind == CXCursor_TemplateTypeParameter)
builder.add_parameter(parse_type_parameter(context, child));
else if (kind == CXCursor_NonTypeTemplateParameter)
builder.add_parameter(parse_non_type_parameter(context, child));
else if (kind == CXCursor_TemplateTemplateParameter)
builder.add_parameter(parse_template_parameter(context, child));
});
}
void handle_comment_attributes(cpp_entity& templ_entity, cpp_entity& non_template)
{
// steal comment
auto comment = type_safe::copy(non_template.comment());
non_template.set_comment(type_safe::nullopt);
templ_entity.set_comment(std::move(comment));
// copy attributes over
templ_entity.add_attribute(non_template.attributes());
}
} }
std::unique_ptr<cpp_template_parameter> parse_non_type_parameter(
const detail::parse_context& context, const CXCursor& cur)
{
DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_NonTypeTemplateParameter,
detail::assert_handler{});
auto name = detail::get_cursor_name(cur);
auto type = clang_getCursorType(cur);
cpp_attribute_list attributes;
auto def = detail::parse_default_value(attributes, context, cur, name.c_str());
detail::cxtokenizer tokenizer(context.tu, context.file, cur);
detail::cxtoken_stream stream(tokenizer, cur);
// see if it is variadic
// syntax a): some-tokens ... name some-tokens
// syntax b): some-tokens (some-tokens ... name) some-tokens-or-...
// name might be empty, so can't loop until it occurs
// some-tokens will not contain ... or parenthesis, however
auto is_variadic = false;
for (; !stream.done(); stream.bump())
{
if (stream.peek() == "...")
{
is_variadic = true;
break;
}
else if (stream.peek() == ")")
break;
}
auto result = cpp_non_type_template_parameter::build(*context.idx, detail::get_entity_id(cur),
name.c_str(),
detail::parse_type(context, cur, type),
is_variadic, std::move(def));
result->add_attribute(attributes);
return std::move(result);
}
std::unique_ptr<cpp_template_template_parameter> parse_template_parameter(
const detail::parse_context& context, const CXCursor& cur)
{
DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_TemplateTemplateParameter,
detail::assert_handler{});
detail::cxtokenizer tokenizer(context.tu, context.file, cur);
detail::cxtoken_stream stream(tokenizer, cur);
auto name = detail::get_cursor_name(cur);
// syntax: template <…> class/typename [...] name [= …]
detail::skip(stream, "template");
detail::skip_brackets(stream);
auto keyword = cpp_template_keyword::keyword_class;
if (detail::skip_if(stream, "typename"))
keyword = cpp_template_keyword::keyword_typename;
else
detail::skip(stream, "class");
auto is_variadic = detail::skip_if(stream, "...");
detail::skip(stream, name.c_str());
// now we can create the builder
cpp_template_template_parameter::builder builder(name.c_str(), is_variadic);
builder.keyword(keyword);
// look for parameters and default
detail::visit_children(cur, [&](const CXCursor& child) {
auto kind = clang_getCursorKind(child);
if (kind == CXCursor_TemplateTypeParameter)
builder.add_parameter(parse_type_parameter(context, child));
else if (kind == CXCursor_NonTypeTemplateParameter)
builder.add_parameter(parse_non_type_parameter(context, child));
else if (kind == CXCursor_TemplateTemplateParameter)
builder.add_parameter(parse_template_parameter(context, child));
else if (kind == CXCursor_TemplateRef)
{
auto target = clang_getCursorReferenced(child);
// stream is after the keyword
// syntax: = default
detail::skip(stream, "=");
std::string spelling;
while (!stream.done())
spelling += stream.get().c_str();
if (stream.unmunch())
{
DEBUG_ASSERT(!spelling.empty() && spelling.back() == '>', detail::assert_handler{});
spelling.pop_back();
DEBUG_ASSERT(!spelling.empty() && spelling.back() == '>', detail::assert_handler{});
}
builder.default_template(
cpp_template_ref(detail::get_entity_id(target), std::move(spelling)));
}
else
DEBUG_ASSERT(clang_isReference(kind), detail::parse_error_handler{}, cur,
"unexpected child of template template parameter");
});
return builder.finish(*context.idx, detail::get_entity_id(cur));
}
template <class Builder>
void parse_parameters(Builder& builder, const detail::parse_context& context, const CXCursor& cur)
{
// now visit to get the parameters
detail::visit_children(cur, [&](const CXCursor& child) {
auto kind = clang_getCursorKind(child);
if (kind == CXCursor_TemplateTypeParameter)
builder.add_parameter(parse_type_parameter(context, child));
else if (kind == CXCursor_NonTypeTemplateParameter)
builder.add_parameter(parse_non_type_parameter(context, child));
else if (kind == CXCursor_TemplateTemplateParameter)
builder.add_parameter(parse_template_parameter(context, child));
});
}
void handle_comment_attributes(cpp_entity& templ_entity, cpp_entity& non_template)
{
// steal comment
auto comment = type_safe::copy(non_template.comment());
non_template.set_comment(type_safe::nullopt);
templ_entity.set_comment(std::move(comment));
// copy attributes over
templ_entity.add_attribute(non_template.attributes());
}
} // namespace
std::unique_ptr<cpp_entity> detail::parse_cpp_alias_template(const detail::parse_context& context, std::unique_ptr<cpp_entity> detail::parse_cpp_alias_template(const detail::parse_context& context,
const CXCursor& cur) const CXCursor& cur)
{ {
DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_TypeAliasTemplateDecl, DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_TypeAliasTemplateDecl,
detail::assert_handler{}); detail::assert_handler{});
auto builder = auto builder
get_builder<cpp_alias_template, cpp_type_alias>(context, cur, [](cpp_entity_kind k) { = get_builder<cpp_alias_template, cpp_type_alias>(context, cur, [](cpp_entity_kind k) {
return k == cpp_entity_kind::type_alias_t; return k == cpp_entity_kind::type_alias_t;
}); });
if (!builder) if (!builder)
return nullptr; return nullptr;
context.comments.match(builder.value().get(), cur); context.comments.match(builder.value().get(), cur);
@ -274,28 +269,27 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_function_template(
namespace namespace
{ {
template <class Builder> template <class Builder>
void parse_arguments(Builder& b, const detail::parse_context& context, const CXCursor& cur) void parse_arguments(Builder& b, const detail::parse_context& context, const CXCursor& cur)
{
detail::cxtokenizer tokenizer(context.tu, context.file, cur);
detail::cxtoken_stream stream(tokenizer, cur);
while (!stream.done() && !detail::skip_if(stream, detail::get_cursor_name(cur).c_str(), true))
stream.bump();
if (stream.peek() == "<")
{ {
detail::cxtokenizer tokenizer(context.tu, context.file, cur); auto iter = detail::find_closing_bracket(stream);
detail::cxtoken_stream stream(tokenizer, cur); stream.bump();
while (!stream.done() auto args = detail::to_string(stream, iter);
&& !detail::skip_if(stream, detail::get_cursor_name(cur).c_str(), true)) b.add_unexposed_arguments(std::move(args));
stream.bump();
if (stream.peek() == "<")
{
auto iter = detail::find_closing_bracket(stream);
stream.bump();
auto args = detail::to_string(stream, iter);
b.add_unexposed_arguments(std::move(args));
}
else
b.add_unexposed_arguments(cpp_token_string::builder().finish());
} }
else
b.add_unexposed_arguments(cpp_token_string::builder().finish());
} }
} // namespace
std::unique_ptr<cpp_entity> detail::try_parse_cpp_function_template_specialization( std::unique_ptr<cpp_entity> detail::try_parse_cpp_function_template_specialization(
const detail::parse_context& context, const CXCursor& cur, bool is_friend) const detail::parse_context& context, const CXCursor& cur, bool is_friend)

File diff suppressed because it is too large Load diff

View file

@ -64,8 +64,8 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_variable(const detail::parse_conte
detail::cxtokenizer tokenizer(context.tu, context.file, cur); detail::cxtokenizer tokenizer(context.tu, context.file, cur);
for (auto& token : tokenizer) for (auto& token : tokenizer)
if (token.value() == "thread_local") if (token.value() == "thread_local")
storage_class = storage_class
cpp_storage_class_specifiers(storage_class | cpp_storage_class_thread_local); = cpp_storage_class_specifiers(storage_class | cpp_storage_class_thread_local);
else if (token.value() == "constexpr") else if (token.value() == "constexpr")
is_constexpr = true; is_constexpr = true;
@ -75,9 +75,9 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_variable(const detail::parse_conte
std::unique_ptr<cpp_variable> result; std::unique_ptr<cpp_variable> result;
if (clang_isCursorDefinition(cur)) if (clang_isCursorDefinition(cur))
{ {
result = result
cpp_variable::build(*context.idx, get_entity_id(cur), name.c_str(), std::move(type), = cpp_variable::build(*context.idx, get_entity_id(cur), name.c_str(), std::move(type),
std::move(default_value), storage_class, is_constexpr); std::move(default_value), storage_class, is_constexpr);
} }
else else
result = cpp_variable::build_declaration(get_entity_id(cur), name.c_str(), std::move(type), result = cpp_variable::build_declaration(get_entity_id(cur), name.c_str(), std::move(type),

View file

@ -18,47 +18,46 @@ using namespace cppast;
namespace namespace
{ {
cpp_access_specifier_kind get_initial_access(const cpp_entity& e) cpp_access_specifier_kind get_initial_access(const cpp_entity& e)
{ {
if (e.kind() == cpp_class::kind()) if (e.kind() == cpp_class::kind())
return static_cast<const cpp_class&>(e).class_kind() == cpp_class_kind::class_t ? return static_cast<const cpp_class&>(e).class_kind() == cpp_class_kind::class_t
cpp_private : ? cpp_private
cpp_public; : cpp_public;
return cpp_public; return cpp_public;
} }
void update_access(cpp_access_specifier_kind& child_access, const cpp_entity& child) void update_access(cpp_access_specifier_kind& child_access, const cpp_entity& child)
{ {
if (child.kind() == cpp_access_specifier::kind()) if (child.kind() == cpp_access_specifier::kind())
child_access = static_cast<const cpp_access_specifier&>(child).access_specifier(); child_access = static_cast<const cpp_access_specifier&>(child).access_specifier();
} }
template <typename T> template <typename T>
bool handle_container(const cpp_entity& e, detail::visitor_callback_t cb, void* functor, bool handle_container(const cpp_entity& e, detail::visitor_callback_t cb, void* functor,
cpp_access_specifier_kind cur_access, bool last_child) cpp_access_specifier_kind cur_access, bool last_child)
{ {
auto& container = static_cast<const T&>(e); auto& container = static_cast<const T&>(e);
auto handle_children = auto handle_children
cb(functor, container, {visitor_info::container_entity_enter, cur_access, last_child}); = cb(functor, container, {visitor_info::container_entity_enter, cur_access, last_child});
if (handle_children) if (handle_children)
{
auto child_access = get_initial_access(e);
for (auto iter = container.begin(); iter != container.end();)
{ {
auto child_access = get_initial_access(e); auto& cur = *iter;
for (auto iter = container.begin(); iter != container.end();) ++iter;
{
auto& cur = *iter;
++iter;
update_access(child_access, cur); update_access(child_access, cur);
if (!detail::visit(cur, cb, functor, child_access, iter == container.end())) if (!detail::visit(cur, cb, functor, child_access, iter == container.end()))
return false; return false;
}
} }
return cb(functor, container,
{visitor_info::container_entity_exit, cur_access, last_child});
} }
return cb(functor, container, {visitor_info::container_entity_exit, cur_access, last_child});
}
} // namespace } // namespace
bool detail::visit(const cpp_entity& e, detail::visitor_callback_t cb, void* functor, bool detail::visit(const cpp_entity& e, detail::visitor_callback_t cb, void* functor,

View file

@ -46,81 +46,84 @@ alignas(type) int var;
auto file = parse({}, "cpp_attribute.cpp", code); auto file = parse({}, "cpp_attribute.cpp", code);
auto check_attribute = [](const cpp_attribute& attr, const char* name, auto check_attribute
type_safe::optional<std::string> scope, bool variadic, = [](const cpp_attribute& attr, const char* name, type_safe::optional<std::string> scope,
const char* args, cpp_attribute_kind kind) { bool variadic, const char* args, cpp_attribute_kind kind) {
REQUIRE(attr.kind() == kind); REQUIRE(attr.kind() == kind);
REQUIRE(attr.name() == name); REQUIRE(attr.name() == name);
REQUIRE(attr.scope() == scope); REQUIRE(attr.scope() == scope);
REQUIRE(attr.is_variadic() == variadic); REQUIRE(attr.is_variadic() == variadic);
if (attr.arguments()) if (attr.arguments())
REQUIRE(attr.arguments().value().as_string() == args); REQUIRE(attr.arguments().value().as_string() == args);
else else
REQUIRE(*args == '\0'); REQUIRE(*args == '\0');
}; };
auto count = auto count
test_visit<cpp_function>(*file, = test_visit<cpp_function>(*file,
[&](const cpp_entity& e) { [&](const cpp_entity& e) {
auto& attributes = e.attributes(); auto& attributes = e.attributes();
REQUIRE(attributes.size() >= 1u); REQUIRE(attributes.size() >= 1u);
auto& attr = attributes.front(); auto& attr = attributes.front();
if (e.name() == "a" || e.name() == "b") if (e.name() == "a" || e.name() == "b")
{ {
REQUIRE(attributes.size() == 2u); REQUIRE(attributes.size() == 2u);
REQUIRE(has_attribute(e, "attribute1")); REQUIRE(has_attribute(e, "attribute1"));
REQUIRE(has_attribute(e, "attribute2")); REQUIRE(has_attribute(e, "attribute2"));
check_attribute(attr, "attribute1", type_safe::nullopt, check_attribute(attr, "attribute1", type_safe::nullopt,
false, "", cpp_attribute_kind::unknown); false, "", cpp_attribute_kind::unknown);
check_attribute(attributes[1u], "attribute2", check_attribute(attributes[1u], "attribute2",
type_safe::nullopt, false, "", type_safe::nullopt, false, "",
cpp_attribute_kind::unknown); cpp_attribute_kind::unknown);
} }
else if (e.name() == "c") else if (e.name() == "c")
check_attribute(attr, "variadic", type_safe::nullopt, true, check_attribute(attr, "variadic", type_safe::nullopt,
"", cpp_attribute_kind::unknown); true, "", cpp_attribute_kind::unknown);
else if (e.name() == "d") else if (e.name() == "d")
{ {
REQUIRE(has_attribute(e, "ns::attribute")); REQUIRE(has_attribute(e, "ns::attribute"));
check_attribute(attr, "attribute", "ns", false, "", check_attribute(attr, "attribute", "ns", false, "",
cpp_attribute_kind::unknown); cpp_attribute_kind::unknown);
} }
else if (e.name() == "e") else if (e.name() == "e")
check_attribute(attr, "attribute", type_safe::nullopt, check_attribute(attr, "attribute", type_safe::nullopt,
false, R"(arg1,arg2,+(){},42,"Hello!")", false, R"(arg1,arg2,+(){},42,"Hello!")",
cpp_attribute_kind::unknown); cpp_attribute_kind::unknown);
else if (e.name() == "f") else if (e.name() == "f")
{ {
REQUIRE(attributes.size() == 2u); REQUIRE(attributes.size() == 2u);
check_attribute(attr, "attribute", "ns", false, "+,-,0 4", check_attribute(attr, "attribute", "ns", false,
cpp_attribute_kind::unknown); "+,-,0 4", cpp_attribute_kind::unknown);
check_attribute(attributes[1u], "other_attribute", check_attribute(attributes[1u], "other_attribute",
type_safe::nullopt, false, "", type_safe::nullopt, false, "",
cpp_attribute_kind::unknown); cpp_attribute_kind::unknown);
} }
else if (e.name() == "g") else if (e.name() == "g")
{ {
REQUIRE(has_attribute(e, cpp_attribute_kind::deprecated)); REQUIRE(
check_attribute(attr, "deprecated", type_safe::nullopt, has_attribute(e, cpp_attribute_kind::deprecated));
false, "", cpp_attribute_kind::deprecated); check_attribute(attr, "deprecated", type_safe::nullopt,
} false, "",
else if (e.name() == "h") cpp_attribute_kind::deprecated);
check_attribute(attr, "maybe_unused", type_safe::nullopt, }
false, "", else if (e.name() == "h")
cpp_attribute_kind::maybe_unused); check_attribute(attr, "maybe_unused", type_safe::nullopt,
else if (e.name() == "i") false, "",
check_attribute(attr, "nodiscard", type_safe::nullopt, cpp_attribute_kind::maybe_unused);
false, "", cpp_attribute_kind::nodiscard); else if (e.name() == "i")
else if (e.name() == "j") check_attribute(attr, "nodiscard", type_safe::nullopt,
check_attribute(attr, "noreturn", type_safe::nullopt, false, "",
false, "", cpp_attribute_kind::noreturn); cpp_attribute_kind::nodiscard);
else if (e.name() == "k") else if (e.name() == "j")
check_attribute(attr, "const", type_safe::nullopt, false, check_attribute(attr, "noreturn", type_safe::nullopt,
"", cpp_attribute_kind::unknown); false, "", cpp_attribute_kind::noreturn);
}, else if (e.name() == "k")
false); check_attribute(attr, "const", type_safe::nullopt, false,
"", cpp_attribute_kind::unknown);
},
false);
REQUIRE(count == 10); REQUIRE(count == 10);
count = test_visit<cpp_class>(*file, count = test_visit<cpp_class>(*file,

View file

@ -226,8 +226,8 @@ int d() {}
} }
else if (entity.value().kind() == cpp_entity_kind::function_template_specialization_t) else if (entity.value().kind() == cpp_entity_kind::function_template_specialization_t)
{ {
auto& func = auto& func
static_cast<const cpp_function_template_specialization&>(entity.value()); = static_cast<const cpp_function_template_specialization&>(entity.value());
if (func.name() == "templ_c") if (func.name() == "templ_c")
{ {
REQUIRE(func.function().is_declaration()); REQUIRE(func.function().is_declaration());

View file

@ -270,15 +270,15 @@ using ns1::d;
)"; )";
cpp_entity_index idx; cpp_entity_index idx;
auto check_declaration = [&](const cpp_using_declaration& decl, const char* target_full_name, auto check_declaration
unsigned no) { = [&](const cpp_using_declaration& decl, const char* target_full_name, unsigned no) {
auto target = decl.target(); auto target = decl.target();
REQUIRE((target.no_overloaded() == no)); REQUIRE((target.no_overloaded() == no));
for (auto entity : target.get(idx)) for (auto entity : target.get(idx))
{ {
REQUIRE(full_name(*entity) == target_full_name); REQUIRE(full_name(*entity) == target_full_name);
} }
}; };
auto file = parse(idx, "cpp_using_declaration.cpp", code); auto file = parse(idx, "cpp_using_declaration.cpp", code);
auto count = test_visit<cpp_using_declaration>(*file, [&](const cpp_using_declaration& decl) { auto count = test_visit<cpp_using_declaration>(*file, [&](const cpp_using_declaration& decl) {

View file

@ -34,36 +34,36 @@ namespace ns2
)"; )";
const char* order[] = {"A", "B", "ns", "C", "D", "E", "ns2", "F"}; const char* order[] = {"A", "B", "ns", "C", "D", "E", "ns2", "F"};
auto check_macro = [](const cpp_macro_definition& macro, const char* replacement, auto check_macro
const char* args) { = [](const cpp_macro_definition& macro, const char* replacement, const char* args) {
REQUIRE(macro.replacement() == replacement); REQUIRE(macro.replacement() == replacement);
if (args) if (args)
{ {
REQUIRE(macro.is_function_like()); REQUIRE(macro.is_function_like());
std::string params; std::string params;
for (auto& param : macro.parameters()) for (auto& param : macro.parameters())
{ {
if (!params.empty()) if (!params.empty())
params += ","; params += ",";
params += param.name(); params += param.name();
} }
if (macro.is_variadic()) if (macro.is_variadic())
{ {
if (!params.empty()) if (!params.empty())
params += ","; params += ",";
params += "..."; params += "...";
} }
REQUIRE(params == args); REQUIRE(params == args);
} }
else else
{ {
REQUIRE(!macro.is_function_like()); REQUIRE(!macro.is_function_like());
REQUIRE(!macro.is_variadic()); REQUIRE(!macro.is_variadic());
REQUIRE(macro.parameters().empty()); REQUIRE(macro.parameters().empty());
} }
}; };
auto file = parse({}, "cpp_macro_definition.cpp", code); auto file = parse({}, "cpp_macro_definition.cpp", code);
auto count = test_visit<cpp_macro_definition>(*file, [&](const cpp_macro_definition& macro) { auto count = test_visit<cpp_macro_definition>(*file, [&](const cpp_macro_definition& macro) {
@ -117,25 +117,25 @@ b
auto file_a = parse(idx, "header_a.hpp", header_a); auto file_a = parse(idx, "header_a.hpp", header_a);
auto file_b = parse(idx, "header_b.hpp", header_b); auto file_b = parse(idx, "header_b.hpp", header_b);
auto count = auto count
test_visit<cpp_include_directive>(*file_a, [&](const cpp_include_directive& include) { = test_visit<cpp_include_directive>(*file_a, [&](const cpp_include_directive& include) {
if (include.name() == "iostream") if (include.name() == "iostream")
{ {
REQUIRE(include.target().name() == include.name()); REQUIRE(include.target().name() == include.name());
REQUIRE(include.include_kind() == cppast::cpp_include_kind::system); REQUIRE(include.include_kind() == cppast::cpp_include_kind::system);
REQUIRE(include.target().get(idx).empty()); REQUIRE(include.target().get(idx).empty());
REQUIRE_THAT(include.full_path(), Catch::EndsWith("iostream")); REQUIRE_THAT(include.full_path(), Catch::EndsWith("iostream"));
} }
else if (include.name() == "cpp_include_directive-header.hpp") else if (include.name() == "cpp_include_directive-header.hpp")
{ {
REQUIRE(include.target().name() == include.name()); REQUIRE(include.target().name() == include.name());
REQUIRE(include.include_kind() == cppast::cpp_include_kind::local); REQUIRE(include.include_kind() == cppast::cpp_include_kind::local);
REQUIRE(include.target().get(idx).empty()); REQUIRE(include.target().get(idx).empty());
REQUIRE(include.full_path() == "./cpp_include_directive-header.hpp"); REQUIRE(include.full_path() == "./cpp_include_directive-header.hpp");
} }
else else
REQUIRE(false); REQUIRE(false);
}); });
REQUIRE(count == 2u); REQUIRE(count == 2u);
count = test_visit<cpp_include_directive>(*file_b, [&](const cpp_include_directive& include) { count = test_visit<cpp_include_directive>(*file_b, [&](const cpp_include_directive& include) {

View file

@ -45,8 +45,8 @@ using e = void;
{ {
REQUIRE(p.kind() == cpp_entity_kind::template_type_parameter_t); REQUIRE(p.kind() == cpp_entity_kind::template_type_parameter_t);
auto& param = auto& param
static_cast<const cpp_template_type_parameter&>(p); = static_cast<const cpp_template_type_parameter&>(p);
if (param.name() == "A") if (param.name() == "A")
{ {
REQUIRE(alias.name() == "a"); REQUIRE(alias.name() == "a");
@ -132,8 +132,8 @@ using d = void;
REQUIRE(p.kind() REQUIRE(p.kind()
== cpp_entity_kind::non_type_template_parameter_t); == cpp_entity_kind::non_type_template_parameter_t);
auto& param = auto& param
static_cast<const cpp_non_type_template_parameter&>(p); = 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");
@ -227,8 +227,8 @@ using d = void;
REQUIRE(p.kind() REQUIRE(p.kind()
== cpp_entity_kind::template_template_parameter_t); == cpp_entity_kind::template_template_parameter_t);
auto& param = auto& param
static_cast<const cpp_template_template_parameter&>(p); = static_cast<const cpp_template_template_parameter&>(p);
REQUIRE(param.keyword() == cpp_template_keyword::keyword_class); REQUIRE(param.keyword() == cpp_template_keyword::keyword_class);
if (param.name() == "A") if (param.name() == "A")
{ {

View file

@ -134,8 +134,8 @@ bool equal_types(const cpp_entity_index& idx, const cpp_type& parsed, const cpp_
case cpp_type_kind::template_parameter_t: 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_t: case cpp_type_kind::template_instantiation_t:
@ -357,8 +357,8 @@ typedef decltype(0) w;
} }
else if (alias.name() == "d") else if (alias.name() == "d")
{ {
auto type = auto type
cpp_pointer_type::build(add_cv(cpp_builtin_type::build(cpp_uint), cpp_cv_const)); = cpp_pointer_type::build(add_cv(cpp_builtin_type::build(cpp_uint), cpp_cv_const));
REQUIRE(equal_types(idx, alias.underlying_type(), *type)); REQUIRE(equal_types(idx, alias.underlying_type(), *type));
} }
else if (alias.name() == "e") else if (alias.name() == "e")
@ -375,9 +375,9 @@ typedef decltype(0) w;
} }
else if (alias.name() == "g") else if (alias.name() == "g")
{ {
auto type = auto type
cpp_reference_type::build(add_cv(cpp_builtin_type::build(cpp_int), cpp_cv_const), = cpp_reference_type::build(add_cv(cpp_builtin_type::build(cpp_int), cpp_cv_const),
cpp_ref_rvalue); cpp_ref_rvalue);
REQUIRE(equal_types(idx, alias.underlying_type(), *type)); REQUIRE(equal_types(idx, alias.underlying_type(), *type));
} }
else if (alias.name() == "h") else if (alias.name() == "h")
@ -399,21 +399,21 @@ typedef decltype(0) w;
} }
else if (alias.name() == "k") else if (alias.name() == "k")
{ {
auto type = auto type
cpp_array_type::build(cpp_builtin_type::build(cpp_int), make_size("42", true)); = cpp_array_type::build(cpp_builtin_type::build(cpp_int), make_size("42", true));
REQUIRE(equal_types(idx, alias.underlying_type(), *type)); REQUIRE(equal_types(idx, alias.underlying_type(), *type));
} }
else if (alias.name() == "l") else if (alias.name() == "l")
{ {
auto type = auto type
cpp_array_type::build(cpp_pointer_type::build(cpp_builtin_type::build(cpp_float)), = cpp_array_type::build(cpp_pointer_type::build(cpp_builtin_type::build(cpp_float)),
nullptr); nullptr);
REQUIRE(equal_types(idx, alias.underlying_type(), *type)); REQUIRE(equal_types(idx, alias.underlying_type(), *type));
} }
else if (alias.name() == "m") else if (alias.name() == "m")
{ {
auto type = auto type
cpp_array_type::build(cpp_builtin_type::build(cpp_char), make_size("42", true)); = cpp_array_type::build(cpp_builtin_type::build(cpp_char), make_size("42", true));
REQUIRE(equal_types(idx, alias.underlying_type(), *type)); REQUIRE(equal_types(idx, alias.underlying_type(), *type));
} }
else if (alias.name() == "n") else if (alias.name() == "n")
@ -474,10 +474,12 @@ typedef decltype(0) w;
} }
else if (alias.name() == "s") else if (alias.name() == "s")
{ {
auto pointee = cpp_member_object_type:: auto pointee
build(cpp_user_defined_type::build(cpp_type_ref(cpp_entity_id(""), "foo")), = cpp_member_object_type::build(cpp_user_defined_type::build(
cpp_unexposed_type::build( cpp_type_ref(cpp_entity_id(""), "foo")),
"int")); // type not exposed directly for some reason cpp_unexposed_type::build(
"int")); // type not exposed directly for some
// reason
auto type = cpp_pointer_type::build(std::move(pointee)); auto type = cpp_pointer_type::build(std::move(pointee));
REQUIRE(equal_types(idx, alias.underlying_type(), *type)); REQUIRE(equal_types(idx, alias.underlying_type(), *type));

View file

@ -16,53 +16,53 @@ TEST_CASE("stdlib", "[!hide][integration]")
//#include <cstdlib> -- problem with compiler built-in stuff on OSX //#include <cstdlib> -- problem with compiler built-in stuff on OSX
#include <csignal> #include <csignal>
//#include <csetjmp> -- same as above //#include <csetjmp> -- same as above
#include <cstdarg>
#include <typeinfo>
#include <typeindex>
#include <type_traits>
#include <bitset> #include <bitset>
#include <functional>
#include <utility>
#include <ctime>
#include <chrono> #include <chrono>
#include <cstdarg>
#include <cstddef> #include <cstddef>
#include <ctime>
#include <functional>
#include <initializer_list> #include <initializer_list>
#include <tuple> #include <tuple>
#include <type_traits>
#include <typeindex>
#include <typeinfo>
#include <utility>
#include <new>
#include <memory> #include <memory>
#include <new>
#include <scoped_allocator> #include <scoped_allocator>
#include <climits>
#include <cfloat> #include <cfloat>
#include <climits>
#include <cstdint> #include <cstdint>
//#include <cinttypes> -- missing types from C header (for some reason) //#include <cinttypes> -- missing types from C header (for some reason)
#include <limits> #include <limits>
//#include <exception> -- weird issue with compiler built-in stuff //#include <exception> -- weird issue with compiler built-in stuff
#include <stdexcept>
#include <cassert> #include <cassert>
#include <system_error>
#include <cerrno> #include <cerrno>
#include <stdexcept>
#include <system_error>
#include <cctype> #include <cctype>
#include <cwctype>
#include <cstring> #include <cstring>
#include <cwchar> #include <cwchar>
#include <cwctype>
//#include <cuchar> -- not supported on CI //#include <cuchar> -- not supported on CI
#include <string> #include <string>
#include <array> #include <array>
#include <vector>
#include <deque> #include <deque>
#include <list>
#include <forward_list> #include <forward_list>
#include <set> #include <list>
#include <map> #include <map>
#include <unordered_set>
#include <unordered_map>
#include <stack>
#include <queue> #include <queue>
#include <set>
#include <stack>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <algorithm> #include <algorithm>
@ -70,22 +70,22 @@ TEST_CASE("stdlib", "[!hide][integration]")
//#include <cmath> -- non-conforming GCC extension with regards to constexpr //#include <cmath> -- non-conforming GCC extension with regards to constexpr
//#include <complex> -- weird double include issue under MSVC //#include <complex> -- weird double include issue under MSVC
#include <valarray>
#include <random>
#include <numeric> #include <numeric>
#include <random>
#include <ratio> #include <ratio>
#include <valarray>
//#include <cfenv> -- same issue with cinttypes //#include <cfenv> -- same issue with cinttypes
#include <iosfwd> #include <cstdio>
#include <fstream>
#include <iomanip>
#include <ios> #include <ios>
#include <iosfwd>
#include <iostream>
#include <istream> #include <istream>
#include <ostream> #include <ostream>
#include <iostream>
#include <fstream>
#include <sstream> #include <sstream>
#include <iomanip>
#include <streambuf> #include <streambuf>
#include <cstdio>
#include <locale> #include <locale>
//#include <clocale> -- issue on OSX //#include <clocale> -- issue on OSX
@ -94,10 +94,10 @@ TEST_CASE("stdlib", "[!hide][integration]")
//#include <atomic> -- issue on MSVC //#include <atomic> -- issue on MSVC
#include <thread>
#include <mutex>
#include <future>
#include <condition_variable> #include <condition_variable>
#include <future>
#include <mutex>
#include <thread>
)"; )";
write_file("stdlib.cpp", code); write_file("stdlib.cpp", code);

View file

@ -57,7 +57,7 @@ TEST_CASE("libclang_compile_config")
} }
])"; ])";
#define CPPAST_DETAIL_DRIVE "C:" # define CPPAST_DETAIL_DRIVE "C:"
#else #else
auto json = R"([ auto json = R"([
@ -83,7 +83,7 @@ TEST_CASE("libclang_compile_config")
} }
])"; ])";
#define CPPAST_DETAIL_DRIVE # define CPPAST_DETAIL_DRIVE
#endif #endif

View file

@ -65,7 +65,8 @@ TEST_CASE("preprocessing use external macro")
auto result = NAN; auto result = NAN;
#endif #endif
)", fast_preprocessing); )",
fast_preprocessing);
test_visit<cpp_variable>(*file, [&](const cpp_variable&) {}); test_visit<cpp_variable>(*file, [&](const cpp_variable&) {});
} }
@ -302,7 +303,8 @@ void j();
{ {
if (comment.content == "cstddef\ncstddef") if (comment.content == "cstddef\ncstddef")
// happens if include parsing is not supported // happens if include parsing is not supported
// error is still going to be detected because if it is supported, the entity will be matched above // error is still going to be detected because if it is supported, the entity will be
// matched above
add = 1u; add = 1u;
else else
REQUIRE(comment.content == "u"); REQUIRE(comment.content == "u");

View file

@ -6,12 +6,12 @@
#include <cxxopts.hpp> #include <cxxopts.hpp>
#include <cppast/libclang_parser.hpp> // for libclang_parser, libclang_compile_config, cpp_entity,... #include <cppast/code_generator.hpp> // for generate_code()
#include <cppast/visitor.hpp> // for visit()
#include <cppast/code_generator.hpp> // for generate_code()
#include <cppast/cpp_entity_kind.hpp> // for the cpp_entity_kind definition #include <cppast/cpp_entity_kind.hpp> // for the cpp_entity_kind definition
#include <cppast/cpp_forward_declarable.hpp> // for is_definition() #include <cppast/cpp_forward_declarable.hpp> // for is_definition()
#include <cppast/cpp_namespace.hpp> // for cpp_namespace #include <cppast/cpp_namespace.hpp> // for cpp_namespace
#include <cppast/libclang_parser.hpp> // for libclang_parser, libclang_compile_config, cpp_entity,...
#include <cppast/visitor.hpp> // for visit()
// print help options // print help options
void print_help(const cxxopts::Options& options) void print_help(const cxxopts::Options& options)
@ -239,12 +239,12 @@ int main(int argc, char* argv[]) try
cppast::libclang_compilation_database database( cppast::libclang_compilation_database database(
options["database_dir"].as<std::string>()); options["database_dir"].as<std::string>());
if (options.count("database_file")) if (options.count("database_file"))
config = config
cppast::libclang_compile_config(database, = cppast::libclang_compile_config(database,
options["database_file"].as<std::string>()); options["database_file"].as<std::string>());
else else
config = config
cppast::libclang_compile_config(database, options["file"].as<std::string>()); = cppast::libclang_compile_config(database, options["file"].as<std::string>());
} }
if (options.count("verbose")) if (options.count("verbose"))