rustc_lint/early/
diagnostics.rs

1#![allow(rustc::diagnostic_outside_of_impl)]
2#![allow(rustc::untranslatable_diagnostic)]
3
4use std::borrow::Cow;
5
6use rustc_ast::util::unicode::TEXT_FLOW_CONTROL_CHARS;
7use rustc_errors::{
8    Applicability, Diag, DiagArgValue, LintDiagnostic, elided_lifetime_in_path_suggestion,
9};
10use rustc_middle::middle::stability;
11use rustc_middle::ty::TyCtxt;
12use rustc_session::Session;
13use rustc_session::lint::{BuiltinLintDiag, ElidedLifetimeResolution};
14use rustc_span::{BytePos, kw};
15use tracing::debug;
16
17use crate::lints::{self, ElidedNamedLifetime};
18
19mod check_cfg;
20
21pub(super) fn decorate_lint(
22    sess: &Session,
23    tcx: Option<TyCtxt<'_>>,
24    diagnostic: BuiltinLintDiag,
25    diag: &mut Diag<'_, ()>,
26) {
27    match diagnostic {
28        BuiltinLintDiag::UnicodeTextFlow(comment_span, content) => {
29            let spans: Vec<_> = content
30                .char_indices()
31                .filter_map(|(i, c)| {
32                    TEXT_FLOW_CONTROL_CHARS.contains(&c).then(|| {
33                        let lo = comment_span.lo() + BytePos(2 + i as u32);
34                        (c, comment_span.with_lo(lo).with_hi(lo + BytePos(c.len_utf8() as u32)))
35                    })
36                })
37                .collect();
38            let characters = spans
39                .iter()
40                .map(|&(c, span)| lints::UnicodeCharNoteSub { span, c_debug: format!("{c:?}") })
41                .collect();
42            let suggestions = (!spans.is_empty()).then_some(lints::UnicodeTextFlowSuggestion {
43                spans: spans.iter().map(|(_c, span)| *span).collect(),
44            });
45
46            lints::UnicodeTextFlow {
47                comment_span,
48                characters,
49                suggestions,
50                num_codepoints: spans.len(),
51            }
52            .decorate_lint(diag);
53        }
54        BuiltinLintDiag::AbsPathWithModule(mod_span) => {
55            let (replacement, applicability) = match sess.source_map().span_to_snippet(mod_span) {
56                Ok(ref s) => {
57                    // FIXME(Manishearth) ideally the emitting code
58                    // can tell us whether or not this is global
59                    let opt_colon = if s.trim_start().starts_with("::") { "" } else { "::" };
60
61                    (format!("crate{opt_colon}{s}"), Applicability::MachineApplicable)
62                }
63                Err(_) => ("crate::<path>".to_string(), Applicability::HasPlaceholders),
64            };
65            lints::AbsPathWithModule {
66                sugg: lints::AbsPathWithModuleSugg { span: mod_span, applicability, replacement },
67            }
68            .decorate_lint(diag);
69        }
70        BuiltinLintDiag::ProcMacroDeriveResolutionFallback { span: macro_span, ns, ident } => {
71            lints::ProcMacroDeriveResolutionFallback { span: macro_span, ns, ident }
72                .decorate_lint(diag)
73        }
74        BuiltinLintDiag::MacroExpandedMacroExportsAccessedByAbsolutePaths(span_def) => {
75            lints::MacroExpandedMacroExportsAccessedByAbsolutePaths { definition: span_def }
76                .decorate_lint(diag)
77        }
78
79        BuiltinLintDiag::ElidedLifetimesInPaths(n, path_span, incl_angl_brckt, insertion_span) => {
80            lints::ElidedLifetimesInPaths {
81                subdiag: elided_lifetime_in_path_suggestion(
82                    sess.source_map(),
83                    n,
84                    path_span,
85                    incl_angl_brckt,
86                    insertion_span,
87                ),
88            }
89            .decorate_lint(diag);
90        }
91        BuiltinLintDiag::UnknownCrateTypes { span, candidate } => {
92            let sugg = candidate.map(|candidate| lints::UnknownCrateTypesSub { span, candidate });
93            lints::UnknownCrateTypes { sugg }.decorate_lint(diag);
94        }
95        BuiltinLintDiag::UnusedImports {
96            remove_whole_use,
97            num_to_remove,
98            remove_spans,
99            test_module_span,
100            span_snippets,
101        } => {
102            let sugg = if remove_whole_use {
103                lints::UnusedImportsSugg::RemoveWholeUse { span: remove_spans[0] }
104            } else {
105                lints::UnusedImportsSugg::RemoveImports { remove_spans, num_to_remove }
106            };
107            let test_module_span =
108                test_module_span.map(|span| sess.source_map().guess_head_span(span));
109
110            lints::UnusedImports {
111                sugg,
112                test_module_span,
113                num_snippets: span_snippets.len(),
114                span_snippets: DiagArgValue::StrListSepByAnd(
115                    span_snippets.into_iter().map(Cow::Owned).collect(),
116                ),
117            }
118            .decorate_lint(diag);
119        }
120        BuiltinLintDiag::RedundantImport(spans, ident) => {
121            let subs = spans
122                .into_iter()
123                .map(|(span, is_imported)| {
124                    (match (span.is_dummy(), is_imported) {
125                        (false, true) => lints::RedundantImportSub::ImportedHere,
126                        (false, false) => lints::RedundantImportSub::DefinedHere,
127                        (true, true) => lints::RedundantImportSub::ImportedPrelude,
128                        (true, false) => lints::RedundantImportSub::DefinedPrelude,
129                    })(span)
130                })
131                .collect();
132            lints::RedundantImport { subs, ident }.decorate_lint(diag);
133        }
134        BuiltinLintDiag::DeprecatedMacro {
135            suggestion,
136            suggestion_span,
137            note,
138            path,
139            since_kind,
140        } => {
141            let sub = suggestion.map(|suggestion| stability::DeprecationSuggestion {
142                span: suggestion_span,
143                kind: "macro".to_owned(),
144                suggestion,
145            });
146
147            stability::Deprecated { sub, kind: "macro".to_owned(), path, note, since_kind }
148                .decorate_lint(diag);
149        }
150        BuiltinLintDiag::UnusedDocComment(attr_span) => {
151            lints::UnusedDocComment { span: attr_span }.decorate_lint(diag);
152        }
153        BuiltinLintDiag::PatternsInFnsWithoutBody { span: remove_span, ident, is_foreign } => {
154            let sub = lints::PatternsInFnsWithoutBodySub { ident, span: remove_span };
155            if is_foreign {
156                lints::PatternsInFnsWithoutBody::Foreign { sub }
157            } else {
158                lints::PatternsInFnsWithoutBody::Bodiless { sub }
159            }
160            .decorate_lint(diag);
161        }
162        BuiltinLintDiag::MissingAbi(label_span, default_abi) => {
163            lints::MissingAbi { span: label_span, default_abi }.decorate_lint(diag);
164        }
165        BuiltinLintDiag::LegacyDeriveHelpers(label_span) => {
166            lints::LegacyDeriveHelpers { span: label_span }.decorate_lint(diag);
167        }
168        BuiltinLintDiag::OrPatternsBackCompat(suggestion_span, suggestion) => {
169            lints::OrPatternsBackCompat { span: suggestion_span, suggestion }.decorate_lint(diag);
170        }
171        BuiltinLintDiag::ReservedPrefix(label_span, prefix) => {
172            lints::ReservedPrefix {
173                label: label_span,
174                suggestion: label_span.shrink_to_hi(),
175                prefix,
176            }
177            .decorate_lint(diag);
178        }
179        BuiltinLintDiag::RawPrefix(label_span) => {
180            lints::RawPrefix { label: label_span, suggestion: label_span.shrink_to_hi() }
181                .decorate_lint(diag);
182        }
183        BuiltinLintDiag::ReservedString { is_string, suggestion } => {
184            if is_string {
185                lints::ReservedString { suggestion }.decorate_lint(diag);
186            } else {
187                lints::ReservedMultihash { suggestion }.decorate_lint(diag);
188            }
189        }
190        BuiltinLintDiag::UnusedBuiltinAttribute { attr_name, macro_name, invoc_span } => {
191            lints::UnusedBuiltinAttribute { invoc_span, attr_name, macro_name }.decorate_lint(diag);
192        }
193        BuiltinLintDiag::TrailingMacro(is_trailing, name) => {
194            lints::TrailingMacro { is_trailing, name }.decorate_lint(diag);
195        }
196        BuiltinLintDiag::BreakWithLabelAndLoop(sugg_span) => {
197            lints::BreakWithLabelAndLoop {
198                sub: lints::BreakWithLabelAndLoopSub {
199                    left: sugg_span.shrink_to_lo(),
200                    right: sugg_span.shrink_to_hi(),
201                },
202            }
203            .decorate_lint(diag);
204        }
205        BuiltinLintDiag::UnexpectedCfgName(name, value) => {
206            check_cfg::unexpected_cfg_name(sess, tcx, name, value).decorate_lint(diag);
207        }
208        BuiltinLintDiag::UnexpectedCfgValue(name, value) => {
209            check_cfg::unexpected_cfg_value(sess, tcx, name, value).decorate_lint(diag);
210        }
211        BuiltinLintDiag::DeprecatedWhereclauseLocation(left_sp, sugg) => {
212            let suggestion = match sugg {
213                Some((right_sp, sugg)) => lints::DeprecatedWhereClauseLocationSugg::MoveToEnd {
214                    left: left_sp,
215                    right: right_sp,
216                    sugg,
217                },
218                None => lints::DeprecatedWhereClauseLocationSugg::RemoveWhere { span: left_sp },
219            };
220            lints::DeprecatedWhereClauseLocation { suggestion }.decorate_lint(diag);
221        }
222        BuiltinLintDiag::MissingUnsafeOnExtern { suggestion } => {
223            lints::MissingUnsafeOnExtern { suggestion }.decorate_lint(diag);
224        }
225        BuiltinLintDiag::SingleUseLifetime {
226            param_span,
227            use_span: Some((use_span, elide)),
228            deletion_span,
229            ident,
230        } => {
231            debug!(?param_span, ?use_span, ?deletion_span);
232            let suggestion = if let Some(deletion_span) = deletion_span {
233                let (use_span, replace_lt) = if elide {
234                    let use_span = sess.source_map().span_extend_while_whitespace(use_span);
235                    (use_span, String::new())
236                } else {
237                    (use_span, "'_".to_owned())
238                };
239                debug!(?deletion_span, ?use_span);
240
241                // issue 107998 for the case such as a wrong function pointer type
242                // `deletion_span` is empty and there is no need to report lifetime uses here
243                let deletion_span =
244                    if deletion_span.is_empty() { None } else { Some(deletion_span) };
245                Some(lints::SingleUseLifetimeSugg { deletion_span, use_span, replace_lt })
246            } else {
247                None
248            };
249
250            lints::SingleUseLifetime { suggestion, param_span, use_span, ident }
251                .decorate_lint(diag);
252        }
253        BuiltinLintDiag::SingleUseLifetime { use_span: None, deletion_span, ident, .. } => {
254            debug!(?deletion_span);
255            lints::UnusedLifetime { deletion_span, ident }.decorate_lint(diag);
256        }
257        BuiltinLintDiag::NamedArgumentUsedPositionally {
258            position_sp_to_replace,
259            position_sp_for_msg,
260            named_arg_sp,
261            named_arg_name,
262            is_formatting_arg,
263        } => {
264            let (suggestion, name) = if let Some(positional_arg_to_replace) = position_sp_to_replace
265            {
266                let mut name = named_arg_name.clone();
267                if is_formatting_arg {
268                    name.push('$')
269                };
270                let span_to_replace = if let Ok(positional_arg_content) =
271                    sess.source_map().span_to_snippet(positional_arg_to_replace)
272                    && positional_arg_content.starts_with(':')
273                {
274                    positional_arg_to_replace.shrink_to_lo()
275                } else {
276                    positional_arg_to_replace
277                };
278                (Some(span_to_replace), name)
279            } else {
280                (None, String::new())
281            };
282
283            lints::NamedArgumentUsedPositionally {
284                named_arg_sp,
285                position_label_sp: position_sp_for_msg,
286                suggestion,
287                name,
288                named_arg_name,
289            }
290            .decorate_lint(diag);
291        }
292        BuiltinLintDiag::ByteSliceInPackedStructWithDerive { ty } => {
293            lints::ByteSliceInPackedStructWithDerive { ty }.decorate_lint(diag);
294        }
295        BuiltinLintDiag::UnusedExternCrate { removal_span } => {
296            lints::UnusedExternCrate { removal_span }.decorate_lint(diag);
297        }
298        BuiltinLintDiag::ExternCrateNotIdiomatic { vis_span, ident_span } => {
299            let suggestion_span = vis_span.between(ident_span);
300            let code = if vis_span.is_empty() { "use " } else { " use " };
301
302            lints::ExternCrateNotIdiomatic { span: suggestion_span, code }.decorate_lint(diag);
303        }
304        BuiltinLintDiag::AmbiguousGlobImports { diag: ambiguity } => {
305            lints::AmbiguousGlobImports { ambiguity }.decorate_lint(diag);
306        }
307        BuiltinLintDiag::AmbiguousGlobReexports {
308            name,
309            namespace,
310            first_reexport_span,
311            duplicate_reexport_span,
312        } => {
313            lints::AmbiguousGlobReexports {
314                first_reexport: first_reexport_span,
315                duplicate_reexport: duplicate_reexport_span,
316                name,
317                namespace,
318            }
319            .decorate_lint(diag);
320        }
321        BuiltinLintDiag::HiddenGlobReexports {
322            name,
323            namespace,
324            glob_reexport_span,
325            private_item_span,
326        } => {
327            lints::HiddenGlobReexports {
328                glob_reexport: glob_reexport_span,
329                private_item: private_item_span,
330
331                name,
332                namespace,
333            }
334            .decorate_lint(diag);
335        }
336        BuiltinLintDiag::UnusedQualifications { removal_span } => {
337            lints::UnusedQualifications { removal_span }.decorate_lint(diag);
338        }
339        BuiltinLintDiag::UnsafeAttrOutsideUnsafe {
340            attribute_name_span,
341            sugg_spans: (left, right),
342        } => {
343            lints::UnsafeAttrOutsideUnsafe {
344                span: attribute_name_span,
345                suggestion: lints::UnsafeAttrOutsideUnsafeSuggestion { left, right },
346            }
347            .decorate_lint(diag);
348        }
349        BuiltinLintDiag::AssociatedConstElidedLifetime {
350            elided,
351            span: lt_span,
352            lifetimes_in_scope,
353        } => {
354            let lt_span = if elided { lt_span.shrink_to_hi() } else { lt_span };
355            let code = if elided { "'static " } else { "'static" };
356            lints::AssociatedConstElidedLifetime {
357                span: lt_span,
358                code,
359                elided,
360                lifetimes_in_scope,
361            }
362            .decorate_lint(diag);
363        }
364        BuiltinLintDiag::RedundantImportVisibility { max_vis, span: vis_span, import_vis } => {
365            lints::RedundantImportVisibility { span: vis_span, help: (), max_vis, import_vis }
366                .decorate_lint(diag);
367        }
368        BuiltinLintDiag::UnknownDiagnosticAttribute { span: typo_span, typo_name } => {
369            let typo = typo_name.map(|typo_name| lints::UnknownDiagnosticAttributeTypoSugg {
370                span: typo_span,
371                typo_name,
372            });
373            lints::UnknownDiagnosticAttribute { typo }.decorate_lint(diag);
374        }
375        BuiltinLintDiag::MacroUseDeprecated => {
376            lints::MacroUseDeprecated.decorate_lint(diag);
377        }
378        BuiltinLintDiag::UnusedMacroUse => lints::UnusedMacroUse.decorate_lint(diag),
379        BuiltinLintDiag::PrivateExternCrateReexport { source: ident, extern_crate_span } => {
380            lints::PrivateExternCrateReexport { ident, sugg: extern_crate_span.shrink_to_lo() }
381                .decorate_lint(diag);
382        }
383        BuiltinLintDiag::UnusedLabel => lints::UnusedLabel.decorate_lint(diag),
384        BuiltinLintDiag::MacroIsPrivate(ident) => {
385            lints::MacroIsPrivate { ident }.decorate_lint(diag);
386        }
387        BuiltinLintDiag::UnusedMacroDefinition(name) => {
388            lints::UnusedMacroDefinition { name }.decorate_lint(diag);
389        }
390        BuiltinLintDiag::MacroRuleNeverUsed(n, name) => {
391            lints::MacroRuleNeverUsed { n: n + 1, name }.decorate_lint(diag);
392        }
393        BuiltinLintDiag::UnstableFeature(msg) => {
394            lints::UnstableFeature { msg }.decorate_lint(diag);
395        }
396        BuiltinLintDiag::AvoidUsingIntelSyntax => {
397            lints::AvoidIntelSyntax.decorate_lint(diag);
398        }
399        BuiltinLintDiag::AvoidUsingAttSyntax => {
400            lints::AvoidAttSyntax.decorate_lint(diag);
401        }
402        BuiltinLintDiag::IncompleteInclude => {
403            lints::IncompleteInclude.decorate_lint(diag);
404        }
405        BuiltinLintDiag::UnnameableTestItems => {
406            lints::UnnameableTestItems.decorate_lint(diag);
407        }
408        BuiltinLintDiag::DuplicateMacroAttribute => {
409            lints::DuplicateMacroAttribute.decorate_lint(diag);
410        }
411        BuiltinLintDiag::CfgAttrNoAttributes => {
412            lints::CfgAttrNoAttributes.decorate_lint(diag);
413        }
414        BuiltinLintDiag::MissingFragmentSpecifier => {
415            lints::MissingFragmentSpecifier.decorate_lint(diag);
416        }
417        BuiltinLintDiag::MetaVariableStillRepeating(name) => {
418            lints::MetaVariableStillRepeating { name }.decorate_lint(diag);
419        }
420        BuiltinLintDiag::MetaVariableWrongOperator => {
421            lints::MetaVariableWrongOperator.decorate_lint(diag);
422        }
423        BuiltinLintDiag::DuplicateMatcherBinding => {
424            lints::DuplicateMatcherBinding.decorate_lint(diag);
425        }
426        BuiltinLintDiag::UnknownMacroVariable(name) => {
427            lints::UnknownMacroVariable { name }.decorate_lint(diag);
428        }
429        BuiltinLintDiag::UnusedCrateDependency { extern_crate, local_crate } => {
430            lints::UnusedCrateDependency { extern_crate, local_crate }.decorate_lint(diag)
431        }
432        BuiltinLintDiag::IllFormedAttributeInput { suggestions } => {
433            lints::IllFormedAttributeInput {
434                num_suggestions: suggestions.len(),
435                suggestions: DiagArgValue::StrListSepByAnd(
436                    suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(),
437                ),
438            }
439            .decorate_lint(diag)
440        }
441        BuiltinLintDiag::InnerAttributeUnstable { is_macro } => if is_macro {
442            lints::InnerAttributeUnstable::InnerMacroAttribute
443        } else {
444            lints::InnerAttributeUnstable::CustomInnerAttribute
445        }
446        .decorate_lint(diag),
447        BuiltinLintDiag::OutOfScopeMacroCalls { path } => {
448            lints::OutOfScopeMacroCalls { path }.decorate_lint(diag)
449        }
450        BuiltinLintDiag::UnexpectedBuiltinCfg { cfg, cfg_name, controlled_by } => {
451            lints::UnexpectedBuiltinCfg { cfg, cfg_name, controlled_by }.decorate_lint(diag)
452        }
453        BuiltinLintDiag::ElidedNamedLifetimes { elided: (span, kind), resolution } => {
454            match resolution {
455                ElidedLifetimeResolution::Static => {
456                    ElidedNamedLifetime { span, kind, name: kw::StaticLifetime, declaration: None }
457                }
458                ElidedLifetimeResolution::Param(name, declaration) => {
459                    ElidedNamedLifetime { span, kind, name, declaration: Some(declaration) }
460                }
461            }
462            .decorate_lint(diag)
463        }
464    }
465}