diff --git a/pkgs/racket-test/tests/match/main.rkt b/pkgs/racket-test/tests/match/main.rkt index 9a77411425..31fd2a9659 100644 --- a/pkgs/racket-test/tests/match/main.rkt +++ b/pkgs/racket-test/tests/match/main.rkt @@ -1,7 +1,7 @@ #lang scheme/base (require (for-syntax scheme/base) - "match-tests.rkt" "other-plt-tests.rkt" "other-tests.rkt" + "match-tests.rkt" "match-exn-tests.rkt" "other-plt-tests.rkt" "other-tests.rkt" "examples.rkt" rackunit rackunit/text-ui) @@ -378,6 +378,7 @@ (run-tests (test-suite "Match Tests" plt-match-tests match-tests + match-exn-tests new-tests ;; from bruce other-tests diff --git a/pkgs/racket-test/tests/match/match-exn-tests.rkt b/pkgs/racket-test/tests/match/match-exn-tests.rkt new file mode 100644 index 0000000000..748851e564 --- /dev/null +++ b/pkgs/racket-test/tests/match/match-exn-tests.rkt @@ -0,0 +1,70 @@ +(module match-tests mzscheme + (require mzlib/match rackunit) + (provide match-exn-tests) + + (define simple-fail-tests + (test-suite + "Simple fall-through tests" + (test-case "No clauses" + (check-exn exn:misc:match? + (lambda () (match 3)))) + (test-case "Fall-through with integer" + (check-exn exn:misc:match? + (lambda () (match 3 + [2 'low] + [4 'high])))) + (test-case "Fall-through with #:when pattern" + (check-exn exn:misc:match? + (lambda () (match 3 + [x #:when (> x 4) x])))) + (test-case "Failure procedure" + (check-exn exn:misc:match? + (lambda () (match 3 + [3 (=> bye) + (bye) + 's] + [2 ':o])))))) + + (define exn-message-tests-1 + (test-suite + "Exception messages for match-let-XYZ" + (test-case "match" + (check-exn #rx"match: no matching clause for 3" + (lambda () (match 3)))) + (test-case "match*" + (check-exn #rx"match\\*: no matching clause for \\(3 4\\)" + (lambda () (match* [3 4])))) + (test-case "match-let" + (check-exn #rx"match-let: no matching clause for 3" + (lambda () (match-let ([2 3]) ':o)))) + (test-case "match-let-values" + (check-exn #rx"match-let-values: no matching clause for \\(3 4\\)" + (lambda () (match-let-values ([(2 x) (values 3 4)]) ':o)))) + (test-case "match-let*" + (check-exn #rx"match-let\\*: no matching clause for 3" + (lambda () (match-let* ([a 3] [2 a]) ':o)))) + (test-case "match-let*-values" + (check-exn #rx"match-let\\*-values: no matching clause for \\(3 4\\)" + (lambda () (match-let*-values ([(a) 3] [(2 x) (values a 4)]) ':o)))))) + + (define exn-message-tests-2 + (test-suite + "Exception messages other match forms" + (test-case "match-letrec" + (check-exn #rx"match-letrec: no matching clause for 3" + (lambda () (match-letrec ([a 3] [2 a]) ':o)))) + (test-case "match-letrec-values" + (check-exn #rx"match-letrec-values: no matching clause for \\(3 5\\)" + (lambda () (match-letrec-values ([(a) 3] [(2 _) (values a 5)]) ':o)))) + (test-case "match-define" + (check-exn #rx"match-define: no matching clause for \\(6 \\. 7\\)" + (lambda () (match-define (cons 3 x) '(6 . 7)) x))) + (test-case "match-define-values" + (check-exn #rx"match-define-values: no matching clause for \\(6 7\\)" + (lambda () (match-define-values (3 x) (values 6 7)) x))))) + + (define match-exn-tests + (test-suite "Tests for exceptions raised by match.rkt" + simple-fail-tests + exn-message-tests-1 + exn-message-tests-2))) diff --git a/racket/collects/racket/match/define-forms.rkt b/racket/collects/racket/match/define-forms.rkt index db1124b9e6..b7825399e0 100644 --- a/racket/collects/racket/match/define-forms.rkt +++ b/racket/collects/racket/match/define-forms.rkt @@ -76,6 +76,8 @@ (define-syntax (match-lambda** stx) (syntax-parse stx [(_ (~and clauses [(pats ...) . rhs]) ...) + (when (null? (syntax-e #'(rhs ...))) + (raise-syntax-error #f "expected at least one clause to match-lambda**" stx)) (with-syntax* ([vars (generate-temporaries (car (syntax-e #'((pats ...) ...))))] [body #`(match*/derived vars #,stx clauses ...)]) (syntax/loc stx (lambda vars body)))])) @@ -95,18 +97,25 @@ (match*/derived #,(append* idss) #,stx [(patss ... ...) (let () body1 body ...)])))])) - (define-syntax (match-let*-values stx) + ;; note: match-let*-values/derived is *not* provided + (define-syntax (match-let*-values/derived stx) (syntax-parse stx - [(_ () body1 body ...) + [(_ orig-stx () body1 body ...) (syntax/loc stx (let () body1 body ...))] - [(_ ([(pats ...) rhs] rest-pats ...) body1 body ...) + [(_ orig-stx ([(pats ...) rhs] rest-pats ...) body1 body ...) (with-syntax ([(ids ...) (generate-temporaries #'(pats ...))]) (quasisyntax/loc stx (let-values ([(ids ...) rhs]) - (match*/derived (ids ...) #,stx + (match*/derived (ids ...) orig-stx [(pats ...) #,(syntax/loc stx - (match-let*-values (rest-pats ...) - body1 body ...))]))))])) + (match-let*-values/derived + orig-stx (rest-pats ...) + body1 body ...))]))))])) + + (define-syntax (match-let*-values stx) + (syntax-parse stx + [(_ (~and cl ([(pats ...) rhs:expr] ...)) body1 body ...) + (quasisyntax/loc stx (match-let*-values/derived #,stx cl body1 body ...))])) ;; there's lots of duplication here to handle named let ;; some factoring out would do a lot of good @@ -122,50 +131,69 @@ (letrec ([nm (lambda vars loop-body)]) (nm init-exp ...))))] [(_ ([pat init-exp:expr] ...) body1 body ...) - (syntax/loc stx (match-let-values ([(pat) init-exp] ...) body1 body ...))])) + (quasisyntax/loc stx + ;; use of match*/derived instead of match-let-values fixes #1431 + ;; alternatively, we could have created let-values/derived but + ;; that is not really necessary + (match*/derived [init-exp ...] #,stx [(pat ...) (let () body1 body ...)]))])) - (define-syntax-rule (match-let* ([pat exp] ...) body1 body ...) - (match-let*-values ([(pat) exp] ...) body1 body ...)) + (define-syntax (match-let* stx) + (syntax-parse stx + [(_ ([pat rhs:expr] ...) body1 body ...) + (quasisyntax/loc stx + (match-let*-values/derived + #,stx + ([(pat) rhs] ...) + body1 body ...))])) + + ;; note: match-define-values/derived is *not* provided + ;; it may be useful enough to suggest we should provide it... + (define-syntax (match-define-values/derived stx) + (syntax-parse stx + [(_ orig-stx (pats ...) rhs:expr) + (with-syntax ([(ids ...) (generate-temporaries #'(pats ...))] + [(pb-ids ...) (pats->bound-vars parse-id (syntax->list #'(pats ...)))]) + (quasisyntax/loc stx + (define-values (pb-ids ...) + (let-values ([(ids ...) rhs]) + (match*/derived (ids ...) orig-stx + [(pats ...) (values pb-ids ...)])))))])) (define-syntax (match-letrec stx) (syntax-parse stx [(_ ((~and cl [pat exp]) ...) body1 body ...) (quasisyntax/loc stx - (let () - #,@(for/list ([c (in-syntax #'(cl ...))] - [p (in-syntax #'(pat ...))] - [e (in-syntax #'(exp ...))]) - (quasisyntax/loc c (match-define #,p #,e))) - body1 body ...))])) + (let () + #,@(for/list ([c (in-syntax #'(cl ...))] + [p (in-syntax #'(pat ...))] + [e (in-syntax #'(exp ...))]) + (quasisyntax/loc c + (match-define-values/derived #,stx (#,p) #,e))) + body1 body ...))])) (define-syntax (match-letrec-values stx) (syntax-parse stx [(_ ((~and cl [(pat ...) exp]) ...) body1 body ...) (quasisyntax/loc stx - (let () - #,@(for/list ([c (in-syntax #'(cl ...))] - [p (in-syntax #'((pat ...) ...))] - [e (in-syntax #'(exp ...))]) - (quasisyntax/loc c (match-define-values #,p #,e))) - body1 body ...))])) + (let () + #,@(for/list ([c (in-syntax #'(cl ...))] + [ps (in-syntax #'((pat ...) ...))] + [e (in-syntax #'(exp ...))]) + (quasisyntax/loc c + (match-define-values/derived #,stx #,ps #,e))) + body1 body ...))])) (define-syntax (match-define stx) (syntax-parse stx [(_ pat rhs:expr) - (let ([p (parse-id #'pat)]) - (with-syntax ([vars (bound-vars p)]) - (quasisyntax/loc stx - (define-values vars (match*/derived (rhs) #,stx - [(pat) (values . vars)])))))])) + (quasisyntax/loc stx + (match-define-values/derived #,stx (pat) rhs))])) (define-syntax (match-define-values stx) (syntax-parse stx [(_ (pats ...) rhs:expr) - (with-syntax ([(ids ...) (pats->bound-vars parse-id (syntax->list #'(pats ...)))]) - (syntax/loc stx - (define-values (ids ...) - (match/values rhs - [(pats ...) (values ids ...)]))))])) + (quasisyntax/loc stx + (match-define-values/derived #,stx (pats ...) rhs))])) (define-syntax (define/match stx) (syntax-parse stx diff --git a/racket/collects/racket/match/gen-match.rkt b/racket/collects/racket/match/gen-match.rkt index 4388df4e19..821713226b 100644 --- a/racket/collects/racket/match/gen-match.rkt +++ b/racket/collects/racket/match/gen-match.rkt @@ -24,9 +24,8 @@ (with-disappeared-uses (syntax-parse clauses [([pats . rhs] ...) - (parameterize ([orig-stx stx]) - (unless (syntax->list es) - (raise-syntax-error 'match* "expected a sequence of expressions to match" es))) + (unless (syntax->list es) + (raise-syntax-error 'match* "expected a sequence of expressions to match" es)) (define/with-syntax form-name (syntax-case stx () [(fname . _) diff --git a/racket/collects/racket/match/patterns.rkt b/racket/collects/racket/match/patterns.rkt index c175293ba5..9c8fe18f58 100644 --- a/racket/collects/racket/match/patterns.rkt +++ b/racket/collects/racket/match/patterns.rkt @@ -12,8 +12,6 @@ get-key (struct-out Row))) -(define orig-stx (make-parameter #f)) - (define-struct Pat () #:transparent) ;; v is an identifier (define-struct (Var Pat) (v)