Skip to content

Commit cf24251

Browse files
Copilotjackfirth
andcommitted
Add simplify-named-let-initialization refactoring rule
Co-authored-by: jackfirth <[email protected]>
1 parent b6e22fb commit cf24251

File tree

3 files changed

+122
-0
lines changed

3 files changed

+122
-0
lines changed

default-recommendations.rkt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
resyntax/default-recommendations/mutability-predicates
3838
resyntax/default-recommendations/numeric-shortcuts
3939
resyntax/default-recommendations/require-and-provide-suggestions
40+
resyntax/default-recommendations/simplify-named-let-initialization
4041
resyntax/default-recommendations/string-shortcuts
4142
resyntax/default-recommendations/syntax-shortcuts
4243
resyntax/default-recommendations/syntax-parse-shortcuts
@@ -79,6 +80,7 @@
7980
resyntax/default-recommendations/mutability-predicates
8081
resyntax/default-recommendations/numeric-shortcuts
8182
resyntax/default-recommendations/require-and-provide-suggestions
83+
resyntax/default-recommendations/simplify-named-let-initialization
8284
resyntax/default-recommendations/string-shortcuts
8385
resyntax/default-recommendations/syntax-parse-shortcuts
8486
resyntax/default-recommendations/syntax-rules-shortcuts
@@ -129,6 +131,7 @@
129131
numeric-shortcuts
130132
provide-contract-migration
131133
require-and-provide-suggestions
134+
simplify-named-let-initialization
132135
string-shortcuts
133136
syntax-shortcuts
134137
syntax-parse-shortcuts
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
#lang resyntax/test
2+
3+
4+
require: resyntax/default-recommendations simplify-named-let-initialization
5+
6+
7+
header:
8+
--------------------
9+
#lang racket
10+
(define (a) 1)
11+
(define (b) 2)
12+
(define (c) 3)
13+
--------------------
14+
15+
test: "original code should be refactorable to new code"
16+
--------------------
17+
(define (f a b c)
18+
(let loop ([x (+ 1 2 3)]
19+
[y (if (a)
20+
(b)
21+
(c))])
22+
(loop x y)))
23+
====================
24+
(define (f a b c)
25+
(define init-y
26+
(if (a)
27+
(b)
28+
(c)))
29+
(let loop ([x (+ 1 2 3)]
30+
[y init-y])
31+
(loop x y)))
32+
--------------------
33+
34+
35+
no-change-test: "code not refactorable when side-effecting expression is present"
36+
--------------------
37+
(define (f a b c)
38+
(let loop ([x (displayln "foo")]
39+
[y (if (a)
40+
(b)
41+
(c))])
42+
(loop x y)))
43+
--------------------
44+
45+
46+
no-change-test: "code not refactorable when all expressions are single-line"
47+
--------------------
48+
(define (f a b c)
49+
(let loop ([x (+ 1 2 3)]
50+
[y 42])
51+
(loop x y)))
52+
--------------------
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
#lang racket/base
2+
3+
4+
(require racket/contract/base)
5+
6+
7+
(provide
8+
(contract-out
9+
[simplify-named-let-initialization refactoring-suite?]))
10+
11+
12+
(require racket/list
13+
racket/syntax
14+
resyntax/base
15+
resyntax/default-recommendations/private/definition-context
16+
resyntax/default-recommendations/private/pure-expression
17+
resyntax/default-recommendations/private/syntax-lines
18+
syntax/parse)
19+
20+
21+
;@----------------------------------------------------------------------------------------------------
22+
23+
24+
(define-definition-context-refactoring-rule simplify-named-let-initialization-rule
25+
#:description
26+
"Complex multi-line initialization expressions in named `let` loops can be extracted into `define`\
27+
bindings to improve readability."
28+
#:literals (let)
29+
(~seq leading-body ...
30+
(let loop-name:id ([binding-id:id binding-expr:expr] ...)
31+
loop-body ...))
32+
33+
#:do [(define-values (bindings-to-extract remaining-bindings)
34+
(for/fold ([extracted '()]
35+
[remaining '()])
36+
([id (in-list (attribute binding-id))]
37+
[expr (in-list (attribute binding-expr))])
38+
(if (multiline-syntax? expr)
39+
(values (cons (list id expr) extracted)
40+
remaining)
41+
(values extracted
42+
(cons (list id expr) remaining)))))]
43+
44+
;; Check that at least one binding expression is multi-line
45+
#:when (not (null? bindings-to-extract))
46+
47+
;; Check that all non-multi-line (remaining) binding expressions are pure
48+
;; (so we can safely reorder by extracting the multi-line ones)
49+
#:when (for/and ([binding (in-list remaining-bindings)])
50+
(syntax-parse (cadr binding)
51+
[:pure-expression #true]
52+
[_ #false]))
53+
54+
#:with ((extracted-id extracted-expr) ...) (reverse bindings-to-extract)
55+
#:with ((kept-id kept-expr) ...) (reverse remaining-bindings)
56+
#:with (init-id ...) (for/list ([id (in-list (attribute extracted-id))])
57+
(format-id id "init-~a" id))
58+
59+
(leading-body ...
60+
(define init-id extracted-expr) ...
61+
(let loop-name ([kept-id kept-expr] ...
62+
[extracted-id init-id] ...)
63+
loop-body ...)))
64+
65+
66+
(define-refactoring-suite simplify-named-let-initialization
67+
#:rules (simplify-named-let-initialization-rule))

0 commit comments

Comments
 (0)