Skip to content

Commit bfcb395

Browse files
Copilotjackfirth
andauthored
Extract named let loopification rules into separate module (#620)
Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: jackfirth <[email protected]>
1 parent 489181b commit bfcb395

File tree

5 files changed

+324
-269
lines changed

5 files changed

+324
-269
lines changed

default-recommendations.rkt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
resyntax/default-recommendations/match-shortcuts
2828
resyntax/default-recommendations/miscellaneous-suggestions
2929
resyntax/default-recommendations/mutability-predicates
30+
resyntax/default-recommendations/named-let-loopification
3031
resyntax/default-recommendations/numeric-shortcuts
3132
resyntax/default-recommendations/require-and-provide-suggestions
3233
resyntax/default-recommendations/string-shortcuts
@@ -62,6 +63,7 @@
6263
resyntax/default-recommendations/match-shortcuts
6364
resyntax/default-recommendations/miscellaneous-suggestions
6465
resyntax/default-recommendations/mutability-predicates
66+
resyntax/default-recommendations/named-let-loopification
6567
resyntax/default-recommendations/numeric-shortcuts
6668
resyntax/default-recommendations/require-and-provide-suggestions
6769
resyntax/default-recommendations/string-shortcuts
@@ -104,6 +106,7 @@
104106
match-shortcuts
105107
miscellaneous-suggestions
106108
mutability-predicates
109+
named-let-loopification
107110
numeric-shortcuts
108111
require-and-provide-suggestions
109112
string-shortcuts

default-recommendations/for-loop-shortcuts-test.rkt

Lines changed: 0 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -624,122 +624,6 @@ test: "nested for/and forms can be flattened to a for*/and form"
624624
------------------------------
625625

626626

627-
test: "named let loop over counter can be replaced by for in-range"
628-
------------------------------------------------------------
629-
(define (f x a b dx)
630-
(let loop ([x a])
631-
(when (< x b)
632-
(displayln x)
633-
(loop (+ x dx)))))
634-
============================================================
635-
(define (f x a b dx)
636-
(for ([x (in-range a b dx)])
637-
(displayln x)))
638-
------------------------------------------------------------
639-
640-
641-
no-change-test: "named let loop over counter not refactorable when loop function used elsewhere"
642-
------------------------------------------------------------
643-
(define (f x a b dx)
644-
(let loop ([x a])
645-
(when (< x b)
646-
(displayln x)
647-
(displayln loop)
648-
(loop (+ x dx)))))
649-
------------------------------------------------------------
650-
651-
652-
test: "named let loop with conditional return over vector can be replaced by for/first"
653-
------------------------------------------------------------
654-
(define vec (vector 0 1 2 3 4 5))
655-
(let loop ([i 0])
656-
(and (< i (vector-length vec))
657-
(let ([x (vector-ref vec i)])
658-
(if (> x 3)
659-
(+ x 42)
660-
(loop (add1 i))))))
661-
============================================================
662-
(define vec (vector 0 1 2 3 4 5))
663-
(let loop ([i 0])
664-
(and (< i (vector-length vec))
665-
(let ([x (vector-ref vec i)])
666-
(if (> x 3)
667-
(+ x 42)
668-
(loop (+ i 1))))))
669-
============================================================
670-
(define vec (vector 0 1 2 3 4 5))
671-
(for/first ([x (in-vector vec)]
672-
#:when (> x 3))
673-
(+ x 42))
674-
------------------------------------------------------------
675-
676-
677-
test: "named let loop over list can be replaced by for/list"
678-
------------------------------------------------------------
679-
(require racket/list)
680-
(let loop ([xs (list 1 2 3)])
681-
(cond
682-
[(null? xs) '()]
683-
[else
684-
(displayln (car xs))
685-
(cons (* (car xs) 10)
686-
(loop (cdr xs)))]))
687-
------------------------------------------------------------
688-
------------------------------------------------------------
689-
(require racket/list)
690-
(let loop ([xs (list 1 2 3)])
691-
(cond
692-
[(empty? xs) '()]
693-
[else
694-
(displayln (first xs))
695-
(cons (* (first xs) 10)
696-
(loop (rest xs)))]))
697-
------------------------------------------------------------
698-
------------------------------------------------------------
699-
(require racket/list)
700-
(for/list ([x (in-list (list 1 2 3))])
701-
(displayln x)
702-
(* x 10))
703-
------------------------------------------------------------
704-
705-
706-
test: "named let loop can be replaced with for/and when equivalent"
707-
------------------------------------------------------------
708-
(require racket/list)
709-
(define (f xs big? red?)
710-
(let loop ([xs xs])
711-
(cond
712-
[(empty? xs) #true]
713-
[(and (big? (first xs)) (not (red? (car xs))))
714-
(loop (rest xs))]
715-
[else #false])))
716-
------------------------------------------------------------
717-
------------------------------------------------------------
718-
(require racket/list)
719-
(define (f xs big? red?)
720-
(for/and ([x (in-list xs)])
721-
(and (big? x) (not (red? x)))))
722-
------------------------------------------------------------
723-
724-
725-
test: "named let loop can be replaced with for/or when equivalent"
726-
------------------------------------------------------------
727-
(require racket/list)
728-
(define (f xs big? red?)
729-
(let loop ([xs xs])
730-
(cond
731-
[(empty? xs) #false]
732-
[(and (big? (first xs)) (not (red? (car xs)))) #true]
733-
[else (loop (rest xs))])))
734-
------------------------------------------------------------
735-
------------------------------------------------------------
736-
(require racket/list)
737-
(define (f xs big? red?)
738-
(for/or ([x (in-list xs)])
739-
(and (big? x) (not (red? x)))))
740-
------------------------------------------------------------
741-
742-
743627
test: "append-map with for/list can be replaced by for*/list"
744628
------------------------------------------------------------
745629
(require racket/list)
@@ -1071,20 +955,6 @@ test: "shadowed and unused in-value clause refactorable to #:do clause"
1071955
--------------------
1072956

1073957

1074-
test: "read-until-eof loop refactorable to for loop with in-port"
1075-
--------------------
1076-
(define (print-reads)
1077-
(let loop ([v (read)])
1078-
(unless (eof-object? v)
1079-
(displayln v)
1080-
(loop (read)))))
1081-
====================
1082-
(define (print-reads)
1083-
(for ([v (in-port)])
1084-
(displayln v)))
1085-
--------------------
1086-
1087-
1088958
test: "index-mutating map to for/list with index"
1089959
--------------------
1090960
(require racket/list)

default-recommendations/for-loop-shortcuts.rkt

Lines changed: 11 additions & 139 deletions
Original file line numberDiff line numberDiff line change
@@ -437,130 +437,19 @@ return just that result."
437437
nested.body ...))
438438

439439

440-
(define-refactoring-rule named-let-loop-to-for-in-range
441-
#:description "This named `let` expression is equivalent to a `for` loop that uses `in-range`."
442-
#:literals (let when < +)
443-
(let loop:id ([x:id start:expr])
444-
(when (< x2:id (~or stop:id stop:literal-constant))
445-
body ...+
446-
(loop2:id (+ x3:id (~or step:id step:literal-constant)))))
447-
#:when (free-identifier=? (attribute x) (attribute x2))
448-
#:when (free-identifier=? (attribute x) (attribute x3))
449-
#:when (free-identifier=? (attribute loop) (attribute loop2))
450-
#:when (or (not (identifier? (attribute step)))
451-
(identifier-binding-unchanged-in-context? (attribute step) this-syntax))
452-
#:when (not (syntax-find-first #'(body ...) id:id
453-
#:when (free-identifier=? (attribute id) (attribute loop))))
454-
(for ([x (in-range start stop step)])
455-
body ...))
456-
457-
458-
(define-refactoring-rule named-let-loop-to-for/list
459-
#:description "This named `let` expression is equivalent to a `for/list` loop."
460-
#:literals (let cond else null? empty? null quote car first cdr rest cons)
461-
(let loop:id ([vs:id init-list])
462-
(cond
463-
[(:empty-predicate-by-any-name vs2:id) :empty-list-by-any-name]
464-
[else
465-
loop-body:expr ...
466-
(cons loop-element:expr (loop2:id (:rest-by-any-name vs3:id)))]))
467-
#:when (log-resyntax-rule-condition (free-identifier=? #'loop #'loop2))
468-
#:when (log-resyntax-rule-condition (free-identifier=? #'vs #'vs2))
469-
#:when (log-resyntax-rule-condition (free-identifier=? #'vs #'vs3))
470-
#:when (log-resyntax-rule-condition
471-
(not
472-
(for/or ([body-stx (in-list (cons #'loop-element (attribute loop-body)))])
473-
(syntax-find-first body-stx
474-
(~and (~var usage (expression-directly-enclosing (attribute vs)))
475-
(~not (:first-by-any-name _)))))))
476-
#:cut
477440

478-
#:with element-id (depluralize-id #'vs)
479-
480-
#:with (modified-result-element modified-body ...)
481-
(for/list ([body-stx (cons #'loop-element (attribute loop-body))])
482-
(syntax-traverse body-stx
483-
[(:first-by-any-name vs-usage:id)
484-
#:when (free-identifier=? (attribute vs-usage) (attribute vs))
485-
(attribute element-id)]))
486-
487-
(for/list ([element-id (in-list init-list)])
488-
modified-body ...
489-
modified-result-element))
490-
491-
492-
(define-refactoring-rule named-let-loop-to-for/and
493-
#:description "This named `let` expression is equivalent to a `for/and` loop."
494-
#:literals (let cond else)
495-
(let loop:id ([vs:id init-list])
496-
(cond
497-
[(:empty-predicate-by-any-name vs2:id) #true]
498-
[element-condition:expr (loop2:id (:rest-by-any-name vs3:id))]
499-
[else #false]))
500-
501-
#:when (free-identifier=? (attribute loop) (attribute loop2))
502-
#:when (free-identifier=? (attribute vs) (attribute vs2))
503-
#:when (free-identifier=? (attribute vs) (attribute vs3))
504-
#:when (not (syntax-find-first (attribute element-condition)
505-
(~and (~var usage (expression-directly-enclosing (attribute vs)))
506-
(~not (:first-by-any-name _)))))
507-
#:cut
508441

509-
#:with element-id (depluralize-id (attribute vs))
510-
#:with modified-element-condition
511-
(syntax-traverse (attribute element-condition)
512-
[(:first-by-any-name vs-usage:id)
513-
#:when (free-identifier=? (attribute vs-usage) (attribute vs))
514-
(attribute element-id)])
515-
516-
(for/and ([element-id (in-list init-list)])
517-
modified-element-condition))
518-
519-
520-
(define-refactoring-rule named-let-loop-to-for/or
521-
#:description "This named `let` expression is equivalent to a `for/or` loop."
522-
#:literals (let cond else)
523-
(let loop:id ([vs:id init-list])
524-
(cond
525-
[(:empty-predicate-by-any-name vs2:id) #false]
526-
[element-condition:expr #true]
527-
[else (loop2:id (:rest-by-any-name vs3:id))]))
528-
529-
#:when (free-identifier=? (attribute loop) (attribute loop2))
530-
#:when (free-identifier=? (attribute vs) (attribute vs2))
531-
#:when (free-identifier=? (attribute vs) (attribute vs3))
532-
#:when (not (syntax-find-first (attribute element-condition)
533-
(~and (~var usage (expression-directly-enclosing (attribute vs)))
534-
(~not (:first-by-any-name _)))))
535-
#:cut
536442

537-
#:with element-id (depluralize-id (attribute vs))
538-
#:with modified-element-condition
539-
(syntax-traverse (attribute element-condition)
540-
[(:first-by-any-name vs-usage:id)
541-
#:when (log-resyntax-rule-condition (free-identifier=? (attribute vs) (attribute vs-usage)))
542-
(attribute element-id)])
543-
544-
(for/or ([element-id (in-list init-list)])
545-
modified-element-condition))
546-
547-
548-
(define-refactoring-rule named-let-loop-to-for/first-in-vector
549-
#:description "This loop can be replaced by a simpler, equivalent `for/first` loop."
550-
#:literals (let add1 + vector-length vector-ref if and <)
551-
(let loop1:id ([i1:id 0])
552-
(and (< i2:id (vector-length vec1:id))
553-
(let ([x:id (vector-ref vec2:id i3:id)])
554-
(if condition:expr
555-
true-branch:expr
556-
(loop2:id (~or (add1 i4:id) (+ i4:id 1) (+ 1 i4:id)))))))
557-
#:when (and (log-resyntax-rule-condition (free-identifier=? #'loop1 #'loop2))
558-
(log-resyntax-rule-condition (free-identifier=? #'i1 #'i2))
559-
(log-resyntax-rule-condition (free-identifier=? #'i1 #'i3))
560-
(log-resyntax-rule-condition (free-identifier=? #'i1 #'i4))
561-
(log-resyntax-rule-condition (free-identifier=? #'vec1 #'vec2)))
562-
(for/first ([x (in-vector vec1)] #:when condition)
563-
true-branch))
443+
444+
445+
446+
447+
448+
449+
450+
451+
452+
564453

565454

566455
(define-refactoring-rule or-let-in-for/and-to-filter-clause
@@ -621,18 +510,7 @@ return just that result."
621510
(for-id (clause-before ... #:do [expr] clause-after ...) body ...))
622511

623512

624-
(define-refactoring-rule named-let-read-loop-to-for-in-port
625-
#:description
626-
"A named `let` that repeatedly calls `read` can be rewritten to a `for` loop using `in-port`."
627-
#:literals (let unless read eof-object?)
628-
(let loop:id ([v:id (read)])
629-
(unless (eof-object? v2:id)
630-
body ...
631-
(loop2:id (read))))
632-
#:when (free-identifier=? (attribute v) (attribute v2))
633-
#:when (free-identifier=? (attribute loop) (attribute loop2))
634-
(for ([v (in-port)])
635-
body ...))
513+
636514

637515

638516
(define-refactoring-rule index-mutating-map-to-for/list
@@ -673,12 +551,6 @@ return just that result."
673551
list->set-to-for/set
674552
list->vector-to-for/vector
675553
map-to-for
676-
named-let-loop-to-for-in-range
677-
named-let-loop-to-for/and
678-
named-let-loop-to-for/first-in-vector
679-
named-let-loop-to-for/list
680-
named-let-loop-to-for/or
681-
named-let-read-loop-to-for-in-port
682554
nested-for-to-for*
683555
nested-for/and-to-for*/and
684556
nested-for/or-to-for*/or

0 commit comments

Comments
 (0)