Skip to content

Refine parameter parsing#57

Merged
leynos merged 7 commits into
mainfrom
codex/add-parse_type_expr-helper-and-update-parse_name_type_pairs
Jul 6, 2025
Merged

Refine parameter parsing#57
leynos merged 7 commits into
mainfrom
codex/add-parse_type_expr-helper-and-update-parse_name_type_pairs

Conversation

@leynos
Copy link
Copy Markdown
Owner

@leynos leynos commented Jul 5, 2025

Summary

  • add parse_type_expr to recursively parse type expressions
  • refactor parse_name_type_pairs to use the new helper
  • document updated approach in the function parsing design guide

Testing

  • make fmt
  • make lint
  • make test

https://chatgpt.com/codex/tasks/task_e_686977f483dc83228f8534e94b29f356

Summary by Sourcery

Refine parameter parsing by extracting type expression parsing into parse_type_expr, simplifying name-type pair extraction with helper functions, and unifying error reporting using a new ParseError enum

New Features:

  • Add recursive parse_type_expr function for nested type expressions
  • Introduce collect_parameter_name and finalise_parameter helpers for parameter parsing
  • Add ParseError enum to unify delimiter mismatches and missing-colon errors

Enhancements:

  • Refactor parse_name_type_pairs to delegate type parsing and simplify main loop
  • Unify error collection to report empty names/types and missing colons with precise spans

Documentation:

  • Update function-parsing-design guide to document parse_type_expr integration and new parsing workflow

Tests:

  • Extend tests to cover nested generics, missing colons, empty names/types, and unclosed delimiters

@sourcery-ai
Copy link
Copy Markdown
Contributor

sourcery-ai Bot commented Jul 5, 2025

Reviewer's Guide

This PR extracts recursive type parsing into a dedicated parse_type_expr helper, refactors parse_name_type_pairs to delegate type parsing (removing manual delimiter tracking and legacy utilities), introduces a unified ParseError enum for error reporting, and updates the design guide accordingly.

File-Level Changes

Change Details Files
Introduce ParseError enum and unify error reporting
  • Define ParseError with variants for delimiters and missing colons
  • Implement Display and Error for ParseError
  • Replace DelimiterError vectors and function signatures to use ParseError
src/parser/ast/parse_utils.rs
Add parse_type_expr for recursive type parsing
  • Implement delimiter-aware loop to consume type tokens until comma or closing parenthesis
  • Filter out whitespace and comment nodes when building the type string
  • Report unclosed delimiters in the returned error list
src/parser/ast/parse_utils.rs
Refactor parse_name_type_pairs to use focused helpers
  • Remove handle_token and finalize_pair implementations
  • Add collect_parameter_name and finalise_parameter to isolate name capture and type parsing
  • Simplify main loop to skip whitespace, collect names, delegate to parse_type_expr, and handle commas/closing paren
  • Enhance missing colon and empty name/type error handling
src/parser/ast/parse_utils.rs
Update function parsing design documentation
  • Add parse_type_expr to the class diagram and usage annotations
  • Describe the delegation-based parsing approach and new error behaviors
  • Explain helper roles and how missing colons or empty fields are reported
docs/function-parsing-design.md

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jul 5, 2025

Warning

Rate limit exceeded

@leynos has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 1 minutes and 30 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 184affb and b043ca6.

📒 Files selected for processing (2)
  • docs/function-parsing-design.md (1 hunks)
  • src/parser/ast/parse_utils.rs (7 hunks)

Summary by CodeRabbit

  • Documentation

    • Updated documentation to explain improved parameter parsing logic, including new error handling for missing colons and delimiter mismatches.
    • Added details on new helper functions and revised class diagrams.
  • Bug Fixes

    • Improved error reporting for missing colons and delimiter mismatches in parameter lists.
    • Ensured empty parameter names or types are now correctly reported as errors.
  • Refactor

    • Simplified and modularised parameter parsing for better clarity and maintainability.

Summary by CodeRabbit

  • Documentation

    • Updated design documentation to describe the new helper function for parsing type expressions and revised the explanation of parameter list parsing.
  • Refactor

    • Simplified and clarified the logic for parsing name-type pairs, improving error tracking and control flow.
  • New Features

    • Introduced a new helper for parsing type expressions, enhancing support for nested delimiters and improving parsing accuracy.

Walkthrough

The parsing logic for extracting name-type parameter pairs was refactored. A new helper function, parse_type_expr, was introduced to handle type expression parsing with nested delimiters. Documentation and diagrams were updated to reflect this new control flow, and the main parsing loop was simplified by delegating delimiter management to the helper.

Changes

File(s) Change Summary
docs/function-parsing-design.md Updated documentation and class diagram to describe and include parse_type_expr in the design.
src/parser/ast/parse_utils.rs Refactored parse_name_type_pairs to use a new parse_type_expr helper, simplifying delimiter handling and error tracking. Added ParseError enum and helper functions collect_parameter_name and finalise_parameter. Removed previous token handler.

Sequence Diagram(s)

sequenceDiagram
    participant Caller
    participant parse_name_type_pairs
    participant parse_type_expr

    Caller->>parse_name_type_pairs: Call with token iterator
    parse_name_type_pairs->>parse_type_expr: For each ':' after a name, call to parse type expr
    parse_type_expr-->>parse_name_type_pairs: Return type string & delimiter errors
    parse_name_type_pairs-->>Caller: Return list of (name, type) pairs & errors
Loading

Possibly related PRs

Poem

In the warren of code, a helper appears,
To parse nested types and quiet our fears.
No more stacks of delimiters deep,
Now parse_type_expr takes the leap!
With pairs parsed clean and errors in tow,
The parser hops on, ready to go.
🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate Unit Tests
  • Create PR with Unit Tests
  • Post Copyable Unit Tests in a Comment
  • Commit Unit Tests in branch codex/add-parse_type_expr-helper-and-update-parse_name_type_pairs

🪧 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.
    • Explain this complex logic.
    • 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. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • 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 src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

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

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai auto-generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

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

Documentation and Community

  • 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.

Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey @leynos - I've reviewed your changes and they look great!

Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments

