Skip to content

Fix five GitHub issues: string coercion, parameter shadowing, operators, scope, typing - #587

Merged
logbie merged 4 commits into
mainfrom
claude/github-issues-review-i7upri
Jul 6, 2026
Merged

Fix five GitHub issues: string coercion, parameter shadowing, operators, scope, typing#587
logbie merged 4 commits into
mainfrom
claude/github-issues-review-i7upri

Conversation

@logbie

@logbie logbie commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator

Summary

This PR fixes five distinct bugs discovered in an open-issues review, all verified against a fresh release build:

Key Changes

#583 — String coercion removed

  • Deleted the hard-coded special case in VariableDeclaration that converted any Text value equal to "[]" into an empty List
  • Quoted strings now retain their type regardless of content, including when built at runtime

#582 — Parameter shadowing fixed

  • Changed parameter binding from define() (rejects outer-scope names) to define_direct() (current-scope only)
  • Applied to both action-call parameters (call_function) and event-handler parameters
  • Parameters now unconditionally shadow same-named globals instead of being silently ignored

#566starts with / ends with operators added

  • Added KeywordStarts and KeywordEnds tokens (contextual, usable as identifiers elsewhere)
  • Implemented infix parse in binary.rs at comparison precedence that desugars to existing starts_with / ends_with builtins
  • Updated route construct's when starts with / when ends with arms to use new tokens
  • No new interpreter or type-checker operator needed; backward compatible

#557 — Include-file scope handling fixed

  • Modified extract_parent_variables to skip Value::NativeFunction entries
  • Included files now behave like the main file: action-locals can shadow builtins (non-fatal) instead of causing fatal conflicts
  • Builtins are already resolved through is_builtin_function, so seeding them as shadowable variables only made includes stricter

#567 — Gradual typing for Any/Unknown values

  • add X to <number> and add X to <list> now accept Any/Unknown operands
  • Binary arithmetic on Any operands degrades gracefully (comparisons yield Boolean, Plus with Text yields Text, others yield Any)
  • split X by Y accepts Any/Unknown for both operands
  • Untyped parameter references now yield Unknown silently instead of raising a false type error

Testing

Added tests/github_issues_batch_test.rs with 13 regression tests covering:

  • Literal and runtime-built "[]" strings
  • Single and multi-parameter shadowing
  • starts/ends with positive/negative cases and stored boolean results
  • All six date-unit words as locals in included files
  • Any/Unknown flowing into add, arithmetic, and split operations

All existing TestPrograms continue to pass (no regressions).

https://claude.ai/code/session_01HYUZcFy2vaiKcT9YzxVZYo

Summary by CodeRabbit

  • Bug Fixes
    • Improved handling of empty-list-like values so text such as [] stays as text instead of being converted unexpectedly.
    • Fixed parameter handling so event and function inputs can override same-named values from outer scope.
    • Added support for starts with and ends with in expressions and route conditions.
    • Reduced false type-checking errors for flexible values like unknown or any-typed data, including lists, arithmetic, and string splitting.
    • Prevented included files from triggering scope conflicts with built-in helpers.

…567)

- #583: stop coercing the Text value "[]" to an empty List in the
  VariableDeclaration handler; a quoted string keeps its Text type.
- #582: bind action/event parameters with define_direct so a parameter
  shadows a same-named global instead of the global overriding the arg.
- #566: add KeywordStarts/KeywordEnds tokens and an infix parse desugaring
  `X starts with Y` / `X ends with Y` to the starts_with/ends_with builtins,
  so they work end-to-end (not just under --analyze); update route arms.
- #557: skip native builtins in extract_parent_variables so an included
  file can use date-unit words (year/month/day/hour/minute/second) as
  action-local variables, matching main-file behavior (non-fatal).
- #567: accept Any/Unknown in the add/split/binary-arithmetic typechecker
  rules and stop erroring on untyped-parameter references (gradual typing).

Adds tests/github_issues_batch_test.rs (13 regression tests). All existing
TestPrograms continue to pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HYUZcFy2vaiKcT9YzxVZYo
Copilot AI review requested due to automatic review settings July 6, 2026 15:58
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@logbie, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 1 minute

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 129e537d-8e83-4f54-83c7-7e5385d819cf

📥 Commits

Reviewing files that changed from the base of the PR and between 4e1d100 and 2ff9790.

📒 Files selected for processing (2)
  • src/typechecker/mod.rs
  • tests/github_issues_batch_test.rs
📝 Walkthrough

Walkthrough

This PR fixes five GitHub issues: prevents "[]" text from being coerced to an empty list, changes parameter binding to use define_direct so parameters shadow globals, adds "starts with"/"ends with" operator parsing, excludes native functions from parent-variable extraction to fix include-file scoping conflicts, and relaxes typechecker rules for Any/Unknown types. Includes regression tests and a dev diary entry.

Changes

