diff --git a/.clang-format b/.clang-format index 7695a3e..88b932e 100644 --- a/.clang-format +++ b/.clang-format @@ -2,10 +2,10 @@ AccessModifierOffset: -4 AlignAfterOpenBracket: Align AlignConsecutiveAssignments: true AlignConsecutiveDeclarations: true -AlignEscapedNewlinesLeft: false +AlignEscapedNewlinesLeft: Right AlignOperands: true AlignTrailingComments: true -AllowAllParametersOfDeclarationOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: false AllowShortBlocksOnASingleLine: false AllowShortCaseLabelsOnASingleLine: false AllowShortFunctionsOnASingleLine: Empty @@ -16,28 +16,50 @@ AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: true BinPackArguments: true BinPackParameters: true -BreakBeforeBinaryOperators: NonAssignment -BreakBeforeBraces: Allman -BreakBeforeTernaryOperators: false -BreakConstructorInitializersBeforeComma: false -ConstructorInitializerAllOnOneLineOrOnePerLine: true +BreakBeforeBraces: Custom +BraceWrapping: + AfterClass: true + AfterControlStatement: 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 ContinuationIndentWidth: 4 -ColumnLimit: 100 Cpp11BracedListStyle: true +DerivePointerAlignment: false +FixNamespaceComments: true +IncludeBlocks: Preserve IndentCaseLabels: false +IndentPPDirectives: AfterHash IndentWidth: 4 IndentWrappedFunctionNames: true -Language: Cpp KeepEmptyLinesAtTheStartOfBlocks: false +Language: Cpp MaxEmptyLinesToKeep: 1 -NamespaceIndentation: All +NamespaceIndentation: Inner PenaltyBreakBeforeFirstCallParameter: 19937 PenaltyReturnTypeOnItsOwnLine: 19937 PointerAlignment: Left -ReflowComments: false -SortIncludes: false +ReflowComments: true +SortIncludes: true +SortUsingDeclarations: true SpaceAfterCStyleCast: false +SpaceAfterTemplateKeyword: true SpaceBeforeAssignmentOperators: true SpaceBeforeParens: ControlStatements SpaceInEmptyParentheses: false diff --git a/example/comparison.cpp b/example/comparison.cpp index bd38267..cb9de55 100644 --- a/example/comparison.cpp +++ b/example/comparison.cpp @@ -5,7 +5,8 @@ /// \file /// 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 #include @@ -94,8 +95,8 @@ void generate_comparison(const cppast::cpp_file& file) { // it is a new class auto& class_ = static_cast(e); - auto& attribute = - cppast::has_attribute(e, "generate::comparison").value(); + auto& attribute + = cppast::has_attribute(e, "generate::comparison").value(); // generate requested operators if (attribute.arguments()) diff --git a/example/enum_category.cpp b/example/enum_category.cpp index f0a6098..e3482f6 100644 --- a/example/enum_category.cpp +++ b/example/enum_category.cpp @@ -5,8 +5,9 @@ /// \file /// Generates enum category functions. /// -/// Given an input file, it will generate definitions for functions marked with [[generate::enum_category(name)]]. -/// The function takes an enumerator and will return true if it is marked with the same category. +/// Given an input file, it will generate definitions for functions marked with +/// [[generate::enum_category(name)]]. The function takes an enumerator and will return true if it +/// is marked with the same category. #include #include @@ -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")) { // ... by looking for the token - auto iter = - std::find_if(attr.value().arguments().value().begin(), - attr.value().arguments().value().end(), - [&](const cppast::cpp_token& tok) { return tok.spelling == name; }); + auto iter + = std::find_if(attr.value().arguments().value().begin(), + attr.value().arguments().value().end(), + [&](const cppast::cpp_token& tok) { return tok.spelling == name; }); return iter != attr.value().arguments().value().end(); } else @@ -42,8 +43,10 @@ const cppast::cpp_enum& get_enum(const cppast::cpp_entity_index& index, // it is an enum assert(param_type.kind() == cppast::cpp_type_kind::user_defined_t); // lookup definition - auto& definition = - static_cast(param_type).entity().get(index)[0u].get(); + auto& definition = static_cast(param_type) + .entity() + .get(index)[0u] + .get(); assert(definition.kind() == cppast::cpp_entity_kind::enum_t); return static_cast(definition); diff --git a/example/enum_to_string.cpp b/example/enum_to_string.cpp index 87f7b28..8dcdc9f 100644 --- a/example/enum_to_string.cpp +++ b/example/enum_to_string.cpp @@ -5,7 +5,8 @@ /// \file /// 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 @@ -43,8 +44,8 @@ void generate_to_string(const cppast::cpp_file& file) << ":\n"; // attribute can be used to override the string - if (auto attr = - cppast::has_attribute(enumerator, "generate::to_string")) + if (auto attr + = cppast::has_attribute(enumerator, "generate::to_string")) std::cout << " return " << attr.value().arguments().value().as_string() << ";\n"; diff --git a/example/serialization.cpp b/example/serialization.cpp index 083e71f..75c18ba 100644 --- a/example/serialization.cpp +++ b/example/serialization.cpp @@ -5,7 +5,8 @@ /// \file /// 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 diff --git a/include/cppast/code_generator.hpp b/include/cppast/code_generator.hpp index 0ac8f92..ed919a8 100644 --- a/include/cppast/code_generator.hpp +++ b/include/cppast/code_generator.hpp @@ -7,535 +7,535 @@ #include -#include #include +#include #include #include namespace cppast { - enum cpp_access_specifier_kind : int; +enum cpp_access_specifier_kind : int; - /// A simple string view implementation, like [std::string_view](). - /// - /// It "views" - stores a pointer to - some kind of string. - class string_view +/// A simple string view implementation, like [std::string_view](). +/// +/// It "views" - stores a pointer to - some kind of string. +class string_view +{ +public: + /// \effects Creates it viewing the [std::string](). + string_view(const std::string& str) noexcept : str_(str.c_str()), length_(str.length()) {} + + /// \effects Creates it viewing the C string `str`. + string_view(const char* str) noexcept : str_(str), length_(std::strlen(str)) {} + + /// \effects Creates it viewing the C string literal. + template + constexpr string_view(const char (&str)[Size]) noexcept : str_(str), length_(Size - 1u) + {} + + /// \returns The number of characters. + constexpr std::size_t length() const noexcept + { + return length_; + } + + /// \returns The C string. + constexpr const char* c_str() const noexcept + { + return str_; + } + + /// \returns The character at the given index. + /// \requires `i < length()`. + char operator[](type_safe::index_t i) const noexcept + { + return type_safe::at(str_, i); + } + +private: + const char* str_; + std::size_t length_; +}; + +/// \exclude +namespace detail +{ + template + class semantic_string_view { public: - /// \effects Creates it viewing the [std::string](). - string_view(const std::string& str) noexcept : str_(str.c_str()), length_(str.length()) {} + template + explicit semantic_string_view(T&& obj, decltype(string_view(std::forward(obj)), 0) = 0) + : str_(std::forward(obj)) + {} - /// \effects Creates it viewing the C string `str`. - string_view(const char* str) noexcept : str_(str), length_(std::strlen(str)) {} - - /// \effects Creates it viewing the C string literal. - template - constexpr string_view(const char (&str)[Size]) noexcept : str_(str), length_(Size - 1u) - { - } - - /// \returns The number of characters. - constexpr std::size_t length() const noexcept - { - return length_; - } - - /// \returns The C string. - constexpr const char* c_str() const noexcept + const string_view& str() const noexcept { return str_; } - /// \returns The character at the given index. - /// \requires `i < length()`. - char operator[](type_safe::index_t i) const noexcept - { - return type_safe::at(str_, i); - } - private: - const char* str_; - std::size_t length_; + string_view str_; }; +} // namespace detail - /// \exclude - namespace detail +/// A special [cppast::string_view]() representing a C++ keyword token. +using keyword = detail::semantic_string_view; + +/// A special [cppast::string_view]() representing a C++ identifier token. +using identifier = detail::semantic_string_view; + +/// A special [cppast::string_view]() representing a C++ string or character literal token. +using string_literal = detail::semantic_string_view; + +/// A special [cppast::string_view]() representing a C++ integer literal token. +using int_literal = detail::semantic_string_view; + +/// A special [cppast::string_view]() representing a C++ floating point literal token. +using float_literal = detail::semantic_string_view; + +/// A special [cppast::string_view]() representing a C++ punctuation token like `.` or `(`. +using punctuation = detail::semantic_string_view; + +/// A special [cppast::string_view]() representing a C++ preprocessor token. +using preprocessor_token = detail::semantic_string_view; + +/// A special [cppast::string_view]() representing a comment. +using comment = detail::semantic_string_view; + +/// A special [cppast::string_view]() representing a sequence of unknown C++ tokens. +using token_seq = detail::semantic_string_view; + +/// Tag type to represent an end-of-line character. +const struct newl_t +{ +} newl{}; + +/// Tag type to represent a single space character. +const struct whitespace_t +{ +} whitespace{}; + +/// Flags that control the code formatting. +/// +/// If a flag is set, it adds additional whitespace. +/// If no flags are set, it will only add the whitespace necessary to separate tokens. +enum class formatting_flags +{ + brace_nl, //< Set to put the opening braces on a new line. + brace_ws, //< Set to put the opening brace at the end of the line after whitespace. + + ptr_ref_var, //< Set to put pointers and references at the variable name (default is type). + + comma_ws, //< Set to put whitespace after a comma. + bracket_ws, //< Set to put whitespace inside brackets. + operator_ws, //< Set to put whitespace around operators. + + _flag_set_size, //< \exclude +}; + +/// A set of formatting flags. +using formatting = type_safe::flag_set; + +/// Base class to control the code generation. +/// +/// Inherit from it to customize how a [cppast::cpp_entity]() is printed +/// by [cppast::generate_code](). +class code_generator +{ +public: + code_generator(const code_generator&) = delete; + + code_generator& operator=(const code_generator&) = delete; + + virtual ~code_generator() noexcept = default; + + /// Flags that control the generation. + enum generation_flags { - template - class semantic_string_view - { - public: - template - explicit semantic_string_view(T&& obj, - decltype(string_view(std::forward(obj)), 0) = 0) - : str_(std::forward(obj)) - { - } - - const string_view& str() const noexcept - { - return str_; - } - - private: - string_view str_; - }; - } // namespace detail - - /// A special [cppast::string_view]() representing a C++ keyword token. - using keyword = detail::semantic_string_view; - - /// A special [cppast::string_view]() representing a C++ identifier token. - using identifier = detail::semantic_string_view; - - /// A special [cppast::string_view]() representing a C++ string or character literal token. - using string_literal = detail::semantic_string_view; - - /// A special [cppast::string_view]() representing a C++ integer literal token. - using int_literal = detail::semantic_string_view; - - /// A special [cppast::string_view]() representing a C++ floating point literal token. - using float_literal = detail::semantic_string_view; - - /// A special [cppast::string_view]() representing a C++ punctuation token like `.` or `(`. - using punctuation = detail::semantic_string_view; - - /// A special [cppast::string_view]() representing a C++ preprocessor token. - using preprocessor_token = detail::semantic_string_view; - - /// A special [cppast::string_view]() representing a comment. - using comment = detail::semantic_string_view; - - /// A special [cppast::string_view]() representing a sequence of unknown C++ tokens. - using token_seq = detail::semantic_string_view; - - /// Tag type to represent an end-of-line character. - const struct newl_t - { - } newl{}; - - /// Tag type to represent a single space character. - const struct whitespace_t - { - } whitespace{}; - - /// Flags that control the code formatting. - /// - /// If a flag is set, it adds additional whitespace. - /// If no flags are set, it will only add the whitespace necessary to separate tokens. - enum class formatting_flags - { - brace_nl, //< Set to put the opening braces on a new line. - brace_ws, //< Set to put the opening brace at the end of the line after whitespace. - - ptr_ref_var, //< Set to put pointers and references at the variable name (default is type). - - comma_ws, //< Set to put whitespace after a comma. - bracket_ws, //< Set to put whitespace inside brackets. - operator_ws, //< Set to put whitespace around operators. - + custom, //< A custom output is written. + exclude, //< Exclude the entire entity. + exclude_return, //< Exclude the return type of a function entity. + exclude_target, //< Exclude the underlying entity of an alias (e.g. typedef). + exclude_noexcept_condition, //< Exclude the condition of a noexcept.` + declaration, //< Only write declaration. + /// For a macro, it won't show the replacement if this flag is set _flag_set_size, //< \exclude }; - /// A set of formatting flags. - using formatting = type_safe::flag_set; + /// Options that control the generation. + using generation_options = type_safe::flag_set; - /// Base class to control the code generation. - /// - /// Inherit from it to customize how a [cppast::cpp_entity]() is printed - /// by [cppast::generate_code](). - class code_generator + /// Sentinel type used to output a given entity. + class output { public: - code_generator(const code_generator&) = delete; - - code_generator& operator=(const code_generator&) = delete; - - virtual ~code_generator() noexcept = default; - - /// Flags that control the generation. - enum generation_flags + /// \effects Creates it giving the generator and the entity. + /// \requires No other output must be active for the same code generator. + explicit output(type_safe::object_ref gen, + type_safe::object_ref e, + cppast::cpp_access_specifier_kind access) + : gen_(gen), e_(e), options_(gen->do_get_options(*e, access)) { - custom, //< A custom output is written. - exclude, //< Exclude the entire entity. - exclude_return, //< Exclude the return type of a function entity. - exclude_target, //< Exclude the underlying entity of an alias (e.g. typedef). - exclude_noexcept_condition, //< Exclude the condition of a noexcept.` - declaration, //< Only write declaration. - /// For a macro, it won't show the replacement if this flag is set - _flag_set_size, //< \exclude - }; - - /// Options that control the generation. - using generation_options = type_safe::flag_set; - - /// Sentinel type used to output a given entity. - class output - { - public: - /// \effects Creates it giving the generator and the entity. - /// \requires No other output must be active for the same code generator. - explicit output(type_safe::object_ref gen, - type_safe::object_ref e, - cppast::cpp_access_specifier_kind access) - : gen_(gen), e_(e), options_(gen->do_get_options(*e, access)) - { - gen_->on_begin(*this, *e_); - } - - ~output() noexcept - { - if (*this) - gen_->on_end(*this, *e_); - } - - output(const output&) = delete; - - output& operator=(const output&) = delete; - - /// \returns Whether or not the `on_XXX` function returned something other than `exclude` or `custom`. - explicit operator bool() const noexcept - { - return !options_.is_set(exclude) && !options_.is_set(custom); - } - - /// \returns The generation options. - generation_options options() const - { - return options_; - } - - /// \returns The generation options for the given entity. - generation_options options(const cpp_entity& e, cpp_access_specifier_kind access) const - { - return gen_->do_get_options(e, access); - } - - /// \returns The formatting. - cppast::formatting formatting() const - { - return gen_->do_get_formatting(); - } - - /// \returns Whether or not the definition should be generated as well. - bool generate_definition() const noexcept - { - return !(options_ & declaration); - } - - /// \returns A reference to the generator. - type_safe::object_ref generator() const noexcept - { - return gen_; - } - - /// \effects Calls `on_container_end()`. - void container_end() const noexcept - { - gen_->on_container_end(*this, *e_); - } - - /// \effects Call `do_indent()` followed by `do_write_newline()` (if `print_newline` is `true`). - void indent(bool print_newline = true) const noexcept - { - gen_->do_indent(); - if (print_newline) - gen_->do_write_newline(); - } - - /// \effects Calls `do_unindent()`. - void unindent() const noexcept - { - gen_->do_unindent(); - } - - /// \effects Calls `func(*this)`. - const output& operator<<(void (*func)(const output&)) const - { - func(*this); - return *this; - } - - /// \effects Calls `do_write_keyword()`. - const output& operator<<(const keyword& k) const - { - gen_->do_write_keyword(k.str()); - return *this; - } - - /// \effects Calls `do_write_identifier()`. - const output& operator<<(const identifier& ident) const - { - gen_->do_write_identifier(ident.str()); - return *this; - } - - /// \effects Calls `do_write_reference()`. - template - const output& operator<<(const basic_cpp_entity_ref& ref) const - { - was_excluded_ = !gen_->do_write_reference(ref.id(), ref.name()); - return *this; - } - - /// \returns Whether or not the last reference was excluded. - bool was_reference_excluded() const - { - return was_excluded_; - } - - /// \effects Calls `do_write_punctuation()`. - const output& operator<<(const punctuation& punct) const - { - gen_->do_write_punctuation(punct.str()); - return *this; - } - - /// \effects Calls `do_write_str_literal`. - const output& operator<<(const string_literal& lit) const - { - gen_->do_write_str_literal(lit.str()); - return *this; - } - - /// \effects Calls `do_write_int_literal()`. - const output& operator<<(const int_literal& lit) const - { - gen_->do_write_int_literal(lit.str()); - return *this; - } - - /// \effects Calls `do_write_float_literal()`. - const output& operator<<(const float_literal& lit) const - { - gen_->do_write_float_literal(lit.str()); - return *this; - } - - /// \effects Calls `do_write_preprocessor()`. - const output& operator<<(const preprocessor_token& tok) const - { - gen_->do_write_preprocessor(tok.str()); - return *this; - } - - /// \effects Calls `do_write_comment()`. - const output& operator<<(const comment& c) const - { - gen_->do_write_comment(c.str()); - return *this; - } - - /// \effects Calls `do_write_token_seq()`. - const output& operator<<(const token_seq& seq) const - { - gen_->do_write_token_seq(seq.str()); - return *this; - } - - /// \effects Calls `do_write_excluded()`. - const output& excluded(const cpp_entity& e) const - { - gen_->do_write_excluded(e); - return *this; - } - - /// \effects Calls `do_write_newline()`. - const output& operator<<(newl_t) const - { - gen_->do_write_newline(); - return *this; - } - - /// \effects Calls `do_write_whitespace()`. - const output& operator<<(whitespace_t) const - { - gen_->do_write_whitespace(); - return *this; - } - - private: - type_safe::object_ref gen_; - type_safe::object_ref e_; - generation_options options_; - mutable bool was_excluded_ = false; - }; - - protected: - code_generator() noexcept = default; - - /// \returns The entity whose code is being generated with [cppast::generate_code()](). - const cpp_entity& main_entity() const noexcept - { - return main_entity_.value(); + gen_->on_begin(*this, *e_); } - /// \effects Generates the code for the specified entity. - /// It can be used to generate additional entities while generating another one. - /// \returns Whether or not any code was generated. - /// \notes This does not affect the main entity, but otherwise behaves just like [cppast::generate_code()](). - bool generate_code(const cpp_entity& entity); + ~output() noexcept + { + if (*this) + gen_->on_end(*this, *e_); + } + + output(const output&) = delete; + + output& operator=(const output&) = delete; + + /// \returns Whether or not the `on_XXX` function returned something other than `exclude` or + /// `custom`. + explicit operator bool() const noexcept + { + return !options_.is_set(exclude) && !options_.is_set(custom); + } + + /// \returns The generation options. + generation_options options() const + { + return options_; + } + + /// \returns The generation options for the given entity. + generation_options options(const cpp_entity& e, cpp_access_specifier_kind access) const + { + return gen_->do_get_options(e, access); + } + + /// \returns The formatting. + cppast::formatting formatting() const + { + return gen_->do_get_formatting(); + } + + /// \returns Whether or not the definition should be generated as well. + bool generate_definition() const noexcept + { + return !(options_ & declaration); + } + + /// \returns A reference to the generator. + type_safe::object_ref generator() const noexcept + { + return gen_; + } + + /// \effects Calls `on_container_end()`. + void container_end() const noexcept + { + gen_->on_container_end(*this, *e_); + } + + /// \effects Call `do_indent()` followed by `do_write_newline()` (if `print_newline` is + /// `true`). + void indent(bool print_newline = true) const noexcept + { + gen_->do_indent(); + if (print_newline) + gen_->do_write_newline(); + } + + /// \effects Calls `do_unindent()`. + void unindent() const noexcept + { + gen_->do_unindent(); + } + + /// \effects Calls `func(*this)`. + const output& operator<<(void (*func)(const output&)) const + { + func(*this); + return *this; + } + + /// \effects Calls `do_write_keyword()`. + const output& operator<<(const keyword& k) const + { + gen_->do_write_keyword(k.str()); + return *this; + } + + /// \effects Calls `do_write_identifier()`. + const output& operator<<(const identifier& ident) const + { + gen_->do_write_identifier(ident.str()); + return *this; + } + + /// \effects Calls `do_write_reference()`. + template + const output& operator<<(const basic_cpp_entity_ref& ref) const + { + was_excluded_ = !gen_->do_write_reference(ref.id(), ref.name()); + return *this; + } + + /// \returns Whether or not the last reference was excluded. + bool was_reference_excluded() const + { + return was_excluded_; + } + + /// \effects Calls `do_write_punctuation()`. + const output& operator<<(const punctuation& punct) const + { + gen_->do_write_punctuation(punct.str()); + return *this; + } + + /// \effects Calls `do_write_str_literal`. + const output& operator<<(const string_literal& lit) const + { + gen_->do_write_str_literal(lit.str()); + return *this; + } + + /// \effects Calls `do_write_int_literal()`. + const output& operator<<(const int_literal& lit) const + { + gen_->do_write_int_literal(lit.str()); + return *this; + } + + /// \effects Calls `do_write_float_literal()`. + const output& operator<<(const float_literal& lit) const + { + gen_->do_write_float_literal(lit.str()); + return *this; + } + + /// \effects Calls `do_write_preprocessor()`. + const output& operator<<(const preprocessor_token& tok) const + { + gen_->do_write_preprocessor(tok.str()); + return *this; + } + + /// \effects Calls `do_write_comment()`. + const output& operator<<(const comment& c) const + { + gen_->do_write_comment(c.str()); + return *this; + } + + /// \effects Calls `do_write_token_seq()`. + const output& operator<<(const token_seq& seq) const + { + gen_->do_write_token_seq(seq.str()); + return *this; + } + + /// \effects Calls `do_write_excluded()`. + const output& excluded(const cpp_entity& e) const + { + gen_->do_write_excluded(e); + return *this; + } + + /// \effects Calls `do_write_newline()`. + const output& operator<<(newl_t) const + { + gen_->do_write_newline(); + return *this; + } + + /// \effects Calls `do_write_whitespace()`. + const output& operator<<(whitespace_t) const + { + gen_->do_write_whitespace(); + return *this; + } private: - /// \returns The formatting options that should be used. - /// The base class version has no flags set. - virtual formatting do_get_formatting() const - { - return {}; - } - - /// \returns The generation options for that entity with the given access specifier. - /// If an entity is not part of a class the access specifier is alwasy [cppast::cpp_public](). - /// The base class version always returns no special options. - virtual generation_options do_get_options(const cpp_entity& e, - cppast::cpp_access_specifier_kind access) - { - (void)e; - (void)access; - return {}; - } - - /// \effects Will be invoked before code of an entity is generated. - /// The base class version has no effect. - virtual void on_begin(const output& out, const cpp_entity& e) - { - (void)out; - (void)e; - } - - /// \effects Will be invoked after all code of an entity has been generated. - /// The base class version has no effect. - virtual void on_end(const output& out, const cpp_entity& e) - { - (void)out; - (void)e; - } - - /// \effects Will be invoked after all children of a container have been generated. - /// It can be used to inject additional children. - /// The base class version has no effect. - virtual void on_container_end(const output& out, const cpp_entity& e) - { - (void)out; - (void)e; - } - - /// \effects Will be invoked when the indentation level should be increased by one. - /// The level change must be applied on the next call to `do_write_newline()`. - virtual void do_indent() = 0; - - /// \effects Will be invoked when the indentation level should be decreased by one. - /// The level change must be applied immediately if nothing else has been written on the current line. - virtual void do_unindent() = 0; - - /// \effects Writes the given token sequence. - virtual void do_write_token_seq(string_view tokens) = 0; - - /// \effects Writes the specified special token. - /// The base class version simply forwards to `do_write_token_seq()`. - /// \returns `do_write_reference()` returns `false`, if the reference was excluded. - /// \notes This is useful for syntax highlighting, for example. - /// \group write - virtual void do_write_keyword(string_view keyword) - { - do_write_token_seq(keyword); - } - /// \group write - virtual void do_write_identifier(string_view identifier) - { - do_write_token_seq(identifier); - } - /// \group write - virtual bool do_write_reference(type_safe::array_ref id, - string_view name) - { - (void)id; - do_write_token_seq(name); - return true; - } - /// \group write - virtual void do_write_punctuation(string_view punct) - { - do_write_token_seq(punct); - } - /// \group write - virtual void do_write_str_literal(string_view str) - { - do_write_token_seq(str); - } - /// \group write - virtual void do_write_int_literal(string_view str) - { - do_write_token_seq(str); - } - /// \group write - virtual void do_write_float_literal(string_view str) - { - do_write_token_seq(str); - } - /// \group write - virtual void do_write_preprocessor(string_view punct) - { - do_write_token_seq(punct); - } - /// \group write - virtual void do_write_comment(string_view c) - { - do_write_token_seq(c); - } - - /// \effects Writes a string for an excluded target or return type for the given entity. - /// The base class version writes the identifier `excluded`. - virtual void do_write_excluded(const cpp_entity& e) - { - (void)e; - do_write_identifier("excluded"); - } - - /// \effects Writes a newline. - /// It is guaranteed that this is the only way a newline will be printed. - /// The base class forwards to `do_write_token_seq()`. - virtual void do_write_newline() - { - do_write_token_seq("\n"); - } - /// \effects Writes a whitespace character. - /// It will be invoked only where a whitespace is truly needed, - /// like between two keywords. - /// The base class forwards to `do_write_token_seq()`. - virtual void do_write_whitespace() - { - do_write_token_seq(" "); - } - - type_safe::optional_ref main_entity_; - - friend bool generate_code(code_generator& generator, const cpp_entity& e); + type_safe::object_ref gen_; + type_safe::object_ref e_; + generation_options options_; + mutable bool was_excluded_ = false; }; - /// Generates code for the given entity. - /// - /// How the code is generated is customized by the generator. - /// - /// \returns Whether or not any code was actually written. - bool generate_code(code_generator& generator, const cpp_entity& e); +protected: + code_generator() noexcept = default; - /// \exclude - class cpp_template_argument; - - /// \exclude - class cpp_token_string; - - /// \exclude - namespace detail + /// \returns The entity whose code is being generated with [cppast::generate_code()](). + const cpp_entity& main_entity() const noexcept { - void write_template_arguments( - code_generator::output& output, - type_safe::optional> arguments); + return main_entity_.value(); + } - void write_token_string(code_generator::output& output, const cpp_token_string& tokens); - } // namespace detail + /// \effects Generates the code for the specified entity. + /// It can be used to generate additional entities while generating another one. + /// \returns Whether or not any code was generated. + /// \notes This does not affect the main entity, but otherwise behaves just like + /// [cppast::generate_code()](). + bool generate_code(const cpp_entity& entity); + +private: + /// \returns The formatting options that should be used. + /// The base class version has no flags set. + virtual formatting do_get_formatting() const + { + return {}; + } + + /// \returns The generation options for that entity with the given access specifier. + /// If an entity is not part of a class the access specifier is alwasy [cppast::cpp_public](). + /// The base class version always returns no special options. + virtual generation_options do_get_options(const cpp_entity& e, + cppast::cpp_access_specifier_kind access) + { + (void)e; + (void)access; + return {}; + } + + /// \effects Will be invoked before code of an entity is generated. + /// The base class version has no effect. + virtual void on_begin(const output& out, const cpp_entity& e) + { + (void)out; + (void)e; + } + + /// \effects Will be invoked after all code of an entity has been generated. + /// The base class version has no effect. + virtual void on_end(const output& out, const cpp_entity& e) + { + (void)out; + (void)e; + } + + /// \effects Will be invoked after all children of a container have been generated. + /// It can be used to inject additional children. + /// The base class version has no effect. + virtual void on_container_end(const output& out, const cpp_entity& e) + { + (void)out; + (void)e; + } + + /// \effects Will be invoked when the indentation level should be increased by one. + /// The level change must be applied on the next call to `do_write_newline()`. + virtual void do_indent() = 0; + + /// \effects Will be invoked when the indentation level should be decreased by one. + /// The level change must be applied immediately if nothing else has been written on the current + /// line. + virtual void do_unindent() = 0; + + /// \effects Writes the given token sequence. + virtual void do_write_token_seq(string_view tokens) = 0; + + /// \effects Writes the specified special token. + /// The base class version simply forwards to `do_write_token_seq()`. + /// \returns `do_write_reference()` returns `false`, if the reference was excluded. + /// \notes This is useful for syntax highlighting, for example. + /// \group write + virtual void do_write_keyword(string_view keyword) + { + do_write_token_seq(keyword); + } + /// \group write + virtual void do_write_identifier(string_view identifier) + { + do_write_token_seq(identifier); + } + /// \group write + virtual bool do_write_reference(type_safe::array_ref id, string_view name) + { + (void)id; + do_write_token_seq(name); + return true; + } + /// \group write + virtual void do_write_punctuation(string_view punct) + { + do_write_token_seq(punct); + } + /// \group write + virtual void do_write_str_literal(string_view str) + { + do_write_token_seq(str); + } + /// \group write + virtual void do_write_int_literal(string_view str) + { + do_write_token_seq(str); + } + /// \group write + virtual void do_write_float_literal(string_view str) + { + do_write_token_seq(str); + } + /// \group write + virtual void do_write_preprocessor(string_view punct) + { + do_write_token_seq(punct); + } + /// \group write + virtual void do_write_comment(string_view c) + { + do_write_token_seq(c); + } + + /// \effects Writes a string for an excluded target or return type for the given entity. + /// The base class version writes the identifier `excluded`. + virtual void do_write_excluded(const cpp_entity& e) + { + (void)e; + do_write_identifier("excluded"); + } + + /// \effects Writes a newline. + /// It is guaranteed that this is the only way a newline will be printed. + /// The base class forwards to `do_write_token_seq()`. + virtual void do_write_newline() + { + do_write_token_seq("\n"); + } + /// \effects Writes a whitespace character. + /// It will be invoked only where a whitespace is truly needed, + /// like between two keywords. + /// The base class forwards to `do_write_token_seq()`. + virtual void do_write_whitespace() + { + do_write_token_seq(" "); + } + + type_safe::optional_ref main_entity_; + + friend bool generate_code(code_generator& generator, const cpp_entity& e); +}; + +/// Generates code for the given entity. +/// +/// How the code is generated is customized by the generator. +/// +/// \returns Whether or not any code was actually written. +bool generate_code(code_generator& generator, const cpp_entity& e); + +/// \exclude +class cpp_template_argument; + +/// \exclude +class cpp_token_string; + +/// \exclude +namespace detail +{ + void write_template_arguments( + code_generator::output& output, + type_safe::optional> arguments); + + void write_token_string(code_generator::output& output, const cpp_token_string& tokens); +} // namespace detail } // namespace cppast #endif // CPPAST_CODE_GENERATOR_HPP_INCLUDED diff --git a/include/cppast/compile_config.hpp b/include/cppast/compile_config.hpp index c3794f9..67db492 100644 --- a/include/cppast/compile_config.hpp +++ b/include/cppast/compile_config.hpp @@ -8,134 +8,134 @@ #include #include -#include #include +#include #include namespace cppast { - /// The C++ standard that should be used. - enum class cpp_standard +/// The C++ standard that should be used. +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, - 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) - { - 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"; + 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"; } - /// Other special compilation flags. - enum class compile_flag + DEBUG_UNREACHABLE(detail::assert_handler{}); + 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; + +/// 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. - ms_compatibility, //< Enable MSVC compatibility. - - _flag_set_size, //< \exclude - }; - - /// A [ts::flag_set]() of [cppast::compile_flag](). - using compile_flags = type_safe::flag_set; - - /// Base class for the configuration of a [cppast::parser](). - class compile_config + /// \effects Adds the given path to the set of include directories. + void add_include_dir(std::string path) { - public: - /// \effects Sets the given C++ standard and compilation flags. - void set_flags(cpp_standard standard, compile_flags flags = {}) - { - do_set_flags(standard, flags); - } + do_add_include_dir(std::move(path)); + } - /// \effects Adds the given path to the set of include directories. - void add_include_dir(std::string path) - { - do_add_include_dir(std::move(path)); - } + /// \effects Defines the given macro. + void define_macro(std::string name, std::string definition) + { + do_add_macro_definition(std::move(name), std::move(definition)); + } - /// \effects Defines the given macro. - void define_macro(std::string name, std::string definition) - { - do_add_macro_definition(std::move(name), std::move(definition)); - } + /// \effects Undefines the given macro. + void undefine_macro(std::string name) + { + do_remove_macro_definition(std::move(name)); + } - /// \effects Undefines the given macro. - void undefine_macro(std::string name) - { - do_remove_macro_definition(std::move(name)); - } + /// \returns A unique name of the configuration. + /// \notes This allows detecting mismatches of configurations and parsers. + const char* name() const noexcept + { + return do_get_name(); + } - /// \returns A unique name of the configuration. - /// \notes This allows detecting mismatches of configurations and parsers. - const char* name() const noexcept - { - return do_get_name(); - } +protected: + compile_config(std::vector def_flags) : flags_(std::move(def_flags)) {} - protected: - compile_config(std::vector def_flags) : flags_(std::move(def_flags)) {} + compile_config(const compile_config&) = default; + compile_config& operator=(const compile_config&) = default; - compile_config(const compile_config&) = default; - compile_config& operator=(const compile_config&) = default; + ~compile_config() noexcept = default; - ~compile_config() noexcept = default; + void add_flag(std::string flag) + { + flags_.push_back(std::move(flag)); + } - void add_flag(std::string flag) - { - flags_.push_back(std::move(flag)); - } + const std::vector& get_flags() const noexcept + { + return flags_; + } - const std::vector& get_flags() const noexcept - { - return flags_; - } +private: + /// \effects Sets the given C++ standard and compilation flags. + virtual void do_set_flags(cpp_standard standard, compile_flags flags) = 0; - private: - /// \effects Sets the given C++ standard and compilation flags. - virtual void do_set_flags(cpp_standard standard, compile_flags flags) = 0; + /// \effects Adds the given path to the set of include directories. + virtual void do_add_include_dir(std::string path) = 0; - /// \effects Adds the given path to the set of include directories. - virtual void do_add_include_dir(std::string path) = 0; + /// \effects Defines the given macro. + virtual void do_add_macro_definition(std::string name, std::string definition) = 0; - /// \effects Defines the given macro. - virtual void do_add_macro_definition(std::string name, std::string definition) = 0; + /// \effects Undefines the given macro. + virtual void do_remove_macro_definition(std::string name) = 0; - /// \effects Undefines the given macro. - virtual void do_remove_macro_definition(std::string name) = 0; + /// \returns A unique name of the configuration. + /// \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. - /// \notes This allows detecting mismatches of configurations and parsers. - virtual const char* do_get_name() const noexcept = 0; - - std::vector flags_; - }; + std::vector flags_; +}; } // namespace cppast #endif // CPPAST_COMPILE_CONFIG_HPP_INCLUDED diff --git a/include/cppast/cpp_alias_template.hpp b/include/cppast/cpp_alias_template.hpp index f1b6218..c86ab33 100644 --- a/include/cppast/cpp_alias_template.hpp +++ b/include/cppast/cpp_alias_template.hpp @@ -10,35 +10,34 @@ namespace cppast { - /// A [cppast::cpp_entity]() modelling a C++ alias template. - class cpp_alias_template final : public cpp_template +/// A [cppast::cpp_entity]() modelling a C++ alias 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 { public: - static cpp_entity_kind kind() noexcept; - - /// Builder for [cppast::cpp_alias_template](). - class builder : public basic_builder - { - 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(*begin()); - } - - private: - cpp_alias_template(std::unique_ptr alias) - : cpp_template(std::unique_ptr(alias.release())) - { - } - - cpp_entity_kind do_get_entity_kind() const noexcept override; - - friend basic_builder; + 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(*begin()); + } + +private: + cpp_alias_template(std::unique_ptr alias) + : cpp_template(std::unique_ptr(alias.release())) + {} + + cpp_entity_kind do_get_entity_kind() const noexcept override; + + friend basic_builder; +}; } // namespace cppast #endif // CPPAST_CPP_ALIAS_TEMPLATE_HPP_INCLUDED diff --git a/include/cppast/cpp_array_type.hpp b/include/cppast/cpp_array_type.hpp index 7ac2550..665c7c1 100644 --- a/include/cppast/cpp_array_type.hpp +++ b/include/cppast/cpp_array_type.hpp @@ -10,46 +10,45 @@ namespace cppast { - /// An array of a [cppast::cpp_type](). - class cpp_array_type final : public cpp_type +/// An array of a [cppast::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 build(std::unique_ptr type, + std::unique_ptr size) { - public: - /// \returns A newly created array. - /// \notes `size` may be `nullptr`. - static std::unique_ptr build(std::unique_ptr type, - std::unique_ptr size) - { - return std::unique_ptr( - new cpp_array_type(std::move(type), std::move(size))); - } + return std::unique_ptr( + new cpp_array_type(std::move(type), std::move(size))); + } - /// \returns A reference to the value [cppast::cpp_type](). - const cpp_type& value_type() const noexcept - { - return *type_; - } + /// \returns A reference to the value [cppast::cpp_type](). + const cpp_type& value_type() const noexcept + { + return *type_; + } - /// \returns An optional reference to the [cppast::cpp_expression]() that is the size of the array. - /// \notes An unsized array - `T[]` - does not have a size. - type_safe::optional_ref size() const noexcept - { - return type_safe::opt_cref(size_.get()); - } + /// \returns An optional reference to the [cppast::cpp_expression]() that is the size of the + /// array. \notes An unsized array - `T[]` - does not have a size. + type_safe::optional_ref size() const noexcept + { + return type_safe::opt_cref(size_.get()); + } - private: - cpp_array_type(std::unique_ptr type, std::unique_ptr size) - : type_(std::move(type)), size_(std::move(size)) - { - } +private: + cpp_array_type(std::unique_ptr type, std::unique_ptr size) + : type_(std::move(type)), size_(std::move(size)) + {} - cpp_type_kind do_get_kind() const noexcept override - { - return cpp_type_kind::array_t; - } + cpp_type_kind do_get_kind() const noexcept override + { + return cpp_type_kind::array_t; + } - std::unique_ptr type_; - std::unique_ptr size_; - }; + std::unique_ptr type_; + std::unique_ptr size_; +}; } // namespace cppast #endif // CPPAST_CPP_ARRAY_TYPE_HPP_INCLUDED diff --git a/include/cppast/cpp_attribute.hpp b/include/cppast/cpp_attribute.hpp index 9c25079..e877e05 100644 --- a/include/cppast/cpp_attribute.hpp +++ b/include/cppast/cpp_attribute.hpp @@ -15,109 +15,107 @@ namespace cppast { - /// The known C++ attributes. - enum class cpp_attribute_kind +/// The known C++ attributes. +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 arguments); + + /// \effects Creates an unknown attribute giving it the optional scope, names, arguments and + /// whether it is variadic. + cpp_attribute(type_safe::optional scope, std::string name, + type_safe::optional 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_, - 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 + /// \returns The name of the attribute. + const std::string& name() const noexcept { - public: - /// \effects Creates a known attribute, potentially with arguments. - cpp_attribute(cpp_attribute_kind kind, type_safe::optional arguments); + return name_; + } - /// \effects Creates an unknown attribute giving it the optional scope, names, arguments and whether it is variadic. - cpp_attribute(type_safe::optional scope, std::string name, - type_safe::optional arguments, bool is_variadic) - : scope_(std::move(scope)), - arguments_(std::move(arguments)), - name_(std::move(name)), - variadic_(is_variadic) - { - } + /// \returns The scope of the attribute, if there is any. + const type_safe::optional& scope() const noexcept + { + return scope_; + } - /// \returns The kind of attribute, if it is known. - const cpp_attribute_kind& kind() const noexcept - { - return kind_; - } + /// \returns Whether or not the attribute is variadic. + bool is_variadic() const noexcept + { + return variadic_; + } - /// \returns The name of the attribute. - const std::string& name() const noexcept - { - return name_; - } + /// \returns The arguments of the attribute, if they are any. + const type_safe::optional& arguments() const noexcept + { + return arguments_; + } - /// \returns The scope of the attribute, if there is any. - const type_safe::optional& scope() const noexcept - { - return scope_; - } +private: + type_safe::optional scope_; + type_safe::optional arguments_; + std::string name_; + cpp_attribute_kind kind_ = cpp_attribute_kind::unknown; + bool variadic_; +}; - /// \returns Whether or not the attribute is variadic. - bool is_variadic() const noexcept - { - return variadic_; - } +/// A list of C++ attributes. +using cpp_attribute_list = std::vector; - /// \returns The arguments of the attribute, if they are any. - const type_safe::optional& arguments() const noexcept - { - return arguments_; - } +/// Checks whether an attribute is given. +/// \returns `true` if the given attribute list (1-2) / entity (3-4) contain +/// an attribute of the given name (1+3) / kind (2+4). +/// `false` otherwise. +/// \group has_attribute +type_safe::optional_ref has_attribute(const cpp_attribute_list& attributes, + const std::string& name); - private: - type_safe::optional scope_; - type_safe::optional arguments_; - std::string name_; - cpp_attribute_kind kind_ = cpp_attribute_kind::unknown; - bool variadic_; - }; +/// \group has_attribute +type_safe::optional_ref has_attribute(const cpp_attribute_list& attributes, + cpp_attribute_kind kind); - /// A list of C++ attributes. - using cpp_attribute_list = std::vector; +class cpp_entity; - /// Checks whether an attribute is given. - /// \returns `true` if the given attribute list (1-2) / entity (3-4) contain - /// an attribute of the given name (1+3) / kind (2+4). - /// `false` otherwise. - /// \group has_attribute - type_safe::optional_ref has_attribute(const cpp_attribute_list& attributes, - const std::string& name); +/// \group has_attribute +type_safe::optional_ref has_attribute(const cpp_entity& e, + const std::string& name); - /// \group has_attribute - type_safe::optional_ref has_attribute(const cpp_attribute_list& attributes, - cpp_attribute_kind kind); - - class cpp_entity; - - /// \group has_attribute - type_safe::optional_ref has_attribute(const cpp_entity& e, - const std::string& name); - - /// \group has_attribute - type_safe::optional_ref has_attribute(const cpp_entity& e, - cpp_attribute_kind kind); +/// \group has_attribute +type_safe::optional_ref has_attribute(const cpp_entity& e, + cpp_attribute_kind kind); } // namespace cppast #endif // CPPAST_CPP_ATTRIBUTE_HPP_INCLUDED diff --git a/include/cppast/cpp_class.hpp b/include/cppast/cpp_class.hpp index 0a5d9a9..3d7870c 100644 --- a/include/cppast/cpp_class.hpp +++ b/include/cppast/cpp_class.hpp @@ -12,239 +12,233 @@ namespace cppast { - /// The keyword used on the declaration of a [cppast::cpp_class](). - enum class cpp_class_kind +/// The keyword used on the declaration of a [cppast::cpp_class](). +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 build(cpp_access_specifier_kind kind) { - class_t, - struct_t, - union_t, - }; + return std::unique_ptr(new cpp_access_specifier(kind)); + } - /// \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 + /// \returns The kind of access specifier. + cpp_access_specifier_kind access_specifier() const noexcept { - cpp_public, - cpp_protected, - cpp_private - }; + return access_; + } - /// \returns The access specifier keyword as a string. - const char* to_string(cpp_access_specifier_kind access) noexcept; +private: + cpp_access_specifier(cpp_access_specifier_kind access) + : cpp_entity(to_string(access)), access_(access) + {} - /// A [cppast::cpp_entity]() modelling a C++ access specifier. - class cpp_access_specifier final : public cpp_entity + cpp_entity_kind do_get_entity_kind() const noexcept override; + + 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 build(std::string name, std::unique_ptr base, + cpp_access_specifier_kind access, bool is_virtual) + { + return std::unique_ptr( + 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 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 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, + public cpp_forward_declarable +{ +public: + static cpp_entity_kind kind() noexcept; + + /// Builds a [cppast::cpp_class](). + class builder { 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. - /// \notes It is not meant to be registered at the [cppast::cpp_entity_index](), - /// as nothing can refer to it. - static std::unique_ptr build(cpp_access_specifier_kind kind) + /// \effects Marks the class as final. + void is_final() noexcept { - return std::unique_ptr(new cpp_access_specifier(kind)); + class_->final_ = true; } - /// \returns The kind of access specifier. - cpp_access_specifier_kind access_specifier() const noexcept + /// \effects Builds a [cppast::cpp_base_class]() and adds it. + cpp_base_class& base_class(std::string name, std::unique_ptr 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 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 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 finish(const cpp_entity_index& idx, cpp_entity_id id, + type_safe::optional semantic_parent); + + /// \effects Marks the class as forward declaration. + /// \returns The finished class. + std::unique_ptr 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 finish(type_safe::optional 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 finish_declaration(cpp_entity_id definition_id); + private: - cpp_access_specifier(cpp_access_specifier_kind access) - : cpp_entity(to_string(access)), access_(access) - { - } - - cpp_entity_kind do_get_entity_kind() const noexcept override; - - cpp_access_specifier_kind access_; + std::unique_ptr class_; }; - /// A [cppast::cpp_entity]() modelling a base class specifier. - class cpp_base_class final : public cpp_entity + /// \returns The keyword used in the declaration of the class. + cpp_class_kind class_kind() const noexcept { - public: - static cpp_entity_kind kind() noexcept; + return kind_; + } - /// \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 build(std::string name, - std::unique_ptr base, - cpp_access_specifier_kind access, - bool is_virtual) - { - return std::unique_ptr( - 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 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 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, - public cpp_forward_declarable + /// \returns Whether or not the class was declared `final`. + bool is_final() const noexcept { - public: - static cpp_entity_kind kind() noexcept; + return final_; + } - /// Builds a [cppast::cpp_class](). - class builder - { - public: - /// \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 An iteratable object iterating over the [cppast::cpp_base_class]() specifiers. + detail::iteratable_intrusive_list bases() const noexcept + { + return type_safe::ref(bases_); + } - /// \effects Marks the class as final. - void is_final() noexcept - { - class_->final_ = true; - } +private: + cpp_class(std::string name, cpp_class_kind kind, bool final) + : cpp_entity(std::move(name)), kind_(kind), final_(final) + {} - /// \effects Builds a [cppast::cpp_base_class]() and adds it. - cpp_base_class& base_class(std::string name, std::unique_ptr 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)); - } + cpp_entity_kind do_get_entity_kind() const noexcept override; - /// \effects Adds a new base class. - cpp_base_class& add_base_class(std::unique_ptr base) noexcept - { - auto bptr = base.get(); - class_->bases_.push_back(*class_, std::move(base)); - return *bptr; - } + type_safe::optional do_get_scope_name() const override + { + return type_safe::ref(*this); + } - /// \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)); - } + detail::intrusive_list bases_; + cpp_class_kind kind_; + bool final_; +}; - /// \effects Adds an entity. - void add_child(std::unique_ptr child) noexcept - { - class_->add_child(std::move(child)); - } +/// \returns The type the base class refers to. +/// It is either a class or some form of typedef. +type_safe::optional_ref get_class_or_typedef(const cpp_entity_index& index, + const cpp_base_class& base); - /// \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 finish(const cpp_entity_index& idx, cpp_entity_id id, - type_safe::optional semantic_parent); - - /// \effects Marks the class as forward declaration. - /// \returns The finished class. - std::unique_ptr 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 finish(type_safe::optional 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 finish_declaration(cpp_entity_id definition_id); - - private: - std::unique_ptr 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 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 do_get_scope_name() const override - { - return type_safe::ref(*this); - } - - detail::intrusive_list 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 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 get_class(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 get_class(const cpp_entity_index& index, + const cpp_base_class& base); } // namespace cppast #endif // CPPAST_CPP_CLASS_HPP_INCLUDED diff --git a/include/cppast/cpp_class_template.hpp b/include/cppast/cpp_class_template.hpp index 91f8d34..9837a03 100644 --- a/include/cppast/cpp_class_template.hpp +++ b/include/cppast/cpp_class_template.hpp @@ -10,65 +10,63 @@ namespace cppast { - /// A [cppast::cpp_entity]() modelling a class template. - class cpp_class_template final : public cpp_template +/// A [cppast::cpp_entity]() modelling a class 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 { public: - static cpp_entity_kind kind() noexcept; - - /// Builder for [cppast::cpp_class_template](). - class builder : public basic_builder - { - public: - using basic_builder::basic_builder; - }; - - /// A reference to the class that is being templated. - const cpp_class& class_() const noexcept - { - return static_cast(*begin()); - } - - private: - cpp_class_template(std::unique_ptr func) - : cpp_template(std::unique_ptr(func.release())) - { - } - - cpp_entity_kind do_get_entity_kind() const noexcept override; - - friend basic_builder; + using basic_builder::basic_builder; }; - /// A [cppast::cpp_entity]() modelling a class template specialization. - class cpp_class_template_specialization final : public cpp_template_specialization + /// A reference to the class that is being templated. + const cpp_class& class_() const noexcept + { + return static_cast(*begin()); + } + +private: + cpp_class_template(std::unique_ptr func) + : cpp_template(std::unique_ptr(func.release())) + {} + + cpp_entity_kind do_get_entity_kind() const noexcept override; + + friend basic_builder; +}; + +/// 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 { public: - static cpp_entity_kind kind() noexcept; - - /// Builder for [cppast::cpp_class_template_specialization](). - class builder : public specialization_builder - { - public: - using specialization_builder::specialization_builder; - }; - - /// A reference to the class that is being specialized. - const cpp_class& class_() const noexcept - { - return static_cast(*begin()); - } - - private: - cpp_class_template_specialization(std::unique_ptr func, cpp_template_ref primary) - : cpp_template_specialization(std::unique_ptr(func.release()), primary) - { - } - - cpp_entity_kind do_get_entity_kind() const noexcept override; - - friend specialization_builder; + using specialization_builder::specialization_builder; }; + + /// A reference to the class that is being specialized. + const cpp_class& class_() const noexcept + { + return static_cast(*begin()); + } + +private: + cpp_class_template_specialization(std::unique_ptr func, cpp_template_ref primary) + : cpp_template_specialization(std::unique_ptr(func.release()), primary) + {} + + cpp_entity_kind do_get_entity_kind() const noexcept override; + + friend specialization_builder; +}; } // namespace cppast #endif // CPPAST_CPP_CLASS_TEMPLATE_HPP_INCLUDED diff --git a/include/cppast/cpp_decltype_type.hpp b/include/cppast/cpp_decltype_type.hpp index 66baa85..ecd39f7 100644 --- a/include/cppast/cpp_decltype_type.hpp +++ b/include/cppast/cpp_decltype_type.hpp @@ -10,51 +10,51 @@ namespace cppast { - /// A [cppast::cpp_type]() that isn't given but taken from an expression. - class cpp_decltype_type final : public cpp_type +/// A [cppast::cpp_type]() that isn't given but taken from an expression. +class cpp_decltype_type final : public cpp_type +{ +public: + /// \returns A newly created `decltype` type. + static std::unique_ptr build(std::unique_ptr expr) { - public: - /// \returns A newly created `decltype` type. - static std::unique_ptr build(std::unique_ptr expr) - { - return std::unique_ptr(new cpp_decltype_type(std::move(expr))); - } + return std::unique_ptr(new cpp_decltype_type(std::move(expr))); + } - /// \returns A reference to the expression given. - const cpp_expression& expression() const noexcept - { - return *expr_; - } - - private: - cpp_decltype_type(std::unique_ptr expr) : expr_(std::move(expr)) {} - - cpp_type_kind do_get_kind() const noexcept override - { - return cpp_type_kind::decltype_t; - } - - std::unique_ptr expr_; - }; - - /// A [cppast::cpp_type]() that isn't given but deduced using the `decltype` rules. - class cpp_decltype_auto_type final : public cpp_type + /// \returns A reference to the expression given. + const cpp_expression& expression() const noexcept { - public: - /// \returns A newly created `auto` type. - static std::unique_ptr build() - { - return std::unique_ptr(new cpp_decltype_auto_type); - } + return *expr_; + } - private: - cpp_decltype_auto_type() = default; +private: + cpp_decltype_type(std::unique_ptr expr) : expr_(std::move(expr)) {} - cpp_type_kind do_get_kind() const noexcept override - { - return cpp_type_kind::decltype_auto_t; - } - }; + cpp_type_kind do_get_kind() const noexcept override + { + return cpp_type_kind::decltype_t; + } + + std::unique_ptr 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 build() + { + return std::unique_ptr(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 #endif // CPPAST_CPP_DECLTYPE_TYPE_HPP_INCLUDED diff --git a/include/cppast/cpp_entity.hpp b/include/cppast/cpp_entity.hpp index d51f059..0dbbf0d 100644 --- a/include/cppast/cpp_entity.hpp +++ b/include/cppast/cpp_entity.hpp @@ -10,229 +10,227 @@ #include -#include #include #include +#include namespace cppast { - class cpp_entity; - enum class cpp_entity_kind; - class cpp_entity_index; - struct cpp_entity_id; - class cpp_template_parameter; - class cpp_template; +class cpp_entity; +enum class cpp_entity_kind; +class cpp_entity_index; +struct cpp_entity_id; +class cpp_template_parameter; +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 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 template_parameters() const noexcept; + +private: + type_safe::object_ref entity_; + type_safe::optional_ref templ_; +}; + +/// The base class for all entities in the C++ AST. +class cpp_entity : detail::intrusive_list_node +{ +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 scope_name() const + { + return do_get_scope_name(); + } + + /// \returns A [ts::optional_ref]() to the parent entity in the AST. + type_safe::optional_ref 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. - class cpp_scope_name - { - public: - /// \effects Creates a scope out of a given entity. - cpp_scope_name(type_safe::object_ref 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 template_parameters() const - noexcept; - - private: - type_safe::object_ref entity_; - type_safe::optional_ref templ_; - }; - - /// The base class for all entities in the C++ AST. - class cpp_entity : detail::intrusive_list_node - { - 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 scope_name() const - { - return do_get_scope_name(); - } - - /// \returns A [ts::optional_ref]() to the parent entity in the AST. - type_safe::optional_ref 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 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 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 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 parent_; - mutable std::atomic user_data_; - - template - friend struct detail::intrusive_list_access; - friend detail::intrusive_list_node; - }; - - /// A [cppast::cpp_entity]() that isn't exposed directly. + /// * 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. /// - /// The only information available is the raw source code. - class cpp_unexposed_entity final : public cpp_entity + /// 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 comment() const noexcept { - public: - static cpp_entity_kind kind() noexcept; + return comment_.empty() ? nullptr : type_safe::opt_ref(&comment_); + } - /// \returns A newly built and registered unexposed entity. - /// \notes It will be registered as a declaration. - static std::unique_ptr build(const cpp_entity_index& index, cpp_entity_id id, - std::string name, cpp_token_string spelling); + /// \effects Sets the associated comment. + /// \requires The comment must not be empty, if there is one. + void set_comment(type_safe::optional comment) noexcept + { + comment_ = comment.value_or(""); + } - /// \returns A newly built unnamed unexposed entity. - /// It will not be registered. - static std::unique_ptr build(cpp_token_string spelling); + /// \returns The list of attributes that are specified for that entity. + const cpp_attribute_list& attributes() const noexcept + { + return attributes_; + } - /// \returns The spelling of that entity. - const cpp_token_string& spelling() const noexcept - { - return spelling_; - } + /// \effects Adds an attribute for that entity. + void add_attribute(cpp_attribute attr) noexcept + { + attributes_.push_back(std::move(attr)); + } - private: - cpp_unexposed_entity(std::string name, cpp_token_string spelling) - : cpp_entity(std::move(name)), spelling_(std::move(spelling)) - { - } + /// \effects Adds multiple arguments for that entity. + void add_attribute(const cpp_attribute_list& list) noexcept + { + 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. - /// 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; +protected: + /// \effects Creates it giving it the the name. + cpp_entity(std::string name) : name_(std::move(name)), user_data_(nullptr) {} - /// \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; +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 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 parent_; + mutable std::atomic user_data_; + + template + friend struct detail::intrusive_list_access; + friend detail::intrusive_list_node; +}; + +/// 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 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 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 #endif // CPPAST_CPP_ENTITY_HPP_INCLUDED diff --git a/include/cppast/cpp_entity_container.hpp b/include/cppast/cpp_entity_container.hpp index 83bd24c..31fd8c8 100644 --- a/include/cppast/cpp_entity_container.hpp +++ b/include/cppast/cpp_entity_container.hpp @@ -9,51 +9,51 @@ namespace cppast { - /// Helper class for entities that are containers. - /// - /// Inherit from it to generate container access. - template - class cpp_entity_container +/// Helper class for entities that are containers. +/// +/// Inherit from it to generate container access. +template +class cpp_entity_container +{ +public: + using iterator = typename detail::intrusive_list::const_iterator; + + /// \returns A const iterator to the first child. + iterator begin() const noexcept { - public: - using iterator = typename detail::intrusive_list::const_iterator; + return children_.begin(); + } - /// \returns A const iterator to the first child. - iterator begin() const noexcept - { - return children_.begin(); - } + /// \returns A const iterator to the last child. + iterator end() const noexcept + { + return children_.end(); + } - /// \returns A const iterator to the last child. - iterator end() const noexcept - { - return children_.end(); - } +protected: + /// \effects Adds a new child to the container. + void add_child(std::unique_ptr ptr) noexcept + { + children_.push_back(static_cast(*this), std::move(ptr)); + } - protected: - /// \effects Adds a new child to the container. - void add_child(std::unique_ptr ptr) noexcept - { - children_.push_back(static_cast(*this), std::move(ptr)); - } + /// \returns A non-const iterator to the first child. + typename detail::intrusive_list::iterator mutable_begin() noexcept + { + return children_.begin(); + } - /// \returns A non-const iterator to the first child. - typename detail::intrusive_list::iterator mutable_begin() noexcept - { - return children_.begin(); - } + /// \returns A non-const iterator one past the last child. + typename detail::intrusive_list::iterator mutable_end() noexcept + { + return children_.begin(); + } - /// \returns A non-const iterator one past the last child. - typename detail::intrusive_list::iterator mutable_end() noexcept - { - return children_.begin(); - } + ~cpp_entity_container() noexcept = default; - ~cpp_entity_container() noexcept = default; - - private: - detail::intrusive_list children_; - }; +private: + detail::intrusive_list children_; +}; } // namespace cppast #endif // CPPAST_CPP_ENTITY_CONTAINER_HPP_INCLUDED diff --git a/include/cppast/cpp_entity_index.hpp b/include/cppast/cpp_entity_index.hpp index abef531..031b50f 100644 --- a/include/cppast/cpp_entity_index.hpp +++ b/include/cppast/cpp_entity_index.hpp @@ -16,130 +16,128 @@ namespace cppast { - class cpp_entity; - class cpp_file; - class cpp_namespace; +class cpp_entity; +class cpp_file; +class cpp_namespace; - /// \exclude - namespace detail +/// \exclude +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; - 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, - type_safe::strong_typedef_op::equality_comparison - { - 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); - } + return *str ? id_hash(str + 1, (hash ^ std::size_t(*str)) * fnv_prime) : hash; } +} // namespace detail - /// 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 +/// 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, + type_safe::strong_typedef_op::equality_comparison +{ + 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: - /// Exception thrown on duplicate entity definition. - 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 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 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 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 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 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 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>; - - private: - struct hash - { - std::size_t operator()(const cpp_entity_id& id) const noexcept - { - return static_cast(id); - } - }; - - struct value - { - type_safe::object_ref entity; - bool is_definition; - - value(type_safe::object_ref e, bool def) - : entity(std::move(e)), is_definition(def) - { - } - }; - - mutable std::mutex mutex_; - mutable std::unordered_map map_; - mutable std::unordered_map>, hash> - ns_; + 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 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 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 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 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 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 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>; + +private: + struct hash + { + std::size_t operator()(const cpp_entity_id& id) const noexcept + { + return static_cast(id); + } + }; + + struct value + { + type_safe::object_ref entity; + bool is_definition; + + value(type_safe::object_ref e, bool def) + : entity(std::move(e)), is_definition(def) + {} + }; + + mutable std::mutex mutex_; + mutable std::unordered_map map_; + mutable std::unordered_map>, hash> + ns_; +}; } // namespace cppast #endif // CPPAST_CPP_ENTITY_INDEX_HPP_INCLUDED diff --git a/include/cppast/cpp_entity_kind.hpp b/include/cppast/cpp_entity_kind.hpp index b687114..2159d53 100644 --- a/include/cppast/cpp_entity_kind.hpp +++ b/include/cppast/cpp_entity_kind.hpp @@ -9,81 +9,81 @@ namespace cppast { - /// All possible kinds of C++ entities. - enum class cpp_entity_kind - { - file_t, +/// All possible kinds of C++ entities. +enum class cpp_entity_kind +{ + file_t, - macro_parameter_t, - macro_definition_t, - include_directive_t, + macro_parameter_t, + macro_definition_t, + include_directive_t, - language_linkage_t, + language_linkage_t, - namespace_t, - namespace_alias_t, - using_directive_t, - using_declaration_t, + namespace_t, + namespace_alias_t, + using_directive_t, + using_declaration_t, - type_alias_t, + type_alias_t, - enum_t, - enum_value_t, + enum_t, + enum_value_t, - class_t, - access_specifier_t, - base_class_t, + class_t, + access_specifier_t, + base_class_t, - variable_t, - member_variable_t, - bitfield_t, + variable_t, + member_variable_t, + bitfield_t, - function_parameter_t, - function_t, - member_function_t, - conversion_op_t, - constructor_t, - destructor_t, + function_parameter_t, + function_t, + member_function_t, + conversion_op_t, + constructor_t, + destructor_t, - friend_t, + friend_t, - template_type_parameter_t, - non_type_template_parameter_t, - template_template_parameter_t, + template_type_parameter_t, + non_type_template_parameter_t, + template_template_parameter_t, - alias_template_t, - variable_template_t, - function_template_t, - function_template_specialization_t, - class_template_t, - class_template_specialization_t, + alias_template_t, + variable_template_t, + function_template_t, + function_template_specialization_t, + class_template_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. - const char* to_string(cpp_entity_kind kind) noexcept; +/// \returns A human readable string describing the entity kind. +const char* to_string(cpp_entity_kind kind) noexcept; - /// \returns Whether or not a given entity kind is a C++ function, - /// that is, it dervies from [cppast::cpp_function_base](). - bool is_function(cpp_entity_kind kind) noexcept; +/// \returns Whether or not a given entity kind is a C++ function, +/// that is, it dervies from [cppast::cpp_function_base](). +bool is_function(cpp_entity_kind kind) noexcept; - /// \returns Whether or not a given entity kind is a C++ (template) parameter. - bool is_parameter(cpp_entity_kind kind) noexcept; +/// \returns Whether or not a given entity kind is a C++ (template) parameter. +bool is_parameter(cpp_entity_kind kind) noexcept; - /// \returns Whether or not a given entity kind is a C++ template, - /// that is, it dervies from [cppast::cpp_template](). - /// \notes A template template parameter is not considered a template for this function. - /// \notes Template specializations are also considered templates here. - bool is_template(cpp_entity_kind kind) noexcept; +/// \returns Whether or not a given entity kind is a C++ template, +/// that is, it dervies from [cppast::cpp_template](). +/// \notes A template template parameter is not considered a template for this function. +/// \notes Template specializations are also considered templates here. +bool is_template(cpp_entity_kind kind) noexcept; - /// \returns Whether or not a given entity kind is a specialization of a C++ template, - /// that is, it derives from [cppast::cpp_template_specialization](). - bool is_template_specialization(cpp_entity_kind kind) noexcept; +/// \returns Whether or not a given entity kind is a specialization of a C++ template, +/// that is, it derives from [cppast::cpp_template_specialization](). +bool is_template_specialization(cpp_entity_kind kind) noexcept; } // namespace cppast #endif // CPPAST_CPP_ENTITY_KIND_HPP_INCLUDED diff --git a/include/cppast/cpp_entity_ref.hpp b/include/cppast/cpp_entity_ref.hpp index 5ede2fb..0c3b3bd 100644 --- a/include/cppast/cpp_entity_ref.hpp +++ b/include/cppast/cpp_entity_ref.hpp @@ -9,123 +9,121 @@ #include -#include #include +#include namespace cppast { - enum class cpp_entity_kind; +enum class cpp_entity_kind; - /// A basic reference to some kind of [cppast::cpp_entity](). - /// - /// It can either refer to a single [cppast::cpp_entity]() - /// or multiple. - /// In the later case it is *overloaded*. - template - class basic_cpp_entity_ref +/// A basic reference to some kind of [cppast::cpp_entity](). +/// +/// It can either refer to a single [cppast::cpp_entity]() +/// or multiple. +/// In the later case it is *overloaded*. +template +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 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: - /// \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 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>{}); - } - - /// \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 id() const noexcept - { - if (is_overloaded()) - { - auto& vec = target_.value(type_safe::variant_type>{}); - return type_safe::ref(vec.data(), vec.size()); - } - else - { - auto& id = target_.value(type_safe::variant_type{}); - 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> get(const cpp_entity_index& idx) const - { - std::vector> result; - get_impl(std::is_convertible{}, result, idx); - return result; - } - - private: - void get_impl(std::true_type, std::vector>& 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::value) - get_impl(std::false_type{}, result, idx); - } - - void get_impl(std::false_type, std::vector>& 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(e)); - }); - if (entity) - result.push_back(type_safe::ref(entity.value())); - } - } - - type_safe::variant> target_; - std::string name_; - }; - - /// \exclude - namespace detail - { - struct cpp_entity_ref_predicate - { - bool operator()(const cpp_entity&) - { - return true; - } - }; + return name_; } - /// A [cppast::basic_cpp_entity_ref]() to any [cppast::cpp_entity](). - using cpp_entity_ref = basic_cpp_entity_ref; + /// \returns Whether or not it refers to multiple entities. + bool is_overloaded() const noexcept + { + return target_.has_value(type_safe::variant_type>{}); + } + + /// \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 id() const noexcept + { + if (is_overloaded()) + { + auto& vec = target_.value(type_safe::variant_type>{}); + return type_safe::ref(vec.data(), vec.size()); + } + else + { + auto& id = target_.value(type_safe::variant_type{}); + 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> get(const cpp_entity_index& idx) const + { + std::vector> result; + get_impl(std::is_convertible{}, result, idx); + return result; + } + +private: + void get_impl(std::true_type, std::vector>& 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::value) + get_impl(std::false_type{}, result, idx); + } + + void get_impl(std::false_type, std::vector>& 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(e)); + }); + if (entity) + result.push_back(type_safe::ref(entity.value())); + } + } + + type_safe::variant> 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; } // namespace cppast #endif // CPPAST_CPP_ENTITY_REF_HPP_INCLUDED diff --git a/include/cppast/cpp_enum.hpp b/include/cppast/cpp_enum.hpp index 4845d79..bd2dffe 100644 --- a/include/cppast/cpp_enum.hpp +++ b/include/cppast/cpp_enum.hpp @@ -9,139 +9,132 @@ #include +#include #include #include -#include #include #include #include namespace cppast { - /// A [cppast::cpp_entity]() modelling the value of an [cppast::cpp_enum](). - class cpp_enum_value final : public cpp_entity +/// A [cppast::cpp_entity]() modelling the value of an [cppast::cpp_enum](). +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 build(const cpp_entity_index& idx, cpp_entity_id id, + std::string name, + std::unique_ptr 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 value() const noexcept + { + return type_safe::opt_cref(value_.get()); + } + +private: + cpp_enum_value(std::string name, std::unique_ptr value) + : cpp_entity(std::move(name)), value_(std::move(value)) + {} + + cpp_entity_kind do_get_entity_kind() const noexcept override; + + std::unique_ptr 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, + public cpp_forward_declarable +{ +public: + static cpp_entity_kind kind() noexcept; + + /// Builds a [cppast::cpp_enum](). + class builder { 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 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. - /// \notes `value` may be `nullptr`, in which case the enum has an implicit value. - static std::unique_ptr build( - const cpp_entity_index& idx, cpp_entity_id id, std::string name, - std::unique_ptr 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 value() const noexcept + /// \effects Adds a [cppast::cpp_enum_value](). + void add_value(std::unique_ptr value) { - 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 finish( + const cpp_entity_index& idx, cpp_entity_id id, + type_safe::optional 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 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: - cpp_enum_value(std::string name, std::unique_ptr value) - : cpp_entity(std::move(name)), value_(std::move(value)) - { - } - - cpp_entity_kind do_get_entity_kind() const noexcept override; - - std::unique_ptr value_; + std::unique_ptr enum_; }; - /// 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, - public cpp_forward_declarable + /// \returns A reference to the underlying [cppast::cpp_type]() of the enum. + const cpp_type& underlying_type() const noexcept { - public: - static cpp_entity_kind kind() noexcept; + return *type_; + } - /// Builds a [cppast::cpp_enum](). - class builder - { - public: - /// \effects Sets the name, underlying type and whether it is scoped. - builder(std::string name, bool scoped, std::unique_ptr type, - bool explicit_type) - : enum_(new cpp_enum(std::move(name), std::move(type), explicit_type, scoped)) - { - } + /// \returns Whether or not the underlying type is explictly given. + bool has_explicit_type() const noexcept + { + return type_given_; + } - /// \effects Adds a [cppast::cpp_enum_value](). - void add_value(std::unique_ptr value) - { - enum_->add_child(std::move(value)); - } + /// \returns Whether or not it is a scoped enumeration (i.e. an `enum class`). + bool is_scoped() const noexcept + { + return scoped_; + } - /// \returns The not yet finished enumeration. - cpp_enum& get() noexcept - { - return *enum_; - } +private: + cpp_enum(std::string name, std::unique_ptr type, bool type_given, bool scoped) + : cpp_entity(std::move(name)), type_(std::move(type)), scoped_(scoped), type_given_(type_given) + {} - /// \effects Registers the enum in the [cppast::cpp_entity_index](), - /// using the given [cppast::cpp_entity_id](). - /// \returns The finished enum. - std::unique_ptr finish( - const cpp_entity_index& idx, cpp_entity_id id, - type_safe::optional 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_); - } + cpp_entity_kind do_get_entity_kind() const noexcept override; - /// \effects Marks the enum as forward declaration. - /// \returns The finished enum. - std::unique_ptr 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_); - } + type_safe::optional do_get_scope_name() const override; - private: - std::unique_ptr enum_; - }; - - /// \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 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 do_get_scope_name() const override; - - std::unique_ptr type_; - bool scoped_, type_given_; - }; + std::unique_ptr type_; + bool scoped_, type_given_; +}; } // namespace cppast #endif // CPPAST_CPP_ENUM_HPP_INCLUDED diff --git a/include/cppast/cpp_expression.hpp b/include/cppast/cpp_expression.hpp index 7a5a998..54b658d 100644 --- a/include/cppast/cpp_expression.hpp +++ b/include/cppast/cpp_expression.hpp @@ -13,139 +13,138 @@ namespace cppast { - /// The kind of a [cppast::cpp_expression](). - enum class cpp_expression_kind +/// The kind of a [cppast::cpp_expression](). +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, - }; - - /// Base class for all C++ expressions. - class cpp_expression + /// \returns The type of the expression. + const cpp_type& type() const noexcept { - public: - cpp_expression(const cpp_expression&) = delete; - cpp_expression& operator=(const cpp_expression&) = delete; + return *type_; + } - 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](). - 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 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 type_; - mutable std::atomic user_data_; - }; - - /// An unexposed [cppast::cpp_expression](). + /// \effects Sets some kind of user data. /// - /// There is no further information than a string available. - class cpp_unexposed_expression final : public cpp_expression + /// 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: - /// \returns A newly created unexposed expression. - static std::unique_ptr build(std::unique_ptr type, - cpp_token_string str) - { - return std::unique_ptr( - new cpp_unexposed_expression(std::move(type), std::move(str))); - } + user_data_ = data; + } - /// \returns The expression as a string. - const cpp_token_string& expression() const noexcept - { - return str_; - } - - private: - cpp_unexposed_expression(std::unique_ptr 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 +protected: + /// \effects Creates it given the type. + /// \requires The type must not be `nullptr`. + cpp_expression(std::unique_ptr type) : type_(std::move(type)), user_data_(nullptr) { - public: - /// \returns A newly created literal expression. - static std::unique_ptr build(std::unique_ptr type, - std::string value) - { - return std::unique_ptr( - new cpp_literal_expression(std::move(type), std::move(value))); - } + DEBUG_ASSERT(type_ != nullptr, detail::precondition_error_handler{}); + } - /// \returns The value of the literal, as string. - const std::string& value() const noexcept - { - return value_; - } +private: + /// \returns The [cppast::cpp_expression_kind](). + virtual cpp_expression_kind do_get_kind() const noexcept = 0; - private: - cpp_literal_expression(std::unique_ptr type, std::string value) - : cpp_expression(std::move(type)), value_(std::move(value)) - { - } + std::unique_ptr type_; + mutable std::atomic user_data_; +}; - cpp_expression_kind do_get_kind() const noexcept override - { - return cpp_expression_kind::literal_t; - } - - std::string value_; - }; - - /// \exclude - namespace detail +/// An unexposed [cppast::cpp_expression](). +/// +/// There is no further information than a string available. +class cpp_unexposed_expression final : public cpp_expression +{ +public: + /// \returns A newly created unexposed expression. + static std::unique_ptr build(std::unique_ptr type, + cpp_token_string str) { - void write_expression(code_generator::output& output, const cpp_expression& expr); - } // namespace detail + return std::unique_ptr( + 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 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 build(std::unique_ptr type, + std::string value) + { + return std::unique_ptr( + 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 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 #endif // CPPAST_CPP_EXPRESSION_HPP_INCLUDED diff --git a/include/cppast/cpp_file.hpp b/include/cppast/cpp_file.hpp index cd26159..f241ebf 100644 --- a/include/cppast/cpp_file.hpp +++ b/include/cppast/cpp_file.hpp @@ -7,96 +7,93 @@ #include -#include #include +#include #include namespace cppast { - /// An unmatched documentation comment. - struct cpp_doc_comment - { - std::string content; - unsigned line; +/// An unmatched documentation comment. +struct cpp_doc_comment +{ + std::string content; + unsigned line; - cpp_doc_comment(std::string content, unsigned line) - : content(std::move(content)), line(line) - { - } - }; + cpp_doc_comment(std::string content, unsigned line) : content(std::move(content)), line(line) {} +}; - /// A [cppast::cpp_entity]() modelling a file. - /// - /// This is the top-level entity of the AST. - class cpp_file final : public cpp_entity, public cpp_entity_container +/// A [cppast::cpp_entity]() modelling a file. +/// +/// This is the top-level entity of the AST. +class cpp_file final : public cpp_entity, public cpp_entity_container +{ +public: + static cpp_entity_kind kind() noexcept; + + /// Builds a [cppast::cpp_file](). + class builder { 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](). - class builder + /// \effects Adds an entity. + void add_child(std::unique_ptr child) noexcept { - public: - /// \effects Sets the file name. - explicit builder(std::string name) : file_(new cpp_file(std::move(name))) {} + file_->add_child(std::move(child)); + } - /// \effects Adds an entity. - void add_child(std::unique_ptr child) noexcept - { - 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 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 file_; - }; - - /// \returns The unmatched documentation comments. - type_safe::array_ref unmatched_comments() const noexcept + /// \effects Adds an unmatched documentation comment. + void add_unmatched_comment(cpp_doc_comment comment) { - 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 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: - 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 comments_; + std::unique_ptr file_; }; - /// \exclude - namespace detail + /// \returns The unmatched documentation comments. + type_safe::array_ref unmatched_comments() const noexcept { - struct cpp_file_ref_predicate - { - bool operator()(const cpp_entity& e); - }; - } // namespace detail + return type_safe::ref(comments_.data(), comments_.size()); + } - /// A reference to a [cppast::cpp_file](). - using cpp_file_ref = basic_cpp_entity_ref; +private: + 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 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; } // namespace cppast #endif // CPPAST_CPP_FILE_HPP_INCLUDED diff --git a/include/cppast/cpp_forward_declarable.hpp b/include/cppast/cpp_forward_declarable.hpp index 4e16899..3b01aa3 100644 --- a/include/cppast/cpp_forward_declarable.hpp +++ b/include/cppast/cpp_forward_declarable.hpp @@ -14,104 +14,103 @@ namespace cppast { - /// Mixin base class for all entities that can have a forward declaration. - /// - /// Examples are [cppast::cpp_enum]() or [cppast::cpp_class](), - /// but also [cppast::cpp_function_base](). - /// Those entities can have multiple declarations and one definition. - class cpp_forward_declarable +/// Mixin base class for all entities that can have a forward declaration. +/// +/// Examples are [cppast::cpp_enum]() or [cppast::cpp_class](), +/// but also [cppast::cpp_function_base](). +/// Those entities can have multiple declarations and one definition. +class cpp_forward_declarable +{ +public: + /// \returns Whether or not the entity is the definition. + bool is_definition() const noexcept { - public: - /// \returns Whether or not the entity is the definition. - bool is_definition() const noexcept - { - return !definition_.has_value(); - } + return !definition_.has_value(); + } - /// \returns Whether or not the entity is "just" a declaration. - bool is_declaration() const noexcept - { - return definition_.has_value(); - } + /// \returns Whether or not the entity is "just" a declaration. + bool is_declaration() const noexcept + { + return definition_.has_value(); + } - /// \returns The [cppast::cpp_entity_id]() of the definition, - /// if the current entity is not the definition. - const type_safe::optional& definition() const noexcept - { - return definition_; - } + /// \returns The [cppast::cpp_entity_id]() of the definition, + /// if the current entity is not the definition. + const type_safe::optional& definition() const noexcept + { + return definition_; + } - /// \returns A reference to the semantic parent of the entity. - /// This applies only to out-of-line definitions - /// and is the entity which owns the declaration. - const type_safe::optional& semantic_parent() const noexcept - { - return semantic_parent_; - } + /// \returns A reference to the semantic parent of the entity. + /// This applies only to out-of-line definitions + /// and is the entity which owns the declaration. + const type_safe::optional& semantic_parent() const noexcept + { + return semantic_parent_; + } - /// \returns The name of the semantic parent, if it has one, - /// else the empty string. - /// \notes This may include template parameters. - std::string semantic_scope() const noexcept - { - return type_safe::copy(semantic_parent_.map(&cpp_entity_ref::name)).value_or(""); - } + /// \returns The name of the semantic parent, if it has one, + /// else the empty string. + /// \notes This may include template parameters. + std::string semantic_scope() const noexcept + { + return type_safe::copy(semantic_parent_.map(&cpp_entity_ref::name)).value_or(""); + } - protected: - /// \effects Marks the entity as definition. - /// \notes If it is not a definition, - /// [*set_definition]() must be called. - cpp_forward_declarable() noexcept = default; +protected: + /// \effects Marks the entity as definition. + /// \notes If it is not a definition, + /// [*set_definition]() must be called. + cpp_forward_declarable() noexcept = default; - ~cpp_forward_declarable() noexcept = default; + ~cpp_forward_declarable() noexcept = default; - /// \effects Sets the definition entity, - /// marking it as a forward declaration. - void mark_declaration(cpp_entity_id def) noexcept - { - definition_ = std::move(def); - } + /// \effects Sets the definition entity, + /// marking it as a forward declaration. + void mark_declaration(cpp_entity_id def) noexcept + { + definition_ = std::move(def); + } - /// \effects Sets the semantic parent of the entity. - void set_semantic_parent(type_safe::optional semantic_parent) noexcept - { - semantic_parent_ = std::move(semantic_parent); - } + /// \effects Sets the semantic parent of the entity. + void set_semantic_parent(type_safe::optional semantic_parent) noexcept + { + semantic_parent_ = std::move(semantic_parent); + } - private: - type_safe::optional semantic_parent_; - type_safe::optional definition_; - }; +private: + type_safe::optional semantic_parent_; + type_safe::optional definition_; +}; - /// \returns Whether or not the given entity is a definition. - bool is_definition(const cpp_entity& e) noexcept; +/// \returns Whether or not the given entity is a definition. +bool is_definition(const cpp_entity& e) noexcept; - class cpp_enum; - class cpp_class; - class cpp_variable; - class cpp_function_base; +class cpp_enum; +class cpp_class; +class cpp_variable; +class cpp_function_base; - /// Gets the definition of an entity. - /// \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), - /// returns a reference to the entity itself. - /// Otherwise lookups the definition id and returns it. - /// \notes The return value will only be `nullptr`, if the definition is not registered. - /// \group get_definition - type_safe::optional_ref get_definition(const cpp_entity_index& idx, - const cpp_entity& e); - /// \group get_definition - type_safe::optional_ref get_definition(const cpp_entity_index& idx, - const cpp_enum& e); - /// \group get_definition - type_safe::optional_ref get_definition(const cpp_entity_index& idx, - const cpp_class& e); - /// \group get_definition - type_safe::optional_ref get_definition(const cpp_entity_index& idx, - const cpp_variable& e); - /// \group get_definition - type_safe::optional_ref get_definition(const cpp_entity_index& idx, - const cpp_function_base& e); +/// Gets the definition of an entity. +/// \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), returns a reference to the entity itself. Otherwise lookups +/// the definition id and returns it. \notes The return value will only be `nullptr`, if the +/// definition is not registered. \group get_definition +type_safe::optional_ref get_definition(const cpp_entity_index& idx, + const cpp_entity& e); +/// \group get_definition +type_safe::optional_ref get_definition(const cpp_entity_index& idx, + const cpp_enum& e); +/// \group get_definition +type_safe::optional_ref get_definition(const cpp_entity_index& idx, + const cpp_class& e); +/// \group get_definition +type_safe::optional_ref get_definition(const cpp_entity_index& idx, + const cpp_variable& e); +/// \group get_definition +type_safe::optional_ref get_definition(const cpp_entity_index& idx, + const cpp_function_base& e); } // namespace cppast diff --git a/include/cppast/cpp_friend.hpp b/include/cppast/cpp_friend.hpp index 4cd8010..d2ba362 100644 --- a/include/cppast/cpp_friend.hpp +++ b/include/cppast/cpp_friend.hpp @@ -13,58 +13,58 @@ namespace cppast { - /// A [cppast::cpp_entity]() representing a friend declaration. - /// - /// It can either declare or define a `friend` function (template), declare a `friend` class, - /// or refer to an existing type. - class cpp_friend : public cpp_entity, private cpp_entity_container +/// A [cppast::cpp_entity]() representing a friend declaration. +/// +/// It can either declare or define a `friend` function (template), declare a `friend` class, +/// or refer to an existing type. +class cpp_friend : public cpp_entity, private cpp_entity_container +{ +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 build(std::unique_ptr e) { - public: - static cpp_entity_kind kind() noexcept; + return std::unique_ptr(new cpp_friend(std::move(e))); + } - /// \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 build(std::unique_ptr e) - { - return std::unique_ptr(new cpp_friend(std::move(e))); - } + /// \returns A newly created friend declaring the given type as `friend`. + /// \notes It will not be registered. + static std::unique_ptr build(std::unique_ptr type) + { + return std::unique_ptr(new cpp_friend(std::move(type))); + } - /// \returns A newly created friend declaring the given type as `friend`. - /// \notes It will not be registered. - static std::unique_ptr build(std::unique_ptr type) - { - return std::unique_ptr(new cpp_friend(std::move(type))); - } + /// \returns An optional reference to the entity it declares as friend, or `nullptr`. + type_safe::optional_ref entity() const noexcept + { + if (begin() == end()) + return nullptr; + return type_safe::ref(*begin()); + } - /// \returns An optional reference to the entity it declares as friend, or `nullptr`. - type_safe::optional_ref entity() const noexcept - { - if (begin() == end()) - return nullptr; - return type_safe::ref(*begin()); - } + /// \returns An optional reference to the type it declares as friend, or `nullptr`. + type_safe::optional_ref type() const noexcept + { + return type_safe::opt_ref(type_.get()); + } - /// \returns An optional reference to the type it declares as friend, or `nullptr`. - type_safe::optional_ref type() const noexcept - { - return type_safe::opt_ref(type_.get()); - } +private: + cpp_friend(std::unique_ptr e) : cpp_entity("") + { + add_child(std::move(e)); + } - private: - cpp_friend(std::unique_ptr e) : cpp_entity("") - { - add_child(std::move(e)); - } + cpp_friend(std::unique_ptr type) : cpp_entity(""), type_(std::move(type)) {} - cpp_friend(std::unique_ptr 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 type_; - std::unique_ptr type_; - - friend cpp_entity_container; - }; + friend cpp_entity_container; +}; } // namespace cppast #endif // CPPAST_CPP_FRIEND_HPP_INCLUDED diff --git a/include/cppast/cpp_function.hpp b/include/cppast/cpp_function.hpp index 2df46be..5eab229 100644 --- a/include/cppast/cpp_function.hpp +++ b/include/cppast/cpp_function.hpp @@ -5,262 +5,261 @@ #ifndef CPPAST_CPP_FUNCTION_HPP_INCLUDED #define CPPAST_CPP_FUNCTION_HPP_INCLUDED -#include #include #include #include #include +#include namespace cppast { - /// A [cppast::cpp_entity]() modelling a function parameter. - class cpp_function_parameter final : public cpp_entity, public cpp_variable_base +/// A [cppast::cpp_entity]() modelling a function parameter. +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 build(const cpp_entity_index& idx, + cpp_entity_id id, std::string name, + std::unique_ptr type, + std::unique_ptr 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 build(std::unique_ptr type, + std::unique_ptr def + = nullptr); + +private: + cpp_function_parameter(std::string name, std::unique_ptr type, + std::unique_ptr 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 parameters() const noexcept { - public: - static cpp_entity_kind kind() noexcept; - - /// \returns A newly created and registered function parameter. - static std::unique_ptr build( - const cpp_entity_index& idx, cpp_entity_id id, std::string name, - std::unique_ptr type, std::unique_ptr 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 build( - std::unique_ptr type, std::unique_ptr def = nullptr); - - private: - cpp_function_parameter(std::string name, std::unique_ptr type, - std::unique_ptr 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; + return type_safe::ref(parameters_); } - /// \returns Whether or not the function body is a definition. - inline bool is_definition(cpp_function_body_kind body) noexcept + /// \returns The [cppast::cpp_function_body_kind](). + /// \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 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. - class cpp_function_base : public cpp_entity, public cpp_forward_declarable + /// Inherit from it to provide additional setter. + template + class basic_builder { public: - /// \returns An iteratable object iterating over the [cppast::cpp_function__parameter]() entities. - detail::iteratable_intrusive_list parameters() const noexcept + /// \effects Sets the name. + basic_builder(std::string name) : function(new T(name)) {} + + /// \effects Adds a parameter. + void add_parameter(std::unique_ptr parameter) { - return type_safe::ref(parameters_); + static_cast(*function).parameters_.push_back(*function, + std::move(parameter)); } - /// \returns The [cppast::cpp_function_body_kind](). - /// \notes This matches the [cppast::cpp_forward_declarable]() queries. - cpp_function_body_kind body_kind() const noexcept + /// \effects Marks the function as variadic. + void is_variadic() { - return body_; + static_cast(*function).variadic_ = true; } - /// \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 noexcept_condition() const noexcept + /// \effects Sets the noexcept condition expression. + void noexcept_condition(std::unique_ptr cond) { - return type_safe::opt_cref(noexcept_expr_.get()); + static_cast(*function).noexcept_expr_ = std::move(cond); } - /// \returns Whether the function has an ellipsis. - bool is_variadic() const noexcept + /// \returns The not yet finished function. + T& get() noexcept { - return variadic_; + return *function; } - /// \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 + /// \effects If the body is a definition, registers it. + /// Else marks it as a declaration. + /// \returns The finished function. + std::unique_ptr finish(const cpp_entity_index& idx, cpp_entity_id id, + cpp_function_body_kind body_kind, + type_safe::optional 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 finish(cpp_entity_id id, cpp_function_body_kind body_kind, + type_safe::optional 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: - /// Builder class for functions. - /// - /// Inherit from it to provide additional setter. - template - class basic_builder - { - public: - /// \effects Sets the name. - basic_builder(std::string name) : function(new T(name)) {} + basic_builder() = default; + ~basic_builder() noexcept = default; - /// \effects Adds a parameter. - void add_parameter(std::unique_ptr parameter) - { - static_cast(*function).parameters_.push_back(*function, - std::move( - parameter)); - } - - /// \effects Marks the function as variadic. - void is_variadic() - { - static_cast(*function).variadic_ = true; - } - - /// \effects Sets the noexcept condition expression. - void noexcept_condition(std::unique_ptr cond) - { - static_cast(*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 finish(const cpp_entity_index& idx, cpp_entity_id id, - cpp_function_body_kind body_kind, - type_safe::optional 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 finish(cpp_entity_id id, cpp_function_body_kind body_kind, - type_safe::optional 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 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 parameters_; - std::unique_ptr noexcept_expr_; - cpp_function_body_kind body_; - bool variadic_; + std::unique_ptr function; }; - /// 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 + 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 parameters_; + std::unique_ptr 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 { public: - static cpp_entity_kind kind() noexcept; - - /// Builds a [cppast::cpp_function](). - class builder : public cpp_function_base::basic_builder + /// \effects Sets the name and return type. + builder(std::string name, std::unique_ptr return_type) { - public: - /// \effects Sets the name and return type. - builder(std::string name, std::unique_ptr return_type) - { - function = std::unique_ptr( - 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_; + function = std::unique_ptr( + new cpp_function(std::move(name), std::move(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 + /// \effects Sets the storage class. + void storage_class(cpp_storage_class_specifiers storage) { - return storage_; + function->storage_ = storage; } - /// \returns Whether the function is marked `constexpr`. - bool is_constexpr() const noexcept + /// \effects Marks the function as `constexpr`. + 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 ret) - : cpp_function_base(std::move(name)), - return_type_(std::move(ret)), - storage_(cpp_storage_class_auto), - constexpr_(false) - { - } - - std::unique_ptr 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 ret) + : cpp_function_base(std::move(name)), return_type_(std::move(ret)), + storage_(cpp_storage_class_auto), constexpr_(false) + {} + + std::unique_ptr return_type_; + cpp_storage_class_specifiers storage_; + bool constexpr_; +}; } // namespace cppast #endif // CPPAST_CPP_FUNCTION_HPP_INCLUDED diff --git a/include/cppast/cpp_function_template.hpp b/include/cppast/cpp_function_template.hpp index 1edb365..05d3e75 100644 --- a/include/cppast/cpp_function_template.hpp +++ b/include/cppast/cpp_function_template.hpp @@ -10,70 +10,68 @@ namespace cppast { - /// A [cppast::cpp_entity]() modelling a function template. - class cpp_function_template final : public cpp_template +/// A [cppast::cpp_entity]() modelling a function 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 { public: - static cpp_entity_kind kind() noexcept; - - /// Builder for [cppast::cpp_function_template](). - class builder : public basic_builder - { - 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(*begin()); - } - - private: - cpp_function_template(std::unique_ptr func) - : cpp_template(std::unique_ptr(func.release())) - { - } - - cpp_entity_kind do_get_entity_kind() const noexcept override; - - friend basic_builder; + using basic_builder::basic_builder; }; - /// A [cppast::cpp_entity]() modelling a function template specialization. - class cpp_function_template_specialization final : public cpp_template_specialization + /// A reference to the function that is being templated. + const cpp_function_base& function() const noexcept + { + return static_cast(*begin()); + } + +private: + cpp_function_template(std::unique_ptr func) + : cpp_template(std::unique_ptr(func.release())) + {} + + cpp_entity_kind do_get_entity_kind() const noexcept override; + + friend basic_builder; +}; + +/// 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 { public: - static cpp_entity_kind kind() noexcept; - - /// Builder for [cppast::cpp_function_template_specialization](). - class builder - : public specialization_builder - { - 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(*begin()); - } + using specialization_builder::specialization_builder; private: - cpp_function_template_specialization(std::unique_ptr func, - cpp_template_ref primary) - : cpp_template_specialization(std::unique_ptr(func.release()), primary) - { - } - - cpp_entity_kind do_get_entity_kind() const noexcept override; - - friend specialization_builder; + using specialization_builder::add_parameter; }; + + /// A reference to the function that is being specialized. + const cpp_function_base& function() const noexcept + { + return static_cast(*begin()); + } + +private: + cpp_function_template_specialization(std::unique_ptr func, + cpp_template_ref primary) + : cpp_template_specialization(std::unique_ptr(func.release()), primary) + {} + + cpp_entity_kind do_get_entity_kind() const noexcept override; + + friend specialization_builder; +}; } // namespace cppast #endif // CPPAST_CPP_FUNCTION_TEMPLATE_HPP_INCLUDED diff --git a/include/cppast/cpp_function_type.hpp b/include/cppast/cpp_function_type.hpp index 763d281..28acc40 100644 --- a/include/cppast/cpp_function_type.hpp +++ b/include/cppast/cpp_function_type.hpp @@ -9,199 +9,195 @@ namespace cppast { - /// A [cppast::cpp_type]() that is a function. - /// - /// A function pointer is created by wrapping it in [cppast::cpp_pointer_type](). - class cpp_function_type final : public cpp_type +/// A [cppast::cpp_type]() that is a function. +/// +/// A function pointer is created by wrapping it in [cppast::cpp_pointer_type](). +class cpp_function_type final : public cpp_type +{ +public: + /// Builds a [cppast::cpp_function_type](). + class builder { public: - /// Builds a [cppast::cpp_function_type](). - class builder + /// \effects Sets the return type. + explicit builder(std::unique_ptr return_type) + : func_(new cpp_function_type(std::move(return_type))) + {} + + /// \effects Adds an parameter type. + void add_parameter(std::unique_ptr arg) { - public: - /// \effects Sets the return type. - explicit builder(std::unique_ptr return_type) - : func_(new cpp_function_type(std::move(return_type))) - { - } - - /// \effects Adds an parameter type. - void add_parameter(std::unique_ptr 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 finish() - { - return std::move(func_); - } - - private: - std::unique_ptr func_; - }; - - /// \returns A reference to the return [cppast::cpp_type](). - const cpp_type& return_type() const noexcept - { - return *return_type_; + func_->parameters_.push_back(*func_, std::move(arg)); } - /// \returns An iteratable object iterating over the parameter types. - detail::iteratable_intrusive_list parameter_types() const noexcept + /// \effects Adds an ellipsis, marking it as variadic. + void is_variadic() { - return type_safe::ref(parameters_); + func_->variadic_ = true; } - /// \returns Whether or not the function is variadic (C-style ellipsis). - bool is_variadic() const noexcept + /// \returns The finished [cppast::cpp_function_type](). + std::unique_ptr finish() { - return variadic_; + return std::move(func_); } private: - cpp_function_type(std::unique_ptr 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 return_type_; - detail::intrusive_list parameters_; - bool variadic_; + std::unique_ptr func_; }; - /// 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 + /// \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. + detail::iteratable_intrusive_list 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 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 return_type_; + detail::intrusive_list 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: - /// Builds a [cppast::cpp_member_function_type](). - class builder + /// \effects Sets the class and return type. + builder(std::unique_ptr class_type, std::unique_ptr 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 arg) { - public: - /// \effects Sets the class and return type. - builder(std::unique_ptr class_type, std::unique_ptr 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 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 finish() - { - return std::move(func_); - } - - private: - std::unique_ptr func_; - }; - - /// \returns A reference to the class [cppast::cpp_type](). - const cpp_type& class_type() const noexcept - { - return *class_type_; + func_->parameters_.push_back(*func_, std::move(arg)); } - /// \returns A reference to the return [cppast::cpp_type](). - const cpp_type& return_type() const noexcept + /// \effects Adds an ellipsis, marking it as variadic. + void is_variadic() { - return *return_type_; + func_->variadic_ = true; } - /// \returns An iteratable object iterating over the parameter types. - detail::iteratable_intrusive_list parameter_types() const noexcept + /// \returns The finished [cppast::cpp_member_function_type](). + std::unique_ptr finish() { - return type_safe::ref(parameters_); - } - - /// \returns Whether or not the function is variadic (C-style ellipsis). - bool is_variadic() const noexcept - { - return variadic_; + return std::move(func_); } private: - cpp_member_function_type(std::unique_ptr class_type, - std::unique_ptr 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 class_type_, return_type_; - detail::intrusive_list parameters_; - bool variadic_; + std::unique_ptr func_; }; - /// 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 + /// \returns A reference to the class [cppast::cpp_type](). + const cpp_type& class_type() const noexcept { - public: - /// \returns A newly created member object type. - static std::unique_ptr build(std::unique_ptr class_type, - std::unique_ptr object_type) - { - return std::unique_ptr( - new cpp_member_object_type(std::move(class_type), std::move(object_type))); - } + return *class_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 return [cppast::cpp_type](). + const cpp_type& return_type() const noexcept + { + return *return_type_; + } - /// \returns A reference to the object [cppast::cpp_type](). - const cpp_type& object_type() const noexcept - { - return *object_type_; - } + /// \returns An iteratable object iterating over the parameter types. + detail::iteratable_intrusive_list parameter_types() const noexcept + { + return type_safe::ref(parameters_); + } - private: - cpp_member_object_type(std::unique_ptr class_type, - std::unique_ptr object_type) - : class_type_(std::move(class_type)), object_type_(std::move(object_type)) - { - } + /// \returns Whether or not the function is variadic (C-style ellipsis). + bool is_variadic() const noexcept + { + return variadic_; + } - cpp_type_kind do_get_kind() const noexcept override - { - return cpp_type_kind::member_object_t; - } +private: + cpp_member_function_type(std::unique_ptr class_type, + std::unique_ptr return_type) + : class_type_(std::move(class_type)), return_type_(std::move(return_type)), variadic_(false) + {} - std::unique_ptr class_type_, object_type_; - }; + cpp_type_kind do_get_kind() const noexcept override + { + return cpp_type_kind::member_function_t; + } + + std::unique_ptr class_type_, return_type_; + detail::intrusive_list 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 build(std::unique_ptr class_type, + std::unique_ptr object_type) + { + return std::unique_ptr( + 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 class_type, + std::unique_ptr 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 class_type_, object_type_; +}; } // namespace cppast #endif // CPPAST_CPP_FUNCTION_TYPE_HPP_INCLUDED diff --git a/include/cppast/cpp_language_linkage.hpp b/include/cppast/cpp_language_linkage.hpp index 1e1e837..66c9482 100644 --- a/include/cppast/cpp_language_linkage.hpp +++ b/include/cppast/cpp_language_linkage.hpp @@ -10,53 +10,51 @@ namespace cppast { - /// A [cppast::cpp_entity]() modelling a language linkage. - class cpp_language_linkage final : public cpp_entity, - public cpp_entity_container +/// A [cppast::cpp_entity]() modelling a language linkage. +class cpp_language_linkage final : public cpp_entity, + public cpp_entity_container +{ +public: + static cpp_entity_kind kind() noexcept; + + /// Builds a [cppast::cpp_language_linkage](). + class builder { 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](). - class builder + /// \effects Adds an entity to the language linkage. + void add_child(std::unique_ptr child) { - public: - /// \effects Sets the name, that is the kind of language linkage. - explicit builder(std::string name) : linkage_(new cpp_language_linkage(std::move(name))) - { - } + linkage_->add_child(std::move(child)); + } - /// \effects Adds an entity to the language linkage. - void add_child(std::unique_ptr child) - { - linkage_->add_child(std::move(child)); - } + /// \returns The not yet finished language linkage. + cpp_language_linkage& get() const noexcept + { + return *linkage_; + } - /// \returns The not yet finished language linkage. - cpp_language_linkage& get() const noexcept - { - return *linkage_; - } - - /// \returns The finalized language linkage. - /// \notes It is not registered on purpose as nothing can refer to it. - std::unique_ptr finish() - { - return std::move(linkage_); - } - - private: - std::unique_ptr linkage_; - }; - - /// \returns `true` if the linkage is a block, `false` otherwise. - bool is_block() const noexcept; + /// \returns The finalized language linkage. + /// \notes It is not registered on purpose as nothing can refer to it. + std::unique_ptr finish() + { + return std::move(linkage_); + } private: - using cpp_entity::cpp_entity; - - cpp_entity_kind do_get_entity_kind() const noexcept override; + std::unique_ptr linkage_; }; + + /// \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 #endif // CPPAST_CPP_LANGUAGE_LINKAGE_HPP_INCLUDED diff --git a/include/cppast/cpp_member_function.hpp b/include/cppast/cpp_member_function.hpp index 4b39255..d3c49c6 100644 --- a/include/cppast/cpp_member_function.hpp +++ b/include/cppast/cpp_member_function.hpp @@ -11,312 +11,305 @@ namespace cppast { - /// The `virtual`-ness of a member function. - /// - /// This is a [ts::flag_set]() `enum`. - /// \notes It does not specify whether a member function is `virtual` or not, - /// only the kind of `virtual`. - /// \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. - enum class cpp_virtual_flags +/// The `virtual`-ness of a member function. +/// +/// This is a [ts::flag_set]() `enum`. +/// \notes It does not specify whether a member function is `virtual` or not, +/// only the kind of `virtual`. +/// \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. +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>; + +/// \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(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(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(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. - 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>; - - /// \returns Whether or not a member function is `virtual`. - inline bool is_virtual(const cpp_virtual& virt) noexcept - { - return virt.has_value(); + return *return_type_; } - /// \returns Whether or not a member function is pure. - inline bool is_pure(const cpp_virtual& virt) noexcept + /// \returns Whether or not it is `virtual`. + bool is_virtual() const noexcept { - return static_cast(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. - inline bool is_overriding(const cpp_virtual& virt) noexcept + /// \returns The `virtual`-ness of the member function. + const cpp_virtual& virtual_info() const noexcept { - return static_cast(virt.value_or(cpp_virtual_flags::pure) - & cpp_virtual_flags::override); + return virtual_; } - /// \returns Whether or not a member function is `final`. - inline bool is_final(const cpp_virtual& virt) noexcept + /// \returns The cv-qualifier on the member function. + cpp_cv cv_qualifier() const noexcept { - return static_cast(virt.value_or(cpp_virtual_flags::pure) & cpp_virtual_flags::final); + return cv_; } - /// 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 + /// \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: + /// Builder class for member functions. + template + class basic_member_builder : public basic_builder { public: - /// \returns The return type of the member function. - const cpp_type& return_type() const noexcept + /// \effects Sets the name and return type. + basic_member_builder(std::string name, std::unique_ptr return_type) { - return *return_type_; + this->function = std::unique_ptr(new T(std::move(name), std::move(return_type))); } - /// \returns Whether or not it is `virtual`. - bool is_virtual() const noexcept + /// \effects Sets the cv- and ref-qualifier. + void cv_ref_qualifier(cpp_cv cv, cpp_reference ref) noexcept { - return virtual_info().has_value(); + auto& base = static_cast(*this->function); + base.cv_ = cv; + base.ref_ = ref; } - /// \returns The `virtual`-ness of the member function. - const cpp_virtual& virtual_info() const noexcept + /// \effects Sets the `virtual`-ness of the function. + void virtual_info(type_safe::flag_set virt) noexcept { - return virtual_; + static_cast(*this->function).virtual_ = virt; } - /// \returns The cv-qualifier on the member function. - cpp_cv cv_qualifier() const noexcept + /// \effects Marks the function as `constexpr`. + void is_constexpr() noexcept { - return cv_; - } - - /// \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_; + static_cast(*this->function).constexpr_ = true; } protected: - /// Builder class for member functions. - template - class basic_member_builder : public basic_builder - { - public: - /// \effects Sets the name and return type. - basic_member_builder(std::string name, std::unique_ptr return_type) - { - this->function = std::unique_ptr(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(*this->function); - base.cv_ = cv; - base.ref_ = ref; - } - - /// \effects Sets the `virtual`-ness of the function. - void virtual_info(type_safe::flag_set virt) noexcept - { - static_cast(*this->function).virtual_ = virt; - } - - /// \effects Marks the function as `constexpr`. - void is_constexpr() noexcept - { - static_cast(*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 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 return_type_; - cpp_virtual virtual_; - cpp_cv cv_; - cpp_reference ref_; - bool constexpr_; + basic_member_builder() noexcept = default; }; - /// A [cppast::cpp_entity]() modelling a member function. - class cpp_member_function final : public cpp_member_function_base + /// \effects Sets name and return type, as well as the rest to defaults. + cpp_member_function_base(std::string name, std::unique_ptr 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 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 { public: - static cpp_entity_kind kind() noexcept; - - /// Builder for [cppast::cpp_member_function](). - class builder : public cpp_member_function_base::basic_member_builder - { - 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; + using cpp_member_function_base::basic_member_builder< + cpp_member_function>::basic_member_builder; }; - /// A [cppast::cpp_entity]() modelling a C++ conversion operator. - class cpp_conversion_op final : public cpp_member_function_base +private: + using cpp_member_function_base::cpp_member_function_base; + + cpp_entity_kind do_get_entity_kind() const noexcept override; + + friend basic_member_builder; +}; + +/// 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 { public: - static cpp_entity_kind kind() noexcept; + using basic_member_builder::basic_member_builder; - /// Builder for [cppast::cpp_conversion_op](). - class builder : public basic_member_builder + /// \effects Marks the conversion operator `explicit`. + void is_explicit() noexcept { - public: - 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_; + function->explicit_ = true; } private: - cpp_conversion_op(std::string name, std::unique_ptr 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; + using basic_member_builder::add_parameter; + using basic_member_builder::is_variadic; }; - /// A [cppast::cpp_entity]() modelling a C++ constructor. - class cpp_constructor final : public cpp_function_base + /// \returns Whether or not the conversion is `explicit`. + bool is_explicit() const noexcept + { + return explicit_; + } + +private: + cpp_conversion_op(std::string name, std::unique_ptr 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; +}; + +/// 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 { public: - static cpp_entity_kind kind() noexcept; + using basic_builder::basic_builder; - /// Builder for [cppast::cpp_constructor](). - class builder : public basic_builder + /// \effects Marks the constructor `explicit`. + void is_explicit() noexcept { - public: - 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_; + function->explicit_ = true; } - /// \returns Whether or not the constructor is `constexpr`. - bool is_constexpr() const noexcept + /// \effects Marks the constructor `constexpr`. + 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; }; - /// A [cppast::cpp_entity]() modelling a C++ destructor. - class cpp_destructor final : public cpp_function_base + /// \returns Whether or not the constructor is `explicit`. + 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; +}; + +/// 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 { public: - static cpp_entity_kind kind() noexcept; + using basic_builder::basic_builder; - /// Builds a [cppast::cpp_destructor](). - class builder : public basic_builder + /// \effects Sets the `virtual`-ness of the destructor. + void virtual_info(cpp_virtual virt) noexcept { - public: - 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_; + function->virtual_ = virt; } 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; + 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: + 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; +}; } // namespace cppast #endif // CPPAST_CPP_MEMBER_FUNCTION_HPP_INCLUDED diff --git a/include/cppast/cpp_member_variable.hpp b/include/cppast/cpp_member_variable.hpp index 25d712b..b5020f4 100644 --- a/include/cppast/cpp_member_variable.hpp +++ b/include/cppast/cpp_member_variable.hpp @@ -10,84 +10,81 @@ namespace cppast { - /// Base class for all kinds of member variables. - class cpp_member_variable_base : public cpp_entity, public cpp_variable_base +/// Base class for all kinds of member variables. +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: - /// \returns Whether or not the member variable is declared `mutable`. - bool is_mutable() const noexcept - { - return mutable_; - } + return mutable_; + } - protected: - cpp_member_variable_base(std::string name, std::unique_ptr type, - std::unique_ptr def, bool is_mutable) - : cpp_entity(std::move(name)), - cpp_variable_base(std::move(type), std::move(def)), - mutable_(is_mutable) - { - } +protected: + cpp_member_variable_base(std::string name, std::unique_ptr type, + std::unique_ptr def, bool is_mutable) + : cpp_entity(std::move(name)), cpp_variable_base(std::move(type), std::move(def)), + mutable_(is_mutable) + {} - private: - bool mutable_; - }; +private: + bool mutable_; +}; - /// A [cppast::cpp_entity]() modelling a C++ member variable. - class cpp_member_variable final : public cpp_member_variable_base +/// A [cppast::cpp_entity]() modelling a C++ member variable. +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 build(const cpp_entity_index& idx, cpp_entity_id id, + std::string name, + std::unique_ptr type, + std::unique_ptr 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 build(const cpp_entity_index& idx, cpp_entity_id id, + std::string name, std::unique_ptr 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 build(std::unique_ptr type, unsigned no_bits, + bool is_mutable); + + /// \returns The number of bits of the bitfield. + unsigned no_bits() const noexcept { - public: - static cpp_entity_kind kind() noexcept; + return bits_; + } - /// \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 build(const cpp_entity_index& idx, - cpp_entity_id id, std::string name, - std::unique_ptr type, - std::unique_ptr def, - bool is_mutable); +private: + cpp_bitfield(std::string name, std::unique_ptr type, unsigned no_bits, + bool is_mutable) + : cpp_member_variable_base(std::move(name), std::move(type), nullptr, is_mutable), + bits_(no_bits) + {} - private: - using cpp_member_variable_base::cpp_member_variable_base; + cpp_entity_kind do_get_entity_kind() const noexcept override; - 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 build(const cpp_entity_index& idx, cpp_entity_id id, - std::string name, std::unique_ptr 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 build(std::unique_ptr 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 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_; - }; + unsigned bits_; +}; } // namespace cppast #endif // CPPAST_CPP_MEMBER_VARIABLE_HPP_INCLUDED diff --git a/include/cppast/cpp_namespace.hpp b/include/cppast/cpp_namespace.hpp index 0c8dca0..120c276 100644 --- a/include/cppast/cpp_namespace.hpp +++ b/include/cppast/cpp_namespace.hpp @@ -7,196 +7,188 @@ #include #include -#include #include +#include namespace cppast { - /// A [cppast::cpp_entity]() modelling a namespace. - class cpp_namespace final : public cpp_entity, - public cpp_entity_container +/// A [cppast::cpp_entity]() modelling a namespace. +class cpp_namespace final : public cpp_entity, + public cpp_entity_container +{ +public: + static cpp_entity_kind kind() noexcept; + + /// Builds a [cppast::cpp_namespace](). + class builder { 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](). - class builder + /// \effects Adds an entity. + void add_child(std::unique_ptr child) noexcept { - public: - /// \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 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 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 namespace_; - }; - - /// \returns Whether or not the namespace is an `inline namespace`. - bool is_inline() const noexcept - { - return inline_; + namespace_->add_child(std::move(child)); } - /// \returns Whether or not the namespace is part of a C++17 nested namespace. - bool is_nested() const noexcept + /// \returns The not yet finished namespace. + cpp_namespace& get() const noexcept { - return nested_; + return *namespace_; } - /// \returns Whether or not the namespace is anonymous. - bool is_anonymous() const noexcept + /// \effects Registers the namespace in the [cppast::cpp_entity_index](), + /// using the given [cppast::cpp_entity_id](). + /// \returns The finished namespace. + std::unique_ptr 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: - cpp_namespace(std::string name, bool is_inline, bool is_nested) - : cpp_entity(std::move(name)), inline_(is_inline), nested_(is_nested) - { - } - - cpp_entity_kind do_get_entity_kind() const noexcept override; - - type_safe::optional do_get_scope_name() const override - { - return type_safe::ref(*this); - } - - bool inline_; - bool nested_; + std::unique_ptr namespace_; }; - /// \exclude - namespace detail + /// \returns Whether or not the namespace is an `inline namespace`. + bool is_inline() const noexcept { - struct cpp_namespace_ref_predicate - { - bool operator()(const cpp_entity& e); - }; - } // namespace detail + return inline_; + } - /// A reference to a [cppast::cpp_namespace](). - using cpp_namespace_ref = - basic_cpp_entity_ref; - - /// A [cppast::cpp_entity]() modelling a namespace alias. - class cpp_namespace_alias final : public cpp_entity + /// \returns Whether or not the namespace is part of a C++17 nested namespace. + bool is_nested() const noexcept { - public: - static cpp_entity_kind kind() noexcept; + return nested_; + } - /// \returns A newly created and registered namespace alias. - static std::unique_ptr build(const cpp_entity_index& idx, - cpp_entity_id id, std::string name, - cpp_namespace_ref target); + /// \returns Whether or not the namespace is anonymous. + bool is_anonymous() const noexcept + { + return name().empty(); + } - /// \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 - { - return target_; - } +private: + cpp_namespace(std::string name, bool is_inline, bool is_nested) + : cpp_entity(std::move(name)), inline_(is_inline), nested_(is_nested) + {} - private: - 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; - cpp_entity_kind do_get_entity_kind() const noexcept override; + type_safe::optional 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 using directive is `using namespace std`, for example. - /// \notes It does not have a name. - class cpp_using_directive final : public cpp_entity +/// A reference to a [cppast::cpp_namespace](). +using cpp_namespace_ref = basic_cpp_entity_ref; + +/// A [cppast::cpp_entity]() modelling a namespace alias. +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 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: - static cpp_entity_kind kind() noexcept; + return 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. - static std::unique_ptr build(cpp_namespace_ref target) - { - return std::unique_ptr(new cpp_using_directive(std::move(target))); - } +private: + cpp_namespace_alias(std::string name, cpp_namespace_ref target) + : cpp_entity(std::move(name)), target_(std::move(target)) + {} - /// \returns The [cppast::cpp_namespace_ref]() that is being used. - const cpp_namespace_ref& target() const - { - return target_; - } + cpp_entity_kind do_get_entity_kind() const noexcept override; - private: - cpp_using_directive(cpp_namespace_ref target) : cpp_entity(""), target_(std::move(target)) - { - } + cpp_namespace_ref 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_; - }; - - /// 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 + /// \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. + static std::unique_ptr build(cpp_namespace_ref target) { - public: - static cpp_entity_kind kind() noexcept; + return std::unique_ptr(new cpp_using_directive(std::move(target))); + } - /// \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 build(cpp_entity_ref target) - { - return std::unique_ptr( - new cpp_using_declaration(std::move(target))); - } + /// \returns The [cppast::cpp_namespace_ref]() that is being used. + const cpp_namespace_ref& target() const + { + return 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_directive(cpp_namespace_ref target) : cpp_entity(""), target_(std::move(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_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 build(cpp_entity_ref target) + { + return std::unique_ptr(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 #endif // CPPAST_CPP_NAMESPACE_HPP_INCLUDED diff --git a/include/cppast/cpp_preprocessor.hpp b/include/cppast/cpp_preprocessor.hpp index 8537732..e18034c 100644 --- a/include/cppast/cpp_preprocessor.hpp +++ b/include/cppast/cpp_preprocessor.hpp @@ -12,197 +12,194 @@ namespace cppast { - /// A [cppast::cpp_entity]() modelling a macro parameter. - class cpp_macro_parameter final : public cpp_entity +/// A [cppast::cpp_entity]() modelling a macro parameter. +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 build(std::string name) + { + return std::unique_ptr(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 build_object_like(std::string name, + std::string replacement) + { + std::unique_ptr 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: - 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 build(std::string name) + /// \effects Sets the name of the function like macro. + function_like_builder(std::string name) : result_(new cpp_macro_definition(std::move(name))) { - return std::unique_ptr(new cpp_macro_parameter(std::move(name))); + result_->kind_ = function_like; } - private: - cpp_macro_parameter(std::string name) : cpp_entity(std::move(name)) {} + /// \effects Sets the replacement text. + 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. - class cpp_macro_definition final : public cpp_entity - { - public: - static cpp_entity_kind kind() noexcept; + /// \effects Adds a parameter. + /// \group param + void parameter(std::unique_ptr 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 A newly built object like macro. + /// \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. - static std::unique_ptr build_object_like(std::string name, - std::string replacement) + std::unique_ptr finish() { - std::unique_ptr 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: - /// \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 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 finish() - { - return std::move(result_); - } - - private: - std::unique_ptr 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 parameters() const noexcept - { - return type_safe::ref(parameters_); + return std::move(result_); } private: - cpp_entity_kind do_get_entity_kind() const noexcept override; - - cpp_macro_definition(std::string name) : cpp_entity(std::move(name)), kind_(object_like) {} - - detail::intrusive_list parameters_; - std::string replacement_; - - enum : char - { - object_like, - function_like, - variadic_function, - } kind_; - - friend function_like_builder; + std::unique_ptr result_; }; - /// The kind of [cppast::cpp_include_directive](). - enum class cpp_include_kind + /// \returns The replacement text of the macro. + const std::string& replacement() const noexcept { - system, //< An `#include <...>`. - local, //< An `#include "..."`. - }; + return replacement_; + } - /// A [cppast::cpp_entity]() modelling an `#include`. - class cpp_include_directive final : public cpp_entity + /// \returns Whether or not it is an object like macro. + bool is_object_like() const noexcept { - public: - static cpp_entity_kind kind() noexcept; + return kind_ == object_like; + } - /// \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 build(const cpp_file_ref& target, - cpp_include_kind kind, - std::string full_path) - { - return std::unique_ptr( - new cpp_include_directive(target, kind, std::move(full_path))); - } + /// \returns Whether or not it is a function like macro. + bool is_function_like() const noexcept + { + return kind_ != object_like; + } - /// \returns A reference to the [cppast::cpp_file]() it includes. - cpp_file_ref target() const noexcept - { - return cpp_file_ref(target_, name()); - } + /// \returns Whether or not it is a variadic macro. + bool is_variadic() const noexcept + { + return kind_ == variadic_function; + } - /// \returns The kind of include it is. - cpp_include_kind include_kind() const noexcept - { - return kind_; - } + /// \returns The parameters of the macro. + /// \notes It has none if it is not a function like macro. + detail::iteratable_intrusive_list parameters() const noexcept + { + return type_safe::ref(parameters_); + } - /// \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; - private: - cpp_entity_kind do_get_entity_kind() const noexcept override; + cpp_macro_definition(std::string name) : cpp_entity(std::move(name)), kind_(object_like) {} - 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{}); - } + detail::intrusive_list parameters_; + std::string replacement_; - cpp_entity_id target_; - cpp_include_kind kind_; - std::string full_path_; - }; + enum : char + { + 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 build(const cpp_file_ref& target, + cpp_include_kind kind, + std::string full_path) + { + return std::unique_ptr( + 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 #endif // CPPAST_CPP_PREPROCESSOR_HPP_INCLUDED diff --git a/include/cppast/cpp_static_assert.hpp b/include/cppast/cpp_static_assert.hpp index 0e9afcf..5f338f4 100644 --- a/include/cppast/cpp_static_assert.hpp +++ b/include/cppast/cpp_static_assert.hpp @@ -10,43 +10,42 @@ 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 build(std::unique_ptr expr, + std::string msg) { - public: - static cpp_entity_kind kind() noexcept; + return std::unique_ptr( + new cpp_static_assert(std::move(expr), std::move(msg))); + } - /// \returns A newly created `static_assert()` entity. - /// \notes It will not be registered as nothing can refer to it. - static std::unique_ptr build(std::unique_ptr expr, - std::string msg) - { - return std::unique_ptr( - new cpp_static_assert(std::move(expr), std::move(msg))); - } + /// \returns A reference to the [cppast::cpp_expression]() that is being asserted. + const cpp_expression& expression() const noexcept + { + return *expr_; + } - /// \returns A reference to the [cppast::cpp_expression]() that is being asserted. - const cpp_expression& expression() const noexcept - { - return *expr_; - } + /// \returns A reference to the message of the assertion. + const std::string& message() const noexcept + { + return msg_; + } - /// \returns A reference to the message of the assertion. - const std::string& message() const noexcept - { - return msg_; - } +private: + cpp_static_assert(std::unique_ptr expr, std::string msg) + : cpp_entity(""), expr_(std::move(expr)), msg_(std::move(msg)) + {} - private: - cpp_static_assert(std::unique_ptr expr, std::string msg) - : cpp_entity(""), expr_(std::move(expr)), msg_(std::move(msg)) - { - } + cpp_entity_kind do_get_entity_kind() const noexcept override; - cpp_entity_kind do_get_entity_kind() const noexcept override; - - std::unique_ptr expr_; - std::string msg_; - }; + std::unique_ptr expr_; + std::string msg_; +}; } // namespace cppast #endif // CPPAST_CPP_STATIC_ASSERT_HPP_INCLUDED diff --git a/include/cppast/cpp_storage_class_specifiers.hpp b/include/cppast/cpp_storage_class_specifiers.hpp index 040bd84..e7df0bc 100644 --- a/include/cppast/cpp_storage_class_specifiers.hpp +++ b/include/cppast/cpp_storage_class_specifiers.hpp @@ -7,49 +7,47 @@ namespace cppast { - /// C++ storage class specifiers. - /// - /// 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, - /// not necessarily their *semantic* meaning. - enum cpp_storage_class_specifiers : int - { - cpp_storage_class_none = 0, //< no storage class specifier given. +/// C++ storage class specifiers. +/// +/// 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, +/// not necessarily their *semantic* meaning. +enum cpp_storage_class_specifiers : int +{ + 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 = - 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_static = 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_thread_local = 8, //< *thread* storage duration. - /// \notes This is the only one that can be combined with the others. - }; + cpp_storage_class_thread_local = 8, //< *thread* storage duration. + /// \notes This is the only one that can be combined with the others. +}; - /// \returns Whether the [cppast::cpp_storage_class_specifiers]() contain `thread_local`. - inline bool is_thread_local(cpp_storage_class_specifiers spec) noexcept - { - return (spec & cpp_storage_class_thread_local) != 0; - } +/// \returns Whether the [cppast::cpp_storage_class_specifiers]() contain `thread_local`. +inline bool is_thread_local(cpp_storage_class_specifiers spec) noexcept +{ + return (spec & cpp_storage_class_thread_local) != 0; +} - /// \returns Whether the [cppast::cpp_storage_class_speicifers]() contain `auto`. - inline bool is_auto(cpp_storage_class_specifiers spec) noexcept - { - return (spec & cpp_storage_class_auto) != 0; - } +/// \returns Whether the [cppast::cpp_storage_class_speicifers]() contain `auto`. +inline bool is_auto(cpp_storage_class_specifiers spec) noexcept +{ + return (spec & cpp_storage_class_auto) != 0; +} - /// \returns Whether the [cppast::cpp_storage_class_specifiers]() contain `static`. - inline bool is_static(cpp_storage_class_specifiers spec) noexcept - { - return (spec & cpp_storage_class_static) != 0; - } +/// \returns Whether the [cppast::cpp_storage_class_specifiers]() contain `static`. +inline bool is_static(cpp_storage_class_specifiers spec) noexcept +{ + return (spec & cpp_storage_class_static) != 0; +} - /// \returns Whether the [cppast::cpp_storage_class_specifiers]() contain `extern`. - inline bool is_extern(cpp_storage_class_specifiers spec) noexcept - { - return (spec & cpp_storage_class_extern) != 0; - } +/// \returns Whether the [cppast::cpp_storage_class_specifiers]() contain `extern`. +inline bool is_extern(cpp_storage_class_specifiers spec) noexcept +{ + return (spec & cpp_storage_class_extern) != 0; +} } // namespace cppast #endif // CPPAST_CPP_STORAGE_CLASS_SPECIFIERS_HPP_INCLUDED diff --git a/include/cppast/cpp_template.hpp b/include/cppast/cpp_template.hpp index da430db..c1cc5f4 100644 --- a/include/cppast/cpp_template.hpp +++ b/include/cppast/cpp_template.hpp @@ -11,275 +11,263 @@ #include #include -#include #include +#include 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 +{ +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 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. - class cpp_template : public cpp_entity, public cpp_entity_container + /// Inherit from it to provide additional setter. + template + class basic_builder { 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 parameters() const noexcept + /// \effects Sets the entity that is begin templated. + basic_builder(std::unique_ptr templ) : template_entity(new T(std::move(templ))) {} + + basic_builder(basic_builder&&) = default; + + /// \effects Adds a parameter. + void add_parameter(std::unique_ptr parameter) { - return type_safe::ref(parameters_); + static_cast(*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 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: - /// Builder class for templates. - /// - /// Inherit from it to provide additional setter. - template - class basic_builder + basic_builder() = default; + ~basic_builder() noexcept = default; + + std::unique_ptr template_entity; + }; + + /// \effects Sets the entity to be templated. + cpp_template(std::unique_ptr entity) : cpp_entity(entity->name()) + { + add_child(std::move(entity)); + } + +private: + type_safe::optional 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 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: - /// \effects Sets the entity that is begin templated. - basic_builder(std::unique_ptr templ) : template_entity(new T(std::move(templ))) - { - } + result_->arguments_.value(type_safe::variant_type>{}) + .push_back(std::move(arg)); + } - basic_builder(basic_builder&&) = default; - - /// \effects Adds a parameter. - void add_parameter(std::unique_ptr parameter) - { - static_cast(*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 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 template_entity; - }; - - /// \effects Sets the entity to be templated. - cpp_template(std::unique_ptr entity) : cpp_entity(entity->name()) + /// \effects Adds unexposed arguments as string. + void add_unexposed_arguments(std::string arg) { - add_child(std::move(entity)); + result_->arguments_ = std::move(arg); + } + + /// \returns The finished instantiation. + std::unique_ptr finish() + { + return std::move(result_); } private: - type_safe::optional 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 parameters_; + std::unique_ptr result_; }; - /// A [cppast::cpp_type]() representing an instantiation of a [cppast::cpp_template](). - class cpp_template_instantiation_type final : public cpp_type + /// \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>{}); + } + + /// \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> arguments() const + noexcept + { + auto& vec = arguments_.value(type_safe::variant_type>{}); + 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{}); + } + +private: + cpp_template_instantiation_type(cpp_template_ref ref) + : arguments_(type_safe::variant_type>{}), + templ_(std::move(ref)) + {} + + cpp_type_kind do_get_kind() const noexcept override + { + return cpp_type_kind::template_instantiation_t; + } + + type_safe::variant, 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>{}); + } + + /// \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 arguments() const noexcept + { + auto& vec = arguments_.value(type_safe::variant_type>{}); + 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{}); + } + + /// \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 specialization_builder : public basic_builder { 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>{}) - .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 finish() - { - return std::move(result_); - } - - private: - std::unique_ptr 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>{}); - } - - /// \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> arguments() const - noexcept - { - auto& vec = - arguments_.value(type_safe::variant_type>{}); - 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{}); - } - - private: - cpp_template_instantiation_type(cpp_template_ref ref) - : arguments_(type_safe::variant_type>{}), - templ_(std::move(ref)) - { - } - - cpp_type_kind do_get_kind() const noexcept override - { - return cpp_type_kind::template_instantiation_t; - } - - type_safe::variant, 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>{}); - } - - /// \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 arguments() const noexcept - { - auto& vec = - arguments_.value(type_safe::variant_type>{}); - 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{}); - } - - /// \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 specialization_builder : public basic_builder - { - public: - /// \effects Sets the entity that is being templated and the primary template. - specialization_builder(std::unique_ptr entity, const cpp_template_ref& templ) - { - this->template_entity = std::unique_ptr(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(*this->template_entity); - specialization.arguments_ - .value(type_safe::variant_type>{}) - .push_back(std::move(arg)); - } - - /// \effects Adds unexposed arguments as string. - void add_unexposed_arguments(cpp_token_string arg) - { - auto& specialization = - static_cast(*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 entity, - const cpp_template_ref& templ) - : cpp_template(std::move(entity)), - arguments_(type_safe::variant_type>{}), - templ_(templ.id()[0u]) + specialization_builder(std::unique_ptr entity, const cpp_template_ref& templ) { - DEBUG_ASSERT(!templ.is_overloaded() - && (templ.name().empty() || templ.name() == begin()->name()), - detail::precondition_error_handler{}, "invalid name of template ref"); + this->template_entity = std::unique_ptr(new T(std::move(entity), templ)); } - private: - type_safe::variant, cpp_token_string> arguments_; - cpp_entity_id 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(*this->template_entity); + specialization.arguments_ + .value(type_safe::variant_type>{}) + .push_back(std::move(arg)); + } + + /// \effects Adds unexposed arguments as string. + void add_unexposed_arguments(cpp_token_string arg) + { + auto& specialization + = static_cast(*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 entity, const cpp_template_ref& templ) + : cpp_template(std::move(entity)), + arguments_(type_safe::variant_type>{}), + 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, cpp_token_string> arguments_; + cpp_entity_id templ_; +}; } // namespace cppast #endif // CPPAST_CPP_TEMPLATE_HPP_INCLUDED diff --git a/include/cppast/cpp_template_parameter.hpp b/include/cppast/cpp_template_parameter.hpp index c489bf1..1012c20 100644 --- a/include/cppast/cpp_template_parameter.hpp +++ b/include/cppast/cpp_template_parameter.hpp @@ -8,308 +8,295 @@ #include #include -#include #include #include +#include namespace cppast { - /// Base class for all entities modelling a template parameter of some kind. - class cpp_template_parameter : public cpp_entity +/// Base class for all entities modelling a template parameter of some kind. +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 build( + const cpp_entity_index& idx, cpp_entity_id id, std::string name, cpp_template_keyword kw, + bool variadic, std::unique_ptr default_type = nullptr); + + /// \returns A [ts::optional_ref]() to the default type. + type_safe::optional_ref 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 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 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; + +/// 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 build( + cpp_template_type_parameter_ref parameter) + { + return std::unique_ptr( + 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 build( + const cpp_entity_index& idx, cpp_entity_id id, std::string name, + std::unique_ptr type, bool is_variadic, + std::unique_ptr default_value = nullptr); + +private: + cpp_non_type_template_parameter(std::string name, std::unique_ptr type, bool variadic, + std::unique_ptr 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; + +/// 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: - /// \returns Whether or not the parameter is variadic. - bool is_variadic() const noexcept + /// \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) { - return variadic_; + parameter_->keyword_ = kw; } - protected: - cpp_template_parameter(std::string name, bool variadic) - : cpp_entity(std::move(name)), variadic_(variadic) + /// \effects Adds a parameter to the template. + void add_parameter(std::unique_ptr 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 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: - bool variadic_; + std::unique_ptr parameter_; }; - /// The kind of keyword used in a template parameter. - enum class cpp_template_keyword + /// \returns An iteratable object containing the template parameters of the template template + /// parameter. + detail::iteratable_intrusive_list parameters() const noexcept { - keyword_class, - keyword_typename - }; + return type_safe::ref(parameters_); + } - /// \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 + /// \returns The keyword used in the template parameter. + cpp_template_keyword keyword() const noexcept { - public: - static cpp_entity_kind kind() noexcept; + return keyword_; + } - /// \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 build( - const cpp_entity_index& idx, cpp_entity_id id, std::string name, - cpp_template_keyword kw, bool variadic, - std::unique_ptr default_type = nullptr); - - /// \returns A [ts::optional_ref]() to the default type. - type_safe::optional_ref 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 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 default_type_; - cpp_template_keyword keyword_; - }; - - /// \exclude - namespace detail + /// \returns A [ts::optional]() that is the default template. + type_safe::optional default_template() const noexcept { - struct cpp_template_parameter_ref_predicate - { - bool operator()(const cpp_entity& e); - }; - } // namespace detail + return default_; + } - /// Reference to a [cppast::cpp_template_type_parameter](). - using cpp_template_type_parameter_ref = - basic_cpp_entity_ref; +private: + cpp_template_template_parameter(std::string name, bool variadic) + : cpp_template_parameter(std::move(name), variadic), + keyword_(cpp_template_keyword::keyword_class) + {} - /// A [cppast::cpp_type]() defined by a [cppast::cpp_template_type_parameter](). - class cpp_template_parameter_type final : public cpp_type + cpp_entity_kind do_get_entity_kind() const noexcept override; + + detail::intrusive_list parameters_; + type_safe::optional 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 ::value, int>::type = 0> + cpp_template_argument(std::unique_ptr type) + : arg_(std::unique_ptr(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 ::value>::type> + cpp_template_argument(std::unique_ptr expr) + : arg_(std::unique_ptr(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 type() const noexcept { - public: - /// \returns A newly created parameter type. - static std::unique_ptr build( - cpp_template_type_parameter_ref parameter) - { - return std::unique_ptr( - new cpp_template_parameter_type(std::move(parameter))); - } + return arg_.optional_value(type_safe::variant_type>{}) + .map([](const std::unique_ptr& type) { return type_safe::ref(*type); }); + } - /// \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 + type_safe::optional_ref expression() const noexcept { - public: - static cpp_entity_kind kind() noexcept; + return arg_.optional_value(type_safe::variant_type>{}) + .map([](const std::unique_ptr& expr) { return type_safe::ref(*expr); }); + } - /// \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 build( - const cpp_entity_index& idx, cpp_entity_id id, std::string name, - std::unique_ptr type, bool is_variadic, - std::unique_ptr default_value = nullptr); - - private: - cpp_non_type_template_parameter(std::string name, std::unique_ptr type, - bool variadic, std::unique_ptr 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 + type_safe::optional_ref template_ref() const noexcept { - struct cpp_template_ref_predicate - { - bool operator()(const cpp_entity& e); - }; - } // namespace detail + return arg_.optional_value(type_safe::variant_type{}); + } - 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; - - /// 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 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 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 parameter_; - }; - - /// \returns An iteratable object containing the template parameters of the template template parameter. - detail::iteratable_intrusive_list 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 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 parameters_; - type_safe::optional 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 ::value, int>::type = 0> - cpp_template_argument(std::unique_ptr type) - : arg_(std::unique_ptr(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 ::value>::type> - cpp_template_argument(std::unique_ptr expr) - : arg_(std::unique_ptr(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 type() const noexcept - { - return arg_.optional_value(type_safe::variant_type>{}) - .map([](const std::unique_ptr& type) { return type_safe::ref(*type); }); - } - - type_safe::optional_ref expression() const noexcept - { - return arg_.optional_value(type_safe::variant_type>{}) - .map([](const std::unique_ptr& expr) { - return type_safe::ref(*expr); - }); - } - - type_safe::optional_ref template_ref() const noexcept - { - return arg_.optional_value(type_safe::variant_type{}); - } - - private: - type_safe::variant, std::unique_ptr, - cpp_template_ref> - arg_; - }; +private: + type_safe::variant, std::unique_ptr, cpp_template_ref> + arg_; +}; } // namespace cppast #endif // CPPAST_CPP_TEMPLATE_PARAMETER_HPP_INCLUDED diff --git a/include/cppast/cpp_token.hpp b/include/cppast/cpp_token.hpp index af90534..7b9f517 100644 --- a/include/cppast/cpp_token.hpp +++ b/include/cppast/cpp_token.hpp @@ -12,126 +12,124 @@ namespace cppast { - /// The kinds of C++ tokens. - enum class cpp_token_kind +/// The kinds of C++ tokens. +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. - 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. - }; + return lhs.spelling == rhs.spelling; + } - /// A C++ token. - struct cpp_token + friend bool operator!=(const cpp_token& lhs, const cpp_token& rhs) noexcept { - std::string spelling; - cpp_token_kind kind; + return !(rhs == lhs); + } +}; - 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 - { - 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 +/// A combination of multiple C++ tokens. +class cpp_token_string +{ +public: + /// Builds a token string. + class builder { public: - /// Builds a token string. - class builder + builder() = default; + + /// \effects Adds a token. + void add_token(cpp_token tok) { - public: - 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 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 tokens) : tokens_(std::move(tokens)) {} - - /// \exclude target - using iterator = std::vector::const_iterator; - - /// \returns An iterator to the first token. - iterator begin() const noexcept - { - return tokens_.begin(); + tokens_.push_back(std::move(tok)); } - /// \returns An iterator one past the last token. - iterator end() const noexcept - { - return tokens_.end(); - } + /// \effects Converts a trailing `>>` to `>` token. + void unmunch(); - /// \returns Whether or not the string is empty. - bool empty() const noexcept + /// \returns The finished string. + 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: std::vector 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 tokens) : tokens_(std::move(tokens)) {} + + /// \exclude target + using iterator = std::vector::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 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 #endif // CPPAST_CPP_TOKEN_HPP_INCLUDED diff --git a/include/cppast/cpp_type.hpp b/include/cppast/cpp_type.hpp index f822536..2e40510 100644 --- a/include/cppast/cpp_type.hpp +++ b/include/cppast/cpp_type.hpp @@ -8,455 +8,452 @@ #include #include -#include -#include #include +#include +#include namespace cppast { - /// The kinds of a [cppast::cpp_type](). - enum class cpp_type_kind +/// The kinds of a [cppast::cpp_type](). +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 +{ +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, - 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 - { - 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 user_data_; - - template - friend struct detail::intrusive_list_access; - friend detail::intrusive_list_node; - }; - - /// 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 build(std::string name) - { - return std::unique_ptr(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 build(cpp_builtin_type_kind kind) - { - return std::unique_ptr(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; - - /// 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 build(cpp_type_ref entity) - { - return std::unique_ptr( - 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 build() - { - return std::unique_ptr(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 build( - std::string name, std::unique_ptr dependee); - - /// \returns A newly created type dependent on a [cppast::cpp_template_instantiation_type](). - static std::unique_ptr build( - std::string name, std::unique_ptr 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 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 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; + return do_get_kind(); } - /// \returns `true` if the qualifier contains `volatile`. - inline bool is_volatile(cpp_cv cv) noexcept + /// \returns The specified user data. + 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](). - class cpp_cv_qualified_type final : public cpp_type + /// \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 { - public: - /// \returns A newly created qualified type. - /// \requires `cv` must not be [cppast::cpp_cv::cpp_cv_none](). - static std::unique_ptr build(std::unique_ptr type, - cpp_cv cv) - { - DEBUG_ASSERT(cv != cpp_cv_none, detail::precondition_error_handler{}); - return std::unique_ptr( - new cpp_cv_qualified_type(std::move(type), cv)); - } + user_data_ = data; + } - /// \returns A reference to the [cppast::cpp_type]() that is qualified. - const cpp_type& type() const noexcept - { - return *type_; - } +protected: + cpp_type() noexcept : user_data_(nullptr) {} - /// \returns The [cppast::cpp_cv]() qualifier. - cpp_cv cv_qualifier() const noexcept - { - return cv_; - } +private: + /// \returns The [cppast::cpp_type_kind](). + virtual cpp_type_kind do_get_kind() const noexcept = 0; - private: - cpp_cv_qualified_type(std::unique_ptr type, cpp_cv cv) - : type_(std::move(type)), cv_(cv) - { - } + void on_insert(const cpp_type&) {} - cpp_type_kind do_get_kind() const noexcept override - { - return cpp_type_kind::cv_qualified_t; - } + mutable std::atomic user_data_; - std::unique_ptr type_; - cpp_cv cv_; + template + friend struct detail::intrusive_list_access; + friend detail::intrusive_list_node; +}; + +/// 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 build(std::string name) + { + return std::unique_ptr(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 build(cpp_builtin_type_kind kind) + { + return std::unique_ptr(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. - const cpp_type& remove_cv(const cpp_type& type) noexcept; +/// Reference to a [cppast::cpp_entity]() representing a new type. +using cpp_type_ref = basic_cpp_entity_ref; - /// \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 +/// 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 build(cpp_type_ref entity) { - public: - /// \returns A newly created pointer type. - static std::unique_ptr build(std::unique_ptr pointee) - { - return std::unique_ptr(new cpp_pointer_type(std::move(pointee))); - } + return std::unique_ptr(new cpp_user_defined_type(std::move(entity))); + } - /// \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 pointee) : pointee_(std::move(pointee)) {} - - cpp_type_kind do_get_kind() const noexcept override - { - return cpp_type_kind::pointer_t; - } - - std::unique_ptr pointee_; - }; - - /// The kinds of C++ references. - enum cpp_reference + /// \returns A [cppast::cpp_type_ref]() to the associated [cppast::cpp_entity]() that is the + /// type. + const cpp_type_ref& entity() const noexcept { - cpp_ref_none, - cpp_ref_lvalue, - cpp_ref_rvalue, - }; + return entity_; + } - /// A reference to a [cppast::cpp_type](). - class cpp_reference_type final : public cpp_type +private: + cpp_user_defined_type(cpp_type_ref entity) : entity_(std::move(entity)) {} + + cpp_type_kind do_get_kind() const noexcept override { - public: - /// \returns A newly created qualified type. - /// \requires `ref` must not be [cppast::cpp_reference::cpp_ref_none](). - static std::unique_ptr build(std::unique_ptr type, - cpp_reference ref) - { - DEBUG_ASSERT(ref != cpp_ref_none, detail::precondition_error_handler{}); - return std::unique_ptr( - new cpp_reference_type(std::move(type), ref)); - } + return cpp_type_kind::user_defined_t; + } - /// \returns A reference to the [cppast::cpp_type]() that is referenced. - const cpp_type& referee() const noexcept - { - return *referee_; - } + cpp_type_ref entity_; +}; - /// \returns The [cppast::cpp_reference]() type. - cpp_reference reference_kind() const noexcept - { - return ref_; - } - - private: - cpp_reference_type(std::unique_ptr 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 referee_; - cpp_reference ref_; - }; - - /// \returns The type as a string representation. - std::string to_string(const cpp_type& type); - - /// \exclude - namespace detail +/// 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 build() { - // 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; + return std::unique_ptr(new cpp_auto_type); + } - // write part of the type that comes before the variable name - void write_type_prefix(code_generator::output& output, const cpp_type& type); +private: + cpp_auto_type() = default; - // 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); + cpp_type_kind do_get_kind() const noexcept override + { + return cpp_type_kind::auto_t; + } +}; - // 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 +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 build( + std::string name, std::unique_ptr dependee); + + /// \returns A newly created type dependent on a [cppast::cpp_template_instantiation_type](). + static std::unique_ptr build( + std::string name, std::unique_ptr 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 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 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 build(std::unique_ptr type, cpp_cv cv) + { + DEBUG_ASSERT(cv != cpp_cv_none, detail::precondition_error_handler{}); + return std::unique_ptr( + 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 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 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 build(std::unique_ptr pointee) + { + return std::unique_ptr(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 pointee) : pointee_(std::move(pointee)) {} + + cpp_type_kind do_get_kind() const noexcept override + { + return cpp_type_kind::pointer_t; + } + + std::unique_ptr 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 build(std::unique_ptr type, + cpp_reference ref) + { + DEBUG_ASSERT(ref != cpp_ref_none, detail::precondition_error_handler{}); + return std::unique_ptr(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 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 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 #endif // CPPAST_CPP_TYPE_HPP_INCLUDED diff --git a/include/cppast/cpp_type_alias.hpp b/include/cppast/cpp_type_alias.hpp index 6ddfea9..06a1974 100644 --- a/include/cppast/cpp_type_alias.hpp +++ b/include/cppast/cpp_type_alias.hpp @@ -10,39 +10,36 @@ namespace cppast { - /// A [cppast::cpp_entity]() modelling a type alias/typedef. - /// \notes There is no distinction between `using` and `typedef` type aliases made in the AST. - class cpp_type_alias final : public cpp_entity +/// A [cppast::cpp_entity]() modelling a type alias/typedef. +/// \notes There is no distinction between `using` and `typedef` type aliases made in the AST. +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 build(const cpp_entity_index& idx, cpp_entity_id id, + std::string name, std::unique_ptr type); + + /// \returns A newly created type alias that isn't registered. + /// \notes This function is intendend for templated type aliases. + static std::unique_ptr build(std::string name, std::unique_ptr type); + + /// \returns A reference to the aliased [cppast::cpp_type](). + const cpp_type& underlying_type() const noexcept { - public: - static cpp_entity_kind kind() noexcept; + return *type_; + } - /// \returns A newly created and registered type alias. - static std::unique_ptr build(const cpp_entity_index& idx, cpp_entity_id id, - std::string name, - std::unique_ptr type); +private: + cpp_type_alias(std::string name, std::unique_ptr type) + : cpp_entity(std::move(name)), type_(std::move(type)) + {} - /// \returns A newly created type alias that isn't registered. - /// \notes This function is intendend for templated type aliases. - static std::unique_ptr build(std::string name, - std::unique_ptr type); + cpp_entity_kind do_get_entity_kind() const noexcept override; - /// \returns A reference to the aliased [cppast::cpp_type](). - const cpp_type& underlying_type() const noexcept - { - return *type_; - } - - private: - cpp_type_alias(std::string name, std::unique_ptr type) - : cpp_entity(std::move(name)), type_(std::move(type)) - { - } - - cpp_entity_kind do_get_entity_kind() const noexcept override; - - std::unique_ptr type_; - }; + std::unique_ptr type_; +}; } // namespace cppast #endif // CPPAST_CPP_TYPE_ALIAS_HPP_INCLUDED diff --git a/include/cppast/cpp_variable.hpp b/include/cppast/cpp_variable.hpp index 0d7064e..894ba9c 100644 --- a/include/cppast/cpp_variable.hpp +++ b/include/cppast/cpp_variable.hpp @@ -12,61 +12,58 @@ namespace cppast { - /// A [cppast::cpp_entity]() modelling a C++ variable. - /// \notes This is not a member variable, - /// use [cppast::cpp_member_variable]() for that. - /// But it can be `static` member variable. - class cpp_variable final : public cpp_entity, - public cpp_variable_base, - public cpp_forward_declarable +/// A [cppast::cpp_entity]() modelling a C++ variable. +/// \notes This is not a member variable, +/// use [cppast::cpp_member_variable]() for that. +/// But it can be `static` member variable. +class cpp_variable final : public cpp_entity, + public cpp_variable_base, + 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 build(const cpp_entity_index& idx, cpp_entity_id id, + std::string name, std::unique_ptr type, + std::unique_ptr 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 build_declaration(cpp_entity_id definition_id, + std::string name, + std::unique_ptr 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: - static cpp_entity_kind kind() noexcept; + return storage_; + } - /// \returns A newly created and registered variable. - /// \notes The default value may be `nullptr` indicating no default value. - static std::unique_ptr build(const cpp_entity_index& idx, cpp_entity_id id, - std::string name, std::unique_ptr type, - std::unique_ptr def, - cpp_storage_class_specifiers spec, - bool is_constexpr); + /// \returns Whether the variable is marked `constexpr`. + bool is_constexpr() const noexcept + { + return 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 build_declaration(cpp_entity_id definition_id, - std::string name, - std::unique_ptr type, - cpp_storage_class_specifiers spec, - bool is_constexpr); +private: + cpp_variable(std::string name, std::unique_ptr type, + std::unique_ptr 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) + {} - /// \returns The [cppast::cpp_storage_specifiers]() on that variable. - cpp_storage_class_specifiers storage_class() const noexcept - { - return storage_; - } + cpp_entity_kind do_get_entity_kind() const noexcept override; - /// \returns Whether the variable is marked `constexpr`. - bool is_constexpr() const noexcept - { - return is_constexpr_; - } - - private: - cpp_variable(std::string name, std::unique_ptr type, - std::unique_ptr 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_; - }; + cpp_storage_class_specifiers storage_; + bool is_constexpr_; +}; } // namespace cppast #endif // CPPAST_CPP_VARIABLE_HPP_INCLUDED diff --git a/include/cppast/cpp_variable_base.hpp b/include/cppast/cpp_variable_base.hpp index 9ac44a8..7b704e4 100644 --- a/include/cppast/cpp_variable_base.hpp +++ b/include/cppast/cpp_variable_base.hpp @@ -10,37 +10,36 @@ namespace cppast { - /// Additional base class for all [cppast::cpp_entity]() modelling some kind of variable. - /// - /// Examples are [cppast::cpp_variable]() or [cppast::cpp_function_parameter](), - /// or anything that is name/type/default-value triple. - class cpp_variable_base +/// Additional base class for all [cppast::cpp_entity]() modelling some kind of variable. +/// +/// Examples are [cppast::cpp_variable]() or [cppast::cpp_function_parameter](), +/// or anything that is name/type/default-value triple. +class cpp_variable_base +{ +public: + /// \returns A reference to the [cppast::cpp_type]() of the variable. + const cpp_type& type() const noexcept { - public: - /// \returns A reference to the [cppast::cpp_type]() of the variable. - const cpp_type& type() const noexcept - { - return *type_; - } + return *type_; + } - /// \returns A [ts::optional_ref]() to the [cppast::cpp_expression]() that is the default value. - type_safe::optional_ref default_value() const noexcept - { - return type_safe::opt_ref(default_.get()); - } + /// \returns A [ts::optional_ref]() to the [cppast::cpp_expression]() that is the default value. + type_safe::optional_ref default_value() const noexcept + { + return type_safe::opt_ref(default_.get()); + } - protected: - cpp_variable_base(std::unique_ptr type, std::unique_ptr def) - : type_(std::move(type)), default_(std::move(def)) - { - } +protected: + cpp_variable_base(std::unique_ptr type, std::unique_ptr def) + : type_(std::move(type)), default_(std::move(def)) + {} - ~cpp_variable_base() noexcept = default; + ~cpp_variable_base() noexcept = default; - private: - std::unique_ptr type_; - std::unique_ptr default_; - }; +private: + std::unique_ptr type_; + std::unique_ptr default_; +}; } // namespace cppast #endif // CPPAST_CPP_VARIABLE_BASE_HPP_INCLUDED diff --git a/include/cppast/cpp_variable_template.hpp b/include/cppast/cpp_variable_template.hpp index fe4e33e..a318c7f 100644 --- a/include/cppast/cpp_variable_template.hpp +++ b/include/cppast/cpp_variable_template.hpp @@ -10,35 +10,34 @@ namespace cppast { - /// A [cppast::cpp_entity]() modelling a C++ alias template. - class cpp_variable_template final : public cpp_template +/// A [cppast::cpp_entity]() modelling a C++ alias 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 { public: - static cpp_entity_kind kind() noexcept; - - /// Builder for [cppast::cpp_variable_template](). - class builder : public basic_builder - { - 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(*begin()); - } - - private: - cpp_variable_template(std::unique_ptr variable) - : cpp_template(std::unique_ptr(variable.release())) - { - } - - cpp_entity_kind do_get_entity_kind() const noexcept override; - - friend basic_builder; + 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(*begin()); + } + +private: + cpp_variable_template(std::unique_ptr variable) + : cpp_template(std::unique_ptr(variable.release())) + {} + + cpp_entity_kind do_get_entity_kind() const noexcept override; + + friend basic_builder; +}; } // namespace cppast #endif // CPPAST_CPP_VARIABLE_TEMPLATE_HPP_INCLUDED diff --git a/include/cppast/detail/assert.hpp b/include/cppast/detail/assert.hpp index 66fc42d..fee744c 100644 --- a/include/cppast/detail/assert.hpp +++ b/include/cppast/detail/assert.hpp @@ -8,31 +8,29 @@ #include #ifdef CPPAST_ENABLE_ASSERTIONS -#define CPPAST_ASSERTION_LEVEL 1 +# define CPPAST_ASSERTION_LEVEL 1 #else -#define CPPAST_ASSERTION_LEVEL 0 +# define CPPAST_ASSERTION_LEVEL 0 #endif #ifdef CPPAST_ENABLE_PRECONDITION_CHECKS -#define CPPAST_PRECONDITION_LEVEL 1 +# define CPPAST_PRECONDITION_LEVEL 1 #else -#define CPPAST_PRECONDITION_LEVEL 0 +# define CPPAST_PRECONDITION_LEVEL 0 #endif namespace cppast { - namespace detail - { - struct assert_handler : debug_assert::set_level, - debug_assert::default_handler - { - }; +namespace detail +{ + struct assert_handler : debug_assert::set_level, + debug_assert::default_handler + {}; - struct precondition_error_handler : debug_assert::set_level, - debug_assert::default_handler - { - }; - } -} // namespace cppast::detail + struct precondition_error_handler : debug_assert::set_level, + debug_assert::default_handler + {}; +} // namespace detail +} // namespace cppast #endif // CPPAST_ASSERT_HPP_INCLUDED diff --git a/include/cppast/detail/intrusive_list.hpp b/include/cppast/detail/intrusive_list.hpp index 3627a10..4743a22 100644 --- a/include/cppast/detail/intrusive_list.hpp +++ b/include/cppast/detail/intrusive_list.hpp @@ -5,8 +5,8 @@ #ifndef CPPAST_INTRUSIVE_LIST_HPP_INCLUDED #define CPPAST_INTRUSIVE_LIST_HPP_INCLUDED -#include #include +#include #include @@ -14,228 +14,226 @@ namespace cppast { - class cpp_file; +class cpp_file; - namespace detail +namespace detail +{ + template + class intrusive_list_node { - template - class intrusive_list_node + std::unique_ptr next_; + + void do_on_insert(const T& parent) noexcept { - std::unique_ptr next_; + static_cast(*this).on_insert(parent); + } - void do_on_insert(const T& parent) noexcept - { - static_cast(*this).on_insert(parent); - } + template + friend struct intrusive_list_access; + }; - template - friend struct intrusive_list_access; - }; - - template - struct intrusive_list_access + template + struct intrusive_list_access + { + template + static T* get_next(const U& obj) { - template - static T* get_next(const U& obj) - { - static_assert(std::is_base_of::value, "must be a base"); - return static_cast(obj.next_.get()); - } + static_assert(std::is_base_of::value, "must be a base"); + return static_cast(obj.next_.get()); + } - template - static T* set_next(U& obj, std::unique_ptr node) - { - static_assert(std::is_base_of::value, "must be a base"); - obj.next_ = std::move(node); - return static_cast(obj.next_.get()); - } - - template - static void on_insert(U& obj, const V& parent) - { - obj.do_on_insert(parent); - } - }; - - template - class intrusive_list_iterator + template + static T* set_next(U& obj, std::unique_ptr node) { - public: - using value_type = T; - using reference = T&; - using pointer = T*; - using difference_type = std::ptrdiff_t; - using iterator_category = std::forward_iterator_tag; + static_assert(std::is_base_of::value, "must be a base"); + obj.next_ = std::move(node); + return static_cast(obj.next_.get()); + } - intrusive_list_iterator() noexcept : cur_(nullptr) {} - - reference operator*() const noexcept - { - return *cur_; - } - - pointer operator->() const noexcept - { - return cur_; - } - - intrusive_list_iterator& operator++() noexcept - { - cur_ = intrusive_list_access::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 - friend class intrusive_list; - }; - - template - class intrusive_list + template + static void on_insert(U& obj, const V& parent) { - public: - intrusive_list() = default; + obj.do_on_insert(parent); + } + }; - //=== modifiers ===// - template ::value>::type> - void push_back(std::unique_ptr obj) noexcept - { - push_back_impl(std::move(obj)); - } + template + class intrusive_list_iterator + { + public: + using value_type = T; + using reference = T&; + using pointer = T*; + using difference_type = std::ptrdiff_t; + using iterator_category = std::forward_iterator_tag; - template ::value, U>::type> - void push_back(const U& parent, std::unique_ptr obj) noexcept - { - push_back_impl(std::move(obj)); - intrusive_list_access::on_insert(last_.value(), parent); - } + intrusive_list_iterator() noexcept : cur_(nullptr) {} - //=== accesors ===// - bool empty() const noexcept - { - return first_ == nullptr; - } - - type_safe::optional_ref front() noexcept - { - return type_safe::opt_ref(first_.get()); - } - - type_safe::optional_ref front() const noexcept - { - return type_safe::opt_cref(first_.get()); - } - - type_safe::optional_ref back() noexcept - { - return last_; - } - - type_safe::optional_ref back() const noexcept - { - return last_; - } - - //=== iterators ===// - using iterator = intrusive_list_iterator; - using const_iterator = intrusive_list_iterator; - - 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 obj) - { - DEBUG_ASSERT(obj != nullptr, detail::assert_handler{}); - - if (last_) - { - auto ptr = intrusive_list_access::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 first_; - type_safe::optional_ref last_; - }; - - template - class iteratable_intrusive_list + reference operator*() const noexcept { - public: - iteratable_intrusive_list(type_safe::object_ref> list) - : list_(list) + return *cur_; + } + + pointer operator->() const noexcept + { + return cur_; + } + + intrusive_list_iterator& operator++() noexcept + { + cur_ = intrusive_list_access::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 + friend class intrusive_list; + }; + + template + class intrusive_list + { + public: + intrusive_list() = default; + + //=== modifiers ===// + template ::value>::type> + void push_back(std::unique_ptr obj) noexcept + { + push_back_impl(std::move(obj)); + } + + template ::value, U>::type> + void push_back(const U& parent, std::unique_ptr obj) noexcept + { + push_back_impl(std::move(obj)); + intrusive_list_access::on_insert(last_.value(), parent); + } + + //=== accesors ===// + bool empty() const noexcept + { + return first_ == nullptr; + } + + type_safe::optional_ref front() noexcept + { + return type_safe::opt_ref(first_.get()); + } + + type_safe::optional_ref front() const noexcept + { + return type_safe::opt_cref(first_.get()); + } + + type_safe::optional_ref back() noexcept + { + return last_; + } + + type_safe::optional_ref back() const noexcept + { + return last_; + } + + //=== iterators ===// + using iterator = intrusive_list_iterator; + using const_iterator = intrusive_list_iterator; + + 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 obj) + { + DEBUG_ASSERT(obj != nullptr, detail::assert_handler{}); + + if (last_) { + auto ptr = intrusive_list_access::set_next(last_.value(), std::move(obj)); + last_ = type_safe::ref(*ptr); } - - bool empty() const noexcept + else { - return list_->empty(); + first_ = std::move(obj); + last_ = type_safe::opt_ref(first_.get()); } + } - using iterator = typename intrusive_list::const_iterator; + std::unique_ptr first_; + type_safe::optional_ref last_; + }; - iterator begin() const noexcept - { - return list_->begin(); - } + template + class iteratable_intrusive_list + { + public: + iteratable_intrusive_list(type_safe::object_ref> list) : list_(list) + {} - iterator end() const noexcept - { - return list_->end(); - } + bool empty() const noexcept + { + return list_->empty(); + } - private: - type_safe::object_ref> list_; - }; - } -} // namespace cppast::detail + using iterator = typename intrusive_list::const_iterator; + + iterator begin() const noexcept + { + return list_->begin(); + } + + iterator end() const noexcept + { + return list_->end(); + } + + private: + type_safe::object_ref> list_; + }; +} // namespace detail +} // namespace cppast #endif // CPPAST_INTRUSIVE_LIST_HPP_INCLUDED diff --git a/include/cppast/diagnostic.hpp b/include/cppast/diagnostic.hpp index 2d4bc3a..7cbcad8 100644 --- a/include/cppast/diagnostic.hpp +++ b/include/cppast/diagnostic.hpp @@ -12,137 +12,138 @@ namespace cppast { - /// Describes a physical source location attached to a [cppast::diagnostic](). - /// \notes All information might be unavailable. - struct source_location +/// Describes a physical source location attached to a [cppast::diagnostic](). +/// \notes All information might be unavailable. +struct source_location +{ + type_safe::optional entity; + type_safe::optional file; + type_safe::optional 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 entity; - type_safe::optional file; - type_safe::optional line, column; + return {std::move(entity), std::move(file), 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) - { - return {std::move(entity), std::move(file), line, column}; - } + /// \returns A source location where only file information is available. + static source_location make_file(std::string file, + type_safe::optional line = type_safe::nullopt, + type_safe::optional column = type_safe::nullopt) + { + return {type_safe::nullopt, std::move(file), line, column}; + } - /// \returns A source location where only file information is available. - static source_location make_file(std::string file, - type_safe::optional line = type_safe::nullopt, - type_safe::optional column = type_safe::nullopt) - { - return {type_safe::nullopt, std::move(file), line, column}; - } + /// \returns A source location where only the entity name is available. + static source_location make_entity(std::string entity) + { + return {std::move(entity), type_safe::nullopt, type_safe::nullopt, type_safe::nullopt}; + } - /// \returns A source location where only the entity name is available. - static source_location make_entity(std::string entity) - { - return {std::move(entity), type_safe::nullopt, type_safe::nullopt, type_safe::nullopt}; - } + /// \returns A source location where no information is avilable. + static source_location make_unknown() + { + return {type_safe::nullopt, type_safe::nullopt, type_safe::nullopt, type_safe::nullopt}; + } - /// \returns A source location where no information is avilable. - static source_location make_unknown() - { - return {type_safe::nullopt, type_safe::nullopt, type_safe::nullopt, type_safe::nullopt}; - } + /// \returns A source location where entity and file name is available. + static source_location make_entity(std::string entity, std::string file) + { + return {std::move(entity), std::move(file), type_safe::nullopt, type_safe::nullopt}; + } - /// \returns A source location where entity and file name is available. - static source_location make_entity(std::string entity, std::string file) + /// \returns A possible string representation of the source location. + /// \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. - /// \notes It will include a separator, but no trailing whitespace. - std::string to_string() const - { - std::string result; - if (file) + if (line) { - result += file.value() + ":"; + result += std::to_string(line.value()); - if (line) - { - result += std::to_string(line.value()); + if (column) + result += "," + std::to_string(column.value()); - if (column) - result += "," + std::to_string(column.value()); - - result += ":"; - } - - if (entity) - result += " (" + entity.value() + "):"; + result += ":"; } - 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](). - enum class severity + return result; + } +}; + +/// 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. - 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) - { - 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"; + 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"; } - /// A diagnostic. - /// - /// It represents an error message from a [cppast::parser](). - struct diagnostic - { - std::string message; - source_location location; - cppast::severity severity; - }; + return "programmer error"; +} - namespace detail - { - template - std::string format(Args&&... args) - { - std::ostringstream stream; - int dummy[] = {(stream << std::forward(args), 0)...}; - (void)dummy; - return stream.str(); - } - } // namespace detail +/// A diagnostic. +/// +/// It represents an error message from a [cppast::parser](). +struct diagnostic +{ + std::string message; + source_location location; + cppast::severity severity; +}; - /// 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](). +namespace detail +{ template - diagnostic format_diagnostic(severity sev, source_location loc, Args&&... args) + std::string format(Args&&... args) { - return {detail::format(std::forward(args)...), std::move(loc), sev}; + std::ostringstream stream; + int dummy[] = {(stream << std::forward(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 +diagnostic format_diagnostic(severity sev, source_location loc, Args&&... args) +{ + return {detail::format(std::forward(args)...), std::move(loc), sev}; +} } // namespace cppast #endif // CPPAST_DIAGNOSTIC_HPP_INCLUDED diff --git a/include/cppast/diagnostic_logger.hpp b/include/cppast/diagnostic_logger.hpp index 9a0c9c8..526dd96 100644 --- a/include/cppast/diagnostic_logger.hpp +++ b/include/cppast/diagnostic_logger.hpp @@ -11,59 +11,60 @@ namespace cppast { - /// Base class for a [cppast::diagnostic]() logger. - /// - /// Its task is controlling how diagnostic are being displayed. - class diagnostic_logger +/// Base class for a [cppast::diagnostic]() logger. +/// +/// Its task is controlling how diagnostic are being displayed. +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: - /// \effects Creates it either as verbose or not. - explicit diagnostic_logger(bool is_verbose = false) noexcept : verbose_(is_verbose) {} + verbose_ = value; + } - 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 - { - 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 default_logger() noexcept; - - /// \returns The default verbose logger object. - type_safe::object_ref 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 + /// \returns Whether or not the logger prints debugging diagnostics. + bool is_verbose() const noexcept { - public: - using diagnostic_logger::diagnostic_logger; + return verbose_; + } - private: - bool do_log(const char* source, const diagnostic& d) const override; - }; +private: + virtual bool do_log(const char* source, const diagnostic& d) const = 0; + + bool verbose_; +}; + +/// \returns The default logger object. +type_safe::object_ref default_logger() noexcept; + +/// \returns The default verbose logger object. +type_safe::object_ref 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 #endif // CPPAST_DIAGNOSTIC_LOGGER_HPP_INCLUDED diff --git a/include/cppast/libclang_parser.hpp b/include/cppast/libclang_parser.hpp index 3c65d09..9dca717 100644 --- a/include/cppast/libclang_parser.hpp +++ b/include/cppast/libclang_parser.hpp @@ -11,252 +11,254 @@ namespace cppast { - class libclang_compile_config; - class libclang_compilation_database; +class libclang_compile_config; +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& flags(const libclang_compile_config& config); + static const std::vector& 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); - }; - - 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)) {} + static bool remove_comments_in_macro(const libclang_compile_config& config); }; - /// 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 + 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. +/// +/// 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: - /// \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 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 logger); - - ~libclang_parser() noexcept override; - - private: - std::unique_ptr do_parse(const cpp_entity_index& idx, std::string path, - const compile_config& config) const override; - - struct impl; - std::unique_ptr 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 - void parse_files(FileParser& parser, Range&& file_names, - const libclang_compilation_database& database) - { - static_assert(std::is_same::value, - "must use the libclang parser"); - parse_files(parser, std::forward(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(); - }); + other.database_ = nullptr; } - /// 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 - void parse_database(FileParser& parser, const libclang_compilation_database& database) - { - static_assert(std::is_same::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(ptr); + ~libclang_compilation_database(); - libclang_compile_config config(data.database, file); - data.parser.parse(std::move(file), std::move(config)); - }); + 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 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 logger); + + ~libclang_parser() noexcept override; + +private: + std::unique_ptr do_parse(const cpp_entity_index& idx, std::string path, + const compile_config& config) const override; + + struct impl; + std::unique_ptr 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 +void parse_files(FileParser& parser, Range&& file_names, + const libclang_compilation_database& database) +{ + static_assert(std::is_same::value, + "must use the libclang parser"); + parse_files(parser, std::forward(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 +void parse_database(FileParser& parser, const libclang_compilation_database& database) +{ + static_assert(std::is_same::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(ptr); + + libclang_compile_config config(data.database, file); + data.parser.parse(std::move(file), std::move(config)); + }); +} } // namespace cppast #endif // CPPAST_LIBCLANG_PARSER_HPP_INCLUDED diff --git a/include/cppast/parser.hpp b/include/cppast/parser.hpp index 860842e..990f0a1 100644 --- a/include/cppast/parser.hpp +++ b/include/cppast/parser.hpp @@ -10,232 +10,223 @@ #include #include #include -#include #include +#include namespace cppast { - class cpp_entity_index; +class cpp_entity_index; - /// Base class for a parser. - /// - /// It reads a C++ source file and creates the matching [cppast::cpp_file](). - /// 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](). - class parser +/// Base class for a parser. +/// +/// It reads a C++ source file and creates the matching [cppast::cpp_file](). +/// 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](). +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 parse(const cpp_entity_index& idx, std::string path, + const compile_config& config) const { - 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 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 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 do_parse(const cpp_entity_index& idx, std::string path, - const compile_config& config) const = 0; - - type_safe::object_ref logger_; - mutable std::atomic error_; - }; - - /// A simple `FileParser` that parses all files synchronously. - /// - /// More advanced parsers could use a thread pool, for example. - template - class simple_file_parser - { - static_assert(std::is_base_of::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 - explicit simple_file_parser(type_safe::object_ref idx, - Args&&... args) - : parser_(std::forward(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 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 files() const noexcept - { - return type_safe::ref(files_); - } - - private: - Parser parser_; - detail::intrusive_list files_; - type_safe::object_ref idx_; - }; - - namespace detail - { - struct std_begin - { - }; - struct adl_begin : std_begin - { - }; - struct member_begin : adl_begin - { - }; - - template - auto get_value_type_impl(member_begin, Range&& r) - -> decltype(std::forward(r).begin()); - - template - auto get_value_type_impl(adl_begin, Range&& r) -> decltype(begin(std::forward(r))); - - template - auto get_value_type_impl(std_begin, Range&& r) - -> decltype(std::begin(std::forward(r))); - - template - using value_type = decltype(*get_value_type_impl(member_begin{}, std::declval())); - } // 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 - auto parse_files(FileParser& parser, Range&& file_names, const Configuration& get_config) -> - typename std::enable_if>()))>::type, - typename FileParser::config>::value>::type - { - for (auto&& file : std::forward(file_names)) - { - auto&& config = get_config(file); - parser.parse(std::forward(file), - std::forward(config)); - } + return do_parse(idx, std::move(path), 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 - void parse_files(FileParser& parser, Range&& file_names, typename FileParser::config config) + /// \returns Whether or not an error occurred during parsing. + /// If that happens, the AST might be incomplete. + bool error() const noexcept { - parse_files(parser, std::forward(file_names), - [&](const std::string&) { return config; }); + return error_; } - /// Parses all files included by `file`. - /// \effects For each [cppast::cpp_include_directive]() in file it will parse the included file. - template - std::size_t resolve_includes(FileParser& parser, const cpp_file& file, - typename FileParser::config config) + /// \effects Resets the error state. + void reset_error() noexcept { - auto count = 0u; - for (auto& entity : file) - { - if (entity.kind() == cpp_include_directive::kind()) - { - auto& include = static_cast(entity); - parser.parse(include.full_path(), config); - ++count; - } - } - return count; + 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 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 do_parse(const cpp_entity_index& idx, std::string path, + const compile_config& config) const = 0; + + type_safe::object_ref logger_; + mutable std::atomic error_; +}; + +/// A simple `FileParser` that parses all files synchronously. +/// +/// More advanced parsers could use a thread pool, for example. +template +class simple_file_parser +{ + static_assert(std::is_base_of::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 + explicit simple_file_parser(type_safe::object_ref idx, Args&&... args) + : parser_(std::forward(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 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 files() const noexcept + { + return type_safe::ref(files_); + } + +private: + Parser parser_; + detail::intrusive_list files_; + type_safe::object_ref idx_; +}; + +namespace detail +{ + struct std_begin + {}; + struct adl_begin : std_begin + {}; + struct member_begin : adl_begin + {}; + + template + auto get_value_type_impl(member_begin, Range&& r) -> decltype(std::forward(r).begin()); + + template + auto get_value_type_impl(adl_begin, Range&& r) -> decltype(begin(std::forward(r))); + + template + auto get_value_type_impl(std_begin, Range&& r) -> decltype(std::begin(std::forward(r))); + + template + using value_type = decltype(*get_value_type_impl(member_begin{}, std::declval())); +} // 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 +auto parse_files(FileParser& parser, Range&& file_names, const Configuration& get_config) -> + typename std::enable_if>()))>::type, + typename FileParser::config>::value>::type +{ + for (auto&& file : std::forward(file_names)) + { + auto&& config = get_config(file); + parser.parse(std::forward(file), std::forward(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 +void parse_files(FileParser& parser, Range&& file_names, typename FileParser::config config) +{ + parse_files(parser, std::forward(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 +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(entity); + parser.parse(include.full_path(), config); + ++count; + } + } + return count; +} } // namespace cppast #endif // CPPAST_PARSER_HPP_INCLUDED diff --git a/include/cppast/visitor.hpp b/include/cppast/visitor.hpp index 28dba8b..f1805ca 100644 --- a/include/cppast/visitor.hpp +++ b/include/cppast/visitor.hpp @@ -13,231 +13,230 @@ namespace cppast { - /// Information about the state of a visit operation. - struct visitor_info +/// Information about the state of a visit operation. +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. - /// If callback returns `false`, none of the children will be visited, - /// going immediately to the exit event. - container_entity_exit, //< Callback called for a container entity after the children. - /// If callback returns `false`, visit operation will be aborted. - } event; + container_entity_enter, //< Callback called for a container entity before the children. + /// If callback returns `false`, none of the children will be visited, + /// going immediately to the exit event. + container_entity_exit, //< Callback called for a container entity after the children. + /// If callback returns `false`, visit operation will be aborted. + } 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. - /// \notes It will always be `false` for the initial entity. - bool last_child; + /// True when the current entity is the last child of the visited parent entity. + /// \notes It will always be `false` for the initial entity. + bool last_child; - /// \returns `true` if the entity was not visited already. - 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 + /// \returns `true` if the entity was not visited already. + bool is_new_entity() const noexcept { - /// Visit should continue. - /// \group continue - continue_visit = true, + return event != container_entity_exit; + } - /// \group continue - continue_visit_children = true, - - /// 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 + /// \returns `true` if the entity was visited already. + bool is_old_entity() const noexcept { - using visitor_callback_t = bool (*)(void* mem, const cpp_entity&, visitor_info info); + return !is_new_entity(); + } +}; - struct visitor_info_void - { - }; - struct visitor_info_bool : visitor_info_void - { - }; +/// A more expressive way to specify the return of a visit operation. +enum visitor_result : bool +{ + /// Visit should continue. + /// \group continue + continue_visit = true, - template - visitor_callback_t get_visitor_callback( - visitor_info_bool, decltype(std::declval()(std::declval(), - visitor_info{})) = true) - { - return [](void* mem, const cpp_entity& e, visitor_info info) { - auto& func = *static_cast(mem); - return func(e, info); - }; - } + /// \group continue + continue_visit_children = true, - template - visitor_callback_t get_visitor_callback( - visitor_info_void, - decltype(std::declval()(std::declval(), visitor_info{}), - 0) = 0) - { - return [](void* mem, const cpp_entity& e, visitor_info info) { - auto& func = *static_cast(mem); - func(e, info); - return true; - }; - } + /// 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, - template - visitor_callback_t get_visitor_callback() - { - return get_visitor_callback(visitor_info_bool{}); - } + /// Visit should be aborted. + /// \notes This only happens when the event is not + /// [cppast::visitor_info::container_entity_enter](). + abort_visit = false, +}; - bool visit(const cpp_entity& e, visitor_callback_t cb, void* functor, - cpp_access_specifier_kind cur_access, bool last_child); - } // namespace detail +/// \exclude +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 - void visit(const cpp_entity& e, Func f) + visitor_callback_t get_visitor_callback( + visitor_info_bool, + decltype(std::declval()(std::declval(), visitor_info{})) = true) { - detail::visit(e, detail::get_visitor_callback(), &f, cpp_public, false); + return [](void* mem, const cpp_entity& e, visitor_info info) { + auto& func = *static_cast(mem); + return func(e, info); + }; } - /// The result of a visitor filter operation. - enum class visit_filter + template + visitor_callback_t get_visitor_callback( + visitor_info_void, + decltype(std::declval()(std::declval(), visitor_info{}), 0) = 0) { - 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. - }; + return [](void* mem, const cpp_entity& e, visitor_info info) { + auto& func = *static_cast(mem); + func(e, info); + return true; + }; + } - namespace detail + template + visitor_callback_t get_visitor_callback() { - using visitor_filter_t = visit_filter (*)(const cpp_entity&); + return get_visitor_callback(visitor_info_bool{}); + } - template - auto invoke_visit_filter(int, Filter f, const cpp_entity& e, - cpp_access_specifier_kind access) - -> decltype(static_cast(f(e, access))) + bool visit(const cpp_entity& e, visitor_callback_t cb, void* functor, + cpp_access_specifier_kind cur_access, bool last_child); +} // namespace detail + +/// 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 +void visit(const cpp_entity& e, Func f) +{ + detail::visit(e, detail::get_visitor_callback(), &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 + auto invoke_visit_filter(int, Filter f, const cpp_entity& e, cpp_access_specifier_kind access) + -> decltype(static_cast(f(e, access))) + { + return static_cast(f(e, access)); + } + + template + auto invoke_visit_filter(short, Filter f, const cpp_entity& e, cpp_access_specifier_kind) + -> decltype(static_cast(f(e))) + { + return static_cast(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 +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(f(e, access)); - } - - template - auto invoke_visit_filter(short, Filter f, const cpp_entity& e, cpp_access_specifier_kind) - -> decltype(static_cast(f(e))) - { - return static_cast(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 - 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()(&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; - } + case visit_filter::include: + return detail::get_visitor_callback()(&f, e, info); + case visit_filter::exclude: return continue_visit; - }); - } - - /// \exclude - namespace detail - { - template - 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; + 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; } - } // namespace detail + return continue_visit; + }); +} - /// Generates a blacklist visitor filter. - /// - /// \returns A visitor filter that excludes all entities having one of the specified kinds. - template - detail::visitor_filter_t blacklist() +/// \exclude +namespace detail +{ + template + bool has_one_of_kind(const cpp_entity& e) { - return [](const cpp_entity& e) { - return detail::has_one_of_kind(e) ? visit_filter::exclude : - visit_filter::include; - }; - } + static_assert(sizeof...(K) > 0, "At least one entity kind must be specified"); + bool result = false; - /// 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 - detail::visitor_filter_t blacklist_and_children() - { - return [](const cpp_entity& e) { - return detail::has_one_of_kind(e) ? visit_filter::exclude_and_children : - visit_filter::include; - }; - } + // poor men's fold + int dummy[]{(result |= (K == e.kind()), 0)...}; + (void)dummy; - /// Generates a whitelist visitor filter. - /// - /// \returns A visitor filter that excludes all entities having not one of the specified kinds. - template - detail::visitor_filter_t whitelist() - { - return [](const cpp_entity& e) { - return detail::has_one_of_kind(e) ? visit_filter::include : - visit_filter::exclude; - }; + return result; } +} // namespace detail + +/// Generates a blacklist visitor filter. +/// +/// \returns A visitor filter that excludes all entities having one of the specified kinds. +template +detail::visitor_filter_t blacklist() +{ + return [](const cpp_entity& e) { + return detail::has_one_of_kind(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 +detail::visitor_filter_t blacklist_and_children() +{ + return [](const cpp_entity& e) { + return detail::has_one_of_kind(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 +detail::visitor_filter_t whitelist() +{ + return [](const cpp_entity& e) { + return detail::has_one_of_kind(e) ? visit_filter::include : visit_filter::exclude; + }; +} } // namespace cppast #endif // CPPAST_VISITOR_HPP_INCLUDED diff --git a/src/code_generator.cpp b/src/code_generator.cpp index 1acbbb7..1c7387d 100644 --- a/src/code_generator.cpp +++ b/src/code_generator.cpp @@ -29,1111 +29,1102 @@ using namespace cppast; namespace { - void opening_brace(const code_generator::output& output) - { - if (output.formatting().is_set(formatting_flags::brace_nl)) - output << newl; - else if (output.formatting().is_set(formatting_flags::brace_ws)) - output << whitespace; - output << punctuation("{"); - } +void opening_brace(const code_generator::output& output) +{ + if (output.formatting().is_set(formatting_flags::brace_nl)) + output << newl; + else if (output.formatting().is_set(formatting_flags::brace_ws)) + output << whitespace; + output << punctuation("{"); +} - void comma(const code_generator::output& output) - { - output << punctuation(","); - if (output.formatting().is_set(formatting_flags::comma_ws)) - output << whitespace; - } +void comma(const code_generator::output& output) +{ + output << punctuation(","); + if (output.formatting().is_set(formatting_flags::comma_ws)) + output << whitespace; +} - void bracket_ws(const code_generator::output& output) - { - if (output.formatting().is_set(formatting_flags::bracket_ws)) - output << whitespace; - } +void bracket_ws(const code_generator::output& output) +{ + if (output.formatting().is_set(formatting_flags::bracket_ws)) + output << whitespace; +} - void operator_ws(const code_generator::output& output) - { - if (output.formatting().is_set(formatting_flags::operator_ws)) - output << whitespace; - } +void operator_ws(const code_generator::output& output) +{ + if (output.formatting().is_set(formatting_flags::operator_ws)) + output << whitespace; +} - bool generate_code_impl(code_generator& generator, const cpp_entity& e, - cpp_access_specifier_kind cur_access); +bool generate_code_impl(code_generator& generator, const cpp_entity& e, + cpp_access_specifier_kind cur_access); - template - bool write_container(code_generator::output& output, const Container& cont, Sep s, - cpp_access_specifier_kind cur_access) +template +bool write_container(code_generator::output& output, const Container& cont, Sep s, + cpp_access_specifier_kind cur_access) +{ + auto need_sep = false; + for (auto& child : cont) { - auto need_sep = false; - for (auto& child : cont) + auto is_excluded = output.options(child, cur_access).is_set(code_generator::exclude); + if (!is_excluded) { - auto is_excluded = output.options(child, cur_access).is_set(code_generator::exclude); - if (!is_excluded) + if (need_sep) + output << s; + need_sep = generate_code_impl(*output.generator(), child, cur_access); + } + } + return need_sep; +} + +bool generate_file(code_generator& generator, const cpp_file& f, + cpp_access_specifier_kind cur_access) +{ + code_generator::output output(type_safe::ref(generator), type_safe::ref(f), cur_access); + if (output) + { + auto need_sep = write_container(output, f, newl, cur_access); + if (!need_sep) + // file empty, write newl + output << newl; + else + output.container_end(); + } + return static_cast(output); +} + +bool generate_macro_parameter(code_generator& generator, const cpp_macro_parameter& param, + cpp_access_specifier_kind cur_access) +{ + code_generator::output output(type_safe::ref(generator), type_safe::ref(param), cur_access); + if (output) + output << identifier(param.name()); + return static_cast(output); +} + +bool generate_macro_definition(code_generator& generator, const cpp_macro_definition& def, + cpp_access_specifier_kind cur_access) +{ + code_generator::output output(type_safe::ref(generator), type_safe::ref(def), cur_access); + if (output) + { + output << preprocessor_token("#define") << whitespace << identifier(def.name()); + if (def.is_function_like()) + { + output << preprocessor_token("(") << bracket_ws; + auto need_sep = write_container(output, def.parameters(), comma, cpp_public); + if (def.is_variadic()) { if (need_sep) - output << s; - need_sep = generate_code_impl(*output.generator(), child, cur_access); + output << comma; + output << preprocessor_token("..."); } + output << bracket_ws << preprocessor_token(")"); } - return need_sep; - } - - bool generate_file(code_generator& generator, const cpp_file& f, - cpp_access_specifier_kind cur_access) - { - code_generator::output output(type_safe::ref(generator), type_safe::ref(f), cur_access); - if (output) - { - auto need_sep = write_container(output, f, newl, cur_access); - if (!need_sep) - // file empty, write newl - output << newl; - else - output.container_end(); - } - return static_cast(output); - } - - bool generate_macro_parameter(code_generator& generator, const cpp_macro_parameter& param, - cpp_access_specifier_kind cur_access) - { - code_generator::output output(type_safe::ref(generator), type_safe::ref(param), cur_access); - if (output) - output << identifier(param.name()); - return static_cast(output); - } - - bool generate_macro_definition(code_generator& generator, const cpp_macro_definition& def, - cpp_access_specifier_kind cur_access) - { - code_generator::output output(type_safe::ref(generator), type_safe::ref(def), cur_access); - if (output) - { - output << preprocessor_token("#define") << whitespace << identifier(def.name()); - if (def.is_function_like()) - { - output << preprocessor_token("(") << bracket_ws; - auto need_sep = write_container(output, def.parameters(), comma, cpp_public); - if (def.is_variadic()) - { - if (need_sep) - output << comma; - output << preprocessor_token("..."); - } - output << bracket_ws << preprocessor_token(")"); - } - if (!def.replacement().empty() && !output.options().is_set(code_generator::declaration)) - output << whitespace << preprocessor_token(def.replacement()) << newl; - else - output << newl; - } - return static_cast(output); - } - - bool generate_include_directive(code_generator& generator, const cpp_include_directive& include, - cpp_access_specifier_kind cur_access) - { - code_generator::output output(type_safe::ref(generator), type_safe::ref(include), - cur_access); - if (output) - { - output << preprocessor_token("#include") << whitespace; - if (include.include_kind() == cpp_include_kind::system) - output << preprocessor_token("<"); - else - output << preprocessor_token("\""); - output << include.target(); - if (include.include_kind() == cpp_include_kind::system) - output << preprocessor_token(">"); - else - output << preprocessor_token("\""); + if (!def.replacement().empty() && !output.options().is_set(code_generator::declaration)) + output << whitespace << preprocessor_token(def.replacement()) << newl; + else output << newl; - } - return static_cast(output); } + return static_cast(output); +} - bool generate_language_linkage(code_generator& generator, const cpp_language_linkage& linkage, - cpp_access_specifier_kind cur_access) +bool generate_include_directive(code_generator& generator, const cpp_include_directive& include, + cpp_access_specifier_kind cur_access) +{ + code_generator::output output(type_safe::ref(generator), type_safe::ref(include), cur_access); + if (output) { - code_generator::output output(type_safe::ref(generator), type_safe::ref(linkage), - cur_access); - if (output) - { - output << keyword("extern") << whitespace << string_literal(linkage.name()); - if (linkage.is_block()) - { - output << opening_brace; - output.indent(); - - write_container(output, linkage, newl, cur_access); - - output.unindent(); - output << punctuation("}") << newl; - } - else - { - output << whitespace; - generate_code_impl(generator, *linkage.begin(), cur_access); - } - } - return static_cast(output); + output << preprocessor_token("#include") << whitespace; + if (include.include_kind() == cpp_include_kind::system) + output << preprocessor_token("<"); + else + output << preprocessor_token("\""); + output << include.target(); + if (include.include_kind() == cpp_include_kind::system) + output << preprocessor_token(">"); + else + output << preprocessor_token("\""); + output << newl; } + return static_cast(output); +} - bool generate_namespace(code_generator& generator, const cpp_namespace& ns, - cpp_access_specifier_kind cur_access) +bool generate_language_linkage(code_generator& generator, const cpp_language_linkage& linkage, + cpp_access_specifier_kind cur_access) +{ + code_generator::output output(type_safe::ref(generator), type_safe::ref(linkage), cur_access); + if (output) { - code_generator::output output(type_safe::ref(generator), type_safe::ref(ns), cur_access); - if (output) + output << keyword("extern") << whitespace << string_literal(linkage.name()); + if (linkage.is_block()) { - if (ns.is_inline()) - output << keyword("inline") << whitespace; - output << keyword("namespace") << whitespace << identifier(ns.name()); output << opening_brace; output.indent(); - write_container(output, ns, newl, cur_access); - output.container_end(); + write_container(output, linkage, newl, cur_access); output.unindent(); output << punctuation("}") << newl; } - return static_cast(output); - } - - bool generate_namespace_alias(code_generator& generator, const cpp_namespace_alias& alias, - cpp_access_specifier_kind cur_access) - { - code_generator::output output(type_safe::ref(generator), type_safe::ref(alias), cur_access); - if (output) + else { - output << keyword("namespace") << whitespace << identifier(alias.name()) << operator_ws - << punctuation("=") << operator_ws; - if (output.options() & code_generator::exclude_target) - output.excluded(alias); - else - output << alias.target(); - output << punctuation(";") << newl; + output << whitespace; + generate_code_impl(generator, *linkage.begin(), cur_access); } - return static_cast(output); } + return static_cast(output); +} - bool generate_using_directive(code_generator& generator, const cpp_using_directive& directive, - cpp_access_specifier_kind cur_access) +bool generate_namespace(code_generator& generator, const cpp_namespace& ns, + cpp_access_specifier_kind cur_access) +{ + code_generator::output output(type_safe::ref(generator), type_safe::ref(ns), cur_access); + if (output) { - code_generator::output output(type_safe::ref(generator), type_safe::ref(directive), - cur_access); - if (output) - output << keyword("using") << whitespace << keyword("namespace") << whitespace - << directive.target() << punctuation(";") << newl; - return static_cast(output); + if (ns.is_inline()) + output << keyword("inline") << whitespace; + output << keyword("namespace") << whitespace << identifier(ns.name()); + output << opening_brace; + output.indent(); + + write_container(output, ns, newl, cur_access); + output.container_end(); + + output.unindent(); + output << punctuation("}") << newl; } + return static_cast(output); +} - bool generate_using_declaration(code_generator& generator, - const cpp_using_declaration& declaration, - cpp_access_specifier_kind cur_access) +bool generate_namespace_alias(code_generator& generator, const cpp_namespace_alias& alias, + cpp_access_specifier_kind cur_access) +{ + code_generator::output output(type_safe::ref(generator), type_safe::ref(alias), cur_access); + if (output) { - code_generator::output output(type_safe::ref(generator), type_safe::ref(declaration), - cur_access); - if (output) - output << keyword("using") << whitespace << declaration.target() << punctuation(";") - << newl; - return static_cast(output); + output << keyword("namespace") << whitespace << identifier(alias.name()) << operator_ws + << punctuation("=") << operator_ws; + if (output.options() & code_generator::exclude_target) + output.excluded(alias); + else + output << alias.target(); + output << punctuation(";") << newl; } + return static_cast(output); +} - bool generate_type_alias(code_generator& generator, const cpp_type_alias& alias, - cpp_access_specifier_kind cur_access) +bool generate_using_directive(code_generator& generator, const cpp_using_directive& directive, + cpp_access_specifier_kind cur_access) +{ + code_generator::output output(type_safe::ref(generator), type_safe::ref(directive), cur_access); + if (output) + output << keyword("using") << whitespace << keyword("namespace") << whitespace + << directive.target() << punctuation(";") << newl; + return static_cast(output); +} + +bool generate_using_declaration(code_generator& generator, const cpp_using_declaration& declaration, + cpp_access_specifier_kind cur_access) +{ + code_generator::output output(type_safe::ref(generator), type_safe::ref(declaration), + cur_access); + if (output) + output << keyword("using") << whitespace << declaration.target() << punctuation(";") + << newl; + return static_cast(output); +} + +bool generate_type_alias(code_generator& generator, const cpp_type_alias& alias, + cpp_access_specifier_kind cur_access) +{ + code_generator::output output(type_safe::ref(generator), type_safe::ref(alias), cur_access); + if (output) { - code_generator::output output(type_safe::ref(generator), type_safe::ref(alias), cur_access); - if (output) + output << keyword("using") << whitespace << identifier(alias.name()) << operator_ws + << punctuation("=") << operator_ws; + if (output.options() & code_generator::exclude_target) + output.excluded(alias); + else + detail::write_type(output, alias.underlying_type(), ""); + output << punctuation(";") << newl; + } + return static_cast(output); +} + +bool generate_enum_value(code_generator& generator, const cpp_enum_value& value, + cpp_access_specifier_kind cur_access) +{ + code_generator::output output(type_safe::ref(generator), type_safe::ref(value), cur_access); + if (output) + { + output << identifier(value.name()); + if (value.value()) { - output << keyword("using") << whitespace << identifier(alias.name()) << operator_ws - << punctuation("=") << operator_ws; - if (output.options() & code_generator::exclude_target) - output.excluded(alias); - else - detail::write_type(output, alias.underlying_type(), ""); - output << punctuation(";") << newl; - } - return static_cast(output); - } - - bool generate_enum_value(code_generator& generator, const cpp_enum_value& value, - cpp_access_specifier_kind cur_access) - { - code_generator::output output(type_safe::ref(generator), type_safe::ref(value), cur_access); - if (output) - { - output << identifier(value.name()); - if (value.value()) - { - output << operator_ws << punctuation("=") << operator_ws; - detail:: - write_expression(output, + output << operator_ws << punctuation("=") << operator_ws; + detail::write_expression(output, value.value() .value()); // should have named something differently... - } } - return static_cast(output); } + return static_cast(output); +} - bool generate_enum(code_generator& generator, const cpp_enum& e, +bool generate_enum(code_generator& generator, const cpp_enum& e, + cpp_access_specifier_kind cur_access) +{ + code_generator::output output(type_safe::ref(generator), type_safe::ref(e), cur_access); + if (output) + { + output << keyword("enum"); + if (e.is_scoped()) + output << whitespace << keyword("class"); + output << whitespace << identifier(e.semantic_scope()) << identifier(e.name()); + if (e.has_explicit_type()) + { + output << newl << punctuation(":") << operator_ws; + detail::write_type(output, e.underlying_type(), ""); + } + + if (output.generate_definition() && e.is_definition()) + { + output << opening_brace; + output.indent(); + + auto need_sep = write_container(output, e, + [](const code_generator::output& out) { + out << punctuation(",") << newl; + }, + cur_access); + if (need_sep) + output << newl; + + output.container_end(); + + output.unindent(); + output << punctuation("};") << newl; + } + else + output << punctuation(";") << newl; + } + return static_cast(output); +} + +void write_access_specifier(code_generator::output& output, cpp_access_specifier_kind access) +{ + output.unindent(); + output << keyword(to_string(access)) << punctuation(":"); + output.indent(); +} + +bool generate_access_specifier(code_generator& generator, const cpp_access_specifier& access, + cpp_access_specifier_kind) +{ + code_generator::output output(type_safe::ref(generator), type_safe::ref(access), + access.access_specifier()); + if (output) + write_access_specifier(output, access.access_specifier()); + return static_cast(output); +} + +bool generate_base_class(code_generator& generator, const cpp_base_class& base, + cpp_access_specifier_kind) +{ + DEBUG_ASSERT(base.parent() && base.parent().value().kind() == cpp_entity_kind::class_t, + detail::assert_handler{}); + auto parent_kind = static_cast(base.parent().value()).class_kind(); + + code_generator::output output(type_safe::ref(generator), type_safe::ref(base), + base.access_specifier()); + if (output) + { + if (base.is_virtual()) + output << keyword("virtual") << whitespace; + + auto access = base.access_specifier(); + if (access == cpp_protected) + output << keyword("protected") << whitespace; + else if (access == cpp_private && parent_kind != cpp_class_kind::class_t) + output << keyword("private") << whitespace; + else if (access == cpp_public && parent_kind == cpp_class_kind::class_t) + output << keyword("public") << whitespace; + + output << identifier(base.name()); + } + return static_cast(output); +} + +void write_specialization_arguments(code_generator::output& output, + const cpp_template_specialization& spec) +{ + if (spec.arguments_exposed()) + detail::write_template_arguments(output, spec.arguments()); + else if (!spec.unexposed_arguments().empty()) + { + output << punctuation("<") << bracket_ws; + detail::write_token_string(output, spec.unexposed_arguments()); + output << bracket_ws << punctuation(">"); + } +} + +void write_bases(code_generator& generator, code_generator::output& output, const cpp_class& c) +{ + auto need_sep = false; + auto first = true; + for (auto& base : c.bases()) + { + auto opt = output.options(base, base.access_specifier()); + if (first && !opt.is_set(code_generator::exclude)) + { + first = false; + output << newl << punctuation(":") << operator_ws; + } + else if (need_sep) + output << comma; + need_sep = generate_base_class(generator, base, cpp_public); + } +} + +bool generate_class(code_generator& generator, const cpp_class& c, + cpp_access_specifier_kind cur_access, + type_safe::optional_ref spec = nullptr) +{ + code_generator::output output(type_safe::ref(generator), type_safe::ref(c), cur_access); + if (output) + { + if (is_friended(c)) + output << keyword("friend") << whitespace; + output << keyword(to_string(c.class_kind())) << whitespace; + + output << identifier(c.semantic_scope()); + if (spec) + { + output << spec.value().primary_template(); + write_specialization_arguments(output, spec.value()); + } + else + output << identifier(c.name()); + + if (c.is_final()) + output << whitespace << keyword("final"); + + if (!output.generate_definition() || c.is_declaration()) + output << punctuation(";") << newl; + else + { + write_bases(generator, output, c); + output << opening_brace; + output.indent(); + + auto need_sep = false; + auto last_access = c.class_kind() == cpp_class_kind::class_t ? cpp_private : cpp_public; + auto last_written_access = last_access; + for (auto& member : c) + { + if (member.kind() == cpp_entity_kind::access_specifier_t) + { + auto& access = static_cast(member); + last_access = access.access_specifier(); + } + else if (output.options(member, last_access).is_set(code_generator::exclude)) + continue; + else + { + if (need_sep) + output << newl; + if (last_access != last_written_access) + { + write_access_specifier(output, last_access); + last_written_access = last_access; + } + need_sep = generate_code_impl(generator, member, last_access); + } + } + + output.container_end(); + + output.unindent(); + output << punctuation("};") << newl; + } + } + return static_cast(output); +} + +bool write_variable_base(code_generator::output& output, const cpp_variable_base& var, + const std::string& name) +{ + detail::write_type(output, var.type(), name); + + if (var.default_value()) + { + output << operator_ws << punctuation("=") << operator_ws; + detail::write_expression(output, var.default_value().value()); + } + return static_cast(output); +} + +void write_storage_class(code_generator::output& output, cpp_storage_class_specifiers storage, + bool is_constexpr) +{ + if (is_static(storage)) + output << keyword("static") << whitespace; + if (is_extern(storage)) + output << keyword("extern") << whitespace; + if (is_thread_local(storage)) + output << keyword("thread_local") << whitespace; + if (is_constexpr) + output << keyword("constexpr") << whitespace; +} + +bool generate_variable(code_generator& generator, const cpp_variable& var, cpp_access_specifier_kind cur_access) +{ + code_generator::output output(type_safe::ref(generator), type_safe::ref(var), cur_access); + if (output) { - code_generator::output output(type_safe::ref(generator), type_safe::ref(e), cur_access); - if (output) + write_storage_class(output, var.storage_class(), var.is_constexpr()); + + write_variable_base(output, var, var.name()); + output << punctuation(";") << newl; + } + return static_cast(output); +} + +bool generate_member_variable(code_generator& generator, const cpp_member_variable& var, + cpp_access_specifier_kind cur_access) +{ + code_generator::output output(type_safe::ref(generator), type_safe::ref(var), cur_access); + if (output) + { + if (var.is_mutable()) + output << keyword("mutable") << whitespace; + write_variable_base(output, var, var.name()); + output << punctuation(";") << newl; + } + return static_cast(output); +} + +bool generate_bitfield(code_generator& generator, const cpp_bitfield& var, + cpp_access_specifier_kind cur_access) +{ + code_generator::output output(type_safe::ref(generator), type_safe::ref(var), cur_access); + if (output) + { + if (var.is_mutable()) + output << keyword("mutable") << whitespace; + write_variable_base(output, var, var.name()); + output << operator_ws << punctuation(":") << operator_ws + << int_literal(std::to_string(var.no_bits())); + output << punctuation(";") << newl; + } + return static_cast(output); +} + +bool generate_function_parameter(code_generator& generator, const cpp_function_parameter& param, + cpp_access_specifier_kind cur_access) +{ + code_generator::output output(type_safe::ref(generator), type_safe::ref(param), cur_access); + if (output) + write_variable_base(output, param, param.name()); + return static_cast(output); +} + +void write_function_parameters(code_generator::output& output, const cpp_function_base& base) +{ + output << punctuation("(") << bracket_ws; + auto need_sep = write_container(output, base.parameters(), comma, cpp_public); + if (base.is_variadic()) + { + if (need_sep) + output << comma; + output << punctuation("..."); + } + output << bracket_ws << punctuation(")"); +} + +void write_noexcept(code_generator::output& output, const cpp_function_base& base, bool need_ws) +{ + if (!base.noexcept_condition()) + return; + else if (need_ws) + output << whitespace; + + auto& cond = base.noexcept_condition().value(); + if (cond.kind() == cpp_expression_kind::literal_t + && static_cast(cond).value() == "true") + output << keyword("noexcept"); + else + { + output << keyword("noexcept") << punctuation("(") << bracket_ws; + // update check when expression gets exposed + if (cond.kind() == cpp_expression_kind::unexposed_t + && static_cast(cond).expression().front().spelling + == "false") + output << keyword("false"); + else if (output.options().is_set(code_generator::exclude_noexcept_condition)) + output.excluded(base); + else + detail::write_expression(output, cond); + output << bracket_ws << punctuation(")"); + } +} + +void write_function_body(code_generator::output& output, const cpp_function_base& base, + bool is_pure_virtual) +{ + switch (base.body_kind()) + { + case cpp_function_declaration: + case cpp_function_definition: + if (is_pure_virtual) + output << operator_ws << punctuation("=") << operator_ws << int_literal("0"); + output << punctuation(";") << newl; + break; + + case cpp_function_defaulted: + output << operator_ws << punctuation("=") << operator_ws << keyword("default") + << punctuation(";") << newl; + break; + case cpp_function_deleted: + output << operator_ws << punctuation("=") << operator_ws << keyword("delete") + << punctuation(";") << newl; + break; + } +} + +bool generate_function(code_generator& generator, const cpp_function& func, + cpp_access_specifier_kind cur_access, + type_safe::optional_ref spec = nullptr) +{ + code_generator::output output(type_safe::ref(generator), type_safe::ref(func), cur_access); + if (output) + { + if (is_friended(func)) + output << keyword("friend") << whitespace; + write_storage_class(output, func.storage_class(), func.is_constexpr()); + + if (output.options() & code_generator::exclude_return) + output.excluded(func) << whitespace; + else if (detail::is_complex_type(func.return_type())) + output << keyword("auto") << whitespace; + else { - output << keyword("enum"); - if (e.is_scoped()) - output << whitespace << keyword("class"); - output << whitespace << identifier(e.semantic_scope()) << identifier(e.name()); - if (e.has_explicit_type()) - { - output << newl << punctuation(":") << operator_ws; - detail::write_type(output, e.underlying_type(), ""); - } - - if (output.generate_definition() && e.is_definition()) - { - output << opening_brace; - output.indent(); - - auto need_sep = write_container(output, e, - [](const code_generator::output& out) { - out << punctuation(",") << newl; - }, - cur_access); - if (need_sep) - output << newl; - - output.container_end(); - - output.unindent(); - output << punctuation("};") << newl; - } - else - output << punctuation(";") << newl; + detail::write_type(output, func.return_type(), ""); + output << whitespace; } - return static_cast(output); - } - void write_access_specifier(code_generator::output& output, cpp_access_specifier_kind access) - { - output.unindent(); - output << keyword(to_string(access)) << punctuation(":"); - output.indent(); - } - - bool generate_access_specifier(code_generator& generator, const cpp_access_specifier& access, - cpp_access_specifier_kind) - { - code_generator::output output(type_safe::ref(generator), type_safe::ref(access), - access.access_specifier()); - if (output) - write_access_specifier(output, access.access_specifier()); - return static_cast(output); - } - - bool generate_base_class(code_generator& generator, const cpp_base_class& base, - cpp_access_specifier_kind) - { - DEBUG_ASSERT(base.parent() && base.parent().value().kind() == cpp_entity_kind::class_t, - detail::assert_handler{}); - auto parent_kind = static_cast(base.parent().value()).class_kind(); - - code_generator::output output(type_safe::ref(generator), type_safe::ref(base), - base.access_specifier()); - if (output) + output << identifier(func.semantic_scope()); + if (spec) { - if (base.is_virtual()) - output << keyword("virtual") << whitespace; - - auto access = base.access_specifier(); - if (access == cpp_protected) - output << keyword("protected") << whitespace; - else if (access == cpp_private && parent_kind != cpp_class_kind::class_t) - output << keyword("private") << whitespace; - else if (access == cpp_public && parent_kind == cpp_class_kind::class_t) - output << keyword("public") << whitespace; - - output << identifier(base.name()); + output << spec.value().primary_template(); + write_specialization_arguments(output, spec.value()); } - return static_cast(output); + else + output << identifier(func.name()); + write_function_parameters(output, func); + write_noexcept(output, func, output.formatting().is_set(formatting_flags::operator_ws)); + + if (!(output.options() & code_generator::exclude_return) + && detail::is_complex_type(func.return_type())) + { + output << operator_ws << punctuation("->") << operator_ws; + detail::write_type(output, func.return_type(), ""); + } + write_function_body(output, func, false); + } + return static_cast(output); +} + +void write_prefix_virtual(code_generator::output& output, const cpp_virtual& virt) +{ + if (is_virtual(virt)) + output << keyword("virtual") << whitespace; +} + +void write_suffix_virtual(code_generator::output& output, const cpp_virtual& virt, + bool is_definition) +{ + if (is_definition) + // don't include it in definition + return; + + if (is_overriding(virt)) + output << whitespace << keyword("override"); + if (is_final(virt)) + output << whitespace << keyword("final"); +} + +bool write_cv_ref(code_generator::output& output, const cpp_member_function_base& base) +{ + auto need_ws = false; + switch (base.cv_qualifier()) + { + case cpp_cv_none: + break; + case cpp_cv_const: + output << operator_ws << keyword("const"); + need_ws = true; + break; + case cpp_cv_volatile: + output << operator_ws << keyword("volatile"); + need_ws = true; + break; + case cpp_cv_const_volatile: + output << operator_ws << keyword("const") << whitespace << keyword("volatile"); + need_ws = true; + break; } - void write_specialization_arguments(code_generator::output& output, - const cpp_template_specialization& spec) + switch (base.ref_qualifier()) { - if (spec.arguments_exposed()) - detail::write_template_arguments(output, spec.arguments()); - else if (!spec.unexposed_arguments().empty()) + case cpp_ref_none: + break; + case cpp_ref_lvalue: + output << operator_ws << punctuation("&"); + need_ws = false; + break; + case cpp_ref_rvalue: + output << operator_ws << punctuation("&&"); + need_ws = false; + break; + } + + return need_ws; +} + +bool generate_member_function(code_generator& generator, const cpp_member_function& func, + cpp_access_specifier_kind cur_access, + type_safe::optional_ref spec + = nullptr) +{ + code_generator::output output(type_safe::ref(generator), type_safe::ref(func), cur_access); + if (output) + { + if (is_friended(func)) + output << keyword("friend") << whitespace; + if (func.is_constexpr()) + output << keyword("constexpr") << whitespace; + else + write_prefix_virtual(output, func.virtual_info()); + + if (output.options() & code_generator::exclude_return) + output.excluded(func) << whitespace; + else if (detail::is_complex_type(func.return_type())) + output << keyword("auto") << whitespace; + else { - output << punctuation("<") << bracket_ws; - detail::write_token_string(output, spec.unexposed_arguments()); - output << bracket_ws << punctuation(">"); + detail::write_type(output, func.return_type(), ""); + output << whitespace; + } + + output << identifier(func.semantic_scope()); + if (spec) + { + output << spec.value().primary_template(); + write_specialization_arguments(output, spec.value()); + } + else + output << identifier(func.name()); + write_function_parameters(output, func); + auto need_ws = write_cv_ref(output, func); + write_noexcept(output, func, + need_ws || output.formatting().is_set(formatting_flags::operator_ws)); + + if (!(output.options() & code_generator::exclude_return) + && detail::is_complex_type(func.return_type())) + { + output << operator_ws << punctuation("->") << operator_ws; + detail::write_type(output, func.return_type(), ""); + } + + write_suffix_virtual(output, func.virtual_info(), func.is_definition()); + write_function_body(output, func, is_pure(func.virtual_info())); + } + return static_cast(output); +} + +bool generate_conversion_op(code_generator& generator, const cpp_conversion_op& op, + cpp_access_specifier_kind cur_access) +{ + code_generator::output output(type_safe::ref(generator), type_safe::ref(op), cur_access); + if (output) + { + if (is_friended(op)) + output << keyword("friend") << whitespace; + if (op.is_explicit()) + output << keyword("explicit") << whitespace; + if (op.is_constexpr()) + output << keyword("constexpr") << whitespace; + else + write_prefix_virtual(output, op.virtual_info()); + + output << identifier(op.semantic_scope()); + + auto pos = op.name().find("operator"); + output << identifier(op.name().substr(0u, pos)) << keyword("operator") << whitespace; + if (output.options() & code_generator::exclude_return) + output.excluded(op); + else + detail::write_type(output, op.return_type(), ""); + + output << punctuation("(") << punctuation(")"); + auto need_ws = write_cv_ref(output, op); + write_noexcept(output, op, + need_ws || output.formatting().is_set(formatting_flags::operator_ws)); + + write_suffix_virtual(output, op.virtual_info(), op.is_definition()); + write_function_body(output, op, is_pure(op.virtual_info())); + } + return static_cast(output); +} + +bool generate_constructor(code_generator& generator, const cpp_constructor& ctor, + cpp_access_specifier_kind cur_access) +{ + code_generator::output output(type_safe::ref(generator), type_safe::ref(ctor), cur_access); + if (output) + { + if (is_friended(ctor)) + output << keyword("friend") << whitespace; + if (ctor.is_explicit()) + output << keyword("explicit") << whitespace; + if (ctor.is_constexpr()) + output << keyword("constexpr") << whitespace; + + output << identifier(ctor.semantic_scope()) << identifier(ctor.name()); + write_function_parameters(output, ctor); + write_noexcept(output, ctor, output.formatting().is_set(formatting_flags::operator_ws)); + + write_function_body(output, ctor, false); + } + return static_cast(output); +} + +bool generate_destructor(code_generator& generator, const cpp_destructor& dtor, + cpp_access_specifier_kind cur_access) +{ + code_generator::output output(type_safe::ref(generator), type_safe::ref(dtor), cur_access); + if (output) + { + if (is_friended(dtor)) + output << keyword("friend") << whitespace; + write_prefix_virtual(output, dtor.virtual_info()); + output << identifier(dtor.semantic_scope()) << identifier(dtor.name()) << punctuation("(") + << punctuation(")"); + write_noexcept(output, dtor, output.formatting().is_set(formatting_flags::operator_ws)); + + write_suffix_virtual(output, dtor.virtual_info(), dtor.is_definition()); + write_function_body(output, dtor, is_pure(dtor.virtual_info())); + } + return static_cast(output); +} + +bool generate_function_base(code_generator& generator, const cpp_function_base& base, + cpp_access_specifier_kind cur_access, + const cpp_template_specialization& spec) +{ + switch (base.kind()) + { + case cpp_entity_kind::function_t: + return generate_function(generator, static_cast(base), cur_access, + type_safe::ref(spec)); + case cpp_entity_kind::member_function_t: + return generate_member_function(generator, static_cast(base), + cur_access, type_safe::ref(spec)); + case cpp_entity_kind::conversion_op_t: + return generate_conversion_op(generator, static_cast(base), + cur_access); + case cpp_entity_kind::constructor_t: + return generate_constructor(generator, static_cast(base), + cur_access); + + default: + DEBUG_UNREACHABLE(detail::assert_handler{}); + break; + } + return false; +} + +bool generate_friend(code_generator& generator, const cpp_friend& f, + cpp_access_specifier_kind cur_access) +{ + code_generator::output output(type_safe::ref(generator), type_safe::ref(f), cur_access); + if (output) + { + if (auto e = f.entity()) + generate_code_impl(generator, e.value(), cur_access); + else if (auto type = f.type()) + { + output << keyword("friend") << whitespace; + detail::write_type(output, type.value(), ""); + output << punctuation(";"); + } + else + DEBUG_UNREACHABLE(detail::assert_handler{}); + } + return static_cast(output); +} + +bool generate_template_type_parameter(code_generator& generator, + const cpp_template_type_parameter& param, + cpp_access_specifier_kind cur_access) +{ + code_generator::output output(type_safe::ref(generator), type_safe::ref(param), cur_access); + if (output) + { + output << keyword(to_string(param.keyword())); + if (param.is_variadic()) + output << operator_ws << punctuation("..."); + if (!param.name().empty()) + output << whitespace << identifier(param.name()); + if (param.default_type()) + { + output << operator_ws << punctuation("=") << operator_ws; + detail::write_type(output, param.default_type().value(), ""); } } + return static_cast(output); +} - void write_bases(code_generator& generator, code_generator::output& output, const cpp_class& c) +bool generate_non_type_template_parameter(code_generator& generator, + const cpp_non_type_template_parameter& param, + cpp_access_specifier_kind cur_access) +{ + code_generator::output output(type_safe::ref(generator), type_safe::ref(param), cur_access); + if (output) { - auto need_sep = false; - auto first = true; - for (auto& base : c.bases()) + detail::write_type(output, param.type(), param.name(), param.is_variadic()); + if (param.default_value()) { - auto opt = output.options(base, base.access_specifier()); - if (first && !opt.is_set(code_generator::exclude)) + output << operator_ws << punctuation("=") << operator_ws; + detail::write_expression(output, param.default_value().value()); + } + } + return static_cast(output); +} + +bool generate_template_template_parameter(code_generator& generator, + const cpp_template_template_parameter& param, + cpp_access_specifier_kind cur_access) +{ + code_generator::output output(type_safe::ref(generator), type_safe::ref(param), cur_access); + if (output) + { + output << keyword("template") << operator_ws << punctuation("<") << bracket_ws; + write_container(output, param.parameters(), punctuation(","), cur_access); + output << bracket_ws << punctuation(">") << operator_ws + << keyword(to_string(param.keyword())); + if (param.is_variadic()) + output << operator_ws << punctuation("..."); + output << whitespace << identifier(param.name()); + if (param.default_template()) + output << operator_ws << punctuation("=") << operator_ws + << param.default_template().value(); + } + return static_cast(output); +} + +void write_template_parameters(code_generator::output& output, const cpp_template& templ, + bool hide_if_empty) +{ + if (!hide_if_empty) + output << keyword("template") << operator_ws << punctuation("<") << bracket_ws; + + auto need_sep = false; + auto need_header = hide_if_empty; + for (auto& param : templ.parameters()) + { + auto is_excluded = output.options(param, cpp_public).is_set(code_generator::exclude); + if (!is_excluded) + { + if (need_header) { - first = false; - output << newl << punctuation(":") << operator_ws; + need_header = false; + output << keyword("template") << operator_ws << punctuation("<") << bracket_ws; } else if (need_sep) output << comma; - need_sep = generate_base_class(generator, base, cpp_public); + need_sep = generate_code_impl(*output.generator(), param, cpp_public); } } - bool generate_class(code_generator& generator, const cpp_class& c, - cpp_access_specifier_kind cur_access, - type_safe::optional_ref spec = nullptr) - { - code_generator::output output(type_safe::ref(generator), type_safe::ref(c), cur_access); - if (output) - { - if (is_friended(c)) - output << keyword("friend") << whitespace; - output << keyword(to_string(c.class_kind())) << whitespace; + if (!need_header) + output << bracket_ws << punctuation(">") << newl; +} - output << identifier(c.semantic_scope()); - if (spec) - { - output << spec.value().primary_template(); - write_specialization_arguments(output, spec.value()); - } - else - output << identifier(c.name()); - - if (c.is_final()) - output << whitespace << keyword("final"); - - if (!output.generate_definition() || c.is_declaration()) - output << punctuation(";") << newl; - else - { - write_bases(generator, output, c); - output << opening_brace; - output.indent(); - - auto need_sep = false; - auto last_access = - c.class_kind() == cpp_class_kind::class_t ? cpp_private : cpp_public; - auto last_written_access = last_access; - for (auto& member : c) - { - if (member.kind() == cpp_entity_kind::access_specifier_t) - { - auto& access = static_cast(member); - last_access = access.access_specifier(); - } - else if (output.options(member, last_access).is_set(code_generator::exclude)) - continue; - else - { - if (need_sep) - output << newl; - if (last_access != last_written_access) - { - write_access_specifier(output, last_access); - last_written_access = last_access; - } - need_sep = generate_code_impl(generator, member, last_access); - } - } - - output.container_end(); - - output.unindent(); - output << punctuation("};") << newl; - } - } - return static_cast(output); - } - - bool write_variable_base(code_generator::output& output, const cpp_variable_base& var, - const std::string& name) - { - detail::write_type(output, var.type(), name); - - if (var.default_value()) - { - output << operator_ws << punctuation("=") << operator_ws; - detail::write_expression(output, var.default_value().value()); - } - return static_cast(output); - } - - void write_storage_class(code_generator::output& output, cpp_storage_class_specifiers storage, - bool is_constexpr) - { - if (is_static(storage)) - output << keyword("static") << whitespace; - if (is_extern(storage)) - output << keyword("extern") << whitespace; - if (is_thread_local(storage)) - output << keyword("thread_local") << whitespace; - if (is_constexpr) - output << keyword("constexpr") << whitespace; - } - - bool generate_variable(code_generator& generator, const cpp_variable& var, - cpp_access_specifier_kind cur_access) - { - code_generator::output output(type_safe::ref(generator), type_safe::ref(var), cur_access); - if (output) - { - write_storage_class(output, var.storage_class(), var.is_constexpr()); - - write_variable_base(output, var, var.name()); - output << punctuation(";") << newl; - } - return static_cast(output); - } - - bool generate_member_variable(code_generator& generator, const cpp_member_variable& var, - cpp_access_specifier_kind cur_access) - { - code_generator::output output(type_safe::ref(generator), type_safe::ref(var), cur_access); - if (output) - { - if (var.is_mutable()) - output << keyword("mutable") << whitespace; - write_variable_base(output, var, var.name()); - output << punctuation(";") << newl; - } - return static_cast(output); - } - - bool generate_bitfield(code_generator& generator, const cpp_bitfield& var, - cpp_access_specifier_kind cur_access) - { - code_generator::output output(type_safe::ref(generator), type_safe::ref(var), cur_access); - if (output) - { - if (var.is_mutable()) - output << keyword("mutable") << whitespace; - write_variable_base(output, var, var.name()); - output << operator_ws << punctuation(":") << operator_ws - << int_literal(std::to_string(var.no_bits())); - output << punctuation(";") << newl; - } - return static_cast(output); - } - - bool generate_function_parameter(code_generator& generator, const cpp_function_parameter& param, - cpp_access_specifier_kind cur_access) - { - code_generator::output output(type_safe::ref(generator), type_safe::ref(param), cur_access); - if (output) - write_variable_base(output, param, param.name()); - return static_cast(output); - } - - void write_function_parameters(code_generator::output& output, const cpp_function_base& base) - { - output << punctuation("(") << bracket_ws; - auto need_sep = write_container(output, base.parameters(), comma, cpp_public); - if (base.is_variadic()) - { - if (need_sep) - output << comma; - output << punctuation("..."); - } - output << bracket_ws << punctuation(")"); - } - - void write_noexcept(code_generator::output& output, const cpp_function_base& base, bool need_ws) - { - if (!base.noexcept_condition()) - return; - else if (need_ws) - output << whitespace; - - auto& cond = base.noexcept_condition().value(); - if (cond.kind() == cpp_expression_kind::literal_t - && static_cast(cond).value() == "true") - output << keyword("noexcept"); - else - { - output << keyword("noexcept") << punctuation("(") << bracket_ws; - // update check when expression gets exposed - if (cond.kind() == cpp_expression_kind::unexposed_t - && static_cast(cond).expression().front().spelling - == "false") - output << keyword("false"); - else if (output.options().is_set(code_generator::exclude_noexcept_condition)) - output.excluded(base); - else - detail::write_expression(output, cond); - output << bracket_ws << punctuation(")"); - } - } - - void write_function_body(code_generator::output& output, const cpp_function_base& base, - bool is_pure_virtual) - { - switch (base.body_kind()) - { - case cpp_function_declaration: - case cpp_function_definition: - if (is_pure_virtual) - output << operator_ws << punctuation("=") << operator_ws << int_literal("0"); - output << punctuation(";") << newl; - break; - - case cpp_function_defaulted: - output << operator_ws << punctuation("=") << operator_ws << keyword("default") - << punctuation(";") << newl; - break; - case cpp_function_deleted: - output << operator_ws << punctuation("=") << operator_ws << keyword("delete") - << punctuation(";") << newl; - break; - } - } - - bool generate_function( - code_generator& generator, const cpp_function& func, cpp_access_specifier_kind cur_access, - type_safe::optional_ref spec = nullptr) - { - code_generator::output output(type_safe::ref(generator), type_safe::ref(func), cur_access); - if (output) - { - if (is_friended(func)) - output << keyword("friend") << whitespace; - write_storage_class(output, func.storage_class(), func.is_constexpr()); - - if (output.options() & code_generator::exclude_return) - output.excluded(func) << whitespace; - else if (detail::is_complex_type(func.return_type())) - output << keyword("auto") << whitespace; - else - { - detail::write_type(output, func.return_type(), ""); - output << whitespace; - } - - output << identifier(func.semantic_scope()); - if (spec) - { - output << spec.value().primary_template(); - write_specialization_arguments(output, spec.value()); - } - else - output << identifier(func.name()); - write_function_parameters(output, func); - write_noexcept(output, func, output.formatting().is_set(formatting_flags::operator_ws)); - - if (!(output.options() & code_generator::exclude_return) - && detail::is_complex_type(func.return_type())) - { - output << operator_ws << punctuation("->") << operator_ws; - detail::write_type(output, func.return_type(), ""); - } - write_function_body(output, func, false); - } - return static_cast(output); - } - - void write_prefix_virtual(code_generator::output& output, const cpp_virtual& virt) - { - if (is_virtual(virt)) - output << keyword("virtual") << whitespace; - } - - void write_suffix_virtual(code_generator::output& output, const cpp_virtual& virt, - bool is_definition) - { - if (is_definition) - // don't include it in definition - return; - - if (is_overriding(virt)) - output << whitespace << keyword("override"); - if (is_final(virt)) - output << whitespace << keyword("final"); - } - - bool write_cv_ref(code_generator::output& output, const cpp_member_function_base& base) - { - auto need_ws = false; - switch (base.cv_qualifier()) - { - case cpp_cv_none: - break; - case cpp_cv_const: - output << operator_ws << keyword("const"); - need_ws = true; - break; - case cpp_cv_volatile: - output << operator_ws << keyword("volatile"); - need_ws = true; - break; - case cpp_cv_const_volatile: - output << operator_ws << keyword("const") << whitespace << keyword("volatile"); - need_ws = true; - break; - } - - switch (base.ref_qualifier()) - { - case cpp_ref_none: - break; - case cpp_ref_lvalue: - output << operator_ws << punctuation("&"); - need_ws = false; - break; - case cpp_ref_rvalue: - output << operator_ws << punctuation("&&"); - need_ws = false; - break; - } - - return need_ws; - } - - bool generate_member_function( - code_generator& generator, const cpp_member_function& func, - cpp_access_specifier_kind cur_access, - type_safe::optional_ref spec = nullptr) - { - code_generator::output output(type_safe::ref(generator), type_safe::ref(func), cur_access); - if (output) - { - if (is_friended(func)) - output << keyword("friend") << whitespace; - if (func.is_constexpr()) - output << keyword("constexpr") << whitespace; - else - write_prefix_virtual(output, func.virtual_info()); - - if (output.options() & code_generator::exclude_return) - output.excluded(func) << whitespace; - else if (detail::is_complex_type(func.return_type())) - output << keyword("auto") << whitespace; - else - { - detail::write_type(output, func.return_type(), ""); - output << whitespace; - } - - output << identifier(func.semantic_scope()); - if (spec) - { - output << spec.value().primary_template(); - write_specialization_arguments(output, spec.value()); - } - else - output << identifier(func.name()); - write_function_parameters(output, func); - auto need_ws = write_cv_ref(output, func); - write_noexcept(output, func, - need_ws || output.formatting().is_set(formatting_flags::operator_ws)); - - if (!(output.options() & code_generator::exclude_return) - && detail::is_complex_type(func.return_type())) - { - output << operator_ws << punctuation("->") << operator_ws; - detail::write_type(output, func.return_type(), ""); - } - - write_suffix_virtual(output, func.virtual_info(), func.is_definition()); - write_function_body(output, func, is_pure(func.virtual_info())); - } - return static_cast(output); - } - - bool generate_conversion_op(code_generator& generator, const cpp_conversion_op& op, - cpp_access_specifier_kind cur_access) - { - code_generator::output output(type_safe::ref(generator), type_safe::ref(op), cur_access); - if (output) - { - if (is_friended(op)) - output << keyword("friend") << whitespace; - if (op.is_explicit()) - output << keyword("explicit") << whitespace; - if (op.is_constexpr()) - output << keyword("constexpr") << whitespace; - else - write_prefix_virtual(output, op.virtual_info()); - - output << identifier(op.semantic_scope()); - - auto pos = op.name().find("operator"); - output << identifier(op.name().substr(0u, pos)) << keyword("operator") << whitespace; - if (output.options() & code_generator::exclude_return) - output.excluded(op); - else - detail::write_type(output, op.return_type(), ""); - - output << punctuation("(") << punctuation(")"); - auto need_ws = write_cv_ref(output, op); - write_noexcept(output, op, - need_ws || output.formatting().is_set(formatting_flags::operator_ws)); - - write_suffix_virtual(output, op.virtual_info(), op.is_definition()); - write_function_body(output, op, is_pure(op.virtual_info())); - } - return static_cast(output); - } - - bool generate_constructor(code_generator& generator, const cpp_constructor& ctor, - cpp_access_specifier_kind cur_access) - { - code_generator::output output(type_safe::ref(generator), type_safe::ref(ctor), cur_access); - if (output) - { - if (is_friended(ctor)) - output << keyword("friend") << whitespace; - if (ctor.is_explicit()) - output << keyword("explicit") << whitespace; - if (ctor.is_constexpr()) - output << keyword("constexpr") << whitespace; - - output << identifier(ctor.semantic_scope()) << identifier(ctor.name()); - write_function_parameters(output, ctor); - write_noexcept(output, ctor, output.formatting().is_set(formatting_flags::operator_ws)); - - write_function_body(output, ctor, false); - } - return static_cast(output); - } - - bool generate_destructor(code_generator& generator, const cpp_destructor& dtor, +bool generate_alias_template(code_generator& generator, const cpp_alias_template& alias, cpp_access_specifier_kind cur_access) +{ + code_generator::output output(type_safe::ref(generator), type_safe::ref(alias), cur_access); + if (output) { - code_generator::output output(type_safe::ref(generator), type_safe::ref(dtor), cur_access); - if (output) - { - if (is_friended(dtor)) - output << keyword("friend") << whitespace; - write_prefix_virtual(output, dtor.virtual_info()); - output << identifier(dtor.semantic_scope()) << identifier(dtor.name()) - << punctuation("(") << punctuation(")"); - write_noexcept(output, dtor, output.formatting().is_set(formatting_flags::operator_ws)); - - write_suffix_virtual(output, dtor.virtual_info(), dtor.is_definition()); - write_function_body(output, dtor, is_pure(dtor.virtual_info())); - } - return static_cast(output); + write_template_parameters(output, alias, true); + generate_code_impl(generator, alias.type_alias(), cur_access); } + return static_cast(output); +} - bool generate_function_base(code_generator& generator, const cpp_function_base& base, - cpp_access_specifier_kind cur_access, - const cpp_template_specialization& spec) - { - switch (base.kind()) - { - case cpp_entity_kind::function_t: - return generate_function(generator, static_cast(base), cur_access, - type_safe::ref(spec)); - case cpp_entity_kind::member_function_t: - return generate_member_function(generator, - static_cast(base), - cur_access, type_safe::ref(spec)); - case cpp_entity_kind::conversion_op_t: - return generate_conversion_op(generator, static_cast(base), - cur_access); - case cpp_entity_kind::constructor_t: - return generate_constructor(generator, static_cast(base), - cur_access); - - default: - DEBUG_UNREACHABLE(detail::assert_handler{}); - break; - } - return false; - } - - bool generate_friend(code_generator& generator, const cpp_friend& f, - cpp_access_specifier_kind cur_access) - { - code_generator::output output(type_safe::ref(generator), type_safe::ref(f), cur_access); - if (output) - { - if (auto e = f.entity()) - generate_code_impl(generator, e.value(), cur_access); - else if (auto type = f.type()) - { - output << keyword("friend") << whitespace; - detail::write_type(output, type.value(), ""); - output << punctuation(";"); - } - else - DEBUG_UNREACHABLE(detail::assert_handler{}); - } - return static_cast(output); - } - - bool generate_template_type_parameter(code_generator& generator, - const cpp_template_type_parameter& param, - cpp_access_specifier_kind cur_access) - { - code_generator::output output(type_safe::ref(generator), type_safe::ref(param), cur_access); - if (output) - { - output << keyword(to_string(param.keyword())); - if (param.is_variadic()) - output << operator_ws << punctuation("..."); - if (!param.name().empty()) - output << whitespace << identifier(param.name()); - if (param.default_type()) - { - output << operator_ws << punctuation("=") << operator_ws; - detail::write_type(output, param.default_type().value(), ""); - } - } - return static_cast(output); - } - - bool generate_non_type_template_parameter(code_generator& generator, - const cpp_non_type_template_parameter& param, - cpp_access_specifier_kind cur_access) - { - code_generator::output output(type_safe::ref(generator), type_safe::ref(param), cur_access); - if (output) - { - detail::write_type(output, param.type(), param.name(), param.is_variadic()); - if (param.default_value()) - { - output << operator_ws << punctuation("=") << operator_ws; - detail::write_expression(output, param.default_value().value()); - } - } - return static_cast(output); - } - - bool generate_template_template_parameter(code_generator& generator, - const cpp_template_template_parameter& param, - cpp_access_specifier_kind cur_access) - { - code_generator::output output(type_safe::ref(generator), type_safe::ref(param), cur_access); - if (output) - { - output << keyword("template") << operator_ws << punctuation("<") << bracket_ws; - write_container(output, param.parameters(), punctuation(","), cur_access); - output << bracket_ws << punctuation(">") << operator_ws - << keyword(to_string(param.keyword())); - if (param.is_variadic()) - output << operator_ws << punctuation("..."); - output << whitespace << identifier(param.name()); - if (param.default_template()) - output << operator_ws << punctuation("=") << operator_ws - << param.default_template().value(); - } - return static_cast(output); - } - - void write_template_parameters(code_generator::output& output, const cpp_template& templ, - bool hide_if_empty) - { - if (!hide_if_empty) - output << keyword("template") << operator_ws << punctuation("<") << bracket_ws; - - auto need_sep = false; - auto need_header = hide_if_empty; - for (auto& param : templ.parameters()) - { - auto is_excluded = output.options(param, cpp_public).is_set(code_generator::exclude); - if (!is_excluded) - { - if (need_header) - { - need_header = false; - output << keyword("template") << operator_ws << punctuation("<") << bracket_ws; - } - else if (need_sep) - output << comma; - need_sep = generate_code_impl(*output.generator(), param, cpp_public); - } - } - - if (!need_header) - output << bracket_ws << punctuation(">") << newl; - } - - bool generate_alias_template(code_generator& generator, const cpp_alias_template& alias, - cpp_access_specifier_kind cur_access) - { - code_generator::output output(type_safe::ref(generator), type_safe::ref(alias), cur_access); - if (output) - { - write_template_parameters(output, alias, true); - generate_code_impl(generator, alias.type_alias(), cur_access); - } - return static_cast(output); - } - - bool generate_variable_template(code_generator& generator, const cpp_variable_template& var, - cpp_access_specifier_kind cur_access) - { - code_generator::output output(type_safe::ref(generator), type_safe::ref(var), cur_access); - if (output) - { - write_template_parameters(output, var, true); - generate_code_impl(generator, var.variable(), cur_access); - } - return static_cast(output); - } - - bool generate_function_template(code_generator& generator, const cpp_function_template& func, - cpp_access_specifier_kind cur_access) - { - code_generator::output output(type_safe::ref(generator), type_safe::ref(func), cur_access); - if (output) - { - write_template_parameters(output, func, true); - generate_code_impl(generator, func.function(), cur_access); - } - return static_cast(output); - } - - bool generate_function_template_specialization(code_generator& generator, - const cpp_function_template_specialization& func, - cpp_access_specifier_kind cur_access) - { - code_generator::output output(type_safe::ref(generator), type_safe::ref(func), cur_access); - if (output) - { - DEBUG_ASSERT(func.is_full_specialization(), detail::assert_handler{}); - if (!is_friended(func)) - // don't write template parameters in friend - write_template_parameters(output, func, false); - generate_function_base(generator, func.function(), cur_access, func); - } - return static_cast(output); - } - - bool generate_class_template(code_generator& generator, const cpp_class_template& templ, - cpp_access_specifier_kind cur_access) - { - code_generator::output output(type_safe::ref(generator), type_safe::ref(templ), cur_access); - if (output) - { - write_template_parameters(output, templ, true); - generate_class(generator, templ.class_(), cur_access); - } - return static_cast(output); - } - - bool generate_class_template_specialization(code_generator& generator, - const cpp_class_template_specialization& templ, - cpp_access_specifier_kind cur_access) - { - code_generator::output output(type_safe::ref(generator), type_safe::ref(templ), cur_access); - if (output) - { - write_template_parameters(output, templ, false); - generate_class(generator, templ.class_(), cur_access, type_safe::ref(templ)); - } - return static_cast(output); - } - - bool generate_static_assert(code_generator& generator, const cpp_static_assert& assert, +bool generate_variable_template(code_generator& generator, const cpp_variable_template& var, cpp_access_specifier_kind cur_access) +{ + code_generator::output output(type_safe::ref(generator), type_safe::ref(var), cur_access); + if (output) { - code_generator::output output(type_safe::ref(generator), type_safe::ref(assert), - cur_access); - if (output) - { - output << keyword("static_assert") << punctuation("(") << bracket_ws; - detail::write_expression(output, assert.expression()); - output << comma << string_literal('"' + assert.message() + '"'); - output << bracket_ws << punctuation(");") << newl; - } - return static_cast(output); + write_template_parameters(output, var, true); + generate_code_impl(generator, var.variable(), cur_access); } + return static_cast(output); +} - bool generate_unexposed(code_generator& generator, const cpp_unexposed_entity& entity, - cpp_access_specifier_kind cur_access) +bool generate_function_template(code_generator& generator, const cpp_function_template& func, + cpp_access_specifier_kind cur_access) +{ + code_generator::output output(type_safe::ref(generator), type_safe::ref(func), cur_access); + if (output) { - code_generator::output output(type_safe::ref(generator), type_safe::ref(entity), - cur_access); - if (output) - { - detail::write_token_string(output, entity.spelling()); - output << newl; - } - return static_cast(output); + write_template_parameters(output, func, true); + generate_code_impl(generator, func.function(), cur_access); } + return static_cast(output); +} - bool generate_code_impl(code_generator& generator, const cpp_entity& e, - cpp_access_specifier_kind cur_access) +bool generate_function_template_specialization(code_generator& generator, + const cpp_function_template_specialization& func, + cpp_access_specifier_kind cur_access) +{ + code_generator::output output(type_safe::ref(generator), type_safe::ref(func), cur_access); + if (output) + { + DEBUG_ASSERT(func.is_full_specialization(), detail::assert_handler{}); + if (!is_friended(func)) + // don't write template parameters in friend + write_template_parameters(output, func, false); + generate_function_base(generator, func.function(), cur_access, func); + } + return static_cast(output); +} + +bool generate_class_template(code_generator& generator, const cpp_class_template& templ, + cpp_access_specifier_kind cur_access) +{ + code_generator::output output(type_safe::ref(generator), type_safe::ref(templ), cur_access); + if (output) + { + write_template_parameters(output, templ, true); + generate_class(generator, templ.class_(), cur_access); + } + return static_cast(output); +} + +bool generate_class_template_specialization(code_generator& generator, + const cpp_class_template_specialization& templ, + cpp_access_specifier_kind cur_access) +{ + code_generator::output output(type_safe::ref(generator), type_safe::ref(templ), cur_access); + if (output) + { + write_template_parameters(output, templ, false); + generate_class(generator, templ.class_(), cur_access, type_safe::ref(templ)); + } + return static_cast(output); +} + +bool generate_static_assert(code_generator& generator, const cpp_static_assert& assert, + cpp_access_specifier_kind cur_access) +{ + code_generator::output output(type_safe::ref(generator), type_safe::ref(assert), cur_access); + if (output) + { + output << keyword("static_assert") << punctuation("(") << bracket_ws; + detail::write_expression(output, assert.expression()); + output << comma << string_literal('"' + assert.message() + '"'); + output << bracket_ws << punctuation(");") << newl; + } + return static_cast(output); +} + +bool generate_unexposed(code_generator& generator, const cpp_unexposed_entity& entity, + cpp_access_specifier_kind cur_access) +{ + code_generator::output output(type_safe::ref(generator), type_safe::ref(entity), cur_access); + if (output) + { + detail::write_token_string(output, entity.spelling()); + output << newl; + } + return static_cast(output); +} + +bool generate_code_impl(code_generator& generator, const cpp_entity& e, + cpp_access_specifier_kind cur_access) +{ + switch (e.kind()) { - switch (e.kind()) - { #define CPPAST_DETAIL_HANDLE(Name) \ case cpp_entity_kind::Name##_t: \ return generate_##Name(generator, static_cast(e), cur_access); - CPPAST_DETAIL_HANDLE(file) + CPPAST_DETAIL_HANDLE(file) - CPPAST_DETAIL_HANDLE(macro_parameter) - CPPAST_DETAIL_HANDLE(macro_definition) - CPPAST_DETAIL_HANDLE(include_directive) + CPPAST_DETAIL_HANDLE(macro_parameter) + CPPAST_DETAIL_HANDLE(macro_definition) + CPPAST_DETAIL_HANDLE(include_directive) - CPPAST_DETAIL_HANDLE(language_linkage) - CPPAST_DETAIL_HANDLE(namespace) - CPPAST_DETAIL_HANDLE(namespace_alias) - CPPAST_DETAIL_HANDLE(using_directive) - CPPAST_DETAIL_HANDLE(using_declaration) + CPPAST_DETAIL_HANDLE(language_linkage) + CPPAST_DETAIL_HANDLE(namespace) + CPPAST_DETAIL_HANDLE(namespace_alias) + CPPAST_DETAIL_HANDLE(using_directive) + CPPAST_DETAIL_HANDLE(using_declaration) - CPPAST_DETAIL_HANDLE(type_alias) + CPPAST_DETAIL_HANDLE(type_alias) - CPPAST_DETAIL_HANDLE(enum) - CPPAST_DETAIL_HANDLE(enum_value) + CPPAST_DETAIL_HANDLE(enum) + CPPAST_DETAIL_HANDLE(enum_value) - CPPAST_DETAIL_HANDLE(class) - CPPAST_DETAIL_HANDLE(access_specifier) - CPPAST_DETAIL_HANDLE(base_class) + CPPAST_DETAIL_HANDLE(class) + CPPAST_DETAIL_HANDLE(access_specifier) + CPPAST_DETAIL_HANDLE(base_class) - CPPAST_DETAIL_HANDLE(variable) - CPPAST_DETAIL_HANDLE(member_variable) - CPPAST_DETAIL_HANDLE(bitfield) + CPPAST_DETAIL_HANDLE(variable) + CPPAST_DETAIL_HANDLE(member_variable) + CPPAST_DETAIL_HANDLE(bitfield) - CPPAST_DETAIL_HANDLE(function_parameter) - CPPAST_DETAIL_HANDLE(function) - CPPAST_DETAIL_HANDLE(member_function) - CPPAST_DETAIL_HANDLE(conversion_op) - CPPAST_DETAIL_HANDLE(constructor) - CPPAST_DETAIL_HANDLE(destructor) + CPPAST_DETAIL_HANDLE(function_parameter) + CPPAST_DETAIL_HANDLE(function) + CPPAST_DETAIL_HANDLE(member_function) + CPPAST_DETAIL_HANDLE(conversion_op) + CPPAST_DETAIL_HANDLE(constructor) + CPPAST_DETAIL_HANDLE(destructor) - CPPAST_DETAIL_HANDLE(friend) + CPPAST_DETAIL_HANDLE(friend) - CPPAST_DETAIL_HANDLE(template_type_parameter) - CPPAST_DETAIL_HANDLE(non_type_template_parameter) - CPPAST_DETAIL_HANDLE(template_template_parameter) + CPPAST_DETAIL_HANDLE(template_type_parameter) + CPPAST_DETAIL_HANDLE(non_type_template_parameter) + CPPAST_DETAIL_HANDLE(template_template_parameter) - CPPAST_DETAIL_HANDLE(alias_template) - CPPAST_DETAIL_HANDLE(variable_template) - CPPAST_DETAIL_HANDLE(function_template) - CPPAST_DETAIL_HANDLE(function_template_specialization) - CPPAST_DETAIL_HANDLE(class_template) - CPPAST_DETAIL_HANDLE(class_template_specialization) + CPPAST_DETAIL_HANDLE(alias_template) + CPPAST_DETAIL_HANDLE(variable_template) + CPPAST_DETAIL_HANDLE(function_template) + CPPAST_DETAIL_HANDLE(function_template_specialization) + CPPAST_DETAIL_HANDLE(class_template) + CPPAST_DETAIL_HANDLE(class_template_specialization) - CPPAST_DETAIL_HANDLE(static_assert) + CPPAST_DETAIL_HANDLE(static_assert) - case cpp_entity_kind::unexposed_t: - return generate_unexposed(generator, static_cast(e), - cur_access); + case cpp_entity_kind::unexposed_t: + return generate_unexposed(generator, static_cast(e), + cur_access); #undef CPPAST_DETAIL_HANDLE - case cpp_entity_kind::count: - DEBUG_UNREACHABLE(detail::assert_handler{}); - break; - } - - return false; + case cpp_entity_kind::count: + DEBUG_UNREACHABLE(detail::assert_handler{}); + break; } + + return false; +} } // namespace bool code_generator::generate_code(const cpp_entity& entity) diff --git a/src/cpp_attribute.cpp b/src/cpp_attribute.cpp index c20e636..ef51dd5 100644 --- a/src/cpp_attribute.cpp +++ b/src/cpp_attribute.cpp @@ -12,32 +12,32 @@ using namespace cppast; 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::carries_dependency: - return "carries_dependency"; - case cpp_attribute_kind::deprecated: - return "deprecated"; - case cpp_attribute_kind::fallthrough: - return "fallthrough"; - case cpp_attribute_kind::maybe_unused: - return "maybe_unused"; - case cpp_attribute_kind::nodiscard: - return "nodiscard"; - case cpp_attribute_kind::noreturn: - return "noreturn"; + case cpp_attribute_kind::alignas_: + return "alignas"; + case cpp_attribute_kind::carries_dependency: + return "carries_dependency"; + case cpp_attribute_kind::deprecated: + return "deprecated"; + case cpp_attribute_kind::fallthrough: + return "fallthrough"; + case cpp_attribute_kind::maybe_unused: + return "maybe_unused"; + case cpp_attribute_kind::nodiscard: + return "nodiscard"; + case cpp_attribute_kind::noreturn: + return "noreturn"; - case cpp_attribute_kind::unknown: - return "unknown"; - } - - return ""; + case cpp_attribute_kind::unknown: + return "unknown"; } + + return ""; } +} // namespace cpp_attribute::cpp_attribute(cpp_attribute_kind kind, type_safe::optional arguments) @@ -49,13 +49,13 @@ cpp_attribute::cpp_attribute(cpp_attribute_kind kind, type_safe::optional_ref cppast::has_attribute( const cpp_attribute_list& attributes, const std::string& name) { - auto iter = - std::find_if(attributes.begin(), attributes.end(), [&](const cpp_attribute& attribute) { - if (attribute.scope()) - return attribute.scope().value() + "::" + attribute.name() == name; - else - return attribute.name() == name; - }); + auto iter + = std::find_if(attributes.begin(), attributes.end(), [&](const cpp_attribute& attribute) { + if (attribute.scope()) + return attribute.scope().value() + "::" + attribute.name() == name; + else + return attribute.name() == name; + }); if (iter == attributes.end()) return nullptr; @@ -66,9 +66,9 @@ type_safe::optional_ref cppast::has_attribute( type_safe::optional_ref cppast::has_attribute( const cpp_attribute_list& attributes, cpp_attribute_kind kind) { - auto iter = - std::find_if(attributes.begin(), attributes.end(), - [&](const cpp_attribute& attribute) { return attribute.kind() == kind; }); + auto iter + = std::find_if(attributes.begin(), attributes.end(), + [&](const cpp_attribute& attribute) { return attribute.kind() == kind; }); if (iter == attributes.end()) return nullptr; diff --git a/src/cpp_class.cpp b/src/cpp_class.cpp index 9b2baa6..c93d005 100644 --- a/src/cpp_class.cpp +++ b/src/cpp_class.cpp @@ -4,10 +4,10 @@ #include -#include -#include #include #include +#include +#include using namespace cppast; @@ -103,70 +103,69 @@ cpp_entity_kind cpp_class::do_get_entity_kind() const noexcept 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(type).entity(); - return cpp_entity_ref(ref.id()[0u], ref.name()); - } - else if (type.kind() == cpp_type_kind::template_instantiation_t) - { - auto& ref = - static_cast(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(""), ""); + auto& ref = static_cast(type).entity(); + return cpp_entity_ref(ref.id()[0u], ref.name()); + } + else if (type.kind() == cpp_type_kind::template_instantiation_t) + { + auto& ref = static_cast(type).primary_template(); + return cpp_entity_ref(ref.id()[0u], ref.name()); } - type_safe::optional_ref get_entity_impl(const cpp_entity_index& index, - const cpp_entity_ref& ref) - { - auto result = ref.get(index); - if (result.empty()) - return nullptr; - DEBUG_ASSERT(result.size() == 1u, detail::assert_handler{}); + 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(""), ""); +} - auto entity = result.front(); - if (entity->kind() == cpp_class_template::kind()) - return type_safe::ref(static_cast(*entity).class_()); - else if (entity->kind() == cpp_class_template_specialization::kind()) - return type_safe::ref( - static_cast(*entity).class_()); - else - return entity; +type_safe::optional_ref get_entity_impl(const cpp_entity_index& index, + const cpp_entity_ref& ref) +{ + auto result = ref.get(index); + if (result.empty()) + return nullptr; + DEBUG_ASSERT(result.size() == 1u, detail::assert_handler{}); + + auto entity = result.front(); + if (entity->kind() == cpp_class_template::kind()) + return type_safe::ref(static_cast(*entity).class_()); + else if (entity->kind() == cpp_class_template_specialization::kind()) + return type_safe::ref( + static_cast(*entity).class_()); + else + return entity; +} + +type_safe::optional_ref 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(entity.value()); + return get_class_impl(index, get_type_ref(alias.type_alias().underlying_type())); } - - type_safe::optional_ref get_class_impl(const cpp_entity_index& index, - const cpp_entity_ref& ref) + else if (entity.value().kind() == cpp_type_alias::kind()) { - auto entity = get_entity_impl(index, ref); - if (!entity) - return nullptr; - - if (entity.value().kind() == cpp_alias_template::kind()) - { - auto& alias = static_cast(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(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(entity.value())); - } + auto& alias = static_cast(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(entity.value())); } } +} // namespace type_safe::optional_ref cppast::get_class(const cpp_entity_index& index, const cpp_base_class& base) diff --git a/src/cpp_entity_index.cpp b/src/cpp_entity_index.cpp index 67cabf0..6ff8f83 100644 --- a/src/cpp_entity_index.cpp +++ b/src/cpp_entity_index.cpp @@ -4,17 +4,16 @@ #include -#include #include #include #include +#include using namespace cppast; cpp_entity_index::duplicate_definition_error::duplicate_definition_error() : std::logic_error("duplicate registration of entity definition") -{ -} +{} void cpp_entity_index::register_definition(cpp_entity_id id, type_safe::object_ref entity) const diff --git a/src/cpp_enum.cpp b/src/cpp_enum.cpp index bca2526..1ccf17c 100644 --- a/src/cpp_enum.cpp +++ b/src/cpp_enum.cpp @@ -17,8 +17,8 @@ std::unique_ptr cpp_enum_value::build(const cpp_entity_index& id std::string name, std::unique_ptr value) { - auto result = - std::unique_ptr(new cpp_enum_value(std::move(name), std::move(value))); + auto result + = std::unique_ptr(new cpp_enum_value(std::move(name), std::move(value))); idx.register_definition(std::move(id), type_safe::ref(*result)); return result; } diff --git a/src/cpp_expression.cpp b/src/cpp_expression.cpp index ff7ccf7..90dbe00 100644 --- a/src/cpp_expression.cpp +++ b/src/cpp_expression.cpp @@ -8,77 +8,77 @@ using namespace cppast; 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(expr.type()).builtin_type_kind(); + else if (expr.type().kind() == cpp_type_kind::pointer_t) { - auto type_kind = cpp_void; - if (expr.type().kind() == cpp_type_kind::builtin_t) - type_kind = static_cast(expr.type()).builtin_type_kind(); - else if (expr.type().kind() == cpp_type_kind::pointer_t) + auto& pointee = static_cast(expr.type()).pointee(); + if (pointee.kind() == cpp_type_kind::builtin_t) { - auto& pointee = static_cast(expr.type()).pointee(); - if (pointee.kind() == cpp_type_kind::builtin_t) - { - auto& builtin_pointee = static_cast(pointee); - if (builtin_pointee.builtin_type_kind() == cpp_char - || builtin_pointee.builtin_type_kind() == cpp_wchar - || builtin_pointee.builtin_type_kind() == cpp_char16 - || builtin_pointee.builtin_type_kind() == cpp_char32) - // pointer to char aka string - type_kind = builtin_pointee.builtin_type_kind(); - } - } - - switch (type_kind) - { - case cpp_void: - output << token_seq(expr.value()); - break; - - case cpp_bool: - output << keyword(expr.value()); - break; - - case cpp_uchar: - case cpp_ushort: - case cpp_uint: - case cpp_ulong: - case cpp_ulonglong: - case cpp_uint128: - case cpp_schar: - case cpp_short: - case cpp_int: - case cpp_long: - case cpp_longlong: - case cpp_int128: - output << int_literal(expr.value()); - break; - - case cpp_float: - case cpp_double: - case cpp_longdouble: - case cpp_float128: - output << float_literal(expr.value()); - break; - - case cpp_char: - case cpp_wchar: - case cpp_char16: - case cpp_char32: - output << string_literal(expr.value()); - break; - - case cpp_nullptr: - output << keyword(expr.value()); - break; + auto& builtin_pointee = static_cast(pointee); + if (builtin_pointee.builtin_type_kind() == cpp_char + || builtin_pointee.builtin_type_kind() == cpp_wchar + || builtin_pointee.builtin_type_kind() == cpp_char16 + || builtin_pointee.builtin_type_kind() == cpp_char32) + // pointer to char aka string + type_kind = builtin_pointee.builtin_type_kind(); } } - 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) { switch (expr.kind()) diff --git a/src/cpp_forward_declarable.cpp b/src/cpp_forward_declarable.cpp index 802be0b..afa7665 100644 --- a/src/cpp_forward_declarable.cpp +++ b/src/cpp_forward_declarable.cpp @@ -15,73 +15,73 @@ using namespace cppast; namespace { - type_safe::optional_ref get_declarable(const cpp_entity& e) +type_safe::optional_ref get_declarable(const cpp_entity& e) +{ + switch (e.kind()) { - switch (e.kind()) - { - case cpp_entity_kind::enum_t: - return type_safe::ref(static_cast(e)); - case cpp_entity_kind::class_t: - return type_safe::ref(static_cast(e)); - case cpp_entity_kind::variable_t: - return type_safe::ref(static_cast(e)); - case cpp_entity_kind::function_t: - case cpp_entity_kind::member_function_t: - case cpp_entity_kind::conversion_op_t: - case cpp_entity_kind::constructor_t: - case cpp_entity_kind::destructor_t: - return type_safe::ref(static_cast(e)); - case cpp_entity_kind::function_template_t: - case cpp_entity_kind::function_template_specialization_t: - case cpp_entity_kind::class_template_t: - case cpp_entity_kind::class_template_specialization_t: - return get_declarable(*static_cast(e).begin()); + case cpp_entity_kind::enum_t: + return type_safe::ref(static_cast(e)); + case cpp_entity_kind::class_t: + return type_safe::ref(static_cast(e)); + case cpp_entity_kind::variable_t: + return type_safe::ref(static_cast(e)); + case cpp_entity_kind::function_t: + case cpp_entity_kind::member_function_t: + case cpp_entity_kind::conversion_op_t: + case cpp_entity_kind::constructor_t: + case cpp_entity_kind::destructor_t: + return type_safe::ref(static_cast(e)); + case cpp_entity_kind::function_template_t: + case cpp_entity_kind::function_template_specialization_t: + case cpp_entity_kind::class_template_t: + case cpp_entity_kind::class_template_specialization_t: + return get_declarable(*static_cast(e).begin()); - case cpp_entity_kind::file_t: - case cpp_entity_kind::macro_parameter_t: - case cpp_entity_kind::macro_definition_t: - case cpp_entity_kind::include_directive_t: - case cpp_entity_kind::language_linkage_t: - case cpp_entity_kind::namespace_t: - case cpp_entity_kind::namespace_alias_t: - case cpp_entity_kind::using_directive_t: - case cpp_entity_kind::using_declaration_t: - case cpp_entity_kind::type_alias_t: - case cpp_entity_kind::enum_value_t: - case cpp_entity_kind::access_specifier_t: - case cpp_entity_kind::base_class_t: - case cpp_entity_kind::member_variable_t: - case cpp_entity_kind::bitfield_t: - case cpp_entity_kind::function_parameter_t: - case cpp_entity_kind::friend_t: - case cpp_entity_kind::template_type_parameter_t: - case cpp_entity_kind::non_type_template_parameter_t: - case cpp_entity_kind::template_template_parameter_t: - case cpp_entity_kind::alias_template_t: - case cpp_entity_kind::variable_template_t: - case cpp_entity_kind::static_assert_t: - case cpp_entity_kind::unexposed_t: - return nullptr; - - case cpp_entity_kind::count: - break; - } - - DEBUG_UNREACHABLE(detail::assert_handler{}); + case cpp_entity_kind::file_t: + case cpp_entity_kind::macro_parameter_t: + case cpp_entity_kind::macro_definition_t: + case cpp_entity_kind::include_directive_t: + case cpp_entity_kind::language_linkage_t: + case cpp_entity_kind::namespace_t: + case cpp_entity_kind::namespace_alias_t: + case cpp_entity_kind::using_directive_t: + case cpp_entity_kind::using_declaration_t: + case cpp_entity_kind::type_alias_t: + case cpp_entity_kind::enum_value_t: + case cpp_entity_kind::access_specifier_t: + case cpp_entity_kind::base_class_t: + case cpp_entity_kind::member_variable_t: + case cpp_entity_kind::bitfield_t: + case cpp_entity_kind::function_parameter_t: + case cpp_entity_kind::friend_t: + case cpp_entity_kind::template_type_parameter_t: + case cpp_entity_kind::non_type_template_parameter_t: + case cpp_entity_kind::template_template_parameter_t: + case cpp_entity_kind::alias_template_t: + case cpp_entity_kind::variable_template_t: + case cpp_entity_kind::static_assert_t: + case cpp_entity_kind::unexposed_t: return nullptr; + + case cpp_entity_kind::count: + break; } - type_safe::optional_ref get_definition_impl(const cpp_entity_index& idx, - const cpp_entity& e) - { - auto declarable = get_declarable(e); - if (!declarable || declarable.value().is_definition()) - // 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()); - } + DEBUG_UNREACHABLE(detail::assert_handler{}); + return nullptr; +} + +type_safe::optional_ref get_definition_impl(const cpp_entity_index& idx, + const cpp_entity& e) +{ + auto declarable = get_declarable(e); + if (!declarable || declarable.value().is_definition()) + // 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 bool cppast::is_definition(const cpp_entity& e) noexcept diff --git a/src/cpp_token.cpp b/src/cpp_token.cpp index e1b1df1..65883a6 100644 --- a/src/cpp_token.cpp +++ b/src/cpp_token.cpp @@ -21,554 +21,554 @@ void cpp_token_string::builder::unmunch() namespace { - template - bool starts_with(const char* ptr, const char (&str)[N]) +template +bool starts_with(const char* ptr, const char (&str)[N]) +{ + return std::strncmp(ptr, str, N - 1u) == 0; +} + +bool starts_with(const char* ptr, const std::string& str) +{ + return std::strncmp(ptr, str.c_str(), str.size()) == 0; +} + +template +bool bump_if(const char*& ptr, const char (&str)[N]) +{ + if (starts_with(ptr, str)) { - return std::strncmp(ptr, str, N - 1u) == 0; + ptr += N - 1; + return true; } + else + return false; +} - bool starts_with(const char* ptr, const std::string& str) +bool bump_if(const char*& ptr, const std::string& str) +{ + if (starts_with(ptr, str)) { - return std::strncmp(ptr, str.c_str(), str.size()) == 0; + ptr += str.size(); + return true; } + else + return false; +} - template - bool bump_if(const char*& ptr, const char (&str)[N]) - { - if (starts_with(ptr, str)) - { - ptr += N - 1; - return true; - } - else - return false; - } +bool is_identifier_nondigit(char c) +{ + // assume ASCII + if (c >= 'a' && c <= 'z') + return true; + else if (c >= 'A' && c <= 'Z') + return true; + else if (c == '_') + return true; + else + // technically \uXXX is allowed as well, but I haven't seen that used ever + return false; +} - bool bump_if(const char*& ptr, const std::string& str) - { - if (starts_with(ptr, str)) - { - ptr += str.size(); - return true; - } - else - return false; - } +bool is_digit(char c) +{ + return c >= '0' && c <= '9'; +} - bool is_identifier_nondigit(char c) - { - // assume ASCII - if (c >= 'a' && c <= 'z') - return true; - else if (c >= 'A' && c <= 'Z') - return true; - else if (c == '_') - return true; - else - // technically \uXXX is allowed as well, but I haven't seen that used ever - return false; - } +bool is_hexadecimal_digit(char c) +{ + return is_digit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); +} - bool is_digit(char c) - { - return c >= '0' && c <= '9'; - } - - bool is_hexadecimal_digit(char c) - { - return is_digit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); - } - - type_safe::optional bump_identifier(const char*& ptr) - { - if (is_identifier_nondigit(*ptr)) - { - std::string result; - result += *ptr++; - - while (is_identifier_nondigit(*ptr) || is_digit(*ptr)) - result += *ptr++; - - return result; - } - else - return type_safe::nullopt; - } - - type_safe::optional identifier_token(const char*& ptr) - { - auto identifier = bump_identifier(ptr); - if (!identifier) - return type_safe::nullopt; - - static constexpr const char* keywords[] = {"alignas", - "alignof", - "asm", - "auto", - "bool", - "break", - "case", - "catch", - "char", - "char16_t", - "char32_t", - "class", - "const", - "constexpr", - "const_cast", - "continue", - "decltype", - "default", - "delete", - "do", - "double", - "dynamic_cast", - "else", - "enum", - "explicit", - "export", - "extern", - "false", - "float", - "for", - "friend", - "goto", - "if", - "inline", - "int", - "long", - "mutable", - "namespace", - "new", - "noexcept", - "nullptr", - "operator", - "private", - "protected", - "public", - "register", - "reinterpret_cast", - "return", - "short", - "signed", - "sizeof", - "static", - "static_assert", - "static_cast", - "struct", - "switch", - "template", - "this", - "thread_local", - "throw", - "true", - "try", - "typedef", - "typeid", - "typename", - "union", - "unsigned", - "using", - "virtual", - "void", - "volatile", - "wchar_t", - "while"}; - auto find_keyword = std::find(std::begin(keywords), std::end(keywords), identifier.value()); - if (find_keyword != std::end(keywords)) - return cpp_token(cpp_token_kind::keyword, identifier.value()); - else if (identifier == "and") - return cpp_token(cpp_token_kind::punctuation, "&&"); - else if (identifier == "and_eq") - return cpp_token(cpp_token_kind::punctuation, "&="); - else if (identifier == "bitand") - return cpp_token(cpp_token_kind::punctuation, "&"); - else if (identifier == "bitor") - return cpp_token(cpp_token_kind::punctuation, "|"); - else if (identifier == "compl") - return cpp_token(cpp_token_kind::punctuation, "~"); - else if (identifier == "not") - return cpp_token(cpp_token_kind::punctuation, "!"); - else if (identifier == "not_eq") - return cpp_token(cpp_token_kind::punctuation, "!="); - else if (identifier == "or") - return cpp_token(cpp_token_kind::punctuation, "||"); - else if (identifier == "or_eq") - return cpp_token(cpp_token_kind::punctuation, "|="); - else if (identifier == "xor") - return cpp_token(cpp_token_kind::punctuation, "^"); - else if (identifier == "xor_eq") - return cpp_token(cpp_token_kind::punctuation, "^="); - else - return cpp_token(cpp_token_kind::identifier, identifier.value()); - } - - void append_udl_suffix(std::string& literal, const char*& ptr) - { - if (auto id = identifier_token(ptr)) - literal += id.value().spelling; - } - - template - std::string parse_digit_sequence(const char*& ptr, DigitPredicate is_digit) +type_safe::optional bump_identifier(const char*& ptr) +{ + if (is_identifier_nondigit(*ptr)) { std::string result; - for (; is_digit(*ptr) || *ptr == '\''; ++ptr) - if (*ptr != '\'') - result += *ptr; - DEBUG_ASSERT(result.empty() || result.back() != '\'', detail::assert_handler{}); + result += *ptr++; + + while (is_identifier_nondigit(*ptr) || is_digit(*ptr)) + result += *ptr++; + return result; } + else + return type_safe::nullopt; +} - void append_integer_suffix(std::string& literal, const char*& ptr) - { - auto append_unsigned_suffix = [](std::string& literal, const char*& ptr) { - if (*ptr == 'u' || *ptr == 'U') - { - literal += *ptr++; - return true; - } - else - return false; - }; - auto append_long_suffix = [](std::string& literal, const char*& ptr) { - if (starts_with(ptr, "ll") || starts_with(ptr, "LL")) - { - literal += *ptr++; - literal += *ptr++; - return true; - } - else if (*ptr == 'l' || *ptr == 'L') - { - literal += *ptr++; - return true; - } - else - return false; - }; +type_safe::optional identifier_token(const char*& ptr) +{ + auto identifier = bump_identifier(ptr); + if (!identifier) + return type_safe::nullopt; - if (append_unsigned_suffix(literal, ptr)) - append_long_suffix(literal, ptr); - else if (append_long_suffix(literal, ptr)) - append_unsigned_suffix(literal, ptr); - else - append_udl_suffix(literal, ptr); - } + static constexpr const char* keywords[] = {"alignas", + "alignof", + "asm", + "auto", + "bool", + "break", + "case", + "catch", + "char", + "char16_t", + "char32_t", + "class", + "const", + "constexpr", + "const_cast", + "continue", + "decltype", + "default", + "delete", + "do", + "double", + "dynamic_cast", + "else", + "enum", + "explicit", + "export", + "extern", + "false", + "float", + "for", + "friend", + "goto", + "if", + "inline", + "int", + "long", + "mutable", + "namespace", + "new", + "noexcept", + "nullptr", + "operator", + "private", + "protected", + "public", + "register", + "reinterpret_cast", + "return", + "short", + "signed", + "sizeof", + "static", + "static_assert", + "static_cast", + "struct", + "switch", + "template", + "this", + "thread_local", + "throw", + "true", + "try", + "typedef", + "typeid", + "typename", + "union", + "unsigned", + "using", + "virtual", + "void", + "volatile", + "wchar_t", + "while"}; + auto find_keyword = std::find(std::begin(keywords), std::end(keywords), identifier.value()); + if (find_keyword != std::end(keywords)) + return cpp_token(cpp_token_kind::keyword, identifier.value()); + else if (identifier == "and") + return cpp_token(cpp_token_kind::punctuation, "&&"); + else if (identifier == "and_eq") + return cpp_token(cpp_token_kind::punctuation, "&="); + else if (identifier == "bitand") + return cpp_token(cpp_token_kind::punctuation, "&"); + else if (identifier == "bitor") + return cpp_token(cpp_token_kind::punctuation, "|"); + else if (identifier == "compl") + return cpp_token(cpp_token_kind::punctuation, "~"); + else if (identifier == "not") + return cpp_token(cpp_token_kind::punctuation, "!"); + else if (identifier == "not_eq") + return cpp_token(cpp_token_kind::punctuation, "!="); + else if (identifier == "or") + return cpp_token(cpp_token_kind::punctuation, "||"); + else if (identifier == "or_eq") + return cpp_token(cpp_token_kind::punctuation, "|="); + else if (identifier == "xor") + return cpp_token(cpp_token_kind::punctuation, "^"); + else if (identifier == "xor_eq") + return cpp_token(cpp_token_kind::punctuation, "^="); + else + return cpp_token(cpp_token_kind::identifier, identifier.value()); +} - void append_floating_point_suffix(std::string& literal, const char*& ptr) - { - if (*ptr == 'f' || *ptr == 'F') +void append_udl_suffix(std::string& literal, const char*& ptr) +{ + if (auto id = identifier_token(ptr)) + literal += id.value().spelling; +} + +template +std::string parse_digit_sequence(const char*& ptr, DigitPredicate is_digit) +{ + std::string result; + for (; is_digit(*ptr) || *ptr == '\''; ++ptr) + if (*ptr != '\'') + result += *ptr; + DEBUG_ASSERT(result.empty() || result.back() != '\'', detail::assert_handler{}); + return result; +} + +void append_integer_suffix(std::string& literal, const char*& ptr) +{ + auto append_unsigned_suffix = [](std::string& literal, const char*& ptr) { + if (*ptr == 'u' || *ptr == 'U') + { literal += *ptr++; + return true; + } + else + return false; + }; + auto append_long_suffix = [](std::string& literal, const char*& ptr) { + if (starts_with(ptr, "ll") || starts_with(ptr, "LL")) + { + literal += *ptr++; + literal += *ptr++; + return true; + } else if (*ptr == 'l' || *ptr == 'L') + { literal += *ptr++; - else - append_udl_suffix(literal, ptr); - } - - type_safe::optional parse_floating_point_exponent(const char*& ptr) - { - if (*ptr == 'e' || *ptr == 'E' || *ptr == 'p' || *ptr == 'P') - { - std::string result; - result += *ptr++; - if (*ptr == '+' || *ptr == '-') - result += *ptr++; - - result += parse_digit_sequence(ptr, &is_digit); - return result; + return true; } else - return type_safe::nullopt; - } + return false; + }; - type_safe::optional numeric_literal_token(const char*& ptr) + if (append_unsigned_suffix(literal, ptr)) + append_long_suffix(literal, ptr); + else if (append_long_suffix(literal, ptr)) + append_unsigned_suffix(literal, ptr); + else + append_udl_suffix(literal, ptr); +} + +void append_floating_point_suffix(std::string& literal, const char*& ptr) +{ + if (*ptr == 'f' || *ptr == 'F') + literal += *ptr++; + else if (*ptr == 'l' || *ptr == 'L') + literal += *ptr++; + else + append_udl_suffix(literal, ptr); +} + +type_safe::optional parse_floating_point_exponent(const char*& ptr) +{ + if (*ptr == 'e' || *ptr == 'E' || *ptr == 'p' || *ptr == 'P') { - if (starts_with(ptr, "0b") || starts_with(ptr, "0B")) // binary integer literal + std::string result; + result += *ptr++; + if (*ptr == '+' || *ptr == '-') + result += *ptr++; + + result += parse_digit_sequence(ptr, &is_digit); + return result; + } + else + return type_safe::nullopt; +} + +type_safe::optional numeric_literal_token(const char*& ptr) +{ + if (starts_with(ptr, "0b") || starts_with(ptr, "0B")) // binary integer literal + { + std::string result; + result += *ptr++; + result += *ptr++; + result += parse_digit_sequence(ptr, [](char c) { return c == '0' || c == '1'; }); + append_integer_suffix(result, ptr); + return cpp_token(cpp_token_kind::int_literal, result); + } + else if (starts_with(ptr, "0x") || starts_with(ptr, "0X")) // hexadecimal literal + { + std::string result; + result += *ptr++; + result += *ptr++; + result += parse_digit_sequence(ptr, &is_hexadecimal_digit); + + auto is_float = false; + if (*ptr == '.') { - std::string result; - result += *ptr++; - result += *ptr++; - result += parse_digit_sequence(ptr, [](char c) { return c == '0' || c == '1'; }); - append_integer_suffix(result, ptr); - return cpp_token(cpp_token_kind::int_literal, result); - } - else if (starts_with(ptr, "0x") || starts_with(ptr, "0X")) // hexadecimal literal - { - std::string result; - result += *ptr++; + // floating point hexadecimal + is_float = true; result += *ptr++; result += parse_digit_sequence(ptr, &is_hexadecimal_digit); - - auto is_float = false; - if (*ptr == '.') - { - // floating point hexadecimal - is_float = true; - result += *ptr++; - result += parse_digit_sequence(ptr, &is_hexadecimal_digit); - } - - if (auto exp = parse_floating_point_exponent(ptr)) - { - is_float = true; - // floating point exponent - result += exp.value(); - } - - if (is_float) - append_floating_point_suffix(result, ptr); - else - append_integer_suffix(result, ptr); - - return cpp_token(is_float ? cpp_token_kind::float_literal : cpp_token_kind::int_literal, - result); } - else if (is_digit(*ptr)) // octal and decimal literals + + if (auto exp = parse_floating_point_exponent(ptr)) { - std::string result; - result += parse_digit_sequence(ptr, &is_digit); - - auto is_float = false; - if (*ptr == '.') - { - // floating point decimal - is_float = true; - result += *ptr++; - result += parse_digit_sequence(ptr, &is_hexadecimal_digit); - } - - if (auto exp = parse_floating_point_exponent(ptr)) - { - // floating point exponent - is_float = true; - result += exp.value(); - } - - if (is_float) - append_floating_point_suffix(result, ptr); - else - append_integer_suffix(result, ptr); - - return cpp_token(is_float ? cpp_token_kind::float_literal : cpp_token_kind::int_literal, - result); + is_float = true; + // floating point exponent + result += exp.value(); } - else if (*ptr == '.' && is_digit(ptr[1])) - { - std::string result; - - // floating point fraction - result += *ptr++; - result += parse_digit_sequence(ptr, &is_digit); - - if (auto exp = parse_floating_point_exponent(ptr)) - result += exp.value(); + if (is_float) append_floating_point_suffix(result, ptr); - return cpp_token(cpp_token_kind::float_literal, result); - } else - return type_safe::nullopt; - } + append_integer_suffix(result, ptr); - type_safe::optional parse_encoding_prefix(const char*& ptr) + return cpp_token(is_float ? cpp_token_kind::float_literal : cpp_token_kind::int_literal, + result); + } + else if (is_digit(*ptr)) // octal and decimal literals { - if (bump_if(ptr, "u8")) - return "u8"; - else if (bump_if(ptr, "u")) - return "u"; - else if (bump_if(ptr, "U")) - return "U"; - else if (bump_if(ptr, "L")) - return "L"; + std::string result; + result += parse_digit_sequence(ptr, &is_digit); + + auto is_float = false; + if (*ptr == '.') + { + // floating point decimal + is_float = true; + result += *ptr++; + result += parse_digit_sequence(ptr, &is_hexadecimal_digit); + } + + if (auto exp = parse_floating_point_exponent(ptr)) + { + // floating point exponent + is_float = true; + result += exp.value(); + } + + if (is_float) + append_floating_point_suffix(result, ptr); else - return type_safe::nullopt; + append_integer_suffix(result, ptr); + + return cpp_token(is_float ? cpp_token_kind::float_literal : cpp_token_kind::int_literal, + result); } - - type_safe::optional character_literal(const char*& ptr) + else if (*ptr == '.' && is_digit(ptr[1])) { - auto save = ptr; - auto prefix = parse_encoding_prefix(ptr); - if (*ptr != '\'') - { - ptr = save; - return type_safe::nullopt; - } - else - { - auto result = prefix.value_or(""); - result += *ptr++; + std::string result; - while (*ptr != '\'') - { - DEBUG_ASSERT(*ptr, detail::assert_handler{}); + // floating point fraction + result += *ptr++; + result += parse_digit_sequence(ptr, &is_digit); - if (*ptr == '\\') - result += *ptr++; - result += *ptr++; - } - result += *ptr++; + if (auto exp = parse_floating_point_exponent(ptr)) + result += exp.value(); - append_udl_suffix(result, ptr); - return cpp_token(cpp_token_kind::char_literal, result); - } + append_floating_point_suffix(result, ptr); + return cpp_token(cpp_token_kind::float_literal, result); } + else + return type_safe::nullopt; +} - type_safe::optional string_literal(const char*& ptr) +type_safe::optional parse_encoding_prefix(const char*& ptr) +{ + if (bump_if(ptr, "u8")) + return "u8"; + else if (bump_if(ptr, "u")) + return "u"; + else if (bump_if(ptr, "U")) + return "U"; + else if (bump_if(ptr, "L")) + return "L"; + else + return type_safe::nullopt; +} + +type_safe::optional character_literal(const char*& ptr) +{ + auto save = ptr; + auto prefix = parse_encoding_prefix(ptr); + if (*ptr != '\'') { - auto save = ptr; - auto prefix = parse_encoding_prefix(ptr); - if (starts_with(ptr, "R\"")) - { - // raw string literal - auto result = prefix.value_or(""); - result += *ptr++; - result += *ptr++; - - std::string terminator; - terminator += ")"; - while (*ptr != '(') - { - result += *ptr; - terminator += *ptr++; - } - result += *ptr++; - terminator += '"'; - - while (!bump_if(ptr, terminator)) - { - DEBUG_ASSERT(ptr, detail::assert_handler{}); - result += *ptr++; - } - result += terminator; - - append_udl_suffix(result, ptr); - return cpp_token(cpp_token_kind::string_literal, result); - } - else if (starts_with(ptr, "\"")) - { - // regular string literal - auto result = prefix.value_or(""); - result += *ptr++; - - while (*ptr != '"') - { - DEBUG_ASSERT(*ptr, detail::assert_handler{}); - - if (*ptr == '\\') - result += *ptr++; - result += *ptr++; - } - result += *ptr++; - - append_udl_suffix(result, ptr); - return cpp_token(cpp_token_kind::string_literal, result); - } - else - { - ptr = save; - return type_safe::nullopt; - } - } - - type_safe::optional digraph_token(const char*& ptr) - { - if (bump_if(ptr, "<%")) - return cpp_token(cpp_token_kind::punctuation, "{"); - else if (bump_if(ptr, "%>")) - return cpp_token(cpp_token_kind::punctuation, "}"); - else if (starts_with(ptr, "<::") && ptr[3] != ':' && ptr[3] != '>') - // don't detect digraph in std::vector<::std::string> - return type_safe::nullopt; - else if (bump_if(ptr, "<:")) - return cpp_token(cpp_token_kind::punctuation, "["); - else if (bump_if(ptr, ":>")) - return cpp_token(cpp_token_kind::punctuation, "]"); - else if (bump_if(ptr, "%:%:")) - return cpp_token(cpp_token_kind::punctuation, "##"); - else if (bump_if(ptr, "%:")) - return cpp_token(cpp_token_kind::punctuation, "#"); - else - return type_safe::nullopt; - } - - type_safe::optional punctuation_token(const char*& ptr) - { - static constexpr const char* punctuations[] = { - // tokens staring with # - "##", - "#", - // tokens starting with . - "...", - ".*", - ".", - // tokens starting with : - "::", - ":", - // tokens starting with + - "+=", - "++", - "+", - // tokens starting with - - "->*", - "->", - "--", - "-=", - "-", - // tokens starting with * - "*=", - "*", - // tokens starting with / - "/=", - "/", - // tokens starting with % - "%=", - "%", - // tokens starting with ^ - "^=", - "^", - // tokens starting with & - "&=", - "&&", - "&", - // tokens starting with | - "|=", - "||", - "|", - // tokens starting with < - "<<=", - "<<", - "<=", - "<", - // tokens starting with > - ">>=", - ">>", - ">=", - ">", - // tokens starting with ! - "!=", - "!", - // tokens starting with = - "==", - "=", - // single tokens - "~", - ";", - "?", - ",", - "{", - "}", - "[", - "]", - "(", - ")", - }; - - for (auto punct : punctuations) - if (bump_if(ptr, punct)) - return cpp_token(cpp_token_kind::punctuation, punct); - + ptr = save; return type_safe::nullopt; } + else + { + auto result = prefix.value_or(""); + result += *ptr++; + + while (*ptr != '\'') + { + DEBUG_ASSERT(*ptr, detail::assert_handler{}); + + if (*ptr == '\\') + result += *ptr++; + result += *ptr++; + } + result += *ptr++; + + append_udl_suffix(result, ptr); + return cpp_token(cpp_token_kind::char_literal, result); + } +} + +type_safe::optional string_literal(const char*& ptr) +{ + auto save = ptr; + auto prefix = parse_encoding_prefix(ptr); + if (starts_with(ptr, "R\"")) + { + // raw string literal + auto result = prefix.value_or(""); + result += *ptr++; + result += *ptr++; + + std::string terminator; + terminator += ")"; + while (*ptr != '(') + { + result += *ptr; + terminator += *ptr++; + } + result += *ptr++; + terminator += '"'; + + while (!bump_if(ptr, terminator)) + { + DEBUG_ASSERT(ptr, detail::assert_handler{}); + result += *ptr++; + } + result += terminator; + + append_udl_suffix(result, ptr); + return cpp_token(cpp_token_kind::string_literal, result); + } + else if (starts_with(ptr, "\"")) + { + // regular string literal + auto result = prefix.value_or(""); + result += *ptr++; + + while (*ptr != '"') + { + DEBUG_ASSERT(*ptr, detail::assert_handler{}); + + if (*ptr == '\\') + result += *ptr++; + result += *ptr++; + } + result += *ptr++; + + append_udl_suffix(result, ptr); + return cpp_token(cpp_token_kind::string_literal, result); + } + else + { + ptr = save; + return type_safe::nullopt; + } +} + +type_safe::optional digraph_token(const char*& ptr) +{ + if (bump_if(ptr, "<%")) + return cpp_token(cpp_token_kind::punctuation, "{"); + else if (bump_if(ptr, "%>")) + return cpp_token(cpp_token_kind::punctuation, "}"); + else if (starts_with(ptr, "<::") && ptr[3] != ':' && ptr[3] != '>') + // don't detect digraph in std::vector<::std::string> + return type_safe::nullopt; + else if (bump_if(ptr, "<:")) + return cpp_token(cpp_token_kind::punctuation, "["); + else if (bump_if(ptr, ":>")) + return cpp_token(cpp_token_kind::punctuation, "]"); + else if (bump_if(ptr, "%:%:")) + return cpp_token(cpp_token_kind::punctuation, "##"); + else if (bump_if(ptr, "%:")) + return cpp_token(cpp_token_kind::punctuation, "#"); + else + return type_safe::nullopt; +} + +type_safe::optional punctuation_token(const char*& ptr) +{ + static constexpr const char* punctuations[] = { + // tokens staring with # + "##", + "#", + // tokens starting with . + "...", + ".*", + ".", + // tokens starting with : + "::", + ":", + // tokens starting with + + "+=", + "++", + "+", + // tokens starting with - + "->*", + "->", + "--", + "-=", + "-", + // tokens starting with * + "*=", + "*", + // tokens starting with / + "/=", + "/", + // tokens starting with % + "%=", + "%", + // tokens starting with ^ + "^=", + "^", + // tokens starting with & + "&=", + "&&", + "&", + // tokens starting with | + "|=", + "||", + "|", + // tokens starting with < + "<<=", + "<<", + "<=", + "<", + // tokens starting with > + ">>=", + ">>", + ">=", + ">", + // tokens starting with ! + "!=", + "!", + // tokens starting with = + "==", + "=", + // single tokens + "~", + ";", + "?", + ",", + "{", + "}", + "[", + "]", + "(", + ")", + }; + + for (auto punct : punctuations) + if (bump_if(ptr, punct)) + return cpp_token(cpp_token_kind::punctuation, punct); + + return type_safe::nullopt; +} } // namespace cpp_token_string cpp_token_string::tokenize(std::string str) @@ -601,10 +601,10 @@ cpp_token_string cpp_token_string::tokenize(std::string str) namespace { - bool is_identifier(char c) - { - return std::isalnum(c) || c == '_'; - } +bool is_identifier(char c) +{ + return std::isalnum(c) || c == '_'; +} } // namespace std::string cpp_token_string::as_string() const diff --git a/src/cpp_type.cpp b/src/cpp_type.cpp index f0834de..77b827f 100644 --- a/src/cpp_type.cpp +++ b/src/cpp_type.cpp @@ -10,8 +10,8 @@ #include #include #include -#include #include +#include using namespace cppast; @@ -174,36 +174,36 @@ std::unique_ptr cpp_dependent_type::build( namespace { - // is directly a complex type - // is_complex_type also checks for children - bool is_direct_complex(const cpp_type& type) noexcept +// is directly a complex type +// is_complex_type also checks for children +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::auto_t: - case cpp_type_kind::decltype_t: - case cpp_type_kind::decltype_auto_t: - case cpp_type_kind::cv_qualified_t: - case cpp_type_kind::pointer_t: - case cpp_type_kind::reference_t: - case cpp_type_kind::template_parameter_t: - case cpp_type_kind::template_instantiation_t: - case cpp_type_kind::dependent_t: - case cpp_type_kind::unexposed_t: - return false; - - case cpp_type_kind::array_t: - case cpp_type_kind::function_t: - case cpp_type_kind::member_function_t: - case cpp_type_kind::member_object_t: - return true; - } - - DEBUG_UNREACHABLE(detail::assert_handler{}); + case cpp_type_kind::builtin_t: + case cpp_type_kind::user_defined_t: + case cpp_type_kind::auto_t: + case cpp_type_kind::decltype_t: + case cpp_type_kind::decltype_auto_t: + case cpp_type_kind::cv_qualified_t: + case cpp_type_kind::pointer_t: + case cpp_type_kind::reference_t: + case cpp_type_kind::template_parameter_t: + case cpp_type_kind::template_instantiation_t: + case cpp_type_kind::dependent_t: + case cpp_type_kind::unexposed_t: return false; + + case cpp_type_kind::array_t: + case cpp_type_kind::function_t: + case cpp_type_kind::member_function_t: + case cpp_type_kind::member_object_t: + return true; } + + DEBUG_UNREACHABLE(detail::assert_handler{}); + return false; +} } // namespace 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 { - void comma(const code_generator::output& output) - { - output << punctuation(","); - if (output.formatting().is_set(formatting_flags::comma_ws)) - output << whitespace; - } +void comma(const code_generator::output& output) +{ + output << punctuation(","); + if (output.formatting().is_set(formatting_flags::comma_ws)) + output << whitespace; +} - void bracket_ws(const code_generator::output& output) - { - if (output.formatting().is_set(formatting_flags::bracket_ws)) - output << whitespace; - } +void bracket_ws(const code_generator::output& output) +{ + if (output.formatting().is_set(formatting_flags::bracket_ws)) + output << whitespace; +} - void operator_ws(const code_generator::output& output) - { - if (output.formatting().is_set(formatting_flags::operator_ws)) - output << whitespace; - } +void operator_ws(const code_generator::output& output) +{ + if (output.formatting().is_set(formatting_flags::operator_ws)) + output << whitespace; +} - void write_builtin(code_generator::output& output, const cpp_builtin_type& type) - { - output << keyword(to_string(type.builtin_type_kind())); - } +void write_builtin(code_generator::output& output, const cpp_builtin_type& type) +{ + output << keyword(to_string(type.builtin_type_kind())); +} - void write_user_defined(code_generator::output& output, const cpp_user_defined_type& type) - { - output << type.entity(); - } +void write_user_defined(code_generator::output& output, const cpp_user_defined_type& type) +{ + output << type.entity(); +} - void write_auto(code_generator::output& output, const cpp_auto_type&) - { - output << keyword("auto"); - } +void write_auto(code_generator::output& output, const cpp_auto_type&) +{ + output << keyword("auto"); +} - void write_decltype(code_generator::output& output, const cpp_decltype_type& type) - { - output << keyword("decltype") << punctuation("(") << bracket_ws; - detail::write_expression(output, type.expression()); - output << bracket_ws << punctuation(")"); - } +void write_decltype(code_generator::output& output, const cpp_decltype_type& type) +{ + output << keyword("decltype") << punctuation("(") << bracket_ws; + detail::write_expression(output, type.expression()); + output << bracket_ws << punctuation(")"); +} - void write_decltype_auto(code_generator::output& output, const cpp_decltype_auto_type&) - { - output << keyword("decltype") << punctuation("(") << bracket_ws << keyword("auto") - << bracket_ws << punctuation(")"); - } +void write_decltype_auto(code_generator::output& output, const cpp_decltype_auto_type&) +{ + output << keyword("decltype") << punctuation("(") << bracket_ws << keyword("auto") << bracket_ws + << punctuation(")"); +} - void write_cv_qualified_prefix(code_generator::output& output, - const cpp_cv_qualified_type& type) - { - detail::write_type_prefix(output, type.type()); +void write_cv_qualified_prefix(code_generator::output& output, const cpp_cv_qualified_type& type) +{ + detail::write_type_prefix(output, type.type()); - if (is_direct_complex(type.type())) - output << punctuation("(") << 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 - void write_parameters(code_generator::output& output, const T& type) - { + if (is_direct_complex(type.type())) output << punctuation("(") << bracket_ws; - auto need_sep = false; - for (auto& param : type.parameter_types()) - { - if (need_sep) - 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("..."); - } + 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()); +} - void write_function_suffix(code_generator::output& output, const cpp_function_type& type) - { - write_parameters(output, 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; +} - detail::write_type_suffix(output, type.return_type()); - } - - const cpp_type& strip_class_type(const cpp_type& type, cpp_cv* cv, cpp_reference* ref) - { - if (type.kind() == cpp_type_kind::cv_qualified_t) - { - auto& cv_qual = static_cast(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(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()); +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; - detail::write_type_prefix(output, strip_class_type(type.class_type(), nullptr, nullptr)); - output << punctuation("::"); - } + else if (output.formatting().is_set(formatting_flags::ptr_ref_var)) + output << whitespace; - void write_member_function_suffix(code_generator::output& output, - const cpp_member_function_type& type) - { + output << punctuation("*"); +} + +void write_pointer_suffix(code_generator::output& output, const cpp_pointer_type& type) +{ + if (pointer_requires_paren(type)) output << bracket_ws << punctuation(")"); - write_parameters(output, type); + detail::write_type_suffix(output, type.pointee()); +} - auto cv = cpp_cv_none; - auto ref = cpp_ref_none; - strip_class_type(type.class_type(), &cv, &ref); +void write_reference_prefix(code_generator::output& output, const cpp_reference_type& type) +{ + detail::write_type_prefix(output, type.referee()); - 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()); + if (is_direct_complex(type.referee())) 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("::"); - } + else if (output.formatting().is_set(formatting_flags::ptr_ref_var)) + output << whitespace; - 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(")"); - } + detail::write_type_suffix(output, type.referee()); +} - void write_template_parameter(code_generator::output& output, - const cpp_template_parameter_type& type) +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 << 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, - const cpp_template_instantiation_type& type) +void write_function_prefix(code_generator::output& output, const cpp_function_type& type) +{ + detail::write_type_prefix(output, type.return_type()); +} + +template +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 (output.was_reference_excluded()) - return; - - if (type.arguments_exposed()) - detail::write_template_arguments(output, type.arguments()); + if (need_sep) + output << comma; else - output << punctuation("<") << bracket_ws << token_seq(type.unexposed_arguments()) - << bracket_ws << punctuation(">"); + 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_dependent(code_generator::output& output, const cpp_dependent_type& type) - { - output << token_seq(type.name()); - } + output << bracket_ws << punctuation(")"); +} - 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(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(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 void detail::write_type_prefix(code_generator::output& output, const cpp_type& type) diff --git a/src/libclang/class_parser.cpp b/src/libclang/class_parser.cpp index 4d0c8a8..4bc19c1 100644 --- a/src/libclang/class_parser.cpp +++ b/src/libclang/class_parser.cpp @@ -2,8 +2,8 @@ // This file is subject to the license terms in the LICENSE file // found in the top-level directory of this distribution. -#include #include +#include #include "libclang_visitor.hpp" #include "parse_functions.hpp" @@ -12,104 +12,104 @@ using namespace cppast; 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()); - 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) - { - 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{}); + 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; } - - 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); - - // [] [virtual] [] - // 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); - } + DEBUG_UNREACHABLE(detail::assert_handler{}); + return cpp_class_kind::class_t; } +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); + + // [] [virtual] [] + // 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 detail::parse_cpp_class(const detail::parse_context& context, const CXCursor& cur, const CXCursor& parent_cur) { @@ -145,9 +145,9 @@ std::unique_ptr detail::parse_cpp_class(const detail::parse_context& stream.bump(); } if (!scope.empty()) - semantic_parent = - cpp_entity_ref(detail::get_entity_id(clang_getCursorSemanticParent(cur)), - std::move(scope)); + semantic_parent + = cpp_entity_ref(detail::get_entity_id(clang_getCursorSemanticParent(cur)), + std::move(scope)); } context.comments.match(builder.get(), cur); @@ -159,12 +159,12 @@ std::unique_ptr detail::parse_cpp_class(const detail::parse_context& add_base_class(builder, context, child, cur); else if (kind == CXCursor_CXXFinalAttr) builder.is_final(); - else if ( - kind == CXCursor_TemplateTypeParameter || kind == CXCursor_NonTypeTemplateParameter - || kind == CXCursor_TemplateTemplateParameter || kind == CXCursor_ParmDecl - || clang_isExpression(kind) || clang_isReference(kind) - || kind - == CXCursor_UnexposedAttr) // I have no idea what this is, but happens on Windows + else if (kind == CXCursor_TemplateTypeParameter + || kind == CXCursor_NonTypeTemplateParameter + || kind == CXCursor_TemplateTemplateParameter || kind == CXCursor_ParmDecl + || clang_isExpression(kind) || clang_isReference(kind) + || kind == CXCursor_UnexposedAttr) // I have no idea what this is, but happens + // on Windows // other children due to templates and stuff return; else if (auto entity = parse_entity(context, &builder.get(), child)) @@ -173,10 +173,10 @@ std::unique_ptr detail::parse_cpp_class(const detail::parse_context& } if (!is_friend && clang_isCursorDefinition(cur)) - return is_templated ? - builder.finish(std::move(semantic_parent)) : - builder.finish(*context.idx, get_entity_id(cur), std::move(semantic_parent)); + return is_templated + ? builder.finish(std::move(semantic_parent)) + : builder.finish(*context.idx, get_entity_id(cur), std::move(semantic_parent)); else - return is_templated ? builder.finish_declaration(detail::get_entity_id(cur)) : - builder.finish_declaration(*context.idx, get_entity_id(cur)); + return is_templated ? builder.finish_declaration(detail::get_entity_id(cur)) + : builder.finish_declaration(*context.idx, get_entity_id(cur)); } diff --git a/src/libclang/cxtokenizer.cpp b/src/libclang/cxtokenizer.cpp index afea2ae..2d3845f 100644 --- a/src/libclang/cxtokenizer.cpp +++ b/src/libclang/cxtokenizer.cpp @@ -13,290 +13,282 @@ using namespace cppast; detail::cxtoken::cxtoken(const CXTranslationUnit& tu_unit, const CXToken& token) : value_(clang_getTokenSpelling(tu_unit, token)), kind_(clang_getTokenKind(token)) -{ -} +{} 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 - || kind == CXCursor_Constructor || kind == CXCursor_Destructor - || kind == CXCursor_ConversionFunction; + clang_tokenize(tu, range, &tokens_, &no_); } - CXSourceLocation get_next_location(const CXTranslationUnit& tu, CXFile file, - const CXSourceLocation& loc, int inc = 1) + ~simple_tokenizer() { - 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); + clang_disposeTokens(tu_, tokens_, no_); } - class simple_tokenizer + simple_tokenizer(const simple_tokenizer&) = delete; + simple_tokenizer& operator=(const simple_tokenizer&) = delete; + + unsigned size() const noexcept { - public: - 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; + return no_; } - // 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) + const CXToken& operator[](unsigned i) const noexcept { - unmunch = false; + return tokens_[i]; + } - auto extent = clang_getCursorExtent(cur); - auto begin = clang_getRangeStart(extent); - auto end = clang_getRangeEnd(extent); +private: + CXTranslationUnit tu_; + CXToken* tokens_; + unsigned no_; +}; - 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) +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 +// 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) - && token_after_is(tu, file, begin, "]", -3)) - { - 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 + while (!token_after_is(tu, file, begin, "[", -1) + && !token_after_is(tu, file, begin, "[", -2)) 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); + + 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 (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 - // 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)) + // 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) { - if (token_after_is(tu, file, next, "(", 0)) - ++paren_count; - else if (token_after_is(tu, file, next, ")", 0)) + begin = get_next_location(tu, file, begin, -1); + if (token_after_is(tu, file, begin, "(", -1)) --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 - 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` + begin = get_next_location(tu, file, begin, -(int(std::strlen("alignas")) + 1)); - // 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); + if (token_after_is(tu, file, begin, "alignas", 0)) + begin = get_next_location(tu, file, begin, -1); + else + begin = save_begin; } - 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 detail::cxtokenizer::cxtokenizer(const CXTranslationUnit& tu, const CXFile& file, @@ -326,15 +318,15 @@ void detail::skip(detail::cxtoken_stream& stream, const char* str) namespace { - bool starts_with(const char*& str, const detail::cxtoken& t) - { - if (std::strncmp(str, t.c_str(), t.value().length()) != 0) - return false; - str += t.value().length(); - while (*str == ' ' || *str == '\t') - ++str; - return true; - } +bool starts_with(const char*& str, const detail::cxtoken& t) +{ + if (std::strncmp(str, t.c_str(), t.value().length()) != 0) + return false; + str += t.value().length(); + while (*str == ' ' || *str == '\t') + ++str; + return true; +} } // namespace 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 { - // whether or not the current angle bracket can be a comparison - // note: this is a heuristic I hope works often enough - bool is_comparison(CXTokenKind last_kind, const detail::cxtoken& cur, CXTokenKind next_kind) - { - if (cur == "<") - return last_kind == CXToken_Literal; - else if (cur == ">") - return next_kind == CXToken_Literal; - return false; - } +// whether or not the current angle bracket can be a comparison +// note: this is a heuristic I hope works often enough +bool is_comparison(CXTokenKind last_kind, const detail::cxtoken& cur, CXTokenKind next_kind) +{ + if (cur == "<") + return last_kind == CXToken_Literal; + else if (cur == ">") + return next_kind == CXToken_Literal; + return false; +} } // namespace detail::cxtoken_iterator detail::find_closing_bracket(detail::cxtoken_stream stream) @@ -430,161 +422,160 @@ void detail::skip_brackets(detail::cxtoken_stream& stream) namespace { - type_safe::optional parse_attribute_using(detail::cxtoken_stream& stream) +type_safe::optional parse_attribute_using(detail::cxtoken_stream& stream) +{ + // using identifier : + if (skip_if(stream, "using")) { - // using identifier : - if (skip_if(stream, "using")) - { - DEBUG_ASSERT(stream.peek().kind() == CXToken_Identifier, detail::parse_error_handler{}, - stream.cursor(), "expected identifier"); - auto scope = stream.get().value().std_str(); - skip(stream, ":"); + DEBUG_ASSERT(stream.peek().kind() == CXToken_Identifier, detail::parse_error_handler{}, + stream.cursor(), "expected identifier"); + auto scope = stream.get().value().std_str(); + skip(stream, ":"); - return scope; - } - else - return type_safe::nullopt; + return scope; } + 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 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") - 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; - } + // 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); - 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 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, "::")) - { - // 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 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); + name = stream.get().value().std_str(); } - bool parse_attribute_impl(cpp_attribute_list& result, detail::cxtoken_stream& stream) + // parse arguments + type_safe::optional 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 + // [[]] + // ^ + skip(stream, "["); + + auto scope = parse_attribute_using(stream); + while (!skip_if(stream, "]")) { - // C++11 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, ","); - } - - // [[]] - // ^ - skip(stream, "]"); - return true; - } - else if (skip_if(stream, "alignas")) - { - // alignas specifier - // alignas() - // ^ - 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__(()) - // ^^ - 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() - // ^ - 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; + auto attribute = parse_attribute_token(stream, scope); + result.push_back(std::move(attribute)); + detail::skip_if(stream, ","); } - return false; + // [[]] + // ^ + skip(stream, "]"); + return true; } + else if (skip_if(stream, "alignas")) + { + // alignas specifier + // alignas() + // ^ + 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__(()) + // ^^ + 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() + // ^ + 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 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 { - 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: - 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{}); + 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; +} } // namespace cpp_token_string detail::to_string(cxtoken_stream& stream, cxtoken_iterator end) diff --git a/src/libclang/cxtokenizer.hpp b/src/libclang/cxtokenizer.hpp index 42d2dfa..081a6a8 100644 --- a/src/libclang/cxtokenizer.hpp +++ b/src/libclang/cxtokenizer.hpp @@ -15,194 +15,189 @@ 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: - 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; + return value_; } - 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 - { - return !(str == tok); - } + private: + cxstring value_; + CXTokenKind kind_; + }; - using cxtoken_iterator = std::vector::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 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); + inline bool operator==(const cxtoken& tok, const char* str) noexcept + { + return tok.value() == str; } -} // 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::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 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 diff --git a/src/libclang/debug_helper.cpp b/src/libclang/debug_helper.cpp index e973ded..343eb59 100644 --- a/src/libclang/debug_helper.cpp +++ b/src/libclang/debug_helper.cpp @@ -28,7 +28,7 @@ detail::cxstring detail::get_type_kind_spelling(const CXType& type) noexcept namespace { - std::mutex mtx; +std::mutex mtx; } void detail::print_cursor_info(const CXCursor& cur) noexcept diff --git a/src/libclang/debug_helper.hpp b/src/libclang/debug_helper.hpp index 756d0f9..1abfeed 100644 --- a/src/libclang/debug_helper.hpp +++ b/src/libclang/debug_helper.hpp @@ -11,21 +11,21 @@ namespace cppast { - namespace detail - { - cxstring get_display_name(const CXCursor& cur) noexcept; +namespace detail +{ + 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, - const CXCursor& cur) noexcept; - } -} // namespace cppast::detail + void print_tokens(const CXTranslationUnit& tu, const CXFile& file, + const CXCursor& cur) noexcept; +} // namespace detail +} // namespace cppast #endif // CPPAST_DEBUG_HELPER_HPP_INCLUDED diff --git a/src/libclang/enum_parser.cpp b/src/libclang/enum_parser.cpp index e1a6201..0e190f0 100644 --- a/src/libclang/enum_parser.cpp +++ b/src/libclang/enum_parser.cpp @@ -4,80 +4,77 @@ #include -#include "parse_functions.hpp" #include "libclang_visitor.hpp" +#include "parse_functions.hpp" using namespace cppast; namespace { - std::unique_ptr parse_enum_value(const detail::parse_context& context, - const CXCursor& cur) +std::unique_ptr parse_enum_value(const detail::parse_context& context, + 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); + + // [], + // or: [] = , + auto& name = stream.get().value(); + auto attributes = detail::parse_attributes(stream); + + std::unique_ptr value; + if (detail::skip_if(stream, "=")) { - if (clang_isAttribute(clang_getCursorKind(cur))) - return nullptr; + 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"); - 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); - - // [], - // or: [] = , - auto& name = stream.get().value(); - auto attributes = detail::parse_attributes(stream); - - std::unique_ptr 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; + value = detail::parse_expression(context, child); + }); } - cpp_enum::builder make_enum_builder(const detail::parse_context& context, const CXCursor& cur, - type_safe::optional& 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] [] 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; - } + 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, + type_safe::optional& 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] [] 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 detail::parse_cpp_enum(const detail::parse_context& context, const CXCursor& cur) { diff --git a/src/libclang/friend_parser.cpp b/src/libclang/friend_parser.cpp index 5e54a84..307c993 100644 --- a/src/libclang/friend_parser.cpp +++ b/src/libclang/friend_parser.cpp @@ -4,11 +4,11 @@ #include -#include #include +#include -#include "parse_functions.hpp" #include "libclang_visitor.hpp" +#include "parse_functions.hpp" using namespace cppast; @@ -45,9 +45,9 @@ std::unique_ptr detail::parse_cpp_friend(const detail::parse_context // we can't use the other branch here, // as then the class name would be wrong auto name = detail::get_cursor_name(referenced); - type = - cpp_user_defined_type::build(cpp_type_ref(detail::get_entity_id(referenced), - namespace_str + "::" + name.c_str())); + type = cpp_user_defined_type::build( + cpp_type_ref(detail::get_entity_id(referenced), + namespace_str + "::" + name.c_str())); } else { diff --git a/src/libclang/function_parser.cpp b/src/libclang/function_parser.cpp index 4e4b2b1..c0f7178 100644 --- a/src/libclang/function_parser.cpp +++ b/src/libclang/function_parser.cpp @@ -12,558 +12,547 @@ using namespace cppast; namespace { - std::unique_ptr parse_parameter(const detail::parse_context& context, - const CXCursor& cur) +std::unique_ptr parse_parameter(const detail::parse_context& context, + const CXCursor& cur) +{ + auto name = detail::get_cursor_name(cur); + auto type = detail::parse_type(context, cur, clang_getCursorType(cur)); + + cpp_attribute_list attributes; + auto default_value = detail::parse_default_value(attributes, context, cur, name.c_str()); + + std::unique_ptr result; + if (name.empty()) + result = cpp_function_parameter::build(std::move(type), std::move(default_value)); + else + result + = cpp_function_parameter::build(*context.idx, detail::get_entity_id(cur), name.c_str(), + std::move(type), std::move(default_value)); + result->add_attribute(attributes); + return result; +} + +template +void add_parameters(const detail::parse_context& context, Builder& builder, const CXCursor& cur) +{ + if (clang_getCursorKind(cur) == CXCursor_FunctionTemplate) { - auto name = detail::get_cursor_name(cur); - auto type = detail::parse_type(context, cur, clang_getCursorType(cur)); + // clang_Cursor_getNumArguments() doesn't work here + // (of course it doesn't...) + detail::visit_children(cur, [&](const CXCursor& child) { + if (clang_getCursorKind(child) != CXCursor_ParmDecl) + return; - cpp_attribute_list attributes; - auto default_value = detail::parse_default_value(attributes, context, cur, name.c_str()); - - std::unique_ptr result; - if (name.empty()) - result = cpp_function_parameter::build(std::move(type), std::move(default_value)); - else - result = cpp_function_parameter::build(*context.idx, detail::get_entity_id(cur), - name.c_str(), std::move(type), - std::move(default_value)); - result->add_attribute(attributes); - return result; + try + { + auto parameter = parse_parameter(context, child); + builder.add_parameter(std::move(parameter)); + } + catch (detail::parse_error& ex) + { + context.error = true; + context.logger->log("libclang parser", ex.get_diagnostic(context.file)); + } + catch (std::logic_error& ex) + { + context.error = true; + context.logger->log("libclang parser", + diagnostic{ex.what(), + detail::make_location(context.file, child), + severity::error}); + } + }); } - - template - void add_parameters(const detail::parse_context& context, Builder& builder, const CXCursor& cur) + else { - if (clang_getCursorKind(cur) == CXCursor_FunctionTemplate) - { - // clang_Cursor_getNumArguments() doesn't work here - // (of course it doesn't...) - detail::visit_children(cur, [&](const CXCursor& child) { - if (clang_getCursorKind(child) != CXCursor_ParmDecl) - return; - - try - { - auto parameter = parse_parameter(context, child); - builder.add_parameter(std::move(parameter)); - } - catch (detail::parse_error& ex) - { - context.error = true; - context.logger->log("libclang parser", ex.get_diagnostic(context.file)); - } - catch (std::logic_error& ex) - { - context.error = true; - context.logger->log("libclang parser", - diagnostic{ex.what(), - detail::make_location(context.file, child), - severity::error}); - } - }); - } - else - { - auto no = clang_Cursor_getNumArguments(cur); - DEBUG_ASSERT(no != -1, detail::parse_error_handler{}, cur, - "unexpected number of arguments"); - for (auto i = 0; i != no; ++i) - try - { - auto parameter = - parse_parameter(context, clang_Cursor_getArgument(cur, unsigned(i))); - builder.add_parameter(std::move(parameter)); - } - catch (detail::parse_error& ex) - { - context.error = true; - context.logger->log("libclang parser", ex.get_diagnostic(context.file)); - } - catch (std::logic_error& ex) - { - context.error = true; - context.logger - ->log("libclang parser", - diagnostic{ex.what(), - detail::make_location(context.file, - clang_Cursor_getArgument(cur, - unsigned( - i))), - severity::error}); - } - } + auto no = clang_Cursor_getNumArguments(cur); + DEBUG_ASSERT(no != -1, detail::parse_error_handler{}, cur, + "unexpected number of arguments"); + for (auto i = 0; i != no; ++i) + try + { + auto parameter + = parse_parameter(context, clang_Cursor_getArgument(cur, unsigned(i))); + builder.add_parameter(std::move(parameter)); + } + catch (detail::parse_error& ex) + { + context.error = true; + context.logger->log("libclang parser", ex.get_diagnostic(context.file)); + } + catch (std::logic_error& ex) + { + context.error = true; + context.logger + ->log("libclang parser", + diagnostic{ex.what(), + detail::make_location(context.file, + clang_Cursor_getArgument(cur, + unsigned(i))), + severity::error}); + } } +} - bool is_templated_cursor(const CXCursor& cur) - { - return clang_getTemplateCursorKind(cur) != CXCursor_NoDeclFound - || !clang_Cursor_isNull(clang_getSpecializedCursorTemplate(cur)); - } +bool is_templated_cursor(const CXCursor& cur) +{ + return clang_getTemplateCursorKind(cur) != CXCursor_NoDeclFound + || !clang_Cursor_isNull(clang_getSpecializedCursorTemplate(cur)); +} - // precondition: after the name - void skip_parameters(detail::cxtoken_stream& stream) - { - if (stream.peek() == "<") - // specialization arguments - detail::skip_brackets(stream); +// precondition: after the name +void skip_parameters(detail::cxtoken_stream& stream) +{ + if (stream.peek() == "<") + // specialization arguments detail::skip_brackets(stream); - } + detail::skip_brackets(stream); +} - std::vector get_semantic_parents(CXCursor cur) - { - std::vector result; - for (; !clang_isTranslationUnit(clang_getCursorKind(cur)); - cur = clang_getCursorSemanticParent(cur)) - result.push_back(cur); - return result; - } +std::vector get_semantic_parents(CXCursor cur) +{ + std::vector result; + for (; !clang_isTranslationUnit(clang_getCursorKind(cur)); + cur = clang_getCursorSemanticParent(cur)) + result.push_back(cur); + return result; +} - bool is_class(const CXCursor& parent) - { - auto kind = clang_getCursorKind(parent); - return kind == CXCursor_ClassDecl || kind == CXCursor_StructDecl - || kind == CXCursor_UnionDecl || kind == CXCursor_ClassTemplate - || kind == CXCursor_ClassTemplatePartialSpecialization; - } +bool is_class(const CXCursor& parent) +{ + auto kind = clang_getCursorKind(parent); + return kind == CXCursor_ClassDecl || kind == CXCursor_StructDecl || kind == CXCursor_UnionDecl + || kind == CXCursor_ClassTemplate || kind == CXCursor_ClassTemplatePartialSpecialization; +} - // returns the scope where the function is contained in - // for regular functions that is the lexcial parent - // for friend functions it is the enclosing scope of the class - CXCursor get_definition_scope(const CXCursor& cur, bool is_friend) +// returns the scope where the function is contained in +// for regular functions that is the lexcial parent +// for friend functions it is the enclosing scope of the class +CXCursor get_definition_scope(const CXCursor& cur, bool is_friend) +{ + auto parent = clang_getCursorLexicalParent(cur); + if (is_friend) { - auto parent = clang_getCursorLexicalParent(cur); - if (is_friend) + // find the lexical parent that isn't a class + // as the definition scope is a namespace + while (is_class(parent)) + parent = clang_getCursorSemanticParent(parent); + + DEBUG_ASSERT(clang_getCursorKind(parent) == CXCursor_Namespace + || clang_getCursorKind(parent) == CXCursor_TranslationUnit, + detail::parse_error_handler{}, cur, + "unable to find definition scope of friend"); + } + return parent; +} + +bool equivalent_cursor(const CXCursor& a, const CXCursor& b) +{ + if (clang_getCursorKind(a) == clang_getCursorKind(b) + && clang_getCursorKind(a) == CXCursor_Namespace) + return detail::cxstring(clang_getCursorUSR(a)) == detail::cxstring(clang_getCursorUSR(b)); + else + return clang_equalCursors(a, b) == 1; +} + +type_safe::optional parse_scope(const CXCursor& cur, bool is_friend) +{ + std::string scope_name; + + auto friended = clang_getCursorReferenced(cur); + if (is_friend && !clang_Cursor_isNull(friended)) + { + // it refers to another function + // find the common parent between the two cursors + // scope is the scope from the common parent down to the function + + auto friended_parents = get_semantic_parents(friended); + auto cur_parents = get_semantic_parents(get_definition_scope(cur, true)); + + // remove common parents + while (!friended_parents.empty() && !cur_parents.empty() + && equivalent_cursor(friended_parents.back(), cur_parents.back())) { - // find the lexical parent that isn't a class - // as the definition scope is a namespace - while (is_class(parent)) - parent = clang_getCursorSemanticParent(parent); + friended_parents.pop_back(); + cur_parents.pop_back(); + } + DEBUG_ASSERT(!clang_isTranslationUnit(clang_getCursorKind(friended_parents.back())) + && !friended_parents.empty(), + detail::parse_error_handler{}, cur, + "invalid common parent of friend and friended"); - DEBUG_ASSERT(clang_getCursorKind(parent) == CXCursor_Namespace - || clang_getCursorKind(parent) == CXCursor_TranslationUnit, + // scope consists of all remaining parents of friended + // (last one is cursor itself) + for (auto iter = friended_parents.rbegin(); iter != std::prev(friended_parents.rend()); + ++iter) + { + auto parent_name = detail::cxstring(clang_getCursorDisplayName(*iter)); + scope_name += parent_name.std_str() + "::"; + } + } + else + { + // find the difference between the definition scope parent and semantic parent + // all semantic parents in between form the scope + // the definition scope is the lexical parent for regular functions, + // and the scope outside of the class for friend functions + for (auto definition = get_definition_scope(cur, is_friend), + parent = clang_getCursorSemanticParent(cur); + !equivalent_cursor(definition, parent); parent = clang_getCursorSemanticParent(parent)) + { + DEBUG_ASSERT(!clang_isTranslationUnit(clang_getCursorKind(parent)), detail::parse_error_handler{}, cur, - "unable to find definition scope of friend"); + "infinite loop while calculating scope"); + auto parent_name = detail::cxstring(clang_getCursorDisplayName(parent)); + scope_name = parent_name.std_str() + "::" + std::move(scope_name); } - return parent; } - bool equivalent_cursor(const CXCursor& a, const CXCursor& b) + if (scope_name.empty()) + return type_safe::nullopt; + else + return cpp_entity_ref(detail::get_entity_id(clang_getCursorSemanticParent(cur)), + std::move(scope_name)); +} + +// just the tokens occurring in the prefix +struct prefix_info +{ + cpp_attribute_list attributes; + bool is_constexpr = false; + bool is_virtual = false; + bool is_explicit = false; + bool is_friend = false; +}; + +bool prefix_end(detail::cxtoken_stream& stream, const char* name, bool is_ctor_dtor) +{ + auto cur = stream.cur(); + // name can have multiple tokens if it is an operator + if (!detail::skip_if(stream, name, true)) + return false; + else if (stream.peek() == "," || stream.peek() == ">" || stream.peek() == ">>") { - if (clang_getCursorKind(a) == clang_getCursorKind(b) - && clang_getCursorKind(a) == CXCursor_Namespace) - return detail::cxstring(clang_getCursorUSR(a)) - == detail::cxstring(clang_getCursorUSR(b)); - else - return clang_equalCursors(a, b) == 1; + // argument to template parameters + stream.set_cur(cur); + return false; } - - type_safe::optional parse_scope(const CXCursor& cur, bool is_friend) + else if (is_ctor_dtor) { - std::string scope_name; - - auto friended = clang_getCursorReferenced(cur); - if (is_friend && !clang_Cursor_isNull(friended)) + // need to make sure it is not actually a class name + if (stream.peek() == "::") { - // it refers to another function - // find the common parent between the two cursors - // scope is the scope from the common parent down to the function - - auto friended_parents = get_semantic_parents(friended); - auto cur_parents = get_semantic_parents(get_definition_scope(cur, true)); - - // remove common parents - while (!friended_parents.empty() && !cur_parents.empty() - && equivalent_cursor(friended_parents.back(), cur_parents.back())) - { - friended_parents.pop_back(); - cur_parents.pop_back(); - } - DEBUG_ASSERT(!clang_isTranslationUnit(clang_getCursorKind(friended_parents.back())) - && !friended_parents.empty(), - detail::parse_error_handler{}, cur, - "invalid common parent of friend and friended"); - - // scope consists of all remaining parents of friended - // (last one is cursor itself) - for (auto iter = friended_parents.rbegin(); iter != std::prev(friended_parents.rend()); - ++iter) - { - auto parent_name = detail::cxstring(clang_getCursorDisplayName(*iter)); - scope_name += parent_name.std_str() + "::"; - } - } - else - { - // find the difference between the definition scope parent and semantic parent - // all semantic parents in between form the scope - // the definition scope is the lexical parent for regular functions, - // and the scope outside of the class for friend functions - for (auto definition = get_definition_scope(cur, is_friend), - parent = clang_getCursorSemanticParent(cur); - !equivalent_cursor(definition, parent); - parent = clang_getCursorSemanticParent(parent)) - { - DEBUG_ASSERT(!clang_isTranslationUnit(clang_getCursorKind(parent)), - detail::parse_error_handler{}, cur, - "infinite loop while calculating scope"); - auto parent_name = detail::cxstring(clang_getCursorDisplayName(parent)); - scope_name = parent_name.std_str() + "::" + std::move(scope_name); - } - } - - if (scope_name.empty()) - return type_safe::nullopt; - else - return cpp_entity_ref(detail::get_entity_id(clang_getCursorSemanticParent(cur)), - std::move(scope_name)); - } - - // just the tokens occurring in the prefix - struct prefix_info - { - cpp_attribute_list attributes; - bool is_constexpr = false; - bool is_virtual = false; - bool is_explicit = false; - bool is_friend = false; - }; - - bool prefix_end(detail::cxtoken_stream& stream, const char* name, bool is_ctor_dtor) - { - auto cur = stream.cur(); - // name can have multiple tokens if it is an operator - if (!detail::skip_if(stream, name, true)) - return false; - else if (stream.peek() == "," || stream.peek() == ">" || stream.peek() == ">>") - { - // argument to template parameters + // after name came "::", it is a class name stream.set_cur(cur); return false; } - else if (is_ctor_dtor) + else if (stream.peek() == "<") { - // need to make sure it is not actually a class name - if (stream.peek() == "::") + // after name came "<", it might be arguments for a class template, + // or just a specialization + // check if ( comes after the arguments + detail::skip_brackets(stream); + if (stream.peek() == "(") { - // after name came "::", it is a class name + // it was just a specialization, we're at the end + stream.set_cur(cur); + return true; + } + else + { + // class arguments stream.set_cur(cur); return false; } - else if (stream.peek() == "<") - { - // after name came "<", it might be arguments for a class template, - // or just a specialization - // check if ( comes after the arguments - detail::skip_brackets(stream); - if (stream.peek() == "(") - { - // it was just a specialization, we're at the end - stream.set_cur(cur); - return true; - } - else - { - // class arguments - stream.set_cur(cur); - return false; - } - } - else - return true; - } - else if (std::strcmp(name, "operator") != 0 && stream.peek().kind() == CXToken_Identifier) - { - // can't be function name - stream.set_cur(cur); - return false; } else return true; } - - prefix_info parse_prefix_info(detail::cxtoken_stream& stream, const char* name, - bool is_ctor_dtor) + else if (std::strcmp(name, "operator") != 0 && stream.peek().kind() == CXToken_Identifier) { - prefix_info result; + // can't be function name + stream.set_cur(cur); + return false; + } + else + return true; +} - while (!stream.done() && !prefix_end(stream, name, is_ctor_dtor)) +prefix_info parse_prefix_info(detail::cxtoken_stream& stream, const char* name, bool is_ctor_dtor) +{ + prefix_info result; + + while (!stream.done() && !prefix_end(stream, name, is_ctor_dtor)) + { + if (detail::skip_if(stream, "constexpr")) + result.is_constexpr = true; + else if (detail::skip_if(stream, "virtual")) + result.is_virtual = true; + else if (detail::skip_if(stream, "explicit")) + result.is_explicit = true; + else { - if (detail::skip_if(stream, "constexpr")) - result.is_constexpr = true; - else if (detail::skip_if(stream, "virtual")) - result.is_virtual = true; - else if (detail::skip_if(stream, "explicit")) - result.is_explicit = true; - else - { - auto attributes = detail::parse_attributes(stream, true); - result.attributes.insert(result.attributes.end(), attributes.begin(), - attributes.end()); - } + auto attributes = detail::parse_attributes(stream, true); + result.attributes.insert(result.attributes.end(), attributes.begin(), attributes.end()); } - DEBUG_ASSERT(!stream.done(), detail::parse_error_handler{}, stream.cursor(), - "unable to find end of function prefix"); - while (detail::skip_if(stream, ")")) - { // function name can be enclosed in parentheses - } - - auto attributes = detail::parse_attributes(stream); - result.attributes.insert(result.attributes.end(), attributes.begin(), attributes.end()); - - return result; + } + DEBUG_ASSERT(!stream.done(), detail::parse_error_handler{}, stream.cursor(), + "unable to find end of function prefix"); + while (detail::skip_if(stream, ")")) + { // function name can be enclosed in parentheses } - // just the tokens occurring in the suffix - struct suffix_info + auto attributes = detail::parse_attributes(stream); + result.attributes.insert(result.attributes.end(), attributes.begin(), attributes.end()); + + return result; +} + +// just the tokens occurring in the suffix +struct suffix_info +{ + cpp_attribute_list attributes; + std::unique_ptr noexcept_condition; + cpp_function_body_kind body_kind; + cpp_cv cv_qualifier = cpp_cv_none; + cpp_reference ref_qualifier = cpp_ref_none; + cpp_virtual virtual_keywords; + + suffix_info(const CXCursor& cur) + : body_kind(clang_isCursorDefinition(cur) ? cpp_function_definition : cpp_function_declaration) + {} +}; + +cpp_cv parse_cv(detail::cxtoken_stream& stream) +{ + if (detail::skip_if(stream, "const")) { - cpp_attribute_list attributes; - std::unique_ptr noexcept_condition; - cpp_function_body_kind body_kind; - cpp_cv cv_qualifier = cpp_cv_none; - cpp_reference ref_qualifier = cpp_ref_none; - cpp_virtual virtual_keywords; - - suffix_info(const CXCursor& cur) - : body_kind(clang_isCursorDefinition(cur) ? cpp_function_definition : - cpp_function_declaration) - { - } - }; - - cpp_cv parse_cv(detail::cxtoken_stream& stream) + if (detail::skip_if(stream, "volatile")) + return cpp_cv_const_volatile; + else + return cpp_cv_const; + } + else if (detail::skip_if(stream, "volatile")) { if (detail::skip_if(stream, "const")) - { - if (detail::skip_if(stream, "volatile")) - return cpp_cv_const_volatile; - else - return cpp_cv_const; - } - else if (detail::skip_if(stream, "volatile")) - { - if (detail::skip_if(stream, "const")) - return cpp_cv_const_volatile; - else - return cpp_cv_volatile; - } + return cpp_cv_const_volatile; else - return cpp_cv_none; + return cpp_cv_volatile; } + else + return cpp_cv_none; +} - cpp_reference parse_ref(detail::cxtoken_stream& stream) +cpp_reference parse_ref(detail::cxtoken_stream& stream) +{ + if (detail::skip_if(stream, "&")) + return cpp_ref_lvalue; + else if (detail::skip_if(stream, "&&")) + return cpp_ref_rvalue; + else + return cpp_ref_none; +} + +std::unique_ptr parse_noexcept(detail::cxtoken_stream& stream, + const detail::parse_context& context) +{ + if (!detail::skip_if(stream, "noexcept")) + return nullptr; + + auto type = cpp_builtin_type::build(cpp_bool); + if (stream.peek().value() != "(") + return cpp_literal_expression::build(std::move(type), "true"); + + auto closing = detail::find_closing_bracket(stream); + + detail::skip(stream, "("); + auto expr = detail::parse_raw_expression(context, stream, closing, std::move(type)); + detail::skip(stream, ")"); + + return expr; +} + +cpp_function_body_kind parse_body_kind(detail::cxtoken_stream& stream, bool& pure_virtual) +{ + pure_virtual = false; + if (detail::skip_if(stream, "default")) + return cpp_function_defaulted; + else if (detail::skip_if(stream, "delete")) + return cpp_function_deleted; + else if (detail::skip_if(stream, "0")) { - if (detail::skip_if(stream, "&")) - return cpp_ref_lvalue; - else if (detail::skip_if(stream, "&&")) - return cpp_ref_rvalue; - else - return cpp_ref_none; - } - - std::unique_ptr parse_noexcept(detail::cxtoken_stream& stream, - const detail::parse_context& context) - { - if (!detail::skip_if(stream, "noexcept")) - return nullptr; - - auto type = cpp_builtin_type::build(cpp_bool); - if (stream.peek().value() != "(") - return cpp_literal_expression::build(std::move(type), "true"); - - auto closing = detail::find_closing_bracket(stream); - - detail::skip(stream, "("); - auto expr = detail::parse_raw_expression(context, stream, closing, std::move(type)); - detail::skip(stream, ")"); - - return expr; - } - - cpp_function_body_kind parse_body_kind(detail::cxtoken_stream& stream, bool& pure_virtual) - { - pure_virtual = false; - if (detail::skip_if(stream, "default")) - return cpp_function_defaulted; - else if (detail::skip_if(stream, "delete")) - return cpp_function_deleted; - else if (detail::skip_if(stream, "0")) - { - pure_virtual = true; - return cpp_function_declaration; - } - - DEBUG_UNREACHABLE(detail::parse_error_handler{}, stream.cursor(), - "unexpected token for function body kind"); + pure_virtual = true; return cpp_function_declaration; } - void parse_body(detail::cxtoken_stream& stream, suffix_info& result, bool allow_virtual) + DEBUG_UNREACHABLE(detail::parse_error_handler{}, stream.cursor(), + "unexpected token for function body kind"); + return cpp_function_declaration; +} + +void parse_body(detail::cxtoken_stream& stream, suffix_info& result, bool allow_virtual) +{ + auto pure_virtual = false; + result.body_kind = parse_body_kind(stream, pure_virtual); + if (pure_virtual) { - auto pure_virtual = false; - result.body_kind = parse_body_kind(stream, pure_virtual); - if (pure_virtual) - { - DEBUG_ASSERT(allow_virtual, detail::parse_error_handler{}, stream.cursor(), - "unexpected token"); - if (result.virtual_keywords) - result.virtual_keywords.value() |= cpp_virtual_flags::pure; - else - result.virtual_keywords = cpp_virtual_flags::pure; - } + DEBUG_ASSERT(allow_virtual, detail::parse_error_handler{}, stream.cursor(), + "unexpected token"); + if (result.virtual_keywords) + result.virtual_keywords.value() |= cpp_virtual_flags::pure; + else + result.virtual_keywords = cpp_virtual_flags::pure; + } +} + +// precondition: we've skipped the function parameters +suffix_info parse_suffix_info(detail::cxtoken_stream& stream, const detail::parse_context& context, + bool allow_qualifier, bool allow_virtual) +{ + suffix_info result(stream.cursor()); + + // syntax: + result.attributes = detail::parse_attributes(stream); + if (allow_qualifier) + { + result.cv_qualifier = parse_cv(stream); + result.ref_qualifier = parse_ref(stream); } - // precondition: we've skipped the function parameters - suffix_info parse_suffix_info(detail::cxtoken_stream& stream, - const detail::parse_context& context, bool allow_qualifier, - bool allow_virtual) + if (detail::skip_if(stream, "throw")) + // just because I can + detail::skip_brackets(stream); + result.noexcept_condition = parse_noexcept(stream, context); + + // check if we have leftovers of the return type + // i.e.: `void (*foo(int a, int b) const)(int)`; + // ^^^^^^- attributes + // ^^^^^^- leftovers + // if we have a closing parenthesis, skip brackets + if (detail::skip_if(stream, ")")) + detail::skip_brackets(stream); + + // check for trailing return type + if (detail::skip_if(stream, "->")) { - suffix_info result(stream.cursor()); - - // syntax: - result.attributes = detail::parse_attributes(stream); - if (allow_qualifier) + // this is rather tricky to skip + // so loop over all tokens and see if matching keytokens occur + // note that this isn't quite correct + // use a heuristic to skip brackets, which should be good enough + while (!stream.done()) { - result.cv_qualifier = parse_cv(stream); - result.ref_qualifier = parse_ref(stream); - } - - if (detail::skip_if(stream, "throw")) - // just because I can - detail::skip_brackets(stream); - result.noexcept_condition = parse_noexcept(stream, context); - - // check if we have leftovers of the return type - // i.e.: `void (*foo(int a, int b) const)(int)`; - // ^^^^^^- attributes - // ^^^^^^- leftovers - // if we have a closing parenthesis, skip brackets - if (detail::skip_if(stream, ")")) - detail::skip_brackets(stream); - - // check for trailing return type - if (detail::skip_if(stream, "->")) - { - // this is rather tricky to skip - // so loop over all tokens and see if matching keytokens occur - // note that this isn't quite correct - // use a heuristic to skip brackets, which should be good enough - while (!stream.done()) - { - auto attributes = detail::parse_attributes(stream); - if (!attributes.empty()) - result.attributes.insert(result.attributes.end(), attributes.begin(), - attributes.end()); - else if (stream.peek() == "(" || stream.peek() == "[" || stream.peek() == "<") - detail::skip_brackets(stream); - else if (stream.peek() == "{") - // begin of definition - break; - else if (detail::skip_if(stream, "override")) - { - DEBUG_ASSERT(allow_virtual, detail::parse_error_handler{}, stream.cursor(), - "unexpected token"); - if (result.virtual_keywords) - result.virtual_keywords.value() |= cpp_virtual_flags::override; - else - result.virtual_keywords = cpp_virtual_flags::override; - } - else if (detail::skip_if(stream, "final")) - { - DEBUG_ASSERT(allow_virtual, detail::parse_error_handler{}, stream.cursor(), - "unexpected token"); - if (result.virtual_keywords) - result.virtual_keywords.value() |= cpp_virtual_flags::final; - else - result.virtual_keywords = cpp_virtual_flags::final; - } - else if (detail::skip_if(stream, "=")) - parse_body(stream, result, allow_virtual); - else - stream.bump(); - } - if (stream.peek() == "{" || stream.peek() == ":" || stream.peek() == "try") - result.body_kind = cpp_function_definition; - } - else - { - // syntax: - if (detail::skip_if(stream, "override")) + auto attributes = detail::parse_attributes(stream); + if (!attributes.empty()) + result.attributes.insert(result.attributes.end(), attributes.begin(), + attributes.end()); + else if (stream.peek() == "(" || stream.peek() == "[" || stream.peek() == "<") + detail::skip_brackets(stream); + else if (stream.peek() == "{") + // begin of definition + break; + else if (detail::skip_if(stream, "override")) { DEBUG_ASSERT(allow_virtual, detail::parse_error_handler{}, stream.cursor(), "unexpected token"); - result.virtual_keywords = cpp_virtual_flags::override; - if (detail::skip_if(stream, "final")) - result.virtual_keywords.value() |= cpp_virtual_flags::final; + if (result.virtual_keywords) + result.virtual_keywords.value() |= cpp_virtual_flags::override; + else + result.virtual_keywords = cpp_virtual_flags::override; } else if (detail::skip_if(stream, "final")) { DEBUG_ASSERT(allow_virtual, detail::parse_error_handler{}, stream.cursor(), "unexpected token"); - result.virtual_keywords = cpp_virtual_flags::final; - if (detail::skip_if(stream, "override")) - result.virtual_keywords.value() |= cpp_virtual_flags::override; + if (result.virtual_keywords) + result.virtual_keywords.value() |= cpp_virtual_flags::final; + else + result.virtual_keywords = cpp_virtual_flags::final; } - - auto attributes = detail::parse_attributes(stream); - if (!attributes.empty()) - result.attributes.insert(result.attributes.end(), attributes.begin(), - attributes.end()); - - if (detail::skip_if(stream, "=")) + else if (detail::skip_if(stream, "=")) parse_body(stream, result, allow_virtual); - else if (detail::skip_if(stream, "{") || detail::skip_if(stream, ":") - || detail::skip_if(stream, "try")) - result.body_kind = cpp_function_definition; + else + stream.bump(); + } + if (stream.peek() == "{" || stream.peek() == ":" || stream.peek() == "try") + result.body_kind = cpp_function_definition; + } + else + { + // syntax: + if (detail::skip_if(stream, "override")) + { + DEBUG_ASSERT(allow_virtual, detail::parse_error_handler{}, stream.cursor(), + "unexpected token"); + result.virtual_keywords = cpp_virtual_flags::override; + if (detail::skip_if(stream, "final")) + result.virtual_keywords.value() |= cpp_virtual_flags::final; + } + else if (detail::skip_if(stream, "final")) + { + DEBUG_ASSERT(allow_virtual, detail::parse_error_handler{}, stream.cursor(), + "unexpected token"); + result.virtual_keywords = cpp_virtual_flags::final; + if (detail::skip_if(stream, "override")) + result.virtual_keywords.value() |= cpp_virtual_flags::override; } - return result; + auto attributes = detail::parse_attributes(stream); + if (!attributes.empty()) + result.attributes.insert(result.attributes.end(), attributes.begin(), attributes.end()); + + if (detail::skip_if(stream, "=")) + parse_body(stream, result, allow_virtual); + else if (detail::skip_if(stream, "{") || detail::skip_if(stream, ":") + || detail::skip_if(stream, "try")) + result.body_kind = cpp_function_definition; } - std::unique_ptr parse_cpp_function_impl(const detail::parse_context& context, - const CXCursor& cur, bool is_static, - bool is_friend) - { - auto name = detail::get_cursor_name(cur); - - detail::cxtokenizer tokenizer(context.tu, context.file, cur); - detail::cxtoken_stream stream(tokenizer, cur); - - auto prefix = parse_prefix_info(stream, name.c_str(), false); - DEBUG_ASSERT(!prefix.is_virtual && !prefix.is_explicit, detail::parse_error_handler{}, cur, - "free function cannot be virtual or explicit"); - - cpp_function::builder builder(name.c_str(), - detail::parse_type(context, cur, - clang_getCursorResultType(cur))); - context.comments.match(builder.get(), cur); - builder.get().add_attribute(prefix.attributes); - - add_parameters(context, builder, cur); - if (clang_Cursor_isVariadic(cur)) - builder.is_variadic(); - builder.storage_class(cpp_storage_class_specifiers( - detail::get_storage_class(cur) - | (is_static ? cpp_storage_class_static : cpp_storage_class_none))); - if (prefix.is_constexpr) - builder.is_constexpr(); - - skip_parameters(stream); - - auto suffix = parse_suffix_info(stream, context, false, false); - builder.get().add_attribute(suffix.attributes); - if (suffix.noexcept_condition) - builder.noexcept_condition(std::move(suffix.noexcept_condition)); - - if (is_templated_cursor(cur)) - return builder.finish(detail::get_entity_id(cur), suffix.body_kind, - parse_scope(cur, is_friend)); - else - return builder.finish(*context.idx, detail::get_entity_id(cur), suffix.body_kind, - parse_scope(cur, is_friend)); - } + return result; } +std::unique_ptr parse_cpp_function_impl(const detail::parse_context& context, + const CXCursor& cur, bool is_static, + bool is_friend) +{ + auto name = detail::get_cursor_name(cur); + + detail::cxtokenizer tokenizer(context.tu, context.file, cur); + detail::cxtoken_stream stream(tokenizer, cur); + + auto prefix = parse_prefix_info(stream, name.c_str(), false); + DEBUG_ASSERT(!prefix.is_virtual && !prefix.is_explicit, detail::parse_error_handler{}, cur, + "free function cannot be virtual or explicit"); + + cpp_function::builder builder(name.c_str(), + detail::parse_type(context, cur, clang_getCursorResultType(cur))); + context.comments.match(builder.get(), cur); + builder.get().add_attribute(prefix.attributes); + + add_parameters(context, builder, cur); + if (clang_Cursor_isVariadic(cur)) + builder.is_variadic(); + builder.storage_class(cpp_storage_class_specifiers( + detail::get_storage_class(cur) + | (is_static ? cpp_storage_class_static : cpp_storage_class_none))); + if (prefix.is_constexpr) + builder.is_constexpr(); + + skip_parameters(stream); + + auto suffix = parse_suffix_info(stream, context, false, false); + builder.get().add_attribute(suffix.attributes); + if (suffix.noexcept_condition) + builder.noexcept_condition(std::move(suffix.noexcept_condition)); + + if (is_templated_cursor(cur)) + return builder.finish(detail::get_entity_id(cur), suffix.body_kind, + parse_scope(cur, is_friend)); + else + return builder.finish(*context.idx, detail::get_entity_id(cur), suffix.body_kind, + parse_scope(cur, is_friend)); +} +} // namespace + std::unique_ptr detail::parse_cpp_function(const detail::parse_context& context, const CXCursor& cur, bool is_friend) { @@ -587,92 +576,91 @@ std::unique_ptr detail::try_parse_static_cpp_function( namespace { - bool overrides_function(const CXCursor& cur) +bool overrides_function(const CXCursor& cur) +{ + CXCursor* overrides = nullptr; + auto num = 0u; + clang_getOverriddenCursors(cur, &overrides, &num); + clang_disposeOverriddenCursors(overrides); + return num != 0u; +} + +cpp_virtual calculate_virtual(const CXCursor& cur, bool virtual_keyword, + const cpp_virtual& virtual_suffix) +{ + if (!clang_CXXMethod_isVirtual(cur) && !virtual_keyword && !virtual_suffix) + return {}; + else if (clang_CXXMethod_isPureVirtual(cur)) { - CXCursor* overrides = nullptr; - auto num = 0u; - clang_getOverriddenCursors(cur, &overrides, &num); - clang_disposeOverriddenCursors(overrides); - return num != 0u; + // pure virtual function - all information in the suffix + DEBUG_ASSERT(virtual_suffix.has_value() && virtual_suffix.value() & cpp_virtual_flags::pure, + detail::parse_error_handler{}, cur, "pure virtual not detected"); + return virtual_suffix; } - - cpp_virtual calculate_virtual(const CXCursor& cur, bool virtual_keyword, - const cpp_virtual& virtual_suffix) + else { - if (!clang_CXXMethod_isVirtual(cur) && !virtual_keyword && !virtual_suffix) - return {}; - else if (clang_CXXMethod_isPureVirtual(cur)) - { - // pure virtual function - all information in the suffix - DEBUG_ASSERT(virtual_suffix.has_value() - && virtual_suffix.value() & cpp_virtual_flags::pure, - detail::parse_error_handler{}, cur, "pure virtual not detected"); - return virtual_suffix; - } - else - { - // non-pure virtual function - DEBUG_ASSERT(!virtual_suffix.has_value() - || !(virtual_suffix.value() & cpp_virtual_flags::pure), - detail::parse_error_handler{}, cur, - "pure virtual function detected, even though it isn't"); - // calculate whether it overrides - auto overrides = !virtual_keyword - || (virtual_suffix.has_value() - && virtual_suffix.value() & cpp_virtual_flags::override) - || overrides_function(cur); + // non-pure virtual function + DEBUG_ASSERT(!virtual_suffix.has_value() + || !(virtual_suffix.value() & cpp_virtual_flags::pure), + detail::parse_error_handler{}, cur, + "pure virtual function detected, even though it isn't"); + // calculate whether it overrides + auto overrides = !virtual_keyword + || (virtual_suffix.has_value() + && virtual_suffix.value() & cpp_virtual_flags::override) + || overrides_function(cur); - // result are all the flags in the suffix - auto result = virtual_suffix; - if (!result) - // make sure it isn't empty - result.emplace(); - if (overrides) - // make sure it contains the override flag - result.value() |= cpp_virtual_flags::override; - return result; - } - } - - template - auto set_qualifier(int, Builder& b, cpp_cv cv, cpp_reference ref) - -> decltype(b.cv_ref_qualifier(cv, ref), true) - { - b.cv_ref_qualifier(cv, ref); - return true; - } - - template - bool set_qualifier(short, Builder&, cpp_cv, cpp_reference) - { - return false; - } - - template - std::unique_ptr handle_suffix(const detail::parse_context& context, - const CXCursor& cur, Builder& builder, - detail::cxtoken_stream& stream, bool is_virtual, - type_safe::optional semantic_parent) - { - auto allow_qualifiers = set_qualifier(0, builder, cpp_cv_none, cpp_ref_none); - - auto suffix = parse_suffix_info(stream, context, allow_qualifiers, true); - builder.get().add_attribute(suffix.attributes); - set_qualifier(0, builder, suffix.cv_qualifier, suffix.ref_qualifier); - if (suffix.noexcept_condition) - builder.noexcept_condition(move(suffix.noexcept_condition)); - if (auto virt = calculate_virtual(cur, is_virtual, suffix.virtual_keywords)) - builder.virtual_info(virt.value()); - - if (is_templated_cursor(cur)) - return builder.finish(detail::get_entity_id(cur), suffix.body_kind, - std::move(semantic_parent)); - else - return builder.finish(*context.idx, detail::get_entity_id(cur), suffix.body_kind, - std::move(semantic_parent)); + // result are all the flags in the suffix + auto result = virtual_suffix; + if (!result) + // make sure it isn't empty + result.emplace(); + if (overrides) + // make sure it contains the override flag + result.value() |= cpp_virtual_flags::override; + return result; } } +template +auto set_qualifier(int, Builder& b, cpp_cv cv, cpp_reference ref) + -> decltype(b.cv_ref_qualifier(cv, ref), true) +{ + b.cv_ref_qualifier(cv, ref); + return true; +} + +template +bool set_qualifier(short, Builder&, cpp_cv, cpp_reference) +{ + return false; +} + +template +std::unique_ptr handle_suffix(const detail::parse_context& context, const CXCursor& cur, + Builder& builder, detail::cxtoken_stream& stream, + bool is_virtual, + type_safe::optional semantic_parent) +{ + auto allow_qualifiers = set_qualifier(0, builder, cpp_cv_none, cpp_ref_none); + + auto suffix = parse_suffix_info(stream, context, allow_qualifiers, true); + builder.get().add_attribute(suffix.attributes); + set_qualifier(0, builder, suffix.cv_qualifier, suffix.ref_qualifier); + if (suffix.noexcept_condition) + builder.noexcept_condition(move(suffix.noexcept_condition)); + if (auto virt = calculate_virtual(cur, is_virtual, suffix.virtual_keywords)) + builder.virtual_info(virt.value()); + + if (is_templated_cursor(cur)) + return builder.finish(detail::get_entity_id(cur), suffix.body_kind, + std::move(semantic_parent)); + else + return builder.finish(*context.idx, detail::get_entity_id(cur), suffix.body_kind, + std::move(semantic_parent)); +} +} // namespace + std::unique_ptr detail::parse_cpp_member_function(const detail::parse_context& context, const CXCursor& cur, bool is_friend) { diff --git a/src/libclang/language_linkage_parser.cpp b/src/libclang/language_linkage_parser.cpp index d88c346..a393935 100644 --- a/src/libclang/language_linkage_parser.cpp +++ b/src/libclang/language_linkage_parser.cpp @@ -4,8 +4,8 @@ #include "parse_functions.hpp" -#include #include +#include #include "libclang_visitor.hpp" diff --git a/src/libclang/libclang_parser.cpp b/src/libclang/libclang_parser.cpp index de1a41b..1bbf585 100644 --- a/src/libclang/libclang_parser.cpp +++ b/src/libclang/libclang_parser.cpp @@ -10,12 +10,12 @@ #include +#include "cxtokenizer.hpp" #include "libclang_visitor.hpp" -#include "raii_wrapper.hpp" #include "parse_error.hpp" #include "parse_functions.hpp" #include "preprocessor.hpp" -#include "cxtokenizer.hpp" +#include "raii_wrapper.hpp" using namespace cppast; @@ -81,22 +81,20 @@ bool libclang_compilation_database::has_config(const char* file_name) const namespace { - int parse_number(const char*& str) +int parse_number(const char*& str) +{ + auto result = 0; + for (; *str && *str != '.'; ++str) { - auto result = 0; - for (; *str && *str != '.'; ++str) - { - result *= 10; - result += int(*str - '0'); - } - return result; + result *= 10; + result += int(*str - '0'); } + return result; +} } // namespace libclang_compile_config::libclang_compile_config() -: compile_config({}), - write_preprocessed_(false), - fast_preprocessing_(false), +: compile_config({}), write_preprocessed_(false), fast_preprocessing_(false), remove_comments_in_macro_(false) { // set given clang binary @@ -117,39 +115,38 @@ libclang_compile_config::libclang_compile_config() namespace { - struct cxcompile_commands_deleter +struct cxcompile_commands_deleter +{ + void operator()(CXCompileCommands cmds) { - void operator()(CXCompileCommands cmds) - { - clang_CompileCommands_dispose(cmds); - } - }; - - using cxcompile_commands = detail::raii_wrapper; - - bool has_drive_prefix(const std::string& file) - { - return file.size() > 2 && file[1] == ':'; + clang_CompileCommands_dispose(cmds); } +}; - bool is_absolute(const std::string& file) - { - return !file.empty() - && (has_drive_prefix(file) || file.front() == '/' || file.front() == '\\'); - } +using cxcompile_commands = detail::raii_wrapper; - 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; - } +bool has_drive_prefix(const std::string& file) +{ + return file.size() > 2 && file[1] == ':'; +} + +bool is_absolute(const std::string& file) +{ + return !file.empty() && (has_drive_prefix(file) || file.front() == '/' || file.front() == '\\'); +} + +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 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 { - bool is_flag(const detail::cxstring& str) - { - return str.length() > 1u && str[0] == '-'; - } +bool is_flag(const detail::cxstring& str) +{ + return str.length() > 1u && str[0] == '-'; +} - const char* find_flag_arg_sep(const std::string& last_flag) - { - if (last_flag[1] == 'D') - // no separator, equal is part of the arg - return nullptr; - return std::strchr(last_flag.c_str(), '='); - } +const char* find_flag_arg_sep(const std::string& last_flag) +{ + if (last_flag[1] == 'D') + // no separator, equal is part of the arg + return nullptr; + return std::strchr(last_flag.c_str(), '='); +} - template - void parse_flags(CXCompileCommand cmd, Func callback) +template +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); - std::string last_flag; - for (auto i = 1u /* 0 is compiler executable */; i != no_args; ++i) + detail::cxstring str(clang_CompileCommand_getArg(cmd, i)); + if (is_flag(str)) { - detail::cxstring str(clang_CompileCommand_getArg(cmd, i)); - if (is_flag(str)) + if (!last_flag.empty()) { - if (!last_flag.empty()) + // process last flag + std::string args; + if (auto ptr = find_flag_arg_sep(last_flag)) { - // process last flag - std::string args; - if (auto ptr = find_flag_arg_sep(last_flag)) - { - auto pos = std::size_t(ptr - last_flag.c_str()); - ++ptr; - while (*ptr) - args += *ptr++; - last_flag.erase(pos); - } - else if (last_flag.size() > 2u) - { - // assume two character flag - args = last_flag.substr(2u); - last_flag.erase(2u); - } - - callback(std::move(last_flag), std::move(args)); + auto pos = std::size_t(ptr - last_flag.c_str()); + ++ptr; + while (*ptr) + args += *ptr++; + last_flag.erase(pos); + } + else if (last_flag.size() > 2u) + { + // assume two character flag + args = last_flag.substr(2u); + last_flag.erase(2u); } - last_flag = str.std_str(); + callback(std::move(last_flag), std::move(args)); } - else if (!last_flag.empty()) - { - // we have flags + args - callback(std::move(last_flag), str.std_str()); - last_flag.clear(); - } - // else skip argument + + last_flag = str.std_str(); } + else if (!last_flag.empty()) + { + // we have flags + args + callback(std::move(last_flag), str.std_str()); + last_flag.clear(); + } + // else skip argument } +} } // namespace libclang_compile_config::libclang_compile_config(const libclang_compilation_database& database, const std::string& file) : libclang_compile_config() { - auto cxcommands = - clang_CompilationDatabase_getCompileCommands(database.database_, file.c_str()); + auto cxcommands + = clang_CompilationDatabase_getCompileCommands(database.database_, file.c_str()); if (cxcommands == nullptr) throw libclang_error(detail::format("no compile commands specified for file '", file, "'")); cxcompile_commands commands(cxcommands); @@ -353,8 +350,8 @@ type_safe::optional cppast::find_config_for( if (database.has_config(file_name)) return libclang_compile_config(database, std::move(file_name)); - static const char* extensions[] = {".h", ".hpp", ".cpp", ".h++", ".c++", ".hxx", - ".cxx", ".hh", ".cc", ".H", ".C"}; + static const char* extensions[] + = {".h", ".hpp", ".cpp", ".h++", ".c++", ".hxx", ".cxx", ".hh", ".cc", ".H", ".C"}; for (auto ext : extensions) { auto name = file_name + ext; @@ -370,125 +367,122 @@ struct libclang_parser::impl detail::cxindex index; impl() : index(clang_createIndex(0, 0)) // no diagnostic, other one is irrelevant - { - } + {} }; libclang_parser::libclang_parser() : libclang_parser(default_logger()) {} libclang_parser::libclang_parser(type_safe::object_ref logger) : parser(logger), pimpl_(new impl) -{ -} +{} libclang_parser::~libclang_parser() noexcept {} namespace { - std::vector get_arguments(const libclang_compile_config& config) +std::vector get_arguments(const libclang_compile_config& config) +{ + std::vector 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 get_severity(const CXDiagnostic& diag) +{ + switch (clang_getDiagnosticSeverity(diag)) { - std::vector 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 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{}); + 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; } - void print_diagnostics(const diagnostic_logger& logger, const CXTranslationUnit& tu) - { - 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); + DEBUG_UNREACHABLE(detail::assert_handler{}); + return type_safe::nullopt; +} - 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()}); - } +void print_diagnostics(const diagnostic_logger& logger, const CXTranslationUnit& tu) +{ + 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); + 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, - const detail::cxindex& idx, - const libclang_compile_config& config, const char* path, - const std::string& source) +detail::cxtranslation_unit get_cxunit(const diagnostic_logger& logger, const detail::cxindex& idx, + const libclang_compile_config& config, const char* path, + 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(args.size()), // arguments (ptr + size) + &file, 1, // unsaved files (ptr + size) + unsigned(flags), &tu); + if (error != CXError_Success) { - 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(args.size()), // arguments (ptr + size) - &file, 1, // unsaved files (ptr + size) - unsigned(flags), &tu); - if (error != CXError_Success) + switch (error) { - switch (error) - { - case CXError_Success: - DEBUG_UNREACHABLE(detail::assert_handler{}); - break; + case CXError_Success: + DEBUG_UNREACHABLE(detail::assert_handler{}); + break; - case CXError_Failure: - throw libclang_error("clang_parseTranslationUnit: generic error"); - case CXError_Crashed: - throw libclang_error("clang_parseTranslationUnit: libclang crashed :("); - case CXError_InvalidArguments: - throw libclang_error("clang_parseTranslationUnit: you shouldn't see this message"); - case CXError_ASTReadError: - throw libclang_error("clang_parseTranslationUnit: AST deserialization error"); - } + case CXError_Failure: + throw libclang_error("clang_parseTranslationUnit: generic error"); + case CXError_Crashed: + throw libclang_error("clang_parseTranslationUnit: libclang crashed :("); + case CXError_InvalidArguments: + throw libclang_error("clang_parseTranslationUnit: you shouldn't see this message"); + case CXError_ASTReadError: + 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) - { - auto loc = clang_getCursorLocation(cursor); + return detail::cxtranslation_unit(tu); +} - unsigned line; - clang_getPresumedLocation(loc, nullptr, &line, nullptr); - return line; - } +unsigned get_line_no(const CXCursor& cursor) +{ + auto loc = clang_getCursorLocation(cursor); + + unsigned line; + clang_getPresumedLocation(loc, nullptr, &line, nullptr); + return line; +} } // namespace std::unique_ptr libclang_parser::do_parse(const cpp_entity_index& idx, std::string path, @@ -530,8 +524,8 @@ std::unique_ptr libclang_parser::do_parse(const cpp_entity_index& idx, && get_line_no(cur) >= include_iter->line, detail::assert_handler{}); - auto full_path = include_iter->full_path.empty() ? include_iter->file_name : - include_iter->full_path; + auto full_path = include_iter->full_path.empty() ? include_iter->file_name + : include_iter->full_path; // if we got an absolute file path for the current file, // also use an absolute file path for the id @@ -543,10 +537,10 @@ std::unique_ptr libclang_parser::do_parse(const cpp_entity_index& idx, else id = cpp_entity_id(include_iter->file_name.c_str()); - auto include = - cpp_include_directive::build(cpp_file_ref(id, - std::move(include_iter->file_name)), - include_iter->kind, std::move(full_path)); + auto include + = cpp_include_directive::build(cpp_file_ref(id, + std::move(include_iter->file_name)), + include_iter->kind, std::move(full_path)); context.comments.match(*include, include_iter->line, false); // must not skip comments, // includes are not reported in order diff --git a/src/libclang/libclang_visitor.hpp b/src/libclang/libclang_visitor.hpp index a51a050..96a54eb 100644 --- a/src/libclang/libclang_visitor.hpp +++ b/src/libclang/libclang_visitor.hpp @@ -11,50 +11,50 @@ namespace cppast { - namespace detail +namespace detail +{ + // visits direct children of an entity + template + void visit_children(CXCursor parent, Func f, bool recurse = false) { - // visits direct children of an entity - template - void visit_children(CXCursor parent, Func f, bool recurse = false) - { - auto continue_lambda = [](CXCursor cur, CXCursor, CXClientData data) { - auto& actual_cb = *static_cast(data); - actual_cb(cur); - return CXChildVisit_Continue; - }; - auto recurse_lambda = [](CXCursor cur, CXCursor, CXClientData data) { - auto& actual_cb = *static_cast(data); - actual_cb(cur); - return CXChildVisit_Recurse; - }; + auto continue_lambda = [](CXCursor cur, CXCursor, CXClientData data) { + auto& actual_cb = *static_cast(data); + actual_cb(cur); + return CXChildVisit_Continue; + }; + auto recurse_lambda = [](CXCursor cur, CXCursor, CXClientData data) { + auto& actual_cb = *static_cast(data); + actual_cb(cur); + return CXChildVisit_Recurse; + }; - if (recurse) - clang_visitChildren(parent, recurse_lambda, &f); - else - clang_visitChildren(parent, continue_lambda, &f); - } - - // visits a translation unit - // notes: only visits if directly defined in file, not included - template - 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); - }); - } + if (recurse) + clang_visitChildren(parent, recurse_lambda, &f); + else + clang_visitChildren(parent, continue_lambda, &f); } -} // namespace cppast::detail + + // visits a translation unit + // notes: only visits if directly defined in file, not included + template + 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 diff --git a/src/libclang/namespace_parser.cpp b/src/libclang/namespace_parser.cpp index c78a0a6..4423c98 100644 --- a/src/libclang/namespace_parser.cpp +++ b/src/libclang/namespace_parser.cpp @@ -13,49 +13,48 @@ using namespace cppast; namespace { - cpp_namespace::builder make_ns_builder(const detail::parse_context& context, - const CXCursor& cur) +cpp_namespace::builder make_ns_builder(const detail::parse_context& context, const CXCursor& cur) +{ + detail::cxtokenizer tokenizer(context.tu, context.file, cur); + detail::cxtoken_stream stream(tokenizer, cur); + // [inline] namespace|:: [] [{] + + 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); - detail::cxtoken_stream stream(tokenizer, cur); - // [inline] namespace|:: [] [{] - - 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); - - // { - // 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; + is_nested = true; + skip(stream, "::"); } + + auto attributes = parse_attributes(stream); + + // { + // 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 detail::parse_cpp_namespace(const detail::parse_context& context, cpp_entity& parent, const CXCursor& cur) @@ -83,27 +82,27 @@ std::unique_ptr detail::parse_cpp_namespace(const detail::parse_cont namespace { - cpp_entity_id parse_ns_target_cursor(const CXCursor& cur) - { - cpp_entity_id result(""); - detail::visit_children(cur, - [&](const CXCursor& child) { - auto referenced = clang_getCursorReferenced(child); - auto kind = clang_getCursorKind(referenced); - if (kind == CXCursor_Namespace) - result = detail::get_entity_id(referenced); - else if (kind == CXCursor_NamespaceAlias) - // get target of namespace alias instead - result = parse_ns_target_cursor(referenced); - else - DEBUG_UNREACHABLE(detail::parse_error_handler{}, cur, - "unexpected target for namespace " - "alias/using directive"); - }, - true); - return result; - } +cpp_entity_id parse_ns_target_cursor(const CXCursor& cur) +{ + cpp_entity_id result(""); + detail::visit_children(cur, + [&](const CXCursor& child) { + auto referenced = clang_getCursorReferenced(child); + auto kind = clang_getCursorKind(referenced); + if (kind == CXCursor_Namespace) + result = detail::get_entity_id(referenced); + else if (kind == CXCursor_NamespaceAlias) + // get target of namespace alias instead + result = parse_ns_target_cursor(referenced); + else + DEBUG_UNREACHABLE(detail::parse_error_handler{}, cur, + "unexpected target for namespace " + "alias/using directive"); + }, + true); + return result; } +} // namespace std::unique_ptr detail::parse_cpp_namespace_alias(const detail::parse_context& context, const CXCursor& cur) @@ -155,54 +154,53 @@ std::unique_ptr detail::parse_cpp_using_directive(const detail::pars namespace { - cpp_entity_ref parse_entity_target_cursor(const CXCursor& cur, std::string name) - { - type_safe::deferred_construction result; - detail::visit_children(cur, - [&](const CXCursor& child) { - if (result) - return; +cpp_entity_ref parse_entity_target_cursor(const CXCursor& cur, std::string name) +{ + type_safe::deferred_construction result; + detail::visit_children(cur, + [&](const CXCursor& child) { + if (result) + return; - switch (clang_getCursorKind(child)) - { - case CXCursor_TypeRef: - case CXCursor_TemplateRef: - case CXCursor_MemberRef: - case CXCursor_VariableRef: - case CXCursor_DeclRefExpr: - { - auto referenced = clang_getCursorReferenced(child); - result = cpp_entity_ref(detail::get_entity_id(referenced), - std::move(name)); - break; - } + switch (clang_getCursorKind(child)) + { + case CXCursor_TypeRef: + case CXCursor_TemplateRef: + case CXCursor_MemberRef: + case CXCursor_VariableRef: + case CXCursor_DeclRefExpr: + { + auto referenced = clang_getCursorReferenced(child); + result = cpp_entity_ref(detail::get_entity_id(referenced), + std::move(name)); + break; + } - case CXCursor_OverloadedDeclRef: - { - auto size = clang_getNumOverloadedDecls(child); - DEBUG_ASSERT(size >= 1u, detail::parse_error_handler{}, cur, - "no target for using declaration"); - std::vector ids; - for (auto i = 0u; i != size; ++i) - ids.push_back(detail::get_entity_id( - clang_getOverloadedDecl(child, i))); - result = cpp_entity_ref(std::move(ids), std::move(name)); - break; - } + case CXCursor_OverloadedDeclRef: + { + auto size = clang_getNumOverloadedDecls(child); + DEBUG_ASSERT(size >= 1u, detail::parse_error_handler{}, cur, + "no target for using declaration"); + std::vector ids; + for (auto i = 0u; i != size; ++i) + ids.push_back(detail::get_entity_id( + clang_getOverloadedDecl(child, i))); + result = cpp_entity_ref(std::move(ids), std::move(name)); + break; + } - case CXCursor_NamespaceRef: - break; // wait for children + case CXCursor_NamespaceRef: + break; // wait for children - default: - DEBUG_UNREACHABLE(detail::parse_error_handler{}, cur, - "unexpected target for using declaration"); - } - - }, - true); - return result.value(); - } + default: + DEBUG_UNREACHABLE(detail::parse_error_handler{}, cur, + "unexpected target for using declaration"); + } + }, + true); + return result.value(); } +} // namespace std::unique_ptr detail::parse_cpp_using_declaration( const detail::parse_context& context, const CXCursor& cur) diff --git a/src/libclang/parse_error.hpp b/src/libclang/parse_error.hpp index 4f63766..72c330f 100644 --- a/src/libclang/parse_error.hpp +++ b/src/libclang/parse_error.hpp @@ -7,89 +7,86 @@ #include -#include #include +#include #include "debug_helper.hpp" 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; - unsigned line; - clang_getPresumedLocation(loc, &file, &line, nullptr); + CXString file; + unsigned line; + clang_getPresumedLocation(loc, &file, &line, nullptr); - 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)); - } - }; + return source_location::make_file(cxstring(file).c_str(), line); } -} // 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 diff --git a/src/libclang/parse_functions.cpp b/src/libclang/parse_functions.cpp index 72aee15..56b7acd 100644 --- a/src/libclang/parse_functions.cpp +++ b/src/libclang/parse_functions.cpp @@ -4,8 +4,8 @@ #include "parse_functions.hpp" -#include #include +#include #include "libclang_visitor.hpp" @@ -100,16 +100,16 @@ void detail::comment_context::match(cpp_entity& e, unsigned line, bool skip_comm namespace { - bool is_friend(const CXCursor& parent_cur) - { +bool is_friend(const CXCursor& parent_cur) +{ #if CPPAST_CINDEX_HAS_FRIEND - return clang_getCursorKind(parent_cur) == CXCursor_FriendDecl; + return clang_getCursorKind(parent_cur) == CXCursor_FriendDecl; #else - (void)parent_cur; - return false; + (void)parent_cur; + return false; #endif - } } +} // namespace std::unique_ptr detail::parse_entity(const detail::parse_context& context, cpp_entity* parent, const CXCursor& cur, @@ -165,25 +165,25 @@ std::unique_ptr detail::parse_entity(const detail::parse_context& co return parse_cpp_member_variable(context, cur); case CXCursor_FunctionDecl: - if (auto tfunc = - try_parse_cpp_function_template_specialization(context, cur, is_friend(parent_cur))) + if (auto tfunc + = try_parse_cpp_function_template_specialization(context, cur, is_friend(parent_cur))) return tfunc; return parse_cpp_function(context, cur, is_friend(parent_cur)); case CXCursor_CXXMethod: - if (auto tfunc = - try_parse_cpp_function_template_specialization(context, cur, is_friend(parent_cur))) + if (auto tfunc + = try_parse_cpp_function_template_specialization(context, cur, is_friend(parent_cur))) return tfunc; else if (auto func = try_parse_static_cpp_function(context, cur)) return func; return parse_cpp_member_function(context, cur, is_friend(parent_cur)); case CXCursor_ConversionFunction: - if (auto tfunc = - try_parse_cpp_function_template_specialization(context, cur, is_friend(parent_cur))) + if (auto tfunc + = try_parse_cpp_function_template_specialization(context, cur, is_friend(parent_cur))) return tfunc; return parse_cpp_conversion_op(context, cur, is_friend(parent_cur)); case CXCursor_Constructor: - if (auto tfunc = - try_parse_cpp_function_template_specialization(context, cur, is_friend(parent_cur))) + if (auto tfunc + = try_parse_cpp_function_template_specialization(context, cur, is_friend(parent_cur))) return tfunc; return parse_cpp_constructor(context, cur, is_friend(parent_cur)); case CXCursor_Destructor: diff --git a/src/libclang/parse_functions.hpp b/src/libclang/parse_functions.hpp index 7814960..e2f6598 100644 --- a/src/libclang/parse_functions.hpp +++ b/src/libclang/parse_functions.hpp @@ -8,166 +8,161 @@ #include #include -#include "raii_wrapper.hpp" #include "cxtokenizer.hpp" // for convenience #include "parse_error.hpp" // for convenience #include "preprocessor.hpp" +#include "raii_wrapper.hpp" #if CINDEX_VERSION_MINOR >= 36 -#define CPPAST_CINDEX_HAS_FRIEND 1 +# define CPPAST_CINDEX_HAS_FRIEND 1 #else -#define CPPAST_CINDEX_HAS_FRIEND 0 +# define CPPAST_CINDEX_HAS_FRIEND 0 #endif namespace cppast { - class cpp_expression; - class cpp_type; - enum cpp_storage_class_specifiers : int; +class cpp_expression; +class cpp_type; +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& comments) + : cur_(comments.data()), end_(comments.data() + comments.size()) + {} - // 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); + // must be called for entities that want an associated comment + // must be called *BEFORE* the children are added + void match(cpp_entity& e, const CXCursor& cur) const; + void match(cpp_entity& e, unsigned line, bool skip_comments = true) const; - // note: does not handle thread_local - cpp_storage_class_specifiers get_storage_class(const CXCursor& cur); + private: + mutable pp_doc_comment* cur_; + pp_doc_comment* end_; + }; - class comment_context - { - public: - explicit comment_context(std::vector& comments) - : cur_(comments.data()), end_(comments.data() + comments.size()) - { - } + struct parse_context + { + CXTranslationUnit tu; + CXFile file; + type_safe::object_ref logger; + type_safe::object_ref idx; + comment_context comments; + mutable bool error; + }; - // must be called for entities that want an associated comment - // must be called *BEFORE* the children are added - void match(cpp_entity& e, const CXCursor& cur) const; - void match(cpp_entity& e, unsigned line, bool skip_comments = true) const; + // parse default value of variable, function parameter... + std::unique_ptr parse_default_value(cpp_attribute_list& attributes, + const parse_context& context, + const CXCursor& cur, const char* name); - private: - mutable pp_doc_comment* cur_; - pp_doc_comment* end_; - }; + std::unique_ptr parse_type(const parse_context& context, const CXCursor& cur, + const CXType& type); - struct parse_context - { - CXTranslationUnit tu; - CXFile file; - type_safe::object_ref logger; - type_safe::object_ref idx; - comment_context comments; - mutable bool error; - }; + // 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 parse_raw_type(const parse_context& context, cxtoken_stream& stream, + cxtoken_iterator end); - // parse default value of variable, function parameter... - std::unique_ptr parse_default_value(cpp_attribute_list& attributes, - const parse_context& context, - const CXCursor& cur, const char* name); - - std::unique_ptr 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 parse_raw_type(const parse_context& context, - cxtoken_stream& stream, cxtoken_iterator end); - - std::unique_ptr 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 parse_raw_expression(const parse_context& context, - cxtoken_stream& stream, - cxtoken_iterator end, - std::unique_ptr 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 try_parse_cpp_language_linkage(const parse_context& context, - const CXCursor& cur); - // CXXMethod - std::unique_ptr try_parse_static_cpp_function(const parse_context& context, - const CXCursor& cur); - - // on all function cursors except on destructor - std::unique_ptr try_parse_cpp_function_template_specialization( - const parse_context& context, const CXCursor& cur, bool is_friend); - - // on class cursors - std::unique_ptr try_parse_full_cpp_class_template_specialization( - const parse_context& context, const CXCursor& cur); - - std::unique_ptr parse_cpp_namespace(const parse_context& context, - cpp_entity& parent, const CXCursor& cur); - std::unique_ptr parse_cpp_namespace_alias(const parse_context& context, - const CXCursor& cur); - std::unique_ptr parse_cpp_using_directive(const parse_context& context, - const CXCursor& cur); - std::unique_ptr parse_cpp_using_declaration(const parse_context& context, - const CXCursor& cur); - - std::unique_ptr parse_cpp_type_alias(const parse_context& context, - const CXCursor& cur, - const CXCursor& template_cur); - std::unique_ptr parse_cpp_enum(const parse_context& context, - const CXCursor& cur); - std::unique_ptr parse_cpp_class(const parse_context& context, - const CXCursor& cur, - const CXCursor& parent_cur); - - std::unique_ptr parse_cpp_variable(const parse_context& context, - const CXCursor& cur); - // also parses bitfields - std::unique_ptr parse_cpp_member_variable(const parse_context& context, - const CXCursor& cur); - - std::unique_ptr parse_cpp_function(const parse_context& context, - const CXCursor& cur, bool is_friend); - std::unique_ptr parse_cpp_member_function(const parse_context& context, - const CXCursor& cur, bool is_friend); - std::unique_ptr parse_cpp_conversion_op(const parse_context& context, - const CXCursor& cur, bool is_friend); - std::unique_ptr parse_cpp_constructor(const parse_context& context, - const CXCursor& cur, bool is_friend); - std::unique_ptr parse_cpp_destructor(const parse_context& context, - const CXCursor& cur, bool is_friend); - - std::unique_ptr parse_cpp_friend(const parse_context& context, + std::unique_ptr 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 parse_raw_expression(const parse_context& context, + cxtoken_stream& stream, + cxtoken_iterator end, + std::unique_ptr type); - std::unique_ptr parse_cpp_alias_template(const parse_context& context, - const CXCursor& cur); - std::unique_ptr parse_cpp_function_template(const parse_context& context, - const CXCursor& cur, - bool is_friend); - std::unique_ptr parse_cpp_class_template(const parse_context& context, - const CXCursor& cur); - std::unique_ptr parse_cpp_class_template_specialization( - const parse_context& context, const CXCursor& cur); + // 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 - std::unique_ptr parse_cpp_static_assert(const parse_context& context, + // unexposed + std::unique_ptr try_parse_cpp_language_linkage(const parse_context& context, + const CXCursor& cur); + // CXXMethod + std::unique_ptr try_parse_static_cpp_function(const parse_context& context, + const CXCursor& cur); + + // on all function cursors except on destructor + std::unique_ptr try_parse_cpp_function_template_specialization( + const parse_context& context, const CXCursor& cur, bool is_friend); + + // on class cursors + std::unique_ptr try_parse_full_cpp_class_template_specialization( + const parse_context& context, const CXCursor& cur); + + std::unique_ptr parse_cpp_namespace(const parse_context& context, + cpp_entity& parent, const CXCursor& cur); + std::unique_ptr parse_cpp_namespace_alias(const parse_context& context, + const CXCursor& cur); + std::unique_ptr parse_cpp_using_directive(const parse_context& context, + const CXCursor& cur); + std::unique_ptr parse_cpp_using_declaration(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 parse_entity( - const parse_context& context, cpp_entity* parent, const CXCursor& cur, - const CXCursor& parent_cur = clang_getNullCursor()); - } -} // namespace cppast::detail + std::unique_ptr parse_cpp_type_alias(const parse_context& context, + const CXCursor& cur, + const CXCursor& template_cur); + std::unique_ptr parse_cpp_enum(const parse_context& context, const CXCursor& cur); + std::unique_ptr parse_cpp_class(const parse_context& context, const CXCursor& cur, + const CXCursor& parent_cur); + + std::unique_ptr parse_cpp_variable(const parse_context& context, + const CXCursor& cur); + // also parses bitfields + std::unique_ptr parse_cpp_member_variable(const parse_context& context, + const CXCursor& cur); + + std::unique_ptr parse_cpp_function(const parse_context& context, + const CXCursor& cur, bool is_friend); + std::unique_ptr parse_cpp_member_function(const parse_context& context, + const CXCursor& cur, bool is_friend); + std::unique_ptr parse_cpp_conversion_op(const parse_context& context, + const CXCursor& cur, bool is_friend); + std::unique_ptr parse_cpp_constructor(const parse_context& context, + const CXCursor& cur, bool is_friend); + std::unique_ptr parse_cpp_destructor(const parse_context& context, + const CXCursor& cur, bool is_friend); + + std::unique_ptr parse_cpp_friend(const parse_context& context, const CXCursor& cur); + + std::unique_ptr parse_cpp_alias_template(const parse_context& context, + const CXCursor& cur); + std::unique_ptr parse_cpp_function_template(const parse_context& context, + const CXCursor& cur, bool is_friend); + std::unique_ptr parse_cpp_class_template(const parse_context& context, + const CXCursor& cur); + std::unique_ptr parse_cpp_class_template_specialization( + const parse_context& context, const CXCursor& cur); + + std::unique_ptr 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 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 diff --git a/src/libclang/preprocessor.cpp b/src/libclang/preprocessor.cpp index 77dc085..5b53d32 100644 --- a/src/libclang/preprocessor.cpp +++ b/src/libclang/preprocessor.cpp @@ -32,1040 +32,1033 @@ bool detail::pp_doc_comment::matches(const cpp_entity&, unsigned e_line) namespace { - //=== diagnostic parsing ===// - source_location parse_source_location(const char*& ptr) +//=== diagnostic parsing ===// +source_location parse_source_location(const char*& ptr) +{ + // format: (): + // or: : + auto fallback = ptr; + std::string filename; + while (*ptr && *ptr != ':' && *ptr != '(') + filename.push_back(*ptr++); + + if (filename == "error" || filename == "warning" || filename == "fatal error") { - // format: (): - // or: : - auto fallback = ptr; - std::string filename; - while (*ptr && *ptr != ':' && *ptr != '(') - filename.push_back(*ptr++); - - if (filename == "error" || filename == "warning" || filename == "fatal error") - { - ptr = fallback; - return {}; - } - - type_safe::optional line; - if (*ptr == '(') - { - ++ptr; - - std::string str; - while (*ptr != ')') - str.push_back(*ptr++); - ++ptr; - - line = unsigned(std::stoi(str)); - } - - DEBUG_ASSERT(*ptr == ':', detail::assert_handler{}); - ++ptr; - - return {type_safe::nullopt, std::move(filename), std::move(line), type_safe::nullopt}; + ptr = fallback; + return {}; } - severity parse_severity(const char*& ptr) + type_safe::optional line; + if (*ptr == '(') { - // format: : - auto fallback = ptr; - std::string sev; - while (*ptr && *ptr != ':') - sev.push_back(*ptr++); ++ptr; - if (sev == "warning") - return severity::warning; - else if (sev == "error") - return severity::error; - else if (sev == "fatal error") - return severity::critical; - else - ptr = fallback; + std::string str; + while (*ptr != ')') + str.push_back(*ptr++); + ++ptr; + + line = unsigned(std::stoi(str)); + } + + DEBUG_ASSERT(*ptr == ':', detail::assert_handler{}); + ++ptr; + + return {type_safe::nullopt, std::move(filename), std::move(line), type_safe::nullopt}; +} + +severity parse_severity(const char*& ptr) +{ + // format: : + auto fallback = ptr; + std::string sev; + while (*ptr && *ptr != ':') + sev.push_back(*ptr++); + ++ptr; + + if (sev == "warning") + return severity::warning; + else if (sev == "error") return severity::error; - } + else if (sev == "fatal error") + return severity::critical; + else + ptr = fallback; + return severity::error; +} - // parse and log diagnostic - void log_diagnostic(const diagnostic_logger& logger, const std::string& msg) - { - auto ptr = msg.c_str(); +// parse and log diagnostic +void log_diagnostic(const diagnostic_logger& logger, const std::string& msg) +{ + auto ptr = msg.c_str(); - auto loc = parse_source_location(ptr); - while (*ptr == ' ') - ++ptr; - - auto sev = parse_severity(ptr); - while (*ptr == ' ') - ++ptr; - - std::string message; - while (*ptr && *ptr != '\n') - message.push_back(*ptr++); - - logger.log("preprocessor", diagnostic{std::move(message), std::move(loc), sev}); - } - - // parses missing header file diagnostic and returns the file name, - // if it is a missing header file diagnostic - ts::optional parse_missing_file(const std::string& cur_file, - const std::string& msg) - { - auto ptr = msg.c_str(); - - auto loc = parse_source_location(ptr); - if (loc.file != cur_file) - return type_safe::nullopt; - - while (*ptr == ' ') - ++ptr; - - parse_severity(ptr); - while (*ptr == ' ') - ++ptr; - - // format 'file-name' file not found - if (*ptr != '\'') - return ts::nullopt; + auto loc = parse_source_location(ptr); + while (*ptr == ' ') ++ptr; - std::string filename; - while (*ptr != '\'') - filename += *ptr++; + auto sev = parse_severity(ptr); + while (*ptr == ' ') ++ptr; - if (std::strcmp(ptr, " file not found") == 0) - return std::move(filename); - else - throw libclang_error("preprocessor: unexpected diagnostic '" + msg + "'"); + std::string message; + while (*ptr && *ptr != '\n') + message.push_back(*ptr++); + + logger.log("preprocessor", diagnostic{std::move(message), std::move(loc), sev}); +} + +// parses missing header file diagnostic and returns the file name, +// if it is a missing header file diagnostic +ts::optional parse_missing_file(const std::string& cur_file, const std::string& msg) +{ + auto ptr = msg.c_str(); + + auto loc = parse_source_location(ptr); + if (loc.file != cur_file) + return type_safe::nullopt; + + while (*ptr == ' ') + ++ptr; + + parse_severity(ptr); + while (*ptr == ' ') + ++ptr; + + // format 'file-name' file not found + if (*ptr != '\'') + return ts::nullopt; + ++ptr; + + std::string filename; + while (*ptr != '\'') + filename += *ptr++; + ++ptr; + + if (std::strcmp(ptr, " file not found") == 0) + return std::move(filename); + else + throw libclang_error("preprocessor: unexpected diagnostic '" + msg + "'"); +} + +//=== external preprocessor invocation ==// +// quote a string +std::string quote(std::string str) +{ + return '"' + std::move(str) + '"'; +} + +std::string diagnostics_flags() +{ + std::string flags; + + // -fno-caret-diagnostics: don't show the source extract in diagnostics + // -fno-show-column: don't show the column number + // -fdiagnostics-format msvc: use easier to parse MSVC format + flags += " -fno-caret-diagnostics -fno-show-column -fdiagnostics-format=msvc"; + // -Wno-*: hide wrong warnings if header file is directly parsed/duplicate macro handling + flags += " -Wno-macro-redefined -Wno-pragma-once-outside-header " + "-Wno-pragma-system-header-outside-header " + "-Wno-include-next-outside-header"; + + return flags; +} + +// get the command that returns all macros defined in the TU +std::string get_macro_command(const libclang_compile_config& c, const char* full_path) +{ + // -x c++: force C++ as input language + // -I.: add current working directory to include search path + // -E: print preprocessor output + // -dM: print macro definitions instead of preprocessed file + auto flags = std::string("-x c++ -I. -E -dM"); + flags += diagnostics_flags(); + + std::string cmd(detail::libclang_compile_config_access::clang_binary(c) + " " + std::move(flags) + + " "); + // other flags + for (auto& flag : detail::libclang_compile_config_access::flags(c)) + { + cmd += flag; + cmd += ' '; } - //=== external preprocessor invocation ==// - // quote a string - std::string quote(std::string str) + return cmd + quote(full_path); +} + +// get the command that preprocess a translation unit given the macros +// macro_file_path == nullptr <=> don't do fast preprocessing +std::string get_preprocess_command(const libclang_compile_config& c, const char* full_path, + const char* macro_file_path) +{ + // -x c++: force C++ as input language + // -E: print preprocessor output + // -dD: keep macros + auto flags = std::string("-x c++ -E -dD"); + + // -CC: keep comments, even in macro + // -C: keep comments, but not in macro + if (!detail::libclang_compile_config_access::remove_comments_in_macro(c)) + flags += " -CC"; + else + flags += " -C"; + + if (macro_file_path) + // -no*: disable default include search paths + flags += " -nostdinc -nostdinc++"; + + if (detail::libclang_compile_config_access::clang_version(c) >= 40000) + // -Xclang -dI: print include directives as well (clang >= 4.0.0) + flags += " -Xclang -dI"; + + flags += diagnostics_flags(); + + if (macro_file_path) { - return '"' + std::move(str) + '"'; + // include file that defines all macros + flags += " -include "; + flags += macro_file_path; } - std::string diagnostics_flags() + std::string cmd(detail::libclang_compile_config_access::clang_binary(c) + " " + std::move(flags) + + " "); + + // other flags + for (const auto& flag : detail::libclang_compile_config_access::flags(c)) { - std::string flags; - - // -fno-caret-diagnostics: don't show the source extract in diagnostics - // -fno-show-column: don't show the column number - // -fdiagnostics-format msvc: use easier to parse MSVC format - flags += " -fno-caret-diagnostics -fno-show-column -fdiagnostics-format=msvc"; - // -Wno-*: hide wrong warnings if header file is directly parsed/duplicate macro handling - flags += " -Wno-macro-redefined -Wno-pragma-once-outside-header " - "-Wno-pragma-system-header-outside-header " - "-Wno-include-next-outside-header"; - - return flags; - } - - // get the command that returns all macros defined in the TU - std::string get_macro_command(const libclang_compile_config& c, const char* full_path) - { - // -x c++: force C++ as input language - // -I.: add current working directory to include search path - // -E: print preprocessor output - // -dM: print macro definitions instead of preprocessed file - auto flags = std::string("-x c++ -I. -E -dM"); - flags += diagnostics_flags(); - - std::string cmd(detail::libclang_compile_config_access::clang_binary(c) + " " - + std::move(flags) + " "); - // other flags - for (auto& flag : detail::libclang_compile_config_access::flags(c)) + DEBUG_ASSERT(flag.size() >= 2u && flag[0] == '-', detail::assert_handler{}, + ("\"" + flag + "\" that's an odd flag").c_str()); + if (!macro_file_path || flag[1] != 'I') { - cmd += flag; + // only add this flag if it is not an include or we're not doing fast preprocessing + cmd += quote(flag); cmd += ' '; } - - return cmd + quote(full_path); } - // get the command that preprocess a translation unit given the macros - // macro_file_path == nullptr <=> don't do fast preprocessing - std::string get_preprocess_command(const libclang_compile_config& c, const char* full_path, - const char* macro_file_path) - { - // -x c++: force C++ as input language - // -E: print preprocessor output - // -dD: keep macros - auto flags = std::string("-x c++ -E -dD"); + return cmd + quote(full_path); +} - // -CC: keep comments, even in macro - // -C: keep comments, but not in macro - if (!detail::libclang_compile_config_access::remove_comments_in_macro(c)) - flags += " -CC"; +std::string get_macro_file_name() +{ + static std::atomic counter(0u); + return "standardese-macro-file-" + std::to_string(++counter) + ".delete-me"; +} + +template +void bump_until(std::istreambuf_iterator& iter, const char (&str)[N]) +{ + auto ptr = &str[0]; + while (ptr != &str[N - 1]) + { + if (iter == std::istreambuf_iterator{}) + // end of file + break; + else if (*iter != *ptr) + { + // try again + ptr = &str[0]; + if (*iter == *ptr) + ++ptr; // it was the first character again + } else - flags += " -C"; + // okay, move forward + ++ptr; - if (macro_file_path) - // -no*: disable default include search paths - flags += " -nostdinc -nostdinc++"; - - if (detail::libclang_compile_config_access::clang_version(c) >= 40000) - // -Xclang -dI: print include directives as well (clang >= 4.0.0) - flags += " -Xclang -dI"; - - flags += diagnostics_flags(); - - if (macro_file_path) - { - // include file that defines all macros - flags += " -include "; - flags += macro_file_path; - } - - std::string cmd(detail::libclang_compile_config_access::clang_binary(c) + " " - + std::move(flags) + " "); - - // other flags - for (const auto& flag : detail::libclang_compile_config_access::flags(c)) - { - DEBUG_ASSERT(flag.size() >= 2u && flag[0] == '-', detail::assert_handler{}, - ("\"" + flag + "\" that's an odd flag").c_str()); - if (!macro_file_path || flag[1] != 'I') - { - // only add this flag if it is not an include or we're not doing fast preprocessing - cmd += quote(flag); - cmd += ' '; - } - } - - return cmd + quote(full_path); + ++iter; } +} - std::string get_macro_file_name() - { - static std::atomic counter(0u); - return "standardese-macro-file-" + std::to_string(++counter) + ".delete-me"; - } +template +void skip_whitespace(Iter& begin, Iter end) +{ + while (begin != end && (*begin == ' ' || *begin == '\t')) + ++begin; +} - template - void bump_until(std::istreambuf_iterator& iter, const char (&str)[N]) +template +std::string get_line(Iter& begin, Iter end) +{ + std::string line; + while (begin != end && *begin != '\n') + line += *begin++; + ++begin; // newline + return line; +} + +type_safe::optional get_include_guard_macro(const std::string& full_path) +{ + std::ifstream file(full_path); + + auto iter = std::istreambuf_iterator(file); + while (iter != std::istreambuf_iterator{}) { - auto ptr = &str[0]; - while (ptr != &str[N - 1]) + if (*iter == '/') { - if (iter == std::istreambuf_iterator{}) - // end of file - break; - else if (*iter != *ptr) - { - // try again - ptr = &str[0]; - if (*iter == *ptr) - ++ptr; // it was the first character again - } - else - // okay, move forward - ++ptr; - ++iter; - } - } - - template - void skip_whitespace(Iter& begin, Iter end) - { - while (begin != end && (*begin == ' ' || *begin == '\t')) - ++begin; - } - - template - std::string get_line(Iter& begin, Iter end) - { - std::string line; - while (begin != end && *begin != '\n') - line += *begin++; - ++begin; // newline - return line; - } - - type_safe::optional get_include_guard_macro(const std::string& full_path) - { - std::ifstream file(full_path); - - auto iter = std::istreambuf_iterator(file); - while (iter != std::istreambuf_iterator{}) - { if (*iter == '/') + // C++ style comment, bump until \n + bump_until(iter, "\n"); + else if (*iter == '*') + // C style comment + bump_until(iter, "*/"); + } + else if (*iter == ' ' || *iter == '\t' || *iter == '\n') + ++iter; // empty + else if (*iter == '#') + { + // preprocessor line + auto if_line = get_line(iter, {}); + if (if_line.compare(0, 3, "#if") != 0) + // not something starting with #if + break; + + skip_whitespace(iter, {}); + + auto macro_line = get_line(iter, {}); + if (macro_line.compare(0, 7, "#define") != 0) + // not a corresponding define + break; + + auto macro_name_begin = std::next(macro_line.begin(), 7); + // skip whitespace after define + skip_whitespace(macro_name_begin, macro_line.end()); + + auto macro_name_end = macro_name_begin; + // skip over identifier + while (macro_name_end != macro_line.end() + && (*macro_name_end == '_' || std::isalnum(*macro_name_end))) + ++macro_name_end; + + auto trailing_ws = macro_line.rbegin(); + skip_whitespace(trailing_ws, macro_line.rend()); + if (macro_name_end != trailing_ws.base()) + // anything else after macro + break; + + std::string macro_name(macro_name_begin, macro_name_end); + if (if_line.find(macro_name) == std::string::npos) + // macro name doesn't occur in if line + break; + else + return macro_name; + } + else + // line is neither empty, comment, nor preprocessor + break; + } + + // assume no include guard followed a bad line + return type_safe::nullopt; +} + +std::string write_macro_file(const libclang_compile_config& c, const std::string& full_path, + const diagnostic_logger& logger) +{ + std::string diagnostic; + auto diagnostic_logger = [&](const char* str, std::size_t n) { + diagnostic.reserve(diagnostic.size() + n); + for (auto end = str + n; str != end; ++str) + if (*str == '\r') + continue; + else if (*str == '\n') { - ++iter; - if (*iter == '/') - // C++ style comment, bump until \n - bump_until(iter, "\n"); - else if (*iter == '*') - // C style comment - bump_until(iter, "*/"); - } - else if (*iter == ' ' || *iter == '\t' || *iter == '\n') - ++iter; // empty - else if (*iter == '#') - { - // preprocessor line - auto if_line = get_line(iter, {}); - if (if_line.compare(0, 3, "#if") != 0) - // not something starting with #if - break; - - skip_whitespace(iter, {}); - - auto macro_line = get_line(iter, {}); - if (macro_line.compare(0, 7, "#define") != 0) - // not a corresponding define - break; - - auto macro_name_begin = std::next(macro_line.begin(), 7); - // skip whitespace after define - skip_whitespace(macro_name_begin, macro_line.end()); - - auto macro_name_end = macro_name_begin; - // skip over identifier - while (macro_name_end != macro_line.end() - && (*macro_name_end == '_' || std::isalnum(*macro_name_end))) - ++macro_name_end; - - auto trailing_ws = macro_line.rbegin(); - skip_whitespace(trailing_ws, macro_line.rend()); - if (macro_name_end != trailing_ws.base()) - // anything else after macro - break; - - std::string macro_name(macro_name_begin, macro_name_end); - if (if_line.find(macro_name) == std::string::npos) - // macro name doesn't occur in if line - break; - else - return macro_name; + // consume current diagnostic + log_diagnostic(logger, diagnostic); + diagnostic.clear(); } else - // line is neither empty, comment, nor preprocessor - break; - } - - // assume no include guard followed a bad line - return type_safe::nullopt; - } - - std::string write_macro_file(const libclang_compile_config& c, const std::string& full_path, - const diagnostic_logger& logger) - { - std::string diagnostic; - auto diagnostic_logger = [&](const char* str, std::size_t n) { - diagnostic.reserve(diagnostic.size() + n); - for (auto end = str + n; str != end; ++str) - if (*str == '\r') - continue; - else if (*str == '\n') - { - // consume current diagnostic - log_diagnostic(logger, diagnostic); - diagnostic.clear(); - } - else - diagnostic.push_back(*str); - }; - - auto file = get_macro_file_name(); - std::ofstream stream(file); - - auto cmd = get_macro_command(c, full_path.c_str()); - tpl::Process process(cmd, "", - [&](const char* str, std::size_t n) { - stream.write(str, std::streamsize(n)); - }, - diagnostic_logger); - - if (auto include_guard = get_include_guard_macro(full_path)) - // undefine include guard - stream << "#undef " << include_guard.value(); - - auto exit_code = process.get_exit_status(); - DEBUG_ASSERT(diagnostic.empty(), detail::assert_handler{}); - if (exit_code != 0) - throw libclang_error("preprocessor (macro): command '" + cmd - + "' exited with non-zero exit code (" + std::to_string(exit_code) - + ")"); - return file; - } - - struct clang_preprocess_result - { - std::string file; - std::vector included_files; // needed for pre-clang 4.0.0 + diagnostic.push_back(*str); }; - clang_preprocess_result clang_preprocess_impl(const libclang_compile_config& c, - const diagnostic_logger& logger, - const std::string& full_path, - const char* macro_path) - { - clang_preprocess_result result; + auto file = get_macro_file_name(); + std::ofstream stream(file); - std::string diagnostic; - auto expect_bad_exit_code = false; - auto diagnostic_handler = [&](const char* str, std::size_t n) { - diagnostic.reserve(diagnostic.size() + n); - for (auto end = str + n; str != end; ++str) - if (*str == '\r') - continue; - else if (*str == '\n') + auto cmd = get_macro_command(c, full_path.c_str()); + tpl::Process process(cmd, "", + [&](const char* str, std::size_t n) { + stream.write(str, std::streamsize(n)); + }, + diagnostic_logger); + + if (auto include_guard = get_include_guard_macro(full_path)) + // undefine include guard + stream << "#undef " << include_guard.value(); + + auto exit_code = process.get_exit_status(); + DEBUG_ASSERT(diagnostic.empty(), detail::assert_handler{}); + if (exit_code != 0) + throw libclang_error("preprocessor (macro): command '" + cmd + + "' exited with non-zero exit code (" + std::to_string(exit_code) + + ")"); + return file; +} + +struct clang_preprocess_result +{ + std::string file; + std::vector included_files; // needed for pre-clang 4.0.0 +}; + +clang_preprocess_result clang_preprocess_impl(const libclang_compile_config& c, + const diagnostic_logger& logger, + const std::string& full_path, const char* macro_path) +{ + clang_preprocess_result result; + + std::string diagnostic; + auto expect_bad_exit_code = false; + auto diagnostic_handler = [&](const char* str, std::size_t n) { + diagnostic.reserve(diagnostic.size() + n); + for (auto end = str + n; str != end; ++str) + if (*str == '\r') + continue; + else if (*str == '\n') + { + // handle current diagnostic + if (macro_path) { - // handle current diagnostic - if (macro_path) - { - // hide diagnostics + // hide diagnostics - auto file = parse_missing_file(full_path, diagnostic); - if (file) - // save for clang without -dI flag - result.included_files.push_back(file.value()); + auto file = parse_missing_file(full_path, diagnostic); + if (file) + // save for clang without -dI flag + result.included_files.push_back(file.value()); - expect_bad_exit_code = true; - } - else - log_diagnostic(logger, diagnostic); - - diagnostic.clear(); + expect_bad_exit_code = true; } else - diagnostic.push_back(*str); - }; + log_diagnostic(logger, diagnostic); - auto cmd = get_preprocess_command(c, full_path.c_str(), macro_path); - tpl::Process process(cmd, "", - [&](const char* str, std::size_t n) { - result.file.reserve(result.file.size() + n); - for (auto ptr = str; ptr != str + n; ++ptr) - if (*ptr == '\t') - result.file += " "; // convert to two spaces - else if (*ptr != '\r') - result.file += *ptr; - }, - diagnostic_handler); - // wait for process end - auto exit_code = process.get_exit_status(); - DEBUG_ASSERT(diagnostic.empty(), detail::assert_handler{}); - if (exit_code != 0 && !expect_bad_exit_code) - throw libclang_error("preprocessor: command '" + cmd - + "' exited with non-zero exit code (" + std::to_string(exit_code) - + ")"); - - return result; - } - - clang_preprocess_result clang_preprocess(const libclang_compile_config& c, - const char* full_path, const diagnostic_logger& logger) - { - if (!std::ifstream(full_path)) - throw libclang_error("preprocessor: file '" + std::string(full_path) - + "' doesn't exist"); - - // if we're fast preprocessing we only preprocess the main file, not includes - // this is done by disabling all include search paths when doing the preprocessing - // to allow macros a separate preprocessing with the -dM flag is done that extracts all macros - // they are then manually defined before - auto fast_preprocessing = detail::libclang_compile_config_access::fast_preprocessing(c); - - auto macro_file = fast_preprocessing ? write_macro_file(c, full_path, logger) : ""; - - clang_preprocess_result result; - try - { - result = clang_preprocess_impl(c, logger, full_path, - fast_preprocessing ? macro_file.c_str() : nullptr); - } - catch (...) - { - if (fast_preprocessing) - { - auto err = std::remove(macro_file.c_str()); - DEBUG_ASSERT(err == 0, detail::assert_handler{}); + diagnostic.clear(); } - throw; - } + else + diagnostic.push_back(*str); + }; + auto cmd = get_preprocess_command(c, full_path.c_str(), macro_path); + tpl::Process process(cmd, "", + [&](const char* str, std::size_t n) { + result.file.reserve(result.file.size() + n); + for (auto ptr = str; ptr != str + n; ++ptr) + if (*ptr == '\t') + result.file += " "; // convert to two spaces + else if (*ptr != '\r') + result.file += *ptr; + }, + diagnostic_handler); + // wait for process end + auto exit_code = process.get_exit_status(); + DEBUG_ASSERT(diagnostic.empty(), detail::assert_handler{}); + if (exit_code != 0 && !expect_bad_exit_code) + throw libclang_error("preprocessor: command '" + cmd + "' exited with non-zero exit code (" + + std::to_string(exit_code) + ")"); + + return result; +} + +clang_preprocess_result clang_preprocess(const libclang_compile_config& c, const char* full_path, + const diagnostic_logger& logger) +{ + if (!std::ifstream(full_path)) + throw libclang_error("preprocessor: file '" + std::string(full_path) + "' doesn't exist"); + + // if we're fast preprocessing we only preprocess the main file, not includes + // this is done by disabling all include search paths when doing the preprocessing + // to allow macros a separate preprocessing with the -dM flag is done that extracts all macros + // they are then manually defined before + auto fast_preprocessing = detail::libclang_compile_config_access::fast_preprocessing(c); + + auto macro_file = fast_preprocessing ? write_macro_file(c, full_path, logger) : ""; + + clang_preprocess_result result; + try + { + result = clang_preprocess_impl(c, logger, full_path, + fast_preprocessing ? macro_file.c_str() : nullptr); + } + catch (...) + { if (fast_preprocessing) { auto err = std::remove(macro_file.c_str()); DEBUG_ASSERT(err == 0, detail::assert_handler{}); } - - return result; + throw; } - //==== parsing ===// - class position + if (fast_preprocessing) { - public: - position(ts::object_ref result, const char* ptr) noexcept - : result_(result), cur_line_(1u), cur_column_(0u), ptr_(ptr), write_(true) - { - } + auto err = std::remove(macro_file.c_str()); + DEBUG_ASSERT(err == 0, detail::assert_handler{}); + } - void set_line(unsigned line) + return result; +} + +//==== parsing ===// +class position +{ +public: + position(ts::object_ref result, const char* ptr) noexcept + : result_(result), cur_line_(1u), cur_column_(0u), ptr_(ptr), write_(true) + {} + + void set_line(unsigned line) + { + if (cur_line_ != line) { - if (cur_line_ != line) + *result_ += "#line " + std::to_string(line) + "\n"; + cur_line_ = line; + cur_column_ = 0; + } + } + + void write_str(std::string str) + { + if (write_ == false) + return; + for (auto c : str) + { + *result_ += c; + ++cur_column_; + + if (c == '\n') { - *result_ += "#line " + std::to_string(line) + "\n"; - cur_line_ = line; + ++cur_line_; + cur_column_ = 0; + } + } + } + + void bump() noexcept + { + if (write_ == true) + { + result_->push_back(*ptr_); + ++cur_column_; + + if (*ptr_ == '\n') + { + ++cur_line_; + cur_column_ = 0; + } + } + ++ptr_; + } + + void bump(std::size_t offset) noexcept + { + if (write_ == true) + { + for (std::size_t i = 0u; i != offset; ++i) + bump(); + } + else + skip(offset); + } + + // no write, no newline detection + void skip(std::size_t offset = 1u) noexcept + { + ptr_ += offset; + } + + void skip_with_linecount() noexcept + { + if (write_ == true) + { + ++cur_column_; + if (*ptr_ == '\n') + { + result_->push_back('\n'); + ++cur_line_; cur_column_ = 0; } } - void write_str(std::string str) - { - if (write_ == false) - return; - for (auto c : str) - { - *result_ += c; - ++cur_column_; - - if (c == '\n') - { - ++cur_line_; - cur_column_ = 0; - } - } - } - - void bump() noexcept - { - if (write_ == true) - { - result_->push_back(*ptr_); - ++cur_column_; - - if (*ptr_ == '\n') - { - ++cur_line_; - cur_column_ = 0; - } - } - ++ptr_; - } - - void bump(std::size_t offset) noexcept - { - if (write_ == true) - { - for (std::size_t i = 0u; i != offset; ++i) - bump(); - } - else - skip(offset); - } - - // no write, no newline detection - void skip(std::size_t offset = 1u) noexcept - { - ptr_ += offset; - } - - void skip_with_linecount() noexcept - { - if (write_ == true) - { - ++cur_column_; - if (*ptr_ == '\n') - { - result_->push_back('\n'); - ++cur_line_; - cur_column_ = 0; - } - } - - ++ptr_; - } - - void enable_write() noexcept - { - write_.set(); - } - - void disable_write() noexcept - { - write_.try_reset(); - } - - bool write_enabled() const noexcept - { - return write_ == true; - } - - explicit operator bool() const noexcept - { - return *ptr_ != '\0'; - } - - const char* ptr() const noexcept - { - return ptr_; - } - - unsigned cur_line() const noexcept - { - return cur_line_; - } - - unsigned cur_column() const noexcept - { - return cur_column_; - } - - bool was_newl() const noexcept - { - return result_->empty() || result_->back() == '\n'; - } - - private: - ts::object_ref result_; - unsigned cur_line_, cur_column_; - const char* ptr_; - ts::flag write_; - }; - - bool starts_with(const position& p, const char* str, std::size_t len) - { - return std::strncmp(p.ptr(), str, len) == 0; + ++ptr_; } - template - bool starts_with(const position& p, const char (&str)[N]) + void enable_write() noexcept { - return std::strncmp(p.ptr(), str, N - 1) == 0; + write_.set(); } - void skip(position& p, const char* str) + void disable_write() noexcept { - DEBUG_ASSERT(starts_with(p, str, std::strlen(str)), detail::assert_handler{}); - p.skip(std::strlen(str)); + write_.try_reset(); } - void bump_spaces(position& p, bool bump = false) + bool write_enabled() const noexcept { - while (starts_with(p, " ")) - if (bump) - p.bump(); - else - p.skip(); + return write_ == true; } - detail::pp_doc_comment parse_c_doc_comment(position& p) + explicit operator bool() const noexcept { - detail::pp_doc_comment result; - result.kind = detail::pp_doc_comment::c; + return *ptr_ != '\0'; + } - auto indent = p.cur_column() + 3; + const char* ptr() const noexcept + { + return ptr_; + } - if (starts_with(p, " ")) - { - // skip one whitespace at most + unsigned cur_line() const noexcept + { + return cur_line_; + } + + unsigned cur_column() const noexcept + { + return cur_column_; + } + + bool was_newl() const noexcept + { + return result_->empty() || result_->back() == '\n'; + } + +private: + ts::object_ref result_; + unsigned cur_line_, cur_column_; + const char* ptr_; + ts::flag write_; +}; + +bool starts_with(const position& p, const char* str, std::size_t len) +{ + return std::strncmp(p.ptr(), str, len) == 0; +} + +template +bool starts_with(const position& p, const char (&str)[N]) +{ + return std::strncmp(p.ptr(), str, N - 1) == 0; +} + +void skip(position& p, const char* str) +{ + DEBUG_ASSERT(starts_with(p, str, std::strlen(str)), detail::assert_handler{}); + p.skip(std::strlen(str)); +} + +void bump_spaces(position& p, bool bump = false) +{ + while (starts_with(p, " ")) + if (bump) + p.bump(); + else p.skip(); - ++indent; - } +} - while (!starts_with(p, "*/")) +detail::pp_doc_comment parse_c_doc_comment(position& p) +{ + detail::pp_doc_comment result; + result.kind = detail::pp_doc_comment::c; + + auto indent = p.cur_column() + 3; + + if (starts_with(p, " ")) + { + // skip one whitespace at most + p.skip(); + ++indent; + } + + while (!starts_with(p, "*/")) + { + if (starts_with(p, "\n")) { - if (starts_with(p, "\n")) + // remove trailing spaces + while (!result.comment.empty() && result.comment.back() == ' ') + result.comment.pop_back(); + + // skip newline(s) + while (starts_with(p, "\n")) { - // remove trailing spaces - while (!result.comment.empty() && result.comment.back() == ' ') - result.comment.pop_back(); - - // skip newline(s) - while (starts_with(p, "\n")) - { - p.skip_with_linecount(); - result.comment += '\n'; - } - - // skip indentation - auto actual_indent = 0u; - for (auto i = 0u; i < indent && starts_with(p, " "); ++i) - { - ++actual_indent; - p.skip(); - } - - auto extra_indent = 0u; - while (starts_with(p, " ")) - { - ++extra_indent; - p.skip(); - } - - // skip continuation star, if any - if (starts_with(p, "*") && !starts_with(p, "*/")) - { - p.skip(); - if (starts_with(p, " ")) - // skip one whitespace at most - p.skip(); - } - else - { - // insert extra indent again - result.comment += std::string(extra_indent, ' '); - // use minimum indent in the future - indent = std::min(actual_indent, indent); - } + p.skip_with_linecount(); + result.comment += '\n'; } - else + + // skip indentation + auto actual_indent = 0u; + for (auto i = 0u; i < indent && starts_with(p, " "); ++i) { - result.comment += *p.ptr(); + ++actual_indent; p.skip(); } - } - p.skip(2u); - // remove trailing star - if (!result.comment.empty() && result.comment.back() == '*') - result.comment.pop_back(); - // remove trailing spaces - while (!result.comment.empty() && result.comment.back() == ' ') - result.comment.pop_back(); + auto extra_indent = 0u; + while (starts_with(p, " ")) + { + ++extra_indent; + p.skip(); + } - result.line = p.cur_line(); - return result; - } - - bool skip_c_comment(position& p, detail::preprocessor_output& output) - { - if (!starts_with(p, "/*")) - return false; - p.skip(2u); - - if (starts_with(p, "*/")) - // empty comment - p.skip(2u); - else if (p.write_enabled() && (starts_with(p, "*") || starts_with(p, "!"))) - { - // doc comment - p.skip(); - output.comments.push_back(parse_c_doc_comment(p)); + // skip continuation star, if any + if (starts_with(p, "*") && !starts_with(p, "*/")) + { + p.skip(); + if (starts_with(p, " ")) + // skip one whitespace at most + p.skip(); + } + else + { + // insert extra indent again + result.comment += std::string(extra_indent, ' '); + // use minimum indent in the future + indent = std::min(actual_indent, indent); + } } else - { - while (!starts_with(p, "*/")) - p.skip_with_linecount(); - p.skip(2u); - } - - return true; - } - - detail::pp_doc_comment parse_cpp_doc_comment(position& p, bool end_of_line) - { - detail::pp_doc_comment result; - result.kind = - end_of_line ? detail::pp_doc_comment::end_of_line : detail::pp_doc_comment::cpp; - if (starts_with(p, " ")) - // skip one whitespace at most - p.skip(); - - while (!starts_with(p, "\n")) { result.comment += *p.ptr(); p.skip(); } - // don't skip newline - - // remove trailing spaces - while (!result.comment.empty() && result.comment.back() == ' ') - result.comment.pop_back(); - result.line = p.cur_line(); - return result; } + p.skip(2u); - bool can_merge_comment(const detail::pp_doc_comment& comment, unsigned cur_line) - { - return comment.line + 1 == cur_line - && (comment.kind == detail::pp_doc_comment::cpp - || comment.kind == detail::pp_doc_comment::end_of_line); - } + // remove trailing star + if (!result.comment.empty() && result.comment.back() == '*') + result.comment.pop_back(); + // remove trailing spaces + while (!result.comment.empty() && result.comment.back() == ' ') + result.comment.pop_back(); - void merge_or_add(detail::preprocessor_output& output, detail::pp_doc_comment comment) - { - if (output.comments.empty() || !can_merge_comment(output.comments.back(), comment.line)) - output.comments.push_back(std::move(comment)); - else - { - auto& result = output.comments.back(); - result.comment += "\n" + std::move(comment.comment); - if (result.kind != detail::pp_doc_comment::end_of_line) - result.line = comment.line; - } - } + result.line = p.cur_line(); + return result; +} - bool skip_cpp_comment(position& p, detail::preprocessor_output& output) - { - if (!starts_with(p, "//")) - return false; +bool skip_c_comment(position& p, detail::preprocessor_output& output) +{ + if (!starts_with(p, "/*")) + return false; + p.skip(2u); + + if (starts_with(p, "*/")) + // empty comment p.skip(2u); + else if (p.write_enabled() && (starts_with(p, "*") || starts_with(p, "!"))) + { + // doc comment + p.skip(); + output.comments.push_back(parse_c_doc_comment(p)); + } + else + { + while (!starts_with(p, "*/")) + p.skip_with_linecount(); + p.skip(2u); + } - if (p.write_enabled() && (starts_with(p, "/") || starts_with(p, "!"))) - { - // C++ style doc comment - p.skip(); - auto comment = parse_cpp_doc_comment(p, false); - merge_or_add(output, std::move(comment)); - } - else if (p.write_enabled() && starts_with(p, "<")) - { - // end of line doc comment - p.skip(); - auto comment = parse_cpp_doc_comment(p, true); - output.comments.push_back(std::move(comment)); - } + return true; +} + +detail::pp_doc_comment parse_cpp_doc_comment(position& p, bool end_of_line) +{ + detail::pp_doc_comment result; + result.kind = end_of_line ? detail::pp_doc_comment::end_of_line : detail::pp_doc_comment::cpp; + if (starts_with(p, " ")) + // skip one whitespace at most + p.skip(); + + while (!starts_with(p, "\n")) + { + result.comment += *p.ptr(); + p.skip(); + } + // don't skip newline + + // remove trailing spaces + while (!result.comment.empty() && result.comment.back() == ' ') + result.comment.pop_back(); + result.line = p.cur_line(); + return result; +} + +bool can_merge_comment(const detail::pp_doc_comment& comment, unsigned cur_line) +{ + return comment.line + 1 == cur_line + && (comment.kind == detail::pp_doc_comment::cpp + || comment.kind == detail::pp_doc_comment::end_of_line); +} + +void merge_or_add(detail::preprocessor_output& output, detail::pp_doc_comment comment) +{ + if (output.comments.empty() || !can_merge_comment(output.comments.back(), comment.line)) + output.comments.push_back(std::move(comment)); + else + { + auto& result = output.comments.back(); + result.comment += "\n" + std::move(comment.comment); + if (result.kind != detail::pp_doc_comment::end_of_line) + result.line = comment.line; + } +} + +bool skip_cpp_comment(position& p, detail::preprocessor_output& output) +{ + if (!starts_with(p, "//")) + return false; + p.skip(2u); + + if (p.write_enabled() && (starts_with(p, "/") || starts_with(p, "!"))) + { + // C++ style doc comment + p.skip(); + auto comment = parse_cpp_doc_comment(p, false); + merge_or_add(output, std::move(comment)); + } + else if (p.write_enabled() && starts_with(p, "<")) + { + // end of line doc comment + p.skip(); + auto comment = parse_cpp_doc_comment(p, true); + output.comments.push_back(std::move(comment)); + } + else + { + auto newline = std::strchr(p.ptr(), '\n'); + p.skip(std::size_t(newline - p.ptr())); // don't skip newline + } + + return true; +} + +std::unique_ptr build(std::string name, ts::optional args, + std::string rep) +{ + if (!args) + return cpp_macro_definition::build_object_like(std::move(name), std::move(rep)); + + cpp_macro_definition::function_like_builder builder{std::move(name)}; + builder.replacement(std::move(rep)); + + auto cur_ptr = args.value().c_str(); + auto cur_param = cur_ptr; + while (*cur_ptr) + { + while (*cur_ptr && *cur_ptr != ',') + ++cur_ptr; + + if (*cur_param == '.') + builder.is_variadic(); else - { - auto newline = std::strchr(p.ptr(), '\n'); - p.skip(std::size_t(newline - p.ptr())); // don't skip newline - } + builder.parameter(std::string(cur_param, cur_ptr)); - return true; + if (*cur_ptr) + cur_param = ++cur_ptr; } - std::unique_ptr build(std::string name, ts::optional args, - std::string rep) + return builder.finish(); +} + +std::unique_ptr parse_macro(position& p, detail::preprocessor_output& output) +{ + // format (at new line): #define [replacement] + // or: #define () [replacement] + // note: keep macro definition in file + if (!p.was_newl() || !starts_with(p, "#define")) + return nullptr; + // read line here for comment matching + auto cur_line = p.cur_line(); + p.bump(std::strlen("#define")); + bump_spaces(p, true); + + std::string name; + while (!starts_with(p, "(") && !starts_with(p, " ") && !starts_with(p, "\n")) { - if (!args) - return cpp_macro_definition::build_object_like(std::move(name), std::move(rep)); - - cpp_macro_definition::function_like_builder builder{std::move(name)}; - builder.replacement(std::move(rep)); - - auto cur_ptr = args.value().c_str(); - auto cur_param = cur_ptr; - while (*cur_ptr) - { - while (*cur_ptr && *cur_ptr != ',') - ++cur_ptr; - - if (*cur_param == '.') - builder.is_variadic(); - else - builder.parameter(std::string(cur_param, cur_ptr)); - - if (*cur_ptr) - cur_param = ++cur_ptr; - } - - return builder.finish(); + name += *p.ptr(); + p.bump(); } - std::unique_ptr parse_macro(position& p, - detail::preprocessor_output& output) + ts::optional args; + if (starts_with(p, "(")) { - // format (at new line): #define [replacement] - // or: #define () [replacement] - // note: keep macro definition in file - if (!p.was_newl() || !starts_with(p, "#define")) - return nullptr; - // read line here for comment matching - auto cur_line = p.cur_line(); - p.bump(std::strlen("#define")); - bump_spaces(p, true); - - std::string name; - while (!starts_with(p, "(") && !starts_with(p, " ") && !starts_with(p, "\n")) - { - name += *p.ptr(); - p.bump(); - } - - ts::optional args; - if (starts_with(p, "(")) - { - std::string str; - for (p.bump(); !starts_with(p, ")"); p.bump()) - str += *p.ptr(); - p.bump(); - args = std::move(str); - } - - std::string rep; - auto in_c_comment = false; - for (bump_spaces(p, true); in_c_comment || !starts_with(p, "\n"); p.bump()) - { - if (starts_with(p, "/*")) - in_c_comment = true; - else if (in_c_comment && starts_with(p, "*/")) - in_c_comment = false; - rep += *p.ptr(); - } - // don't skip newline - - if (!p.write_enabled()) - return nullptr; - - auto result = build(std::move(name), std::move(args), std::move(rep)); - // match comment directly - if (!output.comments.empty() && output.comments.back().matches(*result, cur_line)) - { - result->set_comment(std::move(output.comments.back().comment)); - output.comments.pop_back(); - } - return result; + std::string str; + for (p.bump(); !starts_with(p, ")"); p.bump()) + str += *p.ptr(); + p.bump(); + args = std::move(str); } - ts::optional parse_undef(position& p) + std::string rep; + auto in_c_comment = false; + for (bump_spaces(p, true); in_c_comment || !starts_with(p, "\n"); p.bump()) { - // format (at new line): #undef - // due to a clang bug (http://bugs.llvm.org/show_bug.cgi?id=32631) I'll also an undef in the middle of the line - if (/*!p.was_newl() ||*/ !starts_with(p, "#undef")) - return ts::nullopt; - p.bump(std::strlen("#undef")); - - std::string result; - for (bump_spaces(p, true); !starts_with(p, "\n"); p.bump()) - result += *p.ptr(); - // don't skip newline - - return result; + if (starts_with(p, "/*")) + in_c_comment = true; + else if (in_c_comment && starts_with(p, "*/")) + in_c_comment = false; + rep += *p.ptr(); } + // don't skip newline - type_safe::optional parse_include(position& p) + if (!p.write_enabled()) + return nullptr; + + auto result = build(std::move(name), std::move(args), std::move(rep)); + // match comment directly + if (!output.comments.empty() && output.comments.back().matches(*result, cur_line)) + { + result->set_comment(std::move(output.comments.back().comment)); + output.comments.pop_back(); + } + return result; +} + +ts::optional parse_undef(position& p) +{ + // format (at new line): #undef + // due to a clang bug (http://bugs.llvm.org/show_bug.cgi?id=32631) I'll also an undef in the + // middle of the line + if (/*!p.was_newl() ||*/ !starts_with(p, "#undef")) + return ts::nullopt; + p.bump(std::strlen("#undef")); + + std::string result; + for (bump_spaces(p, true); !starts_with(p, "\n"); p.bump()) + result += *p.ptr(); + // don't skip newline + + return result; +} + +type_safe::optional parse_include(position& p) +{ + // format (at new line, literal <>): #include + // or: #include "filename" + // note: write include back + if (!p.was_newl() || !starts_with(p, "#include")) + return type_safe::nullopt; + p.bump(std::strlen("#include")); + if (starts_with(p, "_next")) + p.bump(std::strlen("_next")); + bump_spaces(p); + + auto include_kind = cpp_include_kind::system; + auto end_str = ""; + if (starts_with(p, "\"")) + { + include_kind = cpp_include_kind::local; + end_str = "\""; + } + else if (starts_with(p, "<")) + { + include_kind = cpp_include_kind::system; + end_str = ">"; + } + else + DEBUG_UNREACHABLE(detail::assert_handler{}); + p.bump(); + + std::string filename; + for (; !starts_with(p, "\"") && !starts_with(p, ">"); p.bump()) + filename += *p.ptr(); + DEBUG_ASSERT(starts_with(p, end_str, std::strlen(end_str)), detail::assert_handler{}, + "bad termination"); + p.bump(); + skip(p, " /* clang -E -dI */"); + DEBUG_ASSERT(starts_with(p, "\n"), detail::assert_handler{}); + // don't skip newline + + if (!p.write_enabled()) + return type_safe::nullopt; + + if (filename.size() > 2u && filename[0] == '.' && (filename[1] == '/' || filename[1] == '\\')) + filename = filename.substr(2); + + return detail::pp_include{std::move(filename), "", include_kind, p.cur_line()}; +} + +bool bump_pragma(position& p) +{ + // format (at new line): #pragma \n + if (!p.was_newl() || !starts_with(p, "#pragma")) + return false; + + while (!starts_with(p, "\n")) + p.bump(); + // don't skip newline + + return true; +} + +struct linemarker +{ + std::string file; + unsigned line; + enum + { + line_directive, // no change in file + enter_new, // open a new file + enter_old, // return to an old file + } flag + = line_directive; + bool is_system = false; +}; + +ts::optional parse_linemarker(position& p) +{ + // format (at new line): # "" + // flag 1: enter_new + // flag 2: enter_old + // flag 3: system file + // flag 4: ignored + if (!p.was_newl() || !starts_with(p, "#")) + return ts::nullopt; + p.skip(); + DEBUG_ASSERT(!starts_with(p, "define") && !starts_with(p, "undef") && !starts_with(p, "pragma"), + detail::assert_handler{}, "handle macros first"); + + linemarker result; + + std::string line; + for (bump_spaces(p); std::isdigit(*p.ptr()); p.skip()) + line += *p.ptr(); + result.line = unsigned(std::stoi(line)); + + bump_spaces(p); + DEBUG_ASSERT(*p.ptr() == '"', detail::assert_handler{}); + p.skip(); + + std::string file_name; + for (; !starts_with(p, "\""); p.skip()) + file_name += *p.ptr(); + p.skip(); + result.file = std::move(file_name); + + for (; !starts_with(p, "\n"); p.skip()) { - // format (at new line, literal <>): #include - // or: #include "filename" - // note: write include back - if (!p.was_newl() || !starts_with(p, "#include")) - return type_safe::nullopt; - p.bump(std::strlen("#include")); - if (starts_with(p, "_next")) - p.bump(std::strlen("_next")); bump_spaces(p); - auto include_kind = cpp_include_kind::system; - auto end_str = ""; - if (starts_with(p, "\"")) + switch (*p.ptr()) { - include_kind = cpp_include_kind::local; - end_str = "\""; + case '1': + DEBUG_ASSERT(result.flag == linemarker::line_directive, detail::assert_handler{}); + result.flag = linemarker::enter_new; + break; + case '2': + DEBUG_ASSERT(result.flag == linemarker::line_directive, detail::assert_handler{}); + result.flag = linemarker::enter_old; + break; + case '3': + result.is_system = true; + break; + case '4': + break; // ignored + + default: + DEBUG_UNREACHABLE(detail::assert_handler{}, "invalid line marker"); + break; } - else if (starts_with(p, "<")) - { - include_kind = cpp_include_kind::system; - end_str = ">"; - } - else - DEBUG_UNREACHABLE(detail::assert_handler{}); - p.bump(); - - std::string filename; - for (; !starts_with(p, "\"") && !starts_with(p, ">"); p.bump()) - filename += *p.ptr(); - DEBUG_ASSERT(starts_with(p, end_str, std::strlen(end_str)), detail::assert_handler{}, - "bad termination"); - p.bump(); - skip(p, " /* clang -E -dI */"); - DEBUG_ASSERT(starts_with(p, "\n"), detail::assert_handler{}); - // don't skip newline - - if (!p.write_enabled()) - return type_safe::nullopt; - - if (filename.size() > 2u && filename[0] == '.' - && (filename[1] == '/' || filename[1] == '\\')) - filename = filename.substr(2); - - return detail::pp_include{std::move(filename), "", include_kind, p.cur_line()}; } + p.skip(); - bool bump_pragma(position& p) - { - // format (at new line): #pragma \n - if (!p.was_newl() || !starts_with(p, "#pragma")) - return false; - - while (!starts_with(p, "\n")) - p.bump(); - // don't skip newline - - return true; - } - - struct linemarker - { - std::string file; - unsigned line; - enum - { - line_directive, // no change in file - enter_new, // open a new file - enter_old, // return to an old file - } flag = line_directive; - bool is_system = false; - }; - - ts::optional parse_linemarker(position& p) - { - // format (at new line): # "" - // flag 1: enter_new - // flag 2: enter_old - // flag 3: system file - // flag 4: ignored - if (!p.was_newl() || !starts_with(p, "#")) - return ts::nullopt; - p.skip(); - DEBUG_ASSERT(!starts_with(p, "define") && !starts_with(p, "undef") - && !starts_with(p, "pragma"), - detail::assert_handler{}, "handle macros first"); - - linemarker result; - - std::string line; - for (bump_spaces(p); std::isdigit(*p.ptr()); p.skip()) - line += *p.ptr(); - result.line = unsigned(std::stoi(line)); - - bump_spaces(p); - DEBUG_ASSERT(*p.ptr() == '"', detail::assert_handler{}); - p.skip(); - - std::string file_name; - for (; !starts_with(p, "\""); p.skip()) - file_name += *p.ptr(); - p.skip(); - result.file = std::move(file_name); - - for (; !starts_with(p, "\n"); p.skip()) - { - bump_spaces(p); - - switch (*p.ptr()) - { - case '1': - DEBUG_ASSERT(result.flag == linemarker::line_directive, detail::assert_handler{}); - result.flag = linemarker::enter_new; - break; - case '2': - DEBUG_ASSERT(result.flag == linemarker::line_directive, detail::assert_handler{}); - result.flag = linemarker::enter_old; - break; - case '3': - result.is_system = true; - break; - case '4': - break; // ignored - - default: - DEBUG_UNREACHABLE(detail::assert_handler{}, "invalid line marker"); - break; - } - } - p.skip(); - - return result; - } + return result; +} } // namespace detail::preprocessor_output detail::preprocess(const libclang_compile_config& config, @@ -1174,12 +1167,14 @@ detail::preprocessor_output detail::preprocess(const libclang_compile_config& co } else { - // this is an indirect include, remember it to get full path for indirect includes + // this is an indirect include, remember it to get full path for indirect + // includes auto& full_path = lm.value().file; - auto last_dir = full_path.find_last_of("/\\"); - auto file_name = - last_dir == std::string::npos ? full_path : full_path.substr(last_dir + 1u); + auto last_dir = full_path.find_last_of("/\\"); + auto file_name = last_dir == std::string::npos + ? full_path + : full_path.substr(last_dir + 1u); indirect_includes.emplace(std::move(file_name), full_path); } @@ -1237,9 +1232,9 @@ detail::preprocessor_output detail::preprocess(const libclang_compile_config& co { auto last_sep = include.file_name.find_last_of("/\\"); - auto iter = indirect_includes.find(last_sep == std::string::npos ? - include.file_name : - include.file_name.substr(last_sep + 1u)); + auto iter = indirect_includes.find(last_sep == std::string::npos + ? include.file_name + : include.file_name.substr(last_sep + 1u)); if (iter != indirect_includes.end()) include.full_path = iter->second; else diff --git a/src/libclang/preprocessor.hpp b/src/libclang/preprocessor.hpp index 5b7969f..b1a4a78 100644 --- a/src/libclang/preprocessor.hpp +++ b/src/libclang/preprocessor.hpp @@ -10,46 +10,46 @@ namespace cppast { - namespace detail +namespace detail +{ + struct pp_macro { - struct pp_macro + std::unique_ptr 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 macro; - unsigned line; - }; + c, + cpp, + end_of_line, + } kind; - struct pp_include - { - std::string file_name, full_path; - cpp_include_kind kind; - unsigned line; - }; + bool matches(const cpp_entity& e, unsigned line); + }; - struct pp_doc_comment - { - std::string comment; - unsigned line; - enum - { - c, - cpp, - end_of_line, - } kind; + struct preprocessor_output + { + std::string source; + std::vector includes; + std::vector macros; + std::vector comments; + }; - bool matches(const cpp_entity& e, unsigned line); - }; - - struct preprocessor_output - { - std::string source; - std::vector includes; - std::vector macros; - std::vector comments; - }; - - preprocessor_output preprocess(const libclang_compile_config& config, const char* path, - const diagnostic_logger& logger); - } -} // namespace cppast::detail + preprocessor_output preprocess(const libclang_compile_config& config, const char* path, + const diagnostic_logger& logger); +} // namespace detail +} // namespace cppast #endif // CPPAST_PREPROCESSOR_HPP_INCLUDED diff --git a/src/libclang/raii_wrapper.hpp b/src/libclang/raii_wrapper.hpp index f93586f..3b1d12f 100644 --- a/src/libclang/raii_wrapper.hpp +++ b/src/libclang/raii_wrapper.hpp @@ -16,170 +16,169 @@ namespace cppast { - namespace detail +namespace detail +{ + template + class raii_wrapper : Deleter { - template - class raii_wrapper : Deleter + static_assert(std::is_pointer::value, ""); + + public: + raii_wrapper() noexcept : obj_(nullptr) {} + + explicit raii_wrapper(T obj) noexcept : obj_(obj) { - static_assert(std::is_pointer::value, ""); + DEBUG_ASSERT(obj_, detail::assert_handler{}); + } - public: - raii_wrapper() noexcept : obj_(nullptr) {} + raii_wrapper(raii_wrapper&& other) noexcept : obj_(other.obj_) + { + DEBUG_ASSERT(obj_, detail::assert_handler{}); + other.obj_ = nullptr; + } - explicit raii_wrapper(T obj) noexcept : obj_(obj) - { - DEBUG_ASSERT(obj_, detail::assert_handler{}); - } + ~raii_wrapper() noexcept + { + if (obj_) + static_cast (*this)(obj_); + } - raii_wrapper(raii_wrapper&& other) noexcept : obj_(other.obj_) - { - DEBUG_ASSERT(obj_, detail::assert_handler{}); - other.obj_ = nullptr; - } + raii_wrapper& operator=(raii_wrapper&& other) noexcept + { + raii_wrapper tmp(std::move(other)); + swap(*this, tmp); + return *this; + } - ~raii_wrapper() noexcept - { - if (obj_) - static_cast (*this)(obj_); - } + friend void swap(raii_wrapper& a, raii_wrapper& b) noexcept + { + std::swap(a.obj_, b.obj_); + } - raii_wrapper& operator=(raii_wrapper&& other) noexcept - { - raii_wrapper tmp(std::move(other)); - swap(*this, tmp); - return *this; - } + T get() const noexcept + { + DEBUG_ASSERT(obj_, detail::assert_handler{}); + return obj_; + } - friend void swap(raii_wrapper& a, raii_wrapper& b) noexcept - { - std::swap(a.obj_, b.obj_); - } + private: + T obj_; + }; - T get() const noexcept - { - DEBUG_ASSERT(obj_, detail::assert_handler{}); - return obj_; - } + struct cxindex_deleter + { + void operator()(CXIndex idx) noexcept + { + clang_disposeIndex(idx); + } + }; - private: - T obj_; + using cxindex = raii_wrapper; + + struct cxtranslation_unit_deleter + { + void operator()(CXTranslationUnit unit) noexcept + { + clang_disposeTranslationUnit(unit); + } + }; + + using cxtranslation_unit = raii_wrapper; + + 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 str_; + }; - struct cxindex_deleter - { - void operator()(CXIndex idx) noexcept - { - clang_disposeIndex(idx); - } - }; - - using cxindex = raii_wrapper; - - struct cxtranslation_unit_deleter - { - void operator()(CXTranslationUnit unit) noexcept - { - clang_disposeTranslationUnit(unit); - } - }; - - using cxtranslation_unit = raii_wrapper; - - 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 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); - } + inline bool operator==(const cxstring& a, const cxstring& b) noexcept + { + return std::strcmp(a.c_str(), b.c_str()) == 0; } -} // 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 diff --git a/src/libclang/template_parser.cpp b/src/libclang/template_parser.cpp index eed3a07..b5e6c3c 100644 --- a/src/libclang/template_parser.cpp +++ b/src/libclang/template_parser.cpp @@ -15,214 +15,209 @@ using namespace cppast; namespace { - template - type_safe::optional get_builder( - const detail::parse_context& context, const CXCursor& cur, Predicate p) - { - // we need the actual entity first, then the parameters - // so two visit calls are required +template +type_safe::optional get_builder(const detail::parse_context& context, + const CXCursor& cur, Predicate p) +{ + // we need the actual entity first, then the parameters + // so two visit calls are required - auto result = clang_getNullCursor(); - detail::visit_children(cur, [&](const CXCursor& child) { - auto kind = clang_getCursorKind(child); - if (kind == CXCursor_TemplateTypeParameter || kind == CXCursor_NonTypeTemplateParameter - || kind == CXCursor_TemplateTemplateParameter) - return; - DEBUG_ASSERT(clang_Cursor_isNull(result), detail::parse_error_handler{}, cur, - "unexpected child of template"); - result = child; - }); - DEBUG_ASSERT(!clang_Cursor_isNull(result), detail::parse_error_handler{}, cur, - "missing child of template"); + auto result = clang_getNullCursor(); + detail::visit_children(cur, [&](const CXCursor& child) { + auto kind = clang_getCursorKind(child); + if (kind == CXCursor_TemplateTypeParameter || kind == CXCursor_NonTypeTemplateParameter + || kind == CXCursor_TemplateTemplateParameter) + return; + DEBUG_ASSERT(clang_Cursor_isNull(result), detail::parse_error_handler{}, cur, + "unexpected child of template"); + result = child; + }); + DEBUG_ASSERT(!clang_Cursor_isNull(result), detail::parse_error_handler{}, cur, + "missing child of template"); - auto entity = detail::parse_entity(context, nullptr, result, cur); - if (!entity) - return type_safe::nullopt; - DEBUG_ASSERT(p(entity->kind()), detail::parse_error_handler{}, cur, - "wrong child of template"); - return typename TemplateT::builder( - std::unique_ptr(static_cast(entity.release()))); - } + auto entity = detail::parse_entity(context, nullptr, result, cur); + if (!entity) + return type_safe::nullopt; + DEBUG_ASSERT(p(entity->kind()), detail::parse_error_handler{}, cur, "wrong child of template"); + return typename TemplateT::builder( + std::unique_ptr(static_cast(entity.release()))); +} - std::unique_ptr parse_type_parameter( - const detail::parse_context& context, const CXCursor& cur) - { - DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_TemplateTypeParameter, - detail::assert_handler{}); +std::unique_ptr parse_type_parameter(const detail::parse_context& context, + const CXCursor& cur) +{ + DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_TemplateTypeParameter, + detail::assert_handler{}); - detail::cxtokenizer tokenizer(context.tu, context.file, cur); - detail::cxtoken_stream stream(tokenizer, cur); - auto name = detail::get_cursor_name(cur); + detail::cxtokenizer tokenizer(context.tu, context.file, cur); + detail::cxtoken_stream stream(tokenizer, cur); + auto name = detail::get_cursor_name(cur); - // syntax: typename/class [...] name [= ...] - auto keyword = cpp_template_keyword::keyword_class; - if (detail::skip_if(stream, "typename")) - keyword = cpp_template_keyword::keyword_typename; - else - detail::skip(stream, "class"); + // syntax: typename/class [...] name [= ...] + 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 variadic = false; - if (detail::skip_if(stream, "...")) - variadic = true; + auto variadic = false; + if (detail::skip_if(stream, "...")) + variadic = true; - if (stream.peek() != "=") - detail::skip(stream, name.c_str()); - - std::unique_ptr 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 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 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, "..."); + if (stream.peek() != "=") 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); + std::unique_ptr def; + if (detail::skip_if(stream, "=")) + // default type + def = detail::parse_raw_type(context, stream, stream.end()); - // 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 - 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()); - } + return cpp_template_type_parameter::build(*context.idx, detail::get_entity_id(cur), + name.c_str(), keyword, variadic, std::move(def)); } +std::unique_ptr 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 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 +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 detail::parse_cpp_alias_template(const detail::parse_context& context, const CXCursor& cur) { DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_TypeAliasTemplateDecl, detail::assert_handler{}); - auto builder = - get_builder(context, cur, [](cpp_entity_kind k) { - return k == cpp_entity_kind::type_alias_t; - }); + auto builder + = get_builder(context, cur, [](cpp_entity_kind k) { + return k == cpp_entity_kind::type_alias_t; + }); if (!builder) return nullptr; context.comments.match(builder.value().get(), cur); @@ -274,28 +269,27 @@ std::unique_ptr detail::parse_cpp_function_template( namespace { - template - void parse_arguments(Builder& b, const detail::parse_context& context, const CXCursor& cur) +template +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); - detail::cxtoken_stream stream(tokenizer, cur); + auto iter = detail::find_closing_bracket(stream); + stream.bump(); - while (!stream.done() - && !detail::skip_if(stream, detail::get_cursor_name(cur).c_str(), true)) - 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()); + auto args = detail::to_string(stream, iter); + b.add_unexposed_arguments(std::move(args)); } + else + b.add_unexposed_arguments(cpp_token_string::builder().finish()); } +} // namespace std::unique_ptr detail::try_parse_cpp_function_template_specialization( const detail::parse_context& context, const CXCursor& cur, bool is_friend) diff --git a/src/libclang/type_parser.cpp b/src/libclang/type_parser.cpp index 6aa5acb..fdd6916 100644 --- a/src/libclang/type_parser.cpp +++ b/src/libclang/type_parser.cpp @@ -21,742 +21,721 @@ using namespace cppast; namespace { - void remove_trailing_ws(std::string& str) +void remove_trailing_ws(std::string& str) +{ + while (!str.empty() && std::isspace(str.back())) + str.pop_back(); +} + +void remove_leading_ws(std::string& str) +{ + while (!str.empty() && std::isspace(str.front())) + str.erase(0, 1); +} + +bool can_extend_suffix(const std::string& str, std::size_t suffix_length) +{ + if (str.length() <= suffix_length + 1u) { - while (!str.empty() && std::isspace(str.back())) - str.pop_back(); + auto c = str[str.length() - suffix_length - 1u]; + return std::isalnum(c) || c == '_'; + } + else + return false; +} + +// if identifier only removes maximal suffix according to tokenization +bool remove_suffix(std::string& str, const char* suffix, bool is_identifier) +{ + auto length = std::strlen(suffix); + if (str.length() >= length && str.compare(str.length() - length, length, suffix) == 0 + && (!is_identifier || !can_extend_suffix(str, length))) + { + str.erase(str.length() - length); + remove_trailing_ws(str); + return true; + } + else + return false; +} + +bool can_extend_prefix(const std::string& str, std::size_t prefix_length) +{ + if (prefix_length < str.size()) + { + auto c = str[prefix_length]; + return std::isalnum(c) || c == '_'; + } + else + return false; +} + +// if identifier only removes maximal suffix according to tokenization +bool remove_prefix(std::string& str, const char* prefix, bool is_identifier) +{ + auto length = std::strlen(prefix); + if (str.length() >= length && str.compare(0u, length, prefix) == 0 + && (!is_identifier || !can_extend_prefix(str, length))) + { + str.erase(0u, length); + remove_leading_ws(str); + return true; + } + else + return false; +} + +bool need_to_remove_scope(const CXCursor& cur, const CXType& type) +{ + if (type.kind != CXType_Typedef) + return false; + + // typedefs declared in the same class include all scopes in the name) + // (because of course they do) + auto parent = clang_getCursorSemanticParent(cur); + auto decl_parent = clang_getCursorSemanticParent(clang_getTypeDeclaration(type)); + return clang_equalCursors(parent, decl_parent) != 0; +} + +std::string get_type_spelling(const CXCursor& cur, const CXType& type) +{ + auto spelling = detail::cxstring(clang_getTypeSpelling(type)).std_str(); + if (need_to_remove_scope(cur, type)) + { + auto colon = spelling.rfind(':'); + if (colon != std::string::npos) + spelling.erase(0, colon + 1u); } - void remove_leading_ws(std::string& str) - { - while (!str.empty() && std::isspace(str.front())) - str.erase(0, 1); - } + return spelling; +} - bool can_extend_suffix(const std::string& str, std::size_t suffix_length) +// const/volatile at the end (because people do that apparently!) +// also used on member function pointers +cpp_cv suffix_cv(std::string& spelling) +{ + auto cv = cpp_cv_none; + if (remove_suffix(spelling, "const", true)) { - if (str.length() <= suffix_length + 1u) - { - auto c = str[str.length() - suffix_length - 1u]; - return std::isalnum(c) || c == '_'; - } + if (remove_suffix(spelling, "volatile", true)) + cv = cpp_cv_const_volatile; else - return false; + cv = cpp_cv_const; } - - // if identifier only removes maximal suffix according to tokenization - bool remove_suffix(std::string& str, const char* suffix, bool is_identifier) + else if (remove_suffix(spelling, "volatile", true)) { - auto length = std::strlen(suffix); - if (str.length() >= length && str.compare(str.length() - length, length, suffix) == 0 - && (!is_identifier || !can_extend_suffix(str, length))) - { - str.erase(str.length() - length); - remove_trailing_ws(str); - return true; - } - else - return false; - } - - bool can_extend_prefix(const std::string& str, std::size_t prefix_length) - { - if (prefix_length < str.size()) - { - auto c = str[prefix_length]; - return std::isalnum(c) || c == '_'; - } - else - return false; - } - - // if identifier only removes maximal suffix according to tokenization - bool remove_prefix(std::string& str, const char* prefix, bool is_identifier) - { - auto length = std::strlen(prefix); - if (str.length() >= length && str.compare(0u, length, prefix) == 0 - && (!is_identifier || !can_extend_prefix(str, length))) - { - str.erase(0u, length); - remove_leading_ws(str); - return true; - } - else - return false; - } - - bool need_to_remove_scope(const CXCursor& cur, const CXType& type) - { - if (type.kind != CXType_Typedef) - return false; - - // typedefs declared in the same class include all scopes in the name) - // (because of course they do) - auto parent = clang_getCursorSemanticParent(cur); - auto decl_parent = clang_getCursorSemanticParent(clang_getTypeDeclaration(type)); - return clang_equalCursors(parent, decl_parent) != 0; - } - - std::string get_type_spelling(const CXCursor& cur, const CXType& type) - { - auto spelling = detail::cxstring(clang_getTypeSpelling(type)).std_str(); - if (need_to_remove_scope(cur, type)) - { - auto colon = spelling.rfind(':'); - if (colon != std::string::npos) - spelling.erase(0, colon + 1u); - } - - return spelling; - } - - // const/volatile at the end (because people do that apparently!) - // also used on member function pointers - cpp_cv suffix_cv(std::string& spelling) - { - auto cv = cpp_cv_none; if (remove_suffix(spelling, "const", true)) - { - if (remove_suffix(spelling, "volatile", true)) - cv = cpp_cv_const_volatile; - else - cv = cpp_cv_const; - } - else if (remove_suffix(spelling, "volatile", true)) - { - if (remove_suffix(spelling, "const", true)) - cv = cpp_cv_const_volatile; - else - cv = cpp_cv_volatile; - } - - return cv; + cv = cpp_cv_const_volatile; + else + cv = cpp_cv_volatile; } - // const/volatile at the beginning - cpp_cv prefix_cv(std::string& spelling) + return cv; +} + +// const/volatile at the beginning +cpp_cv prefix_cv(std::string& spelling) +{ + auto cv = cpp_cv_none; + if (remove_prefix(spelling, "const", true)) + { + if (remove_prefix(spelling, "volatile", true)) + cv = cpp_cv_const_volatile; + else + cv = cpp_cv_const; + } + else if (remove_prefix(spelling, "volatile", true)) { - auto cv = cpp_cv_none; if (remove_prefix(spelling, "const", true)) - { - if (remove_prefix(spelling, "volatile", true)) - cv = cpp_cv_const_volatile; - else - cv = cpp_cv_const; - } - else if (remove_prefix(spelling, "volatile", true)) - { - if (remove_prefix(spelling, "const", true)) - cv = cpp_cv_const_volatile; - else - cv = cpp_cv_volatile; - } - - return cv; + cv = cpp_cv_const_volatile; + else + cv = cpp_cv_volatile; } - cpp_cv merge_cv(cpp_cv a, cpp_cv b) + return cv; +} + +cpp_cv merge_cv(cpp_cv a, cpp_cv b) +{ + switch (a) { - switch (a) + case cpp_cv_none: + return b; + + case cpp_cv_const: + switch (b) { case cpp_cv_none: - return b; - + return cpp_cv_const; case cpp_cv_const: - switch (b) - { - case cpp_cv_none: - return cpp_cv_const; - case cpp_cv_const: - return cpp_cv_const; - case cpp_cv_volatile: - return cpp_cv_const_volatile; - case cpp_cv_const_volatile: - return cpp_cv_const_volatile; - } - break; - + return cpp_cv_const; case cpp_cv_volatile: - switch (b) - { - case cpp_cv_none: - return cpp_cv_volatile; - case cpp_cv_const: - return cpp_cv_const_volatile; - case cpp_cv_volatile: - return cpp_cv_volatile; - case cpp_cv_const_volatile: - return cpp_cv_const_volatile; - } - break; - + return cpp_cv_const_volatile; case cpp_cv_const_volatile: return cpp_cv_const_volatile; } + break; - DEBUG_UNREACHABLE(detail::assert_handler{}); - return cpp_cv_none; - } - - std::unique_ptr make_cv_qualified(std::unique_ptr entity, cpp_cv cv) - { - if (cv == cpp_cv_none) - return entity; - return cpp_cv_qualified_type::build(std::move(entity), cv); - } - - template - std::unique_ptr make_leave_type(const CXCursor& cur, const CXType& type, Builder b) - { - auto spelling = get_type_spelling(cur, type); - - // check for cv qualifiers on the leave type - auto prefix = prefix_cv(spelling); - auto suffix = suffix_cv(spelling); - auto cv = merge_cv(prefix, suffix); - - // remove struct/class/union prefix on inline type definition - // i.e. C's typedef struct idiom - remove_prefix(spelling, "struct", true); - remove_prefix(spelling, "class", true); - remove_prefix(spelling, "union", true); - - auto entity = b(std::move(spelling)); - if (!entity) - return nullptr; - return make_cv_qualified(std::move(entity), cv); - } - - cpp_reference get_reference_kind(const CXType& type) - { - if (type.kind == CXType_LValueReference) - return cpp_ref_lvalue; - else if (type.kind == CXType_RValueReference) - return cpp_ref_rvalue; - return cpp_ref_none; - } - - std::unique_ptr parse_type_impl(const detail::parse_context& context, - const CXCursor& cur, const CXType& type); - - std::unique_ptr parse_array_size(const CXCursor& cur, const CXType& type) - { - auto size = clang_getArraySize(type); - if (size != -1) - return cpp_literal_expression::build(cpp_builtin_type::build(cpp_ulonglong), - std::to_string(size)); - - auto spelling = get_type_spelling(cur, type); - DEBUG_ASSERT(spelling.size() > 2u && spelling.back() == ']', detail::parse_error_handler{}, - type, "unexpected token"); - - std::string size_expr; - auto bracket_count = 1; - for (auto ptr = spelling.c_str() + spelling.size() - 2u; bracket_count != 0; --ptr) + case cpp_cv_volatile: + switch (b) { - if (*ptr == ']') - ++bracket_count; - else if (*ptr == '[') - --bracket_count; - - if (bracket_count != 0) - size_expr += *ptr; + case cpp_cv_none: + return cpp_cv_volatile; + case cpp_cv_const: + return cpp_cv_const_volatile; + case cpp_cv_volatile: + return cpp_cv_volatile; + case cpp_cv_const_volatile: + return cpp_cv_const_volatile; } + break; - return size_expr.empty() ? - nullptr : - cpp_unexposed_expression::build(cpp_builtin_type::build(cpp_ulonglong), - cpp_token_string::tokenize( - std::string(size_expr.rbegin(), - size_expr.rend()))); + case cpp_cv_const_volatile: + return cpp_cv_const_volatile; } - std::unique_ptr try_parse_array_type(const detail::parse_context& context, - const CXCursor& cur, const CXType& type) - { - auto canonical = clang_getCanonicalType(type); - auto value_type = clang_getArrayElementType(type); - if (value_type.kind == CXType_Invalid) - { - // value_type is invalid, however type can still be an array - // as there seems to be a libclang bug - // only if the canonical type is not an array, - // is it truly not an array - value_type = clang_getArrayElementType(canonical); - if (value_type.kind == CXType_Invalid) - return nullptr; - // we have an array, even though type isn't one directly - // only downside of this workaround: we've stripped away typedefs - } + DEBUG_UNREACHABLE(detail::assert_handler{}); + return cpp_cv_none; +} - auto size = parse_array_size(cur, canonical); // type may not work, see above - return cpp_array_type::build(parse_type_impl(context, cur, value_type), std::move(size)); - } +std::unique_ptr make_cv_qualified(std::unique_ptr entity, cpp_cv cv) +{ + if (cv == cpp_cv_none) + return entity; + return cpp_cv_qualified_type::build(std::move(entity), cv); +} - template - std::unique_ptr add_parameters(Builder& builder, const detail::parse_context& context, - const CXCursor& cur, const CXType& type) - { - auto no_args = clang_getNumArgTypes(type); - DEBUG_ASSERT(no_args >= 0, detail::parse_error_handler{}, type, - "invalid number of arguments"); - for (auto i = 0u; i != unsigned(no_args); ++i) - builder.add_parameter(parse_type_impl(context, cur, clang_getArgType(type, i))); +template +std::unique_ptr make_leave_type(const CXCursor& cur, const CXType& type, Builder b) +{ + auto spelling = get_type_spelling(cur, type); - if (clang_isFunctionTypeVariadic(type)) - builder.is_variadic(); + // check for cv qualifiers on the leave type + auto prefix = prefix_cv(spelling); + auto suffix = suffix_cv(spelling); + auto cv = merge_cv(prefix, suffix); - return builder.finish(); - } + // remove struct/class/union prefix on inline type definition + // i.e. C's typedef struct idiom + remove_prefix(spelling, "struct", true); + remove_prefix(spelling, "class", true); + remove_prefix(spelling, "union", true); - std::unique_ptr try_parse_function_type(const detail::parse_context& context, - const CXCursor& cur, const CXType& type) - { - auto result = clang_getResultType(type); - if (result.kind == CXType_Invalid) - // not a function type - return nullptr; - - cpp_function_type::builder builder(parse_type_impl(context, cur, result)); - return add_parameters(builder, context, cur, type); - } - - cpp_reference member_function_ref_qualifier(std::string& spelling) - { - if (remove_suffix(spelling, "&&", false)) - return cpp_ref_rvalue; - else if (remove_suffix(spelling, "&", false)) - return cpp_ref_lvalue; - return cpp_ref_none; - } - - std::unique_ptr make_ref_qualified(std::unique_ptr type, cpp_reference ref) - { - if (ref == cpp_ref_none) - return type; - return cpp_reference_type::build(std::move(type), ref); - } - - std::unique_ptr parse_member_pointee_type(const detail::parse_context& context, - const CXCursor& cur, const CXType& type) - { - auto spelling = get_type_spelling(cur, type); - auto ref = member_function_ref_qualifier(spelling); - auto cv = suffix_cv(spelling); - - auto class_t = clang_Type_getClassType(type); - auto class_entity = - make_ref_qualified(make_cv_qualified(parse_type_impl(context, cur, class_t), cv), ref); - - auto pointee = clang_getPointeeType(type); // for everything except the class type - auto result = clang_getResultType(pointee); - if (result.kind == CXType_Invalid) - { - // member data pointer - return cpp_member_object_type::build(std::move(class_entity), - parse_type_impl(context, cur, pointee)); - } - else - { - cpp_member_function_type::builder builder(std::move(class_entity), - parse_type_impl(context, cur, result)); - return add_parameters(builder, context, cur, pointee); - } - } - - bool is_direct_templated(const CXCursor& cur) - { - // TODO: variable template - auto kind = clang_getCursorKind(cur); - return kind == CXCursor_TypeAliasTemplateDecl || kind == CXCursor_ClassTemplate - || kind == CXCursor_ClassTemplatePartialSpecialization - || kind == CXCursor_FunctionTemplate; - } - - bool check_parent(const CXCursor& parent) - { - if (clang_Cursor_isNull(parent)) - return false; - auto kind = clang_getCursorKind(parent); - return kind != CXCursor_Namespace && kind != CXCursor_TranslationUnit; - } - - CXCursor get_template(CXCursor cur) - { - do - { - if (is_direct_templated(cur)) - return cur; - cur = clang_getCursorSemanticParent(cur); - } while (check_parent(cur)); - return clang_getNullCursor(); - } - - std::unique_ptr try_parse_template_parameter_type( - const detail::parse_context& context, const CXCursor& cur, const CXType& type) - { - // see if we have a parent template - auto templ = get_template(cur); - if (clang_Cursor_isNull(templ)) - // not a template - return nullptr; - - // doesn't respect cv qualifiers properly - auto result = - make_leave_type(cur, type, - [&](std::string&& type_spelling) -> std::unique_ptr { - // look at the template parameters, - // see if we find a matching one - auto param = clang_getNullCursor(); - detail::visit_children(templ, [&](const CXCursor& child) { - if (clang_getCursorKind(child) == CXCursor_TemplateTypeParameter - && get_type_spelling(child, clang_getCursorType(child)) - == type_spelling) - { - // found one - DEBUG_ASSERT(clang_Cursor_isNull(param), - detail::assert_handler{}); - param = child; - } - }); - - if (clang_Cursor_isNull(param)) - return nullptr; - else - // found matching parameter - return cpp_template_parameter_type::build( - cpp_template_type_parameter_ref(detail::get_entity_id( - param), - std::move(type_spelling))); - }); - - if (result) - return result; - else - // try again in a possible parent template - return try_parse_template_parameter_type(context, clang_getCursorSemanticParent(templ), - type); - } - - CXCursor get_instantiation_template(const CXCursor& cur, const CXType& type, - const std::string& templ_name) - { - // look if the type has a declaration that is a template - auto decl = clang_getTypeDeclaration(type); - auto count = clang_Type_getNumTemplateArguments(clang_getCursorType(decl)); - if (count > 0 || is_direct_templated(decl)) - { - auto specialized = clang_getSpecializedCursorTemplate(decl); - if (clang_Cursor_isNull(specialized)) - // is already a template - return decl; - else - return specialized; - } - else - { - // look if the templ_name matches a template template parameter - auto param = clang_getNullCursor(); - detail::visit_children(cur, [&](const CXCursor& child) { - if (clang_getCursorKind(child) == CXCursor_TemplateTemplateParameter - && detail::get_cursor_name(child) == templ_name.c_str()) - { - DEBUG_ASSERT(clang_Cursor_isNull(param), detail::parse_error_handler{}, cur, - "multiple template template parameters with the same name?!"); - param = child; - } - }); - return param; - } - } - - std::unique_ptr try_parse_instantiation_type(const detail::parse_context&, - const CXCursor& cur, const CXType& type) - { - return make_leave_type(cur, type, [&](std::string&& spelling) -> std::unique_ptr { - auto ptr = spelling.c_str(); - - std::string templ_name; - for (; *ptr && *ptr != '<'; ++ptr) - templ_name += *ptr; - if (*ptr != '<') - return nullptr; - ++ptr; - - auto templ = get_instantiation_template(cur, type, templ_name); - if (clang_Cursor_isNull(templ)) - return nullptr; - - cpp_template_instantiation_type::builder builder( - cpp_template_ref(detail::get_entity_id(templ), std::move(templ_name))); - - // parse arguments - // i.e. not parse really, just add the string - if (spelling.empty() || spelling.back() != '>') - return nullptr; - spelling.pop_back(); - while (!spelling.empty() && spelling.back() == ' ') - spelling.pop_back(); - builder.add_unexposed_arguments(ptr); - - return builder.finish(); - }); - } - - std::unique_ptr try_parse_decltype_type(const detail::parse_context&, - const CXCursor& cur, const CXType& type) - { - if (clang_isExpression(clang_getCursorKind(cur))) - return nullptr; // don't use decltype here - - return make_leave_type(cur, type, [&](std::string&& spelling) -> std::unique_ptr { - if (!remove_prefix(spelling, "decltype(", false)) - return nullptr; - remove_suffix(spelling, "...", false); // variadic decltype. fun - DEBUG_ASSERT(!spelling.empty() && spelling.back() == ')', detail::parse_error_handler{}, - type, "unexpected spelling"); - spelling.pop_back(); - - return cpp_decltype_type::build( - cpp_unexposed_expression::build(cpp_unexposed_type::build(""), - cpp_token_string::tokenize(spelling))); - }); - } - - std::unique_ptr parse_type_impl(const detail::parse_context& context, - const CXCursor& cur, const CXType& type) - { - switch (type.kind) - { - // stuff I can't parse - // or have no idea what it is and wait for bug report - case CXType_Invalid: - case CXType_Overload: - case CXType_ObjCId: - case CXType_ObjCClass: - case CXType_ObjCSel: - case CXType_BlockPointer: - case CXType_Vector: - case CXType_ObjCInterface: - case CXType_ObjCObjectPointer: -#if CINDEX_VERSION_MINOR >= 43 - case CXType_Half: - case CXType_Pipe: - case CXType_OCLImage1dRO: - case CXType_OCLImage1dArrayRO: - case CXType_OCLImage1dBufferRO: - case CXType_OCLImage2dRO: - case CXType_OCLImage2dArrayRO: - case CXType_OCLImage2dDepthRO: - case CXType_OCLImage2dArrayDepthRO: - case CXType_OCLImage2dMSAARO: - case CXType_OCLImage2dArrayMSAARO: - case CXType_OCLImage2dMSAADepthRO: - case CXType_OCLImage2dArrayMSAADepthRO: - case CXType_OCLImage3dRO: - case CXType_OCLImage1dWO: - case CXType_OCLImage1dArrayWO: - case CXType_OCLImage1dBufferWO: - case CXType_OCLImage2dWO: - case CXType_OCLImage2dArrayWO: - case CXType_OCLImage2dDepthWO: - case CXType_OCLImage2dArrayDepthWO: - case CXType_OCLImage2dMSAAWO: - case CXType_OCLImage2dArrayMSAAWO: - case CXType_OCLImage2dMSAADepthWO: - case CXType_OCLImage2dArrayMSAADepthWO: - case CXType_OCLImage3dWO: - case CXType_OCLImage1dRW: - case CXType_OCLImage1dArrayRW: - case CXType_OCLImage1dBufferRW: - case CXType_OCLImage2dRW: - case CXType_OCLImage2dArrayRW: - case CXType_OCLImage2dDepthRW: - case CXType_OCLImage2dArrayDepthRW: - case CXType_OCLImage2dMSAARW: - case CXType_OCLImage2dArrayMSAARW: - case CXType_OCLImage2dMSAADepthRW: - case CXType_OCLImage2dArrayMSAADepthRW: - case CXType_OCLImage3dRW: - case CXType_OCLSampler: - case CXType_OCLEvent: - case CXType_OCLQueue: - case CXType_OCLReserveID: -#endif -#if CINDEX_VERSION_MINOR > 43 - case CXType_Float16: -#endif - context.logger->log("libclang parser", - format_diagnostic(severity::warning, detail::make_location(type), - "unexpected type of kind '", - detail::get_type_kind_spelling(type).c_str(), - "'")); - // fallthrough - case CXType_Dependent: // seems to have something to do with expressions, just ignore that (for now?) - case CXType_Unexposed: - if (auto ftype = try_parse_function_type(context, cur, type)) - // guess what: after you've called clang_getPointeeType() on a function pointer - // you'll get an unexposed type - return ftype; - else if (auto atype = try_parse_array_type(context, cur, type)) - // same deal here - return atype; - else if (auto dtype = try_parse_decltype_type(context, cur, type)) - // decltype unexposed - return dtype; - else if (auto itype = try_parse_instantiation_type(context, cur, type)) - // instantiation unexposed - return itype; - else if (auto ptype = try_parse_template_parameter_type(context, cur, type)) - // template parameter type is unexposed - return ptype; - // fallthrough - case CXType_Complex: - return cpp_unexposed_type::build(get_type_spelling(cur, type).c_str()); - - case CXType_Void: - return make_leave_type(cur, type, - [](std::string&&) { return cpp_builtin_type::build(cpp_void); }); - case CXType_Bool: - return make_leave_type(cur, type, - [](std::string&&) { return cpp_builtin_type::build(cpp_bool); }); - case CXType_UChar: - return make_leave_type(cur, type, [](std::string&&) { - return cpp_builtin_type::build(cpp_uchar); - }); - case CXType_UShort: - return make_leave_type(cur, type, [](std::string&&) { - return cpp_builtin_type::build(cpp_ushort); - }); - case CXType_UInt: - return make_leave_type(cur, type, - [](std::string&&) { return cpp_builtin_type::build(cpp_uint); }); - case CXType_ULong: - return make_leave_type(cur, type, [](std::string&&) { - return cpp_builtin_type::build(cpp_ulong); - }); - case CXType_ULongLong: - return make_leave_type(cur, type, [](std::string&&) { - return cpp_builtin_type::build(cpp_ulonglong); - }); - case CXType_UInt128: - return make_leave_type(cur, type, [](std::string&&) { - return cpp_builtin_type::build(cpp_uint128); - }); - case CXType_SChar: - return make_leave_type(cur, type, [](std::string&&) { - return cpp_builtin_type::build(cpp_schar); - }); - case CXType_Short: - return make_leave_type(cur, type, [](std::string&&) { - return cpp_builtin_type::build(cpp_short); - }); - case CXType_Int: - return make_leave_type(cur, type, - [](std::string&&) { return cpp_builtin_type::build(cpp_int); }); - case CXType_Long: - return make_leave_type(cur, type, - [](std::string&&) { return cpp_builtin_type::build(cpp_long); }); - case CXType_LongLong: - return make_leave_type(cur, type, [](std::string&&) { - return cpp_builtin_type::build(cpp_longlong); - }); - case CXType_Int128: - return make_leave_type(cur, type, [](std::string&&) { - return cpp_builtin_type::build(cpp_int128); - }); - case CXType_Float: - return make_leave_type(cur, type, [](std::string&&) { - return cpp_builtin_type::build(cpp_float); - }); - case CXType_Double: - return make_leave_type(cur, type, [](std::string&&) { - return cpp_builtin_type::build(cpp_double); - }); - case CXType_LongDouble: - return make_leave_type(cur, type, [](std::string&&) { - return cpp_builtin_type::build(cpp_longdouble); - }); - case CXType_Float128: - return make_leave_type(cur, type, [](std::string&&) { - return cpp_builtin_type::build(cpp_float128); - }); - case CXType_Char_U: - case CXType_Char_S: - return make_leave_type(cur, type, - [](std::string&&) { return cpp_builtin_type::build(cpp_char); }); - case CXType_Char16: - return make_leave_type(cur, type, [](std::string&&) { - return cpp_builtin_type::build(cpp_char16); - }); - case CXType_Char32: - return make_leave_type(cur, type, [](std::string&&) { - return cpp_builtin_type::build(cpp_char32); - }); - case CXType_WChar: - return make_leave_type(cur, type, [](std::string&&) { - return cpp_builtin_type::build(cpp_wchar); - }); - case CXType_NullPtr: - return make_leave_type(cur, type, [](std::string&&) { - return cpp_builtin_type::build(cpp_nullptr); - }); - - case CXType_Elaborated: - if (auto itype = try_parse_instantiation_type(context, cur, type)) - // elaborated types can be instantiation types - return itype; - // fallthrough - case CXType_Record: - case CXType_Enum: - case CXType_Typedef: - return make_leave_type(cur, type, [&](std::string&& spelling) { - auto decl = clang_getTypeDeclaration(type); - if (remove_prefix(spelling, "(anonymous", false)) - spelling = ""; // anonymous type - return cpp_user_defined_type::build( - cpp_type_ref(detail::get_entity_id(decl), std::move(spelling))); - }); - - case CXType_Pointer: - { - auto pointee = parse_type_impl(context, cur, clang_getPointeeType(type)); - auto pointer = cpp_pointer_type::build(std::move(pointee)); - - auto spelling = get_type_spelling(cur, type); - auto cv = suffix_cv(spelling); - return make_cv_qualified(std::move(pointer), cv); - } - case CXType_LValueReference: - case CXType_RValueReference: - { - auto referee = parse_type_impl(context, cur, clang_getPointeeType(type)); - return cpp_reference_type::build(std::move(referee), get_reference_kind(type)); - } - - case CXType_IncompleteArray: - case CXType_VariableArray: - case CXType_DependentSizedArray: - case CXType_ConstantArray: - return try_parse_array_type(context, cur, type); - - case CXType_FunctionNoProto: - case CXType_FunctionProto: - return try_parse_function_type(context, cur, type); - - case CXType_MemberPointer: - return cpp_pointer_type::build(parse_member_pointee_type(context, cur, type)); - - case CXType_Auto: - return make_leave_type(cur, type, - [&](std::string&&) { return cpp_auto_type::build(); }); - } - - DEBUG_UNREACHABLE(detail::assert_handler{}); + auto entity = b(std::move(spelling)); + if (!entity) return nullptr; + return make_cv_qualified(std::move(entity), cv); +} + +cpp_reference get_reference_kind(const CXType& type) +{ + if (type.kind == CXType_LValueReference) + return cpp_ref_lvalue; + else if (type.kind == CXType_RValueReference) + return cpp_ref_rvalue; + return cpp_ref_none; +} + +std::unique_ptr parse_type_impl(const detail::parse_context& context, const CXCursor& cur, + const CXType& type); + +std::unique_ptr parse_array_size(const CXCursor& cur, const CXType& type) +{ + auto size = clang_getArraySize(type); + if (size != -1) + return cpp_literal_expression::build(cpp_builtin_type::build(cpp_ulonglong), + std::to_string(size)); + + auto spelling = get_type_spelling(cur, type); + DEBUG_ASSERT(spelling.size() > 2u && spelling.back() == ']', detail::parse_error_handler{}, + type, "unexpected token"); + + std::string size_expr; + auto bracket_count = 1; + for (auto ptr = spelling.c_str() + spelling.size() - 2u; bracket_count != 0; --ptr) + { + if (*ptr == ']') + ++bracket_count; + else if (*ptr == '[') + --bracket_count; + + if (bracket_count != 0) + size_expr += *ptr; + } + + return size_expr.empty() + ? nullptr + : cpp_unexposed_expression::build(cpp_builtin_type::build(cpp_ulonglong), + cpp_token_string::tokenize( + std::string(size_expr.rbegin(), + size_expr.rend()))); +} + +std::unique_ptr try_parse_array_type(const detail::parse_context& context, + const CXCursor& cur, const CXType& type) +{ + auto canonical = clang_getCanonicalType(type); + auto value_type = clang_getArrayElementType(type); + if (value_type.kind == CXType_Invalid) + { + // value_type is invalid, however type can still be an array + // as there seems to be a libclang bug + // only if the canonical type is not an array, + // is it truly not an array + value_type = clang_getArrayElementType(canonical); + if (value_type.kind == CXType_Invalid) + return nullptr; + // we have an array, even though type isn't one directly + // only downside of this workaround: we've stripped away typedefs + } + + auto size = parse_array_size(cur, canonical); // type may not work, see above + return cpp_array_type::build(parse_type_impl(context, cur, value_type), std::move(size)); +} + +template +std::unique_ptr add_parameters(Builder& builder, const detail::parse_context& context, + const CXCursor& cur, const CXType& type) +{ + auto no_args = clang_getNumArgTypes(type); + DEBUG_ASSERT(no_args >= 0, detail::parse_error_handler{}, type, "invalid number of arguments"); + for (auto i = 0u; i != unsigned(no_args); ++i) + builder.add_parameter(parse_type_impl(context, cur, clang_getArgType(type, i))); + + if (clang_isFunctionTypeVariadic(type)) + builder.is_variadic(); + + return builder.finish(); +} + +std::unique_ptr try_parse_function_type(const detail::parse_context& context, + const CXCursor& cur, const CXType& type) +{ + auto result = clang_getResultType(type); + if (result.kind == CXType_Invalid) + // not a function type + return nullptr; + + cpp_function_type::builder builder(parse_type_impl(context, cur, result)); + return add_parameters(builder, context, cur, type); +} + +cpp_reference member_function_ref_qualifier(std::string& spelling) +{ + if (remove_suffix(spelling, "&&", false)) + return cpp_ref_rvalue; + else if (remove_suffix(spelling, "&", false)) + return cpp_ref_lvalue; + return cpp_ref_none; +} + +std::unique_ptr make_ref_qualified(std::unique_ptr type, cpp_reference ref) +{ + if (ref == cpp_ref_none) + return type; + return cpp_reference_type::build(std::move(type), ref); +} + +std::unique_ptr parse_member_pointee_type(const detail::parse_context& context, + const CXCursor& cur, const CXType& type) +{ + auto spelling = get_type_spelling(cur, type); + auto ref = member_function_ref_qualifier(spelling); + auto cv = suffix_cv(spelling); + + auto class_t = clang_Type_getClassType(type); + auto class_entity + = make_ref_qualified(make_cv_qualified(parse_type_impl(context, cur, class_t), cv), ref); + + auto pointee = clang_getPointeeType(type); // for everything except the class type + auto result = clang_getResultType(pointee); + if (result.kind == CXType_Invalid) + { + // member data pointer + return cpp_member_object_type::build(std::move(class_entity), + parse_type_impl(context, cur, pointee)); + } + else + { + cpp_member_function_type::builder builder(std::move(class_entity), + parse_type_impl(context, cur, result)); + return add_parameters(builder, context, cur, pointee); } } +bool is_direct_templated(const CXCursor& cur) +{ + // TODO: variable template + auto kind = clang_getCursorKind(cur); + return kind == CXCursor_TypeAliasTemplateDecl || kind == CXCursor_ClassTemplate + || kind == CXCursor_ClassTemplatePartialSpecialization + || kind == CXCursor_FunctionTemplate; +} + +bool check_parent(const CXCursor& parent) +{ + if (clang_Cursor_isNull(parent)) + return false; + auto kind = clang_getCursorKind(parent); + return kind != CXCursor_Namespace && kind != CXCursor_TranslationUnit; +} + +CXCursor get_template(CXCursor cur) +{ + do + { + if (is_direct_templated(cur)) + return cur; + cur = clang_getCursorSemanticParent(cur); + } while (check_parent(cur)); + return clang_getNullCursor(); +} + +std::unique_ptr try_parse_template_parameter_type(const detail::parse_context& context, + const CXCursor& cur, const CXType& type) +{ + // see if we have a parent template + auto templ = get_template(cur); + if (clang_Cursor_isNull(templ)) + // not a template + return nullptr; + + // doesn't respect cv qualifiers properly + auto result + = make_leave_type(cur, type, [&](std::string&& type_spelling) -> std::unique_ptr { + // look at the template parameters, + // see if we find a matching one + auto param = clang_getNullCursor(); + detail::visit_children(templ, [&](const CXCursor& child) { + if (clang_getCursorKind(child) == CXCursor_TemplateTypeParameter + && get_type_spelling(child, clang_getCursorType(child)) == type_spelling) + { + // found one + DEBUG_ASSERT(clang_Cursor_isNull(param), detail::assert_handler{}); + param = child; + } + }); + + if (clang_Cursor_isNull(param)) + return nullptr; + else + // found matching parameter + return cpp_template_parameter_type::build( + cpp_template_type_parameter_ref(detail::get_entity_id(param), + std::move(type_spelling))); + }); + + if (result) + return result; + else + // try again in a possible parent template + return try_parse_template_parameter_type(context, clang_getCursorSemanticParent(templ), + type); +} + +CXCursor get_instantiation_template(const CXCursor& cur, const CXType& type, + const std::string& templ_name) +{ + // look if the type has a declaration that is a template + auto decl = clang_getTypeDeclaration(type); + auto count = clang_Type_getNumTemplateArguments(clang_getCursorType(decl)); + if (count > 0 || is_direct_templated(decl)) + { + auto specialized = clang_getSpecializedCursorTemplate(decl); + if (clang_Cursor_isNull(specialized)) + // is already a template + return decl; + else + return specialized; + } + else + { + // look if the templ_name matches a template template parameter + auto param = clang_getNullCursor(); + detail::visit_children(cur, [&](const CXCursor& child) { + if (clang_getCursorKind(child) == CXCursor_TemplateTemplateParameter + && detail::get_cursor_name(child) == templ_name.c_str()) + { + DEBUG_ASSERT(clang_Cursor_isNull(param), detail::parse_error_handler{}, cur, + "multiple template template parameters with the same name?!"); + param = child; + } + }); + return param; + } +} + +std::unique_ptr try_parse_instantiation_type(const detail::parse_context&, + const CXCursor& cur, const CXType& type) +{ + return make_leave_type(cur, type, [&](std::string&& spelling) -> std::unique_ptr { + auto ptr = spelling.c_str(); + + std::string templ_name; + for (; *ptr && *ptr != '<'; ++ptr) + templ_name += *ptr; + if (*ptr != '<') + return nullptr; + ++ptr; + + auto templ = get_instantiation_template(cur, type, templ_name); + if (clang_Cursor_isNull(templ)) + return nullptr; + + cpp_template_instantiation_type::builder builder( + cpp_template_ref(detail::get_entity_id(templ), std::move(templ_name))); + + // parse arguments + // i.e. not parse really, just add the string + if (spelling.empty() || spelling.back() != '>') + return nullptr; + spelling.pop_back(); + while (!spelling.empty() && spelling.back() == ' ') + spelling.pop_back(); + builder.add_unexposed_arguments(ptr); + + return builder.finish(); + }); +} + +std::unique_ptr try_parse_decltype_type(const detail::parse_context&, const CXCursor& cur, + const CXType& type) +{ + if (clang_isExpression(clang_getCursorKind(cur))) + return nullptr; // don't use decltype here + + return make_leave_type(cur, type, [&](std::string&& spelling) -> std::unique_ptr { + if (!remove_prefix(spelling, "decltype(", false)) + return nullptr; + remove_suffix(spelling, "...", false); // variadic decltype. fun + DEBUG_ASSERT(!spelling.empty() && spelling.back() == ')', detail::parse_error_handler{}, + type, "unexpected spelling"); + spelling.pop_back(); + + return cpp_decltype_type::build( + cpp_unexposed_expression::build(cpp_unexposed_type::build(""), + cpp_token_string::tokenize(spelling))); + }); +} + +std::unique_ptr parse_type_impl(const detail::parse_context& context, const CXCursor& cur, + const CXType& type) +{ + switch (type.kind) + { + // stuff I can't parse + // or have no idea what it is and wait for bug report + case CXType_Invalid: + case CXType_Overload: + case CXType_ObjCId: + case CXType_ObjCClass: + case CXType_ObjCSel: + case CXType_BlockPointer: + case CXType_Vector: + case CXType_ObjCInterface: + case CXType_ObjCObjectPointer: +#if CINDEX_VERSION_MINOR >= 43 + case CXType_Half: + case CXType_Pipe: + case CXType_OCLImage1dRO: + case CXType_OCLImage1dArrayRO: + case CXType_OCLImage1dBufferRO: + case CXType_OCLImage2dRO: + case CXType_OCLImage2dArrayRO: + case CXType_OCLImage2dDepthRO: + case CXType_OCLImage2dArrayDepthRO: + case CXType_OCLImage2dMSAARO: + case CXType_OCLImage2dArrayMSAARO: + case CXType_OCLImage2dMSAADepthRO: + case CXType_OCLImage2dArrayMSAADepthRO: + case CXType_OCLImage3dRO: + case CXType_OCLImage1dWO: + case CXType_OCLImage1dArrayWO: + case CXType_OCLImage1dBufferWO: + case CXType_OCLImage2dWO: + case CXType_OCLImage2dArrayWO: + case CXType_OCLImage2dDepthWO: + case CXType_OCLImage2dArrayDepthWO: + case CXType_OCLImage2dMSAAWO: + case CXType_OCLImage2dArrayMSAAWO: + case CXType_OCLImage2dMSAADepthWO: + case CXType_OCLImage2dArrayMSAADepthWO: + case CXType_OCLImage3dWO: + case CXType_OCLImage1dRW: + case CXType_OCLImage1dArrayRW: + case CXType_OCLImage1dBufferRW: + case CXType_OCLImage2dRW: + case CXType_OCLImage2dArrayRW: + case CXType_OCLImage2dDepthRW: + case CXType_OCLImage2dArrayDepthRW: + case CXType_OCLImage2dMSAARW: + case CXType_OCLImage2dArrayMSAARW: + case CXType_OCLImage2dMSAADepthRW: + case CXType_OCLImage2dArrayMSAADepthRW: + case CXType_OCLImage3dRW: + case CXType_OCLSampler: + case CXType_OCLEvent: + case CXType_OCLQueue: + case CXType_OCLReserveID: +#endif +#if CINDEX_VERSION_MINOR > 43 + case CXType_Float16: +#endif + context.logger->log("libclang parser", + format_diagnostic(severity::warning, detail::make_location(type), + "unexpected type of kind '", + detail::get_type_kind_spelling(type).c_str(), "'")); + // fallthrough + case CXType_Dependent: // seems to have something to do with expressions, just ignore that (for + // now?) + case CXType_Unexposed: + if (auto ftype = try_parse_function_type(context, cur, type)) + // guess what: after you've called clang_getPointeeType() on a function pointer + // you'll get an unexposed type + return ftype; + else if (auto atype = try_parse_array_type(context, cur, type)) + // same deal here + return atype; + else if (auto dtype = try_parse_decltype_type(context, cur, type)) + // decltype unexposed + return dtype; + else if (auto itype = try_parse_instantiation_type(context, cur, type)) + // instantiation unexposed + return itype; + else if (auto ptype = try_parse_template_parameter_type(context, cur, type)) + // template parameter type is unexposed + return ptype; + // fallthrough + case CXType_Complex: + return cpp_unexposed_type::build(get_type_spelling(cur, type).c_str()); + + case CXType_Void: + return make_leave_type(cur, type, + [](std::string&&) { return cpp_builtin_type::build(cpp_void); }); + case CXType_Bool: + return make_leave_type(cur, type, + [](std::string&&) { return cpp_builtin_type::build(cpp_bool); }); + case CXType_UChar: + return make_leave_type(cur, type, + [](std::string&&) { return cpp_builtin_type::build(cpp_uchar); }); + case CXType_UShort: + return make_leave_type(cur, type, + [](std::string&&) { return cpp_builtin_type::build(cpp_ushort); }); + case CXType_UInt: + return make_leave_type(cur, type, + [](std::string&&) { return cpp_builtin_type::build(cpp_uint); }); + case CXType_ULong: + return make_leave_type(cur, type, + [](std::string&&) { return cpp_builtin_type::build(cpp_ulong); }); + case CXType_ULongLong: + return make_leave_type(cur, type, [](std::string&&) { + return cpp_builtin_type::build(cpp_ulonglong); + }); + case CXType_UInt128: + return make_leave_type(cur, type, + [](std::string&&) { return cpp_builtin_type::build(cpp_uint128); }); + case CXType_SChar: + return make_leave_type(cur, type, + [](std::string&&) { return cpp_builtin_type::build(cpp_schar); }); + case CXType_Short: + return make_leave_type(cur, type, + [](std::string&&) { return cpp_builtin_type::build(cpp_short); }); + case CXType_Int: + return make_leave_type(cur, type, + [](std::string&&) { return cpp_builtin_type::build(cpp_int); }); + case CXType_Long: + return make_leave_type(cur, type, + [](std::string&&) { return cpp_builtin_type::build(cpp_long); }); + case CXType_LongLong: + return make_leave_type(cur, type, + [](std::string&&) { return cpp_builtin_type::build(cpp_longlong); }); + case CXType_Int128: + return make_leave_type(cur, type, + [](std::string&&) { return cpp_builtin_type::build(cpp_int128); }); + case CXType_Float: + return make_leave_type(cur, type, + [](std::string&&) { return cpp_builtin_type::build(cpp_float); }); + case CXType_Double: + return make_leave_type(cur, type, + [](std::string&&) { return cpp_builtin_type::build(cpp_double); }); + case CXType_LongDouble: + return make_leave_type(cur, type, [](std::string&&) { + return cpp_builtin_type::build(cpp_longdouble); + }); + case CXType_Float128: + return make_leave_type(cur, type, + [](std::string&&) { return cpp_builtin_type::build(cpp_float128); }); + case CXType_Char_U: + case CXType_Char_S: + return make_leave_type(cur, type, + [](std::string&&) { return cpp_builtin_type::build(cpp_char); }); + case CXType_Char16: + return make_leave_type(cur, type, + [](std::string&&) { return cpp_builtin_type::build(cpp_char16); }); + case CXType_Char32: + return make_leave_type(cur, type, + [](std::string&&) { return cpp_builtin_type::build(cpp_char32); }); + case CXType_WChar: + return make_leave_type(cur, type, + [](std::string&&) { return cpp_builtin_type::build(cpp_wchar); }); + case CXType_NullPtr: + return make_leave_type(cur, type, + [](std::string&&) { return cpp_builtin_type::build(cpp_nullptr); }); + + case CXType_Elaborated: + if (auto itype = try_parse_instantiation_type(context, cur, type)) + // elaborated types can be instantiation types + return itype; + // fallthrough + case CXType_Record: + case CXType_Enum: + case CXType_Typedef: + return make_leave_type(cur, type, [&](std::string&& spelling) { + auto decl = clang_getTypeDeclaration(type); + if (remove_prefix(spelling, "(anonymous", false)) + spelling = ""; // anonymous type + return cpp_user_defined_type::build( + cpp_type_ref(detail::get_entity_id(decl), std::move(spelling))); + }); + + case CXType_Pointer: + { + auto pointee = parse_type_impl(context, cur, clang_getPointeeType(type)); + auto pointer = cpp_pointer_type::build(std::move(pointee)); + + auto spelling = get_type_spelling(cur, type); + auto cv = suffix_cv(spelling); + return make_cv_qualified(std::move(pointer), cv); + } + case CXType_LValueReference: + case CXType_RValueReference: + { + auto referee = parse_type_impl(context, cur, clang_getPointeeType(type)); + return cpp_reference_type::build(std::move(referee), get_reference_kind(type)); + } + + case CXType_IncompleteArray: + case CXType_VariableArray: + case CXType_DependentSizedArray: + case CXType_ConstantArray: + return try_parse_array_type(context, cur, type); + + case CXType_FunctionNoProto: + case CXType_FunctionProto: + return try_parse_function_type(context, cur, type); + + case CXType_MemberPointer: + return cpp_pointer_type::build(parse_member_pointee_type(context, cur, type)); + + case CXType_Auto: + return make_leave_type(cur, type, [&](std::string&&) { return cpp_auto_type::build(); }); + } + + DEBUG_UNREACHABLE(detail::assert_handler{}); + return nullptr; +} +} // namespace + std::unique_ptr detail::parse_type(const detail::parse_context& context, const CXCursor& cur, const CXType& type) { @@ -789,8 +768,8 @@ std::unique_ptr detail::parse_cpp_type_alias(const detail::parse_con result = cpp_type_alias::build(name.c_str(), std::move(type)); else { - result = - cpp_type_alias::build(*context.idx, get_entity_id(cur), name.c_str(), std::move(type)); + result = cpp_type_alias::build(*context.idx, get_entity_id(cur), name.c_str(), + std::move(type)); context.comments.match(*result, cur); } diff --git a/src/libclang/variable_parser.cpp b/src/libclang/variable_parser.cpp index 7a17533..8de053a 100644 --- a/src/libclang/variable_parser.cpp +++ b/src/libclang/variable_parser.cpp @@ -64,8 +64,8 @@ std::unique_ptr detail::parse_cpp_variable(const detail::parse_conte detail::cxtokenizer tokenizer(context.tu, context.file, cur); for (auto& token : tokenizer) if (token.value() == "thread_local") - storage_class = - cpp_storage_class_specifiers(storage_class | cpp_storage_class_thread_local); + storage_class + = cpp_storage_class_specifiers(storage_class | cpp_storage_class_thread_local); else if (token.value() == "constexpr") is_constexpr = true; @@ -75,9 +75,9 @@ std::unique_ptr detail::parse_cpp_variable(const detail::parse_conte std::unique_ptr result; if (clang_isCursorDefinition(cur)) { - result = - cpp_variable::build(*context.idx, get_entity_id(cur), name.c_str(), std::move(type), - std::move(default_value), storage_class, is_constexpr); + result + = cpp_variable::build(*context.idx, get_entity_id(cur), name.c_str(), std::move(type), + std::move(default_value), storage_class, is_constexpr); } else result = cpp_variable::build_declaration(get_entity_id(cur), name.c_str(), std::move(type), diff --git a/src/visitor.cpp b/src/visitor.cpp index f43b47e..20d1ba5 100644 --- a/src/visitor.cpp +++ b/src/visitor.cpp @@ -18,47 +18,46 @@ using namespace cppast; namespace { - cpp_access_specifier_kind get_initial_access(const cpp_entity& e) - { - if (e.kind() == cpp_class::kind()) - return static_cast(e).class_kind() == cpp_class_kind::class_t ? - cpp_private : - cpp_public; - return cpp_public; - } +cpp_access_specifier_kind get_initial_access(const cpp_entity& e) +{ + if (e.kind() == cpp_class::kind()) + return static_cast(e).class_kind() == cpp_class_kind::class_t + ? cpp_private + : cpp_public; + return cpp_public; +} - void update_access(cpp_access_specifier_kind& child_access, const cpp_entity& child) - { - if (child.kind() == cpp_access_specifier::kind()) - child_access = static_cast(child).access_specifier(); - } +void update_access(cpp_access_specifier_kind& child_access, const cpp_entity& child) +{ + if (child.kind() == cpp_access_specifier::kind()) + child_access = static_cast(child).access_specifier(); +} - template - bool handle_container(const cpp_entity& e, detail::visitor_callback_t cb, void* functor, - cpp_access_specifier_kind cur_access, bool last_child) - { - auto& container = static_cast(e); +template +bool handle_container(const cpp_entity& e, detail::visitor_callback_t cb, void* functor, + cpp_access_specifier_kind cur_access, bool last_child) +{ + auto& container = static_cast(e); - auto handle_children = - cb(functor, container, {visitor_info::container_entity_enter, cur_access, last_child}); - if (handle_children) + auto handle_children + = cb(functor, container, {visitor_info::container_entity_enter, cur_access, last_child}); + 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); - for (auto iter = container.begin(); iter != container.end();) - { - auto& cur = *iter; - ++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())) - return false; - } + if (!detail::visit(cur, cb, functor, child_access, iter == container.end())) + 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 bool detail::visit(const cpp_entity& e, detail::visitor_callback_t cb, void* functor, diff --git a/test/cpp_attribute.cpp b/test/cpp_attribute.cpp index 8854f91..c350b4c 100644 --- a/test/cpp_attribute.cpp +++ b/test/cpp_attribute.cpp @@ -46,81 +46,84 @@ alignas(type) int var; auto file = parse({}, "cpp_attribute.cpp", code); - auto check_attribute = [](const cpp_attribute& attr, const char* name, - type_safe::optional scope, bool variadic, - const char* args, cpp_attribute_kind kind) { - REQUIRE(attr.kind() == kind); - REQUIRE(attr.name() == name); - REQUIRE(attr.scope() == scope); - REQUIRE(attr.is_variadic() == variadic); + auto check_attribute + = [](const cpp_attribute& attr, const char* name, type_safe::optional scope, + bool variadic, const char* args, cpp_attribute_kind kind) { + REQUIRE(attr.kind() == kind); + REQUIRE(attr.name() == name); + REQUIRE(attr.scope() == scope); + REQUIRE(attr.is_variadic() == variadic); - if (attr.arguments()) - REQUIRE(attr.arguments().value().as_string() == args); - else - REQUIRE(*args == '\0'); - }; + if (attr.arguments()) + REQUIRE(attr.arguments().value().as_string() == args); + else + REQUIRE(*args == '\0'); + }; - auto count = - test_visit(*file, - [&](const cpp_entity& e) { - auto& attributes = e.attributes(); - REQUIRE(attributes.size() >= 1u); - auto& attr = attributes.front(); + auto count + = test_visit(*file, + [&](const cpp_entity& e) { + auto& attributes = e.attributes(); + REQUIRE(attributes.size() >= 1u); + auto& attr = attributes.front(); - if (e.name() == "a" || e.name() == "b") - { - REQUIRE(attributes.size() == 2u); - REQUIRE(has_attribute(e, "attribute1")); - REQUIRE(has_attribute(e, "attribute2")); - check_attribute(attr, "attribute1", type_safe::nullopt, - false, "", cpp_attribute_kind::unknown); - check_attribute(attributes[1u], "attribute2", - type_safe::nullopt, false, "", - cpp_attribute_kind::unknown); - } - else if (e.name() == "c") - check_attribute(attr, "variadic", type_safe::nullopt, true, - "", cpp_attribute_kind::unknown); - else if (e.name() == "d") - { - REQUIRE(has_attribute(e, "ns::attribute")); - check_attribute(attr, "attribute", "ns", false, "", - cpp_attribute_kind::unknown); - } - else if (e.name() == "e") - check_attribute(attr, "attribute", type_safe::nullopt, - false, R"(arg1,arg2,+(){},42,"Hello!")", - cpp_attribute_kind::unknown); - else if (e.name() == "f") - { - REQUIRE(attributes.size() == 2u); - check_attribute(attr, "attribute", "ns", false, "+,-,0 4", - cpp_attribute_kind::unknown); - check_attribute(attributes[1u], "other_attribute", - type_safe::nullopt, false, "", - cpp_attribute_kind::unknown); - } - else if (e.name() == "g") - { - REQUIRE(has_attribute(e, cpp_attribute_kind::deprecated)); - check_attribute(attr, "deprecated", type_safe::nullopt, - false, "", cpp_attribute_kind::deprecated); - } - else if (e.name() == "h") - check_attribute(attr, "maybe_unused", type_safe::nullopt, - false, "", - cpp_attribute_kind::maybe_unused); - else if (e.name() == "i") - check_attribute(attr, "nodiscard", type_safe::nullopt, - false, "", cpp_attribute_kind::nodiscard); - else if (e.name() == "j") - check_attribute(attr, "noreturn", type_safe::nullopt, - false, "", cpp_attribute_kind::noreturn); - else if (e.name() == "k") - check_attribute(attr, "const", type_safe::nullopt, false, - "", cpp_attribute_kind::unknown); - }, - false); + if (e.name() == "a" || e.name() == "b") + { + REQUIRE(attributes.size() == 2u); + REQUIRE(has_attribute(e, "attribute1")); + REQUIRE(has_attribute(e, "attribute2")); + check_attribute(attr, "attribute1", type_safe::nullopt, + false, "", cpp_attribute_kind::unknown); + check_attribute(attributes[1u], "attribute2", + type_safe::nullopt, false, "", + cpp_attribute_kind::unknown); + } + else if (e.name() == "c") + check_attribute(attr, "variadic", type_safe::nullopt, + true, "", cpp_attribute_kind::unknown); + else if (e.name() == "d") + { + REQUIRE(has_attribute(e, "ns::attribute")); + check_attribute(attr, "attribute", "ns", false, "", + cpp_attribute_kind::unknown); + } + else if (e.name() == "e") + check_attribute(attr, "attribute", type_safe::nullopt, + false, R"(arg1,arg2,+(){},42,"Hello!")", + cpp_attribute_kind::unknown); + else if (e.name() == "f") + { + REQUIRE(attributes.size() == 2u); + check_attribute(attr, "attribute", "ns", false, + "+,-,0 4", cpp_attribute_kind::unknown); + check_attribute(attributes[1u], "other_attribute", + type_safe::nullopt, false, "", + cpp_attribute_kind::unknown); + } + else if (e.name() == "g") + { + REQUIRE( + has_attribute(e, cpp_attribute_kind::deprecated)); + check_attribute(attr, "deprecated", type_safe::nullopt, + false, "", + cpp_attribute_kind::deprecated); + } + else if (e.name() == "h") + check_attribute(attr, "maybe_unused", type_safe::nullopt, + false, "", + cpp_attribute_kind::maybe_unused); + else if (e.name() == "i") + check_attribute(attr, "nodiscard", type_safe::nullopt, + false, "", + cpp_attribute_kind::nodiscard); + else if (e.name() == "j") + check_attribute(attr, "noreturn", type_safe::nullopt, + false, "", cpp_attribute_kind::noreturn); + else if (e.name() == "k") + check_attribute(attr, "const", type_safe::nullopt, false, + "", cpp_attribute_kind::unknown); + }, + false); REQUIRE(count == 10); count = test_visit(*file, diff --git a/test/cpp_friend.cpp b/test/cpp_friend.cpp index 70a8c5c..4ab3bb5 100644 --- a/test/cpp_friend.cpp +++ b/test/cpp_friend.cpp @@ -226,8 +226,8 @@ int d() {} } else if (entity.value().kind() == cpp_entity_kind::function_template_specialization_t) { - auto& func = - static_cast(entity.value()); + auto& func + = static_cast(entity.value()); if (func.name() == "templ_c") { REQUIRE(func.function().is_declaration()); diff --git a/test/cpp_namespace.cpp b/test/cpp_namespace.cpp index 93a4313..e885cad 100644 --- a/test/cpp_namespace.cpp +++ b/test/cpp_namespace.cpp @@ -270,15 +270,15 @@ using ns1::d; )"; cpp_entity_index idx; - auto check_declaration = [&](const cpp_using_declaration& decl, const char* target_full_name, - unsigned no) { - auto target = decl.target(); - REQUIRE((target.no_overloaded() == no)); - for (auto entity : target.get(idx)) - { - REQUIRE(full_name(*entity) == target_full_name); - } - }; + auto check_declaration + = [&](const cpp_using_declaration& decl, const char* target_full_name, unsigned no) { + auto target = decl.target(); + REQUIRE((target.no_overloaded() == no)); + for (auto entity : target.get(idx)) + { + REQUIRE(full_name(*entity) == target_full_name); + } + }; auto file = parse(idx, "cpp_using_declaration.cpp", code); auto count = test_visit(*file, [&](const cpp_using_declaration& decl) { diff --git a/test/cpp_preprocessor.cpp b/test/cpp_preprocessor.cpp index ad3fa40..fbd2cef 100644 --- a/test/cpp_preprocessor.cpp +++ b/test/cpp_preprocessor.cpp @@ -34,36 +34,36 @@ namespace ns2 )"; const char* order[] = {"A", "B", "ns", "C", "D", "E", "ns2", "F"}; - auto check_macro = [](const cpp_macro_definition& macro, const char* replacement, - const char* args) { - REQUIRE(macro.replacement() == replacement); - if (args) - { - REQUIRE(macro.is_function_like()); + auto check_macro + = [](const cpp_macro_definition& macro, const char* replacement, const char* args) { + REQUIRE(macro.replacement() == replacement); + if (args) + { + REQUIRE(macro.is_function_like()); - std::string params; - for (auto& param : macro.parameters()) - { - if (!params.empty()) - params += ","; - params += param.name(); - } - if (macro.is_variadic()) - { - if (!params.empty()) - params += ","; - params += "..."; - } + std::string params; + for (auto& param : macro.parameters()) + { + if (!params.empty()) + params += ","; + params += param.name(); + } + if (macro.is_variadic()) + { + if (!params.empty()) + params += ","; + params += "..."; + } - REQUIRE(params == args); - } - else - { - REQUIRE(!macro.is_function_like()); - REQUIRE(!macro.is_variadic()); - REQUIRE(macro.parameters().empty()); - } - }; + REQUIRE(params == args); + } + else + { + REQUIRE(!macro.is_function_like()); + REQUIRE(!macro.is_variadic()); + REQUIRE(macro.parameters().empty()); + } + }; auto file = parse({}, "cpp_macro_definition.cpp", code); auto count = test_visit(*file, [&](const cpp_macro_definition& macro) { @@ -117,25 +117,25 @@ b auto file_a = parse(idx, "header_a.hpp", header_a); auto file_b = parse(idx, "header_b.hpp", header_b); - auto count = - test_visit(*file_a, [&](const cpp_include_directive& include) { - if (include.name() == "iostream") - { - REQUIRE(include.target().name() == include.name()); - REQUIRE(include.include_kind() == cppast::cpp_include_kind::system); - REQUIRE(include.target().get(idx).empty()); - REQUIRE_THAT(include.full_path(), Catch::EndsWith("iostream")); - } - else if (include.name() == "cpp_include_directive-header.hpp") - { - REQUIRE(include.target().name() == include.name()); - REQUIRE(include.include_kind() == cppast::cpp_include_kind::local); - REQUIRE(include.target().get(idx).empty()); - REQUIRE(include.full_path() == "./cpp_include_directive-header.hpp"); - } - else - REQUIRE(false); - }); + auto count + = test_visit(*file_a, [&](const cpp_include_directive& include) { + if (include.name() == "iostream") + { + REQUIRE(include.target().name() == include.name()); + REQUIRE(include.include_kind() == cppast::cpp_include_kind::system); + REQUIRE(include.target().get(idx).empty()); + REQUIRE_THAT(include.full_path(), Catch::EndsWith("iostream")); + } + else if (include.name() == "cpp_include_directive-header.hpp") + { + REQUIRE(include.target().name() == include.name()); + REQUIRE(include.include_kind() == cppast::cpp_include_kind::local); + REQUIRE(include.target().get(idx).empty()); + REQUIRE(include.full_path() == "./cpp_include_directive-header.hpp"); + } + else + REQUIRE(false); + }); REQUIRE(count == 2u); count = test_visit(*file_b, [&](const cpp_include_directive& include) { diff --git a/test/cpp_template_parameter.cpp b/test/cpp_template_parameter.cpp index d6a393a..c7ba3d8 100644 --- a/test/cpp_template_parameter.cpp +++ b/test/cpp_template_parameter.cpp @@ -45,8 +45,8 @@ using e = void; { REQUIRE(p.kind() == cpp_entity_kind::template_type_parameter_t); - auto& param = - static_cast(p); + auto& param + = static_cast(p); if (param.name() == "A") { REQUIRE(alias.name() == "a"); @@ -132,8 +132,8 @@ using d = void; REQUIRE(p.kind() == cpp_entity_kind::non_type_template_parameter_t); - auto& param = - static_cast(p); + auto& param + = static_cast(p); if (param.name() == "A") { REQUIRE(alias.name() == "a"); @@ -227,8 +227,8 @@ using d = void; REQUIRE(p.kind() == cpp_entity_kind::template_template_parameter_t); - auto& param = - static_cast(p); + auto& param + = static_cast(p); REQUIRE(param.keyword() == cpp_template_keyword::keyword_class); if (param.name() == "A") { diff --git a/test/cpp_type_alias.cpp b/test/cpp_type_alias.cpp index aba8c95..d7492ba 100644 --- a/test/cpp_type_alias.cpp +++ b/test/cpp_type_alias.cpp @@ -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: { auto& entity_parsed = static_cast(parsed).entity(); - auto& entity_synthesized = - static_cast(synthesized).entity(); + auto& entity_synthesized + = static_cast(synthesized).entity(); return equal_ref(idx, entity_parsed, entity_synthesized); } case cpp_type_kind::template_instantiation_t: @@ -357,8 +357,8 @@ typedef decltype(0) w; } else if (alias.name() == "d") { - auto type = - cpp_pointer_type::build(add_cv(cpp_builtin_type::build(cpp_uint), cpp_cv_const)); + auto type + = cpp_pointer_type::build(add_cv(cpp_builtin_type::build(cpp_uint), cpp_cv_const)); REQUIRE(equal_types(idx, alias.underlying_type(), *type)); } else if (alias.name() == "e") @@ -375,9 +375,9 @@ typedef decltype(0) w; } else if (alias.name() == "g") { - auto type = - cpp_reference_type::build(add_cv(cpp_builtin_type::build(cpp_int), cpp_cv_const), - cpp_ref_rvalue); + auto type + = cpp_reference_type::build(add_cv(cpp_builtin_type::build(cpp_int), cpp_cv_const), + cpp_ref_rvalue); REQUIRE(equal_types(idx, alias.underlying_type(), *type)); } else if (alias.name() == "h") @@ -399,21 +399,21 @@ typedef decltype(0) w; } else if (alias.name() == "k") { - auto type = - cpp_array_type::build(cpp_builtin_type::build(cpp_int), make_size("42", true)); + auto type + = cpp_array_type::build(cpp_builtin_type::build(cpp_int), make_size("42", true)); REQUIRE(equal_types(idx, alias.underlying_type(), *type)); } else if (alias.name() == "l") { - auto type = - cpp_array_type::build(cpp_pointer_type::build(cpp_builtin_type::build(cpp_float)), - nullptr); + auto type + = cpp_array_type::build(cpp_pointer_type::build(cpp_builtin_type::build(cpp_float)), + nullptr); REQUIRE(equal_types(idx, alias.underlying_type(), *type)); } else if (alias.name() == "m") { - auto type = - cpp_array_type::build(cpp_builtin_type::build(cpp_char), make_size("42", true)); + auto type + = cpp_array_type::build(cpp_builtin_type::build(cpp_char), make_size("42", true)); REQUIRE(equal_types(idx, alias.underlying_type(), *type)); } else if (alias.name() == "n") @@ -474,10 +474,12 @@ typedef decltype(0) w; } else if (alias.name() == "s") { - auto pointee = cpp_member_object_type:: - build(cpp_user_defined_type::build(cpp_type_ref(cpp_entity_id(""), "foo")), - cpp_unexposed_type::build( - "int")); // type not exposed directly for some reason + auto pointee + = cpp_member_object_type::build(cpp_user_defined_type::build( + cpp_type_ref(cpp_entity_id(""), "foo")), + cpp_unexposed_type::build( + "int")); // type not exposed directly for some + // reason auto type = cpp_pointer_type::build(std::move(pointee)); REQUIRE(equal_types(idx, alias.underlying_type(), *type)); diff --git a/test/integration.cpp b/test/integration.cpp index 2f1efa1..a2b9f36 100644 --- a/test/integration.cpp +++ b/test/integration.cpp @@ -16,53 +16,53 @@ TEST_CASE("stdlib", "[!hide][integration]") //#include -- problem with compiler built-in stuff on OSX #include //#include -- same as above -#include -#include -#include -#include #include -#include -#include -#include #include +#include #include +#include +#include #include #include +#include +#include +#include +#include -#include #include +#include #include -#include #include +#include #include //#include -- missing types from C header (for some reason) #include //#include -- weird issue with compiler built-in stuff -#include #include -#include #include +#include +#include #include -#include #include #include +#include //#include -- not supported on CI #include #include -#include #include -#include #include -#include +#include #include -#include -#include -#include #include +#include +#include +#include +#include +#include #include @@ -70,22 +70,22 @@ TEST_CASE("stdlib", "[!hide][integration]") //#include -- non-conforming GCC extension with regards to constexpr //#include -- weird double include issue under MSVC -#include -#include #include +#include #include +#include //#include -- same issue with cinttypes -#include +#include +#include +#include #include +#include +#include #include #include -#include -#include #include -#include #include -#include #include //#include -- issue on OSX @@ -94,10 +94,10 @@ TEST_CASE("stdlib", "[!hide][integration]") //#include -- issue on MSVC -#include -#include -#include #include +#include +#include +#include )"; write_file("stdlib.cpp", code); diff --git a/test/libclang_parser.cpp b/test/libclang_parser.cpp index e4cf9b4..e4af768 100644 --- a/test/libclang_parser.cpp +++ b/test/libclang_parser.cpp @@ -57,7 +57,7 @@ TEST_CASE("libclang_compile_config") } ])"; -#define CPPAST_DETAIL_DRIVE "C:" +# define CPPAST_DETAIL_DRIVE "C:" #else auto json = R"([ @@ -83,7 +83,7 @@ TEST_CASE("libclang_compile_config") } ])"; -#define CPPAST_DETAIL_DRIVE +# define CPPAST_DETAIL_DRIVE #endif diff --git a/test/preprocessor.cpp b/test/preprocessor.cpp index 1605443..c59ec93 100644 --- a/test/preprocessor.cpp +++ b/test/preprocessor.cpp @@ -65,7 +65,8 @@ TEST_CASE("preprocessing use external macro") auto result = NAN; #endif -)", fast_preprocessing); +)", + fast_preprocessing); test_visit(*file, [&](const cpp_variable&) {}); } @@ -302,7 +303,8 @@ void j(); { if (comment.content == "cstddef\ncstddef") // 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; else REQUIRE(comment.content == "u"); diff --git a/tool/main.cpp b/tool/main.cpp index 8360e8e..c62132e 100644 --- a/tool/main.cpp +++ b/tool/main.cpp @@ -6,12 +6,12 @@ #include -#include // for libclang_parser, libclang_compile_config, cpp_entity,... -#include // for visit() -#include // for generate_code() +#include // for generate_code() #include // for the cpp_entity_kind definition #include // for is_definition() #include // for cpp_namespace +#include // for libclang_parser, libclang_compile_config, cpp_entity,... +#include // for visit() // print help options void print_help(const cxxopts::Options& options) @@ -239,12 +239,12 @@ int main(int argc, char* argv[]) try cppast::libclang_compilation_database database( options["database_dir"].as()); if (options.count("database_file")) - config = - cppast::libclang_compile_config(database, - options["database_file"].as()); + config + = cppast::libclang_compile_config(database, + options["database_file"].as()); else - config = - cppast::libclang_compile_config(database, options["file"].as()); + config + = cppast::libclang_compile_config(database, options["file"].as()); } if (options.count("verbose"))