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::ProcMacroDeriveResolutionFallback { span: macro_span, ns, ident } => {
68            lints::ProcMacroDeriveResolutionFallback { span: macro_span, ns, ident }
69                .decorate_lint(diag)
70        }
71        BuiltinLintDiag::MacroExpandedMacroExportsAccessedByAbsolutePaths(span_def) => {
72            lints::MacroExpandedMacroExportsAccessedByAbsolutePaths { definition: span_def }
73                .decorate_lint(diag)
74        }
75
76        BuiltinLintDiag::ElidedLifetimesInPaths(n, path_span, incl_angl_brckt, insertion_span) => {
77            lints::ElidedLifetimesInPaths {
78                subdiag: elided_lifetime_in_path_suggestion(
79                    sess.source_map(),
80                    n,
81                    path_span,
82                    incl_angl_brckt,
83                    insertion_span,
84                ),
85            }
86            .decorate_lint(diag);
87        }
88        BuiltinLintDiag::UnknownCrateTypes { span, candidate } => {
89            let sugg = candidate.map(|candidate| lints::UnknownCrateTypesSub { span, candidate });
90            lints::UnknownCrateTypes { sugg }.decorate_lint(diag);
91        }
92        BuiltinLintDiag::UnusedImports {
93            remove_whole_use,
94            num_to_remove,
95            remove_spans,
96            test_module_span,
97            span_snippets,
98        } => {
99            let sugg = if remove_whole_use {
100                lints::UnusedImportsSugg::RemoveWholeUse { span: remove_spans[0] }
101            } else {
102                lints::UnusedImportsSugg::RemoveImports { remove_spans, num_to_remove }
103            };
104            let test_module_span =
105                test_module_span.map(|span| sess.source_map().guess_head_span(span));
106
107            lints::UnusedImports {
108                sugg,
109                test_module_span,
110                num_snippets: span_snippets.len(),
111                span_snippets: DiagArgValue::StrListSepByAnd(
112                    span_snippets.into_iter().map(Cow::Owned).collect(),
113                ),
114            }
115            .decorate_lint(diag);
116        }
117        BuiltinLintDiag::RedundantImport(spans, ident) => {
118            let subs = spans
119                .into_iter()
120                .map(|(span, is_imported)| {
121                    (match (span.is_dummy(), is_imported) {
122                        (false, true) => lints::RedundantImportSub::ImportedHere,
123                        (false, false) => lints::RedundantImportSub::DefinedHere,
124                        (true, true) => lints::RedundantImportSub::ImportedPrelude,
125                        (true, false) => lints::RedundantImportSub::DefinedPrelude,
126                    })(span)
127                })
128                .collect();
129            lints::RedundantImport { subs, ident }.decorate_lint(diag);
130        }
131        BuiltinLintDiag::DeprecatedMacro {
132            suggestion,
133            suggestion_span,
134            note,
135            path,
136            since_kind,
137        } => {
138            let sub = suggestion.map(|suggestion| stability::DeprecationSuggestion {
139                span: suggestion_span,
140                kind: "macro".to_owned(),
141                suggestion,
142            });
143
144            stability::Deprecated { sub, kind: "macro".to_owned(), path, note, since_kind }
145                .decorate_lint(diag);
146        }
147        BuiltinLintDiag::UnusedDocComment(attr_span) => {
148            lints::UnusedDocComment { span: attr_span }.decorate_lint(diag);
149        }
150        BuiltinLintDiag::PatternsInFnsWithoutBody { span: remove_span, ident, is_foreign } => {
151            let sub = lints::PatternsInFnsWithoutBodySub { ident, span: remove_span };
152            if is_foreign {
153                lints::PatternsInFnsWithoutBody::Foreign { sub }
154            } else {
155                lints::PatternsInFnsWithoutBody::Bodiless { sub }
156            }
157            .decorate_lint(diag);
158        }
159        BuiltinLintDiag::MissingAbi(label_span, default_abi) => {
160            lints::MissingAbi { span: label_span, default_abi }.decorate_lint(diag);
161        }
162        BuiltinLintDiag::LegacyDeriveHelpers(label_span) => {
163            lints::LegacyDeriveHelpers { span: label_span }.decorate_lint(diag);
164        }
165        BuiltinLintDiag::OrPatternsBackCompat(suggestion_span, suggestion) => {
166            lints::OrPatternsBackCompat { span: suggestion_span, suggestion }.decorate_lint(diag);
167        }
168        BuiltinLintDiag::ReservedPrefix(label_span, prefix) => {
169            lints::ReservedPrefix {
170                label: label_span,
171                suggestion: label_span.shrink_to_hi(),
172                prefix,
173            }
174            .decorate_lint(diag);
175        }
176        BuiltinLintDiag::RawPrefix(label_span) => {
177            lints::RawPrefix { label: label_span, suggestion: label_span.shrink_to_hi() }
178                .decorate_lint(diag);
179        }
180        BuiltinLintDiag::ReservedString { is_string, suggestion } => {
181            if is_string {
182                lints::ReservedString { suggestion }.decorate_lint(diag);
183            } else {
184                lints::ReservedMultihash { suggestion }.decorate_lint(diag);
185            }
186        }
187        BuiltinLintDiag::HiddenUnicodeCodepoints {
188            label,
189            count,
190            span_label,
191            labels,
192            escape,
193            spans,
194        } => {
195            lints::HiddenUnicodeCodepointsDiag {
196                label: &label,
197                count,
198                span_label,
199                labels: labels.map(|spans| lints::HiddenUnicodeCodepointsDiagLabels { spans }),
200                sub: if escape {
201                    lints::HiddenUnicodeCodepointsDiagSub::Escape { spans }
202                } else {
203                    lints::HiddenUnicodeCodepointsDiagSub::NoEscape { spans }
204                },
205            }
206            .decorate_lint(diag);
207        }
208        BuiltinLintDiag::UnusedBuiltinAttribute {
209            attr_name,
210            macro_name,
211            invoc_span,
212            attr_span,
213        } => {
214            lints::UnusedBuiltinAttribute { invoc_span, attr_name, macro_name, attr_span }
215                .decorate_lint(diag);
216        }
217        BuiltinLintDiag::TrailingMacro(is_trailing, name) => {
218            lints::TrailingMacro { is_trailing, name }.decorate_lint(diag);
219        }
220        BuiltinLintDiag::BreakWithLabelAndLoop(sugg_span) => {
221            lints::BreakWithLabelAndLoop {
222                sub: lints::BreakWithLabelAndLoopSub {
223                    left: sugg_span.shrink_to_lo(),
224                    right: sugg_span.shrink_to_hi(),
225                },
226            }
227            .decorate_lint(diag);
228        }
229        BuiltinLintDiag::UnexpectedCfgName(name, value) => {
230            check_cfg::unexpected_cfg_name(sess, tcx, name, value).decorate_lint(diag);
231        }
232        BuiltinLintDiag::UnexpectedCfgValue(name, value) => {
233            check_cfg::unexpected_cfg_value(sess, tcx, name, value).decorate_lint(diag);
234        }
235        BuiltinLintDiag::DeprecatedWhereclauseLocation(left_sp, sugg) => {
236            let suggestion = match sugg {
237                Some((right_sp, sugg)) => lints::DeprecatedWhereClauseLocationSugg::MoveToEnd {
238                    left: left_sp,
239                    right: right_sp,
240                    sugg,
241                },
242                None => lints::DeprecatedWhereClauseLocationSugg::RemoveWhere { span: left_sp },
243            };
244            lints::DeprecatedWhereClauseLocation { suggestion }.decorate_lint(diag);
245        }
246        BuiltinLintDiag::MissingUnsafeOnExtern { suggestion } => {
247            lints::MissingUnsafeOnExtern { suggestion }.decorate_lint(diag);
248        }
249        BuiltinLintDiag::SingleUseLifetime {
250            param_span,
251            use_span: Some((use_span, elide)),
252            deletion_span,
253            ident,
254        } => {
255            debug!(?param_span, ?use_span, ?deletion_span);
256            let suggestion = if let Some(deletion_span) = deletion_span {
257                let (use_span, replace_lt) = if elide {
258                    let use_span = sess.source_map().span_extend_while_whitespace(use_span);
259                    (use_span, String::new())
260                } else {
261                    (use_span, "'_".to_owned())
262                };
263                debug!(?deletion_span, ?use_span);
264
265                // issue 107998 for the case such as a wrong function pointer type
266                // `deletion_span` is empty and there is no need to report lifetime uses here
267                let deletion_span =
268                    if deletion_span.is_empty() { None } else { Some(deletion_span) };
269                Some(lints::SingleUseLifetimeSugg { deletion_span, use_span, replace_lt })
270            } else {
271                None
272            };
273
274            lints::SingleUseLifetime { suggestion, param_span, use_span, ident }
275                .decorate_lint(diag);
276        }
277        BuiltinLintDiag::SingleUseLifetime { use_span: None, deletion_span, ident, .. } => {
278            debug!(?deletion_span);
279            lints::UnusedLifetime { deletion_span, ident }.decorate_lint(diag);
280        }
281        BuiltinLintDiag::NamedArgumentUsedPositionally {
282            position_sp_to_replace,
283            position_sp_for_msg,
284            named_arg_sp,
285            named_arg_name,
286            is_formatting_arg,
287        } => {
288            let (suggestion, name) = if let Some(positional_arg_to_replace) = position_sp_to_replace
289            {
290                let mut name = named_arg_name.clone();
291                if is_formatting_arg {
292                    name.push('$')
293                };
294                let span_to_replace = if let Ok(positional_arg_content) =
295                    sess.source_map().span_to_snippet(positional_arg_to_replace)
296                    && positional_arg_content.starts_with(':')
297                {
298                    positional_arg_to_replace.shrink_to_lo()
299                } else {
300                    positional_arg_to_replace
301                };
302                (Some(span_to_replace), name)
303            } else {
304                (None, String::new())
305            };
306
307            lints::NamedArgumentUsedPositionally {
308                named_arg_sp,
309                position_label_sp: position_sp_for_msg,
310                suggestion,
311                name,
312                named_arg_name,
313            }
314            .decorate_lint(diag);
315        }
316        BuiltinLintDiag::ByteSliceInPackedStructWithDerive { ty } => {
317            lints::ByteSliceInPackedStructWithDerive { ty }.decorate_lint(diag);
318        }
319        BuiltinLintDiag::UnusedExternCrate { span, removal_span } => {
320            lints::UnusedExternCrate { span, removal_span }.decorate_lint(diag);
321        }
322        BuiltinLintDiag::ExternCrateNotIdiomatic { vis_span, ident_span } => {
323            let suggestion_span = vis_span.between(ident_span);
324            let code = if vis_span.is_empty() { "use " } else { " use " };
325
326            lints::ExternCrateNotIdiomatic { span: suggestion_span, code }.decorate_lint(diag);
327        }
328        BuiltinLintDiag::AmbiguousGlobImports { diag: ambiguity } => {
329            lints::AmbiguousGlobImports { ambiguity }.decorate_lint(diag);
330        }
331        BuiltinLintDiag::AmbiguousGlobReexports {
332            name,
333            namespace,
334            first_reexport_span,
335            duplicate_reexport_span,
336        } => {
337            lints::AmbiguousGlobReexports {
338                first_reexport: first_reexport_span,
339                duplicate_reexport: duplicate_reexport_span,
340                name,
341                namespace,
342            }
343            .decorate_lint(diag);
344        }
345        BuiltinLintDiag::HiddenGlobReexports {
346            name,
347            namespace,
348            glob_reexport_span,
349            private_item_span,
350        } => {
351            lints::HiddenGlobReexports {
352                glob_reexport: glob_reexport_span,
353                private_item: private_item_span,
354
355                name,
356                namespace,
357            }
358            .decorate_lint(diag);
359        }
360        BuiltinLintDiag::ReexportPrivateDependency { name, kind, krate } => {
361            lints::ReexportPrivateDependency { name, kind, krate }.decorate_lint(diag);
362        }
363        BuiltinLintDiag::UnusedQualifications { removal_span } => {
364            lints::UnusedQualifications { removal_span }.decorate_lint(diag);
365        }
366        BuiltinLintDiag::UnsafeAttrOutsideUnsafe {
367            attribute_name_span,
368            sugg_spans: (left, right),
369        } => {
370            lints::UnsafeAttrOutsideUnsafe {
371                span: attribute_name_span,
372                suggestion: lints::UnsafeAttrOutsideUnsafeSuggestion { left, right },
373            }
374            .decorate_lint(diag);
375        }
376        BuiltinLintDiag::AssociatedConstElidedLifetime {
377            elided,
378            span: lt_span,
379            lifetimes_in_scope,
380        } => {
381            let lt_span = if elided { lt_span.shrink_to_hi() } else { lt_span };
382            let code = if elided { "'static " } else { "'static" };
383            lints::AssociatedConstElidedLifetime {
384                span: lt_span,
385                code,
386                elided,
387                lifetimes_in_scope,
388            }
389            .decorate_lint(diag);
390        }
391        BuiltinLintDiag::RedundantImportVisibility { max_vis, span: vis_span, import_vis } => {
392            lints::RedundantImportVisibility { span: vis_span, help: (), max_vis, import_vis }
393                .decorate_lint(diag);
394        }
395        BuiltinLintDiag::UnknownDiagnosticAttribute { span: typo_span, typo_name } => {
396            let typo = typo_name.map(|typo_name| lints::UnknownDiagnosticAttributeTypoSugg {
397                span: typo_span,
398                typo_name,
399            });
400            lints::UnknownDiagnosticAttribute { typo }.decorate_lint(diag);
401        }
402        BuiltinLintDiag::MacroUseDeprecated => {
403            lints::MacroUseDeprecated.decorate_lint(diag);
404        }
405        BuiltinLintDiag::UnusedMacroUse => lints::UnusedMacroUse.decorate_lint(diag),
406        BuiltinLintDiag::PrivateExternCrateReexport { source: ident, extern_crate_span } => {
407            lints::PrivateExternCrateReexport { ident, sugg: extern_crate_span.shrink_to_lo() }
408                .decorate_lint(diag);
409        }
410        BuiltinLintDiag::UnusedLabel => lints::UnusedLabel.decorate_lint(diag),
411        BuiltinLintDiag::MacroIsPrivate(ident) => {
412            lints::MacroIsPrivate { ident }.decorate_lint(diag);
413        }
414        BuiltinLintDiag::UnusedMacroDefinition(name) => {
415            lints::UnusedMacroDefinition { name }.decorate_lint(diag);
416        }
417        BuiltinLintDiag::MacroRuleNeverUsed(n, name) => {
418            lints::MacroRuleNeverUsed { n: n + 1, name }.decorate_lint(diag);
419        }
420        BuiltinLintDiag::UnstableFeature(msg) => {
421            lints::UnstableFeature { msg }.decorate_lint(diag);
422        }
423        BuiltinLintDiag::AvoidUsingIntelSyntax => {
424            lints::AvoidIntelSyntax.decorate_lint(diag);
425        }
426        BuiltinLintDiag::AvoidUsingAttSyntax => {
427            lints::AvoidAttSyntax.decorate_lint(diag);
428        }
429        BuiltinLintDiag::IncompleteInclude => {
430            lints::IncompleteInclude.decorate_lint(diag);
431        }
432        BuiltinLintDiag::UnnameableTestItems => {
433            lints::UnnameableTestItems.decorate_lint(diag);
434        }
435        BuiltinLintDiag::DuplicateMacroAttribute => {
436            lints::DuplicateMacroAttribute.decorate_lint(diag);
437        }
438        BuiltinLintDiag::CfgAttrNoAttributes => {
439            lints::CfgAttrNoAttributes.decorate_lint(diag);
440        }
441        BuiltinLintDiag::MetaVariableStillRepeating(name) => {
442            lints::MetaVariableStillRepeating { name }.decorate_lint(diag);
443        }
444        BuiltinLintDiag::MetaVariableWrongOperator => {
445            lints::MetaVariableWrongOperator.decorate_lint(diag);
446        }
447        BuiltinLintDiag::DuplicateMatcherBinding => {
448            lints::DuplicateMatcherBinding.decorate_lint(diag);
449        }
450        BuiltinLintDiag::UnknownMacroVariable(name) => {
451            lints::UnknownMacroVariable { name }.decorate_lint(diag);
452        }
453        BuiltinLintDiag::UnusedCrateDependency { extern_crate, local_crate } => {
454            lints::UnusedCrateDependency { extern_crate, local_crate }.decorate_lint(diag)
455        }
456        BuiltinLintDiag::IllFormedAttributeInput { suggestions, docs } => {
457            lints::IllFormedAttributeInput {
458                num_suggestions: suggestions.len(),
459                suggestions: DiagArgValue::StrListSepByAnd(
460                    suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(),
461                ),
462                has_docs: docs.is_some(),
463                docs: docs.unwrap_or(""),
464            }
465            .decorate_lint(diag)
466        }
467        BuiltinLintDiag::InnerAttributeUnstable { is_macro } => if is_macro {
468            lints::InnerAttributeUnstable::InnerMacroAttribute
469        } else {
470            lints::InnerAttributeUnstable::CustomInnerAttribute
471        }
472        .decorate_lint(diag),
473        BuiltinLintDiag::OutOfScopeMacroCalls { span, path, location } => {
474            lints::OutOfScopeMacroCalls { span, path, location }.decorate_lint(diag)
475        }
476        BuiltinLintDiag::UnexpectedBuiltinCfg { cfg, cfg_name, controlled_by } => {
477            lints::UnexpectedBuiltinCfg { cfg, cfg_name, controlled_by }.decorate_lint(diag)
478        }
479    }
480}