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 {
68 span: macro_span,
69 ns_descr,
70 ident,
71 } => lints::ProcMacroDeriveResolutionFallback { span: macro_span, ns_descr, ident }
72 .decorate_lint(diag),
73 BuiltinLintDiag::MacroExpandedMacroExportsAccessedByAbsolutePaths(span_def) => {
74 lints::MacroExpandedMacroExportsAccessedByAbsolutePaths { definition: span_def }
75 .decorate_lint(diag)
76 }
77
78 BuiltinLintDiag::ElidedLifetimesInPaths(n, path_span, incl_angl_brckt, insertion_span) => {
79 lints::ElidedLifetimesInPaths {
80 subdiag: elided_lifetime_in_path_suggestion(
81 sess.source_map(),
82 n,
83 path_span,
84 incl_angl_brckt,
85 insertion_span,
86 ),
87 }
88 .decorate_lint(diag);
89 }
90 BuiltinLintDiag::UnknownCrateTypes { span, candidate } => {
91 let sugg = candidate.map(|candidate| lints::UnknownCrateTypesSub { span, candidate });
92 lints::UnknownCrateTypes { sugg }.decorate_lint(diag);
93 }
94 BuiltinLintDiag::UnusedImports {
95 remove_whole_use,
96 num_to_remove,
97 remove_spans,
98 test_module_span,
99 span_snippets,
100 } => {
101 let sugg = if remove_whole_use {
102 lints::UnusedImportsSugg::RemoveWholeUse { span: remove_spans[0] }
103 } else {
104 lints::UnusedImportsSugg::RemoveImports { remove_spans, num_to_remove }
105 };
106 let test_module_span =
107 test_module_span.map(|span| sess.source_map().guess_head_span(span));
108
109 lints::UnusedImports {
110 sugg,
111 test_module_span,
112 num_snippets: span_snippets.len(),
113 span_snippets: DiagArgValue::StrListSepByAnd(
114 span_snippets.into_iter().map(Cow::Owned).collect(),
115 ),
116 }
117 .decorate_lint(diag);
118 }
119 BuiltinLintDiag::RedundantImport(spans, ident) => {
120 let subs = spans
121 .into_iter()
122 .map(|(span, is_imported)| {
123 (match (span.is_dummy(), is_imported) {
124 (false, true) => lints::RedundantImportSub::ImportedHere,
125 (false, false) => lints::RedundantImportSub::DefinedHere,
126 (true, true) => lints::RedundantImportSub::ImportedPrelude,
127 (true, false) => lints::RedundantImportSub::DefinedPrelude,
128 })(span)
129 })
130 .collect();
131 lints::RedundantImport { subs, ident }.decorate_lint(diag);
132 }
133 BuiltinLintDiag::DeprecatedMacro {
134 suggestion,
135 suggestion_span,
136 note,
137 path,
138 since_kind,
139 } => {
140 let sub = suggestion.map(|suggestion| stability::DeprecationSuggestion {
141 span: suggestion_span,
142 kind: "macro".to_owned(),
143 suggestion,
144 });
145
146 stability::Deprecated { sub, kind: "macro".to_owned(), path, note, since_kind }
147 .decorate_lint(diag);
148 }
149 BuiltinLintDiag::UnusedDocComment(attr_span) => {
150 lints::UnusedDocComment { span: attr_span }.decorate_lint(diag);
151 }
152 BuiltinLintDiag::PatternsInFnsWithoutBody { span: remove_span, ident, is_foreign } => {
153 let sub = lints::PatternsInFnsWithoutBodySub { ident, span: remove_span };
154 if is_foreign {
155 lints::PatternsInFnsWithoutBody::Foreign { sub }
156 } else {
157 lints::PatternsInFnsWithoutBody::Bodiless { sub }
158 }
159 .decorate_lint(diag);
160 }
161 BuiltinLintDiag::LegacyDeriveHelpers(label_span) => {
162 lints::LegacyDeriveHelpers { span: label_span }.decorate_lint(diag);
163 }
164 BuiltinLintDiag::OrPatternsBackCompat(suggestion_span, suggestion) => {
165 lints::OrPatternsBackCompat { span: suggestion_span, suggestion }.decorate_lint(diag);
166 }
167 BuiltinLintDiag::ReservedPrefix(label_span, prefix) => {
168 lints::ReservedPrefix {
169 label: label_span,
170 suggestion: label_span.shrink_to_hi(),
171 prefix,
172 }
173 .decorate_lint(diag);
174 }
175 BuiltinLintDiag::RawPrefix(label_span) => {
176 lints::RawPrefix { label: label_span, suggestion: label_span.shrink_to_hi() }
177 .decorate_lint(diag);
178 }
179 BuiltinLintDiag::ReservedString { is_string, suggestion } => {
180 if is_string {
181 lints::ReservedString { suggestion }.decorate_lint(diag);
182 } else {
183 lints::ReservedMultihash { suggestion }.decorate_lint(diag);
184 }
185 }
186 BuiltinLintDiag::UnusedBuiltinAttribute {
187 attr_name,
188 macro_name,
189 invoc_span,
190 attr_span,
191 } => {
192 lints::UnusedBuiltinAttribute { invoc_span, attr_name, macro_name, attr_span }
193 .decorate_lint(diag);
194 }
195 BuiltinLintDiag::TrailingMacro(is_trailing, name) => {
196 lints::TrailingMacro { is_trailing, name }.decorate_lint(diag);
197 }
198 BuiltinLintDiag::BreakWithLabelAndLoop(sugg_span) => {
199 lints::BreakWithLabelAndLoop {
200 sub: lints::BreakWithLabelAndLoopSub {
201 left: sugg_span.shrink_to_lo(),
202 right: sugg_span.shrink_to_hi(),
203 },
204 }
205 .decorate_lint(diag);
206 }
207 BuiltinLintDiag::UnexpectedCfgName(name, value) => {
208 check_cfg::unexpected_cfg_name(sess, tcx, name, value).decorate_lint(diag);
209 }
210 BuiltinLintDiag::UnexpectedCfgValue(name, value) => {
211 check_cfg::unexpected_cfg_value(sess, tcx, name, value).decorate_lint(diag);
212 }
213 BuiltinLintDiag::DeprecatedWhereclauseLocation(left_sp, sugg) => {
214 let suggestion = match sugg {
215 Some((right_sp, sugg)) => lints::DeprecatedWhereClauseLocationSugg::MoveToEnd {
216 left: left_sp,
217 right: right_sp,
218 sugg,
219 },
220 None => lints::DeprecatedWhereClauseLocationSugg::RemoveWhere { span: left_sp },
221 };
222 lints::DeprecatedWhereClauseLocation { suggestion }.decorate_lint(diag);
223 }
224 BuiltinLintDiag::MissingUnsafeOnExtern { suggestion } => {
225 lints::MissingUnsafeOnExtern { suggestion }.decorate_lint(diag);
226 }
227 BuiltinLintDiag::SingleUseLifetime {
228 param_span,
229 use_span: Some((use_span, elide)),
230 deletion_span,
231 ident,
232 } => {
233 debug!(?param_span, ?use_span, ?deletion_span);
234 let suggestion = if let Some(deletion_span) = deletion_span {
235 let (use_span, replace_lt) = if elide {
236 let use_span = sess.source_map().span_extend_while_whitespace(use_span);
237 (use_span, String::new())
238 } else {
239 (use_span, "'_".to_owned())
240 };
241 debug!(?deletion_span, ?use_span);
242
243 let deletion_span =
246 if deletion_span.is_empty() { None } else { Some(deletion_span) };
247 Some(lints::SingleUseLifetimeSugg { deletion_span, use_span, replace_lt })
248 } else {
249 None
250 };
251
252 lints::SingleUseLifetime { suggestion, param_span, use_span, ident }
253 .decorate_lint(diag);
254 }
255 BuiltinLintDiag::SingleUseLifetime { use_span: None, deletion_span, ident, .. } => {
256 debug!(?deletion_span);
257 lints::UnusedLifetime { deletion_span, ident }.decorate_lint(diag);
258 }
259 BuiltinLintDiag::NamedArgumentUsedPositionally {
260 position_sp_to_replace,
261 position_sp_for_msg,
262 named_arg_sp,
263 named_arg_name,
264 is_formatting_arg,
265 } => {
266 let (suggestion, name) = if let Some(positional_arg_to_replace) = position_sp_to_replace
267 {
268 let mut name = named_arg_name.clone();
269 if is_formatting_arg {
270 name.push('$')
271 };
272 let span_to_replace = if let Ok(positional_arg_content) =
273 sess.source_map().span_to_snippet(positional_arg_to_replace)
274 && positional_arg_content.starts_with(':')
275 {
276 positional_arg_to_replace.shrink_to_lo()
277 } else {
278 positional_arg_to_replace
279 };
280 (Some(span_to_replace), name)
281 } else {
282 (None, String::new())
283 };
284
285 lints::NamedArgumentUsedPositionally {
286 named_arg_sp,
287 position_label_sp: position_sp_for_msg,
288 suggestion,
289 name,
290 named_arg_name,
291 }
292 .decorate_lint(diag);
293 }
294 BuiltinLintDiag::ByteSliceInPackedStructWithDerive { ty } => {
295 lints::ByteSliceInPackedStructWithDerive { ty }.decorate_lint(diag);
296 }
297 BuiltinLintDiag::UnusedExternCrate { span, removal_span } => {
298 lints::UnusedExternCrate { span, removal_span }.decorate_lint(diag);
299 }
300 BuiltinLintDiag::ExternCrateNotIdiomatic { vis_span, ident_span } => {
301 let suggestion_span = vis_span.between(ident_span);
302 let code = if vis_span.is_empty() { "use " } else { " use " };
303
304 lints::ExternCrateNotIdiomatic { span: suggestion_span, code }.decorate_lint(diag);
305 }
306 BuiltinLintDiag::AmbiguousGlobImports { diag: ambiguity } => {
307 lints::AmbiguousGlobImports { ambiguity }.decorate_lint(diag);
308 }
309 BuiltinLintDiag::AmbiguousGlobReexports {
310 name,
311 namespace,
312 first_reexport_span,
313 duplicate_reexport_span,
314 } => {
315 lints::AmbiguousGlobReexports {
316 first_reexport: first_reexport_span,
317 duplicate_reexport: duplicate_reexport_span,
318 name,
319 namespace,
320 }
321 .decorate_lint(diag);
322 }
323 BuiltinLintDiag::HiddenGlobReexports {
324 name,
325 namespace,
326 glob_reexport_span,
327 private_item_span,
328 } => {
329 lints::HiddenGlobReexports {
330 glob_reexport: glob_reexport_span,
331 private_item: private_item_span,
332
333 name,
334 namespace,
335 }
336 .decorate_lint(diag);
337 }
338 BuiltinLintDiag::ReexportPrivateDependency { name, kind, krate } => {
339 lints::ReexportPrivateDependency { name, kind, krate }.decorate_lint(diag);
340 }
341 BuiltinLintDiag::UnusedQualifications { removal_span } => {
342 lints::UnusedQualifications { removal_span }.decorate_lint(diag);
343 }
344 BuiltinLintDiag::UnsafeAttrOutsideUnsafe {
345 attribute_name_span,
346 sugg_spans: (left, right),
347 } => {
348 lints::UnsafeAttrOutsideUnsafe {
349 span: attribute_name_span,
350 suggestion: lints::UnsafeAttrOutsideUnsafeSuggestion { left, right },
351 }
352 .decorate_lint(diag);
353 }
354 BuiltinLintDiag::AssociatedConstElidedLifetime {
355 elided,
356 span: lt_span,
357 lifetimes_in_scope,
358 } => {
359 let lt_span = if elided { lt_span.shrink_to_hi() } else { lt_span };
360 let code = if elided { "'static " } else { "'static" };
361 lints::AssociatedConstElidedLifetime {
362 span: lt_span,
363 code,
364 elided,
365 lifetimes_in_scope,
366 }
367 .decorate_lint(diag);
368 }
369 BuiltinLintDiag::RedundantImportVisibility { max_vis, span: vis_span, import_vis } => {
370 lints::RedundantImportVisibility { span: vis_span, help: (), max_vis, import_vis }
371 .decorate_lint(diag);
372 }
373 BuiltinLintDiag::UnknownDiagnosticAttribute { span: typo_span, typo_name } => {
374 let typo = typo_name.map(|typo_name| lints::UnknownDiagnosticAttributeTypoSugg {
375 span: typo_span,
376 typo_name,
377 });
378 lints::UnknownDiagnosticAttribute { typo }.decorate_lint(diag);
379 }
380 BuiltinLintDiag::MacroUseDeprecated => {
381 lints::MacroUseDeprecated.decorate_lint(diag);
382 }
383 BuiltinLintDiag::UnusedMacroUse => lints::UnusedMacroUse.decorate_lint(diag),
384 BuiltinLintDiag::PrivateExternCrateReexport { source: ident, extern_crate_span } => {
385 lints::PrivateExternCrateReexport { ident, sugg: extern_crate_span.shrink_to_lo() }
386 .decorate_lint(diag);
387 }
388 BuiltinLintDiag::UnusedLabel => lints::UnusedLabel.decorate_lint(diag),
389 BuiltinLintDiag::MacroIsPrivate(ident) => {
390 lints::MacroIsPrivate { ident }.decorate_lint(diag);
391 }
392 BuiltinLintDiag::UnusedMacroDefinition(name) => {
393 lints::UnusedMacroDefinition { name }.decorate_lint(diag);
394 }
395 BuiltinLintDiag::MacroRuleNeverUsed(n, name) => {
396 lints::MacroRuleNeverUsed { n: n + 1, name }.decorate_lint(diag);
397 }
398 BuiltinLintDiag::UnstableFeature(msg) => {
399 lints::UnstableFeature { msg }.decorate_lint(diag);
400 }
401 BuiltinLintDiag::AvoidUsingIntelSyntax => {
402 lints::AvoidIntelSyntax.decorate_lint(diag);
403 }
404 BuiltinLintDiag::AvoidUsingAttSyntax => {
405 lints::AvoidAttSyntax.decorate_lint(diag);
406 }
407 BuiltinLintDiag::IncompleteInclude => {
408 lints::IncompleteInclude.decorate_lint(diag);
409 }
410 BuiltinLintDiag::UnnameableTestItems => {
411 lints::UnnameableTestItems.decorate_lint(diag);
412 }
413 BuiltinLintDiag::DuplicateMacroAttribute => {
414 lints::DuplicateMacroAttribute.decorate_lint(diag);
415 }
416 BuiltinLintDiag::CfgAttrNoAttributes => {
417 lints::CfgAttrNoAttributes.decorate_lint(diag);
418 }
419 BuiltinLintDiag::MetaVariableStillRepeating(name) => {
420 lints::MetaVariableStillRepeating { name }.decorate_lint(diag);
421 }
422 BuiltinLintDiag::MetaVariableWrongOperator => {
423 lints::MetaVariableWrongOperator.decorate_lint(diag);
424 }
425 BuiltinLintDiag::DuplicateMatcherBinding => {
426 lints::DuplicateMatcherBinding.decorate_lint(diag);
427 }
428 BuiltinLintDiag::UnknownMacroVariable(name) => {
429 lints::UnknownMacroVariable { name }.decorate_lint(diag);
430 }
431 BuiltinLintDiag::UnusedCrateDependency { extern_crate, local_crate } => {
432 lints::UnusedCrateDependency { extern_crate, local_crate }.decorate_lint(diag)
433 }
434 BuiltinLintDiag::IllFormedAttributeInput { suggestions, docs } => {
435 lints::IllFormedAttributeInput {
436 num_suggestions: suggestions.len(),
437 suggestions: DiagArgValue::StrListSepByAnd(
438 suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(),
439 ),
440 has_docs: docs.is_some(),
441 docs: docs.unwrap_or(""),
442 }
443 .decorate_lint(diag)
444 }
445 BuiltinLintDiag::OutOfScopeMacroCalls { span, path, location } => {
446 lints::OutOfScopeMacroCalls { span, path, location }.decorate_lint(diag)
447 }
448 }
449}