rustc_lint/early/
diagnostics.rs

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                    // FIXME(Manishearth) ideally the emitting code
55                    // can tell us whether or not this is global
56                    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                // issue 107998 for the case such as a wrong function pointer type
205                // `deletion_span` is empty and there is no need to report lifetime uses here
206                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}