Syntactic test suite coverage in Dr Racket

Hi. I am trying to use drracket's syntactic test suite coverage.
image
It always marks expressions in functions with optional arguments (see the screenshot).
Can I make drracket test coverage to ignore these lines?

3 Likes

This looks like a bug to me.

The expression (func) calls funcwithout keyword arguments, so the default expressions 100, 200, and, #t are used. Therefore the tool shouldn't color them black.

1 Like

It looks to me like this problem is specific to keyword arguments; when using other default arguments, the coloring appears correct. So, for example,

#lang racket

(define (f [x 99])
  (+ x 1234))

(f)

results in full coverage.

File a bug report?

It also looks like it's limited to keyword argument functions that are directly applied:

#lang racket/base

(define (f [x 42])
  x)
(f)

(define (g #:x [x 42])
  x)
(g) ;<-- actually a macro invocation
(apply g '()) ;<-- actually runtime; now "42" is shown as used

The expansion of f is not much more than case-lambda.

The expansion of g is... fairly complicated. It looks like g is defined as a runtime function value -- but also an g is defined as a syntax transformer, for expansion time. Probably an optimization? Anyway in that case 42 seems truly not to be used, at runtime... or something like that?

(module anonymous-module racket/base
  (#%module-begin
   (module configure-runtime '#%kernel
     (#%module-begin (#%require racket/runtime-config) (#%app configure (quote #f))))
   (define-values (f)
     (let-values (((f)
                   (lambda (x1)
                     (let-values (((x) (if (quote #f) (quote 42) x1))) (let-values () x)))))
       (case-lambda [() (#%app f (quote 42))] [(x1) (#%app f x1)])))
   (#%app call-with-values (lambda () (#%app f)) print-values)
   (define-syntaxes (g)
     (#%app
      make-keyword-syntax
      (lambda () (#%app values (quote-syntax g) (quote-syntax g4)))
      (quote 0)
      '()
      (quote #f)
      '()
      '((#:x x2:18 x3:19 #f 42))))
   (define-values:20 (g)
     (lambda (x2:18)
       (let-values:21 (((x) (if:22 (quote #f) (quote 42) x2:18))) (let-values:23 () x))))
   (define-values:24 (g:25)
     (lambda (given-kws given-args)
       (let-values:26 (((x3:19) (#%app:27 pair?:27 given-kws)))
         (let-values:28 (((x2:18) (if:27 x3:19 (#%app:27 car:27 given-args) (quote 42))))
           (#%app g x2:18)))))
   (define-values:29 (g4)
     (#%app
      make-optional-keyword-procedure
      (lambda (given-kws given-argc)
        (if:30 (#%app:31 =:31 given-argc (quote 2))
          (let-values:32 (((l125477:33) given-kws))
            (let-values:34 (((l125477:33)
                             (if:33 (#%app:33 null?:33 l125477:33)
                               l125477:33
                               (if:33 (#%app:33 eq?:33 (#%app:33 car:33 l125477:33) '#:x)
                                 (#%app:33 cdr:33 l125477:33)
                                 l125477:33))))
              (#%app:35 null?:35 l125477:33)))
          (quote #f)))
      (case-lambda:36 [(given-kws given-args) (#%app:36 g:25 given-kws given-args)])
      null
      '(#:x)
      (case-lambda:37 [() (#%app:37 g:25 null null)])))
   (#%app:38
    call-with-values:38
    (lambda:38 ()
      (let-values:39 ()
        (if:40 (#%app:40 variable-reference-constant?:40 (#%variable-reference:40 g4:41))
          (#%app:40 g:42 (quote 42))
          (#%app:40 g4:41))))
    print-values:38)
   (#%app:43 call-with-values:43 (lambda:43 () (#%app:44 apply:45 g4:46 '())) print-values:43)))

Not looking at the keyword implementation directly but just trying examples it also looks like an optimization to me. While this program shows the 42 as uncovered:

#lang racket
(define (g #:x [x 3])
  x)
(g)

This program doesn't:

#lang racket
(define (g #:x [x (+ 41 1)])
  x)
(g)

I wonder if one of the quoted 42s in the fully expanded form needs to have the source location of the constant put onto it?

1 Like

Ah, that could be it. In the expansion of the direct (g), here:

(#%app:38
    call-with-values:38
    (lambda:38 ()
      (let-values:39 ()
        (if:40 (#%app:40 variable-reference-constant?:40 (#%variable-reference:40 g4:41))
          (#%app:40 g:42 (quote 42))
          (#%app:40 g4:41))))
    print-values:38)

The 42 has srcloc in private/kw.rkt, not in the user program.

1 Like

Right, what seems to happen is that the default value is stored in make-keyword-syntax as a value rather than a syntax object, and so there's no syntax location to re-use. I'm not sure how difficult it would be to change that to using a syntax object instead. The comment on line 1380 of racket/private/kw.rkt suggests that @mflatt thought about at least some aspects of this previously.

I've pushed a repair. Thanks, everyone, for reporting and tracking down the problem.

3 Likes