blob: 1cb0f906f730beec0a5d6620701e5a00fa8b99e5 [file]
use std::borrow::Cow;
use rustc_errors::{
Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, Level,
elided_lifetime_in_path_suggestion,
};
use rustc_hir::lints::{AttributeLintKind, FormatWarning};
use rustc_middle::ty::TyCtxt;
use rustc_session::Session;
use rustc_session::lint::BuiltinLintDiag;
use tracing::debug;
use crate::lints;
mod check_cfg;
/// This is a diagnostic struct that will decorate a `BuiltinLintDiag`
/// Directly creating the lint structs is expensive, using this will only decorate the lint structs when needed.
pub struct DecorateBuiltinLint<'sess, 'tcx> {
pub sess: &'sess Session,
pub tcx: Option<TyCtxt<'tcx>>,
pub diagnostic: BuiltinLintDiag,
}
impl<'a> Diagnostic<'a, ()> for DecorateBuiltinLint<'_, '_> {
fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> {
match self.diagnostic {
BuiltinLintDiag::AbsPathWithModule(mod_span) => {
let (replacement, applicability) =
match self.sess.source_map().span_to_snippet(mod_span) {
Ok(ref s) => {
// FIXME(Manishearth) ideally the emitting code
// can tell us whether or not this is global
let opt_colon =
if s.trim_start().starts_with("::") { "" } else { "::" };
(format!("crate{opt_colon}{s}"), Applicability::MachineApplicable)
}
Err(_) => ("crate::<path>".to_string(), Applicability::HasPlaceholders),
};
lints::AbsPathWithModule {
sugg: lints::AbsPathWithModuleSugg {
span: mod_span,
applicability,
replacement,
},
}
.into_diag(dcx, level)
}
BuiltinLintDiag::ElidedLifetimesInPaths(
n,
path_span,
incl_angl_brckt,
insertion_span,
) => lints::ElidedLifetimesInPaths {
subdiag: elided_lifetime_in_path_suggestion(
self.sess.source_map(),
n,
path_span,
incl_angl_brckt,
insertion_span,
),
}
.into_diag(dcx, level),
BuiltinLintDiag::UnusedImports {
remove_whole_use,
num_to_remove,
remove_spans,
test_module_span,
span_snippets,
} => {
let sugg = if remove_whole_use {
lints::UnusedImportsSugg::RemoveWholeUse { span: remove_spans[0] }
} else {
lints::UnusedImportsSugg::RemoveImports { remove_spans, num_to_remove }
};
let test_module_span =
test_module_span.map(|span| self.sess.source_map().guess_head_span(span));
lints::UnusedImports {
sugg,
test_module_span,
num_snippets: span_snippets.len(),
span_snippets: DiagArgValue::StrListSepByAnd(
span_snippets.into_iter().map(Cow::Owned).collect(),
),
}
.into_diag(dcx, level)
}
BuiltinLintDiag::SingleUseLifetime {
param_span,
use_span,
elidable,
deletion_span,
ident,
} => {
debug!(?param_span, ?use_span, ?deletion_span);
let suggestion = if let Some(deletion_span) = deletion_span {
let (use_span, replace_lt) = if elidable {
let use_span =
self.sess.source_map().span_extend_while_whitespace(use_span);
(use_span, String::new())
} else {
(use_span, "'_".to_owned())
};
debug!(?deletion_span, ?use_span);
// issue 107998 for the case such as a wrong function pointer type
// `deletion_span` is empty and there is no need to report lifetime uses here
let deletion_span =
if deletion_span.is_empty() { None } else { Some(deletion_span) };
Some(lints::SingleUseLifetimeSugg { deletion_span, use_span, replace_lt })
} else {
None
};
lints::SingleUseLifetime { suggestion, param_span, use_span, ident }
.into_diag(dcx, level)
}
BuiltinLintDiag::NamedArgumentUsedPositionally {
position_sp_to_replace,
position_sp_for_msg,
named_arg_sp,
named_arg_name,
is_formatting_arg,
} => {
let (suggestion, name) =
if let Some(positional_arg_to_replace) = position_sp_to_replace {
let mut name = named_arg_name.clone();
if is_formatting_arg {
name.push('$')
};
let span_to_replace = if let Ok(positional_arg_content) =
self.sess.source_map().span_to_snippet(positional_arg_to_replace)
&& positional_arg_content.starts_with(':')
{
positional_arg_to_replace.shrink_to_lo()
} else {
positional_arg_to_replace
};
(Some(span_to_replace), name)
} else {
(None, String::new())
};
lints::NamedArgumentUsedPositionally {
named_arg_sp,
position_label_sp: position_sp_for_msg,
suggestion,
name,
named_arg_name,
}
.into_diag(dcx, level)
}
BuiltinLintDiag::AttributeLint(kind) => {
DecorateAttrLint { sess: self.sess, tcx: self.tcx, diagnostic: &kind }
.into_diag(dcx, level)
}
}
}
}
/// This is a diagnostic struct that will decorate a `AttributeLintKind`
/// Directly creating the lint structs is expensive, using this will only decorate the lint structs when needed.
pub struct DecorateAttrLint<'a, 'sess, 'tcx> {
pub sess: &'sess Session,
pub tcx: Option<TyCtxt<'tcx>>,
pub diagnostic: &'a AttributeLintKind,
}
impl<'a> Diagnostic<'a, ()> for DecorateAttrLint<'_, '_, '_> {
fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> {
match self.diagnostic {
&AttributeLintKind::UnusedDuplicate { this, other, warning } => {
lints::UnusedDuplicate { this, other, warning }.into_diag(dcx, level)
}
AttributeLintKind::IllFormedAttributeInput { suggestions, docs } => {
lints::IllFormedAttributeInput {
num_suggestions: suggestions.len(),
suggestions: DiagArgValue::StrListSepByAnd(
suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(),
),
has_docs: docs.is_some(),
docs: docs.unwrap_or(""),
}
.into_diag(dcx, level)
}
AttributeLintKind::EmptyAttribute { first_span, attr_path, valid_without_list } => {
lints::EmptyAttributeList {
attr_span: *first_span,
attr_path: attr_path.clone(),
valid_without_list: *valid_without_list,
}
.into_diag(dcx, level)
}
AttributeLintKind::InvalidTarget { name, target, applied, only, attr_span } => {
lints::InvalidTargetLint {
name: name.clone(),
target,
applied: DiagArgValue::StrListSepByAnd(
applied.into_iter().map(|i| Cow::Owned(i.to_string())).collect(),
),
only,
attr_span: *attr_span,
}
.into_diag(dcx, level)
}
&AttributeLintKind::InvalidStyle {
ref name,
is_used_as_inner,
target,
target_span,
} => lints::InvalidAttrStyle {
name: name.clone(),
is_used_as_inner,
target_span: (!is_used_as_inner).then_some(target_span),
target,
}
.into_diag(dcx, level),
&AttributeLintKind::UnsafeAttrOutsideUnsafe { attribute_name_span, sugg_spans } => {
lints::UnsafeAttrOutsideUnsafeLint {
span: attribute_name_span,
suggestion: sugg_spans.map(|(left, right)| {
lints::UnsafeAttrOutsideUnsafeSuggestion { left, right }
}),
}
.into_diag(dcx, level)
}
&AttributeLintKind::UnexpectedCfgName(name, value) => {
check_cfg::unexpected_cfg_name(self.sess, self.tcx, name, value)
.into_diag(dcx, level)
}
&AttributeLintKind::UnexpectedCfgValue(name, value) => {
check_cfg::unexpected_cfg_value(self.sess, self.tcx, name, value)
.into_diag(dcx, level)
}
&AttributeLintKind::DuplicateDocAlias { first_definition } => {
lints::DocAliasDuplicated { first_defn: first_definition }.into_diag(dcx, level)
}
&AttributeLintKind::DocAutoCfgExpectsHideOrShow => {
lints::DocAutoCfgExpectsHideOrShow.into_diag(dcx, level)
}
&AttributeLintKind::AmbiguousDeriveHelpers => {
lints::AmbiguousDeriveHelpers.into_diag(dcx, level)
}
&AttributeLintKind::DocAutoCfgHideShowUnexpectedItem { attr_name } => {
lints::DocAutoCfgHideShowUnexpectedItem { attr_name }.into_diag(dcx, level)
}
&AttributeLintKind::DocAutoCfgHideShowExpectsList { attr_name } => {
lints::DocAutoCfgHideShowExpectsList { attr_name }.into_diag(dcx, level)
}
&AttributeLintKind::DocInvalid => lints::DocInvalid.into_diag(dcx, level),
&AttributeLintKind::DocUnknownInclude { span, inner, value } => {
lints::DocUnknownInclude {
inner,
value,
sugg: (span, Applicability::MaybeIncorrect),
}
.into_diag(dcx, level)
}
&AttributeLintKind::DocUnknownSpotlight { span } => {
lints::DocUnknownSpotlight { sugg_span: span }.into_diag(dcx, level)
}
&AttributeLintKind::DocUnknownPasses { name, span } => {
lints::DocUnknownPasses { name, note_span: span }.into_diag(dcx, level)
}
&AttributeLintKind::DocUnknownPlugins { span } => {
lints::DocUnknownPlugins { label_span: span }.into_diag(dcx, level)
}
&AttributeLintKind::DocUnknownAny { name } => {
lints::DocUnknownAny { name }.into_diag(dcx, level)
}
&AttributeLintKind::DocAutoCfgWrongLiteral => {
lints::DocAutoCfgWrongLiteral.into_diag(dcx, level)
}
&AttributeLintKind::DocTestTakesList => lints::DocTestTakesList.into_diag(dcx, level),
&AttributeLintKind::DocTestUnknown { name } => {
lints::DocTestUnknown { name }.into_diag(dcx, level)
}
&AttributeLintKind::DocTestLiteral => lints::DocTestLiteral.into_diag(dcx, level),
&AttributeLintKind::AttrCrateLevelOnly => {
lints::AttrCrateLevelOnly.into_diag(dcx, level)
}
&AttributeLintKind::DoNotRecommendDoesNotExpectArgs => {
lints::DoNotRecommendDoesNotExpectArgs.into_diag(dcx, level)
}
&AttributeLintKind::CrateTypeUnknown { span, suggested } => lints::UnknownCrateTypes {
sugg: suggested.map(|s| lints::UnknownCrateTypesSuggestion { span, snippet: s }),
}
.into_diag(dcx, level),
&AttributeLintKind::MalformedDoc => lints::MalformedDoc.into_diag(dcx, level),
&AttributeLintKind::ExpectedNoArgs => lints::ExpectedNoArgs.into_diag(dcx, level),
&AttributeLintKind::ExpectedNameValue => lints::ExpectedNameValue.into_diag(dcx, level),
&AttributeLintKind::MalformedOnUnimplementedAttr { span } => {
lints::MalformedOnUnimplementedAttrLint { span }.into_diag(dcx, level)
}
&AttributeLintKind::MalformedOnConstAttr { span } => {
lints::MalformedOnConstAttrLint { span }.into_diag(dcx, level)
}
AttributeLintKind::MalformedDiagnosticFormat { warning } => match warning {
FormatWarning::PositionalArgument { .. } => {
lints::DisallowedPositionalArgument.into_diag(dcx, level)
}
FormatWarning::InvalidSpecifier { .. } => {
lints::InvalidFormatSpecifier.into_diag(dcx, level)
}
},
AttributeLintKind::DiagnosticWrappedParserError { description, label, span } => {
lints::WrappedParserError { description, label, span: *span }.into_diag(dcx, level)
}
&AttributeLintKind::IgnoredDiagnosticOption { option_name, first_span, later_span } => {
lints::IgnoredDiagnosticOption { option_name, first_span, later_span }
.into_diag(dcx, level)
}
&AttributeLintKind::MissingOptionsForOnUnimplemented => {
lints::MissingOptionsForOnUnimplementedAttr.into_diag(dcx, level)
}
&AttributeLintKind::MissingOptionsForOnConst => {
lints::MissingOptionsForOnConstAttr.into_diag(dcx, level)
}
&AttributeLintKind::MalformedOnMoveAttr { span } => {
lints::MalformedOnMoveAttrLint { span }.into_diag(dcx, level)
}
&AttributeLintKind::OnMoveMalformedFormatLiterals { name } => {
lints::OnMoveMalformedFormatLiterals { name }.into_diag(dcx, level)
}
&AttributeLintKind::OnMoveMalformedAttrExpectedLiteralOrDelimiter => {
lints::OnMoveMalformedAttrExpectedLiteralOrDelimiter.into_diag(dcx, level)
}
&AttributeLintKind::MissingOptionsForOnMove => {
lints::MissingOptionsForOnMoveAttr.into_diag(dcx, level)
}
}
}
}