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