Fix external macro use when fast_preprocessing=false
Forget to add standard library includes... Fixes #39.
This commit is contained in:
parent
d2dc8907e2
commit
cc5127979b
3 changed files with 226 additions and 191 deletions
|
|
@ -185,6 +185,7 @@ namespace
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the command that preprocess a translation unit given the macros
|
// get the command that preprocess a translation unit given the macros
|
||||||
|
// macro_file_path == nullptr <=> don't do fast preprocessing
|
||||||
std::string get_preprocess_command(const libclang_compile_config& c, const char* full_path,
|
std::string get_preprocess_command(const libclang_compile_config& c, const char* full_path,
|
||||||
const char* macro_file_path)
|
const char* macro_file_path)
|
||||||
{
|
{
|
||||||
|
|
@ -192,11 +193,16 @@ namespace
|
||||||
// -E: print preprocessor output
|
// -E: print preprocessor output
|
||||||
// -CC: keep comments, even in macro
|
// -CC: keep comments, even in macro
|
||||||
// -dD: keep macros
|
// -dD: keep macros
|
||||||
|
auto flags = std::string("-x c++ -E -CC -dD");
|
||||||
|
|
||||||
|
if (macro_file_path)
|
||||||
// -no*: disable default include search paths
|
// -no*: disable default include search paths
|
||||||
auto flags = std::string("-x c++ -E -CC -dD -nostdinc -nostdinc++");
|
flags += " -nostdinc -nostdinc++";
|
||||||
|
|
||||||
if (detail::libclang_compile_config_access::clang_version(c) >= 40000)
|
if (detail::libclang_compile_config_access::clang_version(c) >= 40000)
|
||||||
// -Xclang -dI: print include directives as well (clang >= 4.0.0)
|
// -Xclang -dI: print include directives as well (clang >= 4.0.0)
|
||||||
flags += " -Xclang -dI";
|
flags += " -Xclang -dI";
|
||||||
|
|
||||||
flags += diagnostics_flags();
|
flags += diagnostics_flags();
|
||||||
|
|
||||||
if (macro_file_path)
|
if (macro_file_path)
|
||||||
|
|
@ -209,13 +215,14 @@ namespace
|
||||||
std::string cmd(detail::libclang_compile_config_access::clang_binary(c) + " "
|
std::string cmd(detail::libclang_compile_config_access::clang_binary(c) + " "
|
||||||
+ std::move(flags) + " ");
|
+ std::move(flags) + " ");
|
||||||
|
|
||||||
// other flags, as long as they don't add include directories (if we're doing the single file optimization)
|
// other flags
|
||||||
for (const auto& flag : detail::libclang_compile_config_access::flags(c))
|
for (const auto& flag : detail::libclang_compile_config_access::flags(c))
|
||||||
{
|
{
|
||||||
DEBUG_ASSERT(flag.size() > 2u && flag[0] == '-', detail::assert_handler{},
|
DEBUG_ASSERT(flag.size() > 2u && flag[0] == '-', detail::assert_handler{},
|
||||||
"that's an odd flag");
|
"that's an odd flag");
|
||||||
if (macro_file_path && (flag[0] != '-' || flag[1] != 'I'))
|
if (!macro_file_path || flag[1] != 'I')
|
||||||
{
|
{
|
||||||
|
// only add this flag if it is not an include or we're not doing fast preprocessing
|
||||||
cmd += flag;
|
cmd += flag;
|
||||||
cmd += ' ';
|
cmd += ' ';
|
||||||
}
|
}
|
||||||
|
|
@ -431,6 +438,10 @@ namespace
|
||||||
clang_preprocess_result clang_preprocess(const libclang_compile_config& c,
|
clang_preprocess_result clang_preprocess(const libclang_compile_config& c,
|
||||||
const char* full_path, const diagnostic_logger& logger)
|
const char* full_path, const diagnostic_logger& logger)
|
||||||
{
|
{
|
||||||
|
// if we're fast preprocessing we only preprocess the main file, not includes
|
||||||
|
// this is done by disabling all include search paths when doing the preprocessing
|
||||||
|
// to allow macros a separate preprocessing with the -dM flag is done that extracts all macros
|
||||||
|
// they are then manually defined before
|
||||||
auto fast_preprocessing = detail::libclang_compile_config_access::fast_preprocessing(c);
|
auto fast_preprocessing = detail::libclang_compile_config_access::fast_preprocessing(c);
|
||||||
|
|
||||||
auto macro_file = fast_preprocessing ? write_macro_file(c, full_path, logger) : "";
|
auto macro_file = fast_preprocessing ? write_macro_file(c, full_path, logger) : "";
|
||||||
|
|
@ -706,7 +717,7 @@ namespace
|
||||||
if (starts_with(p, "*/"))
|
if (starts_with(p, "*/"))
|
||||||
// empty comment
|
// empty comment
|
||||||
p.skip(2u);
|
p.skip(2u);
|
||||||
else if (starts_with(p, "*") || starts_with(p, "!"))
|
else if (p.write_enabled() && (starts_with(p, "*") || starts_with(p, "!")))
|
||||||
{
|
{
|
||||||
// doc comment
|
// doc comment
|
||||||
p.skip();
|
p.skip();
|
||||||
|
|
@ -771,14 +782,14 @@ namespace
|
||||||
return false;
|
return false;
|
||||||
p.skip(2u);
|
p.skip(2u);
|
||||||
|
|
||||||
if (starts_with(p, "/") || starts_with(p, "!"))
|
if (p.write_enabled() && (starts_with(p, "/") || starts_with(p, "!")))
|
||||||
{
|
{
|
||||||
// C++ style doc comment
|
// C++ style doc comment
|
||||||
p.skip();
|
p.skip();
|
||||||
auto comment = parse_cpp_doc_comment(p, false);
|
auto comment = parse_cpp_doc_comment(p, false);
|
||||||
merge_or_add(output, std::move(comment));
|
merge_or_add(output, std::move(comment));
|
||||||
}
|
}
|
||||||
else if (starts_with(p, "<"))
|
else if (p.write_enabled() && starts_with(p, "<"))
|
||||||
{
|
{
|
||||||
// end of line doc comment
|
// end of line doc comment
|
||||||
p.skip();
|
p.skip();
|
||||||
|
|
@ -903,6 +914,9 @@ namespace
|
||||||
DEBUG_ASSERT(starts_with(p, "\n"), detail::assert_handler{});
|
DEBUG_ASSERT(starts_with(p, "\n"), detail::assert_handler{});
|
||||||
// don't skip newline
|
// don't skip newline
|
||||||
|
|
||||||
|
if (!p.write_enabled())
|
||||||
|
return type_safe::nullopt;
|
||||||
|
|
||||||
if (filename.size() > 2u && filename[0] == '.'
|
if (filename.size() > 2u && filename[0] == '.'
|
||||||
&& (filename[1] == '/' || filename[1] == '\\'))
|
&& (filename[1] == '/' || filename[1] == '\\'))
|
||||||
filename = filename.substr(2);
|
filename = filename.substr(2);
|
||||||
|
|
@ -1083,7 +1097,10 @@ detail::preprocessor_output detail::preprocess(const libclang_compile_config& co
|
||||||
else if (lm.value().flag == linemarker::enter_old)
|
else if (lm.value().flag == linemarker::enter_old)
|
||||||
{
|
{
|
||||||
if (lm.value().file == path)
|
if (lm.value().file == path)
|
||||||
|
{
|
||||||
p.enable_write();
|
p.enable_write();
|
||||||
|
p.set_line(lm.value().line);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (lm.value().flag == linemarker::line_directive && p.write_enabled())
|
else if (lm.value().flag == linemarker::line_directive && p.write_enabled())
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -136,185 +136,3 @@ b
|
||||||
});
|
});
|
||||||
REQUIRE(count == 1u);
|
REQUIRE(count == 1u);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("preprocessor line numbers")
|
|
||||||
{
|
|
||||||
auto code = R"(/// 1
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
/// 5
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
int var;
|
|
||||||
|
|
||||||
/// 11
|
|
||||||
|
|
||||||
#define foo \
|
|
||||||
\
|
|
||||||
\
|
|
||||||
int \
|
|
||||||
main()
|
|
||||||
|
|
||||||
/// 19
|
|
||||||
|
|
||||||
foo {}
|
|
||||||
|
|
||||||
/// 23
|
|
||||||
|
|
||||||
/* C comment
|
|
||||||
spanning
|
|
||||||
multiple
|
|
||||||
lines
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
/// 37
|
|
||||||
)";
|
|
||||||
|
|
||||||
auto file = parse({}, "preprocessor_line_numbers.cpp", code);
|
|
||||||
for (auto& comment : file->unmatched_comments())
|
|
||||||
{
|
|
||||||
if (comment.content[0] != '\n')
|
|
||||||
REQUIRE(comment.line == std::stoi(comment.content));
|
|
||||||
}
|
|
||||||
REQUIRE((file->unmatched_comments().size() == 6u + 1u));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("comment content")
|
|
||||||
{
|
|
||||||
auto code = R"(
|
|
||||||
/// simple comment
|
|
||||||
|
|
||||||
///no space
|
|
||||||
|
|
||||||
/// multi
|
|
||||||
/// line
|
|
||||||
/// comment
|
|
||||||
|
|
||||||
/** C comment */
|
|
||||||
/**C comment no space*/
|
|
||||||
|
|
||||||
/** Multiline
|
|
||||||
C
|
|
||||||
comment
|
|
||||||
with indent */
|
|
||||||
|
|
||||||
/** Multiline
|
|
||||||
C
|
|
||||||
comment
|
|
||||||
with
|
|
||||||
indent */
|
|
||||||
|
|
||||||
/** Multiline
|
|
||||||
* C
|
|
||||||
* comment
|
|
||||||
* with
|
|
||||||
* indent
|
|
||||||
* star */
|
|
||||||
)";
|
|
||||||
|
|
||||||
auto file = parse({}, "comment-content.cpp", code);
|
|
||||||
auto comments = file->unmatched_comments();
|
|
||||||
REQUIRE((comments.size() == 8u));
|
|
||||||
|
|
||||||
REQUIRE(comments[0u].content == "simple comment");
|
|
||||||
REQUIRE(comments[1u].content == "no space");
|
|
||||||
REQUIRE(comments[2u].content == "multi\nline\ncomment");
|
|
||||||
REQUIRE(comments[3u].content == "C comment");
|
|
||||||
REQUIRE(comments[4u].content == "C comment no space");
|
|
||||||
REQUIRE(comments[5u].content == "Multiline\nC\n comment\nwith indent");
|
|
||||||
REQUIRE(comments[6u].content == "Multiline\nC\n comment\n with\n indent");
|
|
||||||
REQUIRE(comments[7u].content == "Multiline\nC\ncomment\nwith\nindent\nstar");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("comment matching")
|
|
||||||
{
|
|
||||||
auto code = R"(
|
|
||||||
/// u
|
|
||||||
|
|
||||||
/// a
|
|
||||||
/// a
|
|
||||||
struct a {};
|
|
||||||
|
|
||||||
/// u
|
|
||||||
/** b
|
|
||||||
* b */
|
|
||||||
void b(int, float)
|
|
||||||
{
|
|
||||||
auto c = '#';
|
|
||||||
}
|
|
||||||
|
|
||||||
/** u */
|
|
||||||
//! c
|
|
||||||
/// c
|
|
||||||
enum class c
|
|
||||||
{
|
|
||||||
d, //< d
|
|
||||||
/// d
|
|
||||||
e, //< e
|
|
||||||
/// e
|
|
||||||
|
|
||||||
/** f
|
|
||||||
f **/
|
|
||||||
f,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// g
|
|
||||||
/// g
|
|
||||||
#define g(name) \
|
|
||||||
class name \
|
|
||||||
{ \
|
|
||||||
};
|
|
||||||
|
|
||||||
/// h
|
|
||||||
/// h
|
|
||||||
g(h)
|
|
||||||
|
|
||||||
/*! i
|
|
||||||
i */
|
|
||||||
using i = int;
|
|
||||||
|
|
||||||
/// cstddef
|
|
||||||
/// cstddef
|
|
||||||
#include <cstddef>
|
|
||||||
|
|
||||||
/// j
|
|
||||||
/// j
|
|
||||||
template <typename T/**/>
|
|
||||||
void j();
|
|
||||||
)";
|
|
||||||
|
|
||||||
auto file = parse({}, "comment-matching.cpp", code);
|
|
||||||
visit(*file, [&](const cpp_entity& e, visitor_info) {
|
|
||||||
if (e.kind() == cpp_entity_kind::file_t)
|
|
||||||
return true;
|
|
||||||
else if (e.name().empty())
|
|
||||||
return true;
|
|
||||||
else if (is_templated(e))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
INFO(e.name());
|
|
||||||
REQUIRE(e.comment());
|
|
||||||
REQUIRE(e.comment().value() == e.name() + "\n" + e.name());
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
auto add = 0u;
|
|
||||||
for (auto& comment : file->unmatched_comments())
|
|
||||||
{
|
|
||||||
if (comment.content == "cstddef\ncstddef")
|
|
||||||
// happens if include parsing is not supported
|
|
||||||
// error is still going to be detected because if it is supported, the entity will be matched above
|
|
||||||
add = 1u;
|
|
||||||
else
|
|
||||||
REQUIRE(comment.content == "u");
|
|
||||||
}
|
|
||||||
REQUIRE((file->unmatched_comments().size() == 3u + add));
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,11 @@
|
||||||
#include "libclang/preprocessor.hpp"
|
#include "libclang/preprocessor.hpp"
|
||||||
#include "test_parser.hpp"
|
#include "test_parser.hpp"
|
||||||
|
|
||||||
|
#include <cppast/cpp_variable.hpp>
|
||||||
|
|
||||||
using namespace cppast;
|
using namespace cppast;
|
||||||
|
|
||||||
TEST_CASE("preprocessor_parses_escaped_character", "[!hide][clang4]")
|
TEST_CASE("preprocessor escaped character", "[!hide][clang4]")
|
||||||
{
|
{
|
||||||
write_file("ppec.hpp", R"(
|
write_file("ppec.hpp", R"(
|
||||||
)");
|
)");
|
||||||
|
|
@ -33,9 +35,25 @@ TEST_CASE("preprocessor_parses_escaped_character", "[!hide][clang4]")
|
||||||
REQUIRE(preprocessed.includes[0].file_name == "ppec.hpp");
|
REQUIRE(preprocessed.includes[0].file_name == "ppec.hpp");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("preprocessing use external macro")
|
||||||
|
{
|
||||||
|
auto file = parse({}, "preprocessing_external_macro.cpp", R"(
|
||||||
|
#include <cmath>
|
||||||
|
#ifdef _GLIBCXX_RELEASE
|
||||||
|
|
||||||
|
// this requires libstdc++
|
||||||
|
/// auto result=(__builtin_nanf(""));
|
||||||
|
auto result = NAN;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
)");
|
||||||
|
|
||||||
|
test_visit<cpp_variable>(*file, [&](const cpp_variable&) {});
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("fast_preprocessing include guard")
|
TEST_CASE("fast_preprocessing include guard")
|
||||||
{
|
{
|
||||||
auto file_name = "fast_preprocessing_include_guard";
|
auto file_name = "fast_preprocessing_include_guard.hpp";
|
||||||
write_file(file_name, R"(
|
write_file(file_name, R"(
|
||||||
// This is a C++ comment that should get skipped.
|
// This is a C++ comment that should get skipped.
|
||||||
|
|
||||||
|
|
@ -63,3 +81,185 @@ struct foo {};
|
||||||
REQUIRE(result.macros.size() == 1u);
|
REQUIRE(result.macros.size() == 1u);
|
||||||
REQUIRE(result.macros[0].macro->name() == "INCLUDE_GUARD");
|
REQUIRE(result.macros[0].macro->name() == "INCLUDE_GUARD");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("preprocessor line numbers")
|
||||||
|
{
|
||||||
|
auto code = R"(/// 1
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
/// 5
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
int var;
|
||||||
|
|
||||||
|
/// 11
|
||||||
|
|
||||||
|
#define foo \
|
||||||
|
\
|
||||||
|
\
|
||||||
|
int \
|
||||||
|
main()
|
||||||
|
|
||||||
|
/// 19
|
||||||
|
|
||||||
|
foo {}
|
||||||
|
|
||||||
|
/// 23
|
||||||
|
|
||||||
|
/* C comment
|
||||||
|
spanning
|
||||||
|
multiple
|
||||||
|
lines
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
/// 37
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto file = parse({}, "preprocessor_line_numbers.cpp", code);
|
||||||
|
for (auto& comment : file->unmatched_comments())
|
||||||
|
{
|
||||||
|
if (comment.content[0] != '\n')
|
||||||
|
REQUIRE(comment.line == std::stoi(comment.content));
|
||||||
|
}
|
||||||
|
REQUIRE((file->unmatched_comments().size() == 6u + 1u));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("comment content")
|
||||||
|
{
|
||||||
|
auto code = R"(
|
||||||
|
/// simple comment
|
||||||
|
|
||||||
|
///no space
|
||||||
|
|
||||||
|
/// multi
|
||||||
|
/// line
|
||||||
|
/// comment
|
||||||
|
|
||||||
|
/** C comment */
|
||||||
|
/**C comment no space*/
|
||||||
|
|
||||||
|
/** Multiline
|
||||||
|
C
|
||||||
|
comment
|
||||||
|
with indent */
|
||||||
|
|
||||||
|
/** Multiline
|
||||||
|
C
|
||||||
|
comment
|
||||||
|
with
|
||||||
|
indent */
|
||||||
|
|
||||||
|
/** Multiline
|
||||||
|
* C
|
||||||
|
* comment
|
||||||
|
* with
|
||||||
|
* indent
|
||||||
|
* star */
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto file = parse({}, "comment-content.cpp", code);
|
||||||
|
auto comments = file->unmatched_comments();
|
||||||
|
REQUIRE((comments.size() == 8u));
|
||||||
|
|
||||||
|
REQUIRE(comments[0u].content == "simple comment");
|
||||||
|
REQUIRE(comments[1u].content == "no space");
|
||||||
|
REQUIRE(comments[2u].content == "multi\nline\ncomment");
|
||||||
|
REQUIRE(comments[3u].content == "C comment");
|
||||||
|
REQUIRE(comments[4u].content == "C comment no space");
|
||||||
|
REQUIRE(comments[5u].content == "Multiline\nC\n comment\nwith indent");
|
||||||
|
REQUIRE(comments[6u].content == "Multiline\nC\n comment\n with\n indent");
|
||||||
|
REQUIRE(comments[7u].content == "Multiline\nC\ncomment\nwith\nindent\nstar");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("comment matching")
|
||||||
|
{
|
||||||
|
auto code = R"(
|
||||||
|
/// u
|
||||||
|
|
||||||
|
/// a
|
||||||
|
/// a
|
||||||
|
struct a {};
|
||||||
|
|
||||||
|
/// u
|
||||||
|
/** b
|
||||||
|
* b */
|
||||||
|
void b(int, float)
|
||||||
|
{
|
||||||
|
auto c = '#';
|
||||||
|
}
|
||||||
|
|
||||||
|
/** u */
|
||||||
|
//! c
|
||||||
|
/// c
|
||||||
|
enum class c
|
||||||
|
{
|
||||||
|
d, //< d
|
||||||
|
/// d
|
||||||
|
e, //< e
|
||||||
|
/// e
|
||||||
|
|
||||||
|
/** f
|
||||||
|
f **/
|
||||||
|
f,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// g
|
||||||
|
/// g
|
||||||
|
#define g(name) \
|
||||||
|
class name \
|
||||||
|
{ \
|
||||||
|
};
|
||||||
|
|
||||||
|
/// h
|
||||||
|
/// h
|
||||||
|
g(h)
|
||||||
|
|
||||||
|
/*! i
|
||||||
|
i */
|
||||||
|
using i = int;
|
||||||
|
|
||||||
|
/// cstddef
|
||||||
|
/// cstddef
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
/// j
|
||||||
|
/// j
|
||||||
|
template <typename T/**/>
|
||||||
|
void j();
|
||||||
|
)";
|
||||||
|
|
||||||
|
auto file = parse({}, "comment-matching.cpp", code);
|
||||||
|
visit(*file, [&](const cpp_entity& e, visitor_info) {
|
||||||
|
if (e.kind() == cpp_entity_kind::file_t)
|
||||||
|
return true;
|
||||||
|
else if (e.name().empty())
|
||||||
|
return true;
|
||||||
|
else if (is_templated(e))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
INFO(e.name());
|
||||||
|
REQUIRE(e.comment());
|
||||||
|
REQUIRE(e.comment().value() == e.name() + "\n" + e.name());
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
auto add = 0u;
|
||||||
|
for (auto& comment : file->unmatched_comments())
|
||||||
|
{
|
||||||
|
if (comment.content == "cstddef\ncstddef")
|
||||||
|
// happens if include parsing is not supported
|
||||||
|
// error is still going to be detected because if it is supported, the entity will be matched above
|
||||||
|
add = 1u;
|
||||||
|
else
|
||||||
|
REQUIRE(comment.content == "u");
|
||||||
|
}
|
||||||
|
REQUIRE((file->unmatched_comments().size() == 3u + add));
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue