mirror of
https://git.savannah.gnu.org/git/guix.git
synced 2026-04-06 13:10:33 +02:00
records: Warn about shadowing due to inherited field value bindings.
This is a followup to a7c8e68dc5: 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
This commit is contained in:
@@ -24,6 +24,7 @@
|
|||||||
#:use-module (srfi srfi-26)
|
#:use-module (srfi srfi-26)
|
||||||
#:use-module (ice-9 match)
|
#:use-module (ice-9 match)
|
||||||
#:use-module (ice-9 rdelim)
|
#:use-module (ice-9 rdelim)
|
||||||
|
#:autoload (system syntax) (syntax-local-binding)
|
||||||
#:export (define-record-type*
|
#:export (define-record-type*
|
||||||
this-record
|
this-record
|
||||||
|
|
||||||
@@ -206,6 +207,23 @@ of TYPE matches the expansion-time ABI."
|
|||||||
index
|
index
|
||||||
(loop rest (+ 1 index))))))))
|
(loop rest (+ 1 index))))))))
|
||||||
|
|
||||||
|
(define (check-shadowing identifier)
|
||||||
|
;; Warn if IDENTIFIER shadows a local binding.
|
||||||
|
;; Note: not using (guix diagnostics) to remain independent of
|
||||||
|
;; other Guix modules.
|
||||||
|
(when (eq? 'lexical (syntax-local-binding identifier))
|
||||||
|
(format (current-warning-port)
|
||||||
|
"~a: inherited field binding '~a' of \
|
||||||
|
record type '~a' shadows local variable~%"
|
||||||
|
(match (syntax-source identifier)
|
||||||
|
(#f "<unknown-location>")
|
||||||
|
(lst (format #f "~a:~a:~a"
|
||||||
|
(assq-ref lst 'filename)
|
||||||
|
(and=> (assq-ref lst 'line) 1+)
|
||||||
|
(assq-ref lst 'column))))
|
||||||
|
(syntax->datum identifier)
|
||||||
|
(syntax->datum #'type))))
|
||||||
|
|
||||||
(define* (wrap-field-value f value #:optional parent)
|
(define* (wrap-field-value f value #:optional parent)
|
||||||
;; Wrap VALUE, the value of field F, such that its sanitizer is
|
;; Wrap VALUE, the value of field F, such that its sanitizer is
|
||||||
;; called and its properties (thunked, delayed) honored. When
|
;; called and its properties (thunked, delayed) honored. When
|
||||||
@@ -222,6 +240,7 @@ of TYPE matches the expansion-time ABI."
|
|||||||
#`((struct-ref #,parent
|
#`((struct-ref #,parent
|
||||||
#,(field-index f))
|
#,(field-index f))
|
||||||
#,this-identifier)))
|
#,this-identifier)))
|
||||||
|
(check-shadowing f)
|
||||||
#`(lambda (x)
|
#`(lambda (x)
|
||||||
(syntax-parameterize ((#,this-identifier
|
(syntax-parameterize ((#,this-identifier
|
||||||
(lambda (s)
|
(lambda (s)
|
||||||
|
|||||||
@@ -302,6 +302,26 @@
|
|||||||
=>
|
=>
|
||||||
(,(foo-bar child) ,(foo-baz child))))))
|
(,(foo-bar child) ,(foo-baz child))))))
|
||||||
|
|
||||||
|
(test-assert "define-record-type* & inherited value shadowing"
|
||||||
|
(let ((exp '(begin
|
||||||
|
(define-record-type* <foo> foo make-foo
|
||||||
|
foo?
|
||||||
|
(bar foo-bar)
|
||||||
|
(baz foo-baz (thunked)))
|
||||||
|
|
||||||
|
(let ((x (foo (bar 1) (baz 2)))
|
||||||
|
(baz 123))
|
||||||
|
;; Below, the 'baz' binding for the inherited field value
|
||||||
|
;; shadows the 'baz' above, which should trigger a warning.
|
||||||
|
(foo (inherit x)
|
||||||
|
(baz (* baz 2)))))))
|
||||||
|
(string-contains
|
||||||
|
(call-with-output-string
|
||||||
|
(lambda (port)
|
||||||
|
(parameterize ((current-warning-port port))
|
||||||
|
(eval exp (test-module)))))
|
||||||
|
"shadows local variable")))
|
||||||
|
|
||||||
(test-assert "define-record-type* & delayed"
|
(test-assert "define-record-type* & delayed"
|
||||||
(begin
|
(begin
|
||||||
(define-record-type* <foo> foo make-foo
|
(define-record-type* <foo> foo make-foo
|
||||||
|
|||||||
Reference in New Issue
Block a user