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::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}