From 8380d1a0e1b74300dbcc53ab15cefedfe28cdc3a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 24 Oct 2025 02:25:28 +0000 Subject: [PATCH 1/3] Initial plan From e77c2f9a96436f50009454214f25c78b14b9a122 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 24 Oct 2025 02:38:39 +0000 Subject: [PATCH 2/3] Extend define-let-to-multi-define to handle multiple bindings Co-authored-by: jackfirth <8175575+jackfirth@users.noreply.github.com> --- .../define-let-to-multi-define-test.rkt | 115 ++++++++++++++++++ .../formatting-preservation-test.rkt | 2 +- .../let-binding-suggestions.rkt | 33 +++-- 3 files changed, 139 insertions(+), 11 deletions(-) create mode 100644 default-recommendations/define-let-to-multi-define-test.rkt diff --git a/default-recommendations/define-let-to-multi-define-test.rkt b/default-recommendations/define-let-to-multi-define-test.rkt new file mode 100644 index 00000000..2e2f2adb --- /dev/null +++ b/default-recommendations/define-let-to-multi-define-test.rkt @@ -0,0 +1,115 @@ +#lang resyntax/test + + +require: resyntax/default-recommendations let-binding-suggestions + + +header: +- #lang racket/base + + +test: "define-let-to-multi-define with single binding" +------------------------------ +(define (f) + (define a (let ([b 1]) (+ b 10))) + a) +============================== +(define (f) + (define b 1) + (define a (+ b 10)) + a) +------------------------------ + + +test: "define-let-to-multi-define with two bindings" +------------------------------ +(define (f) + (define a (let ([b 1] [c 2]) (+ b c 10))) + a) +============================== +(define (f) + (define b 1) + (define c 2) + (define a (+ b c 10)) + a) +------------------------------ + + +test: "define-let-to-multi-define with three bindings" +------------------------------ +(define (f) + (define result (let ([x 1] [y 2] [z 3]) (* x y z))) + result) +============================== +(define (f) + (define x 1) + (define y 2) + (define z 3) + (define result (* x y z)) + result) +------------------------------ + + +test: "define-let-to-multi-define with body-before" +------------------------------ +(define (f) + (displayln "foo") + (define a (let ([b 1] [c 2]) (+ b c))) + a) +============================== +(define (f) + (displayln "foo") + (define b 1) + (define c 2) + (define a (+ b c)) + a) +------------------------------ + + +test: "define-let-to-multi-define with body-after" +------------------------------ +(define (f) + (define a (let ([b 1] [c 2]) (+ b c))) + (displayln "bar") + a) +============================== +(define (f) + (define b 1) + (define c 2) + (define a (+ b c)) + (displayln "bar") + a) +------------------------------ + + +test: "define-let-to-multi-define preserves complex expressions" +------------------------------ +(define (f x) + (define result + (let ([sum (+ x 1)] + [product (* x 2)]) + (+ sum product))) + result) +============================== +(define (f x) + (define sum (+ x 1)) + (define product (* x 2)) + (define result (+ sum product)) + result) +------------------------------ + + +no-change-test: "define-let-to-multi-define doesn't apply when bindings shadow outer scope" +------------------------------ +(define (f b) + (define a (let ([b 1]) (+ b 10))) + a) +------------------------------ + + +no-change-test: "define-let-to-multi-define doesn't apply when later binding depends on earlier" +------------------------------ +(define (f) + (define a (let ([b 1] [c b]) (+ b c))) + a) +------------------------------ diff --git a/default-recommendations/formatting-preservation-test.rkt b/default-recommendations/formatting-preservation-test.rkt index 607bea60..09f1008d 100644 --- a/default-recommendations/formatting-preservation-test.rkt +++ b/default-recommendations/formatting-preservation-test.rkt @@ -20,7 +20,7 @@ test: "refactoring an expression doesn't affect formatting of unrefactored code" ---------------------------------------- -test: "define-let-to-double-define doesn't reformat the entire definition context" +test: "define-let-to-multi-define doesn't reformat the entire definition context" ---------------------------------------- (define (f) ( displayln "foo" ) diff --git a/default-recommendations/let-binding-suggestions.rkt b/default-recommendations/let-binding-suggestions.rkt index 75c59505..5ca7c0c5 100644 --- a/default-recommendations/let-binding-suggestions.rkt +++ b/default-recommendations/let-binding-suggestions.rkt @@ -71,20 +71,33 @@ (call-with-values (λ () expr) receiver)) -(define-definition-context-refactoring-rule define-let-to-double-define - #:description "This `let` expression can be pulled up into a `define` expression." +(define-definition-context-refactoring-rule define-let-to-multi-define + #:description "This `let` expression can be pulled up into multiple `define` expressions." #:literals (define let) (~seq body-before ... - (~and original-definition (define id:id (let ([nested-id:id nested-expr:expr]) expr:expr))) + (~and original-definition + (define id:id (let ([nested-id:id nested-expr:expr] ...+) expr:expr))) body-after ...) - #:when (identifier-binding-unchanged-in-context? (attribute id) (attribute nested-expr)) - #:when (for/and ([body-free-id - (in-free-id-set - (syntax-free-identifiers #'(body-before ... nested-expr body-after ...)))]) - (identifier-binding-unchanged-in-context? body-free-id (attribute nested-id))) + #:when (identifier-binding-unchanged-in-context? (attribute id) #'(nested-expr ...)) + #:when (for/and ([nested-id (in-list (attribute nested-id))]) + (not (identifier-has-exact-binding-in-context? nested-id (attribute original-definition)))) + #:when (for*/and ([body-free-id + (in-free-id-set + (syntax-free-identifiers #'(body-before ... nested-expr ... body-after ...)))] + [nested-id (in-list (attribute nested-id))]) + (identifier-binding-unchanged-in-context? body-free-id nested-id)) + #:when (for/and ([rhs (in-list (attribute nested-expr))] + [i (in-naturals)] + #:when #true + [nested-id (in-list (attribute nested-id))] + [j (in-naturals)] + #:when (< i j) + #:when #true + [rhs-free-id (in-free-id-set (syntax-free-identifiers rhs))]) + (identifier-binding-unchanged-in-context? rhs-free-id nested-id)) (body-before ... (~@ . (~focus-replacement-on - (~splicing-replacement ((define nested-id nested-expr) (define id expr)) + (~splicing-replacement ((define nested-id nested-expr) ... (define id expr)) #:original original-definition))) body-after ...)) @@ -117,7 +130,7 @@ (define-refactoring-suite let-binding-suggestions #:rules (let-to-define begin0-let-to-define-begin0 - define-let-to-double-define + define-let-to-multi-define delete-redundant-let let-values-then-call-to-call-with-values named-let-to-plain-let)) From 13bd19c60cb21eb6f134f684207cc14b42254886 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 24 Oct 2025 02:48:27 +0000 Subject: [PATCH 3/3] Add clarifying comments to the dependency check logic Co-authored-by: jackfirth <8175575+jackfirth@users.noreply.github.com> --- default-recommendations/let-binding-suggestions.rkt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/default-recommendations/let-binding-suggestions.rkt b/default-recommendations/let-binding-suggestions.rkt index 5ca7c0c5..6dd0e8ce 100644 --- a/default-recommendations/let-binding-suggestions.rkt +++ b/default-recommendations/let-binding-suggestions.rkt @@ -86,13 +86,14 @@ (syntax-free-identifiers #'(body-before ... nested-expr ... body-after ...)))] [nested-id (in-list (attribute nested-id))]) (identifier-binding-unchanged-in-context? body-free-id nested-id)) + ;; Ensure later bindings don't depend on earlier ones (sequential let* semantics) #:when (for/and ([rhs (in-list (attribute nested-expr))] [i (in-naturals)] - #:when #true + #:when #true ; ensures proper sequencing in the nested loop [nested-id (in-list (attribute nested-id))] [j (in-naturals)] #:when (< i j) - #:when #true + #:when #true ; ensures proper sequencing in the nested loop [rhs-free-id (in-free-id-set (syntax-free-identifiers rhs))]) (identifier-binding-unchanged-in-context? rhs-free-id nested-id)) (body-before ...