GitHub issues batch fix

Layer / File(s) Summary
Text/list coercion and native-function scoping fix
src/interpreter/mod.rs, tests/github_issues_batch_test.rs
Removes special-casing that converted evaluated "[]" text into an empty list, and excludes Value::NativeFunction entries from parent-variable extraction; tests confirm "[]" stays Text.
Parameter shadowing via define_direct
src/interpreter/mod.rs, tests/github_issues_batch_test.rs
Event handler and function-call parameter binding switch from define to define_direct, allowing parameters to shadow same-named globals; tests verify single and multiple parameter shadowing.
starts with / ends with operator support
src/lexer/token.rs, src/parser/expr/binary.rs, src/parser/stmt/route.rs, tests/github_issues_batch_test.rs
Adds KeywordStarts/KeywordEnds contextual tokens, parses "starts with"/"ends with" into starts_with/ends_with calls in binary expressions and route patterns, replacing identifier-based matching.
Include-file date-unit local variable scoping
tests/github_issues_batch_test.rs
Tests verify date-unit named locals (year, month, etc.) inside included files no longer trigger fatal outer-scope redefinition errors.
Gradual typing relaxation for Any/Unknown
src/typechecker/mod.rs, tests/github_issues_batch_test.rs
Allows Any/Unknown operands in list additions, arithmetic, unresolved variable inference, binary operations, and string_split, with tests confirming no false diagnostics.
Test infrastructure and dev diary
tests/github_issues_batch_test.rs, Dev diary/2026-07-06-github-issues-batch-583-582-566-557-567.md
Adds shared helpers (wfl_exe, run_src, run_include) and a dev diary entry documenting the fixes and resolved issues.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant Interpreter
  participant Scope

  Caller->>Interpreter: invoke action/event with parameters
  Interpreter->>Scope: define_direct(parameter, value)
  Scope-->>Interpreter: parameter binding shadows outer/global
  Interpreter-->>Caller: execute body with shadowed parameter
Loading

Possibly related PRs

  • WebFirstLanguage/wfl#552: Both PRs modify src/typechecker/mod.rs and included-file scope/type inference around Any/Unknown builtins.
  • WebFirstLanguage/wfl#554: Overlaps with typechecker updates to infer Boolean for Unknown/Any comparisons and more tolerant indexing behavior.
  • WebFirstLanguage/wfl#576: Directly related lexer/parser updates for starts with/ends with and route pattern detection.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the five bug fixes and the main themes of the changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/github-issues-review-i7upri

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.

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/typechecker/mod.rs (1)

1552-1570: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

List element check still rejects adding concrete values — contradicts stated fix.

The guard only checks value_type != Type::Any, but never checks whether the list's own element type is Any. For a variable typed List(Any) (the common return type of push, filter, map, parse_json, etc.), adding e.g. a Number still trips this condition:

  • **element_type != Type::Unknown → true (it's Any)
  • **element_type != value_type → true (Any != Number)
  • value_type != Type::Unknown → true
  • value_type != Type::Any → true (Number)

All four are true, so type_error fires — a false positive that the line-range summary explicitly claims is fixed ("allows adding values to lists when the list element type is Any").

🐛 Proposed fix
                     match &symbol.symbol_type {
                         Some(Type::List(element_type)) => {
                             if **element_type != Type::Unknown
+                                && **element_type != Type::Any
                                 && **element_type != value_type
                                 && value_type != Type::Unknown
                                 && value_type != Type::Any
                             {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/typechecker/mod.rs` around lines 1552 - 1570, The list element
compatibility check in the typechecker still rejects valid writes to List(Any)
values. Update the logic in the list-addition branch inside the type checking
path so the `type_error` guard also treats the list’s own `element_type` of
`Type::Any` as permissive, alongside the existing `Type::Unknown` handling. Make
the fix in the `self.analyzer.get_symbol(list_name)` /
`Type::List(element_type)` match so concrete `value_type`s are accepted when the
list is typed as `Any`.
🧹 Nitpick comments (1)
src/typechecker/mod.rs (1)

1588-1600: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

_ catch-all for AddToListStatement doesn't treat Any-typed variables permissively.

If list_name's symbol type is Any (statically unknown, could be a list at runtime), this arm still raises "Cannot add to non-list variable" since only Some(Type::Unknown) is excluded. For consistency with the gradual-typing intent applied elsewhere in this PR, Any should likely be treated the same as Unknown here.

♻️ Proposed fix
                         _ => {
                             // Variable might not be a list
-                            if symbol.symbol_type != Some(Type::Unknown) {
+                            if symbol.symbol_type != Some(Type::Unknown)
+                                && symbol.symbol_type != Some(Type::Any)
+                            {
                                 self.type_error(
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/typechecker/mod.rs` around lines 1588 - 1600, In the AddToListStatement
handling inside typechecker::mod::TypeChecker, the catch-all arm currently
rejects variables typed as Any even though they should be treated permissively
like Unknown. Update the conditional around self.type_error so that
symbol.symbol_type == Some(Type::Any) is also excluded alongside Type::Unknown,
keeping the existing behavior for definite non-list types while allowing
gradual-typing cases to pass.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/github_issues_batch_test.rs`:
- Around line 22-46: The test helper currently hardcodes the WFL executable path
in wfl_exe(), which can break under cargo test and may use a stale build. Update
wfl_exe() to return the Cargo-provided binary path via
env!("CARGO_BIN_EXE_wfl"), and keep run_src() using that helper so the tests
always invoke the exact test-built binary regardless of working directory or
build mode.

---

Outside diff comments:
In `@src/typechecker/mod.rs`:
- Around line 1552-1570: The list element compatibility check in the typechecker
still rejects valid writes to List(Any) values. Update the logic in the
list-addition branch inside the type checking path so the `type_error` guard
also treats the list’s own `element_type` of `Type::Any` as permissive,
alongside the existing `Type::Unknown` handling. Make the fix in the
`self.analyzer.get_symbol(list_name)` / `Type::List(element_type)` match so
concrete `value_type`s are accepted when the list is typed as `Any`.

---

Nitpick comments:
In `@src/typechecker/mod.rs`:
- Around line 1588-1600: In the AddToListStatement handling inside
typechecker::mod::TypeChecker, the catch-all arm currently rejects variables
typed as Any even though they should be treated permissively like Unknown.
Update the conditional around self.type_error so that symbol.symbol_type ==
Some(Type::Any) is also excluded alongside Type::Unknown, keeping the existing
behavior for definite non-list types while allowing gradual-typing cases to
pass.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6a6a8bc6-7a9c-4407-9ac9-18f87778ac31

📥 Commits

Reviewing files that changed from the base of the PR and between 56c04cd and 4e1d100.

📒 Files selected for processing (7)
  • Dev diary/2026-07-06-github-issues-batch-583-582-566-557-567.md
  • src/interpreter/mod.rs
  • src/lexer/token.rs
  • src/parser/expr/binary.rs
  • src/parser/stmt/route.rs
  • src/typechecker/mod.rs
  • tests/github_issues_batch_test.rs

Comment thread tests/github_issues_batch_test.rs
claude and others added 2 commits July 6, 2026 16:31
Address CodeRabbit review on PR #587: the batch test helper hardcoded
`target/release/wfl`, which is working-directory dependent and can pick up
a stale or missing build. Use Cargo's `env!("CARGO_BIN_EXE_wfl")`, which
resolves to the exact binary built for this integration-test run in any
profile. Verified: all 13 tests pass under plain `cargo test` (debug).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HYUZcFy2vaiKcT9YzxVZYo
@logbie

logbie commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator Author

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @src/typechecker/mod.rs around lines 1552 - 1570, The list element
compatibility check in the typechecker still rejects valid writes to List(Any)
values. Update the logic in the list-addition branch inside the type checking
path so the type_error guard also treats the list’s own element_type of
Type::Any as permissive, alongside the existing Type::Unknown handling. Make
the fix in the self.analyzer.get_symbol(list_name) /
Type::List(element_type) match so concrete value_types are accepted when the
list is typed as Any.

Follow-up to the #567 gradual-typing relaxation: the `add X to <list>` rule
still rejected adding a concrete value to a list whose element type is `Any`
(e.g. a `[1, 2]` literal, typed `List(Any)`), emitting a false
"Cannot add Text to list of Any". Treat an `Any` element type as permissive
alongside the existing `Unknown` handling — a list of statically-unknown
element type accepts any value. Adds a regression test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HYUZcFy2vaiKcT9YzxVZYo
Copilot AI review requested due to automatic review settings July 6, 2026 16:57

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

logbie commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator Author

Verified and fixed in 2ff9790.

The finding was still valid: the add X to <list> rule only treated element_type == Unknown as permissive, so adding a concrete value to a List(Any) (e.g. a [1, 2] literal, which infers as List(Any)) still emitted a false Cannot add … to list of Any. Added **element_type != Type::Any to the guard in the Type::List(element_type) arm, so an Any/Unknown element type accepts any concrete value — consistent with the rest of the #567 gradual-typing relaxation.

Repro now runs clean (no type warning, exit 0):

store xs as [10 and 20]
add "hello" to xs
add 30 to xs
display length of xs   -- 4

Added a regression test (concrete_value_accepted_into_list_of_any); all 14 tests in the batch pass, cargo fmt/clippy clean.

Note: the only red check, copilot-pull-request-reviewer, is failing with a Copilot quota error (HTTP 402, "exceeded your monthly quota") — an account/billing issue, not related to this change; all functional CI (build/test/clippy, integration, WFL programs, formatting) is green.


Generated by Claude Code

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.

3 participants