### Comment 1
<location> `src/parser/ast/parse_utils.rs:265` </location>
<code_context>
-            NodeOrToken::Node(n) => buf.push_str(&n.text().to_string()),
+        }
+
+        if !found_colon {
+            // Skip until comma or closing paren.
+            while let Some(e) = iter.peek() {
</code_context>

<issue_to_address>
Skipping to the next comma or closing parenthesis may mask errors.

Currently, missing colons are not reported, which may hide malformed input. Please add an error to the errors vector when this occurs to improve error visibility.

Suggested implementation:

```rust
        if !found_colon {
            // Skip until comma or closing paren.
            let mut skipped = false;
            while let Some(e) = iter.peek() {

```

```rust
            while let Some(e) = iter.peek() {

```

```rust
                match e {
                    NodeOrToken::Token(t) => match t.kind() {
                        SyntaxKind::T_COLON => {
                            iter.next();
                            found_colon = true;
                            break;
                        }
                        SyntaxKind::T_COMMA | SyntaxKind::T_RPAREN => {
                            skipped = true;
                            break;
                        }
                        _ => {
                            name_buf.push_str(t.text());

```

```rust
            }
            if skipped && !found_colon {
                errors.push(ParseError::MissingColon {
                    message: "Expected ':' after name, but found ',' or ')' instead".to_string(),
                    span: /* TODO: set appropriate span here */
                });
            }
        }

```

- You will need to ensure that `ParseError::MissingColon` exists and takes a `message` and `span`. If not, define it accordingly.
- Replace the `span: /* TODO: set appropriate span here */` with the correct span for the error location, possibly using the last token or node processed.
- Make sure the `errors` vector is in scope and mutable.
</issue_to_address>

### Comment 2
<location> `src/parser/ast/parse_utils.rs:290` </location>
<code_context>
+        let (ty, mut errs) = parse_type_expr(&mut iter);
+        errors.append(&mut errs);
+
+        if !name.is_empty() && !ty.is_empty() {
+            pairs.push((name, ty));
+        }
</code_context>

<issue_to_address>
Silently skipping empty names or types may hide input errors.

Consider appending an error to the errors vector when name or type is empty, so these cases are visible to the caller.
</issue_to_address>

<suggested_fix>
<<<<<<< SEARCH
        let (ty, mut errs) = parse_type_expr(&mut iter);
        errors.append(&mut errs);

        if !name.is_empty() && !ty.is_empty() {
            pairs.push((name, ty));
        }
=======
        let (ty, mut errs) = parse_type_expr(&mut iter);
        errors.append(&mut errs);

        if name.is_empty() {
            errors.push(ParseError::new("Field name is empty"));
        }
        if ty.is_empty() {
            errors.push(ParseError::new("Field type is empty"));
        }
        if !name.is_empty() && !ty.is_empty() {
            pairs.push((name, ty));
        }
>>>>>>> REPLACE

</suggested_fix>

### Comment 3
<location> `src/parser/ast/parse_utils.rs:333` </location>
<code_context>
+/// token that ended the type expression so the caller can consume the comma or
+/// closing parenthesis as appropriate.
+#[must_use]
+pub(super) fn parse_type_expr<I>(iter: &mut std::iter::Peekable<I>) -> (String, Vec<DelimiterError>)
+where
+    I: Iterator<Item = SyntaxElement<DdlogLanguage>>,
</code_context>

<issue_to_address>
parse_type_expr does not check for unclosed delimiters at the end.

Check DelimStack for any remaining unclosed delimiters after the loop and report them as errors.

Suggested implementation:

```rust
    // ... parsing logic ...
    // (Assume there is a DelimStack named `delim_stack` and an error vector named `errors`)
    // ... main parsing loop ...

    // After parsing, check for any unclosed delimiters and report them as errors.
    while let Some(unclosed) = delim_stack.pop() {
        errors.push(DelimiterError::Unclosed {
            delimiter: unclosed.delimiter,
            position: unclosed.position,
        });
    }

```

- If the DelimStack or DelimiterError types are not already defined to support this, you may need to ensure that:
  - DelimStack has a `pop()` method that returns information about the unclosed delimiter (such as its type and position).
  - DelimiterError has a variant like `Unclosed { delimiter, position }`.
- Make sure the `errors` vector is returned as part of the function's return value.
</issue_to_address>

### Comment 4
<location> `src/parser/ast/parse_utils.rs:404` </location>
<code_context>
+                }
+            },
+            NodeOrToken::Node(n) => {
+                buf.push_str(&n.text().to_string());
+                iter.next();
             }
</code_context>

<issue_to_address>
Directly pushing node text may conflate whitespace or comments with type content.

Filter out nodes containing only whitespace or comments before appending their text to avoid including irrelevant content in the type string.
</issue_to_address>

