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_middle::middle::stability;
8use rustc_middle::ty::TyCtxt;
9use rustc_session::Session;
10use rustc_session::lint::BuiltinLintDiag;
11use rustc_span::BytePos;
12use tracing::debug;
13
14use crate::lints;
15
16mod check_cfg;
17
18pub fn decorate_builtin_lint(
19 sess: &Session,
20 tcx: Option<TyCtxt<'_>>,
21 diagnostic: BuiltinLintDiag,
22 diag: &mut Diag<'_, ()>,
23) {
24 match diagnostic {
25 BuiltinLintDiag::UnicodeTextFlow(comment_span, content) => {
26 let spans: Vec<_> = content
27 .char_indices()
28 .filter_map(|(i, c)| {
29 TEXT_FLOW_CONTROL_CHARS.contains(&c).then(|| {
30 let lo = comment_span.lo() + BytePos(2 + i as u32);
31 (c, comment_span.with_lo(lo).with_hi(lo + BytePos(c.len_utf8() as u32)))
32 })
33 })
34 .collect();
35 let characters = spans
36 .iter()
37 .map(|&(c, span)| lints::UnicodeCharNoteSub { span, c_debug: format!("{c:?}") })
38 .collect();
39 let suggestions = (!spans.is_empty()).then_some(lints::UnicodeTextFlowSuggestion {
40 spans: spans.iter().map(|(_c, span)| *span).collect(),
41 });
42
43 lints::UnicodeTextFlow {
44 comment_span,
45 characters,
46 suggestions,
47 num_codepoints: spans.len(),
48 }
49 .decorate_lint(diag);
50 }
51 BuiltinLintDiag::AbsPathWithModule(mod_span) => {
52 let (replacement, applicability) = match sess.source_map().span_to_snippet(mod_span) {
53 Ok(ref s) => {
54 let opt_colon = if s.trim_start().starts_with("::") { "" } else { "::" };
57
58 (format!("crate{opt_colon}{s}"), Applicability::MachineApplicable)
59 }
60 Err(_) => ("crate::<path>".to_string(), Applicability::HasPlaceholders),
61 };
62 lints::AbsPathWithModule {
63 sugg: lints::AbsPathWithModuleSugg { span: mod_span, applicability, replacement },
64 }
65 .decorate_lint(diag);
66 }
67 BuiltinLintDiag::ProcMacroDeriveResolutionFallback { span: macro_span, ns, ident } => {
68 lints::ProcMacroDeriveResolutionFallback { span: macro_span, ns, ident }
69 .decorate_lint(diag)
70 }
71 BuiltinLintDiag::MacroExpandedMacroExportsAccessedByAbsolutePaths(span_def) => {
72 lints::MacroExpandedMacroExportsAccessedByAbsolutePaths { definition: span_def }
73 .decorate_lint(diag)
74 }
75
76 BuiltinLintDiag::ElidedLifetimesInPaths(n, path_span, incl_angl_brckt, insertion_span) => {
77 lints::ElidedLifetimesInPaths {
78 subdiag: elided_lifetime_in_path_suggestion(
79 sess.source_map(),
80 n,
81 path_span,
82 incl_angl_brckt,
83 insertion_span,
84 ),
85 }
86 .decorate_lint(diag);
87 }
88 BuiltinLintDiag::UnknownCrateTypes { span, candidate } => {
89 let sugg = candidate.map(|candidate| lints::UnknownCrateTypesSub { span, candidate });
90 lints::UnknownCrateTypes { sugg }.decorate_lint(diag);
91 }
92 BuiltinLintDiag::UnusedImports {
93 remove_whole_use,
94 num_to_remove,
95 remove_spans,
96 test_module_span,
97 span_snippets,
98 } => {
99 let sugg = if remove_whole_use {
100 lints::UnusedImportsSugg::RemoveWholeUse { span: remove_spans[0] }
101 } else {
102 lints::UnusedImportsSugg::RemoveImports { remove_spans, num_to_remove }
103 };
104 let test_module_span =
105 test_module_span.map(|span| sess.source_map().guess_head_span(span));
106
107 lints::UnusedImports {
108 sugg,
109 test_module_span,
110 num_snippets: span_snippets.len(),
111 span_snippets: DiagArgValue::StrListSepByAnd(
112 span_snippets.into_iter().map(Cow::Owned).collect(),
113 ),
114 }
115 .decorate_lint(diag);
116 }
117 BuiltinLintDiag::RedundantImport(spans, ident) => {
118 let subs = spans
119 .into_iter()
120 .map(|(span, is_imported)| {
121 (match (span.is_dummy(), is_imported) {
122 (false, true) => lints::RedundantImportSub::ImportedHere,
123 (false, false) => lints::RedundantImportSub::DefinedHere,
124 (true, true) => lints::RedundantImportSub::ImportedPrelude,
125 (true, false) => lints::RedundantImportSub::DefinedPrelude,
126 })(span)
127 })
128 .collect();
129 lints::RedundantImport { subs, ident }.decorate_lint(diag);
130 }
131 BuiltinLintDiag::DeprecatedMacro {
132 suggestion,
133 suggestion_span,
134 note,
135 path,
136 since_kind,
137 } => {
138 let sub = suggestion.map(|suggestion| stability::DeprecationSuggestion {
139 span: suggestion_span,
140 kind: "macro".to_owned(),
141 suggestion,
142 });
143
144 stability::Deprecated { sub, kind: "macro".to_owned(), path, note, since_kind }
145 .decorate_lint(diag);
146 }
147 BuiltinLintDiag::UnusedDocComment(attr_span) => {
148 lints::UnusedDocComment { span: attr_span }.decorate_lint(diag);
149 }
150 BuiltinLintDiag::PatternsInFnsWithoutBody { span: remove_span, ident, is_foreign } => {
151 let sub = lints::PatternsInFnsWithoutBodySub { ident, span: remove_span };
152 if is_foreign {
153 lints::PatternsInFnsWithoutBody::Foreign { sub }
154 } else {
155 lints::PatternsInFnsWithoutBody::Bodiless { sub }
156 }
157 .decorate_lint(diag);
158 }
159 BuiltinLintDiag::MissingAbi(label_span, default_abi) => {
160 lints::MissingAbi { span: label_span, default_abi }.decorate_lint(diag);
161 }
162 BuiltinLintDiag::LegacyDeriveHelpers(label_span) => {
163 lints::LegacyDeriveHelpers { span: label_span }.decorate_lint(diag);
164 }
165 BuiltinLintDiag::OrPatternsBackCompat(suggestion_span, suggestion) => {
166 lints::OrPatternsBackCompat { span: suggestion_span, suggestion }.decorate_lint(diag);
167 }
168 BuiltinLintDiag::ReservedPrefix(label_span, prefix) => {
169 lints::ReservedPrefix {
170 label: label_span,
171 suggestion: label_span.shrink_to_hi(),
172 prefix,
173 }
174 .decorate_lint(diag);
175 }
176 BuiltinLintDiag::RawPrefix(label_span) => {
177 lints::RawPrefix { label: label_span, suggestion: label_span.shrink_to_hi() }
178 .decorate_lint(diag);
179 }
180 BuiltinLintDiag::ReservedString { is_string, suggestion } => {
181 if is_string {
182 lints::ReservedString { suggestion }.decorate_lint(diag);
183 } else {
184 lints::ReservedMultihash { suggestion }.decorate_lint(diag);
185 }
186 }
187 BuiltinLintDiag::HiddenUnicodeCodepoints {
188 label,
189 count,
190 span_label,
191 labels,
192 escape,
193 spans,
194 } => {
195 lints::HiddenUnicodeCodepointsDiag {
196 label: &label,
197 count,
198 span_label,
199 labels: labels.map(|spans| lints::HiddenUnicodeCodepointsDiagLabels { spans }),
200 sub: if escape {
201 lints::HiddenUnicodeCodepointsDiagSub::Escape { spans }
202 } else {
203 lints::HiddenUnicodeCodepointsDiagSub::NoEscape { spans }
204 },
205 }
206 .decorate_lint(diag);
207 }
208 BuiltinLintDiag::UnusedBuiltinAttribute {
209 attr_name,
210 macro_name,
211 invoc_span,
212 attr_span,
213 } => {
214 lints::UnusedBuiltinAttribute { invoc_span, attr_name, macro_name, attr_span }
215 .decorate_lint(diag);
216 }
217 BuiltinLintDiag::TrailingMacro(is_trailing, name) => {
218 lints::TrailingMacro { is_trailing, name }.decorate_lint(diag);
219 }
220 BuiltinLintDiag::BreakWithLabelAndLoop(sugg_span) => {
221 lints::BreakWithLabelAndLoop {
222 sub: lints::BreakWithLabelAndLoopSub {
223 left: sugg_span.shrink_to_lo(),
224 right: sugg_span.shrink_to_hi(),
225 },
226 }
227 .decorate_lint(diag);
228 }
229 BuiltinLintDiag::UnexpectedCfgName(name, value) => {
230 check_cfg::unexpected_cfg_name(sess, tcx, name, value).decorate_lint(diag);
231 }
232 BuiltinLintDiag::UnexpectedCfgValue(name, value) => {
233 check_cfg::unexpected_cfg_value(sess, tcx, name, value).decorate_lint(diag);
234 }
235 BuiltinLintDiag::DeprecatedWhereclauseLocation(left_sp, sugg) => {
236 let suggestion = match sugg {
237 Some((right_sp, sugg)) => lints::DeprecatedWhereClauseLocationSugg::MoveToEnd {
238 left: left_sp,
239 right: right_sp,
240 sugg,
241 },
242 None => lints::DeprecatedWhereClauseLocationSugg::RemoveWhere { span: left_sp },
243 };
244 lints::DeprecatedWhereClauseLocation { suggestion }.decorate_lint(diag);
245 }
246 BuiltinLintDiag::MissingUnsafeOnExtern { suggestion } => {
247 lints::MissingUnsafeOnExtern { suggestion }.decorate_lint(diag);
248 }
249 BuiltinLintDiag::SingleUseLifetime {
250 param_span,
251 use_span: Some((use_span, elide)),
252 deletion_span,
253 ident,
254 } => {
255 debug!(?param_span, ?use_span, ?deletion_span);
256 let suggestion = if let Some(deletion_span) = deletion_span {
257 let (use_span, replace_lt) = if elide {
258 let use_span = sess.source_map().span_extend_while_whitespace(use_span);
259 (use_span, String::new())
260 } else {
261 (use_span, "'_".to_owned())
262 };
263 debug!(?deletion_span, ?use_span);
264
265 let deletion_span =
268 if deletion_span.is_empty() { None } else { Some(deletion_span) };
269 Some(lints::SingleUseLifetimeSugg { deletion_span, use_span, replace_lt })
270 } else {
271 None
272 };
273
274 lints::SingleUseLifetime { suggestion, param_span, use_span, ident }
275 .decorate_lint(diag);
276 }
277 BuiltinLintDiag::SingleUseLifetime { use_span: None, deletion_span, ident, .. } => {
278 debug!(?deletion_span);
279 lints::UnusedLifetime { deletion_span, ident }.decorate_lint(diag);
280 }
281 BuiltinLintDiag::NamedArgumentUsedPositionally {
282 position_sp_to_replace,
283 position_sp_for_msg,
284 named_arg_sp,
285 named_arg_name,
286 is_formatting_arg,
287 } => {
288 let (suggestion, name) = if let Some(positional_arg_to_replace) = position_sp_to_replace
289 {
290 let mut name = named_arg_name.clone();
291 if is_formatting_arg {
292 name.push('$')
293 };
294 let span_to_replace = if let Ok(positional_arg_content) =
295 sess.source_map().span_to_snippet(positional_arg_to_replace)
296 && positional_arg_content.starts_with(':')
297 {
298 positional_arg_to_replace.shrink_to_lo()
299 } else {
300 positional_arg_to_replace
301 };
302 (Some(span_to_replace), name)
303 } else {
304 (None, String::new())
305 };
306
307 lints::NamedArgumentUsedPositionally {
308 named_arg_sp,
309 position_label_sp: position_sp_for_msg,
310 suggestion,
311 name,
312 named_arg_name,
313 }
314 .decorate_lint(diag);
315 }
316 BuiltinLintDiag::ByteSliceInPackedStructWithDerive { ty } => {
317 lints::ByteSliceInPackedStructWithDerive { ty }.decorate_lint(diag);
318 }
319 BuiltinLintDiag::UnusedExternCrate { span, removal_span } => {
320 lints::UnusedExternCrate { span, removal_span }.decorate_lint(diag);
321 }
322 BuiltinLintDiag::ExternCrateNotIdiomatic { vis_span, ident_span } => {
323 let suggestion_span = vis_span.between(ident_span);
324 let code = if vis_span.is_empty() { "use " } else { " use " };
325
326 lints::ExternCrateNotIdiomatic { span: suggestion_span, code }.decorate_lint(diag);
327 }
328 BuiltinLintDiag::AmbiguousGlobImports { diag: ambiguity } => {
329 lints::AmbiguousGlobImports { ambiguity }.decorate_lint(diag);
330 }
331 BuiltinLintDiag::AmbiguousGlobReexports {
332 name,
333 namespace,
334 first_reexport_span,
335 duplicate_reexport_span,
336 } => {
337 lints::AmbiguousGlobReexports {
338 first_reexport: first_reexport_span,
339 duplicate_reexport: duplicate_reexport_span,
340 name,
341 namespace,
342 }
343 .decorate_lint(diag);
344 }
345 BuiltinLintDiag::HiddenGlobReexports {
346 name,
347 namespace,
348 glob_reexport_span,
349 private_item_span,
350 } => {
351 lints::HiddenGlobReexports {
352 glob_reexport: glob_reexport_span,
353 private_item: private_item_span,
354
355 name,
356 namespace,
357 }
358 .decorate_lint(diag);
359 }
360 BuiltinLintDiag::ReexportPrivateDependency { name, kind, krate } => {
361 lints::ReexportPrivateDependency { name, kind, krate }.decorate_lint(diag);
362 }
363 BuiltinLintDiag::UnusedQualifications { removal_span } => {
364 lints::UnusedQualifications { removal_span }.decorate_lint(diag);
365 }
366 BuiltinLintDiag::UnsafeAttrOutsideUnsafe {
367 attribute_name_span,
368 sugg_spans: (left, right),
369 } => {
370 lints::UnsafeAttrOutsideUnsafe {
371 span: attribute_name_span,
372 suggestion: lints::UnsafeAttrOutsideUnsafeSuggestion { left, right },
373 }
374 .decorate_lint(diag);
375 }
376 BuiltinLintDiag::AssociatedConstElidedLifetime {
377 elided,
378 span: lt_span,
379 lifetimes_in_scope,
380 } => {
381 let lt_span = if elided { lt_span.shrink_to_hi() } else { lt_span };
382 let code = if elided { "'static " } else { "'static" };
383 lints::AssociatedConstElidedLifetime {
384 span: lt_span,
385 code,
386 elided,
387 lifetimes_in_scope,
388 }
389 .decorate_lint(diag);
390 }
391 BuiltinLintDiag::RedundantImportVisibility { max_vis, span: vis_span, import_vis } => {
392 lints::RedundantImportVisibility { span: vis_span, help: (), max_vis, import_vis }
393 .decorate_lint(diag);
394 }
395 BuiltinLintDiag::UnknownDiagnosticAttribute { span: typo_span, typo_name } => {
396 let typo = typo_name.map(|typo_name| lints::UnknownDiagnosticAttributeTypoSugg {
397 span: typo_span,
398 typo_name,
399 });
400 lints::UnknownDiagnosticAttribute { typo }.decorate_lint(diag);
401 }
402 BuiltinLintDiag::MacroUseDeprecated => {
403 lints::MacroUseDeprecated.decorate_lint(diag);
404 }
405 BuiltinLintDiag::UnusedMacroUse => lints::UnusedMacroUse.decorate_lint(diag),
406 BuiltinLintDiag::PrivateExternCrateReexport { source: ident, extern_crate_span } => {
407 lints::PrivateExternCrateReexport { ident, sugg: extern_crate_span.shrink_to_lo() }
408 .decorate_lint(diag);
409 }
410 BuiltinLintDiag::UnusedLabel => lints::UnusedLabel.decorate_lint(diag),
411 BuiltinLintDiag::MacroIsPrivate(ident) => {
412 lints::MacroIsPrivate { ident }.decorate_lint(diag);
413 }
414 BuiltinLintDiag::UnusedMacroDefinition(name) => {
415 lints::UnusedMacroDefinition { name }.decorate_lint(diag);
416 }
417 BuiltinLintDiag::MacroRuleNeverUsed(n, name) => {
418 lints::MacroRuleNeverUsed { n: n + 1, name }.decorate_lint(diag);
419 }
420 BuiltinLintDiag::UnstableFeature(msg) => {
421 lints::UnstableFeature { msg }.decorate_lint(diag);
422 }
423 BuiltinLintDiag::AvoidUsingIntelSyntax => {
424 lints::AvoidIntelSyntax.decorate_lint(diag);
425 }
426 BuiltinLintDiag::AvoidUsingAttSyntax => {
427 lints::AvoidAttSyntax.decorate_lint(diag);
428 }
429 BuiltinLintDiag::IncompleteInclude => {
430 lints::IncompleteInclude.decorate_lint(diag);
431 }
432 BuiltinLintDiag::UnnameableTestItems => {
433 lints::UnnameableTestItems.decorate_lint(diag);
434 }
435 BuiltinLintDiag::DuplicateMacroAttribute => {
436 lints::DuplicateMacroAttribute.decorate_lint(diag);
437 }
438 BuiltinLintDiag::CfgAttrNoAttributes => {
439 lints::CfgAttrNoAttributes.decorate_lint(diag);
440 }
441 BuiltinLintDiag::MetaVariableStillRepeating(name) => {
442 lints::MetaVariableStillRepeating { name }.decorate_lint(diag);
443 }
444 BuiltinLintDiag::MetaVariableWrongOperator => {
445 lints::MetaVariableWrongOperator.decorate_lint(diag);
446 }
447 BuiltinLintDiag::DuplicateMatcherBinding => {
448 lints::DuplicateMatcherBinding.decorate_lint(diag);
449 }
450 BuiltinLintDiag::UnknownMacroVariable(name) => {
451 lints::UnknownMacroVariable { name }.decorate_lint(diag);
452 }
453 BuiltinLintDiag::UnusedCrateDependency { extern_crate, local_crate } => {
454 lints::UnusedCrateDependency { extern_crate, local_crate }.decorate_lint(diag)
455 }
456 BuiltinLintDiag::IllFormedAttributeInput { suggestions, docs } => {
457 lints::IllFormedAttributeInput {
458 num_suggestions: suggestions.len(),
459 suggestions: DiagArgValue::StrListSepByAnd(
460 suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(),
461 ),
462 has_docs: docs.is_some(),
463 docs: docs.unwrap_or(""),
464 }
465 .decorate_lint(diag)
466 }
467 BuiltinLintDiag::InnerAttributeUnstable { is_macro } => if is_macro {
468 lints::InnerAttributeUnstable::InnerMacroAttribute
469 } else {
470 lints::InnerAttributeUnstable::CustomInnerAttribute
471 }
472 .decorate_lint(diag),
473 BuiltinLintDiag::OutOfScopeMacroCalls { span, path, location } => {
474 lints::OutOfScopeMacroCalls { span, path, location }.decorate_lint(diag)
475 }
476 BuiltinLintDiag::UnexpectedBuiltinCfg { cfg, cfg_name, controlled_by } => {
477 lints::UnexpectedBuiltinCfg { cfg, cfg_name, controlled_by }.decorate_lint(diag)
478 }
479 }
480}