Add basic C++20 support

This commit is contained in:
Minh Lu 2021-02-16 15:45:30 -08:00 committed by Jonathan Müller
commit ce218dfb8a
4 changed files with 73 additions and 25 deletions

View file

@ -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{});

View file

@ -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<cpp_file> 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");

View file

@ -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<cppast::cpp_file> 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<cppast::cpp_file> parse_file(const cppast::cpp_entity_ind
inline std::unique_ptr<cppast::cpp_file> 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

View file

@ -203,7 +203,7 @@ int main(int argc, char* argv[]) try
cxxopts::value<std::string>())
("database_file", "set the file name whose configuration will be used regardless of the current file name",
cxxopts::value<std::string>())
("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<std::string>()->default_value(cppast::to_string(cppast::cpp_standard::cpp_latest)))
("I,include_directory", "add directory to include search path",
cxxopts::value<std::vector<std::string>>())
@ -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<std::string>() == "c++1z")
config.set_flags(cppast::cpp_standard::cpp_1z, flags);
else if (options["std"].as<std::string>() == "c++17")
config.set_flags(cppast::cpp_standard::cpp_17, flags);
else if (options["std"].as<std::string>() == "c++2a")
config.set_flags(cppast::cpp_standard::cpp_2a, flags);
else if (options["std"].as<std::string>() == "c++20")
config.set_flags(cppast::cpp_standard::cpp_20, flags);
else
{
print_error("invalid value '" + options["std"].as<std::string>() + "' for std flag");