Skip to content

Commit 7b25525

Browse files
Add support for C language standards (#159)
1 parent e751294 commit 7b25525

File tree

5 files changed

+147
-11
lines changed

5 files changed

+147
-11
lines changed

include/cppast/compile_config.hpp

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
namespace cppast
1717
{
18-
/// The C++ standard that should be used.
18+
/// The C/C++ standard that should be used.
1919
enum class cpp_standard
2020
{
2121
cpp_98,
@@ -28,7 +28,14 @@ enum class cpp_standard
2828
cpp_20,
2929
cpp_2b,
3030

31+
c_89,
32+
c_99,
33+
c_11,
34+
c_17,
35+
c_2x,
36+
3137
cpp_latest = cpp_standard::cpp_14, //< The latest supported C++ standard.
38+
c_latest = cpp_standard::c_17, //< The latest supported C standard.
3239
};
3340

3441
/// \returns A human readable string representing the option,
@@ -55,12 +62,50 @@ inline const char* to_string(cpp_standard standard) noexcept
5562
return "c++20";
5663
case cpp_standard::cpp_2b:
5764
return "c++2b";
65+
66+
case cpp_standard::c_89:
67+
return "c89";
68+
case cpp_standard::c_99:
69+
return "c99";
70+
case cpp_standard::c_11:
71+
return "c11";
72+
case cpp_standard::c_17:
73+
return "c17";
74+
case cpp_standard::c_2x:
75+
return "c2x";
5876
}
5977

6078
DEBUG_UNREACHABLE(detail::assert_handler{});
6179
return "ups";
6280
}
6381

82+
/// \returns whether the language standard is a C standard
83+
inline bool is_c_standard(cpp_standard standard) noexcept
84+
{
85+
switch (standard)
86+
{
87+
case cpp_standard::cpp_98:
88+
case cpp_standard::cpp_03:
89+
case cpp_standard::cpp_11:
90+
case cpp_standard::cpp_14:
91+
case cpp_standard::cpp_1z:
92+
case cpp_standard::cpp_17:
93+
case cpp_standard::cpp_2a:
94+
case cpp_standard::cpp_20:
95+
case cpp_standard::cpp_2b:
96+
return false;
97+
case cpp_standard::c_89:
98+
case cpp_standard::c_99:
99+
case cpp_standard::c_11:
100+
case cpp_standard::c_17:
101+
case cpp_standard::c_2x:
102+
return true;
103+
}
104+
105+
DEBUG_UNREACHABLE(detail::assert_handler{});
106+
return false;
107+
}
108+
64109
/// Other special compilation flags.
65110
enum class compile_flag
66111
{
@@ -117,6 +162,12 @@ class compile_config
117162
return do_get_name();
118163
}
119164

165+
/// \returns Whether to parse files as C rather than C++.
166+
bool use_c() const noexcept
167+
{
168+
return do_use_c();
169+
}
170+
120171
protected:
121172
compile_config(std::vector<std::string> def_flags) : flags_(std::move(def_flags)) {}
122173

@@ -160,6 +211,9 @@ class compile_config
160211
/// \notes This allows detecting mismatches of configurations and parsers.
161212
virtual const char* do_get_name() const noexcept = 0;
162213

214+
/// \returns Whether to parse files as C rather than C++.
215+
virtual bool do_use_c() const noexcept = 0;
216+
163217
std::vector<std::string> flags_;
164218
};
165219
} // namespace cppast

include/cppast/libclang_parser.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,10 +187,13 @@ class libclang_compile_config final : public compile_config
187187
return "libclang";
188188
}
189189

190+
bool do_use_c() const noexcept override;
191+
190192
std::string clang_binary_;
191193
bool write_preprocessed_ : 1;
192194
bool fast_preprocessing_ : 1;
193195
bool remove_comments_in_macro_ : 1;
196+
bool use_c_ : 1;
194197

195198
friend detail::libclang_compile_config_access;
196199
};

src/libclang/libclang_parser.cpp

