Skip to content

feat(tdl): Add Struct AST node. - #193

Merged
LinZhihao-723 merged 5 commits into
y-scope:mainfrom
LinZhihao-723:ast-struct
Aug 14, 2025
Merged

feat(tdl): Add Struct AST node.#193
LinZhihao-723 merged 5 commits into
y-scope:mainfrom
LinZhihao-723:ast-struct

Conversation

@LinZhihao-723

@LinZhihao-723 LinZhihao-723 commented Aug 14, 2025

Copy link
Copy Markdown
Member

Description

This PR adds Struct AST node as an implementation of Type. A Struct has only one child: the identifier. It also holds a reference to a StructSpec as a shared pointer. However, such a spec is not initialized during the construction. This is because the AST nodes are constructed at the parsing stage. The spec should be checked and set in the semantic analysis stage, while a symbol table is built.

Checklist

  • The PR satisfies the contribution guidelines.
  • This is a breaking change and that has been indicated in the PR title, OR this isn't a
    breaking change.
  • Necessary docs have been updated, OR no docs need to be updated.

Validation performed

  • Ensure all workflows pass.
  • Add unit tests to cover the basic behavior of Struct.

Summary by CodeRabbit

  • New Features

    • Added a Struct type to the Task Definition Language with name handling, serialization, Spec attachment and public API methods (create, serialize, set/get spec).
    • Added public error codes and messages for NullStructSpec, StructSpecAlreadySet and StructSpecNameMismatch.
  • Tests

    • Added tests for Struct creation, naming, child handling, serialization and spec assignment.
    • Covered error scenarios: null spec, duplicate assignment and name/spec mismatch.

@LinZhihao-723
LinZhihao-723 requested review from a team and sitaowang1998 as code owners August 14, 2025 01:53
@coderabbitai

coderabbitai Bot commented Aug 14, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

Adds a new TDL AST node type Struct (header + implementation), registers it in the public build, implements creation, serialization and spec-assignment with explicit error codes and category, and extends parser AST tests to cover creation, naming, serialization, spec assignment, duplicate-spec and mismatched-spec error paths.

Changes

Cohort / File(s) Change Summary
Build config
src/spider/CMakeLists.txt
Registers new Struct node: adds tdl/parser/ast/node_impl/type_impl/Struct.cpp to SPIDER_TDL_SHARED_SOURCES and .../Struct.hpp to SPIDER_TDL_SHARED_HEADERS (exposes Struct in public API).
AST: Struct type
src/spider/tdl/parser/ast/node_impl/type_impl/Struct.hpp, src/spider/tdl/parser/ast/node_impl/type_impl/Struct.cpp
Adds Struct (derives from Type) with factory create(std::unique_ptr<Node>), serialize_to_str(size_t), get_name(), set_spec(std::shared_ptr<StructSpec>), get_spec(); stores optional StructSpec; introduces ErrorCodeEnum (NullStructSpec, StructSpecAlreadySet, StructSpecNameMismatch) and an error-category specialization providing names/messages.
Tests
tests/tdl/test-parser-ast.cpp
Adds Struct tests: constructs Identifier child and Struct, asserts child-count and name, verifies serialization format, tests set_spec success and duplicate-spec error, tests mismatched-spec-name error and null-spec coverage; includes new header and alias.

Sequence Diagram(s)

sequenceDiagram
  participant Test
  participant Identifier
  participant Struct
  participant StructSpec

  Test->>Identifier: Identifier::create("TestStruct")
  Test->>Struct: Struct::create(Identifier)
  Struct-->>Test: Result<unique_ptr<Struct>>

  Test->>StructSpec: StructSpec::create("TestStruct", fields)
  Test->>Struct: Struct::set_spec(StructSpec)
  Struct-->>Test: Result<void> (ok or ErrorCode)

  Test->>Struct: Struct::serialize_to_str(indent)
  Struct-->>Test: Result<string>
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • sitaowang1998

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (5)
src/spider/tdl/parser/ast/node_impl/type_impl/Struct.hpp (2)