<suggested_fix>
<<<<<<< SEARCH
            NodeOrToken::Node(n) => {
+                buf.push_str(&n.text().to_string());
+                iter.next();
=======
            NodeOrToken::Node(n) => {
+                // Only push node text if it is not just whitespace or a comment
+                let text = n.text();
+                let is_whitespace = text.chars().all(|c| c.is_whitespace());
+                let is_comment = n.kind() == SyntaxKind::COMMENT;
+                if !is_whitespace && !is_comment {
+                    buf.push_str(&text.to_string());
+                }
+                iter.next();
>>>>>>> REPLACE

</suggested_fix>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/parser/ast/parse_utils.rs Outdated
Comment thread src/parser/ast/parse_utils.rs Outdated
Comment thread src/parser/ast/parse_utils.rs Outdated
Comment on lines +403 to +405
NodeOrToken::Node(n) => {
buf.push_str(&n.text().to_string());
iter.next();
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.

suggestion (bug_risk): Directly pushing node text may conflate whitespace or comments with type content.

Filter out nodes containing only whitespace or comments before appending their text to avoid including irrelevant content in the type string.

Suggested change
NodeOrToken::Node(n) => {
buf.push_str(&n.text().to_string());
iter.next();
NodeOrToken::Node(n) => {
+ // Only push node text if it is not just whitespace or a comment
+ let text = n.text();
+ let is_whitespace = text.chars().all(|c| c.is_whitespace());
+ let is_comment = n.kind() == SyntaxKind::COMMENT;
+ if !is_whitespace && !is_comment {
+ buf.push_str(&text.to_string());
+ }
+ iter.next();

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🔭 Outside diff range comments (1)
src/parser/ast/parse_utils.rs (1)

199-209: Fix documentation example compilation issues.

The documentation example contains several issues that would prevent compilation:

  • Incorrect module paths in use statements
  • Missing generic parameter specification
  • Unrealistic comment placeholders
-/// use parser::ast::parse_utils::{parse_name_type_pairs, DelimiterError};
-/// use parser::ast::DdlogLanguage;
-/// use parser::syntax::{SyntaxElement, SyntaxKind};
+/// use crate::parser::ast::parse_utils::{parse_name_type_pairs, DelimiterError};
+/// use crate::DdlogLanguage;
+/// use rowan::SyntaxElement;
 ///
-/// // Example: parsing a parameter list "(x: int, y: string)"
-/// let tokens: Vec<SyntaxElement<DdlogLanguage>> = /* token stream for "(x: int, y: string)" */;
-/// let (pairs, errors): (Vec<(String, String)>, Vec<DelimiterError>) = parse_name_type_pairs(tokens.into_iter());
-/// assert_eq!(pairs, vec![("x".to_string(), "int".to_string()), ("y".to_string(), "string".to_string())]);
-/// assert!(errors.is_empty());
+/// ```ignore
+/// // Example: parsing a parameter list "(x: int, y: string)"
+/// // Note: This example requires a parsed syntax tree to obtain tokens
+/// let tokens: Vec<SyntaxElement<DdlogLanguage>> = get_tokens_from_source("(x: int, y: string)");
+/// let (pairs, errors) = parse_name_type_pairs(tokens.into_iter());
+/// assert_eq!(pairs, vec![("x".to_string(), "int".to_string()), ("y".to_string(), "string".to_string())]);
+/// assert!(errors.is_empty());
+/// ```
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 526d1fe and 3ad9ff8.

📒 Files selected for processing (2)
  • docs/function-parsing-design.md (1 hunks)
  • src/parser/ast/parse_utils.rs (3 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
`docs/**/*.md`: Documentation must use en-GB-oxendict spelling and grammar (with the exception of 'license' which is to be left unchanged for community consistency).

docs/**/*.md: Documentation must use en-GB-oxendict spelling and grammar (with the exception of 'license' which is to be left unchanged for community consistency).

📄 Source: CodeRabbit Inference Engine (AGENTS.md)

List of files the instruction was applied to:

  • docs/function-parsing-design.md
`**/*.md`: Markdown paragraphs and bullet points must be wrapped at 80 columns. Code blocks must be wrapped at 120 columns. Tables and headings must not be wrapped.

**/*.md: Markdown paragraphs and bullet points must be wrapped at 80 columns.
Code blocks must be wrapped at 120 columns.
Tables and headings must not be wrapped.

📄 Source: CodeRabbit Inference Engine (AGENTS.md)

List of files the instruction was applied to:

  • docs/function-parsing-design.md
`**/*.md`: * Avoid 2nd person or 1st person pronouns ("I", "you", "we") * Use en...

**/*.md: * Avoid 2nd person or 1st person pronouns ("I", "you", "we")

  • Use en-oxendic spelling and grammar.
  • Paragraphs and bullets must be wrapped to 80 columns, except where a long URL would prevent this (in which case, silence MD013 for that line)
  • Code blocks should be wrapped to 120 columns.
  • Headings must not be wrapped.

⚙️ Source: CodeRabbit Configuration File

List of files the instruction was applied to:

  • docs/function-parsing-design.md
`**/*.rs`: Comment why, not what. Explain assumptions, edge cases, trade-offs, o...

**/*.rs: Comment why, not what. Explain assumptions, edge cases, trade-offs, or complexity. Don't echo the obvious.
Comments must use en-GB-oxendict spelling and grammar.
Function documentation must include clear examples.
Name things precisely. Use clear, descriptive variable and function names. For booleans, prefer names with is, has, or should.
Each file should encapsulate a coherent module. Group related code (e.g., models + utilities + fixtures) close together.
Group by feature, not layer. Colocate views, logic, fixtures, and helpers related to a domain concept rather than splitting by type.
Every module must begin with a module level (//! ) comment explaining the module's purpose and utility.
Document public APIs using Rustdoc comments (///) so documentation can be generated with cargo doc.
Place function attributes after doc comments.
Do not use return in single-line functions.
Prefer immutable data and avoid unnecessary mut bindings.
Handle errors with the Result type instead of panicking where feasible.
Avoid unsafe code unless absolutely necessary and document any usage clearly.
Clippy warnings MUST be disallowed.
Lints must not be silenced except as a last resort.
Lint rule suppressions must be tightly scoped and include a clear reason.
Prefer expect over allow.
Use predicate functions for conditional criteria with more than two branches.
Where a function is too long, extract meaningfully named helper functions adhering to separation of concerns and CQRS.
Where a function has too many parameters, group related parameters in meaningfully named structs.
Where a function is returning a large error consider using Arc to reduce the amount of data returned.
Prefer .expect() over .unwrap().

📄 Source: CodeRabbit Inference Engine (AGENTS.md)

List of files the instruction was applied to:

  • src/parser/ast/parse_utils.rs
`**/*.rs`: * Seek to keep the cyclomatic complexity of functions no more than 12...

**/*.rs: * Seek to keep the cyclomatic complexity of functions no more than 12.

  • Adhere to single responsibility and CQRS

  • Place function attributes after doc comments.

  • Do not use return in single-line functions.

  • Move conditionals with >2 branches into a predicate function.

  • Avoid unsafe unless absolutely necessary.

  • Every module must begin with a //! doc comment that explains the module's purpose and utility.

  • Comments must use en-GB-oxendict spelling and grammar.

  • Lints must not be silenced except as a last resort.

    • #[allow] is forbidden.
    • Only narrowly scoped #[expect(lint, reason = "...")] is allowed.
    • No lint groups, no blanket or file-wide suppression.
    • Include FIXME: with link if a fix is expected.
  • Use rstest fixtures for shared setup and to avoid repetition between tests.

  • Replace duplicated tests with #[rstest(...)] parameterised cases.

  • Prefer mockall for mocks/stubs.

  • Prefer .expect() over .unwrap()

  • Ensure that any API or behavioural changes are reflected in the documentation in docs/

  • Ensure that any completed roadmap steps are recorded in the appropriate roadmap in docs/

⚙️ Source: CodeRabbit Configuration File

List of files the instruction was applied to:

  • src/parser/ast/parse_utils.rs
🪛 LanguageTool
docs/function-parsing-design.md

[uncategorized] ~29-~29: Possible missing comma found.
Context: ...he parameter list. Each time a colon is encountered the function delegates to `parse_type_e...

(AI_HYDRA_LEO_MISSING_COMMA)


[uncategorized] ~31-~31: Use a comma before ‘so’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...per recursively parses nested delimiters so constructs like `Vec<Map<string, Vec<u8...

(COMMA_COMPOUND_SENTENCE_2)

⏰ Context from checks skipped due to timeout of 240000ms (1)
  • GitHub Check: build-test
🔇 Additional comments (2)
src/parser/ast/parse_utils.rs (2)

343-408: The function handles delimiter nesting correctly.

The parse_type_expr function properly manages nested delimiters using the DelimStack and handles various delimiter types including the complex case of shift operators (<< and >>). The logic correctly breaks on commas and closing parentheses when at the outermost level.


514-540: Excellent test coverage with comprehensive edge cases.

The test suite covers a wide range of scenarios including:

  • Basic parameter parsing
  • Nested generic types
  • Complex nested structures
  • Missing colons
  • Empty parameter lists
  • Various delimiter combinations

This thorough testing approach aligns well with the coding guidelines and provides confidence in the refactoring.

Comment thread docs/function-parsing-design.md Outdated
Comment thread docs/function-parsing-design.md Outdated
Comment thread src/parser/ast/parse_utils.rs
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
docs/function-parsing-design.md (2)

29-29: Add missing comma for clarity.

The sentence would benefit from a comma after "encountered" to improve readability and grammatical correctness.

-Each time a colon is encountered the function delegates to `parse_type_expr` to
+Each time a colon is encountered, the function delegates to `parse_type_expr` to

31-31: Add comma before coordinating conjunction.

The sentence connects two independent clauses with "so" and would benefit from a comma for proper grammar.

-delimiters so constructs like `Vec<Map<string, Vec<u8>>>` are handled without
+delimiters, so constructs like `Vec<Map<string, Vec<u8>>>` are handled without
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 3ad9ff8 and b78cf11.

📒 Files selected for processing (2)
  • docs/function-parsing-design.md (1 hunks)
  • src/parser/ast/parse_utils.rs (5 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
`docs/**/*.md`: Documentation must use en-GB-oxendict spelling and grammar (with the exception of 'license' which is to be left unchanged for community consistency).

docs/**/*.md: Documentation must use en-GB-oxendict spelling and grammar (with the exception of 'license' which is to be left unchanged for community consistency).

📄 Source: CodeRabbit Inference Engine (AGENTS.md)

List of files the instruction was applied to:

  • docs/function-parsing-design.md
`**/*.md`: Markdown paragraphs and bullet points must be wrapped at 80 columns. Code blocks must be wrapped at 120 columns. Tables and headings must not be wrapped.

**/*.md: Markdown paragraphs and bullet points must be wrapped at 80 columns.
Code blocks must be wrapped at 120 columns.
Tables and headings must not be wrapped.

📄 Source: CodeRabbit Inference Engine (AGENTS.md)

List of files the instruction was applied to:

  • docs/function-parsing-design.md
`**/*.md`: * Avoid 2nd person or 1st person pronouns ("I", "you", "we") * Use en...

**/*.md: * Avoid 2nd person or 1st person pronouns ("I", "you", "we")

  • Use en-oxendic spelling and grammar.
  • Paragraphs and bullets must be wrapped to 80 columns, except where a long URL would prevent this (in which case, silence MD013 for that line)
  • Code blocks should be wrapped to 120 columns.
  • Headings must not be wrapped.

⚙️ Source: CodeRabbit Configuration File

List of files the instruction was applied to:

  • docs/function-parsing-design.md
`**/*.rs`: Comment why, not what. Explain assumptions, edge cases, trade-offs, o...

**/*.rs: Comment why, not what. Explain assumptions, edge cases, trade-offs, or complexity. Don't echo the obvious.
Comments must use en-GB-oxendict spelling and grammar.
Function documentation must include clear examples.
Name things precisely. Use clear, descriptive variable and function names. For booleans, prefer names with is, has, or should.
Each file should encapsulate a coherent module. Group related code (e.g., models + utilities + fixtures) close together.
Group by feature, not layer. Colocate views, logic, fixtures, and helpers related to a domain concept rather than splitting by type.
Every module must begin with a module level (//! ) comment explaining the module's purpose and utility.
Document public APIs using Rustdoc comments (///) so documentation can be generated with cargo doc.
Place function attributes after doc comments.
Do not use return in single-line functions.
Prefer immutable data and avoid unnecessary mut bindings.
Handle errors with the Result type instead of panicking where feasible.
Avoid unsafe code unless absolutely necessary and document any usage clearly.
Clippy warnings MUST be disallowed.
Lints must not be silenced except as a last resort.
Lint rule suppressions must be tightly scoped and include a clear reason.
Prefer expect over allow.
Use predicate functions for conditional criteria with more than two branches.
Where a function is too long, extract meaningfully named helper functions adhering to separation of concerns and CQRS.
Where a function has too many parameters, group related parameters in meaningfully named structs.
Where a function is returning a large error consider using Arc to reduce the amount of data returned.
Prefer .expect() over .unwrap().

📄 Source: CodeRabbit Inference Engine (AGENTS.md)

List of files the instruction was applied to:

  • src/parser/ast/parse_utils.rs
`**/*.rs`: * Seek to keep the cyclomatic complexity of functions no more than 12...

**/*.rs: * Seek to keep the cyclomatic complexity of functions no more than 12.

  • Adhere to single responsibility and CQRS

  • Place function attributes after doc comments.

  • Do not use return in single-line functions.

  • Move conditionals with >2 branches into a predicate function.

  • Avoid unsafe unless absolutely necessary.

  • Every module must begin with a //! doc comment that explains the module's purpose and utility.

  • Comments must use en-GB-oxendict spelling and grammar.

  • Lints must not be silenced except as a last resort.

    • #[allow] is forbidden.
    • Only narrowly scoped #[expect(lint, reason = "...")] is allowed.
    • No lint groups, no blanket or file-wide suppression.
    • Include FIXME: with link if a fix is expected.
  • Use rstest fixtures for shared setup and to avoid repetition between tests.

  • Replace duplicated tests with #[rstest(...)] parameterised cases.

  • Prefer mockall for mocks/stubs.

  • Prefer .expect() over .unwrap()

  • Ensure that any API or behavioural changes are reflected in the documentation in docs/

  • Ensure that any completed roadmap steps are recorded in the appropriate roadmap in docs/

⚙️ Source: CodeRabbit Configuration File

List of files the instruction was applied to:

  • src/parser/ast/parse_utils.rs
🧬 Code Graph Analysis (1)
src/parser/ast/parse_utils.rs (1)
src/parser/mod.rs (1)
  • skip_whitespace_and_comments (1252-1262)
🪛 LanguageTool
docs/function-parsing-design.md

[uncategorized] ~29-~29: Possible missing comma found.
Context: ...he parameter list. Each time a colon is encountered the function delegates to `parse_type_e...

(AI_HYDRA_LEO_MISSING_COMMA)


[uncategorized] ~31-~31: Use a comma before ‘so’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...per recursively parses nested delimiters so constructs like `Vec<Map<string, Vec<u8...

(COMMA_COMPOUND_SENTENCE_2)


[uncategorized] ~37-~37: Use a comma before ‘so’ if it connects two independent clauses (unless they are closely connected and short).
Context: ... or parenthesis that ended the parameter so diagnostics can highlight the exact loc...

(COMMA_COMPOUND_SENTENCE_2)


[style] ~38-~38: Would you like to use the Oxford spelling “finalize”? The spelling ‘finalise’ is also correct.
Context: ... functions collect_parameter_name and finalise_parameter keep the main loop small by ...

(OXFORD_SPELLING_Z_NOT_S)


[uncategorized] ~40-~40: Possible missing comma found.
Context: ...ll by handling name collection and type parsing respectively.

(AI_HYDRA_LEO_MISSING_COMMA)

🔇 Additional comments (9)
docs/function-parsing-design.md (3)

14-16: LGTM: Diagram accurately reflects the new helper function.

The addition of parse_type_expr to the class diagram correctly represents the new parsing architecture.


21-21: LGTM: Relationship properly documented.

The diagram correctly shows that parse_name_type_pairs now uses parse_type_expr.


40-40: Add comma for clarity.

The sentence would benefit from a comma after "collection" to improve readability.

-by handling name collection and type parsing respectively.
+by handling name collection, and type parsing respectively.

Likely an incorrect or invalid review comment.

src/parser/ast/parse_utils.rs (6)

41-59: Excellent error handling improvement.

The new ParseError enum provides better error categorisation by wrapping both delimiter errors and missing colon errors. The Display implementation ensures clear error messages.


207-245: Well-implemented helper function.

The collect_parameter_name function is well-designed:

  • Properly handles different token types
  • Tracks span information for error reporting
  • Returns clear state about colon presence
  • Trims whitespace appropriately

247-292: Robust parameter finalisation logic.

The finalise_parameter function effectively handles the complexity of parameter validation:

  • Provides clear error messages for missing colons with precise span information
  • Properly skips invalid tokens when errors occur
  • Validates both name and type are non-empty before creating pairs
  • Delegates type parsing to the dedicated helper function

294-384: Excellent refactoring that reduces complexity.

The rewritten parse_name_type_pairs function is much cleaner:

  • Uses helper functions to reduce cyclomatic complexity
  • Clear control flow with explicit loop structure
  • Proper error handling throughout
  • Maintains the same functionality whilst being more readable

386-472: Comprehensive type expression parsing.

The new parse_type_expr function handles nested delimiters excellently:

  • Supports all delimiter types (parentheses, angle brackets, square brackets, braces)
  • Properly handles multi-character tokens like << and >>
  • Uses delimiter stack for correct nesting tracking
  • Stops at appropriate boundaries (comma or closing parenthesis at top level)
  • Returns trimmed result for consistency

575-632: Test updates properly reflect the new error handling.

The test cases have been appropriately updated:

  • Now check for specific error counts rather than just delimiter errors
  • Cover various scenarios including missing colons
  • Validate both successful parsing and error cases
  • Parameter names clearly indicate the expected behaviour

Comment thread docs/function-parsing-design.md Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
src/parser/ast/parse_utils.rs (2)

247-292: Consider reporting empty names or types as errors.

The function silently skips parameters with empty names or types (lines 287-291). As suggested in a previous review, consider appending errors for these cases to improve error visibility.

     skip_whitespace_and_comments(iter);
     let (ty, mut errs) = parse_type_expr(iter);
     errors.append(&mut errs);
 
+    if name.is_empty() && !ty.is_empty() {
+        if let Some(s) = span {
+            errors.push(ParseError::MissingColon {
+                message: "Parameter name is empty".to_string(),
+                span: s,
+            });
+        }
+    }
+    if !name.is_empty() && ty.is_empty() {
+        if let Some(s) = span {
+            errors.push(ParseError::MissingColon {
+                message: "Parameter type is empty".to_string(),
+                span: s,
+            });
+        }
+    }
+
     if !name.is_empty() && !ty.is_empty() {
         Some((name, ty))
     } else {
         None
     }

386-477: Check for unclosed delimiters after parsing.

The function doesn't report errors for any unclosed delimiters remaining in the stack after parsing completes. This was mentioned in a previous review and could hide syntax errors.

         }
     }
 
+    // Check for any unclosed delimiters
+    while let Some(unclosed) = depth.0.pop() {
+        // Create a synthetic error for unclosed delimiter
+        let delimiter_char = match unclosed {
+            Delim::Paren => ")",
+            Delim::Angle => ">",
+            Delim::Bracket => "]",
+            Delim::Brace => "}",
+        };
+        // Since we don't have the original opening position, we can at least indicate the type
+        errors.push(ParseError::MissingColon {
+            message: format!("Unclosed delimiter: expected '{}'", delimiter_char),
+            span: TextRange::empty(0.into()), // This should ideally be the position of the opening delimiter
+        });
+    }
+
     (buf.trim().to_string(), errors)
 }

Alternatively, consider enhancing DelimStack to track the position of opening delimiters for more accurate error reporting.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between b78cf11 and 9c35d62.

📒 Files selected for processing (2)
  • docs/function-parsing-design.md (1 hunks)
  • src/parser/ast/parse_utils.rs (5 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
`docs/**/*.md`: Documentation must use en-GB-oxendict spelling and grammar (with the exception of 'license' which is to be left unchanged for community consistency).

docs/**/*.md: Documentation must use en-GB-oxendict spelling and grammar (with the exception of 'license' which is to be left unchanged for community consistency).

📄 Source: CodeRabbit Inference Engine (AGENTS.md)

List of files the instruction was applied to:

  • docs/function-parsing-design.md
`**/*.md`: Markdown paragraphs and bullet points must be wrapped at 80 columns. Code blocks must be wrapped at 120 columns. Tables and headings must not be wrapped.

**/*.md: Markdown paragraphs and bullet points must be wrapped at 80 columns.
Code blocks must be wrapped at 120 columns.
Tables and headings must not be wrapped.

📄 Source: CodeRabbit Inference Engine (AGENTS.md)

List of files the instruction was applied to:

  • docs/function-parsing-design.md
`**/*.md`: * Avoid 2nd person or 1st person pronouns ("I", "you", "we") * Use en...

**/*.md: * Avoid 2nd person or 1st person pronouns ("I", "you", "we")

  • Use en-oxendic spelling and grammar.
  • Paragraphs and bullets must be wrapped to 80 columns, except where a long URL would prevent this (in which case, silence MD013 for that line)
  • Code blocks should be wrapped to 120 columns.
  • Headings must not be wrapped.

⚙️ Source: CodeRabbit Configuration File

List of files the instruction was applied to:

  • docs/function-parsing-design.md
`**/*.rs`: Comment why, not what. Explain assumptions, edge cases, trade-offs, o...

**/*.rs: Comment why, not what. Explain assumptions, edge cases, trade-offs, or complexity. Don't echo the obvious.
Comments must use en-GB-oxendict spelling and grammar.
Function documentation must include clear examples.
Name things precisely. Use clear, descriptive variable and function names. For booleans, prefer names with is, has, or should.
Each file should encapsulate a coherent module. Group related code (e.g., models + utilities + fixtures) close together.
Group by feature, not layer. Colocate views, logic, fixtures, and helpers related to a domain concept rather than splitting by type.
Every module must begin with a module level (//! ) comment explaining the module's purpose and utility.
Document public APIs using Rustdoc comments (///) so documentation can be generated with cargo doc.
Place function attributes after doc comments.
Do not use return in single-line functions.
Prefer immutable data and avoid unnecessary mut bindings.
Handle errors with the Result type instead of panicking where feasible.
Avoid unsafe code unless absolutely necessary and document any usage clearly.
Clippy warnings MUST be disallowed.
Lints must not be silenced except as a last resort.
Lint rule suppressions must be tightly scoped and include a clear reason.
Prefer expect over allow.
Use predicate functions for conditional criteria with more than two branches.
Where a function is too long, extract meaningfully named helper functions adhering to separation of concerns and CQRS.
Where a function has too many parameters, group related parameters in meaningfully named structs.
Where a function is returning a large error consider using Arc to reduce the amount of data returned.
Prefer .expect() over .unwrap().

📄 Source: CodeRabbit Inference Engine (AGENTS.md)

List of files the instruction was applied to:

  • src/parser/ast/parse_utils.rs
`**/*.rs`: * Seek to keep the cyclomatic complexity of functions no more than 12...

**/*.rs: * Seek to keep the cyclomatic complexity of functions no more than 12.

  • Adhere to single responsibility and CQRS

  • Place function attributes after doc comments.

  • Do not use return in single-line functions.

  • Move conditionals with >2 branches into a predicate function.

  • Avoid unsafe unless absolutely necessary.

  • Every module must begin with a //! doc comment that explains the module's purpose and utility.

  • Comments must use en-GB-oxendict spelling and grammar.

  • Lints must not be silenced except as a last resort.

    • #[allow] is forbidden.
    • Only narrowly scoped #[expect(lint, reason = "...")] is allowed.
    • No lint groups, no blanket or file-wide suppression.
    • Include FIXME: with link if a fix is expected.
  • Use rstest fixtures for shared setup and to avoid repetition between tests.

  • Replace duplicated tests with #[rstest(...)] parameterised cases.

  • Prefer mockall for mocks/stubs.

  • Prefer .expect() over .unwrap()

  • Ensure that any API or behavioural changes are reflected in the documentation in docs/

  • Ensure that any completed roadmap steps are recorded in the appropriate roadmap in docs/

⚙️ Source: CodeRabbit Configuration File

List of files the instruction was applied to:

  • src/parser/ast/parse_utils.rs
🪛 LanguageTool
docs/function-parsing-design.md

[style] ~38-~38: Would you like to use the Oxford spelling “finalize”? The spelling ‘finalise’ is also correct.
Context: ... functions collect_parameter_name and finalise_parameter keep the main loop small by ...

(OXFORD_SPELLING_Z_NOT_S)


[uncategorized] ~40-~40: Possible missing comma found.
Context: ...ll by handling name collection and type parsing respectively.

(AI_HYDRA_LEO_MISSING_COMMA)

🔇 Additional comments (3)
docs/function-parsing-design.md (1)

14-40: Documentation accurately reflects the implementation.

The updated documentation clearly explains the new delegation-based parsing strategy and the role of the parse_type_expr helper function. The explanation of how delimiter management is now handled by the helper is particularly clear.

src/parser/ast/parse_utils.rs (2)

41-60: Well-structured error handling improvements.

The new ParseError enum effectively combines delimiter and missing colon errors, providing clear error messages with precise span information. This addresses the previous concern about error visibility.


319-384: Excellent refactoring of the main parsing loop.

The use of helper functions has significantly improved code readability and maintainability. The separation of concerns between name collection, parameter finalisation, and delimiter handling makes the logic much clearer.

Comment thread src/parser/ast/parse_utils.rs
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 9c35d62 and 184affb.

📒 Files selected for processing (2)
  • docs/function-parsing-design.md (1 hunks)
  • src/parser/ast/parse_utils.rs (7 hunks)
🧰 Additional context used
📓 Path-based instructions (5)
`docs/**/*.md`: Documentation must use en-GB-oxendict spelling and grammar (with the exception of 'license' which is to be left unchanged for community consistency).

docs/**/*.md: Documentation must use en-GB-oxendict spelling and grammar (with the exception of 'license' which is to be left unchanged for community consistency).

📄 Source: CodeRabbit Inference Engine (AGENTS.md)

List of files the instruction was applied to:

  • docs/function-parsing-design.md
`**/*.md`: Markdown paragraphs and bullet points must be wrapped at 80 columns. Code blocks must be wrapped at 120 columns. Tables and headings must not be wrapped.

**/*.md: Markdown paragraphs and bullet points must be wrapped at 80 columns.
Code blocks must be wrapped at 120 columns.
Tables and headings must not be wrapped.

📄 Source: CodeRabbit Inference Engine (AGENTS.md)

List of files the instruction was applied to:

  • docs/function-parsing-design.md
`**/*.md`: * Avoid 2nd person or 1st person pronouns ("I", "you", "we") * Use en...

**/*.md: * Avoid 2nd person or 1st person pronouns ("I", "you", "we")

  • Use en-oxendic spelling and grammar.
  • Paragraphs and bullets must be wrapped to 80 columns, except where a long URL would prevent this (in which case, silence MD013 for that line)
  • Code blocks should be wrapped to 120 columns.
  • Headings must not be wrapped.

⚙️ Source: CodeRabbit Configuration File

List of files the instruction was applied to:

  • docs/function-parsing-design.md
`**/*.rs`: Comment why, not what. Explain assumptions, edge cases, trade-offs, o...

**/*.rs: Comment why, not what. Explain assumptions, edge cases, trade-offs, or complexity. Don't echo the obvious.
Comments must use en-GB-oxendict spelling and grammar.
Function documentation must include clear examples.
Name things precisely. Use clear, descriptive variable and function names. For booleans, prefer names with is, has, or should.
Each file should encapsulate a coherent module. Group related code (e.g., models + utilities + fixtures) close together.
Group by feature, not layer. Colocate views, logic, fixtures, and helpers related to a domain concept rather than splitting by type.
Every module must begin with a module level (//! ) comment explaining the module's purpose and utility.
Document public APIs using Rustdoc comments (///) so documentation can be generated with cargo doc.
Place function attributes after doc comments.
Do not use return in single-line functions.
Prefer immutable data and avoid unnecessary mut bindings.
Handle errors with the Result type instead of panicking where feasible.
Avoid unsafe code unless absolutely necessary and document any usage clearly.
Clippy warnings MUST be disallowed.
Lints must not be silenced except as a last resort.
Lint rule suppressions must be tightly scoped and include a clear reason.
Prefer expect over allow.
Use predicate functions for conditional criteria with more than two branches.
Where a function is too long, extract meaningfully named helper functions adhering to separation of concerns and CQRS.
Where a function has too many parameters, group related parameters in meaningfully named structs.
Where a function is returning a large error consider using Arc to reduce the amount of data returned.
Prefer .expect() over .unwrap().

📄 Source: CodeRabbit Inference Engine (AGENTS.md)

List of files the instruction was applied to:

  • src/parser/ast/parse_utils.rs
`**/*.rs`: * Seek to keep the cyclomatic complexity of functions no more than 12...

**/*.rs: * Seek to keep the cyclomatic complexity of functions no more than 12.

  • Adhere to single responsibility and CQRS

  • Place function attributes after doc comments.

  • Do not use return in single-line functions.

  • Move conditionals with >2 branches into a predicate function.

  • Avoid unsafe unless absolutely necessary.

  • Every module must begin with a //! doc comment that explains the module's purpose and utility.

  • Comments must use en-GB-oxendict spelling and grammar.

  • Lints must not be silenced except as a last resort.

    • #[allow] is forbidden.
    • Only narrowly scoped #[expect(lint, reason = "...")] is allowed.
    • No lint groups, no blanket or file-wide suppression.
    • Include FIXME: with link if a fix is expected.
  • Use rstest fixtures for shared setup and to avoid repetition between tests.

  • Replace duplicated tests with #[rstest(...)] parameterised cases.

  • Prefer mockall for mocks/stubs.

  • Prefer .expect() over .unwrap()

  • Ensure that any API or behavioural changes are reflected in the documentation in docs/

  • Ensure that any completed roadmap steps are recorded in the appropriate roadmap in docs/

⚙️ Source: CodeRabbit Configuration File

List of files the instruction was applied to:

  • src/parser/ast/parse_utils.rs
🪛 LanguageTool
docs/function-parsing-design.md

[style] ~38-~38: Would you like to use the Oxford spelling “finalize”? The spelling ‘finalise’ is also correct.
Context: ... functions collect_parameter_name and finalise_parameter keep the main loop small by ...

(OXFORD_SPELLING_Z_NOT_S)


[uncategorized] ~40-~40: Possible missing comma found.
Context: ...ll by handling name collection and type parsing respectively. Empty names or types are...

(AI_HYDRA_LEO_MISSING_COMMA)


[uncategorized] ~42-~42: A comma might be missing here.
Context: ...types are reported using the same error variant so incomplete declarations do not pass ...

(AI_EN_LECTOR_MISSING_PUNCTUATION_COMMA)


[uncategorized] ~44-~44: Possible missing comma found.
Context: ... comment nodes when assembling the type string and reports any delimiters left unclose...

(AI_HYDRA_LEO_MISSING_COMMA)

🔇 Additional comments (3)
src/parser/ast/parse_utils.rs (3)

41-60: Well-structured error enum implementation.

The ParseError enum properly encapsulates both delimiter and missing colon errors with appropriate Display and Error trait implementations.


207-262: Excellent implementation of parameter name collection.

The function properly tracks the full parameter span by recording start and end positions, addressing the previous concern about span tracking. The logic correctly handles all termination cases.


374-403: Clean refactoring with improved modularity.

The main parsing loop is now much clearer with the extraction of collect_parameter_name and finalise_parameter helper functions. This addresses the previous concern about high cyclomatic complexity.

Comment thread src/parser/ast/parse_utils.rs
Comment thread src/parser/ast/parse_utils.rs
Comment thread docs/function-parsing-design.md Outdated
@leynos
Copy link
Copy Markdown
Owner Author

leynos commented Jul 5, 2025

@sourcery-ai review

Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey @leynos - I've reviewed your changes - here's some feedback:

  • The use of ParseError::MissingColon to report unclosed delimiters in parse_type_expr is semantically misleading—consider emitting the Delimiter variant or introducing a dedicated UnclosedDelimiter variant for unmatched delimiters.
  • finalise_parameter currently reuses the MissingColon variant for empty names, empty types, and missing colons; adding distinct ParseError variants (e.g. MissingName, MissingType) would clarify which error actually occurred.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The use of ParseError::MissingColon to report unclosed delimiters in parse_type_expr is semantically misleading—consider emitting the Delimiter variant or introducing a dedicated UnclosedDelimiter variant for unmatched delimiters.
- finalise_parameter currently reuses the MissingColon variant for empty names, empty types, and missing colons; adding distinct ParseError variants (e.g. MissingName, MissingType) would clarify which error actually occurred.

## Individual Comments

### Comment 1
<location> `src/parser/ast/parse_utils.rs:304` </location>
<code_context>
+    let (ty, mut errs) = parse_type_expr(iter);
+    errors.append(&mut errs);
+
+    if name.is_empty()
+        && !ty.is_empty()
+        && let Some(s) = span
+    {
+        errors.push(ParseError::MissingColon {
+            message: "Parameter name is empty".to_string(),
+            span: s,
+        });
+    }
+    if !name.is_empty()
+        && ty.is_empty()
+        && let Some(s) = span
</code_context>

<issue_to_address>
Empty name or type errors are always reported as MissingColon, which may be misleading.

Consider defining a distinct error variant for empty names or types to make error reporting more accurate.

Suggested implementation:

```rust
        errors.push(ParseError::EmptyParameterName {
            message: "Parameter name is empty".to_string(),
            span: s,
        });

```

```rust
    if !name.is_empty()
        && ty.is_empty()
        && let Some(s) = span
    {
        errors.push(ParseError::EmptyParameterType {
            message: "Parameter type is empty".to_string(),
            span: s,
        });
    }

```

You must also update the `ParseError` enum (likely in `src/parser/ast/mod.rs` or a similar file) to add the following variants:

```rust
EmptyParameterName { message: String, span: Span },
EmptyParameterType { message: String, span: Span },
```

If there are match arms or error formatting logic elsewhere that expects only `MissingColon`, you should update those to handle the new variants as well.
</issue_to_address>

### Comment 2
<location> `src/parser/ast/parse_utils.rs:513` </location>
<code_context>
-            pairs.push((n, ty.to_string()));
-        }
+    // Report any unclosed delimiters that remain on the stack.
+    while let Some(unclosed) = depth.0.pop() {
+        let delimiter_char = match unclosed {
+            Delim::Paren => ")",
+            Delim::Angle => ">",
+            Delim::Bracket => "]",
+            Delim::Brace => "}",
+        };
+        errors.push(ParseError::MissingColon {
+            message: format!("Unclosed delimiter: expected '{delimiter_char}'"),
+            span: TextRange::empty(0.into()),
</code_context>

<issue_to_address>
Unclosed delimiter errors are reported as MissingColon, which may not be accurate.

Consider introducing a specific ParseError variant for unclosed delimiters to improve error clarity and diagnostic accuracy.

Suggested implementation:

```rust
        errors.push(ParseError::UnclosedDelimiter {
            delimiter: delimiter_char.to_string(),
            span: TextRange::empty(0.into()),
        });

```

You must also add a new variant to the `ParseError` enum, likely in `src/parser/ast/mod.rs` or wherever `ParseError` is defined:

```rust
UnclosedDelimiter {
    delimiter: String,
    span: TextRange,
}
```

If you have custom error formatting or display logic, update it to handle the new `UnclosedDelimiter` variant and print a helpful message (e.g., "Unclosed delimiter: expected '{delimiter}'").
</issue_to_address>

### Comment 3
<location> `src/parser/ast/parse_utils.rs:502` </location>
<code_context>
+            },
+            NodeOrToken::Node(n) => {
+                let text = n.text().to_string();
+                let is_whitespace = text.chars().all(char::is_whitespace);
+                let is_comment = n.kind() == SyntaxKind::T_COMMENT;
+                if !is_whitespace && !is_comment {
+                    buf.push_str(&text);
</code_context>

<issue_to_address>
Whitespace and comments are filtered out in parse_type_expr, but not in collect_parameter_name.

This inconsistency may result in parameter names with unwanted whitespace or comments. Please align the handling of whitespace and comments in both functions.
</issue_to_address>

<suggested_fix>
<<<<<<< SEARCH
            NodeOrToken::Node(n) => {
                let text = n.text().to_string();
                let is_whitespace = text.chars().all(char::is_whitespace);
                let is_comment = n.kind() == SyntaxKind::T_COMMENT;
                if !is_whitespace && !is_comment {
                    buf.push_str(&text);
                }
                iter.next();
            }
=======
            NodeOrToken::Node(n) => {
                // Skip whitespace and comment nodes for consistency with parse_type_expr
                let text = n.text();
                if n.kind() == SyntaxKind::T_COMMENT || text.chars().all(char::is_whitespace) {
                    iter.next();
                    continue;
                }
                buf.push_str(text);
                iter.next();
            }
>>>>>>> REPLACE

</suggested_fix>

### Comment 4
<location> `src/parser/ast/parse_utils.rs:41` </location>
<code_context>
     span: TextRange,
 }

+/// Error types produced when parsing name-type pairs.
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub(super) enum ParseError {
</code_context>

<issue_to_address>
The module is missing a `//!` module-level comment at the top.

Please add a `//!` doc comment at the very top of the file to describe the module's purpose, as required by the review instructions.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +304 to +313
if name.is_empty()
&& !ty.is_empty()
&& let Some(s) = span
{
errors.push(ParseError::MissingColon {
message: "Parameter name is empty".to_string(),
span: s,
});
}
if !name.is_empty()
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.

suggestion: Empty name or type errors are always reported as MissingColon, which may be misleading.

Consider defining a distinct error variant for empty names or types to make error reporting more accurate.

Suggested implementation:

        errors.push(ParseError::EmptyParameterName {
            message: "Parameter name is empty".to_string(),
            span: s,
        });
    if !name.is_empty()
        && ty.is_empty()
        && let Some(s) = span
    {
        errors.push(ParseError::EmptyParameterType {
            message: "Parameter type is empty".to_string(),
            span: s,
        });
    }

You must also update the ParseError enum (likely in src/parser/ast/mod.rs or a similar file) to add the following variants:

EmptyParameterName { message: String, span: Span },
EmptyParameterType { message: String, span: Span },

If there are match arms or error formatting logic elsewhere that expects only MissingColon, you should update those to handle the new variants as well.

{
if !found_colon {
if let Some(s) = span {
errors.push(ParseError::MissingColon {
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.

suggestion: Unclosed delimiter errors are reported as MissingColon, which may not be accurate.

Consider introducing a specific ParseError variant for unclosed delimiters to improve error clarity and diagnostic accuracy.

Suggested implementation:

        errors.push(ParseError::UnclosedDelimiter {
            delimiter: delimiter_char.to_string(),
            span: TextRange::empty(0.into()),
        });

You must also add a new variant to the ParseError enum, likely in src/parser/ast/mod.rs or wherever ParseError is defined:

UnclosedDelimiter {
    delimiter: String,
    span: TextRange,
}

If you have custom error formatting or display logic, update it to handle the new UnclosedDelimiter variant and print a helpful message (e.g., "Unclosed delimiter: expected '{delimiter}'").

Comment on lines +500 to 508
NodeOrToken::Node(n) => {
let text = n.text().to_string();
let is_whitespace = text.chars().all(char::is_whitespace);
let is_comment = n.kind() == SyntaxKind::T_COMMENT;
if !is_whitespace && !is_comment {
buf.push_str(&text);
}
iter.next();
}
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.

suggestion: Whitespace and comments are filtered out in parse_type_expr, but not in collect_parameter_name.

This inconsistency may result in parameter names with unwanted whitespace or comments. Please align the handling of whitespace and comments in both functions.

Suggested change
NodeOrToken::Node(n) => {
let text = n.text().to_string();
let is_whitespace = text.chars().all(char::is_whitespace);
let is_comment = n.kind() == SyntaxKind::T_COMMENT;
if !is_whitespace && !is_comment {
buf.push_str(&text);
}
iter.next();
}
NodeOrToken::Node(n) => {
// Skip whitespace and comment nodes for consistency with parse_type_expr
let text = n.text();
if n.kind() == SyntaxKind::T_COMMENT || text.chars().all(char::is_whitespace) {
iter.next();
continue;
}
buf.push_str(text);
iter.next();
}

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

New GitHub issue for this, please

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.

I have created an issue for your comment: #61

Comment thread src/parser/ast/parse_utils.rs
@leynos leynos merged commit fd98f09 into main Jul 6, 2025
2 checks passed
@leynos leynos deleted the codex/add-parse_type_expr-helper-and-update-parse_name_type_pairs branch July 6, 2025 00:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant