Skip to content

Add support for C language standards #159

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 55 additions & 1 deletion include/cppast/compile_config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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
{
Expand Down Expand Up @@ -117,6 +162,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<std::string> def_flags) : flags_(std::move(def_flags)) {}

Expand Down Expand Up @@ -160,6 +211,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<std::string> flags_;
};
} // namespace cppast
Expand Down
3 changes: 3 additions & 0 deletions include/cppast/libclang_parser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -187,10 +187,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;
};
Expand Down
84 changes: 78 additions & 6 deletions src/libclang/libclang_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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")
Expand All @@ -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;
}
});
}
}
Expand All @@ -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);
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
{
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
10 changes: 6 additions & 4 deletions src/libclang/preprocessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
Expand Down
5 changes: 5 additions & 0 deletions test/parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down