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::AmbiguousGlobReexports {
255 name,
256 namespace,
257 first_reexport_span,
258 duplicate_reexport_span,
259 } => {
260 lints::AmbiguousGlobReexports {
261 first_reexport: first_reexport_span,
262 duplicate_reexport: duplicate_reexport_span,
263 name,
264 namespace,
265 }
266 .decorate_lint(diag);
267 }
268 BuiltinLintDiag::HiddenGlobReexports {
269 name,
270 namespace,
271 glob_reexport_span,
272 private_item_span,
273 } => {
274 lints::HiddenGlobReexports {
275 glob_reexport: glob_reexport_span,
276 private_item: private_item_span,
277
278 name,
279 namespace,
280 }
281 .decorate_lint(diag);
282 }
283 BuiltinLintDiag::UnusedQualifications { removal_span } => {
284 lints::UnusedQualifications { removal_span }.decorate_lint(diag);
285 }
286 BuiltinLintDiag::AssociatedConstElidedLifetime {
287 elided,
288 span: lt_span,
289 lifetimes_in_scope,
290 } => {
291 let lt_span = if elided { lt_span.shrink_to_hi() } else { lt_span };
292 let code = if elided { "'static " } else { "'static" };
293 lints::AssociatedConstElidedLifetime {
294 span: lt_span,
295 code,
296 elided,
297 lifetimes_in_scope,
298 }
299 .decorate_lint(diag);
300 }
301 BuiltinLintDiag::UnusedCrateDependency { extern_crate, local_crate } => {
302 lints::UnusedCrateDependency { extern_crate, local_crate }.decorate_lint(diag)
303 }
304 BuiltinLintDiag::IllFormedAttributeInput { suggestions, docs } => {
305 lints::IllFormedAttributeInput {
306 num_suggestions: suggestions.len(),
307 suggestions: DiagArgValue::StrListSepByAnd(
308 suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(),
309 ),
310 has_docs: docs.is_some(),
311 docs: docs.unwrap_or(""),
312 }
313 .decorate_lint(diag)
314 }
315 }
316}