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