50-59: Document the null-spec error and keep the contract explicit.

Reflect the new error in the API contract of set_spec to prevent misuse.

  /**
   * Sets the specification for this struct.
   * @param spec
   * @return A void result on success, or an error code indicating the failure:
   * - ErrorCodeEnum::StructSpecNameMismatch if `spec`'s name does not match the underlying name.
   * - ErrorCodeEnum::StructSpecAlreadySet if the specification has already been set.
+  * - ErrorCodeEnum::NullStructSpec if `spec` is nullptr.
   */
 [[nodiscard]] auto set_spec(std::shared_ptr<StructSpec> spec)
         -> ystdlib::error_handling::Result<void>;

44-49: get_name() assumes the factory invariant; consider a debug assertion.

Not a blocker, but adding a debug-time assertion that the child is an Identifier makes violations easier to catch during development.

 [[nodiscard]] auto get_name() const -> std::string_view {
-    // The factory function ensures that the first child is of type `Identifier`.
+    // The factory function ensures that the first child is of type `Identifier`.
+    // Consider guarding this in debug builds if available:
+    // YSTDLIB_ASSERT(dynamic_cast<Identifier const*>(get_child_unsafe(0)) != nullptr);
     // NOLINTNEXTLINE(cppcoreguidelines-pro-type-static-cast-downcast)
     return static_cast<Identifier const*>(get_child_unsafe(0))->get_name();
 }
src/spider/tdl/parser/ast/node_impl/type_impl/Struct.cpp (1)

16-34: Prefer specialising the primary template directly rather than via a type alias.

Some compilers/tools can be finicky with explicit specializations referred to through aliases. Specialising ystdlib::error_handling::ErrorCategoryStruct::ErrorCodeEnum directly avoids ambiguity. Also, add the message for the new NullStructSpec code.

