Add support for C language standards (#159)
This commit is contained in:
parent
e751294b96
commit
7b25525232
5 changed files with 147 additions and 11 deletions
|
|
@ -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,
|
||||
|
|
@ -28,7 +28,14 @@ enum class cpp_standard
|
|||
cpp_20,
|
||||
cpp_2b,
|
||||
|
||||
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,
|
||||
|
|
@ -55,12 +62,50 @@ inline const char* to_string(cpp_standard standard) noexcept
|
|||
return "c++20";
|
||||
case cpp_standard::cpp_2b:
|
||||
return "c++2b";
|
||||
|
||||
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 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:
|
||||
case cpp_standard::cpp_2b:
|
||||
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
|
||||
{
|
||||
|
|
@ -117,6 +162,12 @@ public:
|
|||
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<std::string> def_flags) : flags_(std::move(def_flags)) {}
|
||||
|
||||
|
|
@ -160,6 +211,9 @@ private:
|
|||
/// \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<std::string> flags_;
|
||||
};
|
||||
} // namespace cppast
|
||||
|
|
|
|||
|
|
@ -187,10 +187,13 @@ private:
|
|||
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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ libclang_compile_config::libclang_compile_config() : libclang_compile_config(CPP
|
|||
|
||||
libclang_compile_config::libclang_compile_config(std::string clang_binary)
|
||||
: 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(clang_binary);
|
||||
|
|
@ -226,6 +226,11 @@ cppast::libclang_compile_config::libclang_compile_config(
|
|||
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)));
|
||||
use_c_ = (exe.find("++", 0) == std::string::npos);
|
||||
|
||||
auto dir = detail::cxstring(clang_CompileCommand_getDirectory(cmd));
|
||||
parse_flags(cmd, [&](std::string flag, std::string args) {
|
||||
if (flag == "-I")
|
||||
|
|
@ -243,11 +248,21 @@ cppast::libclang_compile_config::libclang_compile_config(
|
|||
add_flag(std::move(flag));
|
||||
}
|
||||
else if (flag == "-std")
|
||||
// standard
|
||||
{
|
||||
use_c_ = (args.find("++") == std::string::npos);
|
||||
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;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -270,8 +285,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);
|
||||
|
|
@ -424,6 +440,57 @@ void libclang_compile_config::do_set_flags(cpp_standard standard, compile_flags
|
|||
add_flag("-std=c++2a");
|
||||
break;
|
||||
}
|
||||
else
|
||||
throw std::invalid_argument("c++2b 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)
|
||||
|
|
@ -461,6 +528,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<libclang_compile_config> cppast::find_config_for(
|
||||
const libclang_compilation_database& database, std::string file_name)
|
||||
{
|
||||
|
|
@ -474,7 +546,7 @@ type_safe::optional<libclang_compile_config> 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;
|
||||
|
|
@ -511,8 +583,8 @@ namespace
|
|||
std::vector<const char*> get_arguments(const libclang_compile_config& config)
|
||||
{
|
||||
std::vector<const char*> 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;
|
||||
|
|
|
|||
|
|
@ -174,11 +174,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)
|
||||
|
|
@ -198,10 +199,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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue