mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-29 11:37:39 +00:00
Auto merge of #119443 - Urgau:move-around-builtin-diag-context, r=Nilstrieb
Move around the code responsible for decorating builtin diagnostics This PR move the code responsible for decorating builtin diagnostics into a separate sub-module for ease of use and readability. While my original intention was to also move the check-cfg unexpected logic in their own function I changed my mind after moving the match altogether. I can move those if desired. Fixes https://github.com/rust-lang/rust/pull/119425#discussion_r1438446596 r? `@Nilstrieb`
This commit is contained in:
commit
2a3e63551f
@ -18,34 +18,32 @@ use self::TargetLint::*;
|
|||||||
|
|
||||||
use crate::levels::LintLevelsBuilder;
|
use crate::levels::LintLevelsBuilder;
|
||||||
use crate::passes::{EarlyLintPassObject, LateLintPassObject};
|
use crate::passes::{EarlyLintPassObject, LateLintPassObject};
|
||||||
use rustc_ast::util::unicode::TEXT_FLOW_CONTROL_CHARS;
|
|
||||||
use rustc_data_structures::fx::FxHashMap;
|
use rustc_data_structures::fx::FxHashMap;
|
||||||
use rustc_data_structures::sync;
|
use rustc_data_structures::sync;
|
||||||
use rustc_errors::{add_elided_lifetime_in_path_suggestion, DiagnosticBuilder, DiagnosticMessage};
|
use rustc_errors::{DecorateLint, DiagnosticBuilder, DiagnosticMessage, MultiSpan};
|
||||||
use rustc_errors::{Applicability, DecorateLint, MultiSpan, SuggestionStyle};
|
|
||||||
use rustc_feature::Features;
|
use rustc_feature::Features;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def::Res;
|
use rustc_hir::def::Res;
|
||||||
use rustc_hir::def_id::{CrateNum, DefId};
|
use rustc_hir::def_id::{CrateNum, DefId};
|
||||||
use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData};
|
use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData};
|
||||||
use rustc_middle::middle::privacy::EffectiveVisibilities;
|
use rustc_middle::middle::privacy::EffectiveVisibilities;
|
||||||
use rustc_middle::middle::stability;
|
|
||||||
use rustc_middle::ty::layout::{LayoutError, LayoutOfHelpers, TyAndLayout};
|
use rustc_middle::ty::layout::{LayoutError, LayoutOfHelpers, TyAndLayout};
|
||||||
use rustc_middle::ty::print::{with_no_trimmed_paths, PrintError};
|
use rustc_middle::ty::print::{with_no_trimmed_paths, PrintError};
|
||||||
use rustc_middle::ty::{self, print::Printer, GenericArg, RegisteredTools, Ty, TyCtxt};
|
use rustc_middle::ty::{self, print::Printer, GenericArg, RegisteredTools, Ty, TyCtxt};
|
||||||
use rustc_session::config::ExpectedValues;
|
|
||||||
use rustc_session::lint::{BuiltinLintDiagnostics, LintExpectationId};
|
use rustc_session::lint::{BuiltinLintDiagnostics, LintExpectationId};
|
||||||
use rustc_session::lint::{FutureIncompatibleInfo, Level, Lint, LintBuffer, LintId};
|
use rustc_session::lint::{FutureIncompatibleInfo, Level, Lint, LintBuffer, LintId};
|
||||||
use rustc_session::{LintStoreMarker, Session};
|
use rustc_session::{LintStoreMarker, Session};
|
||||||
use rustc_span::edit_distance::find_best_match_for_name;
|
use rustc_span::edit_distance::find_best_match_for_name;
|
||||||
use rustc_span::symbol::{sym, Ident, Symbol};
|
use rustc_span::symbol::{sym, Ident, Symbol};
|
||||||
use rustc_span::{BytePos, Span};
|
use rustc_span::Span;
|
||||||
use rustc_target::abi;
|
use rustc_target::abi;
|
||||||
|
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::iter;
|
use std::iter;
|
||||||
use std::slice;
|
use std::slice;
|
||||||
|
|
||||||
|
mod diagnostics;
|
||||||
|
|
||||||
type EarlyLintPassFactory = dyn Fn() -> EarlyLintPassObject + sync::DynSend + sync::DynSync;
|
type EarlyLintPassFactory = dyn Fn() -> EarlyLintPassObject + sync::DynSend + sync::DynSync;
|
||||||
type LateLintPassFactory =
|
type LateLintPassFactory =
|
||||||
dyn for<'tcx> Fn(TyCtxt<'tcx>) -> LateLintPassObject<'tcx> + sync::DynSend + sync::DynSync;
|
dyn for<'tcx> Fn(TyCtxt<'tcx>) -> LateLintPassObject<'tcx> + sync::DynSend + sync::DynSync;
|
||||||
@ -531,445 +529,9 @@ pub trait LintContext {
|
|||||||
diagnostic: BuiltinLintDiagnostics,
|
diagnostic: BuiltinLintDiagnostics,
|
||||||
) {
|
) {
|
||||||
// We first generate a blank diagnostic.
|
// We first generate a blank diagnostic.
|
||||||
self.lookup(lint, span, msg,|db| {
|
self.lookup(lint, span, msg, |db| {
|
||||||
// Now, set up surrounding context.
|
// Now, set up surrounding context.
|
||||||
let sess = self.sess();
|
diagnostics::builtin(self.sess(), diagnostic, db);
|
||||||
match diagnostic {
|
|
||||||
BuiltinLintDiagnostics::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"),
|
|
||||||
};
|
|
||||||
db.span_label(span, format!(
|
|
||||||
"this comment contains {an}invisible unicode text flow control codepoint{s}",
|
|
||||||
));
|
|
||||||
for (c, span) in &spans {
|
|
||||||
db.span_label(*span, format!("{c:?}"));
|
|
||||||
}
|
|
||||||
db.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() {
|
|
||||||
db.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,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
BuiltinLintDiagnostics::Normal => (),
|
|
||||||
BuiltinLintDiagnostics::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),
|
|
||||||
};
|
|
||||||
db.span_suggestion(span, "use `crate`", sugg, app);
|
|
||||||
}
|
|
||||||
BuiltinLintDiagnostics::ProcMacroDeriveResolutionFallback(span) => {
|
|
||||||
db.span_label(
|
|
||||||
span,
|
|
||||||
"names from parent modules are not accessible without an explicit import",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
BuiltinLintDiagnostics::MacroExpandedMacroExportsAccessedByAbsolutePaths(
|
|
||||||
span_def,
|
|
||||||
) => {
|
|
||||||
db.span_note(span_def, "the macro is defined here");
|
|
||||||
}
|
|
||||||
BuiltinLintDiagnostics::ElidedLifetimesInPaths(
|
|
||||||
n,
|
|
||||||
path_span,
|
|
||||||
incl_angl_brckt,
|
|
||||||
insertion_span,
|
|
||||||
) => {
|
|
||||||
add_elided_lifetime_in_path_suggestion(
|
|
||||||
sess.source_map(),
|
|
||||||
db,
|
|
||||||
n,
|
|
||||||
path_span,
|
|
||||||
incl_angl_brckt,
|
|
||||||
insertion_span,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
BuiltinLintDiagnostics::UnknownCrateTypes(span, note, sugg) => {
|
|
||||||
db.span_suggestion(span, note, sugg, Applicability::MaybeIncorrect);
|
|
||||||
}
|
|
||||||
BuiltinLintDiagnostics::UnusedImports(message, replaces, in_test_module) => {
|
|
||||||
if !replaces.is_empty() {
|
|
||||||
db.tool_only_multipart_suggestion(
|
|
||||||
message,
|
|
||||||
replaces,
|
|
||||||
Applicability::MachineApplicable,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(span) = in_test_module {
|
|
||||||
db.span_help(
|
|
||||||
self.sess().source_map().guess_head_span(span),
|
|
||||||
"consider adding a `#[cfg(test)]` to the containing module",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BuiltinLintDiagnostics::RedundantImport(spans, ident) => {
|
|
||||||
for (span, is_imported) in spans {
|
|
||||||
let introduced = if is_imported { "imported" } else { "defined" };
|
|
||||||
db.span_label(
|
|
||||||
span,
|
|
||||||
format!("the item `{ident}` is already {introduced} here"),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BuiltinLintDiagnostics::DeprecatedMacro(suggestion, span) => {
|
|
||||||
stability::deprecation_suggestion(db, "macro", suggestion, span)
|
|
||||||
}
|
|
||||||
BuiltinLintDiagnostics::UnusedDocComment(span) => {
|
|
||||||
db.span_label(span, "rustdoc does not generate documentation for macro invocations");
|
|
||||||
db.help("to document an item produced by a macro, \
|
|
||||||
the macro must produce the documentation as part of its expansion");
|
|
||||||
}
|
|
||||||
BuiltinLintDiagnostics::PatternsInFnsWithoutBody(span, ident) => {
|
|
||||||
db.span_suggestion(span, "remove `mut` from the parameter", ident, Applicability::MachineApplicable);
|
|
||||||
}
|
|
||||||
BuiltinLintDiagnostics::MissingAbi(span, default_abi) => {
|
|
||||||
db.span_label(span, "ABI should be specified here");
|
|
||||||
db.help(format!("the default ABI is {}", default_abi.name()));
|
|
||||||
}
|
|
||||||
BuiltinLintDiagnostics::LegacyDeriveHelpers(span) => {
|
|
||||||
db.span_label(span, "the attribute is introduced here");
|
|
||||||
}
|
|
||||||
BuiltinLintDiagnostics::ProcMacroBackCompat(note) => {
|
|
||||||
db.note(note);
|
|
||||||
}
|
|
||||||
BuiltinLintDiagnostics::OrPatternsBackCompat(span,suggestion) => {
|
|
||||||
db.span_suggestion(span, "use pat_param to preserve semantics", suggestion, Applicability::MachineApplicable);
|
|
||||||
}
|
|
||||||
BuiltinLintDiagnostics::ReservedPrefix(span) => {
|
|
||||||
db.span_label(span, "unknown prefix");
|
|
||||||
db.span_suggestion_verbose(
|
|
||||||
span.shrink_to_hi(),
|
|
||||||
"insert whitespace here to avoid this being parsed as a prefix in Rust 2021",
|
|
||||||
" ",
|
|
||||||
Applicability::MachineApplicable,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
BuiltinLintDiagnostics::UnusedBuiltinAttribute {
|
|
||||||
attr_name,
|
|
||||||
macro_name,
|
|
||||||
invoc_span
|
|
||||||
} => {
|
|
||||||
db.span_note(
|
|
||||||
invoc_span,
|
|
||||||
format!("the built-in attribute `{attr_name}` will be ignored, since it's applied to the macro invocation `{macro_name}`")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
BuiltinLintDiagnostics::TrailingMacro(is_trailing, name) => {
|
|
||||||
if is_trailing {
|
|
||||||
db.note("macro invocations at the end of a block are treated as expressions");
|
|
||||||
db.note(format!("to ignore the value produced by the macro, add a semicolon after the invocation of `{name}`"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BuiltinLintDiagnostics::BreakWithLabelAndLoop(span) => {
|
|
||||||
db.multipart_suggestion(
|
|
||||||
"wrap this expression in parentheses",
|
|
||||||
vec![(span.shrink_to_lo(), "(".to_string()),
|
|
||||||
(span.shrink_to_hi(), ")".to_string())],
|
|
||||||
Applicability::MachineApplicable
|
|
||||||
);
|
|
||||||
}
|
|
||||||
BuiltinLintDiagnostics::NamedAsmLabel(help) => {
|
|
||||||
db.help(help);
|
|
||||||
db.note("see the asm section of Rust By Example <https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html#labels> for more information");
|
|
||||||
},
|
|
||||||
BuiltinLintDiagnostics::UnexpectedCfgName((name, name_span), value) => {
|
|
||||||
let possibilities: Vec<Symbol> = sess.parse_sess.check_config.expecteds.keys().copied().collect();
|
|
||||||
let is_from_cargo = std::env::var_os("CARGO").is_some();
|
|
||||||
let mut is_feature_cfg = name == sym::feature;
|
|
||||||
|
|
||||||
if is_feature_cfg && is_from_cargo {
|
|
||||||
db.help("consider defining some features in `Cargo.toml`");
|
|
||||||
// Suggest the most probable if we found one
|
|
||||||
} else if let Some(best_match) = find_best_match_for_name(&possibilities, name, None) {
|
|
||||||
if let Some(ExpectedValues::Some(best_match_values)) =
|
|
||||||
sess.parse_sess.check_config.expecteds.get(&best_match) {
|
|
||||||
let mut possibilities = best_match_values.iter()
|
|
||||||
.flatten()
|
|
||||||
.map(Symbol::as_str)
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
possibilities.sort();
|
|
||||||
|
|
||||||
let mut should_print_possibilities = true;
|
|
||||||
if let Some((value, value_span)) = value {
|
|
||||||
if best_match_values.contains(&Some(value)) {
|
|
||||||
db.span_suggestion(name_span, "there is a config with a similar name and value", best_match, Applicability::MaybeIncorrect);
|
|
||||||
should_print_possibilities = false;
|
|
||||||
} else if best_match_values.contains(&None) {
|
|
||||||
db.span_suggestion(name_span.to(value_span), "there is a config with a similar name and no value", best_match, Applicability::MaybeIncorrect);
|
|
||||||
should_print_possibilities = false;
|
|
||||||
} else if let Some(first_value) = possibilities.first() {
|
|
||||||
db.span_suggestion(name_span.to(value_span), "there is a config with a similar name and different values", format!("{best_match} = \"{first_value}\""), Applicability::MaybeIncorrect);
|
|
||||||
} else {
|
|
||||||
db.span_suggestion(name_span.to(value_span), "there is a config with a similar name and different values", best_match, Applicability::MaybeIncorrect);
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
db.span_suggestion(name_span, "there is a config with a similar name", best_match, Applicability::MaybeIncorrect);
|
|
||||||
}
|
|
||||||
|
|
||||||
if !possibilities.is_empty() && should_print_possibilities {
|
|
||||||
let possibilities = possibilities.join("`, `");
|
|
||||||
db.help(format!("expected values for `{best_match}` are: `{possibilities}`"));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
db.span_suggestion(name_span, "there is a config with a similar name", best_match, Applicability::MaybeIncorrect);
|
|
||||||
}
|
|
||||||
|
|
||||||
is_feature_cfg |= best_match == sym::feature;
|
|
||||||
} else if !possibilities.is_empty() {
|
|
||||||
let mut possibilities = possibilities.iter()
|
|
||||||
.map(Symbol::as_str)
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
possibilities.sort();
|
|
||||||
let possibilities = possibilities.join("`, `");
|
|
||||||
|
|
||||||
// The list of expected names can be long (even by default) and
|
|
||||||
// so the diagnostic produced can take a lot of space. To avoid
|
|
||||||
// cloging the user output we only want to print that diagnostic
|
|
||||||
// once.
|
|
||||||
db.help_once(format!("expected names are: `{possibilities}`"));
|
|
||||||
}
|
|
||||||
|
|
||||||
let inst = if let Some((value, _value_span)) = value {
|
|
||||||
let pre = if is_from_cargo { "\\" } else { "" };
|
|
||||||
format!("cfg({name}, values({pre}\"{value}{pre}\"))")
|
|
||||||
} else {
|
|
||||||
format!("cfg({name})")
|
|
||||||
};
|
|
||||||
|
|
||||||
if is_from_cargo {
|
|
||||||
if !is_feature_cfg {
|
|
||||||
db.help(format!("consider using a Cargo feature instead or adding `println!(\"cargo:rustc-check-cfg={inst}\");` to the top of a `build.rs`"));
|
|
||||||
}
|
|
||||||
db.note("see <https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#check-cfg> for more information about checking conditional configuration");
|
|
||||||
} else {
|
|
||||||
db.help(format!("to expect this configuration use `--check-cfg={inst}`"));
|
|
||||||
db.note("see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
BuiltinLintDiagnostics::UnexpectedCfgValue((name, name_span), value) => {
|
|
||||||
let Some(ExpectedValues::Some(values)) = &sess.parse_sess.check_config.expecteds.get(&name) else {
|
|
||||||
bug!("it shouldn't be possible to have a diagnostic on a value whose name is not in values");
|
|
||||||
};
|
|
||||||
let mut have_none_possibility = false;
|
|
||||||
let possibilities: Vec<Symbol> = values.iter()
|
|
||||||
.inspect(|a| have_none_possibility |= a.is_none())
|
|
||||||
.copied()
|
|
||||||
.flatten()
|
|
||||||
.collect();
|
|
||||||
let is_from_cargo = std::env::var_os("CARGO").is_some();
|
|
||||||
|
|
||||||
// Show the full list if all possible values for a given name, but don't do it
|
|
||||||
// for names as the possibilities could be very long
|
|
||||||
if !possibilities.is_empty() {
|
|
||||||
{
|
|
||||||
let mut possibilities = possibilities.iter().map(Symbol::as_str).collect::<Vec<_>>();
|
|
||||||
possibilities.sort();
|
|
||||||
|
|
||||||
let possibilities = possibilities.join("`, `");
|
|
||||||
let none = if have_none_possibility { "(none), " } else { "" };
|
|
||||||
|
|
||||||
db.note(format!("expected values for `{name}` are: {none}`{possibilities}`"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some((value, value_span)) = value {
|
|
||||||
// Suggest the most probable if we found one
|
|
||||||
if let Some(best_match) = find_best_match_for_name(&possibilities, value, None) {
|
|
||||||
db.span_suggestion(value_span, "there is a expected value with a similar name", format!("\"{best_match}\""), Applicability::MaybeIncorrect);
|
|
||||||
|
|
||||||
}
|
|
||||||
} else if let &[first_possibility] = &possibilities[..] {
|
|
||||||
db.span_suggestion(name_span.shrink_to_hi(), "specify a config value", format!(" = \"{first_possibility}\""), Applicability::MaybeIncorrect);
|
|
||||||
}
|
|
||||||
} else if have_none_possibility {
|
|
||||||
db.note(format!("no expected value for `{name}`"));
|
|
||||||
if let Some((_value, value_span)) = value {
|
|
||||||
db.span_suggestion(name_span.shrink_to_hi().to(value_span), "remove the value", "", Applicability::MaybeIncorrect);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let inst = if let Some((value, _value_span)) = value {
|
|
||||||
let pre = if is_from_cargo { "\\" } else { "" };
|
|
||||||
format!("cfg({name}, values({pre}\"{value}{pre}\"))")
|
|
||||||
} else {
|
|
||||||
format!("cfg({name})")
|
|
||||||
};
|
|
||||||
|
|
||||||
if is_from_cargo {
|
|
||||||
if name == sym::feature {
|
|
||||||
if let Some((value, _value_span)) = value {
|
|
||||||
db.help(format!("consider adding `{value}` as a feature in `Cargo.toml`"));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
db.help(format!("consider using a Cargo feature instead or adding `println!(\"cargo:rustc-check-cfg={inst}\");` to the top of a `build.rs`"));
|
|
||||||
}
|
|
||||||
db.note("see <https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#check-cfg> for more information about checking conditional configuration");
|
|
||||||
} else {
|
|
||||||
db.help(format!("to expect this configuration use `--check-cfg={inst}`"));
|
|
||||||
db.note("see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
BuiltinLintDiagnostics::DeprecatedWhereclauseLocation(new_span, suggestion) => {
|
|
||||||
db.multipart_suggestion(
|
|
||||||
"move it to the end of the type declaration",
|
|
||||||
vec![(db.span.primary_span().unwrap(), "".to_string()), (new_span, suggestion)],
|
|
||||||
Applicability::MachineApplicable,
|
|
||||||
);
|
|
||||||
db.note(
|
|
||||||
"see issue #89122 <https://github.com/rust-lang/rust/issues/89122> for more information",
|
|
||||||
);
|
|
||||||
},
|
|
||||||
BuiltinLintDiagnostics::SingleUseLifetime {
|
|
||||||
param_span,
|
|
||||||
use_span: Some((use_span, elide)),
|
|
||||||
deletion_span,
|
|
||||||
} => {
|
|
||||||
debug!(?param_span, ?use_span, ?deletion_span);
|
|
||||||
db.span_label(param_span, "this lifetime...");
|
|
||||||
db.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(
|
|
||||||
use_span,
|
|
||||||
char::is_whitespace,
|
|
||||||
).unwrap_or(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)]
|
|
||||||
};
|
|
||||||
db.multipart_suggestion(
|
|
||||||
msg,
|
|
||||||
suggestions,
|
|
||||||
Applicability::MachineApplicable,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
BuiltinLintDiagnostics::SingleUseLifetime {
|
|
||||||
param_span: _,
|
|
||||||
use_span: None,
|
|
||||||
deletion_span,
|
|
||||||
} => {
|
|
||||||
debug!(?deletion_span);
|
|
||||||
if let Some(deletion_span) = deletion_span {
|
|
||||||
db.span_suggestion(
|
|
||||||
deletion_span,
|
|
||||||
"elide the unused lifetime",
|
|
||||||
"",
|
|
||||||
Applicability::MachineApplicable,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
BuiltinLintDiagnostics::NamedArgumentUsedPositionally{ position_sp_to_replace, position_sp_for_msg, named_arg_sp, named_arg_name, is_formatting_arg} => {
|
|
||||||
db.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");
|
|
||||||
db.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) =
|
|
||||||
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
|
|
||||||
};
|
|
||||||
db.span_suggestion_verbose(
|
|
||||||
span_to_replace,
|
|
||||||
"use the named argument by name to avoid ambiguity",
|
|
||||||
name,
|
|
||||||
Applicability::MaybeIncorrect,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BuiltinLintDiagnostics::ByteSliceInPackedStructWithDerive => {
|
|
||||||
db.help("consider implementing the trait by hand, or remove the `packed` attribute");
|
|
||||||
}
|
|
||||||
BuiltinLintDiagnostics::UnusedExternCrate { removal_span }=> {
|
|
||||||
db.span_suggestion(
|
|
||||||
removal_span,
|
|
||||||
"remove it",
|
|
||||||
"",
|
|
||||||
Applicability::MachineApplicable,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
BuiltinLintDiagnostics::ExternCrateNotIdiomatic { vis_span, ident_span }=> {
|
|
||||||
let suggestion_span = vis_span.between(ident_span);
|
|
||||||
db.span_suggestion_verbose(
|
|
||||||
suggestion_span,
|
|
||||||
"convert it to a `use`",
|
|
||||||
if vis_span.is_empty() { "use " } else { " use " },
|
|
||||||
Applicability::MachineApplicable,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
BuiltinLintDiagnostics::AmbiguousGlobImports { diag } => {
|
|
||||||
rustc_errors::report_ambiguity_error(db, diag);
|
|
||||||
}
|
|
||||||
BuiltinLintDiagnostics::AmbiguousGlobReexports { name, namespace, first_reexport_span, duplicate_reexport_span } => {
|
|
||||||
db.span_label(first_reexport_span, format!("the name `{name}` in the {namespace} namespace is first re-exported here"));
|
|
||||||
db.span_label(duplicate_reexport_span, format!("but the name `{name}` in the {namespace} namespace is also re-exported here"));
|
|
||||||
}
|
|
||||||
BuiltinLintDiagnostics::HiddenGlobReexports { name, namespace, glob_reexport_span, private_item_span } => {
|
|
||||||
db.span_note(glob_reexport_span, format!("the name `{name}` in the {namespace} namespace is supposed to be publicly re-exported here"));
|
|
||||||
db.span_note(private_item_span, "but the private item here shadows it".to_owned());
|
|
||||||
}
|
|
||||||
BuiltinLintDiagnostics::UnusedQualifications { removal_span } => {
|
|
||||||
db.span_suggestion_verbose(
|
|
||||||
removal_span,
|
|
||||||
"remove the unnecessary path segments",
|
|
||||||
"",
|
|
||||||
Applicability::MachineApplicable
|
|
||||||
);
|
|
||||||
}
|
|
||||||
BuiltinLintDiagnostics::AssociatedConstElidedLifetime { elided, span } => {
|
|
||||||
db.span_suggestion_verbose(
|
|
||||||
if elided { span.shrink_to_hi() } else { span },
|
|
||||||
"use the `'static` lifetime",
|
|
||||||
if elided { "'static " } else { "'static" },
|
|
||||||
Applicability::MachineApplicable
|
|
||||||
);
|
|
||||||
},
|
|
||||||
BuiltinLintDiagnostics::RedundantImportVisibility { max_vis, span } => {
|
|
||||||
db.span_note(span, format!("the most public imported item is `{max_vis}`"));
|
|
||||||
db.help("reduce the glob import's visibility or increase visibility of imported items");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Rewrap `db`, and pass control to the user.
|
// Rewrap `db`, and pass control to the user.
|
||||||
decorate(db)
|
decorate(db)
|
||||||
});
|
});
|
||||||
|
532
compiler/rustc_lint/src/context/diagnostics.rs
Normal file
532
compiler/rustc_lint/src/context/diagnostics.rs
Normal file
@ -0,0 +1,532 @@
|
|||||||
|
use rustc_ast::util::unicode::TEXT_FLOW_CONTROL_CHARS;
|
||||||
|
use rustc_errors::{add_elided_lifetime_in_path_suggestion, DiagnosticBuilder};
|
||||||
|
use rustc_errors::{Applicability, SuggestionStyle};
|
||||||
|
use rustc_middle::middle::stability;
|
||||||
|
use rustc_session::config::ExpectedValues;
|
||||||
|
use rustc_session::lint::BuiltinLintDiagnostics;
|
||||||
|
use rustc_session::Session;
|
||||||
|
use rustc_span::edit_distance::find_best_match_for_name;
|
||||||
|
use rustc_span::symbol::{sym, Symbol};
|
||||||
|
use rustc_span::BytePos;
|
||||||
|
|
||||||
|
pub(super) fn builtin(
|
||||||
|
sess: &Session,
|
||||||
|
diagnostic: BuiltinLintDiagnostics,
|
||||||
|
db: &mut DiagnosticBuilder<'_, ()>,
|
||||||
|
) {
|
||||||
|
match diagnostic {
|
||||||
|
BuiltinLintDiagnostics::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"),
|
||||||
|
};
|
||||||
|
db.span_label(
|
||||||
|
span,
|
||||||
|
format!(
|
||||||
|
"this comment contains {an}invisible unicode text flow control codepoint{s}",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
for (c, span) in &spans {
|
||||||
|
db.span_label(*span, format!("{c:?}"));
|
||||||
|
}
|
||||||
|
db.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() {
|
||||||
|
db.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,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BuiltinLintDiagnostics::Normal => (),
|
||||||
|
BuiltinLintDiagnostics::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),
|
||||||
|
};
|
||||||
|
db.span_suggestion(span, "use `crate`", sugg, app);
|
||||||
|
}
|
||||||
|
BuiltinLintDiagnostics::ProcMacroDeriveResolutionFallback(span) => {
|
||||||
|
db.span_label(
|
||||||
|
span,
|
||||||
|
"names from parent modules are not accessible without an explicit import",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
BuiltinLintDiagnostics::MacroExpandedMacroExportsAccessedByAbsolutePaths(span_def) => {
|
||||||
|
db.span_note(span_def, "the macro is defined here");
|
||||||
|
}
|
||||||
|
BuiltinLintDiagnostics::ElidedLifetimesInPaths(
|
||||||
|
n,
|
||||||
|
path_span,
|
||||||
|
incl_angl_brckt,
|
||||||
|
insertion_span,
|
||||||
|
) => {
|
||||||
|
add_elided_lifetime_in_path_suggestion(
|
||||||
|
sess.source_map(),
|
||||||
|
db,
|
||||||
|
n,
|
||||||
|
path_span,
|
||||||
|
incl_angl_brckt,
|
||||||
|
insertion_span,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
BuiltinLintDiagnostics::UnknownCrateTypes(span, note, sugg) => {
|
||||||
|
db.span_suggestion(span, note, sugg, Applicability::MaybeIncorrect);
|
||||||
|
}
|
||||||
|
BuiltinLintDiagnostics::UnusedImports(message, replaces, in_test_module) => {
|
||||||
|
if !replaces.is_empty() {
|
||||||
|
db.tool_only_multipart_suggestion(
|
||||||
|
message,
|
||||||
|
replaces,
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(span) = in_test_module {
|
||||||
|
db.span_help(
|
||||||
|
sess.source_map().guess_head_span(span),
|
||||||
|
"consider adding a `#[cfg(test)]` to the containing module",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BuiltinLintDiagnostics::RedundantImport(spans, ident) => {
|
||||||
|
for (span, is_imported) in spans {
|
||||||
|
let introduced = if is_imported { "imported" } else { "defined" };
|
||||||
|
db.span_label(span, format!("the item `{ident}` is already {introduced} here"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BuiltinLintDiagnostics::DeprecatedMacro(suggestion, span) => {
|
||||||
|
stability::deprecation_suggestion(db, "macro", suggestion, span)
|
||||||
|
}
|
||||||
|
BuiltinLintDiagnostics::UnusedDocComment(span) => {
|
||||||
|
db.span_label(span, "rustdoc does not generate documentation for macro invocations");
|
||||||
|
db.help("to document an item produced by a macro, \
|
||||||
|
the macro must produce the documentation as part of its expansion");
|
||||||
|
}
|
||||||
|
BuiltinLintDiagnostics::PatternsInFnsWithoutBody(span, ident) => {
|
||||||
|
db.span_suggestion(
|
||||||
|
span,
|
||||||
|
"remove `mut` from the parameter",
|
||||||
|
ident,
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
BuiltinLintDiagnostics::MissingAbi(span, default_abi) => {
|
||||||
|
db.span_label(span, "ABI should be specified here");
|
||||||
|
db.help(format!("the default ABI is {}", default_abi.name()));
|
||||||
|
}
|
||||||
|
BuiltinLintDiagnostics::LegacyDeriveHelpers(span) => {
|
||||||
|
db.span_label(span, "the attribute is introduced here");
|
||||||
|
}
|
||||||
|
BuiltinLintDiagnostics::ProcMacroBackCompat(note) => {
|
||||||
|
db.note(note);
|
||||||
|
}
|
||||||
|
BuiltinLintDiagnostics::OrPatternsBackCompat(span, suggestion) => {
|
||||||
|
db.span_suggestion(
|
||||||
|
span,
|
||||||
|
"use pat_param to preserve semantics",
|
||||||
|
suggestion,
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
BuiltinLintDiagnostics::ReservedPrefix(span) => {
|
||||||
|
db.span_label(span, "unknown prefix");
|
||||||
|
db.span_suggestion_verbose(
|
||||||
|
span.shrink_to_hi(),
|
||||||
|
"insert whitespace here to avoid this being parsed as a prefix in Rust 2021",
|
||||||
|
" ",
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
BuiltinLintDiagnostics::UnusedBuiltinAttribute { attr_name, macro_name, invoc_span } => {
|
||||||
|
db.span_note(
|
||||||
|
invoc_span,
|
||||||
|
format!("the built-in attribute `{attr_name}` will be ignored, since it's applied to the macro invocation `{macro_name}`")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
BuiltinLintDiagnostics::TrailingMacro(is_trailing, name) => {
|
||||||
|
if is_trailing {
|
||||||
|
db.note("macro invocations at the end of a block are treated as expressions");
|
||||||
|
db.note(format!("to ignore the value produced by the macro, add a semicolon after the invocation of `{name}`"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BuiltinLintDiagnostics::BreakWithLabelAndLoop(span) => {
|
||||||
|
db.multipart_suggestion(
|
||||||
|
"wrap this expression in parentheses",
|
||||||
|
vec![
|
||||||
|
(span.shrink_to_lo(), "(".to_string()),
|
||||||
|
(span.shrink_to_hi(), ")".to_string()),
|
||||||
|
],
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
BuiltinLintDiagnostics::NamedAsmLabel(help) => {
|
||||||
|
db.help(help);
|
||||||
|
db.note("see the asm section of Rust By Example <https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html#labels> for more information");
|
||||||
|
}
|
||||||
|
BuiltinLintDiagnostics::UnexpectedCfgName((name, name_span), value) => {
|
||||||
|
let possibilities: Vec<Symbol> =
|
||||||
|
sess.parse_sess.check_config.expecteds.keys().copied().collect();
|
||||||
|
let is_from_cargo = std::env::var_os("CARGO").is_some();
|
||||||
|
let mut is_feature_cfg = name == sym::feature;
|
||||||
|
|
||||||
|
if is_feature_cfg && is_from_cargo {
|
||||||
|
db.help("consider defining some features in `Cargo.toml`");
|
||||||
|
// Suggest the most probable if we found one
|
||||||
|
} else if let Some(best_match) = find_best_match_for_name(&possibilities, name, None) {
|
||||||
|
if let Some(ExpectedValues::Some(best_match_values)) =
|
||||||
|
sess.parse_sess.check_config.expecteds.get(&best_match)
|
||||||
|
{
|
||||||
|
let mut possibilities =
|
||||||
|
best_match_values.iter().flatten().map(Symbol::as_str).collect::<Vec<_>>();
|
||||||
|
possibilities.sort();
|
||||||
|
|
||||||
|
let mut should_print_possibilities = true;
|
||||||
|
if let Some((value, value_span)) = value {
|
||||||
|
if best_match_values.contains(&Some(value)) {
|
||||||
|
db.span_suggestion(
|
||||||
|
name_span,
|
||||||
|
"there is a config with a similar name and value",
|
||||||
|
best_match,
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
should_print_possibilities = false;
|
||||||
|
} else if best_match_values.contains(&None) {
|
||||||
|
db.span_suggestion(
|
||||||
|
name_span.to(value_span),
|
||||||
|
"there is a config with a similar name and no value",
|
||||||
|
best_match,
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
should_print_possibilities = false;
|
||||||
|
} else if let Some(first_value) = possibilities.first() {
|
||||||
|
db.span_suggestion(
|
||||||
|
name_span.to(value_span),
|
||||||
|
"there is a config with a similar name and different values",
|
||||||
|
format!("{best_match} = \"{first_value}\""),
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
db.span_suggestion(
|
||||||
|
name_span.to(value_span),
|
||||||
|
"there is a config with a similar name and different values",
|
||||||
|
best_match,
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
db.span_suggestion(
|
||||||
|
name_span,
|
||||||
|
"there is a config with a similar name",
|
||||||
|
best_match,
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !possibilities.is_empty() && should_print_possibilities {
|
||||||
|
let possibilities = possibilities.join("`, `");
|
||||||
|
db.help(format!(
|
||||||
|
"expected values for `{best_match}` are: `{possibilities}`"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
db.span_suggestion(
|
||||||
|
name_span,
|
||||||
|
"there is a config with a similar name",
|
||||||
|
best_match,
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
is_feature_cfg |= best_match == sym::feature;
|
||||||
|
} else if !possibilities.is_empty() {
|
||||||
|
let mut possibilities =
|
||||||
|
possibilities.iter().map(Symbol::as_str).collect::<Vec<_>>();
|
||||||
|
possibilities.sort();
|
||||||
|
let possibilities = possibilities.join("`, `");
|
||||||
|
|
||||||
|
// The list of expected names can be long (even by default) and
|
||||||
|
// so the diagnostic produced can take a lot of space. To avoid
|
||||||
|
// cloging the user output we only want to print that diagnostic
|
||||||
|
// once.
|
||||||
|
db.help_once(format!("expected names are: `{possibilities}`"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let inst = if let Some((value, _value_span)) = value {
|
||||||
|
let pre = if is_from_cargo { "\\" } else { "" };
|
||||||
|
format!("cfg({name}, values({pre}\"{value}{pre}\"))")
|
||||||
|
} else {
|
||||||
|
format!("cfg({name})")
|
||||||
|
};
|
||||||
|
|
||||||
|
if is_from_cargo {
|
||||||
|
if !is_feature_cfg {
|
||||||
|
db.help(format!("consider using a Cargo feature instead or adding `println!(\"cargo:rustc-check-cfg={inst}\");` to the top of a `build.rs`"));
|
||||||
|
}
|
||||||
|
db.note("see <https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#check-cfg> for more information about checking conditional configuration");
|
||||||
|
} else {
|
||||||
|
db.help(format!("to expect this configuration use `--check-cfg={inst}`"));
|
||||||
|
db.note("see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BuiltinLintDiagnostics::UnexpectedCfgValue((name, name_span), value) => {
|
||||||
|
let Some(ExpectedValues::Some(values)) =
|
||||||
|
&sess.parse_sess.check_config.expecteds.get(&name)
|
||||||
|
else {
|
||||||
|
bug!(
|
||||||
|
"it shouldn't be possible to have a diagnostic on a value whose name is not in values"
|
||||||
|
);
|
||||||
|
};
|
||||||
|
let mut have_none_possibility = false;
|
||||||
|
let possibilities: Vec<Symbol> = values
|
||||||
|
.iter()
|
||||||
|
.inspect(|a| have_none_possibility |= a.is_none())
|
||||||
|
.copied()
|
||||||
|
.flatten()
|
||||||
|
.collect();
|
||||||
|
let is_from_cargo = std::env::var_os("CARGO").is_some();
|
||||||
|
|
||||||
|
// Show the full list if all possible values for a given name, but don't do it
|
||||||
|
// for names as the possibilities could be very long
|
||||||
|
if !possibilities.is_empty() {
|
||||||
|
{
|
||||||
|
let mut possibilities =
|
||||||
|
possibilities.iter().map(Symbol::as_str).collect::<Vec<_>>();
|
||||||
|
possibilities.sort();
|
||||||
|
|
||||||
|
let possibilities = possibilities.join("`, `");
|
||||||
|
let none = if have_none_possibility { "(none), " } else { "" };
|
||||||
|
|
||||||
|
db.note(format!("expected values for `{name}` are: {none}`{possibilities}`"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some((value, value_span)) = value {
|
||||||
|
// Suggest the most probable if we found one
|
||||||
|
if let Some(best_match) = find_best_match_for_name(&possibilities, value, None)
|
||||||
|
{
|
||||||
|
db.span_suggestion(
|
||||||
|
value_span,
|
||||||
|
"there is a expected value with a similar name",
|
||||||
|
format!("\"{best_match}\""),
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if let &[first_possibility] = &possibilities[..] {
|
||||||
|
db.span_suggestion(
|
||||||
|
name_span.shrink_to_hi(),
|
||||||
|
"specify a config value",
|
||||||
|
format!(" = \"{first_possibility}\""),
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if have_none_possibility {
|
||||||
|
db.note(format!("no expected value for `{name}`"));
|
||||||
|
if let Some((_value, value_span)) = value {
|
||||||
|
db.span_suggestion(
|
||||||
|
name_span.shrink_to_hi().to(value_span),
|
||||||
|
"remove the value",
|
||||||
|
"",
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let inst = if let Some((value, _value_span)) = value {
|
||||||
|
let pre = if is_from_cargo { "\\" } else { "" };
|
||||||
|
format!("cfg({name}, values({pre}\"{value}{pre}\"))")
|
||||||
|
} else {
|
||||||
|
format!("cfg({name})")
|
||||||
|
};
|
||||||
|
|
||||||
|
if is_from_cargo {
|
||||||
|
if name == sym::feature {
|
||||||
|
if let Some((value, _value_span)) = value {
|
||||||
|
db.help(format!("consider adding `{value}` as a feature in `Cargo.toml`"));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
db.help(format!("consider using a Cargo feature instead or adding `println!(\"cargo:rustc-check-cfg={inst}\");` to the top of a `build.rs`"));
|
||||||
|
}
|
||||||
|
db.note("see <https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#check-cfg> for more information about checking conditional configuration");
|
||||||
|
} else {
|
||||||
|
db.help(format!("to expect this configuration use `--check-cfg={inst}`"));
|
||||||
|
db.note("see <https://doc.rust-lang.org/nightly/unstable-book/compiler-flags/check-cfg.html> for more information about checking conditional configuration");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BuiltinLintDiagnostics::DeprecatedWhereclauseLocation(new_span, suggestion) => {
|
||||||
|
db.multipart_suggestion(
|
||||||
|
"move it to the end of the type declaration",
|
||||||
|
vec![(db.span.primary_span().unwrap(), "".to_string()), (new_span, suggestion)],
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
);
|
||||||
|
db.note(
|
||||||
|
"see issue #89122 <https://github.com/rust-lang/rust/issues/89122> for more information",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
BuiltinLintDiagnostics::SingleUseLifetime {
|
||||||
|
param_span,
|
||||||
|
use_span: Some((use_span, elide)),
|
||||||
|
deletion_span,
|
||||||
|
} => {
|
||||||
|
debug!(?param_span, ?use_span, ?deletion_span);
|
||||||
|
db.span_label(param_span, "this lifetime...");
|
||||||
|
db.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(use_span, char::is_whitespace)
|
||||||
|
.unwrap_or(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)]
|
||||||
|
};
|
||||||
|
db.multipart_suggestion(msg, suggestions, Applicability::MachineApplicable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BuiltinLintDiagnostics::SingleUseLifetime {
|
||||||
|
param_span: _,
|
||||||
|
use_span: None,
|
||||||
|
deletion_span,
|
||||||
|
} => {
|
||||||
|
debug!(?deletion_span);
|
||||||
|
if let Some(deletion_span) = deletion_span {
|
||||||
|
db.span_suggestion(
|
||||||
|
deletion_span,
|
||||||
|
"elide the unused lifetime",
|
||||||
|
"",
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BuiltinLintDiagnostics::NamedArgumentUsedPositionally {
|
||||||
|
position_sp_to_replace,
|
||||||
|
position_sp_for_msg,
|
||||||
|
named_arg_sp,
|
||||||
|
named_arg_name,
|
||||||
|
is_formatting_arg,
|
||||||
|
} => {
|
||||||
|
db.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"
|
||||||
|
);
|
||||||
|
db.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
|
||||||
|
};
|
||||||
|
db.span_suggestion_verbose(
|
||||||
|
span_to_replace,
|
||||||
|
"use the named argument by name to avoid ambiguity",
|
||||||
|
name,
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BuiltinLintDiagnostics::ByteSliceInPackedStructWithDerive => {
|
||||||
|
db.help("consider implementing the trait by hand, or remove the `packed` attribute");
|
||||||
|
}
|
||||||
|
BuiltinLintDiagnostics::UnusedExternCrate { removal_span } => {
|
||||||
|
db.span_suggestion(removal_span, "remove it", "", Applicability::MachineApplicable);
|
||||||
|
}
|
||||||
|
BuiltinLintDiagnostics::ExternCrateNotIdiomatic { vis_span, ident_span } => {
|
||||||
|
let suggestion_span = vis_span.between(ident_span);
|
||||||
|
db.span_suggestion_verbose(
|
||||||
|
suggestion_span,
|
||||||
|
"convert it to a `use`",
|
||||||
|
if vis_span.is_empty() { "use " } else { " use " },
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
BuiltinLintDiagnostics::AmbiguousGlobImports { diag } => {
|
||||||
|
rustc_errors::report_ambiguity_error(db, diag);
|
||||||
|
}
|
||||||
|
BuiltinLintDiagnostics::AmbiguousGlobReexports {
|
||||||
|
name,
|
||||||
|
namespace,
|
||||||
|
first_reexport_span,
|
||||||
|
duplicate_reexport_span,
|
||||||
|
} => {
|
||||||
|
db.span_label(
|
||||||
|
first_reexport_span,
|
||||||
|
format!("the name `{name}` in the {namespace} namespace is first re-exported here"),
|
||||||
|
);
|
||||||
|
db.span_label(
|
||||||
|
duplicate_reexport_span,
|
||||||
|
format!(
|
||||||
|
"but the name `{name}` in the {namespace} namespace is also re-exported here"
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
BuiltinLintDiagnostics::HiddenGlobReexports {
|
||||||
|
name,
|
||||||
|
namespace,
|
||||||
|
glob_reexport_span,
|
||||||
|
private_item_span,
|
||||||
|
} => {
|
||||||
|
db.span_note(glob_reexport_span, format!("the name `{name}` in the {namespace} namespace is supposed to be publicly re-exported here"));
|
||||||
|
db.span_note(private_item_span, "but the private item here shadows it".to_owned());
|
||||||
|
}
|
||||||
|
BuiltinLintDiagnostics::UnusedQualifications { removal_span } => {
|
||||||
|
db.span_suggestion_verbose(
|
||||||
|
removal_span,
|
||||||
|
"remove the unnecessary path segments",
|
||||||
|
"",
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
BuiltinLintDiagnostics::AssociatedConstElidedLifetime { elided, span } => {
|
||||||
|
db.span_suggestion_verbose(
|
||||||
|
if elided { span.shrink_to_hi() } else { span },
|
||||||
|
"use the `'static` lifetime",
|
||||||
|
if elided { "'static " } else { "'static" },
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
BuiltinLintDiagnostics::RedundantImportVisibility { max_vis, span } => {
|
||||||
|
db.span_note(span, format!("the most public imported item is `{max_vis}`"));
|
||||||
|
db.help("reduce the glob import's visibility or increase visibility of imported items");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user