Lines changed: 78 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ libclang_compile_config::libclang_compile_config() : libclang_compile_config(CPP
8282

8383
libclang_compile_config::libclang_compile_config(std::string clang_binary)
8484
: compile_config({}), write_preprocessed_(false), fast_preprocessing_(false),
85-
remove_comments_in_macro_(false)
85+
remove_comments_in_macro_(false), use_c_(false)
8686
{
8787
// set given clang binary
8888
set_clang_binary(clang_binary);
@@ -226,6 +226,11 @@ cppast::libclang_compile_config::libclang_compile_config(
226226
for (auto i = 0u; i != size; ++i)
227227
{
228228
auto cmd = clang_CompileCommands_getCommand(commands.get(), i);
229+
230+
// If ++ exists within the compiler name (e.g. clang++, g++, etc), use C++
231+
std::string exe(clang_getCString(clang_CompileCommand_getArg(cmd, 0)));
232+
use_c_ = (exe.find("++", 0) == std::string::npos);
233+
229234
auto dir = detail::cxstring(clang_CompileCommand_getDirectory(cmd));
230235
parse_flags(cmd, [&](std::string flag, std::string args) {
231236
if (flag == "-I")
@@ -243,11 +248,21 @@ cppast::libclang_compile_config::libclang_compile_config(
243248
add_flag(std::move(flag));
244249
}
245250
else if (flag == "-std")
246-
// standard
251+
{
252+
use_c_ = (args.find("++") == std::string::npos);
247253
add_flag(std::move(flag) + "=" + std::move(args));
254+
}
248255
else if (flag == "-f")
249256
// other options
250257
add_flag(std::move(flag) + std::move(args));
258+
else if (flag == "-x")
259+
{
260+
// language
261+
if (args == "c")
262+
use_c_ = true;
263+
else
264+
use_c_ = false;
265+
}
251266
});
252267
}
253268
}
@@ -270,8 +285,9 @@ bool is_valid_binary(const std::string& binary)
270285
void add_default_include_dirs(libclang_compile_config& config)
271286
{
272287
std::string verbose_output;
288+
std::string language = config.use_c() ? "-xc" : "-xc++";
273289
tpl::Process process(
274-
detail::libclang_compile_config_access::clang_binary(config) + " -x c++ -v -", "",
290+
detail::libclang_compile_config_access::clang_binary(config) + " " + language + " -v -", "",
275291
[](const char*, std::size_t) {},
276292
[&](const char* str, std::size_t n) { verbose_output.append(str, n); }, true);
277293
process.write("", 1);
@@ -424,6 +440,57 @@ void libclang_compile_config::do_set_flags(cpp_standard standard, compile_flags
424440
add_flag("-std=c++2a");
425441
break;
426442
}
443+
else
444+
throw std::invalid_argument("c++2b is not yet supported for current version of clang");
445+
case cpp_standard::c_89:
446+
if (flags & compile_flag::gnu_extensions)
447+
add_flag("-std=gnu89");
448+
else
449+
add_flag("-std=c89");
450+
break;
451+
case cpp_standard::c_99:
452+
if (flags & compile_flag::gnu_extensions)
453+
add_flag("-std=gnu99");
454+
else
455+
add_flag("-std=c99");
456+
break;
457+
case cpp_standard::c_11:
458+
if (flags & compile_flag::gnu_extensions)
459+
add_flag("-std=gnu11");
460+
else
461+
add_flag("-std=c11");
462+
break;
463+
case cpp_standard::c_17:
464+
if (libclang_parser::libclang_minor_version() >= 45)
465+
{ // Corresponds to Clang version 6
466+
if (flags & compile_flag::gnu_extensions)
467+
add_flag("-std=gnu17");
468+
else
469+
add_flag("-std=c17");
470+
break;
471+
}
472+
else
473+
throw std::invalid_argument("c17 is not yet supported for current version of clang");
474+
case cpp_standard::c_2x:
475+
if (libclang_parser::libclang_minor_version() >= 59)
476+
{ // Corresponds to Clang version 9
477+
if (flags & compile_flag::gnu_extensions)
478+
add_flag("-std=gnu2x");
479+
else
480+
add_flag("-std=c2x");
481+
break;
482+
}
483+
else
484+
throw std::invalid_argument("c2x is not yet supported for current version of clang");
485+
}
486+
487+
// Add language flag for C or C++
488+
if (is_c_standard(standard)) {
489+
add_flag("-xc");
490+
use_c_ = true;
491+
} else {
492+
add_flag("-xc++");
493+
use_c_ = false;
427494
}
428495

429496
if (flags & compile_flag::ms_compatibility)
@@ -461,6 +528,11 @@ void libclang_compile_config::do_remove_macro_definition(std::string name)
461528
add_flag("-U" + std::move(name));
462529
}
463530

531+
bool libclang_compile_config::do_use_c() const noexcept
532+
{
533+
return use_c_;
534+
}
535+
464536
type_safe::optional<libclang_compile_config> cppast::find_config_for(
465537
const libclang_compilation_database& database, std::string file_name)
466538
{
@@ -474,7 +546,7 @@ type_safe::optional<libclang_compile_config> cppast::find_config_for(
474546
if (database.has_config(file_name))
475547
return libclang_compile_config(database, std::move(file_name));
476548
static const char* extensions[]
477-
= {".h", ".hpp", ".cpp", ".h++", ".c++", ".hxx", ".cxx", ".hh", ".cc", ".H", ".C"};
549+
= {".h", ".hpp", ".cpp", ".h++", ".c++", ".hxx", ".cxx", ".hh", ".cc", ".H", ".C", ".c"};
478550
for (auto ext : extensions)
479551
{
480552
auto name = file_name + ext;
@@ -511,8 +583,8 @@ namespace
511583
std::vector<const char*> get_arguments(const libclang_compile_config& config)
512584
{
513585
std::vector<const char*> args
514-
// TODO: Why? and Why?
515-
= {"-x", "c++", "-I."}; // force C++ and enable current directory for include search
586+
// TODO: Why?
587+
= {"-I."}; // enable current directory for include search
516588
for (auto& flag : detail::libclang_compile_config_access::flags(config))
517589
args.push_back(flag.c_str());
518590
return args;

src/libclang/preprocessor.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -174,11 +174,12 @@ std::string diagnostics_flags()
174174
// get the command that returns all macros defined in the TU
175175
std::string get_macro_command(const libclang_compile_config& c, const char* full_path)
176176
{
177-
// -x c++: force C++ as input language
177+
// -xc/-xc++: force C or C++ as input language
178178
// -I.: add current working directory to include search path
179179
// -E: print preprocessor output
180180
// -dM: print macro definitions instead of preprocessed file
181-
auto flags = std::string("-x c++ -I. -E -dM");
181+
std::string language = c.use_c() ? "-xc" : "-xc++";
182+
auto flags = language + " -I. -E -dM";
182183
flags += diagnostics_flags();
183184

184185
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
198199
std::string get_preprocess_command(const libclang_compile_config& c, const char* full_path,
199200
const char* macro_file_path)
200201
{
201-
// -x c++: force C++ as input language
202+
// -xc/-xc++: force C or C++ as input language
202203
// -E: print preprocessor output
203204
// -dD: keep macros
204-
auto flags = std::string("-x c++ -E -dD");
205+
std::string language = c.use_c() ? "-xc" : "-xc++";
206+
auto flags = language + " -E -dD";
205207

206208
// -CC: keep comments, even in macro
207209
// -C: keep comments, but not in macro

test/parser.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ TEST_CASE("parse_files")
2727
{
2828
return "null";
2929
}
30+
31+
bool do_use_c() const noexcept override
32+
{
33+
return false;
34+
}
3035
} config;
3136

3237
class null_parser : public parser

0 commit comments

Comments
 (0)