From 792d46e7c33dd6efd17556fb44162653a8368bfb Mon Sep 17 00:00:00 2001 From: Jack Firth Date: Tue, 7 Jul 2026 01:01:03 -0700 Subject: [PATCH] Only rewrite multi-body when/unless expressions to loop keywords The when-expression-in-for-loop-to-when-keyword and unless-expression-in-for-loop-to-unless-keyword rules rewrote any when or unless expression wrapping a loop body, including ones with a single body form. Rewriting those doesn't meaningfully reduce indentation or improve the code, so now the rules only fire when the when or unless expression has multiple body forms. Fixes #765. Co-Authored-By: Claude Fable 5 --- .../loops/for-loop-shortcuts-test.rkt | 48 +++++++++++++++++-- .../loops/for-loop-shortcuts.rkt | 12 +++-- 2 files changed, 52 insertions(+), 8 deletions(-) diff --git a/default-recommendations/loops/for-loop-shortcuts-test.rkt b/default-recommendations/loops/for-loop-shortcuts-test.rkt index 8ec3c5b..3a850c5 100644 --- a/default-recommendations/loops/for-loop-shortcuts-test.rkt +++ b/default-recommendations/loops/for-loop-shortcuts-test.rkt @@ -341,54 +341,94 @@ test: "nested for/and forms can be flattened to a for*/and form" ------------------------------ -test: "(when ...) in a for loop refactored to #:when clause" +test: "multi-body (when ...) in a for loop refactored to #:when clause" ------------------------------------------------------------ (for ([x (in-list (list 1 2 'a 3 'b 4))]) (when (number? x) + (displayln x) (displayln x))) ============================================================ (for ([x (in-list (list 1 2 'a 3 'b 4))] #:when (number? x)) + (displayln x) (displayln x)) ------------------------------------------------------------ -test: "(when ...) in a for* loop refactored to #:when clause" +test: "multi-body (when ...) in a for* loop refactored to #:when clause" ------------------------------------------------------------ (for* ([x (in-list (list 1 2 'a 3 'b 4))]) (when (number? x) + (displayln x) (displayln x))) ============================================================ (for* ([x (in-list (list 1 2 'a 3 'b 4))] #:when (number? x)) + (displayln x) (displayln x)) ------------------------------------------------------------ -test: "(unless ...) in a for loop refactored to #:when clause" +test: "multi-body (unless ...) in a for loop refactored to #:unless clause" ------------------------------------------------------------ (for ([x (in-list (list 1 2 'a 3 'b 4))]) (unless (number? x) + (displayln x) (displayln x))) ============================================================ (for ([x (in-list (list 1 2 'a 3 'b 4))] #:unless (number? x)) + (displayln x) (displayln x)) ------------------------------------------------------------ -test: "(unless ...) in a for* loop refactored to #:when clause" +test: "multi-body (unless ...) in a for* loop refactored to #:unless clause" ------------------------------------------------------------ (for* ([x (in-list (list 1 2 'a 3 'b 4))]) (unless (number? x) + (displayln x) (displayln x))) ============================================================ (for* ([x (in-list (list 1 2 'a 3 'b 4))] #:unless (number? x)) + (displayln x) (displayln x)) ------------------------------------------------------------ +no-change-test: "single-body (when ...) in a for loop not refactorable to #:when clause" +------------------------------------------------------------ +(for ([x (in-list (list 1 2 'a 3 'b 4))]) + (when (number? x) + (displayln x))) +------------------------------------------------------------ + + +no-change-test: "single-body (when ...) in a for* loop not refactorable to #:when clause" +------------------------------------------------------------ +(for* ([x (in-list (list 1 2 'a 3 'b 4))]) + (when (number? x) + (displayln x))) +------------------------------------------------------------ + + +no-change-test: "single-body (unless ...) in a for loop not refactorable to #:unless clause" +------------------------------------------------------------ +(for ([x (in-list (list 1 2 'a 3 'b 4))]) + (unless (number? x) + (displayln x))) +------------------------------------------------------------ + + +no-change-test: "single-body (unless ...) in a for* loop not refactorable to #:unless clause" +------------------------------------------------------------ +(for* ([x (in-list (list 1 2 'a 3 'b 4))]) + (unless (number? x) + (displayln x))) +------------------------------------------------------------ + + test: "for/vector with in-range 0 n gets #:length n" ------------------------------------------------------------ (define n 5) diff --git a/default-recommendations/loops/for-loop-shortcuts.rkt b/default-recommendations/loops/for-loop-shortcuts.rkt index 75bc4b6..2f1fca8 100644 --- a/default-recommendations/loops/for-loop-shortcuts.rkt +++ b/default-recommendations/loops/for-loop-shortcuts.rkt @@ -257,18 +257,22 @@ return just that result." (~@ . (~splicing-replacement (last-condition.refactored ...) #:original original-body)))) +;; The following two rules require multiple body forms because rewriting a single-body when/unless +;; expression doesn't meaningfully reduce indentation or improve the code. + + (define-refactoring-rule when-expression-in-for-loop-to-when-keyword #:description "Use the `#:when` keyword instead of `when` to reduce loop body indentation." #:literals (when for for*) - ((~or for-id:for for-id:for*) (clause ...) (when condition body ...)) - (for-id (clause ... #:when condition) body ...)) + ((~or for-id:for for-id:for*) (clause ...) (when condition first-body remaining-body ...+)) + (for-id (clause ... #:when condition) first-body remaining-body ...)) (define-refactoring-rule unless-expression-in-for-loop-to-unless-keyword #:description "Use the `#:unless` keyword instead of `unless` to reduce loop body indentation." #:literals (unless for for*) - ((~or for-id:for for-id:for*) (clause ...) (unless condition body ...)) - (for-id (clause ... #:unless condition) body ...)) + ((~or for-id:for for-id:for*) (clause ...) (unless condition first-body remaining-body ...+)) + (for-id (clause ... #:unless condition) first-body remaining-body ...)) (define-refactoring-rule in-hash-to-in-hash-keys