Skip to content

Refactored expr analysis and fixed command multiline bug#108

Merged
KrosFire merged 2 commits intomainfrom
improve-analysis-functions
Feb 23, 2026
Merged

Refactored expr analysis and fixed command multiline bug#108
KrosFire merged 2 commits intomainfrom
improve-analysis-functions

Conversation

@KrosFire
Copy link
Member

@KrosFire KrosFire commented Feb 22, 2026

Summary by CodeRabbit

  • Improvements

    • Command parsing now accepts backslash and dash characters in command strings for more flexible syntax.
    • Expression analysis and type-checking logic made more consistent, reducing duplicated behavior and improving reliability.
  • Tests

    • Added a test covering multiline commands with backslash continuation to ensure correct parsing.

@KrosFire KrosFire requested a review from Copilot February 22, 2026 20:38
@coderabbitai
Copy link

coderabbitai bot commented Feb 22, 2026

📝 Walkthrough

Walkthrough

Refactors expression analysis in crates/analysis/src/alpha050/exp.rs by introducing internal macros to centralize recursive analysis, type propagation, and error reporting; relaxes command token filtering to allow backslash and dash in crates/grammar/.../command.rs; and adds a parser test for multiline command escapes.

Changes

Cohort / File(s) Summary
Expression analysis refactor
crates/analysis/src/alpha050/exp.rs
Replaces extensive inline pattern-matching and recursive analyze_exp calls with two internal macros (analyze_expr!, analyze_binop_codep!) to standardize operand analysis, propagate exp_ty/return_ty and is_propagating_failure, centralize type constraints and error reporting, and reduce duplication across many match arms.
Command parser token filter
crates/grammar/src/alpha050/expressions/atom/command.rs
Tightens the command parser's filter predicate to exclude only $ and {, thereby permitting backslash (\) and dash (-) characters in command text tokens.
Parser test
crates/server/tests/grammar/alpha050.rs
Adds a test case (test_lexer_command_escapes) that parses a multiline command with a backslash continuation and newline, asserting the parsed token/AST snapshot.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I hop through macros, tidy and spry,
Folding repetitive branches tidy and nigh.
Backslashes gambol, dashes run free,
Tests applaud the clearer tree. ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects both main changes: refactoring expr analysis and fixing a command multiline bug.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch improve-analysis-functions

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


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

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR refactors expression analysis to reduce duplicated logic and adjusts command parsing to handle multiline commands correctly.

Changes:

  • Added a grammar regression test + snapshot to cover $ ... \ newline continuation in commands.
  • Refactored analyze_exp by introducing helper macros to centralize propagation/return tracking and binop handling.
  • Updated analysis snapshots to reflect the new inference / error reporting behavior.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
