From 4d28a2663655889a54487e4d7de2396f4b05edb8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 8 Nov 2025 17:06:11 +0000 Subject: [PATCH 1/3] Initial plan From 89ebfa78fdc31ef59503c4df6660c79173a86c2d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 8 Nov 2025 17:51:16 +0000 Subject: [PATCH 2/3] Add compilation checking to filter out broken refactoring suggestions Co-authored-by: jackfirth <8175575+jackfirth@users.noreply.github.com> --- main.rkt | 52 ++++++++++++++++++++++++++++++++-- private/refactoring-result.rkt | 5 ++++ private/source.rkt | 8 ++++++ 3 files changed, 63 insertions(+), 2 deletions(-) diff --git a/main.rkt b/main.rkt index 23dee88d..ab6f0b24 100644 --- a/main.rkt +++ b/main.rkt @@ -214,7 +214,19 @@ (refactor-visited-forms #:analysis analysis #:suite effective-suite #:comments comments #:lines lines))) - (refactoring-result-set #:base-source source #:results results)) + (define result-set (refactoring-result-set #:base-source source #:results results)) + + ;; Filter out result sets that produce non-compiling code + (cond + [(and (not (empty? results)) + (not (refactoring-result-set-compiles? result-set))) + (log-resyntax-warning + "dropping ~a refactoring suggestion~a for ~a because the modified code does not compile" + (length results) + (if (equal? (length results) 1) "" "s") + (or (source-path source) "string source")) + (refactoring-result-set #:base-source source #:results '())] + [else result-set])) (define/guard (reysntax-analyze-for-properties-only source #:suite [suite default-recommendations]) @@ -335,6 +347,18 @@ (grouping into-list) (mapping (λ (e) (refactoring-result-set #:base-source (entry-key e) #:results (entry-value e)))) + (filtering + (λ (result-set) + (define compiles? (refactoring-result-set-compiles? result-set)) + (unless compiles? + (define source (refactoring-result-set-base-source result-set)) + (define num-results (length (refactoring-result-set-results result-set))) + (log-resyntax-warning + "dropping ~a refactoring suggestion~a for ~a because the modified code does not compile" + num-results + (if (equal? num-results 1) "" "s") + (or (source-path source) "string source"))) + compiles?)) (indexing refactoring-result-set-base-source) #:into into-hash)) @@ -478,4 +502,28 @@ (check-false (set-empty? (refactoring-suite-analyzers test-suite))) ;; Verify that all analyzers in the suite are expansion-analyzer? (check-true (for/and ([analyzer (in-set (refactoring-suite-analyzers test-suite))]) - (expansion-analyzer? analyzer))))) + (expansion-analyzer? analyzer)))) + + (test-case "broken refactoring rules are filtered out" + ;; Define a refactoring rule that produces code that doesn't compile + (define-refactoring-rule breaking-rule + #:description "Breaking refactoring rule" + #:datum-literals (foo) + #:literals (define) + (define foo 42) + (if)) + + (define breaking-suite (refactoring-suite #:rules (list breaking-rule))) + (define test-source (string-source "#lang racket/base\n\n(define foo 42)\n")) + + ;; Test with direct analyze + (define result-set (resyntax-analyze test-source #:suite breaking-suite)) + (check-equal? (length (refactoring-result-set-results result-set)) 0 + "Breaking suggestions should be filtered from resyntax-analyze") + + ;; Test with multipass analyze + (define analysis + (resyntax-analyze-all (hash test-source (range-set (unbounded-range #:comparator natural<=>))) + #:suite breaking-suite)) + (check-equal? (resyntax-analysis-total-fixes analysis) 0 + "Breaking suggestions should be filtered from resyntax-analyze-all"))) diff --git a/private/refactoring-result.rkt b/private/refactoring-result.rkt index f407cfbe..9b5d5edf 100644 --- a/private/refactoring-result.rkt +++ b/private/refactoring-result.rkt @@ -31,6 +31,7 @@ [refactoring-result-set-updated-source (-> refactoring-result-set? modified-source?)] [refactoring-result-set-results (-> refactoring-result-set? (listof refactoring-result?))] [refactoring-result-set-modified-lines (-> refactoring-result-set? immutable-range-set?)] + [refactoring-result-set-compiles? (-> refactoring-result-set? boolean?)] [refactoring-result-map-commits (-> (hash/c source? refactoring-result-set?) (listof resyntax-commit?))])) @@ -129,6 +130,10 @@ #:into (into-range-set natural<=>))) +(define (refactoring-result-set-compiles? result-set) + (source-can-expand? (refactoring-result-set-updated-source result-set))) + + (define string-replacement<=> (comparator-map natural<=> string-replacement-start)) diff --git a/private/source.rkt b/private/source.rkt index b4f26ba3..203e9996 100644 --- a/private/source.rkt +++ b/private/source.rkt @@ -16,6 +16,7 @@ [source-read-syntax (-> source? syntax?)] [source-read-language (-> source? (or/c module-path? #false))] [source-expand (-> source? syntax?)] + [source-can-expand? (-> source? boolean?)] [source-text-of (-> source? syntax? immutable-string?)] [file-source? (-> any/c boolean?)] [file-source (-> path-string? file-source?)] @@ -132,6 +133,13 @@ (expand (source-read-syntax code))) +(define (source-can-expand? code) + (with-handlers ([exn:fail? (λ (_) #false)]) + (parameterize ([current-namespace (make-base-namespace)]) + (source-expand code)) + #true)) + + (define/guard (source-path code) (guard-match (or (file-source path) (modified-source (file-source path) _)) code #:else #false) path) From 573c066ab7862b51aa0a8ad3b8ffb1b5eeba13aa Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 9 Nov 2025 03:38:40 +0000 Subject: [PATCH 3/3] Add tests for source-can-expand? function Co-authored-by: jackfirth <8175575+jackfirth@users.noreply.github.com> --- private/source.rkt | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/private/source.rkt b/private/source.rkt index 203e9996..1f7ddd47 100644 --- a/private/source.rkt +++ b/private/source.rkt @@ -126,7 +126,23 @@ (check-equal? (source-read-language (string-source "#lang scribble/manual")) 'scribble/manual) (check-equal? (source-read-language (string-source "#lang info")) 'info) (check-equal? (source-read-language (string-source "#lang setup/infotab")) 'setup/infotab) - (check-equal? (source-read-language (string-source "(void)")) #false))) + (check-equal? (source-read-language (string-source "(void)")) #false)) + + (test-case "source-can-expand?" + ;; Valid racket code should expand successfully + (check-true (source-can-expand? (string-source "#lang racket/base\n(define x 42)"))) + (check-true (source-can-expand? (string-source "#lang racket\n(or 1 2 3)"))) + + ;; Invalid racket code should not expand + (check-false (source-can-expand? (string-source "#lang racket/base\n(if)"))) + (check-false (source-can-expand? (string-source "#lang racket/base\n(define)"))) + + ;; Modified sources should also be testable + (define orig (string-source "#lang racket/base\n(define foo 42)")) + (define valid-mod (modified-source orig "#lang racket/base\n(define foo 43)")) + (define invalid-mod (modified-source orig "#lang racket/base\n(if)")) + (check-true (source-can-expand? valid-mod)) + (check-false (source-can-expand? invalid-mod)))) (define (source-expand code)