summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_lint/src
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_lint/src')
-rw-r--r--compiler/rustc_lint/src/builtin.rs105
-rw-r--r--compiler/rustc_lint/src/context.rs104
-rw-r--r--compiler/rustc_lint/src/drop_forget_useless.rs164
-rw-r--r--compiler/rustc_lint/src/early.rs2
-rw-r--r--compiler/rustc_lint/src/enum_intrinsics_non_enums.rs2
-rw-r--r--compiler/rustc_lint/src/errors.rs2
-rw-r--r--compiler/rustc_lint/src/expect.rs2
-rw-r--r--compiler/rustc_lint/src/internal.rs87
-rw-r--r--compiler/rustc_lint/src/late.rs10
-rw-r--r--compiler/rustc_lint/src/levels.rs14
-rw-r--r--compiler/rustc_lint/src/lib.rs8
-rw-r--r--compiler/rustc_lint/src/lints.rs53
-rw-r--r--compiler/rustc_lint/src/non_fmt_panic.rs2
-rw-r--r--compiler/rustc_lint/src/nonstandard_style.rs8
-rw-r--r--compiler/rustc_lint/src/noop_method_call.rs117
-rw-r--r--compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs4
-rw-r--r--compiler/rustc_lint/src/types.rs6
-rw-r--r--compiler/rustc_lint/src/unused.rs95
18 files changed, 592 insertions, 193 deletions
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index 6b387df78..85141836e 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -22,7 +22,7 @@
use crate::fluent_generated as fluent;
use crate::{
- errors::BuiltinEllpisisInclusiveRangePatterns,
+ errors::BuiltinEllipsisInclusiveRangePatterns,
lints::{
BuiltinAnonymousParams, BuiltinBoxPointers, BuiltinClashingExtern,
BuiltinClashingExternSub, BuiltinConstNoMangle, BuiltinDeprecatedAttrLink,
@@ -62,7 +62,9 @@ use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::layout::{LayoutError, LayoutOf};
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::subst::GenericArgKind;
+use rustc_middle::ty::TypeVisitableExt;
use rustc_middle::ty::{self, Instance, Ty, TyCtxt, VariantDef};
+use rustc_session::config::ExpectedValues;
use rustc_session::lint::{BuiltinLintDiagnostics, FutureIncompatibilityReason};
use rustc_span::edition::Edition;
use rustc_span::source_map::Spanned;
@@ -116,8 +118,7 @@ impl EarlyLintPass for WhileTrue {
#[inline]
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
if let ast::ExprKind::While(cond, _, label) = &e.kind
- && let cond = pierce_parens(cond)
- && let ast::ExprKind::Lit(token_lit) = cond.kind
+ && let ast::ExprKind::Lit(token_lit) = pierce_parens(cond).kind
&& let token::Lit { kind: token::Bool, symbol: kw::True, .. } = token_lit
&& !cond.span.from_expansion()
{
@@ -166,10 +167,8 @@ declare_lint_pass!(BoxPointers => [BOX_POINTERS]);
impl BoxPointers {
fn check_heap_type(&self, cx: &LateContext<'_>, span: Span, ty: Ty<'_>) {
for leaf in ty.walk() {
- if let GenericArgKind::Type(leaf_ty) = leaf.unpack() {
- if leaf_ty.is_box() {
- cx.emit_spanned_lint(BOX_POINTERS, span, BuiltinBoxPointers { ty });
- }
+ if let GenericArgKind::Type(leaf_ty) = leaf.unpack() && leaf_ty.is_box() {
+ cx.emit_spanned_lint(BOX_POINTERS, span, BuiltinBoxPointers { ty });
}
}
}
@@ -548,32 +547,17 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
}
fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) {
- match it.kind {
- hir::ItemKind::Trait(..) => {
- // Issue #11592: traits are always considered exported, even when private.
- if cx.tcx.visibility(it.owner_id)
- == ty::Visibility::Restricted(
- cx.tcx.parent_module_from_def_id(it.owner_id.def_id).to_def_id(),
- )
- {
- return;
- }
- }
- hir::ItemKind::TyAlias(..)
- | hir::ItemKind::Fn(..)
- | hir::ItemKind::Macro(..)
- | hir::ItemKind::Mod(..)
- | hir::ItemKind::Enum(..)
- | hir::ItemKind::Struct(..)
- | hir::ItemKind::Union(..)
- | hir::ItemKind::Const(..)
- | hir::ItemKind::Static(..) => {}
-
- _ => return,
- };
+ // Previously the Impl and Use types have been excluded from missing docs,
+ // so we will continue to exclude them for compatibility.
+ //
+ // The documentation on `ExternCrate` is not used at the moment so no need to warn for it.
+ if let hir::ItemKind::Impl(..) | hir::ItemKind::Use(..) | hir::ItemKind::ExternCrate(_) =
+ it.kind
+ {
+ return;
+ }
let (article, desc) = cx.tcx.article_and_description(it.owner_id.to_def_id());
-
self.check_missing_docs_attrs(cx, it.owner_id.def_id, article, desc);
}
@@ -631,7 +615,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
declare_lint! {
/// The `missing_copy_implementations` lint detects potentially-forgotten
- /// implementations of [`Copy`].
+ /// implementations of [`Copy`] for public types.
///
/// [`Copy`]: https://doc.rust-lang.org/std/marker/trait.Copy.html
///
@@ -667,7 +651,9 @@ declare_lint_pass!(MissingCopyImplementations => [MISSING_COPY_IMPLEMENTATIONS])
impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations {
fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
- if !cx.effective_visibilities.is_reachable(item.owner_id.def_id) {
+ if !(cx.effective_visibilities.is_reachable(item.owner_id.def_id)
+ && cx.tcx.local_visibility(item.owner_id.def_id).is_public())
+ {
return;
}
let (def, ty) = match item.kind {
@@ -747,7 +733,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations {
declare_lint! {
/// The `missing_debug_implementations` lint detects missing
- /// implementations of [`fmt::Debug`].
+ /// implementations of [`fmt::Debug`] for public types.
///
/// [`fmt::Debug`]: https://doc.rust-lang.org/std/fmt/trait.Debug.html
///
@@ -786,7 +772,9 @@ impl_lint_pass!(MissingDebugImplementations => [MISSING_DEBUG_IMPLEMENTATIONS]);
impl<'tcx> LateLintPass<'tcx> for MissingDebugImplementations {
fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
- if !cx.effective_visibilities.is_reachable(item.owner_id.def_id) {
+ if !(cx.effective_visibilities.is_reachable(item.owner_id.def_id)
+ && cx.tcx.local_visibility(item.owner_id.def_id).is_public())
+ {
return;
}
@@ -977,7 +965,7 @@ fn warn_if_doc(cx: &EarlyContext<'_>, node_span: Span, node_kind: &str, attrs: &
Some(sugared_span.map_or(attr.span, |span| span.with_hi(attr.span.hi())));
}
- if attrs.peek().map_or(false, |next_attr| next_attr.is_doc_comment()) {
+ if attrs.peek().is_some_and(|next_attr| next_attr.is_doc_comment()) {
continue;
}
@@ -1463,6 +1451,10 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds {
// Bounds are respected for `type X = impl Trait`
return;
}
+ if cx.tcx.type_of(item.owner_id).skip_binder().has_inherent_projections() {
+ // Bounds are respected for `type X = … Type::Inherent …`
+ return;
+ }
// There must not be a where clause
if type_alias_generics.predicates.is_empty() {
return;
@@ -1582,7 +1574,6 @@ declare_lint_pass!(
impl<'tcx> LateLintPass<'tcx> for TrivialConstraints {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
- use rustc_middle::ty::visit::TypeVisitableExt;
use rustc_middle::ty::Clause;
use rustc_middle::ty::PredicateKind::*;
@@ -1711,13 +1702,13 @@ impl EarlyLintPass for EllipsisInclusiveRangePatterns {
}
}
- let (parenthesise, endpoints) = match &pat.kind {
+ let (parentheses, endpoints) = match &pat.kind {
PatKind::Ref(subpat, _) => (true, matches_ellipsis_pat(&subpat)),
_ => (false, matches_ellipsis_pat(pat)),
};
if let Some((start, end, join)) = endpoints {
- if parenthesise {
+ if parentheses {
self.node_id = Some(pat.id);
let end = expr_to_string(&end);
let replace = match start {
@@ -1725,7 +1716,7 @@ impl EarlyLintPass for EllipsisInclusiveRangePatterns {
None => format!("&(..={})", end),
};
if join.edition() >= Edition::Edition2021 {
- cx.sess().emit_err(BuiltinEllpisisInclusiveRangePatterns {
+ cx.sess().emit_err(BuiltinEllipsisInclusiveRangePatterns {
span: pat.span,
suggestion: pat.span,
replace,
@@ -1743,7 +1734,7 @@ impl EarlyLintPass for EllipsisInclusiveRangePatterns {
} else {
let replace = "..=";
if join.edition() >= Edition::Edition2021 {
- cx.sess().emit_err(BuiltinEllpisisInclusiveRangePatterns {
+ cx.sess().emit_err(BuiltinEllipsisInclusiveRangePatterns {
span: pat.span,
suggestion: join,
replace: replace.to_string(),
@@ -1899,8 +1890,8 @@ declare_lint_pass!(
struct UnderMacro(bool);
impl KeywordIdents {
- fn check_tokens(&mut self, cx: &EarlyContext<'_>, tokens: TokenStream) {
- for tt in tokens.into_trees() {
+ fn check_tokens(&mut self, cx: &EarlyContext<'_>, tokens: &TokenStream) {
+ for tt in tokens.trees() {
match tt {
// Only report non-raw idents.
TokenTree::Token(token, _) => {
@@ -1961,10 +1952,10 @@ impl KeywordIdents {
impl EarlyLintPass for KeywordIdents {
fn check_mac_def(&mut self, cx: &EarlyContext<'_>, mac_def: &ast::MacroDef) {
- self.check_tokens(cx, mac_def.body.tokens.clone());
+ self.check_tokens(cx, &mac_def.body.tokens);
}
fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &ast::MacCall) {
- self.check_tokens(cx, mac.args.tokens.clone());
+ self.check_tokens(cx, &mac.args.tokens);
}
fn check_ident(&mut self, cx: &EarlyContext<'_>, ident: Ident) {
self.check_ident_token(cx, UnderMacro(false), ident);
@@ -2560,7 +2551,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
.subst(cx.tcx, substs)
.apply_any_module(cx.tcx, cx.param_env)
{
- // Entirely skip uninhbaited variants.
+ // Entirely skip uninhabited variants.
Some(false) => return None,
// Forward the others, but remember which ones are definitely inhabited.
Some(true) => true,
@@ -2628,7 +2619,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
if let Some(err) = with_no_trimmed_paths!(ty_find_init_error(cx, conjured_ty, init)) {
let msg = match init {
InitKind::Zeroed => fluent::lint_builtin_unpermitted_type_init_zeroed,
- InitKind::Uninit => fluent::lint_builtin_unpermitted_type_init_unint,
+ InitKind::Uninit => fluent::lint_builtin_unpermitted_type_init_uninit,
};
let sub = BuiltinUnpermittedTypeInitSub { err };
cx.emit_spanned_lint(
@@ -2919,6 +2910,7 @@ impl ClashingExternDeclarations {
| (Generator(..), Generator(..))
| (GeneratorWitness(..), GeneratorWitness(..))
| (Alias(ty::Projection, ..), Alias(ty::Projection, ..))
+ | (Alias(ty::Inherent, ..), Alias(ty::Inherent, ..))
| (Alias(ty::Opaque, ..), Alias(ty::Opaque, ..)) => false,
// These definitely should have been caught above.
@@ -3308,16 +3300,15 @@ impl EarlyLintPass for UnexpectedCfgs {
let cfg = &cx.sess().parse_sess.config;
let check_cfg = &cx.sess().parse_sess.check_config;
for &(name, value) in cfg {
- if let Some(names_valid) = &check_cfg.names_valid && !names_valid.contains(&name){
- cx.emit_lint(UNEXPECTED_CFGS, BuiltinUnexpectedCliConfigName {
- name,
- });
- }
- if let Some(value) = value && let Some(values) = check_cfg.values_valid.get(&name) && !values.contains(&value) {
- cx.emit_lint(
- UNEXPECTED_CFGS,
- BuiltinUnexpectedCliConfigValue { name, value },
- );
+ match check_cfg.expecteds.get(&name) {
+ Some(ExpectedValues::Some(values)) if !values.contains(&value) => {
+ let value = value.unwrap_or(kw::Empty);
+ cx.emit_lint(UNEXPECTED_CFGS, BuiltinUnexpectedCliConfigValue { name, value });
+ }
+ None if check_cfg.exhaustive_names => {
+ cx.emit_lint(UNEXPECTED_CFGS, BuiltinUnexpectedCliConfigName { name });
+ }
+ _ => { /* expected */ }
}
}
}
diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs
index 626c09fea..1d0c43e95 100644
--- a/compiler/rustc_lint/src/context.rs
+++ b/compiler/rustc_lint/src/context.rs
@@ -36,6 +36,7 @@ use rustc_middle::middle::stability;
use rustc_middle::ty::layout::{LayoutError, LayoutOfHelpers, TyAndLayout};
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{self, print::Printer, subst::GenericArg, RegisteredTools, Ty, TyCtxt};
+use rustc_session::config::ExpectedValues;
use rustc_session::lint::{BuiltinLintDiagnostics, LintExpectationId};
use rustc_session::lint::{FutureIncompatibleInfo, Level, Lint, LintBuffer, LintId};
use rustc_session::Session;
@@ -48,9 +49,9 @@ use std::cell::Cell;
use std::iter;
use std::slice;
-type EarlyLintPassFactory = dyn Fn() -> EarlyLintPassObject + sync::Send + sync::Sync;
+type EarlyLintPassFactory = dyn Fn() -> EarlyLintPassObject + sync::DynSend + sync::DynSync;
type LateLintPassFactory =
- dyn for<'tcx> Fn(TyCtxt<'tcx>) -> LateLintPassObject<'tcx> + sync::Send + sync::Sync;
+ dyn for<'tcx> Fn(TyCtxt<'tcx>) -> LateLintPassObject<'tcx> + sync::DynSend + sync::DynSync;
/// Information about the registered lints.
///
@@ -168,7 +169,7 @@ impl LintStore {
pub fn register_early_pass(
&mut self,
- pass: impl Fn() -> EarlyLintPassObject + 'static + sync::Send + sync::Sync,
+ pass: impl Fn() -> EarlyLintPassObject + 'static + sync::DynSend + sync::DynSync,
) {
self.early_passes.push(Box::new(pass));
}
@@ -181,7 +182,7 @@ impl LintStore {
/// * See [rust-clippy#5518](https://github.com/rust-lang/rust-clippy/pull/5518)
pub fn register_pre_expansion_pass(
&mut self,
- pass: impl Fn() -> EarlyLintPassObject + 'static + sync::Send + sync::Sync,
+ pass: impl Fn() -> EarlyLintPassObject + 'static + sync::DynSend + sync::DynSync,
) {
self.pre_expansion_passes.push(Box::new(pass));
}
@@ -190,8 +191,8 @@ impl LintStore {
&mut self,
pass: impl for<'tcx> Fn(TyCtxt<'tcx>) -> LateLintPassObject<'tcx>
+ 'static
- + sync::Send
- + sync::Sync,
+ + sync::DynSend
+ + sync::DynSync,
) {
self.late_passes.push(Box::new(pass));
}
@@ -200,8 +201,8 @@ impl LintStore {
&mut self,
pass: impl for<'tcx> Fn(TyCtxt<'tcx>) -> LateLintPassObject<'tcx>
+ 'static
- + sync::Send
- + sync::Sync,
+ + sync::DynSend
+ + sync::DynSync,
) {
self.late_module_passes.push(Box::new(pass));
}
@@ -616,7 +617,7 @@ pub trait LintContext: Sized {
1 => ("an ", ""),
_ => ("", "s"),
};
- db.span_label(span, &format!(
+ db.span_label(span, format!(
"this comment contains {}invisible unicode text flow control codepoint{}",
an,
s,
@@ -680,12 +681,12 @@ pub trait LintContext: Sized {
);
}
BuiltinLintDiagnostics::UnknownCrateTypes(span, note, sugg) => {
- db.span_suggestion(span, &note, sugg, Applicability::MaybeIncorrect);
+ 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,
+ message,
replaces,
Applicability::MachineApplicable,
);
@@ -720,13 +721,13 @@ pub trait LintContext: Sized {
}
BuiltinLintDiagnostics::MissingAbi(span, default_abi) => {
db.span_label(span, "ABI should be specified here");
- db.help(&format!("the default ABI is {}", default_abi.name()));
+ 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);
+ db.note(note);
}
BuiltinLintDiagnostics::OrPatternsBackCompat(span,suggestion) => {
db.span_suggestion(span, "use pat_param to preserve semantics", suggestion, Applicability::MachineApplicable);
@@ -747,13 +748,13 @@ pub trait LintContext: Sized {
} => {
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}`")
+ 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}`"));
+ db.note(format!("to ignore the value produced by the macro, add a semicolon after the invocation of `{name}`"));
}
}
BuiltinLintDiagnostics::BreakWithLabelAndLoop(span) => {
@@ -765,25 +766,55 @@ pub trait LintContext: Sized {
);
}
BuiltinLintDiagnostics::NamedAsmLabel(help) => {
- db.help(&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::UnexpectedCfg((name, name_span), None) => {
- let Some(names_valid) = &sess.parse_sess.check_config.names_valid else {
- bug!("it shouldn't be possible to have a diagnostic on a name if name checking is not enabled");
- };
- let possibilities: Vec<Symbol> = names_valid.iter().map(|s| *s).collect();
+ BuiltinLintDiagnostics::UnexpectedCfgName((name, name_span), value) => {
+ let possibilities: Vec<Symbol> = sess.parse_sess.check_config.expecteds.keys().map(|s| *s).collect();
// Suggest the most probable if we found one
if let Some(best_match) = find_best_match_for_name(&possibilities, name, None) {
- db.span_suggestion(name_span, "did you mean", best_match, Applicability::MaybeIncorrect);
+ 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();
+
+ 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);
+ } 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);
+ } 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() {
+ 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);
+ }
}
},
- BuiltinLintDiagnostics::UnexpectedCfg((name, name_span), Some((value, value_span))) => {
- let Some(values) = &sess.parse_sess.check_config.values_valid.get(&name) else {
+ 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 possibilities: Vec<Symbol> = values.iter().map(|&s| s).collect();
+ let mut have_none_possibility = false;
+ let possibilities: Vec<Symbol> = values.iter()
+ .inspect(|a| have_none_possibility |= a.is_none())
+ .copied()
+ .flatten()
+ .collect();
// 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
@@ -792,17 +823,24 @@ pub trait LintContext: Sized {
let mut possibilities = possibilities.iter().map(Symbol::as_str).collect::<Vec<_>>();
possibilities.sort();
- let possibilities = possibilities.join(", ");
- db.note(&format!("expected values for `{name}` are: {possibilities}"));
+ let possibilities = possibilities.join("`, `");
+ let none = if have_none_possibility { "(none), " } else { "" };
+
+ db.note(format!("expected values for `{name}` are: {none}`{possibilities}`"));
}
- // 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, "did you mean", format!("\"{best_match}\""), Applicability::MaybeIncorrect);
+ 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 {
- db.note(&format!("no expected value for `{name}`"));
- if name != sym::feature {
+ } 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);
}
}
diff --git a/compiler/rustc_lint/src/drop_forget_useless.rs b/compiler/rustc_lint/src/drop_forget_useless.rs
new file mode 100644
index 000000000..77e4a7669
--- /dev/null
+++ b/compiler/rustc_lint/src/drop_forget_useless.rs
@@ -0,0 +1,164 @@
+use rustc_hir::{Arm, Expr, ExprKind, Node};
+use rustc_span::sym;
+
+use crate::{
+ lints::{DropCopyDiag, DropRefDiag, ForgetCopyDiag, ForgetRefDiag},
+ LateContext, LateLintPass, LintContext,
+};
+
+declare_lint! {
+ /// The `dropping_references` lint checks for calls to `std::mem::drop` with a reference
+ /// instead of an owned value.
+ ///
+ /// ### Example
+ ///
+ /// ```rust
+ /// # fn operation_that_requires_mutex_to_be_unlocked() {} // just to make it compile
+ /// # let mutex = std::sync::Mutex::new(1); // just to make it compile
+ /// let mut lock_guard = mutex.lock();
+ /// std::mem::drop(&lock_guard); // Should have been drop(lock_guard), mutex
+ /// // still locked
+ /// operation_that_requires_mutex_to_be_unlocked();
+ /// ```
+ ///
+ /// {{produces}}
+ ///
+ /// ### Explanation
+ ///
+ /// Calling `drop` on a reference will only drop the
+ /// reference itself, which is a no-op. It will not call the `drop` method (from
+ /// the `Drop` trait implementation) on the underlying referenced value, which
+ /// is likely what was intended.
+ pub DROPPING_REFERENCES,
+ Warn,
+ "calls to `std::mem::drop` with a reference instead of an owned value"
+}
+
+declare_lint! {
+ /// The `forgetting_references` lint checks for calls to `std::mem::forget` with a reference
+ /// instead of an owned value.
+ ///
+ /// ### Example
+ ///
+ /// ```rust
+ /// let x = Box::new(1);
+ /// std::mem::forget(&x); // Should have been forget(x), x will still be dropped
+ /// ```
+ ///
+ /// {{produces}}
+ ///
+ /// ### Explanation
+ ///
+ /// Calling `forget` on a reference will only forget the
+ /// reference itself, which is a no-op. It will not forget the underlying
+ /// referenced value, which is likely what was intended.
+ pub FORGETTING_REFERENCES,
+ Warn,
+ "calls to `std::mem::forget` with a reference instead of an owned value"
+}
+
+declare_lint! {
+ /// The `dropping_copy_types` lint checks for calls to `std::mem::drop` with a value
+ /// that derives the Copy trait.
+ ///
+ /// ### Example
+ ///
+ /// ```rust
+ /// let x: i32 = 42; // i32 implements Copy
+ /// std::mem::drop(x); // A copy of x is passed to the function, leaving the
+ /// // original unaffected
+ /// ```
+ ///
+ /// {{produces}}
+ ///
+ /// ### Explanation
+ ///
+ /// Calling `std::mem::drop` [does nothing for types that
+ /// implement Copy](https://doc.rust-lang.org/std/mem/fn.drop.html), since the
+ /// value will be copied and moved into the function on invocation.
+ pub DROPPING_COPY_TYPES,
+ Warn,
+ "calls to `std::mem::drop` with a value that implements Copy"
+}
+
+declare_lint! {
+ /// The `forgetting_copy_types` lint checks for calls to `std::mem::forget` with a value
+ /// that derives the Copy trait.
+ ///
+ /// ### Example
+ ///
+ /// ```rust
+ /// let x: i32 = 42; // i32 implements Copy
+ /// std::mem::forget(x); // A copy of x is passed to the function, leaving the
+ /// // original unaffected
+ /// ```
+ ///
+ /// {{produces}}
+ ///
+ /// ### Explanation
+ ///
+ /// Calling `std::mem::forget` [does nothing for types that
+ /// implement Copy](https://doc.rust-lang.org/std/mem/fn.drop.html) since the
+ /// value will be copied and moved into the function on invocation.
+ ///
+ /// An alternative, but also valid, explanation is that Copy types do not
+ /// implement the Drop trait, which means they have no destructors. Without a
+ /// destructor, there is nothing for `std::mem::forget` to ignore.
+ pub FORGETTING_COPY_TYPES,
+ Warn,
+ "calls to `std::mem::forget` with a value that implements Copy"
+}
+
+declare_lint_pass!(DropForgetUseless => [DROPPING_REFERENCES, FORGETTING_REFERENCES, DROPPING_COPY_TYPES, FORGETTING_COPY_TYPES]);
+
+impl<'tcx> LateLintPass<'tcx> for DropForgetUseless {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
+ if let ExprKind::Call(path, [arg]) = expr.kind
+ && let ExprKind::Path(ref qpath) = path.kind
+ && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
+ && let Some(fn_name) = cx.tcx.get_diagnostic_name(def_id)
+ {
+ let arg_ty = cx.typeck_results().expr_ty(arg);
+ let is_copy = arg_ty.is_copy_modulo_regions(cx.tcx, cx.param_env);
+ let drop_is_single_call_in_arm = is_single_call_in_arm(cx, arg, expr);
+ match fn_name {
+ sym::mem_drop if arg_ty.is_ref() && !drop_is_single_call_in_arm => {
+ cx.emit_spanned_lint(DROPPING_REFERENCES, expr.span, DropRefDiag { arg_ty, label: arg.span });
+ },
+ sym::mem_forget if arg_ty.is_ref() => {
+ cx.emit_spanned_lint(FORGETTING_REFERENCES, expr.span, ForgetRefDiag { arg_ty, label: arg.span });
+ },
+ sym::mem_drop if is_copy && !drop_is_single_call_in_arm => {
+ cx.emit_spanned_lint(DROPPING_COPY_TYPES, expr.span, DropCopyDiag { arg_ty, label: arg.span });
+ }
+ sym::mem_forget if is_copy => {
+ cx.emit_spanned_lint(FORGETTING_COPY_TYPES, expr.span, ForgetCopyDiag { arg_ty, label: arg.span });
+ }
+ _ => return,
+ };
+ }
+ }
+}
+
+// Dropping returned value of a function, as in the following snippet is considered idiomatic, see
+// rust-lang/rust-clippy#9482 for examples.
+//
+// ```
+// match <var> {
+// <pat> => drop(fn_with_side_effect_and_returning_some_value()),
+// ..
+// }
+// ```
+fn is_single_call_in_arm<'tcx>(
+ cx: &LateContext<'tcx>,
+ arg: &'tcx Expr<'_>,
+ drop_expr: &'tcx Expr<'_>,
+) -> bool {
+ if arg.can_have_side_effects() {
+ let parent_node = cx.tcx.hir().find_parent(drop_expr.hir_id);
+ if let Some(Node::Arm(Arm { body, .. })) = &parent_node {
+ return body.hir_id == drop_expr.hir_id;
+ }
+ }
+ false
+}
diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs
index 65607d718..9f1f5a26e 100644
--- a/compiler/rustc_lint/src/early.rs
+++ b/compiler/rustc_lint/src/early.rs
@@ -428,7 +428,7 @@ pub fn check_ast_node_inner<'a, T: EarlyLintPass>(
for early_lint in lints {
sess.delay_span_bug(
early_lint.span,
- &format!(
+ format!(
"failed to process buffered lint here (dummy = {})",
id == ast::DUMMY_NODE_ID
),
diff --git a/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs b/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs
index f1ba192f2..2ce28f3a0 100644
--- a/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs
+++ b/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs
@@ -42,7 +42,7 @@ declare_lint_pass!(EnumIntrinsicsNonEnums => [ENUM_INTRINSICS_NON_ENUMS]);
/// Returns `true` if we know for sure that the given type is not an enum. Note that for cases where
/// the type is generic, we can't be certain if it will be an enum so we have to assume that it is.
fn is_non_enum(t: Ty<'_>) -> bool {
- !t.is_enum() && !t.needs_subst()
+ !t.is_enum() && !t.has_param()
}
fn enforce_mem_discriminant(
diff --git a/compiler/rustc_lint/src/errors.rs b/compiler/rustc_lint/src/errors.rs
index 9af5284df..bbae3d368 100644
--- a/compiler/rustc_lint/src/errors.rs
+++ b/compiler/rustc_lint/src/errors.rs
@@ -81,7 +81,7 @@ pub struct UnknownToolInScopedLint {
#[derive(Diagnostic)]
#[diag(lint_builtin_ellipsis_inclusive_range_patterns, code = "E0783")]
-pub struct BuiltinEllpisisInclusiveRangePatterns {
+pub struct BuiltinEllipsisInclusiveRangePatterns {
#[primary_span]
pub span: Span,
#[suggestion(style = "short", code = "{replace}", applicability = "machine-applicable")]
diff --git a/compiler/rustc_lint/src/expect.rs b/compiler/rustc_lint/src/expect.rs
index e9eb14ea1..b1266b58a 100644
--- a/compiler/rustc_lint/src/expect.rs
+++ b/compiler/rustc_lint/src/expect.rs
@@ -1,5 +1,5 @@
use crate::lints::{Expectation, ExpectationNote};
-use rustc_middle::ty::query::Providers;
+use rustc_middle::query::Providers;
use rustc_middle::ty::TyCtxt;
use rustc_session::lint::builtin::UNFULFILLED_LINT_EXPECTATIONS;
use rustc_session::lint::LintExpectationId;
diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs
index 4ac589c2e..6f773e04a 100644
--- a/compiler/rustc_lint/src/internal.rs
+++ b/compiler/rustc_lint/src/internal.rs
@@ -4,6 +4,7 @@
use crate::lints::{
BadOptAccessDiag, DefaultHashTypesDiag, DiagOutOfImpl, LintPassByHand, NonExistentDocKeyword,
QueryInstability, TyQualified, TykindDiag, TykindKind, UntranslatableDiag,
+ UntranslatableDiagnosticTrivial,
};
use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
use rustc_ast as ast;
@@ -366,7 +367,15 @@ declare_tool_lint! {
report_in_external_macro: true
}
-declare_lint_pass!(Diagnostics => [ UNTRANSLATABLE_DIAGNOSTIC, DIAGNOSTIC_OUTSIDE_OF_IMPL ]);
+declare_tool_lint! {
+ /// The `untranslatable_diagnostic_trivial` lint detects diagnostics created using only static strings.
+ pub rustc::UNTRANSLATABLE_DIAGNOSTIC_TRIVIAL,
+ Deny,
+ "prevent creation of diagnostics which cannot be translated, which use only static strings",
+ report_in_external_macro: true
+}
+
+declare_lint_pass!(Diagnostics => [ UNTRANSLATABLE_DIAGNOSTIC, DIAGNOSTIC_OUTSIDE_OF_IMPL, UNTRANSLATABLE_DIAGNOSTIC_TRIVIAL ]);
impl LateLintPass<'_> for Diagnostics {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
@@ -374,9 +383,8 @@ impl LateLintPass<'_> for Diagnostics {
debug!(?span, ?def_id, ?substs);
let has_attr = ty::Instance::resolve(cx.tcx, cx.param_env, def_id, substs)
.ok()
- .and_then(|inst| inst)
- .map(|inst| cx.tcx.has_attr(inst.def_id(), sym::rustc_lint_diagnostics))
- .unwrap_or(false);
+ .flatten()
+ .is_some_and(|inst| cx.tcx.has_attr(inst.def_id(), sym::rustc_lint_diagnostics));
if !has_attr {
return;
}
@@ -423,6 +431,77 @@ impl LateLintPass<'_> for Diagnostics {
}
}
+impl EarlyLintPass for Diagnostics {
+ #[allow(unused_must_use)]
+ fn check_stmt(&mut self, cx: &EarlyContext<'_>, stmt: &ast::Stmt) {
+ // Looking for a straight chain of method calls from 'struct_span_err' to 'emit'.
+ let ast::StmtKind::Semi(expr) = &stmt.kind else {
+ return;
+ };
+ let ast::ExprKind::MethodCall(meth) = &expr.kind else {
+ return;
+ };
+ if meth.seg.ident.name != sym::emit || !meth.args.is_empty() {
+ return;
+ }
+ let mut segments = vec![];
+ let mut cur = &meth.receiver;
+ let fake = &[].into();
+ loop {
+ match &cur.kind {
+ ast::ExprKind::Call(func, args) => {
+ if let ast::ExprKind::Path(_, path) = &func.kind {
+ segments.push((path.segments.last().unwrap().ident.name, args))
+ }
+ break;
+ }
+ ast::ExprKind::MethodCall(method) => {
+ segments.push((method.seg.ident.name, &method.args));
+ cur = &method.receiver;
+ }
+ ast::ExprKind::MacCall(mac) => {
+ segments.push((mac.path.segments.last().unwrap().ident.name, fake));
+ break;
+ }
+ _ => {
+ break;
+ }
+ }
+ }
+ segments.reverse();
+ if segments.is_empty() {
+ return;
+ }
+ if segments[0].0.as_str() != "struct_span_err" {
+ return;
+ }
+ if !segments.iter().all(|(name, args)| {
+ let arg = match name.as_str() {
+ "struct_span_err" | "span_note" | "span_label" | "span_help" if args.len() == 2 => {
+ &args[1]
+ }
+ "note" | "help" if args.len() == 1 => &args[0],
+ _ => {
+ return false;
+ }
+ };
+ if let ast::ExprKind::Lit(lit) = arg.kind
+ && let ast::token::LitKind::Str = lit.kind {
+ true
+ } else {
+ false
+ }
+ }) {
+ return;
+ }
+ cx.emit_spanned_lint(
+ UNTRANSLATABLE_DIAGNOSTIC_TRIVIAL,
+ stmt.span,
+ UntranslatableDiagnosticTrivial,
+ );
+ }
+}
+
declare_tool_lint! {
/// The `bad_opt_access` lint detects accessing options by field instead of
/// the wrapper function.
diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs
index b42878a02..8a4a451f8 100644
--- a/compiler/rustc_lint/src/late.rs
+++ b/compiler/rustc_lint/src/late.rs
@@ -16,7 +16,7 @@
use crate::{passes::LateLintPassObject, LateContext, LateLintPass, LintStore};
use rustc_ast as ast;
-use rustc_data_structures::sync::join;
+use rustc_data_structures::sync::{join, DynSend};
use rustc_hir as hir;
use rustc_hir::def_id::LocalDefId;
use rustc_hir::intravisit as hir_visit;
@@ -240,8 +240,10 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas
}
fn visit_arm(&mut self, a: &'tcx hir::Arm<'tcx>) {
- lint_callback!(self, check_arm, a);
- hir_visit::walk_arm(self, a);
+ self.with_lint_attrs(a.hir_id, |cx| {
+ lint_callback!(cx, check_arm, a);
+ hir_visit::walk_arm(cx, a);
+ })
}
fn visit_generic_param(&mut self, p: &'tcx hir::GenericParam<'tcx>) {
@@ -429,7 +431,7 @@ fn late_lint_crate_inner<'tcx, T: LateLintPass<'tcx>>(
/// Performs lint checking on a crate.
pub fn check_crate<'tcx, T: LateLintPass<'tcx> + 'tcx>(
tcx: TyCtxt<'tcx>,
- builtin_lints: impl FnOnce() -> T + Send,
+ builtin_lints: impl FnOnce() -> T + Send + DynSend,
) {
join(
|| {
diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs
index bb863f095..8376835f5 100644
--- a/compiler/rustc_lint/src/levels.rs
+++ b/compiler/rustc_lint/src/levels.rs
@@ -14,13 +14,13 @@ use rustc_errors::{DecorateLint, DiagnosticBuilder, DiagnosticMessage, MultiSpan
use rustc_hir as hir;
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::HirId;
-use rustc_index::vec::IndexVec;
+use rustc_index::IndexVec;
use rustc_middle::hir::nested_filter;
use rustc_middle::lint::{
reveal_actual_level, struct_lint_level, LevelAndSource, LintExpectation, LintLevelSource,
ShallowLintLevelMap,
};
-use rustc_middle::ty::query::Providers;
+use rustc_middle::query::Providers;
use rustc_middle::ty::{RegisteredTools, TyCtxt};
use rustc_session::lint::builtin::{RENAMED_AND_REMOVED_LINTS, UNKNOWN_LINTS, UNUSED_ATTRIBUTES};
use rustc_session::lint::{
@@ -242,7 +242,9 @@ impl LintLevelsProvider for LintLevelQueryMap<'_> {
struct QueryMapExpectationsWrapper<'tcx> {
tcx: TyCtxt<'tcx>,
+ /// HirId of the currently investigated element.
cur: HirId,
+ /// Level map for `cur`.
specs: ShallowLintLevelMap,
expectations: Vec<(LintExpectationId, LintExpectation)>,
unstable_to_stable_ids: FxHashMap<LintExpectationId, LintExpectationId>,
@@ -255,11 +257,11 @@ impl LintLevelsProvider for QueryMapExpectationsWrapper<'_> {
self.specs.specs.get(&self.cur.local_id).unwrap_or(&self.empty)
}
fn insert(&mut self, id: LintId, lvl: LevelAndSource) {
- let specs = self.specs.specs.get_mut_or_insert_default(self.cur.local_id);
- specs.clear();
- specs.insert(id, lvl);
+ self.specs.specs.get_mut_or_insert_default(self.cur.local_id).insert(id, lvl);
}
fn get_lint_level(&self, lint: &'static Lint, _: &Session) -> LevelAndSource {
+ // We cannot use `tcx.lint_level_at_node` because we want to know in which order the
+ // attributes have been inserted, in particular whether an `expect` follows a `forbid`.
self.specs.lint_level_id_at_node(self.tcx, LintId::of(lint), self.cur)
}
fn push_expectation(&mut self, id: LintExpectationId, expectation: LintExpectation) {
@@ -355,7 +357,9 @@ impl<'tcx> Visitor<'tcx> for LintLevelsBuilder<'_, LintLevelQueryMap<'tcx>> {
impl<'tcx> LintLevelsBuilder<'_, QueryMapExpectationsWrapper<'tcx>> {
fn add_id(&mut self, hir_id: HirId) {
+ // Change both the `HirId` and the associated specs.
self.provider.cur = hir_id;
+ self.provider.specs.specs.clear();
self.add(self.provider.tcx.hir().attrs(hir_id), hir_id == hir::CRATE_HIR_ID, Some(hir_id));
}
}
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index b35785405..dfddfe09a 100644
--- a/compiler/rustc_lint/src/lib.rs
+++ b/compiler/rustc_lint/src/lib.rs
@@ -52,6 +52,7 @@ mod array_into_iter;
pub mod builtin;
mod context;
mod deref_into_dyn_supertrait;
+mod drop_forget_useless;
mod early;
mod enum_intrinsics_non_enums;
mod errors;
@@ -82,10 +83,10 @@ pub use array_into_iter::ARRAY_INTO_ITER;
use rustc_ast as ast;
use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage};
+use rustc_fluent_macro::fluent_messages;
use rustc_hir as hir;
use rustc_hir::def_id::LocalDefId;
-use rustc_macros::fluent_messages;
-use rustc_middle::ty::query::Providers;
+use rustc_middle::query::Providers;
use rustc_middle::ty::TyCtxt;
use rustc_session::lint::builtin::{
BARE_TRAIT_OBJECTS, ELIDED_LIFETIMES_IN_PATHS, EXPLICIT_OUTLIVES_REQUIREMENTS,
@@ -96,6 +97,7 @@ use rustc_span::Span;
use array_into_iter::ArrayIntoIter;
use builtin::*;
use deref_into_dyn_supertrait::*;
+use drop_forget_useless::*;
use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums;
use for_loops_over_fallibles::*;
use hidden_unicode_codepoints::*;
@@ -201,6 +203,7 @@ late_lint_methods!(
[
ForLoopsOverFallibles: ForLoopsOverFallibles,
DerefIntoDynSupertrait: DerefIntoDynSupertrait,
+ DropForgetUseless: DropForgetUseless,
HardwiredLints: HardwiredLints,
ImproperCTypesDeclarations: ImproperCTypesDeclarations,
ImproperCTypesDefinitions: ImproperCTypesDefinitions,
@@ -518,6 +521,7 @@ fn register_internals(store: &mut LintStore) {
store.register_lints(&TyTyKind::get_lints());
store.register_late_pass(|_| Box::new(TyTyKind));
store.register_lints(&Diagnostics::get_lints());
+ store.register_early_pass(|| Box::new(Diagnostics));
store.register_late_pass(|_| Box::new(Diagnostics));
store.register_lints(&BadOptAccess::get_lints());
store.register_late_pass(|_| Box::new(BadOptAccess));
diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs
index 1d5e02369..d96723a68 100644
--- a/compiler/rustc_lint/src/lints.rs
+++ b/compiler/rustc_lint/src/lints.rs
@@ -662,6 +662,43 @@ pub struct ForLoopsOverFalliblesSuggestion<'a> {
pub end_span: Span,
}
+// drop_forget_useless.rs
+#[derive(LintDiagnostic)]
+#[diag(lint_dropping_references)]
+#[note]
+pub struct DropRefDiag<'a> {
+ pub arg_ty: Ty<'a>,
+ #[label]
+ pub label: Span,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_dropping_copy_types)]
+#[note]
+pub struct DropCopyDiag<'a> {
+ pub arg_ty: Ty<'a>,
+ #[label]
+ pub label: Span,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_forgetting_references)]
+#[note]
+pub struct ForgetRefDiag<'a> {
+ pub arg_ty: Ty<'a>,
+ #[label]
+ pub label: Span,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_forgetting_copy_types)]
+#[note]
+pub struct ForgetCopyDiag<'a> {
+ pub arg_ty: Ty<'a>,
+ #[label]
+ pub label: Span,
+}
+
// hidden_unicode_codepoints.rs
#[derive(LintDiagnostic)]
#[diag(lint_hidden_unicode_codepoints)]
@@ -821,6 +858,10 @@ pub struct DiagOutOfImpl;
pub struct UntranslatableDiag;
#[derive(LintDiagnostic)]
+#[diag(lint_trivial_untranslatable_diag)]
+pub struct UntranslatableDiagnosticTrivial;
+
+#[derive(LintDiagnostic)]
#[diag(lint_bad_opt_access)]
pub struct BadOptAccessDiag<'a> {
pub msg: &'a str,
@@ -1146,6 +1187,18 @@ pub struct NoopMethodCallDiag<'a> {
pub label: Span,
}
+#[derive(LintDiagnostic)]
+#[diag(lint_suspicious_double_ref_deref)]
+pub struct SuspiciousDoubleRefDerefDiag<'a> {
+ pub ty: Ty<'a>,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_suspicious_double_ref_clone)]
+pub struct SuspiciousDoubleRefCloneDiag<'a> {
+ pub ty: Ty<'a>,
+}
+
// pass_by_value.rs
#[derive(LintDiagnostic)]
#[diag(lint_pass_by_value)]
diff --git a/compiler/rustc_lint/src/non_fmt_panic.rs b/compiler/rustc_lint/src/non_fmt_panic.rs
index 5bb1abfd2..b218cc578 100644
--- a/compiler/rustc_lint/src/non_fmt_panic.rs
+++ b/compiler/rustc_lint/src/non_fmt_panic.rs
@@ -128,7 +128,7 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc
// No clue where this argument is coming from.
return lint;
}
- if arg_macro.map_or(false, |id| cx.tcx.is_diagnostic_item(sym::format_macro, id)) {
+ if arg_macro.is_some_and(|id| cx.tcx.is_diagnostic_item(sym::format_macro, id)) {
// A case of `panic!(format!(..))`.
lint.note(fluent::lint_supports_fmt_note);
if let Some((open, close, _)) = find_delimiters(cx, arg_span) {
diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs
index 9efc14849..79253cbc8 100644
--- a/compiler/rustc_lint/src/nonstandard_style.rs
+++ b/compiler/rustc_lint/src/nonstandard_style.rs
@@ -33,6 +33,11 @@ pub fn method_context(cx: &LateContext<'_>, id: LocalDefId) -> MethodLateContext
}
}
+fn assoc_item_in_trait_impl(cx: &LateContext<'_>, ii: &hir::ImplItem<'_>) -> bool {
+ let item = cx.tcx.associated_item(ii.owner_id);
+ item.trait_item_def_id.is_some()
+}
+
declare_lint! {
/// The `non_camel_case_types` lint detects types, variants, traits and
/// type parameters that don't have camel case names.
@@ -177,6 +182,7 @@ impl EarlyLintPass for NonCamelCaseTypes {
// trait impls where we should have warned for the trait definition already.
ast::ItemKind::Impl(box ast::Impl { of_trait: None, items, .. }) => {
for it in items {
+ // FIXME: this doesn't respect `#[allow(..)]` on the item itself.
if let ast::AssocItemKind::Type(..) = it.kind {
self.check_case(cx, "associated type", &it.ident);
}
@@ -505,7 +511,7 @@ impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals {
}
fn check_impl_item(&mut self, cx: &LateContext<'_>, ii: &hir::ImplItem<'_>) {
- if let hir::ImplItemKind::Const(..) = ii.kind {
+ if let hir::ImplItemKind::Const(..) = ii.kind && !assoc_item_in_trait_impl(cx, ii) {
NonUpperCaseGlobals::check_upper_case(cx, "associated constant", &ii.ident);
}
}
diff --git a/compiler/rustc_lint/src/noop_method_call.rs b/compiler/rustc_lint/src/noop_method_call.rs
index d67a00619..d56c35bb6 100644
--- a/compiler/rustc_lint/src/noop_method_call.rs
+++ b/compiler/rustc_lint/src/noop_method_call.rs
@@ -1,10 +1,13 @@
use crate::context::LintContext;
-use crate::lints::NoopMethodCallDiag;
+use crate::lints::{
+ NoopMethodCallDiag, SuspiciousDoubleRefCloneDiag, SuspiciousDoubleRefDerefDiag,
+};
use crate::LateContext;
use crate::LateLintPass;
use rustc_hir::def::DefKind;
use rustc_hir::{Expr, ExprKind};
use rustc_middle::ty;
+use rustc_middle::ty::adjustment::Adjust;
use rustc_span::symbol::sym;
declare_lint! {
@@ -35,32 +38,62 @@ declare_lint! {
"detects the use of well-known noop methods"
}
-declare_lint_pass!(NoopMethodCall => [NOOP_METHOD_CALL]);
+declare_lint! {
+ /// The `suspicious_double_ref_op` lint checks for usage of `.clone()`/`.borrow()`/`.deref()`
+ /// on an `&&T` when `T: !Deref/Borrow/Clone`, which means the call will return the inner `&T`,
+ /// instead of performing the operation on the underlying `T` and can be confusing.
+ ///
+ /// ### Example
+ ///
+ /// ```rust
+ /// # #![allow(unused)]
+ /// struct Foo;
+ /// let foo = &&Foo;
+ /// let clone: &Foo = foo.clone();
+ /// ```
+ ///
+ /// {{produces}}
+ ///
+ /// ### Explanation
+ ///
+ /// Since `Foo` doesn't implement `Clone`, running `.clone()` only dereferences the double
+ /// reference, instead of cloning the inner type which should be what was intended.
+ pub SUSPICIOUS_DOUBLE_REF_OP,
+ Warn,
+ "suspicious call of trait method on `&&T`"
+}
+
+declare_lint_pass!(NoopMethodCall => [NOOP_METHOD_CALL, SUSPICIOUS_DOUBLE_REF_OP]);
impl<'tcx> LateLintPass<'tcx> for NoopMethodCall {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
// We only care about method calls.
- let ExprKind::MethodCall(call, receiver, ..) = &expr.kind else {
- return
+ let ExprKind::MethodCall(call, receiver, _, call_span) = &expr.kind else {
+ return;
};
+
+ if call_span.from_expansion() {
+ return;
+ }
+
// We only care about method calls corresponding to the `Clone`, `Deref` and `Borrow`
// traits and ignore any other method call.
- let did = match cx.typeck_results().type_dependent_def(expr.hir_id) {
- // Verify we are dealing with a method/associated function.
- Some((DefKind::AssocFn, did)) => match cx.tcx.trait_of_item(did) {
- // Check that we're dealing with a trait method for one of the traits we care about.
- Some(trait_id)
- if matches!(
- cx.tcx.get_diagnostic_name(trait_id),
- Some(sym::Borrow | sym::Clone | sym::Deref)
- ) =>
- {
- did
- }
- _ => return,
- },
- _ => return,
+
+ let Some((DefKind::AssocFn, did)) =
+ cx.typeck_results().type_dependent_def(expr.hir_id)
+ else {
+ return;
};
+
+ let Some(trait_id) = cx.tcx.trait_of_item(did) else { return };
+
+ if !matches!(
+ cx.tcx.get_diagnostic_name(trait_id),
+ Some(sym::Borrow | sym::Clone | sym::Deref)
+ ) {
+ return;
+ };
+
let substs = cx
.tcx
.normalize_erasing_regions(cx.param_env, cx.typeck_results().node_substs(expr.hir_id));
@@ -70,25 +103,43 @@ impl<'tcx> LateLintPass<'tcx> for NoopMethodCall {
};
// (Re)check that it implements the noop diagnostic.
let Some(name) = cx.tcx.get_diagnostic_name(i.def_id()) else { return };
- if !matches!(
- name,
- sym::noop_method_borrow | sym::noop_method_clone | sym::noop_method_deref
- ) {
- return;
- }
+
let receiver_ty = cx.typeck_results().expr_ty(receiver);
let expr_ty = cx.typeck_results().expr_ty_adjusted(expr);
- if receiver_ty != expr_ty {
- // This lint will only trigger if the receiver type and resulting expression \
- // type are the same, implying that the method call is unnecessary.
+ let arg_adjustments = cx.typeck_results().expr_adjustments(receiver);
+
+ // If there is any user defined auto-deref step, then we don't want to warn.
+ // https://github.com/rust-lang/rust-clippy/issues/9272
+ if arg_adjustments.iter().any(|adj| matches!(adj.kind, Adjust::Deref(Some(_)))) {
return;
}
+
let expr_span = expr.span;
let span = expr_span.with_lo(receiver.span.hi());
- cx.emit_spanned_lint(
- NOOP_METHOD_CALL,
- span,
- NoopMethodCallDiag { method: call.ident.name, receiver_ty, label: span },
- );
+
+ if receiver_ty == expr_ty {
+ cx.emit_spanned_lint(
+ NOOP_METHOD_CALL,
+ span,
+ NoopMethodCallDiag { method: call.ident.name, receiver_ty, label: span },
+ );
+ } else {
+ match name {
+ // If `type_of(x) == T` and `x.borrow()` is used to get `&T`,
+ // then that should be allowed
+ sym::noop_method_borrow => return,
+ sym::noop_method_clone => cx.emit_spanned_lint(
+ SUSPICIOUS_DOUBLE_REF_OP,
+ span,
+ SuspiciousDoubleRefCloneDiag { ty: expr_ty },
+ ),
+ sym::noop_method_deref => cx.emit_spanned_lint(
+ SUSPICIOUS_DOUBLE_REF_OP,
+ span,
+ SuspiciousDoubleRefDerefDiag { ty: expr_ty },
+ ),
+ _ => return,
+ }
+ }
}
}
diff --git a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs
index f9d43fe22..15715c8fc 100644
--- a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs
+++ b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs
@@ -74,7 +74,7 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound {
// For every projection predicate in the opaque type's explicit bounds,
// check that the type that we're assigning actually satisfies the bounds
// of the associated type.
- for &(pred, pred_span) in cx.tcx.explicit_item_bounds(def_id) {
+ for (pred, pred_span) in cx.tcx.explicit_item_bounds(def_id).subst_identity_iter_copied() {
// Liberate bound regions in the predicate since we
// don't actually care about lifetimes in this check.
let predicate = cx.tcx.liberate_late_bound_regions(def_id, pred.kind());
@@ -112,7 +112,7 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound {
// with `impl Send: OtherTrait`.
for (assoc_pred, assoc_pred_span) in cx
.tcx
- .bound_explicit_item_bounds(proj.projection_ty.def_id)
+ .explicit_item_bounds(proj.projection_ty.def_id)
.subst_iter_copied(cx.tcx, &proj.projection_ty.substs)
{
let assoc_pred = assoc_pred.fold_with(proj_replacer);
diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs
index a6ba74220..4bf4fda82 100644
--- a/compiler/rustc_lint/src/types.rs
+++ b/compiler/rustc_lint/src/types.rs
@@ -677,7 +677,7 @@ pub fn transparent_newtype_field<'a, 'tcx>(
let param_env = tcx.param_env(variant.def_id);
variant.fields.iter().find(|field| {
let field_ty = tcx.type_of(field.did).subst_identity();
- let is_zst = tcx.layout_of(param_env.and(field_ty)).map_or(false, |layout| layout.is_zst());
+ let is_zst = tcx.layout_of(param_env.and(field_ty)).is_ok_and(|layout| layout.is_zst());
!is_zst
})
}
@@ -1119,14 +1119,14 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
// `extern "C" fn` functions can have type parameters, which may or may not be FFI-safe,
// so they are currently ignored for the purposes of this lint.
- ty::Param(..) | ty::Alias(ty::Projection, ..)
+ ty::Param(..) | ty::Alias(ty::Projection | ty::Inherent, ..)
if matches!(self.mode, CItemKind::Definition) =>
{
FfiSafe
}
ty::Param(..)
- | ty::Alias(ty::Projection, ..)
+ | ty::Alias(ty::Projection | ty::Inherent, ..)
| ty::Infer(..)
| ty::Bound(..)
| ty::Error(_)
diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs
index 28cc63198..8f75fa11d 100644
--- a/compiler/rustc_lint/src/unused.rs
+++ b/compiler/rustc_lint/src/unused.rs
@@ -256,7 +256,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
}
ty::Adt(def, _) => is_def_must_use(cx, def.did(), span),
ty::Alias(ty::Opaque, ty::AliasTy { def_id: def, .. }) => {
- elaborate(cx.tcx, cx.tcx.explicit_item_bounds(def).iter().cloned())
+ elaborate(cx.tcx, cx.tcx.explicit_item_bounds(def).subst_identity_iter_copied())
// We only care about self bounds for the impl-trait
.filter_only_self()
.find_map(|(pred, _span)| {
@@ -571,36 +571,50 @@ trait UnusedDelimLint {
}
}
- // Prevent false-positives in cases like `fn x() -> u8 { ({ 0 } + 1) }`
- let lhs_needs_parens = {
+ // Check if LHS needs parens to prevent false-positives in cases like `fn x() -> u8 { ({ 0 } + 1) }`.
+ {
let mut innermost = inner;
loop {
innermost = match &innermost.kind {
- ExprKind::Binary(_, lhs, _rhs) => lhs,
+ ExprKind::Binary(_op, lhs, _rhs) => lhs,
ExprKind::Call(fn_, _params) => fn_,
ExprKind::Cast(expr, _ty) => expr,
ExprKind::Type(expr, _ty) => expr,
ExprKind::Index(base, _subscript) => base,
- _ => break false,
+ _ => break,
};
if !classify::expr_requires_semi_to_be_stmt(innermost) {
- break true;
+ return true;
}
}
- };
+ }
- lhs_needs_parens
- || (followed_by_block
- && match &inner.kind {
- ExprKind::Ret(_)
- | ExprKind::Break(..)
- | ExprKind::Yield(..)
- | ExprKind::Yeet(..) => true,
- ExprKind::Range(_lhs, Some(rhs), _limits) => {
- matches!(rhs.kind, ExprKind::Block(..))
- }
- _ => parser::contains_exterior_struct_lit(&inner),
- })
+ // Check if RHS needs parens to prevent false-positives in cases like `if (() == return) {}`.
+ if !followed_by_block {
+ return false;
+ }
+ let mut innermost = inner;
+ loop {
+ innermost = match &innermost.kind {
+ ExprKind::Unary(_op, expr) => expr,
+ ExprKind::Binary(_op, _lhs, rhs) => rhs,
+ ExprKind::AssignOp(_op, _lhs, rhs) => rhs,
+ ExprKind::Assign(_lhs, rhs, _span) => rhs,
+
+ ExprKind::Ret(_) | ExprKind::Yield(..) | ExprKind::Yeet(..) => return true,
+
+ ExprKind::Break(_label, None) => return false,
+ ExprKind::Break(_label, Some(break_expr)) => {
+ return matches!(break_expr.kind, ExprKind::Block(..));
+ }
+
+ ExprKind::Range(_lhs, Some(rhs), _limits) => {
+ return matches!(rhs.kind, ExprKind::Block(..));
+ }
+
+ _ => return parser::contains_exterior_struct_lit(&inner),
+ }
+ }
}
fn emit_unused_delims_expr(
@@ -638,26 +652,20 @@ trait UnusedDelimLint {
return;
}
let spans = match value.kind {
- ast::ExprKind::Block(ref block, None) if block.stmts.len() == 1 => {
- if let Some(span) = block.stmts[0].span.find_ancestor_inside(value.span) {
- Some((value.span.with_hi(span.lo()), value.span.with_lo(span.hi())))
- } else {
- None
- }
- }
+ ast::ExprKind::Block(ref block, None) if block.stmts.len() == 1 => block.stmts[0]
+ .span
+ .find_ancestor_inside(value.span)
+ .map(|span| (value.span.with_hi(span.lo()), value.span.with_lo(span.hi()))),
ast::ExprKind::Paren(ref expr) => {
- let expr_span = expr.span.find_ancestor_inside(value.span);
- if let Some(expr_span) = expr_span {
- Some((value.span.with_hi(expr_span.lo()), value.span.with_lo(expr_span.hi())))
- } else {
- None
- }
+ expr.span.find_ancestor_inside(value.span).map(|expr_span| {
+ (value.span.with_hi(expr_span.lo()), value.span.with_lo(expr_span.hi()))
+ })
}
_ => return,
};
let keep_space = (
- left_pos.map_or(false, |s| s >= value.span.lo()),
- right_pos.map_or(false, |s| s <= value.span.hi()),
+ left_pos.is_some_and(|s| s >= value.span.lo()),
+ right_pos.is_some_and(|s| s <= value.span.hi()),
);
self.emit_unused_delims(cx, value.span, spans, ctx.into(), keep_space);
}
@@ -930,11 +938,10 @@ impl UnusedParens {
// Otherwise proceed with linting.
_ => {}
}
- let spans = if let Some(inner) = inner.span.find_ancestor_inside(value.span) {
- Some((value.span.with_hi(inner.lo()), value.span.with_lo(inner.hi())))
- } else {
- None
- };
+ let spans = inner
+ .span
+ .find_ancestor_inside(value.span)
+ .map(|inner| (value.span.with_hi(inner.lo()), value.span.with_lo(inner.hi())));
self.emit_unused_delims(cx, value.span, spans, "pattern", keep_space);
}
}
@@ -1045,11 +1052,11 @@ impl EarlyLintPass for UnusedParens {
if self.with_self_ty_parens && b.generic_params.len() > 0 => {}
ast::TyKind::ImplTrait(_, bounds) if bounds.len() > 1 => {}
_ => {
- let spans = if let Some(r) = r.span.find_ancestor_inside(ty.span) {
- Some((ty.span.with_hi(r.lo()), ty.span.with_lo(r.hi())))
- } else {
- None
- };
+ let spans = r
+ .span
+ .find_ancestor_inside(ty.span)
+ .map(|r| (ty.span.with_hi(r.lo()), ty.span.with_lo(r.hi())));
+
self.emit_unused_delims(cx, ty.span, spans, "type", (false, false));
}
}