crates/server/tests/grammar/alpha050.rs Adds a new snapshot-based test for multiline command parsing.
crates/server/tests/grammar/snapshots/r#mod__grammar__alpha050__command_new_line.snap New snapshot capturing the expected AST for the multiline command case.
crates/grammar/src/alpha050/expressions/atom/command.rs Adjusts token filtering so backslash/newline can be included in command text.
crates/analysis/src/alpha050/exp.rs Refactors expression analysis with macros and consolidates multiple match arms.
crates/server/tests/analysis/snapshots/*.snap Updates snapshots for the refactored/changed analysis behavior and paths.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +113 to +114
rhs_result.exp_ty.to_string(&scoped_generic_types),
lhs_result.exp_ty.to_string(&scoped_generic_types),
Copy link

Copilot AI Feb 22, 2026

Choose a reason for hiding this comment

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

The error message has expected and found reversed. Here, lhs_result.exp_ty is the expected type used to analyze the RHS, while rhs_result.exp_ty is the actual type inferred for the RHS; the message should print expected lhs_result.exp_ty and found rhs_result.exp_ty.

Suggested change
rhs_result.exp_ty.to_string(&scoped_generic_types),
lhs_result.exp_ty.to_string(&scoped_generic_types),
lhs_result.exp_ty.to_string(&scoped_generic_types),
rhs_result.exp_ty.to_string(&scoped_generic_types),

Copilot uses AI. Check for mistakes.
Comment on lines +494 to 503
Expression::Multiply(exp1, exp2)
| Expression::Divide(exp1, exp2)
| Expression::Modulo(exp1, exp2)
| Expression::Subtract(exp1, exp2) => {
analyze_binop_codep!(
exp1,
DataType::Union([DataType::Number, DataType::Int].to_vec()),
files,
scoped_generic_types,
contexts,
);
let ExpAnalysisResult {
return_ty: return2,
is_propagating_failure: prop2,
exp_ty: exp_ty2,
} = analyze_exp(
file_id,
file_version,
exp2,
DataType::Union([DataType::Number, DataType::Int].to_vec()),
files,
scoped_generic_types,
contexts,
);

is_propagating_failure |= prop1 || prop2;
return_types.extend(return1);
return_types.extend(return2);

if exp_ty1 == DataType::Number || exp_ty2 == DataType::Number {
DataType::Number
} else {
DataType::Int
}
exp2
)
}
Copy link

Copilot AI Feb 22, 2026

Choose a reason for hiding this comment

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

These numeric operators previously returned Number if either operand was Number, otherwise Int. After the refactor, the result is whatever type the LHS analyzed to (often Int | Number), which changes the inferred result type and can propagate a less-precise or incorrect type. Consider restoring the numeric promotion logic (compute from both operand types) instead of returning the LHS type.

Copilot uses AI. Check for mistakes.
Copy link

@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

🧹 Nitpick comments (1)
crates/server/tests/grammar/alpha050.rs (1)

263-271: Consider parse_unwrap() if error-free parsing is the expected contract.

Using parse() folds errors silently into the snapshot, so a regression that introduces parse errors would show up only as a snapshot diff rather than a test failure with a clear message. If the intention is that this input should parse without errors after the fix, parse_unwrap() makes that invariant explicit and produces a more actionable failure.

🔧 Suggested change
-    assert_debug_snapshot!(
-        "command_new_line",
-        parse(&compiler.tokenize(
-            r#"
-    $ MY_VAR=1 \
-     my_command $
-    "#
-        ))
-    );
+    assert_debug_snapshot!(
+        "command_new_line",
+        parse_unwrap(&compiler.tokenize(
+            r#"
+    $ MY_VAR=1 \
+     my_command $
+    "#
+        ))
+    );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/server/tests/grammar/alpha050.rs` around lines 263 - 271, The test
currently calls parse(&compiler.tokenize(...)) which will embed parse errors
into the snapshot; replace that call with parse_unwrap(&compiler.tokenize(...))
so the test asserts a no-error parse invariant and fails fast on parse
errors—update the invocation in the assert_debug_snapshot! for the
"command_new_line" case to use parse_unwrap instead of parse.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/analysis/src/alpha050/exp.rs`:
- Around line 100-118: The match on lhs_result.exp_ty moves the DataType out
causing later uses to be a use-after-move; change the pattern to inspect a
cloned value instead (e.g., use lhs_result.exp_ty.clone() when matching Generic)
so that scoped_generic_types.constrain_generic_type still receives the id while
subsequent calls to matches_type(&rhs_result.exp_ty, &lhs_result.exp_ty,
&scoped_generic_types) and the return can use lhs_result.exp_ty; mirror the
approach used in the Ge/Gt arm (if let DataType::Generic(id) =
lhs.exp_ty.clone()) to avoid moving lhs_result.exp_ty.

---

Nitpick comments:
In `@crates/server/tests/grammar/alpha050.rs`:
- Around line 263-271: The test currently calls parse(&compiler.tokenize(...))
which will embed parse errors into the snapshot; replace that call with
parse_unwrap(&compiler.tokenize(...)) so the test asserts a no-error parse
invariant and fails fast on parse errors—update the invocation in the
assert_debug_snapshot! for the "command_new_line" case to use parse_unwrap
instead of parse.

@codecov
Copy link

codecov bot commented Feb 22, 2026

Codecov Report

❌ Patch coverage is 48.33333% with 31 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
crates/analysis/src/alpha050/exp.rs 45.61% 31 Missing ⚠️

📢 Thoughts on this report? Let us know!

Copy link

@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: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/analysis/src/alpha050/exp.rs`:
- Around line 95-122: The macro analyze_binop_codep! currently checks types with
matches_type(&rhs, &lhs, ...) and reports "Expected {rhs}, found {lhs}" at
$exp1.1, which inverts expected/actual and uses the wrong span, and it
duplicates errors because analyze_expr!($exp2, lhs_result.exp_ty.clone())
already reports mismatches; fix by removing the explicit !matches_type
error-reporting entirely (keep only the Generic(id) constrain_generic_type call)
so analyze_expr handles non-generic mismatches, or if you must keep an explicit
check, call matches_type(&lhs_result.exp_ty, &rhs_result.exp_ty,
&scoped_generic_types), format the message "Expected type {lhs}, found type
{rhs}", and report it at the RHS span $exp2.1; locate these changes in the
analyze_binop_codep! macro and around the analyze_expr!($exp2, ...) call.
- Around line 136-153: Matching on array_result.exp_ty consumes it, causing a
use-after-move when you later try to return the original generic variant; change
the pattern to avoid moving the scrutinee (e.g., match by reference or bind the
id without consuming the whole DataType) and when the branch intends to return
the original generic type return DataType::Generic(id_clone_or_copy)
reconstructed from the bound id (or clone/copy the id beforehand) instead of
referencing array_result.exp_ty; specifically update the DataType::Generic(id)
arm and its call to scoped_generic_types.get_recursive(id) so id is not moved
(or use a taken copy) and return a reconstructed DataType::Generic(...) when the
slice-indexing case requires the original generic rather than the inner element
type.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7c3b866 and 8c361c0.

📒 Files selected for processing (1)
  • crates/analysis/src/alpha050/exp.rs

@KrosFire KrosFire merged commit ce4fad3 into main Feb 23, 2026
6 of 7 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