From ce218dfb8a94fa412b04a0f3f5ab9e48596939de Mon Sep 17 00:00:00 2001 From: Minh Lu Date: Tue, 16 Feb 2021 15:45:30 -0800 Subject: [PATCH] Add basic C++20 support --- include/cppast/compile_config.hpp | 12 +++++-- src/libclang/libclang_parser.cpp | 53 +++++++++++++++++++++++++------ test/test_parser.hpp | 25 +++++++-------- tool/main.cpp | 8 ++++- 4 files changed, 73 insertions(+), 25 deletions(-) diff --git a/include/cppast/compile_config.hpp b/include/cppast/compile_config.hpp index 646af1c..ab4b960 100644 --- a/include/cppast/compile_config.hpp +++ b/include/cppast/compile_config.hpp @@ -22,8 +22,10 @@ enum class cpp_standard cpp_03, cpp_11, cpp_14, - - cpp_1z, //< Upcoming C++17 (experimental). + cpp_1z, + cpp_17, + cpp_2a, + cpp_20, cpp_latest = cpp_standard::cpp_14, //< The latest supported C++ standard. }; @@ -44,6 +46,12 @@ inline const char* to_string(cpp_standard standard) noexcept return "c++14"; case cpp_standard::cpp_1z: return "c++1z"; + case cpp_standard::cpp_17: + return "c++17"; + case cpp_standard::cpp_2a: + return "c++2a"; + case cpp_standard::cpp_20: + return "c++20"; } DEBUG_UNREACHABLE(detail::assert_handler{}); diff --git a/src/libclang/libclang_parser.cpp b/src/libclang/libclang_parser.cpp index f8ed00b..3d252f5 100644 --- a/src/libclang/libclang_parser.cpp +++ b/src/libclang/libclang_parser.cpp @@ -248,8 +248,8 @@ namespace { bool is_valid_binary(const std::string& binary) { - tpl::Process process(binary + " -v", "", [](const char*, std::size_t) {}, - [](const char*, std::size_t) {}); + tpl::Process process( + binary + " -v", "", [](const char*, std::size_t) {}, [](const char*, std::size_t) {}); return process.get_exit_status() == 0; } @@ -262,11 +262,10 @@ bool is_valid_binary(const std::string& binary) void add_default_include_dirs(libclang_compile_config& config) { std::string verbose_output; - tpl::Process process(detail::libclang_compile_config_access::clang_binary(config) - + " -x c++ -v -", - "", [](const char*, std::size_t) {}, - [&](const char* str, std::size_t n) { verbose_output.append(str, n); }, - true); + tpl::Process process( + detail::libclang_compile_config_access::clang_binary(config) + " -x c++ -v -", "", + [](const char*, std::size_t) {}, + [&](const char* str, std::size_t n) { verbose_output.append(str, n); }, true); process.write("", 1); process.close_stdin(); process.get_exit_status(); @@ -326,7 +325,9 @@ bool libclang_compile_config::set_clang_binary(std::string binary) // 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++-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)) { @@ -373,6 +374,39 @@ void libclang_compile_config::do_set_flags(cpp_standard standard, compile_flags else add_flag("-std=c++1z"); break; + case cpp_standard::cpp_17: + if (libclang_parser::libclang_minor_version() >= 43) + { // Corresponds to Clang version 5 + if (flags & compile_flag::gnu_extensions) + add_flag("-std=gnu++17"); + else + add_flag("-std=c++17"); + break; + } + else + throw std::invalid_argument("c++17 is not yet supported for current version of clang"); + case cpp_standard::cpp_2a: + if (libclang_parser::libclang_minor_version() >= 59) + { // Corresponds to Clang version 9 + if (flags & compile_flag::gnu_extensions) + add_flag("-std=gnu++2a"); + else + add_flag("-std=c++2a"); + break; + } + else + throw std::invalid_argument("c++2a is not yet supported for current version of clang"); + case cpp_standard::cpp_20: + if (libclang_parser::libclang_minor_version() >= 60) + { // Corresponds to Clang version 10 + if (flags & compile_flag::gnu_extensions) + add_flag("-std=gnu++20"); + else + add_flag("-std=c++20"); + break; + } + else + throw std::invalid_argument("c++20 is not yet supported for current version of clang"); } if (flags & compile_flag::ms_compatibility) @@ -559,7 +593,8 @@ unsigned get_line_no(const CXCursor& cursor) } } // namespace std::unique_ptr libclang_parser::do_parse(const cpp_entity_index& idx, std::string path, - const compile_config& c) const try + const compile_config& c) const +try { DEBUG_ASSERT(std::strcmp(c.name(), "libclang") == 0, detail::precondition_error_handler{}, "config has mismatched type"); diff --git a/test/test_parser.hpp b/test/test_parser.hpp index 5f36a30..661fdfb 100644 --- a/test/test_parser.hpp +++ b/test/test_parser.hpp @@ -23,21 +23,18 @@ inline void write_file(const char* name, const char* code) file << code; } -inline cppast::libclang_compile_config make_test_config() -{ - using namespace cppast; - - libclang_compile_config config; - config.set_flags(cpp_standard::cpp_latest); - return config; -} - inline std::unique_ptr parse_file(const cppast::cpp_entity_index& idx, const char* name, - bool fast_preprocessing = false) + bool fast_preprocessing = false, + cppast::cpp_standard standard + = cppast::cpp_standard::cpp_latest) { using namespace cppast; - static auto config = make_test_config(); + + // Creating a config is expensive, so we remember a default one. + static auto default_config = libclang_compile_config(); + auto config = default_config; + config.set_flags(standard); config.fast_preprocessing(fast_preprocessing); libclang_parser p(default_logger()); @@ -50,10 +47,12 @@ inline std::unique_ptr parse_file(const cppast::cpp_entity_ind inline std::unique_ptr parse(const cppast::cpp_entity_index& idx, const char* name, const char* code, - bool fast_preprocessing = false) + bool fast_preprocessing = false, + cppast::cpp_standard standard + = cppast::cpp_standard::cpp_latest) { write_file(name, code); - return parse_file(idx, name, fast_preprocessing); + return parse_file(idx, name, fast_preprocessing, standard); } class test_generator : public cppast::code_generator diff --git a/tool/main.cpp b/tool/main.cpp index fe3bba6..016acf0 100644 --- a/tool/main.cpp +++ b/tool/main.cpp @@ -203,7 +203,7 @@ int main(int argc, char* argv[]) try cxxopts::value()) ("database_file", "set the file name whose configuration will be used regardless of the current file name", cxxopts::value()) - ("std", "set the C++ standard (c++98, c++03, c++11, c++14, c++1z (experimental))", + ("std", "set the C++ standard (c++98, c++03, c++11, c++14, c++1z (experimental), c++17, c++2a, c++20)", cxxopts::value()->default_value(cppast::to_string(cppast::cpp_standard::cpp_latest))) ("I,include_directory", "add directory to include search path", cxxopts::value>()) @@ -304,6 +304,12 @@ int main(int argc, char* argv[]) try config.set_flags(cppast::cpp_standard::cpp_14, flags); else if (options["std"].as() == "c++1z") config.set_flags(cppast::cpp_standard::cpp_1z, flags); + else if (options["std"].as() == "c++17") + config.set_flags(cppast::cpp_standard::cpp_17, flags); + else if (options["std"].as() == "c++2a") + config.set_flags(cppast::cpp_standard::cpp_2a, flags); + else if (options["std"].as() == "c++20") + config.set_flags(cppast::cpp_standard::cpp_20, flags); else { print_error("invalid value '" + options["std"].as() + "' for std flag");