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
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
//! Helpers shared between sibling rules.
//!
//! Each helper lives here only because more than one rule needs it.
//! Anything used by a single rule belongs in that rule's own file.
use ;
use OnceLock;
use ;
use rustc_hir as hir;
use HirId;
use ;
use ;
use UnicodeWidthStr;
/// Whether the HIR node at `hir_id` (whose own span is `span`)
/// originates in an external proc-macro (or `macro_rules!`)
/// expansion.
///
/// `declare_tool_lint!(... report_in_external_macro: false)` only
/// inspects the diagnostic span when deciding whether to suppress.
/// Proc-macro derives such as `clap_derive`'s `default_value_t`
/// expansion synthesise nodes whose identifier inherits a
/// user-source span (the span of the attribute that drove the
/// expansion) so that downstream compile errors point somewhere a
/// user can fix; from the lint's perspective the identifier looks
/// user-authored even though the surrounding statement only exists
/// in the expansion. Every rule whose diagnostic span is narrower
/// than the syntactic node that produced the violation must
/// therefore check the structural-parent span explicitly.
///
/// Two checks are needed because some structural spans cover only
/// the identifier itself (a `<T>` generic parameter has no other
/// tokens), so the node's own `Span::in_external_macro` returns
/// false. Walking up to the enclosing item and checking its
/// `def_span` catches that case — the synthesised owner item's
/// span carries the expansion's `SyntaxContext`. Regression
/// fixtures live in `ui/*_proc_macro.rs` with a minimal derive in
/// `ui/auxiliary/proc_macro_synth_binding.rs`.
pub
/// Crate-wide configuration table, deserialised from the top-level
/// `[perfectionist]` table of `dylint.toml`. Each entry of `enable`
/// flips a rule that was off by default to on; each entry of
/// `disable` flips a rule that was on by default (the common case)
/// to off. The two arrays accept either a bare rule name (a string)
/// or an inline `{ name, reason }` table — the `reason` field is
/// decorative and ignored at runtime, present so config authors can
/// leave a rationale next to the entry for future readers without
/// hiding it in a TOML comment. Listing the same rule under both
/// arrays is a config error.
///
/// "Enable" / "disable" deliberately doesn't mention lint levels: it
/// toggles whether the rule's pass is installed at all. The lint
/// itself stays registered either way, so `#[expect/allow/deny(
/// perfectionist::<rule>)]` at the call site continues to resolve
/// against the registered lint set; users that want to escalate a
/// rule's level above `Warn` reach for `#![deny(perfectionist::
/// <rule>)]` or `DYLINT_RUSTFLAGS=-D perfectionist::<rule>` as
/// before — the only mechanism rustc actually exposes for level
/// changes from outside the source.
/// Each `enable` / `disable` entry deserialises from either a bare
/// string or an inline `{ name = "...", reason = "..." }` table.
/// `#[serde(untagged)]` is what makes the array mixable, so a
/// config author can write
/// `enable = ["a", { name = "b", reason = "rationale" }]` in a
/// single literal array. The table shape is its own struct so we
/// can put `#[serde(deny_unknown_fields)]` on it — serde doesn't
/// honour that attribute on inline enum-variant shapes, only on
/// named structs.
/// Whether a rule's pass is installed at all. Both the per-rule
/// baseline (each rule's `DEFAULT_STATE` constant) and the
/// user-supplied override (the `enable` / `disable` arrays in
/// `dylint.toml`) speak this same alphabet, so a single type is
/// used end-to-end and no `bool` ever bridges the two. Mirrors
/// `gen-docs`'s own copy of this enum (separate crate, same shape) —
/// the doc generator reads each rule's `DEFAULT_STATE` constant
/// directly to render the rule's catalogue entry.
pub
/// Resolved per-rule override map, built once from the
/// `[perfectionist]` table of `dylint.toml`. Rules absent from this
/// map fall through to the per-rule `DEFAULT_STATE` constant each
/// `register_pass` passes to [`resolved_state`].
static GLOBAL_OVERRIDES: = new;
/// Parse the `[perfectionist]` table of `dylint.toml` and stash the
/// resolved override map. Called from
/// [`crate::register_lints`] immediately after
/// [`dylint_linting::init_config`] so that every per-rule
/// `register_pass` can consult [`resolved_state`] when deciding
/// whether to install its pass.
///
/// Panics if any rule name appears under both `enable` and
/// `disable` — that's a contradiction the runtime can't sensibly
/// resolve, and silently picking one direction would hide a
/// user-side mistake. Unknown rule names are silently ignored: the
/// override map keys that don't match any registered rule have no
/// effect (the rule never registers, so there's nothing to toggle),
/// and validating against the registered set here would duplicate
/// the existing `perfectionist::unknown_perfectionist_lints` rule's
/// purpose at a config-loading layer that has no diagnostic surface.
pub
/// Effective [`DefaultState`] for the rule named `name` (unqualified
/// — no `perfectionist::` prefix). Resolution order:
///
/// 1. If `name` appears under `disable` in the `[perfectionist]`
/// table, return [`DefaultState::Inactive`].
/// 2. If it appears under `enable`, return [`DefaultState::Active`].
/// 3. Otherwise return `default` — the per-rule baseline.
///
/// Each rule's `register_pass` passes its own baseline as
/// `default`: most rules pass [`DefaultState::Active`]; rules
/// listed in `src/rules/<name>.rs` as
/// `DEFAULT_STATE: DefaultState = DefaultState::Inactive` pass
/// `Inactive` and ship turned off until the user opts in.
pub
/// Render a lint-control meta item's path as its fully-namespaced
/// name (`clippy::too_many_arguments`, `dead_code`), joining the path
/// segments with `::`. Shared by the lint-level rules that classify or
/// report lint names by the printed form they wear in diagnostics and
/// `#[allow(...)]` attributes — `lint_silence_reason`,
/// `unknown_perfectionist_lints`, and `prefer_expect_over_allow`.
pub
/// Look up the `reason = "<literal>"` field in a lint-level attribute's
/// argument list. Returns the [`MetaItemLit`] (so callers can inspect the
/// literal's text and span) or `None` if no `reason` field is present.
///
/// Shared between the lint-level rules that all consume the same
/// notion of "this attribute carries an explanatory reason":
/// `lint_silence_reason`, `lint_downgrade_reason`, and
/// `lint_reason_from_comment`. The arg list is the post-`meta_item_list`
/// vector of nested meta items — the same shape every caller already
/// constructs from `Attribute::meta_item_list()`.
pub
/// Unicode display width of a line of source text, in terminal
/// columns — the measure an editor's column ruler reports, not the
/// byte length. A CJK ideograph counts as two columns, a combining
/// mark as zero, an ASCII character as one. Used by the rules that
/// gate on a source line being "too wide" (`print_macro_split`, and
/// the planned `prefer_text_block`), so the threshold means the same
/// thing across scripts.
pub
/// Whether `name` is exactly one ASCII letter (`a`..=`z` or
/// `A`..=`Z`). Used by every `single_letter_*` rule.
pub
/// Extract the identifier from a plain `Binding(_, _, ident, None)`
/// pattern. Returns `None` for any non-binding pattern or a binding
/// with a sub-pattern. Used by the `let`-binding, function-parameter,
/// and closure-parameter rules.
pub
/// Sibling of [`binding_ident`] that returns the binding's [`HirId`]
/// instead of its `Ident`. Used by the closure-parameter rule to test
/// whether a particular expression refers to one of the closure's
/// parameters.
pub
/// Resolve a `&str` set from a curated built-in default, a
/// user-supplied `extras` list, and a user-supplied `ignore`
/// list. Used by rules whose runtime set key remains a `String`
/// (currently just `non_exhaustive_error`, whose suffix lookup is
/// `str::ends_with`-shaped); the four rules whose late-pass
/// lookup key is a [`Symbol`] use the sibling
/// [`resolve_symbol_set`] instead. The [`BTreeSet`] return is
/// convenient for set membership lookups and has the side
/// benefit of dropping duplicates when defaults and extras
/// overlap; callers that need a `Vec`-shaped result can
/// `.into_iter().collect()` it themselves.
pub
/// Sibling of [`resolve_string_set`] that interns each name as
/// a [`Symbol`] in one pass — skipping the intermediate
/// `BTreeSet<String>` of the string-shaped variant. Used by rules
/// whose late-pass lookup key is already a [`Symbol`]
/// (`unicode_ellipsis_in_panic_messages`, `single_letter_closure_param`'s
/// trivial-callback list), so that membership checks reduce to
/// integer compares instead of `Symbol::as_str` → `String`
/// round-trips.
///
/// Must be called inside a rustc session, since [`Symbol::intern`]
/// reaches into the per-session symbol table.
pub
/// Sibling of [`resolve_symbol_set`] keyed on [`char`] instead of
/// `String`, for the `single_letter_*` rules' `extra_allowed_idents`
/// and `extra_denied_idents` knobs. `char::encode_utf8` writes
/// into a small stack buffer and hands [`Symbol::intern`] the
/// resulting `&str`. `extras` and `ignore` accept anything that
/// iterates into `char` — notably the rules'
/// `Vec<crate::ascii_letter::AsciiLetter>` knobs, whose `Into<char>`
/// keeps the ASCII-letter restriction in the type rather than at
/// this boundary — so each caller hands its config field straight
/// in without a per-call conversion.
///
/// Must be called inside a rustc session.
pub