diff --git a/include/cppast/compile_config.hpp b/include/cppast/compile_config.hpp index 565cb579..3085ac89 100644 --- a/include/cppast/compile_config.hpp +++ b/include/cppast/compile_config.hpp @@ -15,7 +15,7 @@ namespace cppast { -/// The C++ standard that should be used. +/// The C/C++ standard that should be used. enum class cpp_standard { cpp_98, @@ -26,8 +26,14 @@ enum class cpp_standard cpp_17, cpp_2a, cpp_20, + c_89, + c_99, + c_11, + c_17, + c_2x, cpp_latest = cpp_standard::cpp_14, //< The latest supported C++ standard. + c_latest = cpp_standard::c_17, //< The latest supported C standard. }; /// \returns A human readable string representing the option, @@ -52,12 +58,82 @@ inline const char* to_string(cpp_standard standard) noexcept return "c++2a"; case cpp_standard::cpp_20: return "c++20"; + case cpp_standard::c_89: + return "c89"; + case cpp_standard::c_99: + return "c99"; + case cpp_standard::c_11: + return "c11"; + case cpp_standard::c_17: + return "c17"; + case cpp_standard::c_2x: + return "c2x"; } DEBUG_UNREACHABLE(detail::assert_handler{}); return "ups"; } +/// \returns The C/C++ standard corresponding to the string, e.g. `cpp_14` for `c++14` +/// \throws std::invalid_argument for an unknown language standard +inline cpp_standard to_standard(const std::string& str) +{ + if (str == "c++98") + return cpp_standard::cpp_98; + else if (str == "c++03") + return cpp_standard::cpp_03; + else if (str == "c++11") + return cpp_standard::cpp_11; + else if (str == "c++14") + return cpp_standard::cpp_14; + else if (str == "c++1z") + return cpp_standard::cpp_1z; + else if (str == "c++17") + return cpp_standard::cpp_17; + else if (str == "c++2a") + return cpp_standard::cpp_2a; + else if (str == "c++20") + return cpp_standard::cpp_20; + else if (str == "c89") + return cpp_standard::c_89; + else if (str == "c99") + return cpp_standard::c_99; + else if (str == "c11") + return cpp_standard::c_11; + else if (str == "c17") + return cpp_standard::c_17; + else if (str == "c2x") + return cpp_standard::c_2x; + else + throw std::invalid_argument("invalid C/C++ standard '" + str + "'"); +} + +/// \returns whether the language standard is a C standard +inline bool is_c_standard(cpp_standard standard) noexcept +{ + switch (standard) + { + case cpp_standard::cpp_98: + case cpp_standard::cpp_03: + case cpp_standard::cpp_11: + case cpp_standard::cpp_14: + case cpp_standard::cpp_1z: + case cpp_standard::cpp_17: + case cpp_standard::cpp_2a: + case cpp_standard::cpp_20: + return false; + case cpp_standard::c_89: + case cpp_standard::c_99: + case cpp_standard::c_11: + case cpp_standard::c_17: + case cpp_standard::c_2x: + return true; + } + + DEBUG_UNREACHABLE(detail::assert_handler{}); + return false; +} + /// Other special compilation flags. enum class compile_flag { @@ -114,6 +190,12 @@ class compile_config return do_get_name(); } + /// \returns Whether to parse files as C rather than C++. + bool use_c() const noexcept + { + return do_use_c(); + } + protected: compile_config(std::vector def_flags) : flags_(std::move(def_flags)) {} @@ -157,6 +239,9 @@ class compile_config /// \notes This allows detecting mismatches of configurations and parsers. virtual const char* do_get_name() const noexcept = 0; + /// \returns Whether to parse files as C rather than C++. + virtual bool do_use_c() const noexcept = 0; + std::vector flags_; }; } // namespace cppast diff --git a/include/cppast/cpp_storage_class_specifiers.hpp b/include/cppast/cpp_storage_class_specifiers.hpp index 697f85db..9e40d365 100644 --- a/include/cppast/cpp_storage_class_specifiers.hpp +++ b/include/cppast/cpp_storage_class_specifiers.hpp @@ -19,10 +19,16 @@ enum cpp_storage_class_specifiers : int 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_thread_local = 8, //< *thread* storage duration. + cpp_storage_class_register = 2, //< *automatic* storage duration. + /// Hints to the compiler to keep this in a register. + /// \notes In C a register variable cannot have its address taken. For C++ `register` is + /// deprecated in C++11 and removed in C++17. + + cpp_storage_class_static = 4, //< *static* or *thread* storage duration and *internal* linkage. + cpp_storage_class_extern = 8, //< *static* or *thread* storage duration and *external* linkage. + + cpp_storage_class_thread_local = 16, //< *thread* storage duration. /// \notes This is the only one that can be combined with the others. }; @@ -49,6 +55,13 @@ 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 `register`. +inline bool is_register(cpp_storage_class_specifiers spec) noexcept +{ + return (spec & cpp_storage_class_register) != 0; +} } // namespace cppast + #endif // CPPAST_CPP_STORAGE_CLASS_SPECIFIERS_HPP_INCLUDED diff --git a/include/cppast/cpp_type.hpp b/include/cppast/cpp_type.hpp index d275614d..78ad6058 100644 --- a/include/cppast/cpp_type.hpp +++ b/include/cppast/cpp_type.hpp @@ -286,25 +286,47 @@ class cpp_dependent_type final : public cpp_type std::unique_ptr dependee_; }; -/// The kinds of C++ cv qualifiers. -enum cpp_cv : int +/// Flags for the kinds of C/C++ qualifiers. +enum cpp_cv_flags : int { - cpp_cv_none, - cpp_cv_const, - cpp_cv_volatile, - cpp_cv_const_volatile, + cpp_cv_const, //< constant type + cpp_cv_volatile, //< volatile type + cpp_cv_restrict, //< restrict pointer type (C-only) + cpp_cv_atomic, //< atomic type (C-only) + + _flag_set_size, //< \exclude }; +/// Flag set for the kinds for C/C++ qualifiers. +using cpp_cv = type_safe::flag_set; + +/// Represents a const volatile qualified type +constexpr cpp_cv cpp_cv_const_volatile = cpp_cv(cpp_cv_const) | cpp_cv_volatile; +/// Represents an unqualified type +constexpr cpp_cv cpp_cv_none = cpp_cv(); + /// \returns `true` if the qualifier contains `const`. -inline bool is_const(cpp_cv cv) noexcept +inline bool is_const(const cpp_cv& cv) noexcept { - return cv == cpp_cv_const || cv == cpp_cv_const_volatile; + return (cv & cpp_cv_const) != 0; } /// \returns `true` if the qualifier contains `volatile`. -inline bool is_volatile(cpp_cv cv) noexcept +inline bool is_volatile(const cpp_cv& cv) noexcept +{ + return (cv & cpp_cv_volatile) != 0; +} + +/// \returns `true` if the qualifier contains `restrict`. +inline bool is_restrict(const cpp_cv& cv) noexcept { - return cv == cpp_cv_volatile || cv == cpp_cv_const_volatile; + return (cv & cpp_cv_restrict) != 0; +} + +/// \returns `true` if the qualifier contains `atomic`. +inline bool is_atomic(const cpp_cv& cv) noexcept +{ + return (cv & cpp_cv_atomic) != 0; } /// A [cppast::cpp_cv]() qualified [cppast::cpp_type](). @@ -355,6 +377,12 @@ 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; +/// \returns The type without top-level restrict qualifiers. +const cpp_type& remove_restrict(const cpp_type& type) noexcept; + +/// \returns The type without top-level atomic qualifiers. +const cpp_type& remove_atomic(const cpp_type& type) noexcept; + /// A pointer to a [cppast::cpp_type](). class cpp_pointer_type final : public cpp_type { diff --git a/include/cppast/cpp_type_alias.hpp b/include/cppast/cpp_type_alias.hpp index 637e9b46..6c2a8e01 100644 --- a/include/cppast/cpp_type_alias.hpp +++ b/include/cppast/cpp_type_alias.hpp @@ -18,11 +18,13 @@ class cpp_type_alias final : public cpp_entity /// \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); + std::string name, std::unique_ptr type, + bool use_c_style = false); /// \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); + static std::unique_ptr build(std::string name, std::unique_ptr type, + bool use_c_style = false); /// \returns A reference to the aliased [cppast::cpp_type](). const cpp_type& underlying_type() const noexcept @@ -30,14 +32,21 @@ class cpp_type_alias final : public cpp_entity return *type_; } + /// \returns Whether to generate C-style typedefs instead of C++ using statements + bool use_c_style() const noexcept + { + return use_c_style_; + } + private: - cpp_type_alias(std::string name, std::unique_ptr type) - : cpp_entity(std::move(name)), type_(std::move(type)) + cpp_type_alias(std::string name, std::unique_ptr type, bool use_c_style) + : cpp_entity(std::move(name)), type_(std::move(type)), use_c_style_(use_c_style) {} cpp_entity_kind do_get_entity_kind() const noexcept override; std::unique_ptr type_; + bool use_c_style_; }; } // namespace cppast diff --git a/include/cppast/cppast_fwd.hpp b/include/cppast/cppast_fwd.hpp index 2f777766..556bf803 100644 --- a/include/cppast/cppast_fwd.hpp +++ b/include/cppast/cppast_fwd.hpp @@ -102,7 +102,7 @@ enum class visit_filter; enum cpp_access_specifier_kind : int; enum cpp_builtin_type_kind : int; -enum cpp_cv : int; +enum cpp_cv_flags : int; enum cpp_function_body_kind : int; enum cpp_reference : int; enum cpp_storage_class_specifiers : int; diff --git a/include/cppast/libclang_parser.hpp b/include/cppast/libclang_parser.hpp index c96ae18b..b445855c 100644 --- a/include/cppast/libclang_parser.hpp +++ b/include/cppast/libclang_parser.hpp @@ -166,10 +166,13 @@ class libclang_compile_config final : public compile_config return "libclang"; } + bool do_use_c() const noexcept override; + std::string clang_binary_; bool write_preprocessed_ : 1; bool fast_preprocessing_ : 1; bool remove_comments_in_macro_ : 1; + bool use_c_ : 1; friend detail::libclang_compile_config_access; }; diff --git a/src/code_generator.cpp b/src/code_generator.cpp index 20f264c9..92d5ed62 100644 --- a/src/code_generator.cpp +++ b/src/code_generator.cpp @@ -241,13 +241,25 @@ bool generate_type_alias(code_generator& generator, const cpp_type_alias& alias, 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); + if (alias.use_c_style()) + { + output << keyword("typedef") << whitespace; + if (output.options() & code_generator::exclude_target) + output.excluded(alias); + else + detail::write_type(output, alias.underlying_type(), alias.name()); + output << punctuation(";") << newl; + } else - detail::write_type(output, alias.underlying_type(), ""); - output << punctuation(";") << newl; + { + 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); } @@ -471,6 +483,8 @@ void write_storage_class(code_generator::output& output, cpp_storage_class_speci output << keyword("extern") << whitespace; if (is_thread_local(storage)) output << keyword("thread_local") << whitespace; + if (is_register(storage)) + output << keyword("register") << whitespace; if (is_constexpr) output << keyword("constexpr") << whitespace; else if (is_consteval) @@ -658,22 +672,28 @@ void write_suffix_virtual(code_generator::output& output, const cpp_virtual& vir bool write_cv_ref(code_generator::output& output, const cpp_member_function_base& base) { auto need_ws = false; - switch (base.cv_qualifier()) + auto cv = base.cv_qualifier(); + + std::vector qualifiers; + if (is_const(cv)) + qualifiers.push_back("const"); + if (is_volatile(cv)) + qualifiers.push_back("volatile"); + if (is_atomic(cv)) + qualifiers.push_back("_Atomic"); + if (is_restrict(cv)) + qualifiers.push_back("restrict"); + + bool first = true; + for (auto& q : qualifiers) { - 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"); + if (first) + output << operator_ws; + else + output << whitespace; + output << keyword(std::move(q)); + first = false; need_ws = true; - break; } switch (base.ref_qualifier()) diff --git a/src/cpp_member_function.cpp b/src/cpp_member_function.cpp index 346a7b23..0c2428de 100644 --- a/src/cpp_member_function.cpp +++ b/src/cpp_member_function.cpp @@ -15,6 +15,10 @@ std::string cpp_member_function_base::do_get_signature() const result += " const"; if (is_volatile(cv_qualifier())) result += " volatile"; + if (is_atomic(cv_qualifier())) + result += " _Atomic"; + if (is_restrict(cv_qualifier())) + result += " restrict"; if (ref_qualifier() == cpp_ref_lvalue) result += " &"; diff --git a/src/cpp_type.cpp b/src/cpp_type.cpp index f11e72e7..4b3616aa 100644 --- a/src/cpp_type.cpp +++ b/src/cpp_type.cpp @@ -108,6 +108,28 @@ const cpp_type& cppast::remove_volatile(const cpp_type& type) noexcept return type; } +const cpp_type& cppast::remove_restrict(const cpp_type& type) noexcept +{ + if (type.kind() == cpp_type_kind::cv_qualified_t) + { + auto& cv = static_cast(type); + if (is_restrict(cv.cv_qualifier())) + return cv.type(); + } + return type; +} + +const cpp_type& cppast::remove_atomic(const cpp_type& type) noexcept +{ + if (type.kind() == cpp_type_kind::cv_qualified_t) + { + auto& cv = static_cast(type); + if (is_atomic(cv.cv_qualifier())) + return cv.type(); + } + return type; +} + bool detail::cpp_type_ref_predicate::operator()(const cpp_entity& e) { switch (e.kind()) @@ -283,6 +305,10 @@ void write_cv_qualified_prefix(code_generator::output& output, const cpp_cv_qual output << whitespace << keyword("const"); if (is_volatile(type.cv_qualifier())) output << whitespace << keyword("volatile"); + if (is_atomic(type.cv_qualifier())) + output << whitespace << keyword("_Atomic"); + if (is_restrict(type.cv_qualifier())) + output << whitespace << keyword("restrict"); } void write_cv_qualified_suffix(code_generator::output& output, const cpp_cv_qualified_type& type) @@ -435,16 +461,28 @@ void write_member_function_suffix(code_generator::output& output, output << bracket_ws << punctuation(")"); write_parameters(output, type); - auto cv = cpp_cv_none; - auto ref = cpp_ref_none; + cpp_cv 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"); + std::vector qualifiers; + if (is_const(cv)) + qualifiers.push_back("const"); + if (is_volatile(cv)) + qualifiers.push_back("volatile"); + if (is_atomic(cv)) + qualifiers.push_back("_Atomic"); + if (is_restrict(cv)) + qualifiers.push_back("restrict"); + + bool first = true; + for (auto& q : qualifiers) + { + if (!first) + output << whitespace; + output << keyword(std::move(q)); + first = false; + } if (ref == cpp_ref_lvalue) output << operator_ws << punctuation("&") << operator_ws; diff --git a/src/cpp_type_alias.cpp b/src/cpp_type_alias.cpp index fd45d852..86727b68 100644 --- a/src/cpp_type_alias.cpp +++ b/src/cpp_type_alias.cpp @@ -14,17 +14,19 @@ cpp_entity_kind cpp_type_alias::kind() noexcept std::unique_ptr cpp_type_alias::build(const cpp_entity_index& idx, cpp_entity_id id, std::string name, - std::unique_ptr type) + std::unique_ptr type, + bool use_c_style) { - auto result = build(std::move(name), std::move(type)); + auto result = build(std::move(name), std::move(type), use_c_style); idx.register_forward_declaration(std::move(id), type_safe::cref(*result)); // not a definition return result; } std::unique_ptr cpp_type_alias::build(std::string name, - std::unique_ptr type) + std::unique_ptr type, + bool use_c_style) { - return std::unique_ptr(new cpp_type_alias(std::move(name), std::move(type))); + return std::unique_ptr(new cpp_type_alias(std::move(name), std::move(type), use_c_style)); } cpp_entity_kind cpp_type_alias::do_get_entity_kind() const noexcept diff --git a/src/libclang/function_parser.cpp b/src/libclang/function_parser.cpp index 5cff0ccd..fdc99ff0 100644 --- a/src/libclang/function_parser.cpp +++ b/src/libclang/function_parser.cpp @@ -327,22 +327,24 @@ struct suffix_info cpp_cv parse_cv(detail::cxtoken_stream& stream) { - 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")) + bool found_qualifier = true; + cpp_cv qualifier = cpp_cv_none; + + while (found_qualifier) { if (detail::skip_if(stream, "const")) - return cpp_cv_const_volatile; + qualifier |= cpp_cv_const; + else if (detail::skip_if(stream, "volatile")) + qualifier |= cpp_cv_volatile; + else if (detail::skip_if(stream, "restrict")) + qualifier |= cpp_cv_restrict; + else if (detail::skip_if(stream, "_Atomic")) + qualifier |= cpp_cv_atomic; else - return cpp_cv_volatile; + found_qualifier = false; } - else - return cpp_cv_none; + + return qualifier; } cpp_reference parse_ref(detail::cxtoken_stream& stream) diff --git a/src/libclang/libclang_parser.cpp b/src/libclang/libclang_parser.cpp index b45290bd..eac97178 100644 --- a/src/libclang/libclang_parser.cpp +++ b/src/libclang/libclang_parser.cpp @@ -80,7 +80,7 @@ namespace libclang_compile_config::libclang_compile_config() : compile_config({}), write_preprocessed_(false), fast_preprocessing_(false), - remove_comments_in_macro_(false) + remove_comments_in_macro_(false), use_c_(false) { // set given clang binary set_clang_binary(CPPAST_CLANG_BINARY); @@ -195,7 +195,22 @@ void parse_flags(CXCompileCommand cmd, Func callback) else if (!last_flag.empty()) { // we have flags + args - callback(std::move(last_flag), str.std_str()); + std::string args; + if (auto ptr = find_flag_arg_sep(last_flag)) + { + // If last flag contains an '=' then this is a positional argument, not its value + auto pos = std::size_t(ptr - last_flag.c_str()); + ++ptr; + while (*ptr) + args += *ptr++; + last_flag.erase(pos); + + callback(std::move(last_flag), std::move(args)); + } + else + { + callback(std::move(last_flag), str.std_str()); + } last_flag.clear(); } // else skip argument @@ -217,6 +232,14 @@ libclang_compile_config::libclang_compile_config(const libclang_compilation_data for (auto i = 0u; i != size; ++i) { auto cmd = clang_CompileCommands_getCommand(commands.get(), i); + + // If ++ exists within the compiler name (e.g. clang++, g++, etc), use C++ + std::string exe(clang_getCString(clang_CompileCommand_getArg(cmd, 0))); + if (exe.find("++", 0) != std::string::npos) + use_c_ = false; + else + use_c_ = true; + auto dir = detail::cxstring(clang_CompileCommand_getDirectory(cmd)); parse_flags(cmd, [&](std::string flag, std::string args) { if (flag == "-I") @@ -234,11 +257,30 @@ libclang_compile_config::libclang_compile_config(const libclang_compilation_data add_flag(std::move(flag)); } else if (flag == "-std") + { // standard + try + { + // detect whether the language standard is a C standard + use_c_ = is_c_standard(to_standard(args)); + } + catch (const std::invalid_argument&) + { + use_c_ = false; + } add_flag(std::move(flag) + "=" + std::move(args)); + } else if (flag == "-f") // other options add_flag(std::move(flag) + std::move(args)); + else if (flag == "-x") + { + // language + if (args == "c") + use_c_ = true; + else + use_c_ = false; + } }); } } @@ -261,8 +303,9 @@ bool is_valid_binary(const std::string& binary) void add_default_include_dirs(libclang_compile_config& config) { std::string verbose_output; + std::string language = config.use_c() ? "-xc" : "-xc++"; tpl::Process process( - detail::libclang_compile_config_access::clang_binary(config) + " -x c++ -v -", "", + detail::libclang_compile_config_access::clang_binary(config) + " " + language + " -v -", "", [](const char*, std::size_t) {}, [&](const char* str, std::size_t n) { verbose_output.append(str, n); }, true); process.write("", 1); @@ -321,19 +364,19 @@ bool libclang_compile_config::set_clang_binary(std::string binary) } else { + static const char* versions[] = {"4.0", "5.0", "6.0", "7", "8", "9", "10", "11", "12", "13", "14"}; // first search in current directory, then in PATH - static const char* paths[] - = {"./clang++", "clang++", "./clang++-4.0", "clang++-4.0", "./clang++-5.0", - "clang++-5.0", "./clang++-6.0", "clang++-6.0", "./clang-7", "clang-7", - "./clang-8", "clang-8", "./clang-9", "clang-9", "./clang-10", - "clang-10", "./clang-11", "clang-11"}; - for (auto& p : paths) - if (is_valid_binary(p)) + for (auto& v : versions) + { + // use clang rather than clang++ and use -x to choose C or C++ + std::string path = std::string("clang-") + v; + if (is_valid_binary(path)) { - clang_binary_ = p; + clang_binary_ = std::move(path); add_default_include_dirs(*this); return false; } + } throw std::invalid_argument("unable to find clang binary '" + binary + "'"); } @@ -406,6 +449,55 @@ void libclang_compile_config::do_set_flags(cpp_standard standard, compile_flags } else throw std::invalid_argument("c++20 is not yet supported for current version of clang"); + case cpp_standard::c_89: + if (flags & compile_flag::gnu_extensions) + add_flag("-std=gnu89"); + else + add_flag("-std=c89"); + break; + case cpp_standard::c_99: + if (flags & compile_flag::gnu_extensions) + add_flag("-std=gnu99"); + else + add_flag("-std=c99"); + break; + case cpp_standard::c_11: + if (flags & compile_flag::gnu_extensions) + add_flag("-std=gnu11"); + else + add_flag("-std=c11"); + break; + case cpp_standard::c_17: + if (libclang_parser::libclang_minor_version() >= 45) + { // Corresponds to Clang version 6 + if (flags & compile_flag::gnu_extensions) + add_flag("-std=gnu17"); + else + add_flag("-std=c17"); + break; + } + else + throw std::invalid_argument("c17 is not yet supported for current version of clang"); + case cpp_standard::c_2x: + if (libclang_parser::libclang_minor_version() >= 59) + { // Corresponds to Clang version 9 + if (flags & compile_flag::gnu_extensions) + add_flag("-std=gnu2x"); + else + add_flag("-std=c2x"); + break; + } + else + throw std::invalid_argument("c2x is not yet supported for current version of clang"); + } + + // Add language flag for C or C++ + if (is_c_standard(standard)) { + add_flag("-xc"); + use_c_ = true; + } else { + add_flag("-xc++"); + use_c_ = false; } if (flags & compile_flag::ms_compatibility) @@ -443,6 +535,11 @@ void libclang_compile_config::do_remove_macro_definition(std::string name) add_flag("-U" + std::move(name)); } +bool libclang_compile_config::do_use_c() const noexcept +{ + return use_c_; +} + type_safe::optional cppast::find_config_for( const libclang_compilation_database& database, std::string file_name) { @@ -456,7 +553,7 @@ 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"}; + = {".h", ".hpp", ".cpp", ".h++", ".c++", ".hxx", ".cxx", ".hh", ".cc", ".H", ".C", ".c"}; for (auto ext : extensions) { auto name = file_name + ext; @@ -493,8 +590,8 @@ namespace std::vector get_arguments(const libclang_compile_config& config) { std::vector args - // TODO: Why? and Why? - = {"-x", "c++", "-I."}; // force C++ and enable current directory for include search + // TODO: Why? + = {"-I."}; // enable current directory for include search for (auto& flag : detail::libclang_compile_config_access::flags(config)) args.push_back(flag.c_str()); return args; @@ -622,6 +719,7 @@ try type_safe::ref(logger()), type_safe::ref(idx), detail::comment_context(preprocessed.comments), + c.use_c(), false}; detail::visit_tu(tu, path.c_str(), [&](const CXCursor& cur) { if (clang_getCursorKind(cur) == CXCursor_InclusionDirective) diff --git a/src/libclang/parse_functions.cpp b/src/libclang/parse_functions.cpp index 39e9aa92..75b46b2a 100644 --- a/src/libclang/parse_functions.cpp +++ b/src/libclang/parse_functions.cpp @@ -57,9 +57,11 @@ cpp_storage_class_specifiers detail::get_storage_class(const CXCursor& cur) return cpp_storage_class_none; case CX_SC_Auto: - case CX_SC_Register: return cpp_storage_class_auto; + case CX_SC_Register: + return cpp_storage_class_register; + case CX_SC_Extern: return cpp_storage_class_extern; case CX_SC_Static: diff --git a/src/libclang/parse_functions.hpp b/src/libclang/parse_functions.hpp index 822b2039..9d5b3c83 100644 --- a/src/libclang/parse_functions.hpp +++ b/src/libclang/parse_functions.hpp @@ -54,6 +54,7 @@ namespace detail type_safe::object_ref logger; type_safe::object_ref idx; comment_context comments; + bool use_c_style; mutable bool error; }; diff --git a/src/libclang/preprocessor.cpp b/src/libclang/preprocessor.cpp index bebfc343..e150bda9 100644 --- a/src/libclang/preprocessor.cpp +++ b/src/libclang/preprocessor.cpp @@ -165,11 +165,12 @@ std::string diagnostics_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 + // -xc/-xc++: force C or 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"); + std::string language = c.use_c() ? "-xc" : "-xc++"; + auto flags = language + " -I. -E -dM"; flags += diagnostics_flags(); std::string cmd(detail::libclang_compile_config_access::clang_binary(c) + " " + std::move(flags) @@ -189,10 +190,11 @@ std::string get_macro_command(const libclang_compile_config& c, const char* full 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 + // -xc/-xc++: force C or C++ as input language // -E: print preprocessor output // -dD: keep macros - auto flags = std::string("-x c++ -E -dD"); + std::string language = c.use_c() ? "-xc" : "-xc++"; + auto flags = language + " -E -dD"; // -CC: keep comments, even in macro // -C: keep comments, but not in macro diff --git a/src/libclang/type_parser.cpp b/src/libclang/type_parser.cpp index a636b82a..1b49f558 100644 --- a/src/libclang/type_parser.cpp +++ b/src/libclang/type_parser.cpp @@ -134,20 +134,21 @@ std::string get_type_spelling(const CXCursor& cur, const CXType& type) // 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)) + bool found_cv = true; + cpp_cv cv = cpp_cv_none; + + while (found_cv) { if (remove_suffix(spelling, "const", true)) - cv = cpp_cv_const_volatile; + cv |= cpp_cv_const; + else if (remove_suffix(spelling, "volatile", true)) + cv |= cpp_cv_volatile; + else if (remove_suffix(spelling, "restrict", true)) + cv |= cpp_cv_restrict; + else if (remove_suffix(spelling, "_Atomic", true)) + cv |= cpp_cv_atomic; else - cv = cpp_cv_volatile; + found_cv = false; } return cv; @@ -156,66 +157,29 @@ cpp_cv suffix_cv(std::string& spelling) // 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)) + bool found_cv = true; + cpp_cv cv = cpp_cv_none; + + while (found_cv) { if (remove_prefix(spelling, "const", true)) - cv = cpp_cv_const_volatile; + cv |= cpp_cv_const; + else if (remove_prefix(spelling, "volatile", true)) + cv |= cpp_cv_volatile; + else if (remove_prefix(spelling, "restrict", true)) + cv |= cpp_cv_restrict; + else if (remove_prefix(spelling, "_Atomic", true)) + cv |= cpp_cv_atomic; else - cv = cpp_cv_volatile; + found_cv = false; } return cv; } -cpp_cv merge_cv(cpp_cv a, cpp_cv b) +cpp_cv merge_cv(const cpp_cv& a, const cpp_cv& b) { - switch (a) - { - case cpp_cv_none: - return b; - - 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; - - 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; - - case cpp_cv_const_volatile: - return cpp_cv_const_volatile; - } - - DEBUG_UNREACHABLE(detail::assert_handler{}); - return cpp_cv_none; + return combo(a) | b; } std::unique_ptr make_cv_qualified(std::unique_ptr entity, cpp_cv cv) @@ -741,11 +705,11 @@ std::unique_ptr detail::parse_cpp_type_alias(const detail::parse_con std::unique_ptr result; if (!clang_Cursor_isNull(template_cur)) - result = cpp_type_alias::build(name.c_str(), std::move(type)); + result = cpp_type_alias::build(name.c_str(), std::move(type), context.use_c_style); else { result = cpp_type_alias::build(*context.idx, get_entity_id(cur), name.c_str(), - std::move(type)); + std::move(type), context.use_c_style); context.comments.match(*result, cur); } diff --git a/src/libclang/variable_parser.cpp b/src/libclang/variable_parser.cpp index 2bfb1db9..f9172dc5 100644 --- a/src/libclang/variable_parser.cpp +++ b/src/libclang/variable_parser.cpp @@ -62,7 +62,7 @@ std::unique_ptr detail::parse_cpp_variable(const detail::parse_conte // can't appear anywhere else, so good enough detail::cxtokenizer tokenizer(context.tu, context.file, cur); for (auto& token : tokenizer) - if (token.value() == "thread_local") + if (token.value() == "thread_local" || token.value() == "_Thread_local") storage_class = cpp_storage_class_specifiers(storage_class | cpp_storage_class_thread_local); else if (token.value() == "constexpr") diff --git a/test/parser.cpp b/test/parser.cpp index b5dd667e..e7865061 100644 --- a/test/parser.cpp +++ b/test/parser.cpp @@ -27,6 +27,11 @@ TEST_CASE("parse_files") { return "null"; } + + bool do_use_c() const noexcept override + { + return false; + } } config; class null_parser : public parser