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