improve generic method default error msg:

- report position of bad self arg
- report and label other args, including kw, optional, rest, if present

merge of github pull-request #821
This commit is contained in:
Stephen Chang 2015-04-30 12:35:30 -04:00
parent d6b587288a
commit 56cf724d12
2 changed files with 181 additions and 3 deletions

View File

@ -62,4 +62,78 @@
;; 1000 ms should be far more than enough.
(or (engine-run 1000 e)
(error "computation did not terminate")))))
;; pull-request #821, report correct position of contract violation
(check-exn #rx"meth:.*1st"
(lambda () (convert-compile-time-error
(let ()
(define-generics AA [meth AA b c])
(meth 1 2 3))))
"1st arg contract violation")
(check-exn #rx"meth:.*2nd"
(lambda () (convert-compile-time-error
(let ()
(define-generics AA [meth a AA c])
(meth 1 2 3))))
"2nd arg contract violation")
(check-exn #rx"meth:.*3rd"
(lambda () (convert-compile-time-error
(let ()
(define-generics AA [meth a b AA])
(meth 1 2 3))))
"3rd arg contract violation")
(check-exn #rx"optional.*default"
(lambda () (convert-compile-time-error
(let ()
(define-generics AA [meth a b AA [d]])
(meth 1 2 3))))
"omitted optional arg")
(check-exn #rx"optional.*optarg"
(lambda () (convert-compile-time-error
(let ()
(define-generics AA [meth a b AA [d]])
(meth 1 2 3 "optarg"))))
"given optional arg")
(check-exn #rx"required keyword"
(lambda () (convert-compile-time-error
(let ()
(define-generics AA [meth a b AA [d] #:reqkw reqkw])
(meth 1 2 3 "optarg"))))
"omitted required kw arg")
(check-exn #rx"#:reqkw: \"requiredkw"
(lambda () (convert-compile-time-error
(let ()
(define-generics AA [meth a b AA [d] #:reqkw reqkw])
(meth 1 2 3 "optarg" #:reqkw "requiredkw"))))
"given required kw arg")
(check-exn #rx"#:optkw.*optional.*default"
(lambda () (convert-compile-time-error
(let ()
(define-generics AA [meth a b AA [d] #:optkw [optkw]])
(meth 1 2 3))))
"omitted optional kw arg")
(check-exn #rx"#:optkw.*optional.*optarg"
(lambda () (convert-compile-time-error
(let ()
(define-generics AA [meth a b AA [d] #:optkw [optkw]])
(meth 1 2 3 #:optkw "optarg"))))
"given optional kw arg")
(check-exn #rx"argument position: 1st$"
(lambda () (convert-compile-time-error
(let ()
(define-generics AA [meth AA])
(meth 1))))
"no other args")
(check-exn #rx"argument position: 1st\n"
(lambda () (convert-compile-time-error
(let ()
(define-generics AA [meth AA a])
(meth 1 2))))
"other args present")
(check-exn #rx"rest args:"
(lambda () (convert-compile-time-error
(let ()
(define-generics AA [meth AA . a])
(meth 1 2))))
"rest args present")
)

View File

@ -65,6 +65,93 @@
(define defaults-by-type (map generate-methods default-preds))
(define defaults-by-method (transpose-methods defaults-by-type))
(define/with-syntax ((req req-kw opt opt-kw rest) ...)
(stx-map
(λ (sg) (call-with-values (lambda () (parse-method-signature sg)) list))
#'(method-signature ...)))
;; (0-based) position of the "self" argument, for error reporting purposes
(define/with-syntax (self-i ...)
(stx-map
(λ (req-ids)
(for/first ([(id i) (in-indexed (syntax->list req-ids))]
#:when (free-identifier=? id #'self-name))
i))
#'(req ...)))
;; interleaves two stx lists
(define (stx-merge stx)
(define stx-lst1 (syntax->list (stx-car stx)))
(define stx-lst2 (syntax->list (car (stx-cdr stx))))
(apply append (map list stx-lst1 stx-lst2)))
; removes the lst element at (0-based) index pos
(define (remove-at lst pos)
(let loop ([i 0] [lst lst])
(cond [(null? lst) '()]
[else (if (= i pos)
(cdr lst)
(cons (car lst) (loop (add1 i) (cdr lst))))])))
(define (stx-drop-last stx)
(reverse (cdr (reverse (syntax->list stx)))))
(define/with-syntax ((req-name ...) ...) #'(req ...))
(define/with-syntax (((reqkw-key reqkw-val) ...) ...) #'(req-kw ...))
(define/with-syntax ((opt-name ...) ...) #'(opt ...))
(define/with-syntax (((optkw-key optkw-val) ...) ...) #'(opt-kw ...))
(define/with-syntax (arg-labels/restargs ...)
#'(((symbol->string 'req-name) ...
(string-append (symbol->string 'opt-name) " (optional)") ...
(string-append "#:" (keyword->string 'reqkw-key)) ...
(string-append "#:" (keyword->string 'optkw-key) " (optional)") ...
"rest args") ...))
(define/with-syntax (arg-labels ...) ; drop restargs if none
(stx-map
(λ (labels rst)
(if (syntax->datum rst) labels (stx-drop-last labels)))
#'(arg-labels/restargs ...) #'(rest ...)))
(define/with-syntax (arg-vals/restargs ...)
#`((req-name ...
opt-name ...
reqkw-val ...
optkw-val ...
rest) ...))
(define/with-syntax (arg-vals ...) ; drop restargs if none
(stx-map
(λ (args rst)
(if (syntax->datum rst) args (stx-drop-last args)))
#'(arg-vals/restargs ...) #'(rest ...)))
(define/with-syntax (bad-arg-label ...) ; only the bad arg name
(stx-map
(λ (args i) (list-ref (stx->list args) (syntax->datum i)))
#'(arg-labels ...) #'(self-i ...)))
(define/with-syntax (bad-arg ...) ; only the bad arg
(stx-map
(λ (args i) (list-ref (stx->list args) (syntax->datum i)))
#'(arg-vals ...) #'(self-i ...)))
(define/with-syntax (other-arg-labels ...) ; other arg labels
(stx-map
(λ (args i) (remove-at (stx->list args) (syntax->datum i)))
#'(arg-labels ...) #'(self-i ...)))
(define/with-syntax (other-args ...) ; other args
(stx-map
(λ (args i) (remove-at (stx->list args) (syntax->datum i)))
#'(arg-vals ...) #'(self-i ...)))
(define/with-syntax ((other-labels+args ...) ...) ; other args interleaved
(stx-map stx-merge #'((other-arg-labels other-args) ...)))
(define/with-syntax (err-fmt-str ...)
(stx-map
(λ (labels)
(if (null? (syntax->list labels))
(string-append "contract violation:\n"
"expected: ~a\n"
"given: ~v\n"
"argument position: ~a")
(string-append "contract violation:\n"
"expected: ~a\n"
"given: ~v\n"
"argument position: ~a\n"
"other arguments...:")))
#'(other-arg-labels ...)))
(define/with-syntax size n)
(define/with-syntax [method-index ...] method-indices)
(define/with-syntax contract-str
@ -178,6 +265,21 @@
[else (raise-argument-error 'supported-name
'contract-str
self-name)]))
;; Converts 0-based index to an ordinal string
;; 0 => 1st
;; 1 => 2nd
(define (pos->ord n)
(define n/1base (add1 n))
(string-append
(number->string n/1base)
(case n/1base
[(11 12 13) "th"]
[else (case (remainder n/1base 10)
[(1) "st"]
[(2) "nd"]
[(3) "rd"]
[else "th"])])))
(define-generic-method
method-name
method-signature
@ -189,9 +291,11 @@
(vector-ref (accessor-name self-name) 'method-index)]
[(default-disp-name self-name) default-by-method]
...
[else (raise-argument-error 'method-name
'contract-str
self-name)])
[else
(raise-arguments-error
'method-name
(format err-fmt-str 'contract-str bad-arg (pos->ord self-i))
other-labels+args ...)])
fallback)
original)
...