-using StructErrorCodeCategory = ystdlib::error_handling::ErrorCategory<Struct::ErrorCodeEnum>;
-
-template <>
-auto StructErrorCodeCategory::name() const noexcept -> char const* {
+template <>
+auto ystdlib::error_handling::ErrorCategory<Struct::ErrorCodeEnum>::name() const noexcept
+        -> char const* {
     return "spider::tdl::parser::ast::node_impl::type_impl::Struct";
 }
 
 template <>
-auto StructErrorCodeCategory::message(Struct::ErrorCodeEnum error_enum) const -> std::string {
+auto ystdlib::error_handling::ErrorCategory<Struct::ErrorCodeEnum>::message(
+        Struct::ErrorCodeEnum error_enum) const -> std::string {
     switch (error_enum) {
         case Struct::ErrorCodeEnum::StructSpecAlreadySet:
             return "The struct spec is already set.";
         case Struct::ErrorCodeEnum::StructSpecNameMismatch:
             return "The struct spec name does not match the type name.";
+        case Struct::ErrorCodeEnum::NullStructSpec:
+            return "The struct spec is null.";
         default:
             return "Unknown error code enum";
     }
 }
tests/tdl/test-parser-ast.cpp (2)

363-376: Also assert get_spec() after setting the spec to catch regressions.

After the successful set_spec, verify get_spec() is non-null and (optionally) points to the same object.

 REQUIRE_FALSE(struct_node->set_spec(struct_spec_result.value()).has_error());
+REQUIRE(struct_node->get_spec() != nullptr);
+// Optional: if struct_spec_result.value() is a shared_ptr<StructSpec>, compare addresses.
+REQUIRE(struct_node->get_spec() == struct_spec_node);

Note: If comparing addresses, ensure struct_spec_node remains valid and refers to the same underlying object.


386-395: Good negative test; consider adding an invalid-child-type case for Struct::create.

You’ve covered name mismatch and duplicate assignment. It’d be valuable to assert that Struct::create rejects a non-Identifier child and propagates Node::UnexpectedChildNodeType (mirroring your container tests).

For example, add:

SECTION("Invalid input for Struct creation") {
    auto wrong_name{Int::create(IntSpec::Int64)};
    auto struct_result{Struct::create(std::move(wrong_name))};
    REQUIRE(struct_result.has_error());
    REQUIRE(struct_result.error() == Node::ErrorCode{Node::ErrorCodeEnum::UnexpectedChildNodeType});
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4a061e0 and fc289d7.

📒 Files selected for processing (4)
  • src/spider/CMakeLists.txt (2 hunks)
  • src/spider/tdl/parser/ast/node_impl/type_impl/Struct.cpp (1 hunks)
  • src/spider/tdl/parser/ast/node_impl/type_impl/Struct.hpp (1 hunks)
  • tests/tdl/test-parser-ast.cpp (4 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: lint
  • GitHub Check: non-storage-unit-tests (ubuntu-22.04)
  • GitHub Check: non-storage-unit-tests (ubuntu-24.04)
🔇 Additional comments (8)
src/spider/CMakeLists.txt (2)

198-214: Struct node is properly wired into the TDL target.

Struct.cpp is added to SPIDER_TDL_SHARED_SOURCES and will be compiled into spider_tdl as expected.


215-236: Public header exposure looks correct.

Struct.hpp is exported via SPIDER_TDL_SHARED_HEADERS and target_sources(spider_tdl PUBLIC ...), making it available to dependants. If you have install/export rules elsewhere, ensure this header is included there as well.

Would you like me to scan the repository for install/export rules to ensure Struct.hpp is included where needed?

src/spider/tdl/parser/ast/node_impl/type_impl/Struct.cpp (2)

46-58: LGTM: Serialization is consistent with the test expectation and existing style.

Indentation and child serialization match "[Type[Struct]]" with a nested Identifier.


37-44: No change needed — validate_child_node_type handles nullptr and the make_unique(T{}) usage is consistent

validate_child_node_type (src/spider/tdl/parser/ast/utils.hpp) uses dynamic_cast and returns Node::ErrorCodeEnum::UnexpectedChildNodeType for nullptr, so no extra null check is required. The make_unique(T{}) pattern is used consistently across the codebase (e.g. NamedVar.cpp, StructSpec.cpp, Tuple.cpp, Map.cpp, List.cpp, Struct.cpp), so changing this one factory would be inconsistent; if you want to avoid constructing via the move/copy ctor, do a separate, repository-wide refactor.

Files inspected:

  • src/spider/tdl/parser/ast/utils.hpp (validate_child_node_type)
  • src/spider/tdl/parser/ast/node_impl/type_impl/Struct.cpp
  • src/spider/tdl/parser/ast/node_impl/NamedVar.cpp
  • src/spider/tdl/parser/ast/node_impl/StructSpec.cpp
  • src/spider/tdl/parser/ast/node_impl/type_impl/container_impl/Tuple.cpp
  • src/spider/tdl/parser/ast/node_impl/type_impl/container_impl/Map.cpp
  • src/spider/tdl/parser/ast/node_impl/type_impl/container_impl/List.cpp
tests/tdl/test-parser-ast.cpp (4)

25-26: Include and alias of Struct are correct.

Public header path and namespace aliasing match the new API surface.


331-361: StructSpec serialization test is thorough and stable.

Covers all fields and matches the expected tree shape. No issues.


377-385: Struct serialization test matches the implementation.

The expected string aligns with Struct::serialize_to_str.


317-329: Nice coverage for StructSpec properties.

Validates child count and name; pairs well with the serialization checks.

Comment thread src/spider/tdl/parser/ast/node_impl/type_impl/Struct.cpp
Comment thread src/spider/tdl/parser/ast/node_impl/type_impl/Struct.hpp
Comment thread src/spider/tdl/parser/ast/node_impl/type_impl/Struct.cpp
Comment thread tests/tdl/test-parser-ast.cpp Outdated
Comment thread tests/tdl/test-parser-ast.cpp Outdated
Comment on lines +372 to +375
auto const duplicated_set_spec{struct_node->set_spec(struct_spec_result.value())};
REQUIRE(duplicated_set_spec.has_error());
REQUIRE(duplicated_set_spec.error()
== Struct::ErrorCode{Struct::ErrorCodeEnum::StructSpecAlreadySet});

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate test should be put into a separate section.

Suggested change
auto const duplicated_set_spec{struct_node->set_spec(struct_spec_result.value())};
REQUIRE(duplicated_set_spec.has_error());
REQUIRE(duplicated_set_spec.error()
== Struct::ErrorCode{Struct::ErrorCodeEnum::StructSpecAlreadySet});
SECTION("Set spec twice.")
auto const duplicated_set_spec{struct_node->set_spec(struct_spec_result.value())};
REQUIRE(duplicated_set_spec.has_error());
REQUIRE(duplicated_set_spec.error()
== Struct::ErrorCode{Struct::ErrorCodeEnum::StructSpecAlreadySet});
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is necessary: adding one more level of SECTION will enforce the control flow before this section to run twice iiuc.

Comment thread tests/tdl/test-parser-ast.cpp Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (4)
tests/tdl/test-parser-ast.cpp (4)

401-407: Avoid redundant dynamic_cast on StructSpec shared_ptr

The dynamic_cast here is unnecessary since you already checked the Result has no error and you’re holding a shared_ptr. A simple non-null check communicates intent better.

Apply this diff:

-        REQUIRE(nullptr != dynamic_cast<StructSpec const*>(struct_spec_result.value().get()));
+        REQUIRE(struct_spec_result.value() != nullptr);

408-434: Add a post-set_spec serialization assertion

Given Struct serialization currently prints only the name, it’s valuable to assert serialization remains unchanged after spec assignment. This prevents regressions if future changes inadvertently alter Struct’s serialization when a spec is set.

Apply this diff right after verifying get_spec() is non-null:

             // Set the `StructSpec` to the `Struct`
             REQUIRE_FALSE(struct_node->set_spec(struct_spec_result.value()).has_error());
             REQUIRE(nullptr != struct_node->get_spec());
+
+            // Serialization should be unaffected by spec assignment
+            auto const serialized_after_set{struct_node->serialize_to_str(0)};
+            REQUIRE_FALSE(serialized_after_set.has_error());
+            REQUIRE(serialized_after_set.value() == cExpectedSerializedResult);

436-445: Ensure no partial state on failed set_spec (spec remains null)

After the name-mismatch error, also assert that the Struct’s spec remains unset to guard against partial updates.

Apply this diff after the error assertion:

             REQUIRE(set_spec_result.has_error());
             REQUIRE(set_spec_result.error()
                     == Struct::ErrorCode{Struct::ErrorCodeEnum::StructSpecNameMismatch});
+            // Ensure spec is still null after a failed set_spec
+            REQUIRE(nullptr == struct_node->get_spec());

390-446: Add negative test: Struct::create should reject non-Identifier name

Since Struct::create validates the child node type (Identifier expected), add a test ensuring incorrect child types (e.g., NamedVar) yield UnexpectedChildNodeType. This mirrors existing negative coverage for containers and hardens the contract.

Apply this diff to add a sibling SECTION under "Struct", right after creating struct_spec_result:

         REQUIRE_FALSE(struct_spec_result.has_error());
-        REQUIRE(nullptr != dynamic_cast<StructSpec const*>(struct_spec_result.value().get()));
+        REQUIRE(struct_spec_result.value() != nullptr);
 
+        SECTION("Invalid name type") {
+            auto named_var_result{
+                    NamedVar::create(Identifier::create("m_x"), Int::create(IntSpec::Int64))
+            };
+            REQUIRE_FALSE(named_var_result.has_error());
+            auto bad_struct_result{Struct::create(std::move(named_var_result.value()))};
+            REQUIRE(bad_struct_result.has_error());
+            REQUIRE(bad_struct_result.error()
+                    == Node::ErrorCode{Node::ErrorCodeEnum::UnexpectedChildNodeType});
+        }
+
         SECTION("Struct with StructSpec") {
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these settings in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between fc289d7 and 930d32c.

📒 Files selected for processing (1)
  • tests/tdl/test-parser-ast.cpp (3 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
tests/tdl/test-parser-ast.cpp (3)
src/spider/tdl/parser/ast/node_impl/type_impl/Struct.hpp (1)
  • Struct (64-64)
src/spider/tdl/parser/ast/node_impl/type_impl/Struct.cpp (2)
  • create (37-44)
  • create (37-38)
src/spider/tdl/parser/ast/node_impl/NamedVar.cpp (2)
  • create (17-26)
  • create (17-18)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: lint
  • GitHub Check: non-storage-unit-tests (ubuntu-24.04)
  • GitHub Check: non-storage-unit-tests (ubuntu-22.04)
🔇 Additional comments (2)
tests/tdl/test-parser-ast.cpp (2)

25-25: Correct header inclusion for new Struct type

Including Struct.hpp is necessary and correct for the new tests. No issues.


41-41: Good alias for brevity and readability

The using-alias for Struct reduces verbosity and keeps the test readable. Looks good.

@LinZhihao-723 LinZhihao-723 changed the title feat(tdl): Add Struct AST node. (#190) feat(tdl): Add Struct AST node. Aug 14, 2025

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (2)
tests/tdl/test-parser-ast.cpp (2)

436-440: Optional: Move duplicate-set-spec check into its own SECTION for isolation.
Keeping it separate avoids reusing state mutated by earlier steps and clarifies intent. That said, Catch2 re-runs parent scopes for each SECTION, so your current approach is still correct.


408-440: Strengthen assertions: verify spec identity and serialisation remains unchanged after spec is set.

Confirmed: Struct::get_spec() returns StructSpec const* (m_spec is std::shared_ptr), so compare to struct_spec_result.value().get().

  • File to update: tests/tdl/test-parser-ast.cpp — SECTION("Struct with StructSpec") (around lines ~408–440)

Apply this diff:

             // Set the `StructSpec` to the `Struct`
             REQUIRE_FALSE(struct_node->set_spec(struct_spec_result.value()).has_error());
-            REQUIRE(nullptr != struct_node->get_spec());
+            REQUIRE(nullptr != struct_node->get_spec());
+            // Ensure the exact spec instance is stored.
+            REQUIRE(struct_node->get_spec() == struct_spec_result.value().get());
+
+            // Serialisation should not include StructSpec and thus remain unchanged.
+            auto const serialized_after_set{struct_node->serialize_to_str(0)};
+            REQUIRE_FALSE(serialized_after_set.has_error());
+            REQUIRE(serialized_after_set.value() == cExpectedSerializedResult);
🧹 Nitpick comments (4)
tests/tdl/test-parser-ast.cpp (2)

393-407: Factor out StructSpec construction to a helper to avoid state sharing across SECTIONs.
Although Catch2 SECTION execution model makes this safe, a small builder eliminates reliance on that nuance and reduces duplication.

Consider extracting a helper:

auto make_test_struct_spec(std::string name = "TestStruct") -> std::shared_ptr<StructSpec> {
    auto int_field = NamedVar::create(Identifier::create("m_int"), Int::create(IntSpec::Int64));
    REQUIRE_FALSE(int_field.has_error());
    std::vector<std::unique_ptr<Node>> fields;
    fields.emplace_back(std::move(int_field.value()));
    auto spec = StructSpec::create(Identifier::create(std::move(name)), std::move(fields));
    REQUIRE_FALSE(spec.has_error());
    return spec.value();
}

Then use auto struct_spec = make_test_struct_spec(std::string{cTestStructName}); in each SECTION.


406-407: Redundant dynamic_cast on a known type.
StructSpec::create(...) returns a std::shared_ptr<StructSpec>, so dynamic_cast<StructSpec const*> is unnecessary here.

Apply this diff:

-        REQUIRE(nullptr != dynamic_cast<StructSpec const*>(struct_spec_result.value().get()));
+        REQUIRE(struct_spec_result.value() != nullptr);
src/spider/tdl/parser/ast/node_impl/type_impl/Struct.cpp (2)

20-37: Nit: Harmonise error messages and wording.

  • Consider using “null” instead of “NULL” for consistency with typical wording.
  • Optionally, make the default message more specific, e.g., “Unknown Struct::ErrorCodeEnum”.

Apply this diff:

-            return "The struct spec is NULL.";
+            return "The struct spec is null.";
-            return "Unknown error code enum";
+            return "Unknown Struct::ErrorCodeEnum";

63-78: set_spec covers all error paths and avoids null dereference.
Order of checks favours “already set” over “null arg”; if the intended contract is to validate inputs first regardless of state, consider swapping the first two checks. Otherwise, this is fine and well-tested.

Optional reordering for input-first validation:

-auto Struct::set_spec(std::shared_ptr<StructSpec> spec) -> ystdlib::error_handling::Result<void> {
-    if (nullptr != m_spec) {
-        return Struct::ErrorCode{Struct::ErrorCodeEnum::StructSpecAlreadySet};
-    }
-
-    if (nullptr == spec) {
-        return Struct::ErrorCode{Struct::ErrorCodeEnum::NullStructSpec};
-    }
+auto Struct::set_spec(std::shared_ptr<StructSpec> spec) -> ystdlib::error_handling::Result<void> {
+    if (nullptr == spec) {
+        return Struct::ErrorCode{Struct::ErrorCodeEnum::NullStructSpec};
+    }
+    if (nullptr != m_spec) {
+        return Struct::ErrorCode{Struct::ErrorCodeEnum::StructSpecAlreadySet};
+    }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these settings in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 930d32c and 3986457.

📒 Files selected for processing (3)
  • src/spider/tdl/parser/ast/node_impl/type_impl/Struct.cpp (1 hunks)
  • src/spider/tdl/parser/ast/node_impl/type_impl/Struct.hpp (1 hunks)
  • tests/tdl/test-parser-ast.cpp (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/spider/tdl/parser/ast/node_impl/type_impl/Struct.hpp
🧰 Additional context used
🧬 Code Graph Analysis (2)
src/spider/tdl/parser/ast/node_impl/type_impl/Struct.cpp (1)
src/spider/tdl/parser/ast/node_impl/type_impl/Struct.hpp (4)
  • Struct (66-66)
  • name (37-38)
  • indentation_level (41-42)
  • spec (59-60)
tests/tdl/test-parser-ast.cpp (3)
src/spider/tdl/parser/ast/node_impl/type_impl/Struct.hpp (1)
  • Struct (66-66)
src/spider/tdl/parser/ast/node_impl/type_impl/Struct.cpp (2)
  • create (40-47)
  • create (40-41)
src/spider/tdl/parser/ast/node_impl/NamedVar.cpp (2)
  • create (17-26)
  • create (17-18)
🔇 Additional comments (4)
tests/tdl/test-parser-ast.cpp (2)

25-25: Include of Struct header looks correct and scoped.
Header path aligns with other includes and matches usage below.


41-41: Using-alias for Struct improves readability.
Keeps test code concise and consistent with existing aliases.

src/spider/tdl/parser/ast/node_impl/type_impl/Struct.cpp (2)

40-47: Factory validates child type and attaches name correctly. LGTM.
Type validation and child wiring are consistent with the rest of the AST design.


49-61: Serialisation format matches existing conventions.
Indentation and child serialisation align with other node implementations.

Comment thread tests/tdl/test-parser-ast.cpp
Comment on lines +442 to +451
SECTION("Set spec to a wrong Struct") {
auto struct_result{Struct::create(Identifier::create("WrongStruct"))};
REQUIRE_FALSE(struct_result.has_error());
auto* struct_node{dynamic_cast<Struct*>(struct_result.value().get())};
REQUIRE(nullptr != struct_node);
auto const set_spec_result{struct_node->set_spec(struct_spec_result.value())};
REQUIRE(set_spec_result.has_error());
REQUIRE(set_spec_result.error()
== Struct::ErrorCode{Struct::ErrorCodeEnum::StructSpecNameMismatch});
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Also assert struct state remains unchanged after a failed set_spec due to name mismatch.
Ensures no partial assignment occurs on error.

Apply this diff:

             auto const set_spec_result{struct_node->set_spec(struct_spec_result.value())};
             REQUIRE(set_spec_result.has_error());
             REQUIRE(set_spec_result.error()
                     == Struct::ErrorCode{Struct::ErrorCodeEnum::StructSpecNameMismatch});
+            // Ensure no spec was installed on error.
+            REQUIRE(struct_node->get_spec() == nullptr);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
SECTION("Set spec to a wrong Struct") {
auto struct_result{Struct::create(Identifier::create("WrongStruct"))};
REQUIRE_FALSE(struct_result.has_error());
auto* struct_node{dynamic_cast<Struct*>(struct_result.value().get())};
REQUIRE(nullptr != struct_node);
auto const set_spec_result{struct_node->set_spec(struct_spec_result.value())};
REQUIRE(set_spec_result.has_error());
REQUIRE(set_spec_result.error()
== Struct::ErrorCode{Struct::ErrorCodeEnum::StructSpecNameMismatch});
}
SECTION("Set spec to a wrong Struct") {
auto struct_result{Struct::create(Identifier::create("WrongStruct"))};
REQUIRE_FALSE(struct_result.has_error());
auto* struct_node{dynamic_cast<Struct*>(struct_result.value().get())};
REQUIRE(nullptr != struct_node);
auto const set_spec_result{struct_node->set_spec(struct_spec_result.value())};
REQUIRE(set_spec_result.has_error());
REQUIRE(set_spec_result.error()
== Struct::ErrorCode{Struct::ErrorCodeEnum::StructSpecNameMismatch});
// Ensure no spec was installed on error.
REQUIRE(struct_node->get_spec() == nullptr);
}
🤖 Prompt for AI Agents
In tests/tdl/test-parser-ast.cpp around lines 442 to 451, after asserting that
set_spec returned a name-mismatch error, add assertions that the Struct object's
state was not mutated: confirm the struct's identifier/name is still
"WrongStruct" and that its spec pointer/member remains unset (or unchanged) so
no partial assignment occurred on error.

sitaowang1998
sitaowang1998 previously approved these changes Aug 14, 2025

@sitaowang1998 sitaowang1998 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. It would be nice to have the last coderabbit suggestion.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
tests/tdl/test-parser-ast.cpp (2)

443-452: Also assert state is unchanged on name-mismatch error.
After a failed set_spec due to StructSpecNameMismatch, ensure no partial assignment occurred and the struct name/spec remain intact.

Apply this diff to extend the assertions:

             auto const set_spec_result{struct_node->set_spec(struct_spec_result.value())};
             REQUIRE(set_spec_result.has_error());
             REQUIRE(set_spec_result.error()
                     == Struct::ErrorCode{Struct::ErrorCodeEnum::StructSpecNameMismatch});
+            // Ensure no spec was installed on error and name is unchanged.
+            REQUIRE(struct_node->get_spec() == nullptr);
+            REQUIRE(struct_node->get_name() == "WrongStruct");

389-453: Add a negative test: Struct::create should reject non-Identifier names.
This mirrors the existing container invalid-input tests and exercises validate_child_node_type in Struct::create.

Append this SECTION within the "Struct" block (e.g., after the “Set spec to a wrong Struct” section):

         SECTION("Set spec to a wrong Struct") {
             auto struct_result{Struct::create(Identifier::create("WrongStruct"))};
             REQUIRE_FALSE(struct_result.has_error());
             auto* struct_node{dynamic_cast<Struct*>(struct_result.value().get())};
             REQUIRE(nullptr != struct_node);
             auto const set_spec_result{struct_node->set_spec(struct_spec_result.value())};
             REQUIRE(set_spec_result.has_error());
             REQUIRE(set_spec_result.error()
                     == Struct::ErrorCode{Struct::ErrorCodeEnum::StructSpecNameMismatch});
         }
+
+        SECTION("Invalid inputs for Struct creation") {
+            // Non-Identifier as name should be rejected.
+            auto invalid_name{Int::create(IntSpec::Int64)};
+            auto invalid_struct{Struct::create(std::move(invalid_name))};
+            REQUIRE(invalid_struct.has_error());
+            REQUIRE(invalid_struct.error()
+                    == Node::ErrorCode{Node::ErrorCodeEnum::UnexpectedChildNodeType});
+        }
🧹 Nitpick comments (1)
tests/tdl/test-parser-ast.cpp (1)

408-441: Strengthen post-conditions: also assert the installed spec equals the provided shared_ptr.
After a successful set_spec, verify the struct holds the exact same shared_ptr (not just non-null). This tightens the state validation and guards against inadvertent copies of a different instance.

Apply this diff inside the same SECTION after setting the spec:

             // Set the `StructSpec` to the `Struct`
             REQUIRE_FALSE(struct_node->set_spec(struct_spec_result.value()).has_error());
             REQUIRE(nullptr != struct_node->get_spec());
+            // The struct should retain the exact provided spec instance.
+            REQUIRE(struct_node->get_spec() == struct_spec_result.value());

             // Ensure `StructSpec` can't be set again
             auto const duplicated_set_spec{struct_node->set_spec(struct_spec_result.value())};
             REQUIRE(duplicated_set_spec.has_error());
             REQUIRE(duplicated_set_spec.error()
                     == Struct::ErrorCode{Struct::ErrorCodeEnum::StructSpecAlreadySet});
+            // Spec remains unchanged after duplicate set attempt.
+            REQUIRE(struct_node->get_spec() == struct_spec_result.value());
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these settings in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 3986457 and ecec2e8.

📒 Files selected for processing (1)
  • tests/tdl/test-parser-ast.cpp (3 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
tests/tdl/test-parser-ast.cpp (3)
src/spider/tdl/parser/ast/node_impl/type_impl/Struct.hpp (1)
  • Struct (66-66)
src/spider/tdl/parser/ast/node_impl/type_impl/Struct.cpp (2)
  • create (40-47)
  • create (40-41)
src/spider/tdl/parser/ast/node_impl/NamedVar.cpp (2)
  • create (17-26)
  • create (17-18)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: lint
  • GitHub Check: non-storage-unit-tests (ubuntu-22.04)
  • GitHub Check: non-storage-unit-tests (ubuntu-24.04)
🔇 Additional comments (3)
tests/tdl/test-parser-ast.cpp (3)

25-25: Header include for Struct is correct and appropriately scoped.
Matches the new public API location and keeps include grouping consistent with neighbouring type headers.


41-41: Alias for Struct improves readability and consistency.
Keeps usage aligned with other type aliases in this test file.


389-407: Good: Reusable StructSpec fixture and type check before use.
Creating a minimal valid StructSpec once per SECTION path and sanity-checking its dynamic type is clean and avoids duplication.

@LinZhihao-723
LinZhihao-723 merged commit 5c977eb into y-scope:main Aug 14, 2025
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants