summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLudovic Courtès <ludo@gnu.org>2026-03-23 21:29:21 +0100
committerLudovic Courtès <ludo@gnu.org>2026-03-29 22:32:57 +0200
commitf0e22ae62c143fbf00bde368c2adbc848cff5ff5 (patch)
treec1270b04b087752a1308dfc3e603a1f4d8a1e36a
parente3a708ed79d0f3e03aed0aaf044a447df16646ea (diff)
records: Warn about shadowing due to inherited field value bindings.
This is a followup to a7c8e68dc51144a6d3981b770aca9c4897fc7c0c: this commit introduced a new binding in the body of field values, which could silently shadow outer bindings. This new warning catches potentially unwanted shadowing. * guix/records.scm (make-syntactic-constructor)[check-shadowing]: New procedure. [wrap-field-value]: Use it. * tests/records.scm ("define-record-type* & inherited value shadowing"): New test. Change-Id: I81ad14cf10da7213e9f8db987c8b0bd4c41acba2 Signed-off-by: Ludovic Courtès <ludo@gnu.org> Merges: #7424
-rw-r--r--guix/records.scm19
-rw-r--r--tests/records.scm20
2 files changed, 39 insertions, 0 deletions
diff --git a/guix/records.scm b/guix/records.scm
index bf746d3b5d9..52d03f73942 100644
--- a/guix/records.scm
+++ b/guix/records.scm
@@ -24,6 +24,7 @@
24 #:use-module (srfi srfi-26) 24 #:use-module (srfi srfi-26)
25 #:use-module (ice-9 match) 25 #:use-module (ice-9 match)
26 #:use-module (ice-9 rdelim) 26 #:use-module (ice-9 rdelim)
27 #:autoload (system syntax) (syntax-local-binding)
27 #:export (define-record-type* 28 #:export (define-record-type*
28 this-record 29 this-record
29 30
@@ -206,6 +207,23 @@ of TYPE matches the expansion-time ABI."
206 index 207 index
207 (loop rest (+ 1 index)))))))) 208 (loop rest (+ 1 index))))))))
208 209
210 (define (check-shadowing identifier)
211 ;; Warn if IDENTIFIER shadows a local binding.
212 ;; Note: not using (guix diagnostics) to remain independent of
213 ;; other Guix modules.
214 (when (eq? 'lexical (syntax-local-binding identifier))
215 (format (current-warning-port)
216 "~a: inherited field binding '~a' of \
217record type '~a' shadows local variable~%"
218 (match (syntax-source identifier)
219 (#f "<unknown-location>")
220 (lst (format #f "~a:~a:~a"
221 (assq-ref lst 'filename)
222 (and=> (assq-ref lst 'line) 1+)
223 (assq-ref lst 'column))))
224 (syntax->datum identifier)
225 (syntax->datum #'type))))
226
209 (define* (wrap-field-value f value #:optional parent) 227 (define* (wrap-field-value f value #:optional parent)
210 ;; Wrap VALUE, the value of field F, such that its sanitizer is 228 ;; Wrap VALUE, the value of field F, such that its sanitizer is
211 ;; called and its properties (thunked, delayed) honored. When 229 ;; called and its properties (thunked, delayed) honored. When
@@ -222,6 +240,7 @@ of TYPE matches the expansion-time ABI."
222 #`((struct-ref #,parent 240 #`((struct-ref #,parent
223 #,(field-index f)) 241 #,(field-index f))
224 #,this-identifier))) 242 #,this-identifier)))
243 (check-shadowing f)
225 #`(lambda (x) 244 #`(lambda (x)
226 (syntax-parameterize ((#,this-identifier 245 (syntax-parameterize ((#,this-identifier
227 (lambda (s) 246 (lambda (s)
diff --git a/tests/records.scm b/tests/records.scm
index 9c071334d50..57a21d2effc 100644
--- a/tests/records.scm
+++ b/tests/records.scm
@@ -302,6 +302,26 @@
302 => 302 =>
303 (,(foo-bar child) ,(foo-baz child)))))) 303 (,(foo-bar child) ,(foo-baz child))))))
304 304
305(test-assert "define-record-type* & inherited value shadowing"
306 (let ((exp '(begin
307 (define-record-type* <foo> foo make-foo
308 foo?
309 (bar foo-bar)
310 (baz foo-baz (thunked)))
311
312 (let ((x (foo (bar 1) (baz 2)))
313 (baz 123))
314 ;; Below, the 'baz' binding for the inherited field value
315 ;; shadows the 'baz' above, which should trigger a warning.
316 (foo (inherit x)
317 (baz (* baz 2)))))))
318 (string-contains
319 (call-with-output-string
320 (lambda (port)
321 (parameterize ((current-warning-port port))
322 (eval exp (test-module)))))
323 "shadows local variable")))
324
305(test-assert "define-record-type* & delayed" 325(test-assert "define-record-type* & delayed"
306 (begin 326 (begin
307 (define-record-type* <foo> foo make-foo 327 (define-record-type* <foo> foo make-foo