|
11 | 11 | source-code-analysis?)] |
12 | 12 | [source-code-analysis? (-> any/c boolean?)] |
13 | 13 | [source-code-analysis-code (-> source-code-analysis? source?)] |
| 14 | + [source-code-analysis-enriched-syntax (-> source-code-analysis? syntax?)] |
| 15 | + [source-code-analysis-visited-paths (-> source-code-analysis? (listof syntax-path?))] |
14 | 16 | [source-code-analysis-visited-forms (-> source-code-analysis? (listof syntax?))] |
15 | 17 | [source-code-analysis-expansion-time-output (-> source-code-analysis? immutable-string?)] |
16 | 18 | [source-code-analysis-namespace (-> source-code-analysis? namespace?)] |
|
33 | 35 | rebellion/collection/sorted-map |
34 | 36 | rebellion/collection/sorted-set |
35 | 37 | rebellion/collection/vector/builder |
| 38 | + rebellion/streaming/reducer |
36 | 39 | rebellion/streaming/transducer |
37 | 40 | rebellion/type/record |
38 | 41 | resyntax/default-recommendations/analyzers/identifier-usage |
|
55 | 58 |
|
56 | 59 |
|
57 | 60 | (define-record-type source-code-analysis |
58 | | - (code visited-forms expansion-time-output namespace added-syntax-properties)) |
| 61 | + (code enriched-syntax visited-paths expansion-time-output namespace added-syntax-properties)) |
| 62 | + |
| 63 | + |
| 64 | +(define (source-code-analysis-visited-forms analysis) |
| 65 | + (define stx (source-code-analysis-enriched-syntax analysis)) |
| 66 | + (define paths (source-code-analysis-visited-paths analysis)) |
| 67 | + (for/list ([path (in-sorted-set paths)]) |
| 68 | + (syntax-ref stx path))) |
59 | 69 |
|
60 | 70 |
|
61 | 71 | (define (source-analyze code |
|
76 | 86 | (log-resyntax-debug "original source name: ~a" program-source-name) |
77 | 87 | (log-resyntax-debug "original syntax:\n ~a" program-stx) |
78 | 88 | (define current-expand-observe (dynamic-require ''#%expobs 'current-expand-observe)) |
79 | | - (define original-visits (make-vector-builder)) |
80 | | - (define most-recent-visits-by-original-path (make-hash)) |
| 89 | + (define visited-syntaxes (make-mutable-sorted-map #:key-comparator syntax-path<=>)) |
| 90 | + (define context-syntaxes (make-mutable-sorted-map #:key-comparator syntax-path<=>)) |
81 | 91 |
|
82 | 92 | (define/guard (resyntax-should-analyze-syntax? stx #:as-visit? [as-visit? #true]) |
83 | 93 | (guard (syntax-original-and-from-source? stx program-source-name) #:else #false) |
|
98 | 108 | (define/match (observe-event! sig val) |
99 | 109 | [('visit (? syntax? visited)) |
100 | 110 | (when (resyntax-should-analyze-syntax? visited) |
101 | | - (vector-builder-add original-visits visited)) |
| 111 | + (define visited-path (syntax-original-path visited)) |
| 112 | + (unless visited-path |
| 113 | + (raise-arguments-error |
| 114 | + 'source-analyze "visit is missing original path" |
| 115 | + "visited syntax" visited)) |
| 116 | + (sorted-map-put-if-absent! visited-syntaxes visited-path visited)) |
102 | 117 | (for ([visit-subform (in-stream (syntax-search-everything visited))] |
103 | 118 | #:when (and (resyntax-should-analyze-syntax? visit-subform #:as-visit? #false) |
104 | 119 | (syntax-has-original-path? visit-subform))) |
105 | 120 | (define path (syntax-original-path visit-subform)) |
106 | | - (hash-set! most-recent-visits-by-original-path path visit-subform))] |
| 121 | + (sorted-map-put! context-syntaxes path visit-subform))] |
107 | 122 | [(_ _) (void)]) |
108 | 123 |
|
109 | 124 | (define output-port (open-output-string)) |
|
112 | 127 | [current-output-port output-port]) |
113 | 128 | (expand program-stx))) |
114 | 129 |
|
| 130 | + (define visited-paths (sorted-set->immutable-sorted-set (sorted-map-keys visited-syntaxes))) |
| 131 | + |
115 | 132 | ;; We evaluate the module in order to ensure it's declared in the namespace, then we attach it at |
116 | 133 | ;; expansion time to ensure the module is visited (but not instantiated). This allows refactoring |
117 | 134 | ;; rules to access expansion-time values reflectively via the analysis namespace. |
|
121 | 138 | (define output (get-output-string output-port)) |
122 | 139 | (define movement-table (syntax-movement-table expanded)) |
123 | 140 |
|
| 141 | + ;; Here we search the expanded syntax for unambiguously original syntax objects and insert them |
| 142 | + ;; into the context table. This ensures that any identifiers which survive expansion will contain |
| 143 | + ;; all their post-expansion scopes when being analyzed by rules. |
| 144 | + (for ([e (in-sorted-map movement-table)]) |
| 145 | + (match-define (entry orig-path exp-paths) e) |
| 146 | + (define num-orig-paths |
| 147 | + (transduce exp-paths |
| 148 | + (filtering (λ (path) (syntax-original? (syntax-ref expanded path)))) |
| 149 | + #:into into-count)) |
| 150 | + (when (>= num-orig-paths 2) |
| 151 | + (log-resyntax-debug |
| 152 | + (string-append |
| 153 | + "ignoring expansion lexical context for original path ~a because" |
| 154 | + " multiple expanded forms claim to originate from that path and be original" |
| 155 | + " syntax.") |
| 156 | + orig-path)) |
| 157 | + (when (equal? num-orig-paths 1) |
| 158 | + (define exp-path (present-value (sorted-set-least-element exp-paths))) |
| 159 | + (sorted-map-put! context-syntaxes orig-path (syntax-ref expanded exp-path)))) |
| 160 | + |
| 161 | + (define enriched-program-stx-without-analyzer-props |
| 162 | + (for/fold ([program-stx program-stx]) |
| 163 | + ([e (in-sorted-map context-syntaxes)]) |
| 164 | + (match-define (entry orig-path context-stx) e) |
| 165 | + (define child-stx (syntax-ref program-stx orig-path)) |
| 166 | + (define stx-to-use-for-props (sorted-map-get visited-syntaxes orig-path child-stx)) |
| 167 | + (define enriched-child |
| 168 | + (datum->syntax context-stx (syntax-e child-stx) child-stx stx-to-use-for-props)) |
| 169 | + (syntax-set program-stx orig-path enriched-child))) |
| 170 | + |
124 | 171 | (define property-selection-table |
125 | 172 | (transduce movement-table |
126 | 173 | (filtering |
|
151 | 198 | (define valid? (syntax-contains-path? expanded path)) |
152 | 199 | (unless valid? |
153 | 200 | (log-resyntax-warning |
154 | | - "ignoring property with out-of-syntax path returned by analyzer~n path: ~a~n property key: ~a" |
| 201 | + (string-append |
| 202 | + "ignoring property with out-of-syntax path returned by analyzer\n" |
| 203 | + " path: ~a\n" |
| 204 | + " property key: ~a") |
155 | 205 | path |
156 | 206 | key)) |
157 | 207 | valid?)) |
|
170 | 220 | (string-indent (pretty-format expansion-analyzer-props-adjusted-for-visits) #:amount 2)) |
171 | 221 | (log-resyntax-debug "syntax properties from expansion analyzers:\n~a" props-str)) |
172 | 222 |
|
173 | | - (define (enrich stx #:skip-root? [skip-root? #false]) |
174 | | - (syntax-traverse stx |
175 | | - #:skip-root? skip-root? |
176 | | - [child |
177 | | - #:do [(define child-stx (attribute child)) |
178 | | - (define orig-path (syntax-original-path child-stx))] |
179 | | - #:when (and orig-path (sorted-map-contains-key? movement-table orig-path)) |
180 | | - #:do [(define expansions |
181 | | - (transduce (sorted-map-get movement-table orig-path) |
182 | | - (mapping (λ (p) (syntax-ref expanded p))) |
183 | | - (filtering syntax-original?) |
184 | | - #:into into-list))] |
185 | | - #:when (equal? (length expansions) 1) |
186 | | - (match-define (list expanded-child) expansions) |
187 | | - (log-resyntax-debug "enriching ~a with scopes from expansion" child-stx) |
188 | | - (enrich (datum->syntax expanded-child (syntax-e child-stx) child-stx child-stx) |
189 | | - #:skip-root? #true)] |
190 | | - [child |
191 | | - #:do [(define child-stx (attribute child)) |
192 | | - (define orig-path (syntax-original-path child-stx))] |
193 | | - #:when (and orig-path (hash-has-key? most-recent-visits-by-original-path orig-path)) |
194 | | - #:do [(define visit (hash-ref most-recent-visits-by-original-path orig-path))] |
195 | | - (log-resyntax-debug "enriching ~a with scopes from visit" child-stx) |
196 | | - (enrich (datum->syntax visit (syntax-e child-stx) child-stx child-stx) #:skip-root? #true)] |
197 | | - #:parent-context-modifier values |
198 | | - #:parent-srcloc-modifier values |
199 | | - #:parent-props-modifier values)) |
200 | | - |
201 | | - (define visited |
202 | | - (transduce (build-vector original-visits) |
203 | | - (peeking |
204 | | - (λ (visit) |
205 | | - (unless (syntax-original-path visit) |
206 | | - (raise-arguments-error |
207 | | - 'source-analyze "pre-enriched visit is missing original path" |
208 | | - "visited syntax" visit)))) |
209 | | - (deduplicating #:key syntax-original-path) |
210 | | - (mapping |
211 | | - (λ (visit) |
212 | | - (define path (syntax-original-path visit)) |
213 | | - (define visit-props |
214 | | - (syntax-property-bundle-get-all-properties |
215 | | - expansion-analyzer-props-adjusted-for-visits path)) |
216 | | - (syntax-add-all-properties visit visit-props))) |
217 | | - (mapping enrich) |
218 | | - (peeking |
219 | | - (λ (visit) |
220 | | - (unless (syntax-original-path visit) |
221 | | - (raise-arguments-error |
222 | | - 'source-analyze "post-enriched visit is missing original path" |
223 | | - "visited syntax" visit)))) |
224 | | - (sorting syntax-path<=> #:key syntax-original-path) |
225 | | - #:into into-list)) |
226 | | - |
227 | | - (log-resyntax-debug "visited ~a forms" (length visited)) |
| 223 | + (define enriched-program-stx |
| 224 | + (syntax-add-all-properties enriched-program-stx-without-analyzer-props |
| 225 | + expansion-analyzer-props-adjusted-for-visits)) |
| 226 | + |
| 227 | + (log-resyntax-debug "visited ~a forms" (sorted-set-size visited-paths)) |
228 | 228 | (source-code-analysis #:code code |
229 | | - #:visited-forms visited |
| 229 | + #:enriched-syntax enriched-program-stx |
| 230 | + #:visited-paths visited-paths |
230 | 231 | #:expansion-time-output output |
231 | 232 | #:namespace ns |
232 | 233 | #:added-syntax-properties expansion-analyzer-props-adjusted-for-visits))) |
|
0 commit comments