Skip to content

Commit 186fd5e

Browse files
Copilotjackfirth
andauthored
Make analyzer timeout configurable (#749)
Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: jackfirth <[email protected]>
1 parent 61e585b commit 186fd5e

File tree

6 files changed

+108
-46
lines changed

6 files changed

+108
-46
lines changed

cli.rkt

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737

3838
(define-enum-type resyntax-output-format (plain-text github-pull-request-review git-commit-message json))
3939
(define-enum-type resyntax-fix-method (modify-files create-multiple-git-commits))
40-
(define-record-type resyntax-analyze-options (targets suite output-format output-destination))
40+
(define-record-type resyntax-analyze-options (targets suite output-format output-destination analyzer-timeout-ms))
4141

4242

4343
(define-record-type resyntax-fix-options
@@ -48,7 +48,8 @@
4848
max-fixes
4949
max-modified-files
5050
max-modified-lines
51-
max-pass-count))
51+
max-pass-count
52+
analyzer-timeout-ms))
5253

5354

5455
(define all-lines (range-set (unbounded-range #:comparator natural<=>)))
@@ -60,6 +61,7 @@
6061
(define selected-rule #false)
6162
(define output-format plain-text)
6263
(define output-destination 'console)
64+
(define analyzer-timeout-ms 10000)
6365

6466
(command-line
6567
#:program "resyntax analyze"
@@ -105,6 +107,11 @@ changed relative to baseref are analyzed."
105107
(define rule-sym (string->symbol rule-name))
106108
(set! selected-rule rule-sym))
107109

110+
("--analyzer-timeout"
111+
timeout-ms
112+
"The timeout in milliseconds for expansion analyzers. Defaults to 10000 (10 seconds)."
113+
(set! analyzer-timeout-ms (string->number timeout-ms)))
114+
108115
("--output-to-file"
109116
outputpath
110117
"Store results in a file instead of printing them to the console."
@@ -128,7 +135,8 @@ determined by the GITHUB_REPOSITORY and GITHUB_REF environment variables."
128135
#:targets (build-vector targets)
129136
#:suite suite
130137
#:output-format output-format
131-
#:output-destination output-destination))
138+
#:output-destination output-destination
139+
#:analyzer-timeout-ms analyzer-timeout-ms))
132140

133141

134142
(define (resyntax-fix-parse-command-line)
@@ -143,6 +151,7 @@ determined by the GITHUB_REPOSITORY and GITHUB_REF environment variables."
143151
(define max-pass-count 10)
144152
(define max-modified-files +inf.0)
145153
(define max-modified-lines +inf.0)
154+
(define analyzer-timeout-ms 10000)
146155

147156
(command-line
148157
#:program "resyntax fix"
@@ -197,6 +206,11 @@ changed relative to baseref are analyzed and fixed."
197206
(define rule-sym (string->symbol rule-name))
198207
(set! selected-rule rule-sym))
199208

209+
("--analyzer-timeout"
210+
timeout-ms
211+
"The timeout in milliseconds for expansion analyzers. Defaults to 10000 (10 seconds)."
212+
(set! analyzer-timeout-ms (string->number timeout-ms)))
213+
200214
("--max-pass-count"
201215
passcount
202216
"The maximum number of times Resyntax will fix each file. By default, Resyntax runs at most 10 \
@@ -235,7 +249,8 @@ are needed when applying a fix unlocks further fixes."
235249
#:max-fixes max-fixes
236250
#:max-modified-files max-modified-files
237251
#:max-modified-lines max-modified-lines
238-
#:max-pass-count max-pass-count))
252+
#:max-pass-count max-pass-count
253+
#:analyzer-timeout-ms analyzer-timeout-ms))
239254

240255

241256
(define (resyntax-run)
@@ -273,7 +288,8 @@ For help on these, use 'analyze --help' or 'fix --help'."
273288
(define analysis
274289
(resyntax-analyze-all sources
275290
#:suite (resyntax-analyze-options-suite options)
276-
#:max-passes 1))
291+
#:max-passes 1
292+
#:timeout-ms (resyntax-analyze-options-analyzer-timeout-ms options)))
277293
(define results
278294
(transduce (resyntax-analysis-all-results analysis)
279295
(append-mapping in-hash-values)
@@ -322,7 +338,8 @@ For help on these, use 'analyze --help' or 'fix --help'."
322338
#:max-fixes (resyntax-fix-options-max-fixes options)
323339
#:max-passes (resyntax-fix-options-max-pass-count options)
324340
#:max-modified-sources max-modified-files
325-
#:max-modified-lines max-modified-lines))
341+
#:max-modified-lines max-modified-lines
342+
#:timeout-ms (resyntax-fix-options-analyzer-timeout-ms options)))
326343
(match fix-method
327344
[(== modify-files)
328345
(resyntax-analysis-write-file-changes! analysis)]

main.rkt

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,18 @@
1717
[resyntax-analysis-write-file-changes! (-> resyntax-analysis? void?)]
1818
[resyntax-analysis-commit-fixes! (-> resyntax-analysis? void?)]
1919
[resyntax-analyze
20-
(->* (source?) (#:suite refactoring-suite? #:lines range-set?) refactoring-result-set?)]
20+
(->* (source?) (#:suite refactoring-suite? #:lines range-set? #:timeout-ms exact-nonnegative-integer?) refactoring-result-set?)]
2121
[resyntax-analyze-all
2222
(->* ((hash/c source? range-set? #:flat? #true))
2323
(#:suite refactoring-suite?
2424
#:max-fixes (or/c exact-nonnegative-integer? +inf.0)
2525
#:max-passes exact-nonnegative-integer?
2626
#:max-modified-sources (or/c exact-nonnegative-integer? +inf.0)
27-
#:max-modified-lines (or/c exact-nonnegative-integer? +inf.0))
27+
#:max-modified-lines (or/c exact-nonnegative-integer? +inf.0)
28+
#:timeout-ms exact-nonnegative-integer?)
2829
resyntax-analysis?)]
2930
[reysntax-analyze-for-properties-only
30-
(->* (source?) (#:suite refactoring-suite?) syntax-property-bundle?)]
31+
(->* (source?) (#:suite refactoring-suite? #:timeout-ms exact-nonnegative-integer?) syntax-property-bundle?)]
3132
[refactor! (-> (sequence/c refactoring-result?) void?)]))
3233

3334

@@ -167,7 +168,8 @@
167168

168169
(define/guard (resyntax-analyze source
169170
#:suite [suite default-recommendations]
170-
#:lines [lines (range-set (unbounded-range #:comparator natural<=>))])
171+
#:lines [lines (range-set (unbounded-range #:comparator natural<=>))]
172+
#:timeout-ms [timeout-ms 10000])
171173
(define comments (with-input-from-source source read-comment-locations))
172174
(define source-lang (source-read-language source))
173175
(guard source-lang #:else
@@ -210,7 +212,8 @@
210212
(with-handlers ([exn:fail? skip])
211213
(define analysis (source-analyze source
212214
#:lines lines
213-
#:analyzers (refactoring-suite-analyzers effective-suite)))
215+
#:analyzers (refactoring-suite-analyzers effective-suite)
216+
#:timeout-ms timeout-ms))
214217
(refactor-visited-forms
215218
#:analysis analysis #:suite effective-suite #:comments comments #:lines lines)))
216219

@@ -229,7 +232,9 @@
229232
[else result-set]))
230233

231234

232-
(define/guard (reysntax-analyze-for-properties-only source #:suite [suite default-recommendations])
235+
(define/guard (reysntax-analyze-for-properties-only source
236+
#:suite [suite default-recommendations]
237+
#:timeout-ms [timeout-ms 10000])
233238
(define comments (with-input-from-source source read-comment-locations))
234239
(define full-source (source->string source))
235240
(guard (string-prefix? full-source "#lang racket") #:else
@@ -251,7 +256,8 @@
251256
[exn:fail:filesystem:missing-module? skip]
252257
[exn:fail:contract:variable? skip])
253258
(define analysis (source-analyze source
254-
#:analyzers (refactoring-suite-analyzers suite)))
259+
#:analyzers (refactoring-suite-analyzers suite)
260+
#:timeout-ms timeout-ms))
255261
(source-code-analysis-added-syntax-properties analysis)))
256262

257263

@@ -260,7 +266,8 @@
260266
#:max-fixes [max-fixes +inf.0]
261267
#:max-passes [max-passes 10]
262268
#:max-modified-sources [max-modified-sources +inf.0]
263-
#:max-modified-lines [max-modified-lines +inf.0])
269+
#:max-modified-lines [max-modified-lines +inf.0]
270+
#:timeout-ms [timeout-ms 10000])
264271
(log-resyntax-info "--- analyzing code ---")
265272
(for/fold ([pass-result-lists '()]
266273
[sources sources]
@@ -274,7 +281,8 @@
274281
#:suite suite
275282
#:max-fixes max-fixes
276283
#:max-modified-sources max-modified-sources
277-
#:max-modified-lines max-modified-lines))
284+
#:max-modified-lines max-modified-lines
285+
#:timeout-ms timeout-ms))
278286
(define pass-fix-count (count-total-results pass-results))
279287
(define new-max-fixes (- max-fixes pass-fix-count))]
280288
#:break (hash-empty? pass-results)
@@ -298,7 +306,8 @@
298306
#:suite suite
299307
#:max-fixes max-fixes
300308
#:max-modified-sources max-modified-sources
301-
#:max-modified-lines max-modified-lines)
309+
#:max-modified-lines max-modified-lines
310+
#:timeout-ms timeout-ms)
302311
(transduce (in-hash-entries sources) ; entries with source keys and line range set values
303312

304313
;; The following steps perform a kind of layered shuffle: the files to refactor are
@@ -326,7 +335,7 @@
326335
(append-mapping
327336
(λ (e)
328337
(match-define (entry source lines) e)
329-
(define result-set (resyntax-analyze source #:suite suite #:lines lines))
338+
(define result-set (resyntax-analyze source #:suite suite #:lines lines #:timeout-ms timeout-ms))
330339
(refactoring-result-set-results result-set)))
331340
(limiting max-modified-lines
332341
#:by (λ (result)
@@ -524,6 +533,7 @@
524533
;; Test with multipass analyze
525534
(define analysis
526535
(resyntax-analyze-all (hash test-source (range-set (unbounded-range #:comparator natural<=>)))
527-
#:suite breaking-suite))
536+
#:suite breaking-suite
537+
#:timeout-ms 10000))
528538
(check-equal? (resyntax-analysis-total-fixes analysis) 0
529539
"Breaking suggestions should be filtered from resyntax-analyze-all")))

private/analysis.rkt

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
(provide
88
(contract-out
9-
[source-analyze (->* (source? #:analyzers (sequence/c expansion-analyzer?))
9+
[source-analyze (->* (source? #:analyzers (sequence/c expansion-analyzer?) #:timeout-ms exact-nonnegative-integer?)
1010
(#:lines range-set?)
1111
source-code-analysis?)]
1212
[source-code-analysis? (-> any/c boolean?)]
@@ -68,15 +68,9 @@
6868
(syntax-ref stx path)))
6969

7070

71-
;; Timeout for analyzers in seconds
72-
(define analyzer-timeout-seconds 10)
73-
74-
;; Milliseconds per second (for time conversions)
75-
(define milliseconds-per-second 1000)
76-
7771
;; Run an analyzer with a timeout. Returns the result or an empty syntax property bundle
7872
;; if timeout occurred or an error was raised.
79-
(define (run-analyzer-with-timeout analyzer expanded source-name)
73+
(define (run-analyzer-with-timeout analyzer expanded source-name timeout-ms)
8074
(define result-box (box #false))
8175
(define error-box (box #false))
8276
(define start-time (current-inexact-milliseconds))
@@ -87,8 +81,7 @@
8781
(with-handlers ([exn:fail? (λ (e) (set-box! error-box e))])
8882
(set-box! result-box (expansion-analyze analyzer expanded))))))
8983

90-
(define timeout-evt (alarm-evt (+ (current-inexact-milliseconds)
91-
(* analyzer-timeout-seconds milliseconds-per-second))))
84+
(define timeout-evt (alarm-evt (+ (current-inexact-milliseconds) timeout-ms)))
9285

9386
;; Wait for either completion or timeout
9487
(define final-result
@@ -100,9 +93,9 @@
10093
[(eq? final-result 'timeout)
10194
(kill-thread analyzer-thread)
10295
(log-resyntax-warning
103-
"analyzer ~a timed out after ~a seconds while analyzing ~a"
96+
"analyzer ~a timed out after ~a ms while analyzing ~a"
10497
(object-name analyzer)
105-
analyzer-timeout-seconds
98+
timeout-ms
10699
source-name)
107100
(syntax-property-bundle)]
108101
[else
@@ -125,7 +118,8 @@
125118

126119
(define (source-analyze code
127120
#:lines [lines (range-set (unbounded-range #:comparator natural<=>))]
128-
#:analyzers analyzers)
121+
#:analyzers analyzers
122+
#:timeout-ms timeout-ms)
129123
(define ns (make-base-namespace))
130124
(parameterize ([current-directory (or (source-directory code) (current-directory))]
131125
[current-namespace ns])
@@ -245,7 +239,7 @@
245239
(transduce analyzers
246240
(append-mapping
247241
(λ (analyzer)
248-
(define result (run-analyzer-with-timeout analyzer expanded program-source-name))
242+
(define result (run-analyzer-with-timeout analyzer expanded program-source-name timeout-ms))
249243
(syntax-property-bundle-entries result)))
250244
(filtering
251245
(λ (prop-entry)
@@ -312,20 +306,21 @@
312306
(define test-source (string-source "#lang racket/base (define x 1)"))
313307

314308
;; Test with empty analyzers list
315-
(define analysis-empty (source-analyze test-source #:analyzers '()))
309+
(define analysis-empty (source-analyze test-source #:analyzers '() #:timeout-ms 10000))
316310
(check-true (source-code-analysis? analysis-empty))
317311

318312
;; Test with single analyzer
319313
(define analysis-single
320-
(source-analyze test-source #:analyzers (list identifier-usage-analyzer)))
314+
(source-analyze test-source #:analyzers (list identifier-usage-analyzer) #:timeout-ms 10000))
321315
(check-true (source-code-analysis? analysis-single))
322316

323317
;; Test with default analyzers (should match default behavior)
324318
(define analysis-default
325319
(source-analyze test-source
326320
#:analyzers (list identifier-usage-analyzer
327321
ignored-result-values-analyzer
328-
variable-mutability-analyzer)))
322+
variable-mutability-analyzer)
323+
#:timeout-ms 10000))
329324
(check-true (source-code-analysis? analysis-default)))
330325

331326
(test-case "source-analyze filters out properties with invalid paths"
@@ -345,7 +340,7 @@
345340
(syntax-property-entry (syntax-path (list 0 999)) 'another-invalid-prop #true)))))
346341

347342
;; Run analysis with the bad analyzer - should not crash
348-
(define analysis (source-analyze test-source #:analyzers (list bad-analyzer)))
343+
(define analysis (source-analyze test-source #:analyzers (list bad-analyzer) #:timeout-ms 10000))
349344

350345
;; Check that the analysis completed successfully
351346
(check-true (source-code-analysis? analysis))
@@ -372,13 +367,13 @@
372367
(make-expansion-analyzer
373368
#:name 'slow-analyzer
374369
(λ (expanded)
375-
;; Sleep for 11 seconds - slightly longer than the 10 second timeout
376-
(sleep 11)
370+
;; Sleep for 200ms - longer than a 100ms timeout
371+
(sleep 0.2)
377372
(syntax-property-bundle
378373
(syntax-property-entry empty-syntax-path 'slow-prop #true)))))
379374

380-
;; Run analysis with the slow analyzer - should timeout and not crash
381-
(define analysis (source-analyze test-source #:analyzers (list slow-analyzer)))
375+
;; Run analysis with the slow analyzer and a short timeout - should timeout and not crash
376+
(define analysis (source-analyze test-source #:analyzers (list slow-analyzer) #:timeout-ms 100))
382377

383378
;; Check that the analysis completed (even though the analyzer timed out)
384379
(check-true (source-code-analysis? analysis))
@@ -403,7 +398,7 @@
403398
(syntax-property-entry empty-syntax-path 'fast-prop #true)))))
404399

405400
;; Run analysis with the fast analyzer - should complete successfully
406-
(define analysis (source-analyze test-source #:analyzers (list fast-analyzer)))
401+
(define analysis (source-analyze test-source #:analyzers (list fast-analyzer) #:timeout-ms 10000))
407402

408403
;; Check that the analysis completed
409404
(check-true (source-code-analysis? analysis))
@@ -427,7 +422,7 @@
427422
(error "intentional test error"))))
428423

429424
;; Run analysis with the failing analyzer - should skip it and not crash
430-
(define analysis (source-analyze test-source #:analyzers (list failing-analyzer)))
425+
(define analysis (source-analyze test-source #:analyzers (list failing-analyzer) #:timeout-ms 10000))
431426

432427
;; Check that the analysis completed (even though the analyzer failed)
433428
(check-true (source-code-analysis? analysis))))

test.rkt

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,21 @@
147147

148148
(pattern (~seq (#:option #:lines (~and line-set (#:range-set _ ...))))
149149
#:with (id ...) (list #'current-line-mask)
150-
#:with (value ...) (list #'line-set)))
150+
#:with (value ...) (list #'line-set))
151+
152+
(pattern (~seq (#:option #:analyzer-timeout-millis timeout-value:number))
153+
#:with (id ...) (list #'current-analyzer-timeout-millis)
154+
#:with (value ...) (list #`'#,(syntax-e #'timeout-value)))
155+
156+
(pattern (~seq (#:option #:lines (~and line-set (#:range-set _ ...)))
157+
(#:option #:analyzer-timeout-millis timeout-value:number))
158+
#:with (id ...) (list #'current-line-mask #'current-analyzer-timeout-millis)
159+
#:with (value ...) (list #'line-set #`'#,(syntax-e #'timeout-value)))
160+
161+
(pattern (~seq (#:option #:analyzer-timeout-millis timeout-value:number)
162+
(#:option #:lines (~and line-set (#:range-set _ ...))))
163+
#:with (id ...) (list #'current-analyzer-timeout-millis #'current-line-mask)
164+
#:with (value ...) (list #`'#,(syntax-e #'timeout-value) #'line-set)))
151165

152166
(define-splicing-syntax-class code-block-test-args
153167
#:attributes ([check 1])

test/analyzer-timeout-test.rkt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#lang resyntax/test
2+
3+
4+
header:
5+
------------------------------
6+
#lang racket/base
7+
------------------------------
8+
9+
10+
test: "timeout parameter can be customized in tests"
11+
@analyzer-timeout-millis 5000
12+
- (or 1 (or 2 3))
13+
- (or 1 2 3)
14+
15+
16+
no-change-test: "timeout parameter works with no-change-test"
17+
@analyzer-timeout-millis 5000
18+
- (define x 42)

0 commit comments

Comments
 (0)