1use std::borrow::Cow;
2
3use rustc_ast::util::unicode::TEXT_FLOW_CONTROL_CHARS;
4use rustc_errors::{
5 Applicability, Diag, DiagArgValue, LintDiagnostic, elided_lifetime_in_path_suggestion,
6};
7use rustc_hir::lints::AttributeLintKind;
8use rustc_middle::middle::stability;
9use rustc_middle::ty::TyCtxt;
10use rustc_session::Session;
11use rustc_session::lint::BuiltinLintDiag;
12use rustc_span::BytePos;
13use tracing::debug;
14
15use crate::lints;
16
17mod check_cfg;
18
19pub fn decorate_builtin_lint(
20 sess: &Session,
21 tcx: Option<TyCtxt<'_>>,
22 diagnostic: BuiltinLintDiag,
23 diag: &mut Diag<'_, ()>,
24) {
25 match diagnostic {
26 BuiltinLintDiag::UnicodeTextFlow(comment_span, content) => {
27 let spans: Vec<_> = content
28 .char_indices()
29 .filter_map(|(i, c)| {
30 TEXT_FLOW_CONTROL_CHARS.contains(&c).then(|| {
31 let lo = comment_span.lo() + BytePos(2 + i as u32);
32 (c, comment_span.with_lo(lo).with_hi(lo + BytePos(c.len_utf8() as u32)))
33 })
34 })
35 .collect();
36 let characters = spans
37 .iter()
38 .map(|&(c, span)| lints::UnicodeCharNoteSub { span, c_debug: format!("{c:?}") })
39 .collect();
40 let suggestions = (!spans.is_empty()).then_some(lints::UnicodeTextFlowSuggestion {
41 spans: spans.iter().map(|(_c, span)| *span).collect(),
42 });
43
44 lints::UnicodeTextFlow {
45 comment_span,
46 characters,
47 suggestions,
48 num_codepoints: spans.len(),
49 }
50 .decorate_lint(diag);
51 }
52 BuiltinLintDiag::AbsPathWithModule(mod_span) => {
53 let (replacement, applicability) = match sess.source_map().span_to_snippet(mod_span) {
54 Ok(ref s) => {
55 let opt_colon = if s.trim_start().starts_with("::") { "" } else { "::" };
58
59 (format!("crate{opt_colon}{s}"), Applicability::MachineApplicable)
60 }
61 Err(_) => ("crate::<path>".to_string(), Applicability::HasPlaceholders),
62 };
63 lints::AbsPathWithModule {
64 sugg: lints::AbsPathWithModuleSugg { span: mod_span, applicability, replacement },
65 }
66 .decorate_lint(diag);
67 }
68 BuiltinLintDiag::ElidedLifetimesInPaths(n, path_span, incl_angl_brckt, insertion_span) => {
69 lints::ElidedLifetimesInPaths {
70 subdiag: elided_lifetime_in_path_suggestion(
71 sess.source_map(),
72 n,
73 path_span,
74 incl_angl_brckt,
75 insertion_span,
76 ),
77 }
78 .decorate_lint(diag);
79 }
80 BuiltinLintDiag::UnusedImports {
81 remove_whole_use,
82 num_to_remove,
83 remove_spans,
84 test_module_span,
85 span_snippets,
86 } => {
87 let sugg = if remove_whole_use {
88 lints::UnusedImportsSugg::RemoveWholeUse { span: remove_spans[0] }
89 } else {
90 lints::UnusedImportsSugg::RemoveImports { remove_spans, num_to_remove }
91 };
92 let test_module_span =
93 test_module_span.map(|span| sess.source_map().guess_head_span(span));
94
95 lints::UnusedImports {
96 sugg,
97 test_module_span,
98 num_snippets: span_snippets.len(),
99 span_snippets: DiagArgValue::StrListSepByAnd(
100 span_snippets.into_iter().map(Cow::Owned).collect(),
101 ),
102 }
103 .decorate_lint(diag);
104 }
105 BuiltinLintDiag::RedundantImport(spans, ident) => {
106 let subs = spans
107 .into_iter()
108 .map(|(span, is_imported)| {
109 (match (span.is_dummy(), is_imported) {
110 (false, true) => lints::RedundantImportSub::ImportedHere,
111 (false, false) => lints::RedundantImportSub::DefinedHere,
112 (true, true) => lints::RedundantImportSub::ImportedPrelude,
113 (true, false) => lints::RedundantImportSub::DefinedPrelude,
114 })(span)
115 })
116 .collect();
117 lints::RedundantImport { subs, ident }.decorate_lint(diag);
118 }
119 BuiltinLintDiag::DeprecatedMacro {
120 suggestion,
121 suggestion_span,
122 note,
123 path,
124 since_kind,
125 } => {
126 let sub = suggestion.map(|suggestion| stability::DeprecationSuggestion {
127 span: suggestion_span,
128 kind: "macro".to_owned(),
129 suggestion,
130 });
131
132 stability::Deprecated { sub, kind: "macro".to_owned(), path, note, since_kind }
133 .decorate_lint(diag);
134 }
135 BuiltinLintDiag::PatternsInFnsWithoutBody { span: remove_span, ident, is_foreign } => {
136 let sub = lints::PatternsInFnsWithoutBodySub { ident, span: remove_span };
137 if is_foreign {
138 lints::PatternsInFnsWithoutBody::Foreign { sub }
139 } else {
140 lints::PatternsInFnsWithoutBody::Bodiless { sub }
141 }
142 .decorate_lint(diag);
143 }
144 BuiltinLintDiag::ReservedPrefix(label_span, prefix) => {
145 lints::ReservedPrefix {
146 label: label_span,
147 suggestion: label_span.shrink_to_hi(),
148 prefix,
149 }
150 .decorate_lint(diag);
151 }
152 BuiltinLintDiag::RawPrefix(label_span) => {
153 lints::RawPrefix { label: label_span, suggestion: label_span.shrink_to_hi() }
154 .decorate_lint(diag);
155 }
156 BuiltinLintDiag::ReservedString { is_string, suggestion } => {
157 if is_string {
158 lints::ReservedString { suggestion }.decorate_lint(diag);
159 } else {
160 lints::ReservedMultihash { suggestion }.decorate_lint(diag);
161 }
162 }
163 BuiltinLintDiag::BreakWithLabelAndLoop(sugg_span) => {
164 lints::BreakWithLabelAndLoop {
165 sub: lints::BreakWithLabelAndLoopSub {
166 left: sugg_span.shrink_to_lo(),
167 right: sugg_span.shrink_to_hi(),
168 },
169 }
170 .decorate_lint(diag);
171 }
172 BuiltinLintDiag::DeprecatedWhereclauseLocation(left_sp, sugg) => {
173 let suggestion = match sugg {
174 Some((right_sp, sugg)) => lints::DeprecatedWhereClauseLocationSugg::MoveToEnd {
175 left: left_sp,
176 right: right_sp,
177 sugg,
178 },
179 None => lints::DeprecatedWhereClauseLocationSugg::RemoveWhere { span: left_sp },
180 };
181 lints::DeprecatedWhereClauseLocation { suggestion }.decorate_lint(diag);
182 }
183 BuiltinLintDiag::SingleUseLifetime {
184 param_span,
185 use_span: Some((use_span, elide)),
186 deletion_span,
187 ident,
188 } => {
189 debug!(?param_span, ?use_span, ?deletion_span);
190 let suggestion = if let Some(deletion_span) = deletion_span {
191 let (use_span, replace_lt) = if elide {
192 let use_span = sess.source_map().span_extend_while_whitespace(use_span);
193 (use_span, String::new())
194 } else {
195 (use_span, "'_".to_owned())
196 };
197 debug!(?deletion_span, ?use_span);
198
199 let deletion_span =
202 if deletion_span.is_empty() { None } else { Some(deletion_span) };
203 Some(lints::SingleUseLifetimeSugg { deletion_span, use_span, replace_lt })
204 } else {
205 None
206 };
207
208 lints::SingleUseLifetime { suggestion, param_span, use_span, ident }
209 .decorate_lint(diag);
210 }
211 BuiltinLintDiag::SingleUseLifetime { use_span: None, deletion_span, ident, .. } => {
212 lints::UnusedLifetime { deletion_span, ident }.decorate_lint(diag);
213 }
214 BuiltinLintDiag::NamedArgumentUsedPositionally {
215 position_sp_to_replace,
216 position_sp_for_msg,
217 named_arg_sp,
218 named_arg_name,
219 is_formatting_arg,
220 } => {
221 let (suggestion, name) = if let Some(positional_arg_to_replace) = position_sp_to_replace
222 {
223 let mut name = named_arg_name.clone();
224 if is_formatting_arg {
225 name.push('$')
226 };
227 let span_to_replace = if let Ok(positional_arg_content) =
228 sess.source_map().span_to_snippet(positional_arg_to_replace)
229 && positional_arg_content.starts_with(':')
230 {
231 positional_arg_to_replace.shrink_to_lo()
232 } else {
233 positional_arg_to_replace
234 };
235 (Some(span_to_replace), name)
236 } else {
237 (None, String::new())
238 };
239
240 lints::NamedArgumentUsedPositionally {
241 named_arg_sp,
242 position_label_sp: position_sp_for_msg,
243 suggestion,
244 name,
245 named_arg_name,
246 }
247 .decorate_lint(diag);
248 }
249 BuiltinLintDiag::AmbiguousGlobReexports {
250 name,
251 namespace,
252 first_reexport_span,
253 duplicate_reexport_span,
254 } => {
255 lints::AmbiguousGlobReexports {
256 first_reexport: first_reexport_span,
257 duplicate_reexport: duplicate_reexport_span,
258 name,
259 namespace,
260 }
261 .decorate_lint(diag);
262 }
263 BuiltinLintDiag::HiddenGlobReexports {
264 name,
265 namespace,
266 glob_reexport_span,
267 private_item_span,
268 } => {
269 lints::HiddenGlobReexports {
270 glob_reexport: glob_reexport_span,
271 private_item: private_item_span,
272
273 name,
274 namespace,
275 }
276 .decorate_lint(diag);
277 }
278 BuiltinLintDiag::UnusedQualifications { removal_span } => {
279 lints::UnusedQualifications { removal_span }.decorate_lint(diag);
280 }
281 BuiltinLintDiag::AssociatedConstElidedLifetime {
282 elided,
283 span: lt_span,
284 lifetimes_in_scope,
285 } => {
286 let lt_span = if elided { lt_span.shrink_to_hi() } else { lt_span };
287 let code = if elided { "'static " } else { "'static" };
288 lints::AssociatedConstElidedLifetime {
289 span: lt_span,
290 code,
291 elided,
292 lifetimes_in_scope,
293 }
294 .decorate_lint(diag);
295 }
296 BuiltinLintDiag::UnusedCrateDependency { extern_crate, local_crate } => {
297 lints::UnusedCrateDependency { extern_crate, local_crate }.decorate_lint(diag)
298 }
299 BuiltinLintDiag::UnusedVisibility(span) => {
300 lints::UnusedVisibility { span }.decorate_lint(diag)
301 }
302 BuiltinLintDiag::AttributeLint(kind) => decorate_attribute_lint(sess, tcx, &kind, diag),
303 }
304}
305
306pub fn decorate_attribute_lint(
307 sess: &Session,
308 tcx: Option<TyCtxt<'_>>,
309 kind: &AttributeLintKind,
310 diag: &mut Diag<'_, ()>,
311) {
312 match kind {
313 &AttributeLintKind::UnusedDuplicate { this, other, warning } => {
314 lints::UnusedDuplicate { this, other, warning }.decorate_lint(diag)
315 }
316 AttributeLintKind::IllFormedAttributeInput { suggestions, docs } => {
317 lints::IllFormedAttributeInput {
318 num_suggestions: suggestions.len(),
319 suggestions: DiagArgValue::StrListSepByAnd(
320 suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(),
321 ),
322 has_docs: docs.is_some(),
323 docs: docs.unwrap_or(""),
324 }
325 .decorate_lint(diag)
326 }
327 AttributeLintKind::EmptyAttribute { first_span, attr_path, valid_without_list } => {
328 lints::EmptyAttributeList {
329 attr_span: *first_span,
330 attr_path: attr_path.clone(),
331 valid_without_list: *valid_without_list,
332 }
333 .decorate_lint(diag)
334 }
335 AttributeLintKind::InvalidTarget { name, target, applied, only, attr_span } => {
336 lints::InvalidTargetLint {
337 name: name.clone(),
338 target,
339 applied: DiagArgValue::StrListSepByAnd(
340 applied.into_iter().map(|i| Cow::Owned(i.to_string())).collect(),
341 ),
342 only,
343 attr_span: *attr_span,
344 }
345 .decorate_lint(diag)
346 }
347 &AttributeLintKind::InvalidStyle { ref name, is_used_as_inner, target, target_span } => {
348 lints::InvalidAttrStyle {
349 name: name.clone(),
350 is_used_as_inner,
351 target_span: (!is_used_as_inner).then_some(target_span),
352 target,
353 }
354 .decorate_lint(diag)
355 }
356 &AttributeLintKind::UnsafeAttrOutsideUnsafe { attribute_name_span, sugg_spans } => {
357 lints::UnsafeAttrOutsideUnsafeLint {
358 span: attribute_name_span,
359 suggestion: sugg_spans
360 .map(|(left, right)| lints::UnsafeAttrOutsideUnsafeSuggestion { left, right }),
361 }
362 .decorate_lint(diag)
363 }
364 &AttributeLintKind::UnexpectedCfgName(name, value) => {
365 check_cfg::unexpected_cfg_name(sess, tcx, name, value).decorate_lint(diag)
366 }
367 &AttributeLintKind::UnexpectedCfgValue(name, value) => {
368 check_cfg::unexpected_cfg_value(sess, tcx, name, value).decorate_lint(diag)
369 }
370 &AttributeLintKind::DuplicateDocAlias { first_definition } => {
371 lints::DocAliasDuplicated { first_defn: first_definition }.decorate_lint(diag)
372 }
373
374 &AttributeLintKind::DocAutoCfgExpectsHideOrShow => {
375 lints::DocAutoCfgExpectsHideOrShow.decorate_lint(diag)
376 }
377
378 &AttributeLintKind::DocAutoCfgHideShowUnexpectedItem { attr_name } => {
379 lints::DocAutoCfgHideShowUnexpectedItem { attr_name }.decorate_lint(diag)
380 }
381
382 &AttributeLintKind::DocAutoCfgHideShowExpectsList { attr_name } => {
383 lints::DocAutoCfgHideShowExpectsList { attr_name }.decorate_lint(diag)
384 }
385
386 &AttributeLintKind::DocInvalid => { lints::DocInvalid }.decorate_lint(diag),
387
388 &AttributeLintKind::DocUnknownInclude { span, inner, value } => {
389 lints::DocUnknownInclude { inner, value, sugg: (span, Applicability::MaybeIncorrect) }
390 }
391 .decorate_lint(diag),
392
393 &AttributeLintKind::DocUnknownSpotlight { span } => {
394 lints::DocUnknownSpotlight { sugg_span: span }.decorate_lint(diag)
395 }
396
397 &AttributeLintKind::DocUnknownPasses { name, span } => {
398 lints::DocUnknownPasses { name, note_span: span }.decorate_lint(diag)
399 }
400
401 &AttributeLintKind::DocUnknownPlugins { span } => {
402 lints::DocUnknownPlugins { label_span: span }.decorate_lint(diag)
403 }
404
405 &AttributeLintKind::DocUnknownAny { name } => {
406 lints::DocUnknownAny { name }.decorate_lint(diag)
407 }
408
409 &AttributeLintKind::DocAutoCfgWrongLiteral => {
410 lints::DocAutoCfgWrongLiteral.decorate_lint(diag)
411 }
412
413 &AttributeLintKind::DocTestTakesList => lints::DocTestTakesList.decorate_lint(diag),
414
415 &AttributeLintKind::DocTestUnknown { name } => {
416 lints::DocTestUnknown { name }.decorate_lint(diag)
417 }
418
419 &AttributeLintKind::DocTestLiteral => lints::DocTestLiteral.decorate_lint(diag),
420 }
421}