1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
//! Re-parsing a crate's module files from a `LateLintPass`.
//!
//! A rule that inspects the *source-level* layout of `use` statements —
//! the blank-line grouping of `perfectionist::import_grouping`, the
//! granularity of `perfectionist::import_granularity`, the `self`
//! handling of `perfectionist::self_import` — hits a wall in a
//! pre-expansion `EarlyLintPass`: an out-of-line `mod foo;` module is
//! still `ModKind::Unloaded` there (its file is not parsed until macro
//! expansion), so the walk never sees it and silently skips every
//! separate-file submodule.
//!
//! Running in a `LateLintPass` and re-parsing each module file instead
//! reaches every submodule while keeping `#[cfg(...)]` gates intact —
//! parsing does not strip cfg, unlike the post-expansion AST, which is
//! why the pre-expansion pass existed in the first place.
//!
//! Two entry points share the same re-parse machinery:
//! [`parse_crate_module_files`] returns every file's freshly parsed
//! [`Crate`] alongside the body spans of the crate's live modules (the
//! inline-recursion guard a caller needs to skip cfg-disabled inline
//! modules), and [`for_each_module_file`] is a thin callback wrapper for
//! callers that handle one file at a time and do their own descent.
use HashSet;
use Arc;
use Crate;
use DiagCtxt;
use SilentEmitter;
use ;
use StripTokens;
use new_parser_from_source_str;
use ParseSess;
use LOCAL_CRATE;
use SourceMap;
use ;
/// The byte range `(lo, hi)` of a module body, used as a stable key
/// across the re-parse / HIR boundary. Full [`rustc_span::Span`]
/// equality also compares the `parent` `LocalDefId` and `SyntaxContext`,
/// which differ between a freshly re-parsed span and the HIR-lowered
/// span of the same source bytes; the byte range matches and uniquely
/// identifies a module body.
pub type SpanRange = ;
/// Re-parse every on-disk source file that backs a module in this
/// crate's HIR module tree, returning each file's freshly parsed
/// [`Crate`] (crate root plus every out-of-line `mod foo;` file) along
/// with `live_module_spans`.
///
/// `live_module_spans` is the body [`SpanRange`] of every module live in
/// the compiled crate (each inline `mod m { ... }` and out-of-line
/// `mod foo;`). A re-parse keeps cfg-disabled modules — parsing does not
/// strip cfg — so a caller walking the re-parsed AST must consult this
/// set before descending into an inline module, or it would lint a
/// `#[cfg(FALSE)] mod m { ... }` (e.g. `#[cfg(test)] mod tests`) that is
/// not part of the build. The result is a tuple rather than a named
/// struct because a struct field of type [`Vec<Crate>`] makes rustdoc's
/// auto-trait synthesis overflow on the AST's recursive type graph.
///
/// The throwaway [`ParseSess`] shares the real [`SourceMap`], so every
/// span in the returned ASTs — and any suggestion built from them —
/// points at the real files. Its [`DiagCtxt`] is wired to a
/// [`SilentEmitter`], so a file that does not parse as a standalone
/// module is skipped rather than surfacing parse errors.
///
/// Re-parsing is scoped to files that actually back a module in the HIR
/// module tree, which excludes `include!` fragments, `include_str!`-ed
/// `.rs` data, and proc-macro-synthesised modules — none of which should
/// be re-parsed and flagged as if the user wrote them as a module.
pub
/// The on-disk source files that back a module in this crate's HIR
/// module tree, keyed by [`FileName`].
///
/// This is the set of files the user actually wrote as Rust modules —
/// the crate root and every out-of-line `mod foo;` file. It deliberately
/// excludes everything that lands in the source map without being a
/// module the user authored: `include_str!` / `include_bytes!` data
/// files (which may be YAML, lock files, plain text, ...), `include!`
/// fragments spliced inline rather than backing their own module, and
/// proc-macro-synthesised `<proc-macro source>` modules.
///
/// The comment-scanning rules tokenize the local crate's files as Rust
/// and must filter the source map through this set: `bare_url`,
/// `bare_email`, `bare_issue_reference`, and `unicode_ellipsis_in_docs`
/// via the shared [`crate::comment_walk::walk_local_comments`] walker, plus
/// `unicode_ellipsis_in_comments` through its own token loop. Otherwise
/// a bare `http(s)://` URL inside an
/// `include_str!`-ed YAML file lexes as a `//` line comment and gets
/// flagged (and, worse, autofix-rewritten) as if it were a Rust comment.
/// See <https://github.com/KSXGitHub/perfectionist/issues/179>.
pub
/// Re-parse every on-disk source file that backs a module in the crate's
/// HIR module tree, calling `handle` once with each successfully-parsed
/// module as a standalone [`Crate`]. Within a single file, only that
/// file's own items are present — an out-of-line `mod foo;` it declares
/// is `ModKind::Unloaded` in a fresh parse, but `foo`'s file appears in
/// the source map in its own right and is handled by its own `handle`
/// call, so a caller's walk stays within one file at a time.
///
/// This is a thin wrapper over [`parse_crate_module_files`] for callers
/// that process one file at a time and do not need the
/// `live_module_spans` inline-recursion guard. A caller that descends
/// into inline `mod { ... }` bodies itself is responsible for whatever
/// cfg handling it needs.
///
/// The module tree is enumerated from the crate root and the crate's
/// *free* items, so a `mod` declared at module scope (nested to any
/// depth) is covered, but an out-of-line module declared inside a
/// function body — a `#[path]`-only construct, since a body `mod foo;`
/// does not otherwise resolve to a file — is not.
pub
/// Record the on-disk source file that holds a module's body, keyed by
/// name. A dummy span (no real body) contributes nothing. Only
/// [`FileName::Real`] files count: a module synthesised by a proc macro
/// has a `<proc-macro source>` file that must not be re-parsed and
/// flagged as if the user wrote it.
/// Re-parse a module's source file from its already-loaded text. Returns
/// `None` (silently discarding buffered diagnostics — `parse_psess` is
/// wired to a [`SilentEmitter`]) when the file does not parse as a
/// standalone module. The shared source map already holds this file and
/// deduplicates by name, so the parser reuses the loaded [`SourceFile`]
/// (preserving the real spans) and the passed source text is ignored —
/// hence the empty string, which avoids both a disk re-read and a clone
/// of the whole file.