blob: b8aac6c15d9487d5098a3c51c4a5da67f85beac3 [file] [log] [blame]
#![allow(rustc::diagnostic_outside_of_impl)]
#![allow(rustc::untranslatable_diagnostic)]
use rustc_ast::util::unicode::TEXT_FLOW_CONTROL_CHARS;
use rustc_errors::{elided_lifetime_in_path_suggestion, Diag};
use rustc_errors::{Applicability, SuggestionStyle};
use rustc_middle::middle::stability;
use rustc_session::lint::BuiltinLintDiag;
use rustc_session::Session;
use rustc_span::BytePos;
mod check_cfg;
pub(super) fn builtin(sess: &Session, diagnostic: BuiltinLintDiag, diag: &mut Diag<'_, ()>) {
match diagnostic {
BuiltinLintDiag::UnicodeTextFlow(span, content) => {
let spans: Vec<_> = content
.char_indices()
.filter_map(|(i, c)| {
TEXT_FLOW_CONTROL_CHARS.contains(&c).then(|| {
let lo = span.lo() + BytePos(2 + i as u32);
(c, span.with_lo(lo).with_hi(lo + BytePos(c.len_utf8() as u32)))
})
})
.collect();
let (an, s) = match spans.len() {
1 => ("an ", ""),
_ => ("", "s"),
};
diag.span_label(
span,
format!(
"this comment contains {an}invisible unicode text flow control codepoint{s}",
),
);
for (c, span) in &spans {
diag.span_label(*span, format!("{c:?}"));
}
diag.note(
"these kind of unicode codepoints change the way text flows on \
applications that support them, but can cause confusion because they \
change the order of characters on the screen",
);
if !spans.is_empty() {
diag.multipart_suggestion_with_style(
"if their presence wasn't intentional, you can remove them",
spans.into_iter().map(|(_, span)| (span, "".to_string())).collect(),
Applicability::MachineApplicable,
SuggestionStyle::HideCodeAlways,
);
}
}
BuiltinLintDiag::Normal => (),
BuiltinLintDiag::AbsPathWithModule(span) => {
let (sugg, app) = match sess.source_map().span_to_snippet(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),
};
diag.span_suggestion(span, "use `crate`", sugg, app);
}
BuiltinLintDiag::ProcMacroDeriveResolutionFallback(span) => {
diag.span_label(
span,
"names from parent modules are not accessible without an explicit import",
);
}
BuiltinLintDiag::MacroExpandedMacroExportsAccessedByAbsolutePaths(span_def) => {
diag.span_note(span_def, "the macro is defined here");
}
BuiltinLintDiag::ElidedLifetimesInPaths(n, path_span, incl_angl_brckt, insertion_span) => {
diag.subdiagnostic(
sess.dcx(),
elided_lifetime_in_path_suggestion(
sess.source_map(),
n,
path_span,
incl_angl_brckt,
insertion_span,
),
);
}
BuiltinLintDiag::UnknownCrateTypes(span, note, sugg) => {
diag.span_suggestion(span, note, sugg, Applicability::MaybeIncorrect);
}
BuiltinLintDiag::UnusedImports(message, replaces, in_test_module) => {
if !replaces.is_empty() {
diag.tool_only_multipart_suggestion(
message,
replaces,
Applicability::MachineApplicable,
);
}
if let Some(span) = in_test_module {
diag.span_help(
sess.source_map().guess_head_span(span),
"if this is a test module, consider adding a `#[cfg(test)]` to the containing module",
);
}
}
BuiltinLintDiag::RedundantImport(spans, ident) => {
for (span, is_imported) in spans {
let introduced = if is_imported { "imported" } else { "defined" };
let span_msg = if span.is_dummy() { "by the extern prelude" } else { "here" };
diag.span_label(
span,
format!("the item `{ident}` is already {introduced} {span_msg}"),
);
}
}
BuiltinLintDiag::DeprecatedMacro(suggestion, span) => {
stability::deprecation_suggestion(diag, "macro", suggestion, span)
}
BuiltinLintDiag::UnusedDocComment(span) => {
diag.span_label(span, "rustdoc does not generate documentation for macro invocations");
diag.help("to document an item produced by a macro, \
the macro must produce the documentation as part of its expansion");
}
BuiltinLintDiag::PatternsInFnsWithoutBody(span, ident) => {
diag.span_suggestion(
span,
"remove `mut` from the parameter",
ident,
Applicability::MachineApplicable,
);
}
BuiltinLintDiag::MissingAbi(span, default_abi) => {
diag.span_label(span, "ABI should be specified here");
diag.help(format!("the default ABI is {}", default_abi.name()));
}
BuiltinLintDiag::LegacyDeriveHelpers(span) => {
diag.span_label(span, "the attribute is introduced here");
}
BuiltinLintDiag::ProcMacroBackCompat(note) => {
diag.note(note);
}
BuiltinLintDiag::OrPatternsBackCompat(span, suggestion) => {
diag.span_suggestion(
span,
"use pat_param to preserve semantics",
suggestion,
Applicability::MachineApplicable,
);
}
BuiltinLintDiag::ReservedPrefix(span) => {
diag.span_label(span, "unknown prefix");
diag.span_suggestion_verbose(
span.shrink_to_hi(),
"insert whitespace here to avoid this being parsed as a prefix in Rust 2021",
" ",
Applicability::MachineApplicable,
);
}
BuiltinLintDiag::UnusedBuiltinAttribute { attr_name, macro_name, invoc_span } => {
diag.span_note(
invoc_span,
format!("the built-in attribute `{attr_name}` will be ignored, since it's applied to the macro invocation `{macro_name}`")
);
}
BuiltinLintDiag::TrailingMacro(is_trailing, name) => {
if is_trailing {
diag.note("macro invocations at the end of a block are treated as expressions");
diag.note(format!("to ignore the value produced by the macro, add a semicolon after the invocation of `{name}`"));
}
}
BuiltinLintDiag::BreakWithLabelAndLoop(span) => {
diag.multipart_suggestion(
"wrap this expression in parentheses",
vec![
(span.shrink_to_lo(), "(".to_string()),
(span.shrink_to_hi(), ")".to_string()),
],
Applicability::MachineApplicable,
);
}
BuiltinLintDiag::UnexpectedCfgName(name, value) => {
check_cfg::unexpected_cfg_name(sess, diag, name, value)
}
BuiltinLintDiag::UnexpectedCfgValue(name, value) => {
check_cfg::unexpected_cfg_value(sess, diag, name, value)
}
BuiltinLintDiag::DeprecatedWhereclauseLocation(sugg) => {
let left_sp = diag.span.primary_span().unwrap();
match sugg {
Some((right_sp, sugg)) => diag.multipart_suggestion(
"move it to the end of the type declaration",
vec![(left_sp, String::new()), (right_sp, sugg)],
Applicability::MachineApplicable,
),
None => diag.span_suggestion(
left_sp,
"remove this `where`",
"",
Applicability::MachineApplicable,
),
};
diag.note("see issue #89122 <https://github.com/rust-lang/rust/issues/89122> for more information");
}
BuiltinLintDiag::SingleUseLifetime {
param_span,
use_span: Some((use_span, elide)),
deletion_span,
} => {
debug!(?param_span, ?use_span, ?deletion_span);
diag.span_label(param_span, "this lifetime...");
diag.span_label(use_span, "...is used only here");
if let Some(deletion_span) = deletion_span {
let msg = "elide the single-use lifetime";
let (use_span, replace_lt) = if elide {
let use_span = 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 suggestions = if deletion_span.is_empty() {
vec![(use_span, replace_lt)]
} else {
vec![(deletion_span, String::new()), (use_span, replace_lt)]
};
diag.multipart_suggestion(msg, suggestions, Applicability::MachineApplicable);
}
}
BuiltinLintDiag::SingleUseLifetime { param_span: _, use_span: None, deletion_span } => {
debug!(?deletion_span);
if let Some(deletion_span) = deletion_span {
diag.span_suggestion(
deletion_span,
"elide the unused lifetime",
"",
Applicability::MachineApplicable,
);
}
}
BuiltinLintDiag::NamedArgumentUsedPositionally {
position_sp_to_replace,
position_sp_for_msg,
named_arg_sp,
named_arg_name,
is_formatting_arg,
} => {
diag.span_label(
named_arg_sp,
"this named argument is referred to by position in formatting string",
);
if let Some(positional_arg_for_msg) = position_sp_for_msg {
let msg = format!(
"this formatting argument uses named argument `{named_arg_name}` by position"
);
diag.span_label(positional_arg_for_msg, msg);
}
if let Some(positional_arg_to_replace) = position_sp_to_replace {
let name = if is_formatting_arg { named_arg_name + "$" } else { named_arg_name };
let span_to_replace = if let Ok(positional_arg_content) =
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
};
diag.span_suggestion_verbose(
span_to_replace,
"use the named argument by name to avoid ambiguity",
name,
Applicability::MaybeIncorrect,
);
}
}
BuiltinLintDiag::ByteSliceInPackedStructWithDerive => {
diag.help("consider implementing the trait by hand, or remove the `packed` attribute");
}
BuiltinLintDiag::UnusedExternCrate { removal_span } => {
diag.span_suggestion(removal_span, "remove it", "", Applicability::MachineApplicable);
}
BuiltinLintDiag::ExternCrateNotIdiomatic { vis_span, ident_span } => {
let suggestion_span = vis_span.between(ident_span);
diag.span_suggestion_verbose(
suggestion_span,
"convert it to a `use`",
if vis_span.is_empty() { "use " } else { " use " },
Applicability::MachineApplicable,
);
}
BuiltinLintDiag::AmbiguousGlobImports { diag: ambiguity } => {
rustc_errors::report_ambiguity_error(diag, ambiguity);
}
BuiltinLintDiag::AmbiguousGlobReexports {
name,
namespace,
first_reexport_span,
duplicate_reexport_span,
} => {
diag.span_label(
first_reexport_span,
format!("the name `{name}` in the {namespace} namespace is first re-exported here"),
);
diag.span_label(
duplicate_reexport_span,
format!(
"but the name `{name}` in the {namespace} namespace is also re-exported here"
),
);
}
BuiltinLintDiag::HiddenGlobReexports {
name,
namespace,
glob_reexport_span,
private_item_span,
} => {
diag.span_note(glob_reexport_span, format!("the name `{name}` in the {namespace} namespace is supposed to be publicly re-exported here"));
diag.span_note(private_item_span, "but the private item here shadows it".to_owned());
}
BuiltinLintDiag::UnusedQualifications { removal_span } => {
diag.span_suggestion_verbose(
removal_span,
"remove the unnecessary path segments",
"",
Applicability::MachineApplicable,
);
}
BuiltinLintDiag::AssociatedConstElidedLifetime { elided, span } => {
diag.span_suggestion_verbose(
if elided { span.shrink_to_hi() } else { span },
"use the `'static` lifetime",
if elided { "'static " } else { "'static" },
Applicability::MachineApplicable,
);
}
BuiltinLintDiag::RedundantImportVisibility { max_vis, span } => {
diag.span_note(span, format!("the most public imported item is `{max_vis}`"));
diag.help(
"reduce the glob import's visibility or increase visibility of imported items",
);
}
BuiltinLintDiag::MaybeTypo { span, name } => {
diag.span_suggestion_verbose(
span,
"an attribute with a similar name exists",
name,
Applicability::MachineApplicable,
);
}
}
}