From ac435f914c2461691263b1becc64854f9de2fddd Mon Sep 17 00:00:00 2001 From: iyush Date: Sat, 15 Apr 2023 16:40:40 +0200 Subject: [PATCH] LibCpp: Support for parsing c-style fixed arrays (arr[2]) Also adds tests for finding declaration of arrays inside CppComprehension which requires proper parsing for passing. Side-effect of this patch: if we ctrl+click on array variables, it should jump to the correct declaration inside HackStudio. --- .../LibCodeComprehension/Cpp/Tests.cpp | 80 +++++++++++++++++-- .../Tests/find_array_variable_declaration.cpp | 11 +++ Userland/Libraries/LibCpp/AST.cpp | 18 +++++ Userland/Libraries/LibCpp/AST.h | 23 +++++- Userland/Libraries/LibCpp/Parser.cpp | 38 +++++++++ .../Tests/parser/array-initialization.ast | 19 +++++ .../Tests/parser/array-initialization.cpp | 3 + 7 files changed, 184 insertions(+), 8 deletions(-) create mode 100644 Userland/Libraries/LibCodeComprehension/Cpp/Tests/find_array_variable_declaration.cpp create mode 100644 Userland/Libraries/LibCpp/Tests/parser/array-initialization.ast create mode 100644 Userland/Libraries/LibCpp/Tests/parser/array-initialization.cpp diff --git a/Userland/Libraries/LibCodeComprehension/Cpp/Tests.cpp b/Userland/Libraries/LibCodeComprehension/Cpp/Tests.cpp index fbe9d92b17..15e0211408 100644 --- a/Userland/Libraries/LibCodeComprehension/Cpp/Tests.cpp +++ b/Userland/Libraries/LibCodeComprehension/Cpp/Tests.cpp @@ -33,6 +33,12 @@ static bool s_some_test_failed = false; return; \ } while (0) +#define RUN(function) \ + function; \ + if (s_some_test_failed) { \ + return 1; \ + } + constexpr auto TESTS_ROOT_DIR = "/home/anon/Tests/cpp-tests/comprehension"sv; class FileDB : public CodeComprehension::FileDB { @@ -61,18 +67,24 @@ static void test_complete_local_args(); static void test_complete_local_vars(); static void test_complete_type(); static void test_find_variable_definition(); +static void test_find_array_variable_declaration_single(); +static void test_find_array_variable_declaration_single_empty(); +static void test_find_array_variable_declaration_double(); static void test_complete_includes(); static void test_parameters_hint(); int run_tests() { - test_complete_local_args(); - test_complete_local_vars(); - test_complete_type(); - test_find_variable_definition(); - test_complete_includes(); - test_parameters_hint(); - return s_some_test_failed ? 1 : 0; + RUN(test_complete_local_args()); + RUN(test_complete_local_vars()); + RUN(test_complete_type()); + RUN(test_find_variable_definition()); + RUN(test_find_array_variable_declaration_single()); + RUN(test_find_array_variable_declaration_single_empty()); + RUN(test_find_array_variable_declaration_double()); + RUN(test_complete_includes()); + RUN(test_parameters_hint()); + return 0; } static void add_file(FileDB& filedb, DeprecatedString const& name) @@ -145,6 +157,60 @@ void test_find_variable_definition() FAIL("wrong declaration location"); } +void test_find_array_variable_declaration_single() +{ + I_TEST(Find 1D Array as a Variable Declaration) + FileDB filedb; + auto filename = "find_array_variable_declaration.cpp"; + add_file(filedb, filename); + CodeComprehension::Cpp::CppComprehensionEngine engine(filedb); + auto position = engine.find_declaration_of(filename, { 3, 6 }); + if (!position.has_value()) + FAIL("declaration not found"); + + if (position.value().file == filename && position.value().line == 2 && position.value().column >= 4) + PASS; + + printf("Found at position %zu %zu\n", position.value().line, position.value().column); + FAIL("wrong declaration location"); +} + +void test_find_array_variable_declaration_single_empty() +{ + I_TEST(Find 1D Empty size Array as a Variable Declaration) + FileDB filedb; + auto filename = "find_array_variable_declaration.cpp"; + add_file(filedb, filename); + CodeComprehension::Cpp::CppComprehensionEngine engine(filedb); + auto position = engine.find_declaration_of(filename, { 6, 6 }); + if (!position.has_value()) + FAIL("declaration not found"); + + if (position.value().file == filename && position.value().line == 5 && position.value().column >= 4) + PASS; + + printf("Found at position %zu %zu\n", position.value().line, position.value().column); + FAIL("wrong declaration location"); +} + +void test_find_array_variable_declaration_double() +{ + I_TEST(Find 2D Array as a Variable Declaration) + FileDB filedb; + auto filename = "find_array_variable_declaration.cpp"; + add_file(filedb, filename); + CodeComprehension::Cpp::CppComprehensionEngine engine(filedb); + auto position = engine.find_declaration_of(filename, { 9, 6 }); + if (!position.has_value()) + FAIL("declaration not found"); + + if (position.value().file == filename && position.value().line == 8 && position.value().column >= 4) + PASS; + + printf("Found at position %zu %zu\n", position.value().line, position.value().column); + FAIL("wrong declaration location"); +} + void test_complete_includes() { I_TEST(Complete include statements) diff --git a/Userland/Libraries/LibCodeComprehension/Cpp/Tests/find_array_variable_declaration.cpp b/Userland/Libraries/LibCodeComprehension/Cpp/Tests/find_array_variable_declaration.cpp new file mode 100644 index 0000000000..fe2d5edc5e --- /dev/null +++ b/Userland/Libraries/LibCodeComprehension/Cpp/Tests/find_array_variable_declaration.cpp @@ -0,0 +1,11 @@ +void foo() +{ + int arr[2] = {1,2}; + arr[1] = 0; + + int arr2[] = {5,6}; + arr2[0] = 11; + + int arr3[1][2] = {{3,4}}; + arr3[0][0] = 72; +} diff --git a/Userland/Libraries/LibCpp/AST.cpp b/Userland/Libraries/LibCpp/AST.cpp index 610f7ada75..92c666b9a0 100644 --- a/Userland/Libraries/LibCpp/AST.cpp +++ b/Userland/Libraries/LibCpp/AST.cpp @@ -572,6 +572,24 @@ StringView TemplatizedName::full_name() const return *m_full_name; } +void SizedName::dump(FILE* output, size_t indent) const +{ + Name::dump(output, indent); + print_indent(output, indent + 1); + + StringBuilder dimension_info; + for (auto const& dim : m_dimensions) { + dimension_info.append('['); + dimension_info.append(dim); + dimension_info.append(']'); + } + + if (dimension_info.is_empty()) { + dimension_info.append("[]"sv); + } + outln(output, "{}", dimension_info.to_deprecated_string()); +} + void CppCastExpression::dump(FILE* output, size_t indent) const { ASTNode::dump(output, indent); diff --git a/Userland/Libraries/LibCpp/AST.h b/Userland/Libraries/LibCpp/AST.h index f3acad7a53..072ffc8382 100644 --- a/Userland/Libraries/LibCpp/AST.h +++ b/Userland/Libraries/LibCpp/AST.h @@ -434,6 +434,7 @@ public: virtual void dump(FILE* = stdout, size_t indent = 0) const override; virtual bool is_name() const override { return true; } virtual bool is_templatized() const { return false; } + virtual bool is_sized() const { return false; } Name(ASTNode const* parent, Optional start, Optional end, DeprecatedString const& filename) : Expression(parent, start, end, filename) @@ -453,6 +454,25 @@ private: mutable Optional m_full_name; }; +class SizedName : public Name { +public: + virtual ~SizedName() override = default; + virtual StringView class_name() const override { return "SizedName"sv; } + virtual bool is_sized() const override { return true; } + void dump(FILE* output, size_t indent) const override; + + SizedName(ASTNode const* parent, Optional start, Optional end, DeprecatedString const& filename) + : Name(parent, start, end, filename) + { + } + + void append_dimension(StringView dim) { m_dimensions.append(dim); }; + +private: + Vector m_dimensions; + mutable Optional m_full_name; +}; + class TemplatizedName : public Name { public: virtual ~TemplatizedName() override = default; @@ -1020,5 +1040,6 @@ template<> inline bool ASTNode::fast_is() const { return is_type() && verify_cast(*this).is_named_type(); } template<> inline bool ASTNode::fast_is() const { return is_name() && verify_cast(*this).is_templatized(); } - +template<> +inline bool ASTNode::fast_is() const { return is_name() && verify_cast(*this).is_sized(); } } diff --git a/Userland/Libraries/LibCpp/Parser.cpp b/Userland/Libraries/LibCpp/Parser.cpp index 5cf2eb9ca9..69250b4b26 100644 --- a/Userland/Libraries/LibCpp/Parser.cpp +++ b/Userland/Libraries/LibCpp/Parser.cpp @@ -294,6 +294,19 @@ bool Parser::match_variable_declaration() (void)parse_name(get_dummy_node()); + while (!eof() && (peek().type() == Token::Type::LeftBracket)) { + consume(Token::Type::LeftBracket); + + if (match(Token::Type::Integer)) { + consume(Token::Type::Integer); + } + if (!match(Token::Type::RightBracket)) { + error("No closing right bracket"sv); + return false; + } + consume(Token::Type::RightBracket); + } + if (match(Token::Type::Equals)) { consume(Token::Type::Equals); if (!match_expression()) { @@ -1449,7 +1462,9 @@ NonnullRefPtr Parser::parse_name(ASTNode const& parent) return name_node; } + bool is_templatized = false; if (match_template_arguments()) { + is_templatized = true; consume(Token::Type::Less); NonnullRefPtr templatized_name = create_ast_node(parent, name_node->start(), {}); templatized_name->set_name(name_node->name()); @@ -1464,6 +1479,27 @@ NonnullRefPtr Parser::parse_name(ASTNode const& parent) consume(Token::Type::Greater); } + if (!is_templatized && (peek().type() == Token::Type::LeftBracket)) { + NonnullRefPtr sized_name = create_ast_node(parent, name_node->start(), {}); + sized_name->set_name(name_node->name()); + sized_name->set_scope(name_node->scope()); + + while (peek().type() == Token::Type::LeftBracket) { + consume(Token::Type::LeftBracket); + + StringView size = "0"sv; + if (peek().type() == Token::Type::Integer) { + auto token = consume(Token::Type::Integer); + size = token.text(); + } + sized_name->append_dimension(size); + + consume(Token::Type::RightBracket); + } + name_node->set_end(position()); + name_node = sized_name; + } + name_node->set_end(previous_token_end()); return name_node; } @@ -1564,6 +1600,8 @@ NonnullRefPtr Parser::parse_braced_init_list(ASTNode const consume(Token::Type::LeftCurly); while (!eof() && peek().type() != Token::Type::RightCurly) { init_list->add_expression(parse_expression(*init_list)); + if (peek().type() == Token::Type::Comma) + consume(Token::Type::Comma); } consume(Token::Type::RightCurly); init_list->set_end(position()); diff --git a/Userland/Libraries/LibCpp/Tests/parser/array-initialization.ast b/Userland/Libraries/LibCpp/Tests/parser/array-initialization.ast new file mode 100644 index 0000000000..2d29850149 --- /dev/null +++ b/Userland/Libraries/LibCpp/Tests/parser/array-initialization.ast @@ -0,0 +1,19 @@ +TranslationUnit[0:0->2:0] + FunctionDeclaration[0:0->2:0] + NamedType[0:0->0:3] + void + foo + ( + ) + FunctionDefinition[0:11->2:0] + { + VariableDeclaration[1:4->1:20] + NamedType[1:4->1:6] + int + x + BracedInitList[1:15->1:20] + NumericLiteral[1:16->1:16] + 1 + NumericLiteral[1:18->1:18] + 2 + } diff --git a/Userland/Libraries/LibCpp/Tests/parser/array-initialization.cpp b/Userland/Libraries/LibCpp/Tests/parser/array-initialization.cpp new file mode 100644 index 0000000000..b03eb20dee --- /dev/null +++ b/Userland/Libraries/LibCpp/Tests/parser/array-initialization.cpp @@ -0,0 +1,3 @@ +void foo() { + int x[0] = {1,2}; +}