Skip to content

Commit ef1a5e9

Browse files
Copilotjackfirth
andauthored
Count disappeared macro uses via origin property (#751)
Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: jackfirth <[email protected]>
1 parent 1c757cf commit ef1a5e9

File tree

2 files changed

+103
-53
lines changed

2 files changed

+103
-53
lines changed

default-recommendations/analyzers/identifier-usage-test.rkt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,3 +377,16 @@ analysis-test: "twice-used local variable in macro definition"
377377
@inspect - a
378378
@property usage-count
379379
@assert 2
380+
381+
382+
analysis-test: "disappeared use of macro"
383+
--------------------
384+
(require (for-syntax racket/base))
385+
(define-syntax (m stx)
386+
#'(void))
387+
(m)
388+
--------------------
389+
@within - (m stx)
390+
@inspect - m
391+
@property usage-count
392+
@assert 1

default-recommendations/analyzers/identifier-usage.rkt

Lines changed: 90 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -32,64 +32,101 @@
3232
;@----------------------------------------------------------------------------------------------------
3333

3434

35+
;; Extract identifiers from the 'origin syntax property
36+
;; The 'origin property can be either:
37+
;; - A single syntax object
38+
;; - A list of syntax objects and/or pairs
39+
;; - Pairs can contain syntax objects or lists of syntax objects
40+
;; We extract all identifiers from it recursively and label them with the given phase
41+
(define (origin-property-identifiers stx phase)
42+
(define origin (syntax-property stx 'origin))
43+
44+
(define (extract-ids obj)
45+
(cond
46+
[(not obj) (stream)]
47+
[(identifier? obj)
48+
;; Add the phase property to the identifier so it matches correctly
49+
(stream (syntax-property obj 'phase phase))]
50+
[(syntax? obj) (stream)] ; syntax but not identifier
51+
[(pair? obj)
52+
(stream-append (extract-ids (car obj))
53+
(extract-ids (cdr obj)))]
54+
[(list? obj)
55+
(apply stream-append (map extract-ids obj))]
56+
[else (stream)]))
57+
58+
(extract-ids origin))
59+
60+
3561
;; Find all identifier usage sites (not binding sites)
3662
(define (usage-site-identifiers expanded-stx)
3763
(let loop ([expanded-stx expanded-stx] [phase 0])
3864
(define (recur stx)
3965
(loop stx phase))
40-
(syntax-search expanded-stx
41-
#:literal-sets ([kernel-literals #:phase phase])
42-
43-
;; Phase mismatch - recurse with correct phase
44-
[(id:id _ ...)
45-
#:do [(define id-phase (syntax-property (attribute id) 'phase))]
46-
#:when (not (equal? id-phase phase))
47-
(loop this-syntax id-phase)]
48-
49-
;; Skip quote-syntax - no identifier usages inside
50-
[(quote-syntax _ ...) (stream)]
51-
52-
;; define-values: recurse into RHS only (LHS is bindings)
53-
[(define-values (_ ...) rhs)
54-
(recur (attribute rhs))]
55-
56-
;; define-syntaxes: recurse into RHS at phase+1 (LHS is bindings)
57-
[(define-syntaxes (_ ...) rhs)
58-
(loop (attribute rhs) (add1 phase))]
59-
60-
;; let-values/letrec-values: recurse into RHS and body (binding ids excluded by pattern)
61-
[((~or let-values letrec-values) ([(_ ...) rhs] ...) body ...)
62-
(apply stream-append (append (map recur (attribute rhs))
63-
(map recur (attribute body))))]
64-
65-
;; lambda: formals are bindings, recurse into body only
66-
[(#%plain-lambda _ body ...)
67-
(apply stream-append (map recur (attribute body)))]
68-
69-
;; case-lambda: formals are bindings, recurse into bodies only
70-
[(case-lambda [_ body ...] ...)
71-
(apply stream-append (map recur (append* (attribute body))))]
72-
73-
;; set!: the identifier is used, and recurse into RHS
74-
[(set! id:id rhs)
75-
(stream-cons (attribute id) (recur (attribute rhs)))]
76-
77-
;; #%top: the identifier is used
78-
[(#%top . id:id)
79-
(stream (attribute id))]
80-
81-
;; #%variable-reference with identifier
82-
[(#%variable-reference id:id)
83-
(stream (attribute id))]
84-
85-
;; #%variable-reference with #%top
86-
[(#%variable-reference (#%top . id:id))
87-
(stream (attribute id))]
88-
89-
;; Standalone identifier - this is a usage!
90-
[id:id
91-
#:when (identifier? this-syntax)
92-
(stream (attribute id))])))
66+
67+
;; Collect identifiers from origin properties of all syntax objects
68+
(define origin-ids
69+
(apply stream-append
70+
(for/list ([stx-node (in-stream (syntax-search-everything expanded-stx))])
71+
(origin-property-identifiers stx-node phase))))
72+
73+
;; Collect identifiers from the expanded syntax tree
74+
(define expanded-ids
75+
(syntax-search expanded-stx
76+
#:literal-sets ([kernel-literals #:phase phase])
77+
78+
;; Phase mismatch - recurse with correct phase
79+
[(id:id _ ...)
80+
#:do [(define id-phase (syntax-property (attribute id) 'phase))]
81+
#:when (not (equal? id-phase phase))
82+
(loop this-syntax id-phase)]
83+
84+
;; Skip quote-syntax - no identifier usages inside
85+
[(quote-syntax _ ...) (stream)]
86+
87+
;; define-values: recurse into RHS only (LHS is bindings)
88+
[(define-values (_ ...) rhs)
89+
(recur (attribute rhs))]
90+
91+
;; define-syntaxes: recurse into RHS at phase+1 (LHS is bindings)
92+
[(define-syntaxes (_ ...) rhs)
93+
(loop (attribute rhs) (add1 phase))]
94+
95+
;; let-values/letrec-values: recurse into RHS and body (binding ids excluded by pattern)
96+
[((~or let-values letrec-values) ([(_ ...) rhs] ...) body ...)
97+
(apply stream-append (append (map recur (attribute rhs))
98+
(map recur (attribute body))))]
99+
100+
;; lambda: formals are bindings, recurse into body only
101+
[(#%plain-lambda _ body ...)
102+
(apply stream-append (map recur (attribute body)))]
103+
104+
;; case-lambda: formals are bindings, recurse into bodies only
105+
[(case-lambda [_ body ...] ...)
106+
(apply stream-append (map recur (append* (attribute body))))]
107+
108+
;; set!: the identifier is used, and recurse into RHS
109+
[(set! id:id rhs)
110+
(stream-cons (attribute id) (recur (attribute rhs)))]
111+
112+
;; #%top: the identifier is used
113+
[(#%top . id:id)
114+
(stream (attribute id))]
115+
116+
;; #%variable-reference with identifier
117+
[(#%variable-reference id:id)
118+
(stream (attribute id))]
119+
120+
;; #%variable-reference with #%top
121+
[(#%variable-reference (#%top . id:id))
122+
(stream (attribute id))]
123+
124+
;; Standalone identifier - this is a usage!
125+
[id:id
126+
#:when (identifier? this-syntax)
127+
(stream (attribute id))]))
128+
129+
(stream-append origin-ids expanded-ids)))
93130

94131

95132
(define (fully-expanded-syntax-binding-table stx)

0 commit comments

Comments
 (0)