diff --git a/src/libclang/debug_helper.cpp b/src/libclang/debug_helper.cpp index 4d53ce5..a3ab29a 100644 --- a/src/libclang/debug_helper.cpp +++ b/src/libclang/debug_helper.cpp @@ -34,16 +34,16 @@ namespace void detail::print_cursor_info(const CXCursor& cur) noexcept { std::lock_guard lock(mtx); - std::printf("[debug] cursor '%s' (%s): %s\n", get_display_name(cur).c_str(), - cxstring(clang_getCursorKindSpelling(cur.kind)).c_str(), - cxstring(clang_getCursorUSR(cur)).c_str()); + std::fprintf(stderr, "[debug] cursor '%s' (%s): %s\n", get_display_name(cur).c_str(), + cxstring(clang_getCursorKindSpelling(cur.kind)).c_str(), + cxstring(clang_getCursorUSR(cur)).c_str()); } void detail::print_type_info(const CXType& type) noexcept { std::lock_guard lock(mtx); - std::printf("[debug] type '%s' (%s)\n", cxstring(clang_getTypeSpelling(type)).c_str(), - get_type_kind_spelling(type).c_str()); + std::fprintf(stderr, "[debug] type '%s' (%s)\n", cxstring(clang_getTypeSpelling(type)).c_str(), + get_type_kind_spelling(type).c_str()); } void detail::print_tokens(const CXTranslationUnit& tu, const CXFile& file, @@ -52,6 +52,6 @@ void detail::print_tokens(const CXTranslationUnit& tu, const CXFile& file, std::lock_guard lock(mtx); detail::tokenizer tokenizer(tu, file, cur); for (auto& token : tokenizer) - std::printf("%s ", token.c_str()); - std::puts("\n"); + std::fprintf(stderr, "%s ", token.c_str()); + std::fputs("\n", stderr); } diff --git a/src/libclang/expression_parser.cpp b/src/libclang/expression_parser.cpp index 025f0f2..49e5293 100644 --- a/src/libclang/expression_parser.cpp +++ b/src/libclang/expression_parser.cpp @@ -43,5 +43,7 @@ std::unique_ptr detail::parse_raw_expression(const parse_context if (stream.done()) return nullptr; auto expr = to_string(stream, end); + if (!expr.empty() && expr.back() == ';') + expr.pop_back(); return cpp_unexposed_expression::build(std::move(type), std::move(expr)); } diff --git a/src/libclang/function_parser.cpp b/src/libclang/function_parser.cpp index 78bf359..684f603 100644 --- a/src/libclang/function_parser.cpp +++ b/src/libclang/function_parser.cpp @@ -15,24 +15,10 @@ namespace std::unique_ptr parse_parameter(const detail::parse_context& context, const CXCursor& cur) { - auto name = detail::get_cursor_name(cur); - auto type = detail::parse_type(context, cur, clang_getCursorType(cur)); - auto is_decltype = type->kind() == cpp_type_kind::decltype_t; + auto name = detail::get_cursor_name(cur); + auto type = detail::parse_type(context, cur, clang_getCursorType(cur)); - std::unique_ptr default_value; - detail::visit_children(cur, [&](const CXCursor& child) { - if (!clang_isExpression(clang_getCursorKind(child))) - return; - else if (is_decltype) - // skip first expression then - is_decltype = false; - else - { - DEBUG_ASSERT(!default_value, detail::parse_error_handler{}, child, - "unexpected child cursor of function parameter"); - default_value = detail::parse_expression(context, child); - } - }); + auto default_value = detail::parse_default_value(context, cur, name.c_str()); if (name.empty()) return cpp_function_parameter::build(std::move(type), std::move(default_value)); diff --git a/src/libclang/parse_functions.hpp b/src/libclang/parse_functions.hpp index 964a2dd..e2081fd 100644 --- a/src/libclang/parse_functions.hpp +++ b/src/libclang/parse_functions.hpp @@ -58,6 +58,10 @@ namespace cppast comment_context comments; }; + // parse default value of variable, function parameter... + std::unique_ptr parse_default_value(const parse_context& context, + const CXCursor& cur, const char* name); + std::unique_ptr parse_type(const parse_context& context, const CXCursor& cur, const CXType& type); diff --git a/src/libclang/template_parser.cpp b/src/libclang/template_parser.cpp index fb32eaa..554ae9e 100644 --- a/src/libclang/template_parser.cpp +++ b/src/libclang/template_parser.cpp @@ -85,14 +85,7 @@ namespace auto name = detail::get_cursor_name(cur); auto type = clang_getCursorType(cur); - - std::unique_ptr def; - detail::visit_children(cur, [&](const CXCursor& child) { - DEBUG_ASSERT(clang_isExpression(clang_getCursorKind(child)) && !def, - detail::parse_error_handler{}, cur, - "unexpected child cursor of non type template parameter"); - def = detail::parse_expression(context, child); - }); + auto def = detail::parse_default_value(context, cur, name.c_str()); detail::tokenizer tokenizer(context.tu, context.file, cur); detail::token_stream stream(tokenizer, cur); diff --git a/src/libclang/tokenizer.cpp b/src/libclang/tokenizer.cpp index b6068f0..152ed2a 100644 --- a/src/libclang/tokenizer.cpp +++ b/src/libclang/tokenizer.cpp @@ -177,7 +177,8 @@ namespace end = get_next_location(tu, file, end, -1); } else if (clang_isExpression(kind) || kind == CXCursor_CXXBaseSpecifier - || kind == CXCursor_FieldDecl || kind == CXCursor_TemplateTypeParameter + || kind == CXCursor_FieldDecl || kind == CXCursor_ParmDecl + || kind == CXCursor_TemplateTypeParameter || kind == CXCursor_NonTypeTemplateParameter || kind == CXCursor_TemplateTemplateParameter) // need to shrink range by one diff --git a/src/libclang/type_parser.cpp b/src/libclang/type_parser.cpp index 79286f6..c309c5e 100644 --- a/src/libclang/type_parser.cpp +++ b/src/libclang/type_parser.cpp @@ -444,17 +444,13 @@ namespace return make_leave_type(type, [&](std::string&& spelling) -> std::unique_ptr { if (!remove_prefix(spelling, "decltype(")) return nullptr; - - std::unique_ptr expr; - detail::visit_children(cur, [&](const CXCursor& child) { - if (!expr && clang_isExpression(clang_getCursorKind(child))) - // first expression child belongs to the decltype - expr = detail::parse_expression(context, child); - }); - DEBUG_ASSERT(expr != nullptr, detail::parse_error_handler{}, cur, - "missing child of cursor"); - - return cpp_decltype_type::build(std::move(expr)); + DEBUG_ASSERT(!spelling.empty() && spelling.back() == ')', detail::parse_error_handler{}, + type, "unexpected spelling"); + spelling.pop_back(); + return cpp_decltype_type::build( + cpp_unexposed_expression::build(detail::parse_type(context, cur, + clang_getCanonicalType(type)), + spelling)); }); } diff --git a/src/libclang/variable_parser.cpp b/src/libclang/variable_parser.cpp index ffa5550..a350b09 100644 --- a/src/libclang/variable_parser.cpp +++ b/src/libclang/variable_parser.cpp @@ -12,29 +12,38 @@ using namespace cppast; -namespace +std::unique_ptr detail::parse_default_value(const detail::parse_context& context, + const CXCursor& cur, const char* name) { - std::unique_ptr parse_default_value(const detail::parse_context& context, - const CXCursor& cur, bool uses_decltype) - { - std::unique_ptr expression; - detail::visit_children(cur, [&](const CXCursor& child) { - if (clang_isDeclaration(clang_getCursorKind(child))) - return; - else if (uses_decltype) - // skip first expression, belongs to the decltype - uses_decltype = false; - else - { - DEBUG_ASSERT(!expression && clang_isExpression(child.kind), - detail::parse_error_handler{}, cur, - "unexpected child cursor of variable"); + detail::tokenizer tokenizer(context.tu, context.file, cur); + detail::token_stream stream(tokenizer, cur); - expression = detail::parse_expression(context, child); - } - }); - return expression; + auto has_default = false; + auto got_name = *name == '\0'; + for (auto paren_count = 0; !stream.done();) + { + if (detail::skip_if(stream, "(")) + ++paren_count; + else if (detail::skip_if(stream, ")")) + --paren_count; + else if (!got_name && detail::skip_if(stream, name)) + got_name = true; + else if (paren_count == 0 && got_name && detail::skip_if(stream, "=")) + { + // heuristic: we're outside of parens, the name was already encountered + // and we have an equal sign -> treat this as default value + // (yes this breaks for evil types) + has_default = true; + break; + } + else + stream.bump(); } + if (has_default) + return parse_raw_expression(context, stream, stream.end(), + parse_type(context, cur, clang_getCursorType(cur))); + else + return nullptr; } std::unique_ptr detail::parse_cpp_variable(const detail::parse_context& context, @@ -46,7 +55,6 @@ std::unique_ptr detail::parse_cpp_variable(const detail::parse_conte auto type = parse_type(context, cur, clang_getCursorType(cur)); auto storage_class = get_storage_class(cur); auto is_constexpr = false; - auto uses_decltype = false; // just look for thread local or constexpr // can't appear anywhere else, so good enough @@ -57,13 +65,11 @@ std::unique_ptr detail::parse_cpp_variable(const detail::parse_conte cpp_storage_class_specifiers(storage_class | cpp_storage_class_thread_local); else if (token.value() == "constexpr") is_constexpr = true; - else if (token.value() == "decltype") - uses_decltype = true; std::unique_ptr result; if (clang_isCursorDefinition(cur)) { - auto default_value = parse_default_value(context, cur, uses_decltype); + auto default_value = parse_default_value(context, cur, name.c_str()); result = cpp_variable::build(*context.idx, get_entity_id(cur), name.c_str(), std::move(type), std::move(default_value), storage_class, is_constexpr); @@ -97,17 +103,7 @@ std::unique_ptr detail::parse_cpp_member_variable(const detail::pars } else { - // parse_default_value() doesn't work here - tokenizer tokenizer(context.tu, context.file, cur); - token_stream stream(tokenizer, cur); - - // look for the equal sign, default value starts there - while (!stream.done() && !skip_if(stream, "=")) - stream.bump(); - auto default_value = - parse_raw_expression(context, stream, stream.end(), - parse_type(context, cur, clang_getCursorType(cur))); - + auto default_value = parse_default_value(context, cur, name.c_str()); result = cpp_member_variable::build(*context.idx, get_entity_id(cur), name.c_str(), std::move(type), std::move(default_value), is_mutable); } diff --git a/test/cpp_function.cpp b/test/cpp_function.cpp index 95f4780..6869b5f 100644 --- a/test/cpp_function.cpp +++ b/test/cpp_function.cpp @@ -133,9 +133,9 @@ void ns::l() REQUIRE( equal_types(idx, param.type(), *cpp_decltype_type::build( - cpp_literal_expression::build(cpp_builtin_type::build( - cpp_int), - "42")))); + cpp_unexposed_expression::build(cpp_builtin_type::build( + cpp_int), + "42")))); REQUIRE(!param.default_value()); } else diff --git a/test/cpp_template_parameter.cpp b/test/cpp_template_parameter.cpp index 07ab190..adb01ed 100644 --- a/test/cpp_template_parameter.cpp +++ b/test/cpp_template_parameter.cpp @@ -159,9 +159,9 @@ using d = void; REQUIRE(param.default_value()); REQUIRE( equal_expressions(param.default_value().value(), - *cpp_literal_expression::build(cpp_builtin_type::build( - cpp_nullptr), - "nullptr"))); + *cpp_unexposed_expression::build(cpp_builtin_type::build( + cpp_nullptr), + "nullptr"))); } else if (param.name() == "C") { diff --git a/test/cpp_type_alias.cpp b/test/cpp_type_alias.cpp index e4519b0..5013aa8 100644 --- a/test/cpp_type_alias.cpp +++ b/test/cpp_type_alias.cpp @@ -498,7 +498,7 @@ typedef decltype(0) w; else if (alias.name() == "w") { auto type = cpp_decltype_type::build( - cpp_literal_expression::build(cpp_builtin_type::build(cpp_int), "0")); + cpp_unexposed_expression::build(cpp_builtin_type::build(cpp_int), "0")); REQUIRE(equal_types(idx, alias.underlying_type(), *type)); } else diff --git a/test/cpp_variable.cpp b/test/cpp_variable.cpp index 19e91d9..ae68acc 100644 --- a/test/cpp_variable.cpp +++ b/test/cpp_variable.cpp @@ -4,6 +4,7 @@ #include +#include #include #include "test_parser.hpp" @@ -52,6 +53,12 @@ const auto& n = m; decltype(0) o; /// decltype(o) const& p=o; const decltype(o)& p = o; + +// array +/// int q[42]; +int q[42]; +/// int r[1]={0}; +int r[] = {0}; )"; cpp_entity_index idx; @@ -73,6 +80,7 @@ const decltype(o)& p = o; if (var.default_value()) { REQUIRE(default_value); + INFO(get_code(var)); REQUIRE(equal_expressions(var.default_value().value(), default_value.value())); } else @@ -114,8 +122,8 @@ const decltype(o)& p = o; check_variable(var, *cpp_cv_qualified_type::build(cpp_builtin_type::build(cpp_int), cpp_cv_const), type_safe::ref( - *cpp_literal_expression::build(cpp_builtin_type::build(cpp_int), - "12")), + *cpp_unexposed_expression::build(cpp_builtin_type::build(cpp_int), + "12")), cpp_storage_class_none, true, false); else if (var.name() == "i") { @@ -142,12 +150,7 @@ const decltype(o)& p = o; { check_variable(var, *cpp_user_defined_type::build(cpp_type_ref(cpp_entity_id(""), "baz")), - type_safe::ref( - *cpp_unexposed_expression::build(cpp_user_defined_type::build( - cpp_type_ref(cpp_entity_id(""), - "baz")), - "{}")), - cpp_storage_class_none, false, false); + nullptr, cpp_storage_class_none, false, false); return false; } else if (var.name() == "l") @@ -159,8 +162,8 @@ const decltype(o)& p = o; else if (var.name() == "m") check_variable(var, *cpp_auto_type::build(), type_safe::ref( - *cpp_literal_expression::build(cpp_builtin_type::build(cpp_int), - "128")), + *cpp_unexposed_expression::build(cpp_builtin_type::build(cpp_int), + "128")), cpp_storage_class_none, false, false); else if (var.name() == "n") check_variable(var, *cpp_reference_type:: @@ -172,9 +175,10 @@ const decltype(o)& p = o; "m")), cpp_storage_class_none, false, false); else if (var.name() == "o") - check_variable(var, *cpp_decltype_type::build( - cpp_literal_expression::build(cpp_builtin_type::build(cpp_int), - "0")), + check_variable(var, + *cpp_decltype_type::build( + cpp_unexposed_expression::build(cpp_builtin_type::build(cpp_int), + "0")), nullptr, cpp_storage_class_none, false, false); else if (var.name() == "p") check_variable(var, *cpp_reference_type:: @@ -189,12 +193,29 @@ const decltype(o)& p = o; *cpp_unexposed_expression::build(cpp_builtin_type::build(cpp_int), "o")), cpp_storage_class_none, false, false); + else if (var.name() == "q") + check_variable(var, + *cpp_array_type::build(cpp_builtin_type::build(cpp_int), + cpp_literal_expression:: + build(cpp_builtin_type::build(cpp_ulonglong), + "42")), + nullptr, cpp_storage_class_none, false, false); + else if (var.name() == "r") + check_variable(var, + *cpp_array_type::build(cpp_builtin_type::build(cpp_int), + cpp_literal_expression:: + build(cpp_builtin_type::build(cpp_ulonglong), + "1")), + type_safe::ref( + *cpp_unexposed_expression::build(cpp_unexposed_type::build(""), + "{0}")), + cpp_storage_class_none, false, false); else REQUIRE(false); return true; }); - REQUIRE(count == 16u); + REQUIRE(count == 18u); } TEST_CASE("static cpp_variable")