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_hir::lints::AttributeLintKind;
8use rustc_middle::middle::stability;
9use rustc_middle::ty::TyCtxt;
10use rustc_session::Session;
11use rustc_session::lint::BuiltinLintDiag;
12use rustc_span::BytePos;
13use tracing::debug;
14
15use crate::lints;
16
17mod check_cfg;
18
19pub fn decorate_builtin_lint(
20    sess: &Session,
21    tcx: Option<TyCtxt<'_>>,
22    diagnostic: BuiltinLintDiag,
23    diag: &mut Diag<'_, ()>,
24) {
25    match diagnostic {
26        BuiltinLintDiag::UnicodeTextFlow(comment_span, content) => {
27            let spans: Vec<_> = content
28                .char_indices()
29                .filter_map(|(i, c)| {
30                    TEXT_FLOW_CONTROL_CHARS.contains(&c).then(|| {
31                        let lo = comment_span.lo() + BytePos(2 + i as u32);
32                        (c, comment_span.with_lo(lo).with_hi(lo + BytePos(c.len_utf8() as u32)))
33                    })
34                })
35                .collect();
36            let characters = spans
37                .iter()
38                .map(|&(c, span)| lints::UnicodeCharNoteSub { span, c_debug: format!("{c:?}") })
39                .collect();
40            let suggestions = (!spans.is_empty()).then_some(lints::UnicodeTextFlowSuggestion {
41                spans: spans.iter().map(|(_c, span)| *span).collect(),
42            });
43
44            lints::UnicodeTextFlow {
45                comment_span,
46                characters,
47                suggestions,
48                num_codepoints: spans.len(),
49            }
50            .decorate_lint(diag);
51        }
52        BuiltinLintDiag::AbsPathWithModule(mod_span) => {
53            let (replacement, applicability) = match sess.source_map().span_to_snippet(mod_span) {
54                Ok(ref s) => {
55                    // FIXME(Manishearth) ideally the emitting code
56                    // can tell us whether or not this is global
57                    let opt_colon = if s.trim_start().starts_with("::") { "" } else { "::" };
58
59                    (format!("crate{opt_colon}{s}"), Applicability::MachineApplicable)
60                }
61                Err(_) => ("crate::<path>".to_string(), Applicability::HasPlaceholders),
62            };
63            lints::AbsPathWithModule {
64                sugg: lints::AbsPathWithModuleSugg { span: mod_span, applicability, replacement },
65            }
66            .decorate_lint(diag);
67        }
68        BuiltinLintDiag::ElidedLifetimesInPaths(n, path_span, incl_angl_brckt, insertion_span) => {
69            lints::ElidedLifetimesInPaths {
70                subdiag: elided_lifetime_in_path_suggestion(
71                    sess.source_map(),
72                    n,
73                    path_span,
74                    incl_angl_brckt,
75                    insertion_span,
76                ),
77            }
78            .decorate_lint(diag);
79        }
80        BuiltinLintDiag::UnusedImports {
81            remove_whole_use,
82            num_to_remove,
83            remove_spans,
84            test_module_span,
85            span_snippets,
86        } => {
87            let sugg = if remove_whole_use {
88                lints::UnusedImportsSugg::RemoveWholeUse { span: remove_spans[0] }
89            } else {
90                lints::UnusedImportsSugg::RemoveImports { remove_spans, num_to_remove }
91            };
92            let test_module_span =
93                test_module_span.map(|span| sess.source_map().guess_head_span(span));
94
95            lints::UnusedImports {
96                sugg,
97                test_module_span,
98                num_snippets: span_snippets.len(),
99                span_snippets: DiagArgValue::StrListSepByAnd(
100                    span_snippets.into_iter().map(Cow::Owned).collect(),
101                ),
102            }
103            .decorate_lint(diag);
104        }
105        BuiltinLintDiag::RedundantImport(spans, ident) => {
106            let subs = spans
107                .into_iter()
108                .map(|(span, is_imported)| {
109                    (match (span.is_dummy(), is_imported) {
110                        (false, true) => lints::RedundantImportSub::ImportedHere,
111                        (false, false) => lints::RedundantImportSub::DefinedHere,
112                        (true, true) => lints::RedundantImportSub::ImportedPrelude,
113                        (true, false) => lints::RedundantImportSub::DefinedPrelude,
114                    })(span)
115                })
116                .collect();
117            lints::RedundantImport { subs, ident }.decorate_lint(diag);
118        }
119        BuiltinLintDiag::DeprecatedMacro {
120            suggestion,
121            suggestion_span,
122            note,
123            path,
124            since_kind,
125        } => {
126            let sub = suggestion.map(|suggestion| stability::DeprecationSuggestion {
127                span: suggestion_span,
128                kind: "macro".to_owned(),
129                suggestion,
130            });
131
132            stability::Deprecated { sub, kind: "macro".to_owned(), path, note, since_kind }
133                .decorate_lint(diag);
134        }
135        BuiltinLintDiag::PatternsInFnsWithoutBody { span: remove_span, ident, is_foreign } => {
136            let sub = lints::PatternsInFnsWithoutBodySub { ident, span: remove_span };
137            if is_foreign {
138                lints::PatternsInFnsWithoutBody::Foreign { sub }
139            } else {
140                lints::PatternsInFnsWithoutBody::Bodiless { sub }
141            }
142            .decorate_lint(diag);
143        }
144        BuiltinLintDiag::ReservedPrefix(label_span, prefix) => {
145            lints::ReservedPrefix {
146                label: label_span,
147                suggestion: label_span.shrink_to_hi(),
148                prefix,
149            }
150            .decorate_lint(diag);
151        }
152        BuiltinLintDiag::RawPrefix(label_span) => {
153            lints::RawPrefix { label: label_span, suggestion: label_span.shrink_to_hi() }
154                .decorate_lint(diag);
155        }
156        BuiltinLintDiag::ReservedString { is_string, suggestion } => {
157            if is_string {
158                lints::ReservedString { suggestion }.decorate_lint(diag);
159            } else {
160                lints::ReservedMultihash { suggestion }.decorate_lint(diag);
161            }
162        }
163        BuiltinLintDiag::BreakWithLabelAndLoop(sugg_span) => {
164            lints::BreakWithLabelAndLoop {
165                sub: lints::BreakWithLabelAndLoopSub {
166                    left: sugg_span.shrink_to_lo(),
167                    right: sugg_span.shrink_to_hi(),
168                },
169            }
170            .decorate_lint(diag);
171        }
172        BuiltinLintDiag::DeprecatedWhereclauseLocation(left_sp, sugg) => {
173            let suggestion = match sugg {
174                Some((right_sp, sugg)) => lints::DeprecatedWhereClauseLocationSugg::MoveToEnd {
175                    left: left_sp,
176                    right: right_sp,
177                    sugg,
178                },
179                None => lints::DeprecatedWhereClauseLocationSugg::RemoveWhere { span: left_sp },
180            };
181            lints::DeprecatedWhereClauseLocation { suggestion }.decorate_lint(diag);
182        }
183        BuiltinLintDiag::SingleUseLifetime {
184            param_span,
185            use_span: Some((use_span, elide)),
186            deletion_span,
187            ident,
188        } => {
189            debug!(?param_span, ?use_span, ?deletion_span);
190            let suggestion = if let Some(deletion_span) = deletion_span {
191                let (use_span, replace_lt) = if elide {
192                    let use_span = sess.source_map().span_extend_while_whitespace(use_span);
193                    (use_span, String::new())
194                } else {
195                    (use_span, "'_".to_owned())
196                };
197                debug!(?deletion_span, ?use_span);
198
199                // issue 107998 for the case such as a wrong function pointer type
200                // `deletion_span` is empty and there is no need to report lifetime uses here
201                let deletion_span =
202                    if deletion_span.is_empty() { None } else { Some(deletion_span) };
203                Some(lints::SingleUseLifetimeSugg { deletion_span, use_span, replace_lt })
204            } else {
205                None
206            };
207
208            lints::SingleUseLifetime { suggestion, param_span, use_span, ident }
209                .decorate_lint(diag);
210        }
211        BuiltinLintDiag::SingleUseLifetime { use_span: None, deletion_span, ident, .. } => {
212            lints::UnusedLifetime { deletion_span, ident }.decorate_lint(diag);
213        }
214        BuiltinLintDiag::NamedArgumentUsedPositionally {
215            position_sp_to_replace,
216            position_sp_for_msg,
217            named_arg_sp,
218            named_arg_name,
219            is_formatting_arg,
220        } => {
221            let (suggestion, name) = if let Some(positional_arg_to_replace) = position_sp_to_replace
222            {
223                let mut name = named_arg_name.clone();
224                if is_formatting_arg {
225                    name.push('$')
226                };
227                let span_to_replace = if let Ok(positional_arg_content) =
228                    sess.source_map().span_to_snippet(positional_arg_to_replace)
229                    && positional_arg_content.starts_with(':')
230                {
231                    positional_arg_to_replace.shrink_to_lo()
232                } else {
233                    positional_arg_to_replace
234                };
235                (Some(span_to_replace), name)
236            } else {
237                (None, String::new())
238            };
239
240            lints::NamedArgumentUsedPositionally {
241                named_arg_sp,
242                position_label_sp: position_sp_for_msg,
243                suggestion,
244                name,
245                named_arg_name,
246            }
247            .decorate_lint(diag);
248        }
249        BuiltinLintDiag::AmbiguousGlobReexports {
250            name,
251            namespace,
252            first_reexport_span,
253            duplicate_reexport_span,
254        } => {
255            lints::AmbiguousGlobReexports {
256                first_reexport: first_reexport_span,
257                duplicate_reexport: duplicate_reexport_span,
258                name,
259                namespace,
260            }
261            .decorate_lint(diag);
262        }
263        BuiltinLintDiag::HiddenGlobReexports {
264            name,
265            namespace,
266            glob_reexport_span,
267            private_item_span,
268        } => {
269            lints::HiddenGlobReexports {
270                glob_reexport: glob_reexport_span,
271                private_item: private_item_span,
272
273                name,
274                namespace,
275            }
276            .decorate_lint(diag);
277        }
278        BuiltinLintDiag::UnusedQualifications { removal_span } => {
279            lints::UnusedQualifications { removal_span }.decorate_lint(diag);
280        }
281        BuiltinLintDiag::AssociatedConstElidedLifetime {
282            elided,
283            span: lt_span,
284            lifetimes_in_scope,
285        } => {
286            let lt_span = if elided { lt_span.shrink_to_hi() } else { lt_span };
287            let code = if elided { "'static " } else { "'static" };
288            lints::AssociatedConstElidedLifetime {
289                span: lt_span,
290                code,
291                elided,
292                lifetimes_in_scope,
293            }
294            .decorate_lint(diag);
295        }
296        BuiltinLintDiag::UnusedCrateDependency { extern_crate, local_crate } => {
297            lints::UnusedCrateDependency { extern_crate, local_crate }.decorate_lint(diag)
298        }
299        BuiltinLintDiag::UnusedVisibility(span) => {
300            lints::UnusedVisibility { span }.decorate_lint(diag)
301        }
302        BuiltinLintDiag::AttributeLint(kind) => decorate_attribute_lint(sess, tcx, &kind, diag),
303    }
304}
305
306pub fn decorate_attribute_lint(
307    sess: &Session,
308    tcx: Option<TyCtxt<'_>>,
309    kind: &AttributeLintKind,
310    diag: &mut Diag<'_, ()>,
311) {
312    match kind {
313        &AttributeLintKind::UnusedDuplicate { this, other, warning } => {
314            lints::UnusedDuplicate { this, other, warning }.decorate_lint(diag)
315        }
316        AttributeLintKind::IllFormedAttributeInput { suggestions, docs } => {
317            lints::IllFormedAttributeInput {
318                num_suggestions: suggestions.len(),
319                suggestions: DiagArgValue::StrListSepByAnd(
320                    suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(),
321                ),
322                has_docs: docs.is_some(),
323                docs: docs.unwrap_or(""),
324            }
325            .decorate_lint(diag)
326        }
327        AttributeLintKind::EmptyAttribute { first_span, attr_path, valid_without_list } => {
328            lints::EmptyAttributeList {
329                attr_span: *first_span,
330                attr_path: attr_path.clone(),
331                valid_without_list: *valid_without_list,
332            }
333            .decorate_lint(diag)
334        }
335        AttributeLintKind::InvalidTarget { name, target, applied, only, attr_span } => {
336            lints::InvalidTargetLint {
337                name: name.clone(),
338                target,
339                applied: DiagArgValue::StrListSepByAnd(
340                    applied.into_iter().map(|i| Cow::Owned(i.to_string())).collect(),
341                ),
342                only,
343                attr_span: *attr_span,
344            }
345            .decorate_lint(diag)
346        }
347        &AttributeLintKind::InvalidStyle { ref name, is_used_as_inner, target, target_span } => {
348            lints::InvalidAttrStyle {
349                name: name.clone(),
350                is_used_as_inner,
351                target_span: (!is_used_as_inner).then_some(target_span),
352                target,
353            }
354            .decorate_lint(diag)
355        }
356        &AttributeLintKind::UnsafeAttrOutsideUnsafe { attribute_name_span, sugg_spans } => {
357            lints::UnsafeAttrOutsideUnsafeLint {
358                span: attribute_name_span,
359                suggestion: sugg_spans
360                    .map(|(left, right)| lints::UnsafeAttrOutsideUnsafeSuggestion { left, right }),
361            }
362            .decorate_lint(diag)
363        }
364        &AttributeLintKind::UnexpectedCfgName(name, value) => {
365            check_cfg::unexpected_cfg_name(sess, tcx, name, value).decorate_lint(diag)
366        }
367        &AttributeLintKind::UnexpectedCfgValue(name, value) => {
368            check_cfg::unexpected_cfg_value(sess, tcx, name, value).decorate_lint(diag)
369        }
370        &AttributeLintKind::DuplicateDocAlias { first_definition } => {
371            lints::DocAliasDuplicated { first_defn: first_definition }.decorate_lint(diag)
372        }
373
374        &AttributeLintKind::DocAutoCfgExpectsHideOrShow => {
375            lints::DocAutoCfgExpectsHideOrShow.decorate_lint(diag)
376        }
377
378        &AttributeLintKind::DocAutoCfgHideShowUnexpectedItem { attr_name } => {
379            lints::DocAutoCfgHideShowUnexpectedItem { attr_name }.decorate_lint(diag)
380        }
381
382        &AttributeLintKind::DocAutoCfgHideShowExpectsList { attr_name } => {
383            lints::DocAutoCfgHideShowExpectsList { attr_name }.decorate_lint(diag)
384        }
385
386        &AttributeLintKind::DocInvalid => { lints::DocInvalid }.decorate_lint(diag),
387
388        &AttributeLintKind::DocUnknownInclude { span, inner, value } => {
389            lints::DocUnknownInclude { inner, value, sugg: (span, Applicability::MaybeIncorrect) }
390        }
391        .decorate_lint(diag),
392
393        &AttributeLintKind::DocUnknownSpotlight { span } => {
394            lints::DocUnknownSpotlight { sugg_span: span }.decorate_lint(diag)
395        }
396
397        &AttributeLintKind::DocUnknownPasses { name, span } => {
398            lints::DocUnknownPasses { name, note_span: span }.decorate_lint(diag)
399        }
400
401        &AttributeLintKind::DocUnknownPlugins { span } => {
402            lints::DocUnknownPlugins { label_span: span }.decorate_lint(diag)
403        }
404
405        &AttributeLintKind::DocUnknownAny { name } => {
406            lints::DocUnknownAny { name }.decorate_lint(diag)
407        }
408
409        &AttributeLintKind::DocAutoCfgWrongLiteral => {
410            lints::DocAutoCfgWrongLiteral.decorate_lint(diag)
411        }
412
413        &AttributeLintKind::DocTestTakesList => lints::DocTestTakesList.decorate_lint(diag),
414
415        &AttributeLintKind::DocTestUnknown { name } => {
416            lints::DocTestUnknown { name }.decorate_lint(diag)
417        }
418
419        &AttributeLintKind::DocTestLiteral => lints::DocTestLiteral.decorate_lint(diag),
420    }
421}