diff options
Diffstat (limited to 'compiler/rustc_lint')
22 files changed, 699 insertions, 395 deletions
diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 068f1372c..40e6b1b57 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -1,3 +1,7 @@ +lint_ambiguous_wide_pointer_comparisons = ambiguous wide pointer comparison, the comparison includes metadata which may not be expected + .addr_metadata_suggestion = use explicit `std::ptr::eq` method to compare metadata and addresses + .addr_suggestion = use `std::ptr::addr_eq` or untyped pointers to only compare their addresses + lint_array_into_iter = this method call resolves to `<&{$target} as IntoIterator>::into_iter` (due to backwards compatibility), but will resolve to <{$target} as IntoIterator>::into_iter in Rust 2021 .use_iter_suggestion = use `.iter()` instead of `.into_iter()` to avoid ambiguity @@ -128,12 +132,6 @@ lint_builtin_type_alias_generic_bounds = bounds on generic parameters are not en lint_builtin_type_alias_where_clause = where clauses are not enforced in type aliases .suggestion = the clause will not be checked when the type alias is used, and should be removed -lint_builtin_unexpected_cli_config_name = unexpected `{$name}` as condition name - .help = was set with `--cfg` but isn't in the `--check-cfg` expected names - -lint_builtin_unexpected_cli_config_value = unexpected condition value `{$value}` for condition name `{$name}` - .help = was set with `--cfg` but isn't in the `--check-cfg` expected values - lint_builtin_unpermitted_type_init_label = this code causes undefined behavior when executed lint_builtin_unpermitted_type_init_label_suggestion = help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done @@ -496,8 +494,10 @@ lint_requested_level = requested on the command line with `{$level} {$lint_name} lint_span_use_eq_ctxt = use `.eq_ctxt()` instead of `.ctxt() == .ctxt()` -lint_supertrait_as_deref_target = `{$t}` implements `Deref` with supertrait `{$target_principal}` as target - .label = target type is set here +lint_supertrait_as_deref_target = this `Deref` implementation is covered by an implicit supertrait coercion + .label = `{$self_ty}` implements `Deref<Target = dyn {$target_principal}>` which conflicts with supertrait `{$supertrait_principal}` + .label2 = target type is a supertrait of `{$self_ty}` + .help = consider removing this implementation or replacing it with a method instead lint_suspicious_double_ref_clone = using `.clone()` on a double reference, which returns `{$ty}` instead of cloning the inner type @@ -523,6 +523,9 @@ lint_undropped_manually_drops = calls to `std::mem::drop` with `std::mem::Manual lint_ungated_async_fn_track_caller = `#[track_caller]` on async functions is a no-op .label = this function will not propagate the caller location +lint_unit_bindings = binding has unit type `()` + .label = this pattern is inferred to be the unit type `()` + lint_unknown_gated_lint = unknown lint: `{$name}` .note = the `{$name}` lint is unstable diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 6f6150a41..045ff38c0 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -33,7 +33,6 @@ use crate::{ BuiltinMutablesTransmutes, BuiltinNoMangleGeneric, BuiltinNonShorthandFieldPatterns, BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds, BuiltinTypeAliasGenericBounds, BuiltinTypeAliasGenericBoundsSuggestion, BuiltinTypeAliasWhereClause, - BuiltinUnexpectedCliConfigName, BuiltinUnexpectedCliConfigValue, BuiltinUngatedAsyncFnTrackCaller, BuiltinUnpermittedTypeInit, BuiltinUnpermittedTypeInitSub, BuiltinUnreachablePub, BuiltinUnsafe, BuiltinUnstableFeatures, BuiltinUnusedDocComment, BuiltinUnusedDocCommentSub, @@ -41,7 +40,6 @@ use crate::{ }, EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext, }; -use rustc_ast::attr; use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_ast::visit::{FnCtxt, FnKind}; use rustc_ast::{self as ast, *}; @@ -60,7 +58,6 @@ use rustc_middle::ty::GenericArgKind; use rustc_middle::ty::ToPredicate; use rustc_middle::ty::TypeVisitableExt; use rustc_middle::ty::{self, 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; @@ -265,7 +262,7 @@ impl<'tcx> LateLintPass<'tcx> for NonShorthandFieldPatterns { continue; } if let PatKind::Binding(binding_annot, _, ident, None) = fieldpat.pat.kind { - if cx.tcx.find_field_index(ident, &variant) + if cx.tcx.find_field_index(ident, variant) == Some(cx.typeck_results().field_index(fieldpat.hir_id)) { cx.emit_spanned_lint( @@ -509,7 +506,7 @@ impl MissingDoc { } } - let attrs = cx.tcx.hir().attrs(cx.tcx.hir().local_def_id_to_hir_id(def_id)); + let attrs = cx.tcx.hir().attrs(cx.tcx.local_def_id_to_hir_id(def_id)); let has_doc = attrs.iter().any(has_doc); if !has_doc { cx.emit_spanned_lint( @@ -635,21 +632,21 @@ impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations { return; } let (def, ty) = match item.kind { - hir::ItemKind::Struct(_, ref ast_generics) => { + hir::ItemKind::Struct(_, ast_generics) => { if !ast_generics.params.is_empty() { return; } let def = cx.tcx.adt_def(item.owner_id); (def, Ty::new_adt(cx.tcx, def, ty::List::empty())) } - hir::ItemKind::Union(_, ref ast_generics) => { + hir::ItemKind::Union(_, ast_generics) => { if !ast_generics.params.is_empty() { return; } let def = cx.tcx.adt_def(item.owner_id); (def, Ty::new_adt(cx.tcx, def, ty::List::empty())) } - hir::ItemKind::Enum(_, ref ast_generics) => { + hir::ItemKind::Enum(_, ast_generics) => { if !ast_generics.params.is_empty() { return; } @@ -1002,8 +999,10 @@ impl EarlyLintPass for UnusedDocComment { } fn check_arm(&mut self, cx: &EarlyContext<'_>, arm: &ast::Arm) { - let arm_span = arm.pat.span.with_hi(arm.body.span.hi()); - warn_if_doc(cx, arm_span, "match arms", &arm.attrs); + if let Some(body) = &arm.body { + let arm_span = arm.pat.span.with_hi(body.span.hi()); + warn_if_doc(cx, arm_span, "match arms", &arm.attrs); + } } fn check_pat(&mut self, cx: &EarlyContext<'_>, pat: &ast::Pat) { @@ -1029,7 +1028,7 @@ impl EarlyLintPass for UnusedDocComment { } fn check_block(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) { - warn_if_doc(cx, block.span, "blocks", &block.attrs()); + warn_if_doc(cx, block.span, "blocks", block.attrs()); } fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { @@ -1119,7 +1118,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems { } }; match it.kind { - hir::ItemKind::Fn(.., ref generics, _) => { + hir::ItemKind::Fn(.., generics, _) => { if let Some(no_mangle_attr) = attr::find_by_name(attrs, sym::no_mangle) { check_no_mangle_on_generic_fn(no_mangle_attr, None, generics, it.span); } @@ -1446,10 +1445,10 @@ declare_lint_pass!( impl TypeAliasBounds { pub(crate) fn is_type_variable_assoc(qpath: &hir::QPath<'_>) -> bool { match *qpath { - hir::QPath::TypeRelative(ref ty, _) => { + hir::QPath::TypeRelative(ty, _) => { // If this is a type variable, we found a `T::Assoc`. match ty.kind { - hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) => { + hir::TyKind::Path(hir::QPath::Resolved(None, path)) => { matches!(path.res, Res::Def(DefKind::TyParam, _)) } _ => false, @@ -1716,16 +1715,16 @@ impl EarlyLintPass for EllipsisInclusiveRangePatterns { } let (parentheses, endpoints) = match &pat.kind { - PatKind::Ref(subpat, _) => (true, matches_ellipsis_pat(&subpat)), + PatKind::Ref(subpat, _) => (true, matches_ellipsis_pat(subpat)), _ => (false, matches_ellipsis_pat(pat)), }; if let Some((start, end, join)) = endpoints { if parentheses { self.node_id = Some(pat.id); - let end = expr_to_string(&end); + let end = expr_to_string(end); let replace = match start { - Some(start) => format!("&({}..={})", expr_to_string(&start), end), + Some(start) => format!("&({}..={})", expr_to_string(start), end), None => format!("&(..={end})"), }; if join.edition() >= Edition::Edition2021 { @@ -1836,7 +1835,7 @@ impl KeywordIdents { self.check_ident_token(cx, UnderMacro(true), ident); } } - TokenTree::Delimited(_, _, tts) => self.check_tokens(cx, tts), + TokenTree::Delimited(.., tts) => self.check_tokens(cx, tts), } } } @@ -1910,7 +1909,7 @@ impl ExplicitOutlivesRequirements { .iter() .filter_map(|(clause, _)| match clause.kind().skip_binder() { ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(a, b)) => match *a { - ty::ReEarlyBound(ebr) if ebr.def_id == def_id => Some(b), + ty::ReEarlyParam(ebr) if ebr.def_id == def_id => Some(b), _ => None, }, _ => None, @@ -1953,7 +1952,7 @@ impl ExplicitOutlivesRequirements { let is_inferred = match tcx.named_bound_var(lifetime.hir_id) { Some(ResolvedArg::EarlyBound(def_id)) => inferred_outlives .iter() - .any(|r| matches!(**r, ty::ReEarlyBound(ebr) if { ebr.def_id == def_id })), + .any(|r| matches!(**r, ty::ReEarlyParam(ebr) if { ebr.def_id == def_id })), _ => false, }; @@ -2383,7 +2382,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue { /// Determine if this expression is a "dangerous initialization". fn is_dangerous_init(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<InitKind> { - if let hir::ExprKind::Call(ref path_expr, ref args) = expr.kind { + if let hir::ExprKind::Call(path_expr, args) = expr.kind { // Find calls to `mem::{uninitialized,zeroed}` methods. if let hir::ExprKind::Path(ref qpath) = path_expr.kind { let def_id = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id()?; @@ -2400,7 +2399,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue { if cx.tcx.is_diagnostic_item(sym::assume_init, def_id) { // This is a call to *some* method named `assume_init`. // See if the `self` parameter is one of the dangerous constructors. - if let hir::ExprKind::Call(ref path_expr, _) = receiver.kind { + if let hir::ExprKind::Call(path_expr, _) = receiver.kind { if let hir::ExprKind::Path(ref qpath) = path_expr.kind { let def_id = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id()?; match cx.tcx.get_diagnostic_name(def_id) { @@ -2542,7 +2541,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue { return variant_find_init_error( cx, ty, - &first_variant.0, + first_variant.0, args, "field of the only potentially inhabited enum variant", init, @@ -2648,13 +2647,13 @@ impl<'tcx> LateLintPass<'tcx> for DerefNullPtr { /// test if expression is a null ptr fn is_null_ptr(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { match &expr.kind { - rustc_hir::ExprKind::Cast(ref expr, ref ty) => { + rustc_hir::ExprKind::Cast(expr, ty) => { if let rustc_hir::TyKind::Ptr(_) = ty.kind { return is_zero(expr) || is_null_ptr(cx, expr); } } // check for call to `core::ptr::null` or `core::ptr::null_mut` - rustc_hir::ExprKind::Call(ref path, _) => { + rustc_hir::ExprKind::Call(path, _) => { if let rustc_hir::ExprKind::Path(ref qpath) = path.kind { if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() { return matches!( @@ -2672,7 +2671,7 @@ impl<'tcx> LateLintPass<'tcx> for DerefNullPtr { /// test if expression is the literal `0` fn is_zero(expr: &hir::Expr<'_>) -> bool { match &expr.kind { - rustc_hir::ExprKind::Lit(ref lit) => { + rustc_hir::ExprKind::Lit(lit) => { if let LitKind::Int(a, _) = lit.node { return a == 0; } @@ -2801,7 +2800,7 @@ impl<'tcx> LateLintPass<'tcx> for NamedAsmLabels { NAMED_ASM_LABELS, Some(target_spans), fluent::lint_builtin_asm_labels, - |lint| lint, + |_| {}, BuiltinLintDiagnostics::NamedAsmLabel( "only local labels of the form `<number>:` should be used in inline asm" .to_string(), @@ -2889,26 +2888,3 @@ impl EarlyLintPass for SpecialModuleName { } } } - -pub use rustc_session::lint::builtin::UNEXPECTED_CFGS; - -declare_lint_pass!(UnexpectedCfgs => [UNEXPECTED_CFGS]); - -impl EarlyLintPass for UnexpectedCfgs { - fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) { - let cfg = &cx.sess().parse_sess.config; - let check_cfg = &cx.sess().parse_sess.check_config; - for &(name, value) in cfg { - 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 a5f4c5ff0..c7a9d5e80 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -36,7 +36,7 @@ use rustc_middle::ty::{self, print::Printer, GenericArg, RegisteredTools, Ty, Ty use rustc_session::config::ExpectedValues; use rustc_session::lint::{BuiltinLintDiagnostics, LintExpectationId}; use rustc_session::lint::{FutureIncompatibleInfo, Level, Lint, LintBuffer, LintId}; -use rustc_session::Session; +use rustc_session::{LintStoreMarker, Session}; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::{BytePos, Span}; @@ -77,6 +77,8 @@ pub struct LintStore { lint_groups: FxHashMap<&'static str, LintGroup>, } +impl LintStoreMarker for LintStore {} + /// The target of the `by_name` map, which accounts for renaming/deprecation. #[derive(Debug)] enum TargetLint { @@ -385,7 +387,7 @@ impl LintStore { }; } Some(LintGroup { lint_ids, .. }) => { - return CheckLintNameResult::Tool(Ok(&lint_ids)); + return CheckLintNameResult::Tool(Ok(lint_ids)); } }, Some(Id(id)) => return CheckLintNameResult::Tool(Ok(slice::from_ref(id))), @@ -406,12 +408,12 @@ impl LintStore { if let Some(LintAlias { name, silent }) = depr { let LintGroup { lint_ids, .. } = self.lint_groups.get(name).unwrap(); return if *silent { - CheckLintNameResult::Ok(&lint_ids) + CheckLintNameResult::Ok(lint_ids) } else { - CheckLintNameResult::Tool(Err((Some(&lint_ids), (*name).to_string()))) + CheckLintNameResult::Tool(Err((Some(lint_ids), (*name).to_string()))) }; } - CheckLintNameResult::Ok(&lint_ids) + CheckLintNameResult::Ok(lint_ids) } }, Some(Id(id)) => CheckLintNameResult::Ok(slice::from_ref(id)), @@ -458,12 +460,12 @@ impl LintStore { if let Some(LintAlias { name, silent }) = depr { let LintGroup { lint_ids, .. } = self.lint_groups.get(name).unwrap(); return if *silent { - CheckLintNameResult::Tool(Err((Some(&lint_ids), complete_name))) + CheckLintNameResult::Tool(Err((Some(lint_ids), complete_name))) } else { - CheckLintNameResult::Tool(Err((Some(&lint_ids), (*name).to_string()))) + CheckLintNameResult::Tool(Err((Some(lint_ids), (*name).to_string()))) }; } - CheckLintNameResult::Tool(Err((Some(&lint_ids), complete_name))) + CheckLintNameResult::Tool(Err((Some(lint_ids), complete_name))) } }, Some(Id(id)) => { @@ -497,9 +499,6 @@ pub struct LateContext<'tcx> { /// Items accessible from the crate being checked. pub effective_visibilities: &'tcx EffectiveVisibilities, - /// The store of registered lints and the lint levels. - pub lint_store: &'tcx LintStore, - pub last_node_with_lint_attrs: hir::HirId, /// Generic type parameters in scope for the item we are in. @@ -515,21 +514,11 @@ pub struct EarlyContext<'a> { pub buffered: LintBuffer, } -pub trait LintPassObject: Sized {} - -impl LintPassObject for EarlyLintPassObject {} - -impl LintPassObject for LateLintPassObject<'_> {} - -pub trait LintContext: Sized { - type PassObject: LintPassObject; - +pub trait LintContext { fn sess(&self) -> &Session; - fn lints(&self) -> &LintStore; - /// Emit a lint at the appropriate level, with an optional associated span and an existing diagnostic. - /// - /// Return value of the `decorate` closure is ignored, see [`struct_lint_level`] for a detailed explanation. + /// Emit a lint at the appropriate level, with an optional associated span and an existing + /// diagnostic. /// /// [`struct_lint_level`]: rustc_middle::lint::struct_lint_level#decorate-signature #[rustc_lint_diagnostics] @@ -538,9 +527,7 @@ pub trait LintContext: Sized { lint: &'static Lint, span: Option<impl Into<MultiSpan>>, msg: impl Into<DiagnosticMessage>, - decorate: impl for<'a, 'b> FnOnce( - &'b mut DiagnosticBuilder<'a, ()>, - ) -> &'b mut DiagnosticBuilder<'a, ()>, + decorate: impl for<'a, 'b> FnOnce(&'b mut DiagnosticBuilder<'a, ()>), diagnostic: BuiltinLintDiagnostics, ) { // We first generate a blank diagnostic. @@ -713,10 +700,14 @@ pub trait LintContext: Sized { 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().map(|s| *s).collect(); + 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 - if let Some(best_match) = find_best_match_for_name(&possibilities, name, None) { + } 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() @@ -749,6 +740,8 @@ pub trait LintContext: Sized { } 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) @@ -762,6 +755,23 @@ pub trait LintContext: Sized { // 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 { @@ -773,6 +783,7 @@ pub trait LintContext: Sized { .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 @@ -793,6 +804,8 @@ pub trait LintContext: Sized { db.span_suggestion(value_span, "there is a expected value with a similar name", format!("\"{best_match}\""), Applicability::MaybeIncorrect); } + } else if name == sym::feature && is_from_cargo { + db.help(format!("consider defining `{name}` as feature in `Cargo.toml`")); } else if let &[first_possibility] = &possibilities[..] { db.span_suggestion(name_span.shrink_to_hi(), "specify a config value", format!(" = \"{first_possibility}\""), Applicability::MaybeIncorrect); } @@ -802,6 +815,27 @@ pub trait LintContext: Sized { 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( @@ -932,6 +966,10 @@ pub trait LintContext: Sized { 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. @@ -943,8 +981,6 @@ pub trait LintContext: Sized { // set the span in their `decorate` function (preferably using set_span). /// Emit a lint at the appropriate level, with an optional associated span. /// - /// Return value of the `decorate` closure is ignored, see [`struct_lint_level`] for a detailed explanation. - /// /// [`struct_lint_level`]: rustc_middle::lint::struct_lint_level#decorate-signature #[rustc_lint_diagnostics] fn lookup<S: Into<MultiSpan>>( @@ -952,9 +988,7 @@ pub trait LintContext: Sized { lint: &'static Lint, span: Option<S>, msg: impl Into<DiagnosticMessage>, - decorate: impl for<'a, 'b> FnOnce( - &'b mut DiagnosticBuilder<'a, ()>, - ) -> &'b mut DiagnosticBuilder<'a, ()>, + decorate: impl for<'a, 'b> FnOnce(&'b mut DiagnosticBuilder<'a, ()>), ); /// Emit a lint at `span` from a lint struct (some type that implements `DecorateLint`, @@ -965,13 +999,13 @@ pub trait LintContext: Sized { span: S, decorator: impl for<'a> DecorateLint<'a, ()>, ) { - self.lookup(lint, Some(span), decorator.msg(), |diag| decorator.decorate_lint(diag)); + self.lookup(lint, Some(span), decorator.msg(), |diag| { + decorator.decorate_lint(diag); + }); } /// Emit a lint at the appropriate level, with an associated span. /// - /// Return value of the `decorate` closure is ignored, see [`struct_lint_level`] for a detailed explanation. - /// /// [`struct_lint_level`]: rustc_middle::lint::struct_lint_level#decorate-signature #[rustc_lint_diagnostics] fn struct_span_lint<S: Into<MultiSpan>>( @@ -979,9 +1013,7 @@ pub trait LintContext: Sized { lint: &'static Lint, span: S, msg: impl Into<DiagnosticMessage>, - decorate: impl for<'a, 'b> FnOnce( - &'b mut DiagnosticBuilder<'a, ()>, - ) -> &'b mut DiagnosticBuilder<'a, ()>, + decorate: impl for<'a, 'b> FnOnce(&'b mut DiagnosticBuilder<'a, ()>), ) { self.lookup(lint, Some(span), msg, decorate); } @@ -990,23 +1022,19 @@ pub trait LintContext: Sized { /// generated by `#[derive(LintDiagnostic)]`). fn emit_lint(&self, lint: &'static Lint, decorator: impl for<'a> DecorateLint<'a, ()>) { self.lookup(lint, None as Option<Span>, decorator.msg(), |diag| { - decorator.decorate_lint(diag) + decorator.decorate_lint(diag); }); } /// Emit a lint at the appropriate level, with no associated span. /// - /// Return value of the `decorate` closure is ignored, see [`struct_lint_level`] for a detailed explanation. - /// /// [`struct_lint_level`]: rustc_middle::lint::struct_lint_level#decorate-signature #[rustc_lint_diagnostics] fn lint( &self, lint: &'static Lint, msg: impl Into<DiagnosticMessage>, - decorate: impl for<'a, 'b> FnOnce( - &'b mut DiagnosticBuilder<'a, ()>, - ) -> &'b mut DiagnosticBuilder<'a, ()>, + decorate: impl for<'a, 'b> FnOnce(&'b mut DiagnosticBuilder<'a, ()>), ) { self.lookup(lint, None as Option<Span>, msg, decorate); } @@ -1059,15 +1087,9 @@ impl<'a> EarlyContext<'a> { } impl<'tcx> LintContext for LateContext<'tcx> { - type PassObject = LateLintPassObject<'tcx>; - /// Gets the overall compiler `Session` object. fn sess(&self) -> &Session { - &self.tcx.sess - } - - fn lints(&self) -> &LintStore { - &*self.lint_store + self.tcx.sess } #[rustc_lint_diagnostics] @@ -1076,9 +1098,7 @@ impl<'tcx> LintContext for LateContext<'tcx> { lint: &'static Lint, span: Option<S>, msg: impl Into<DiagnosticMessage>, - decorate: impl for<'a, 'b> FnOnce( - &'b mut DiagnosticBuilder<'a, ()>, - ) -> &'b mut DiagnosticBuilder<'a, ()>, + decorate: impl for<'a, 'b> FnOnce(&'b mut DiagnosticBuilder<'a, ()>), ) { let hir_id = self.last_node_with_lint_attrs; @@ -1094,15 +1114,9 @@ impl<'tcx> LintContext for LateContext<'tcx> { } impl LintContext for EarlyContext<'_> { - type PassObject = EarlyLintPassObject; - /// Gets the overall compiler `Session` object. fn sess(&self) -> &Session { - &self.builder.sess() - } - - fn lints(&self) -> &LintStore { - self.builder.lint_store() + self.builder.sess() } #[rustc_lint_diagnostics] @@ -1111,9 +1125,7 @@ impl LintContext for EarlyContext<'_> { lint: &'static Lint, span: Option<S>, msg: impl Into<DiagnosticMessage>, - decorate: impl for<'a, 'b> FnOnce( - &'b mut DiagnosticBuilder<'a, ()>, - ) -> &'b mut DiagnosticBuilder<'a, ()>, + decorate: impl for<'a, 'b> FnOnce(&'b mut DiagnosticBuilder<'a, ()>), ) { self.builder.struct_lint(lint, span.map(|s| s.into()), msg, decorate) } @@ -1149,7 +1161,7 @@ impl<'tcx> LateContext<'tcx> { /// bodies (e.g. for paths in `hir::Ty`), without any risk of ICE-ing. pub fn qpath_res(&self, qpath: &hir::QPath<'_>, id: hir::HirId) -> Res { match *qpath { - hir::QPath::Resolved(_, ref path) => path.res, + hir::QPath::Resolved(_, path) => path.res, hir::QPath::TypeRelative(..) | hir::QPath::LangItem(..) => self .maybe_typeck_results() .filter(|typeck_results| typeck_results.hir_owner == id.owner) diff --git a/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs b/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs index d2d99bc0d..4673b801d 100644 --- a/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs +++ b/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs @@ -50,7 +50,7 @@ declare_lint! { Warn, "`Deref` implementation usage with a supertrait trait object for output might be shadowed in the future", @future_incompatible = FutureIncompatibleInfo { - reason: FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps, + reason: FutureIncompatibilityReason::FutureReleaseSemanticsChange, reference: "issue #89460 <https://github.com/rust-lang/rust/issues/89460>", }; } @@ -59,32 +59,46 @@ declare_lint_pass!(DerefIntoDynSupertrait => [DEREF_INTO_DYN_SUPERTRAIT]); impl<'tcx> LateLintPass<'tcx> for DerefIntoDynSupertrait { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { + let tcx = cx.tcx; // `Deref` is being implemented for `t` if let hir::ItemKind::Impl(impl_) = item.kind + // the trait is a `Deref` implementation && let Some(trait_) = &impl_.of_trait - && let t = cx.tcx.type_of(item.owner_id).instantiate_identity() - && let opt_did @ Some(did) = trait_.trait_def_id() - && opt_did == cx.tcx.lang_items().deref_trait() - // `t` is `dyn t_principal` - && let ty::Dynamic(data, _, ty::Dyn) = t.kind() - && let Some(t_principal) = data.principal() + && let Some(did) = trait_.trait_def_id() + && Some(did) == tcx.lang_items().deref_trait() + // the self type is `dyn t_principal` + && let self_ty = tcx.type_of(item.owner_id).instantiate_identity() + && let ty::Dynamic(data, _, ty::Dyn) = self_ty.kind() + && let Some(self_principal) = data.principal() // `<T as Deref>::Target` is `dyn target_principal` - && let Some(target) = cx.get_associated_type(t, did, "Target") + && let Some(target) = cx.get_associated_type(self_ty, did, "Target") && let ty::Dynamic(data, _, ty::Dyn) = target.kind() && let Some(target_principal) = data.principal() // `target_principal` is a supertrait of `t_principal` - && supertraits(cx.tcx, t_principal.with_self_ty(cx.tcx, cx.tcx.types.trait_object_dummy_self)) - .any(|sup| sup.map_bound(|x| ty::ExistentialTraitRef::erase_self_ty(cx.tcx, x)) == target_principal) + && let Some(supertrait_principal) = supertraits(tcx, self_principal.with_self_ty(tcx, self_ty)) + .find(|supertrait| supertrait.def_id() == target_principal.def_id()) { - let label = impl_ + // erase regions in self type for better diagnostic presentation + let (self_ty, target_principal, supertrait_principal) = + tcx.erase_regions((self_ty, target_principal, supertrait_principal)); + let label2 = impl_ .items .iter() .find_map(|i| (i.ident.name == sym::Target).then_some(i.span)) .map(|label| SupertraitAsDerefTargetLabel { label }); + let span = tcx.def_span(item.owner_id.def_id); cx.emit_spanned_lint( DEREF_INTO_DYN_SUPERTRAIT, - cx.tcx.def_span(item.owner_id.def_id), - SupertraitAsDerefTarget { t, target_principal, label }, + span, + SupertraitAsDerefTarget { + self_ty, + supertrait_principal: supertrait_principal.map_bound(|trait_ref| { + ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref) + }), + target_principal, + label: span, + label2, + }, ); } } diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index d102e3a6c..b9add9e9f 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -45,13 +45,7 @@ impl<'a, T: EarlyLintPass> EarlyContextAndPass<'a, T> { fn inlined_check_id(&mut self, id: ast::NodeId) { for early_lint in self.context.buffered.take(id) { let BufferedEarlyLint { span, msg, node_id: _, lint_id, diagnostic } = early_lint; - self.context.lookup_with_diagnostics( - lint_id.lint, - Some(span), - msg, - |lint| lint, - diagnostic, - ); + self.context.lookup_with_diagnostics(lint_id.lint, Some(span), msg, |_| {}, diagnostic); } } @@ -162,8 +156,8 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> // Explicitly check for lints associated with 'closure_id', since // it does not have a corresponding AST node if let ast_visit::FnKind::Fn(_, _, sig, _, _, _) = fk { - if let ast::Async::Yes { closure_id, .. } = sig.header.asyncness { - self.check_id(closure_id); + if let Some(coroutine_kind) = sig.header.coroutine_kind { + self.check_id(coroutine_kind.closure_id()); } } } @@ -223,9 +217,11 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> // it does not have a corresponding AST node match e.kind { ast::ExprKind::Closure(box ast::Closure { - asyncness: ast::Async::Yes { closure_id, .. }, + coroutine_kind: Some(coroutine_kind), .. - }) => self.check_id(closure_id), + }) => { + self.check_id(coroutine_kind.closure_id()); + } _ => {} } lint_callback!(self, check_expr_post, e); @@ -350,7 +346,7 @@ impl<'a> EarlyCheckNode<'a> for (&'a ast::Crate, &'a [ast::Attribute]) { where 'a: 'b, { - &self.1 + self.1 } fn check<'b, T: EarlyLintPass>(self, cx: &mut EarlyContextAndPass<'b, T>) where @@ -430,7 +426,7 @@ pub fn check_ast_node_inner<'a, T: EarlyLintPass>( // that was not lint-checked (perhaps it doesn't exist?). This is a bug. for (id, lints) in cx.context.buffered.map { for early_lint in lints { - sess.delay_span_bug( + sess.span_delayed_bug( early_lint.span, format!( "failed to process buffered lint here (dummy = {})", diff --git a/compiler/rustc_lint/src/expect.rs b/compiler/rustc_lint/src/expect.rs index 740c90757..5dcc1bce5 100644 --- a/compiler/rustc_lint/src/expect.rs +++ b/compiler/rustc_lint/src/expect.rs @@ -16,7 +16,7 @@ fn check_expectations(tcx: TyCtxt<'_>, tool_filter: Option<Symbol>) { } let lint_expectations = tcx.lint_expectations(()); - let fulfilled_expectations = tcx.sess.diagnostic().steal_fulfilled_expectation_ids(); + let fulfilled_expectations = tcx.sess.dcx().steal_fulfilled_expectation_ids(); tracing::debug!(?lint_expectations, ?fulfilled_expectations); @@ -24,7 +24,7 @@ fn check_expectations(tcx: TyCtxt<'_>, tool_filter: Option<Symbol>) { // This check will always be true, since `lint_expectations` only // holds stable ids if let LintExpectationId::Stable { hir_id, .. } = id { - if !fulfilled_expectations.contains(&id) + if !fulfilled_expectations.contains(id) && tool_filter.map_or(true, |filter| expectation.lint_tool == Some(filter)) { let rationale = expectation.reason.map(|rationale| ExpectationNote { rationale }); diff --git a/compiler/rustc_lint/src/for_loops_over_fallibles.rs b/compiler/rustc_lint/src/for_loops_over_fallibles.rs index c8ec0458b..ea922785a 100644 --- a/compiler/rustc_lint/src/for_loops_over_fallibles.rs +++ b/compiler/rustc_lint/src/for_loops_over_fallibles.rs @@ -137,7 +137,7 @@ fn suggest_question_mark<'tcx>( // Check that the function/closure/constant we are in has a `Result` type. // Otherwise suggesting using `?` may not be a good idea. { - let ty = cx.typeck_results().expr_ty(&cx.tcx.hir().body(body_id).value); + let ty = cx.typeck_results().expr_ty(cx.tcx.hir().body(body_id).value); let ty::Adt(ret_adt, ..) = ty.kind() else { return false }; if !cx.tcx.is_diagnostic_item(sym::Result, ret_adt.did()) { return false; diff --git a/compiler/rustc_lint/src/foreign_modules.rs b/compiler/rustc_lint/src/foreign_modules.rs index 86b3b4ad0..31d9c0d33 100644 --- a/compiler/rustc_lint/src/foreign_modules.rs +++ b/compiler/rustc_lint/src/foreign_modules.rs @@ -341,8 +341,8 @@ fn structurally_same_type_impl<'tcx>( // We don't compare regions, but leaving bound regions around ICEs, so // we erase them. - let a_sig = tcx.erase_late_bound_regions(a_poly_sig); - let b_sig = tcx.erase_late_bound_regions(b_poly_sig); + let a_sig = tcx.instantiate_bound_regions_with_erased(a_poly_sig); + let b_sig = tcx.instantiate_bound_regions_with_erased(b_poly_sig); (a_sig.abi, a_sig.unsafety, a_sig.c_variadic) == (b_sig.abi, b_sig.unsafety, b_sig.c_variadic) diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index 2d86129c4..53d99c7f7 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -34,7 +34,7 @@ declare_lint_pass!(DefaultHashTypes => [DEFAULT_HASH_TYPES]); impl LateLintPass<'_> for DefaultHashTypes { fn check_path(&mut self, cx: &LateContext<'_>, path: &Path<'_>, hir_id: HirId) { let Res::Def(rustc_hir::def::DefKind::Struct, def_id) = path.res else { return }; - if matches!(cx.tcx.hir().get(hir_id), Node::Item(Item { kind: ItemKind::Use(..), .. })) { + if matches!(cx.tcx.hir_node(hir_id), Node::Item(Item { kind: ItemKind::Use(..), .. })) { // don't lint imports, only actual usages return; } @@ -196,7 +196,7 @@ impl<'tcx> LateLintPass<'tcx> for TyTyKind { } } else if !ty.span.from_expansion() && path.segments.len() > 1 - && let Some(ty) = is_ty_or_ty_ctxt(cx, &path) + && let Some(ty) = is_ty_or_ty_ctxt(cx, path) { cx.emit_spanned_lint( USAGE_OF_QUALIFIED_TY, diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs index 6c8b60c8d..caa015565 100644 --- a/compiler/rustc_lint/src/late.rs +++ b/compiler/rustc_lint/src/late.rs @@ -17,22 +17,25 @@ use crate::{passes::LateLintPassObject, LateContext, LateLintPass, LintStore}; use rustc_ast as ast; use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_data_structures::sync::join; +use rustc_data_structures::sync::{join, Lrc}; use rustc_hir as hir; use rustc_hir::def_id::{LocalDefId, LocalModDefId}; use rustc_hir::intravisit as hir_visit; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, TyCtxt}; use rustc_session::lint::LintPass; +use rustc_session::Session; use rustc_span::Span; use std::any::Any; use std::cell::Cell; -/// Extract the `LintStore` from the query context. -/// This function exists because we've erased `LintStore` as `dyn Any` in the context. -pub fn unerased_lint_store(tcx: TyCtxt<'_>) -> &LintStore { - let store: &dyn Any = &*tcx.lint_store; +/// Extract the [`LintStore`] from [`Session`]. +/// +/// This function exists because [`Session::lint_store`] is type-erased. +pub fn unerased_lint_store(sess: &Session) -> &LintStore { + let store: &Lrc<_> = sess.lint_store.as_ref().unwrap(); + let store: &dyn Any = &**store; store.downcast_ref().unwrap() } @@ -276,7 +279,7 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) { let generics = self.context.generics.take(); - self.context.generics = Some(&trait_item.generics); + self.context.generics = Some(trait_item.generics); self.with_lint_attrs(trait_item.hir_id(), |cx| { cx.with_param_env(trait_item.owner_id, |cx| { lint_callback!(cx, check_trait_item, trait_item); @@ -288,7 +291,7 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) { let generics = self.context.generics.take(); - self.context.generics = Some(&impl_item.generics); + self.context.generics = Some(impl_item.generics); self.with_lint_attrs(impl_item.hir_id(), |cx| { cx.with_param_env(impl_item.owner_id, |cx| { lint_callback!(cx, check_impl_item, impl_item); @@ -352,9 +355,8 @@ pub fn late_lint_mod<'tcx, T: LateLintPass<'tcx> + 'tcx>( enclosing_body: None, cached_typeck_results: Cell::new(None), param_env: ty::ParamEnv::empty(), - effective_visibilities: &tcx.effective_visibilities(()), - lint_store: unerased_lint_store(tcx), - last_node_with_lint_attrs: tcx.hir().local_def_id_to_hir_id(module_def_id), + effective_visibilities: tcx.effective_visibilities(()), + last_node_with_lint_attrs: tcx.local_def_id_to_hir_id(module_def_id.into()), generics: None, only_module: true, }; @@ -362,8 +364,11 @@ pub fn late_lint_mod<'tcx, T: LateLintPass<'tcx> + 'tcx>( // Note: `passes` is often empty. In that case, it's faster to run // `builtin_lints` directly rather than bundling it up into the // `RuntimeCombinedLateLintPass`. - let mut passes: Vec<_> = - unerased_lint_store(tcx).late_module_passes.iter().map(|mk_pass| (mk_pass)(tcx)).collect(); + let mut passes: Vec<_> = unerased_lint_store(tcx.sess) + .late_module_passes + .iter() + .map(|mk_pass| (mk_pass)(tcx)) + .collect(); if passes.is_empty() { late_lint_mod_inner(tcx, module_def_id, context, builtin_lints); } else { @@ -400,7 +405,7 @@ fn late_lint_mod_inner<'tcx, T: LateLintPass<'tcx>>( fn late_lint_crate<'tcx>(tcx: TyCtxt<'tcx>) { // Note: `passes` is often empty. let mut passes: Vec<_> = - unerased_lint_store(tcx).late_passes.iter().map(|mk_pass| (mk_pass)(tcx)).collect(); + unerased_lint_store(tcx.sess).late_passes.iter().map(|mk_pass| (mk_pass)(tcx)).collect(); if passes.is_empty() { return; @@ -411,8 +416,7 @@ fn late_lint_crate<'tcx>(tcx: TyCtxt<'tcx>) { enclosing_body: None, cached_typeck_results: Cell::new(None), param_env: ty::ParamEnv::empty(), - effective_visibilities: &tcx.effective_visibilities(()), - lint_store: unerased_lint_store(tcx), + effective_visibilities: tcx.effective_visibilities(()), last_node_with_lint_attrs: hir::CRATE_HIR_ID, generics: None, only_module: false, diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 0d20f6232..6eff2bfe1 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -56,7 +56,6 @@ struct LintLevelSets { } rustc_index::newtype_index! { - #[custom_encodable] // we don't need encoding struct LintStackIndex { const COMMAND_LINE = 0; } @@ -123,7 +122,7 @@ impl LintLevelSets { } fn lint_expectations(tcx: TyCtxt<'_>, (): ()) -> Vec<(LintExpectationId, LintExpectation)> { - let store = unerased_lint_store(tcx); + let store = unerased_lint_store(tcx.sess); let mut builder = LintLevelsBuilder { sess: tcx.sess, @@ -138,21 +137,21 @@ fn lint_expectations(tcx: TyCtxt<'_>, (): ()) -> Vec<(LintExpectationId, LintExp }, warn_about_weird_lints: false, store, - registered_tools: &tcx.registered_tools(()), + registered_tools: tcx.registered_tools(()), }; builder.add_command_line(); builder.add_id(hir::CRATE_HIR_ID); tcx.hir().walk_toplevel_module(&mut builder); - tcx.sess.diagnostic().update_unstable_expectation_id(&builder.provider.unstable_to_stable_ids); + tcx.sess.dcx().update_unstable_expectation_id(&builder.provider.unstable_to_stable_ids); builder.provider.expectations } #[instrument(level = "trace", skip(tcx), ret)] fn shallow_lint_levels_on(tcx: TyCtxt<'_>, owner: hir::OwnerId) -> ShallowLintLevelMap { - let store = unerased_lint_store(tcx); + let store = unerased_lint_store(tcx.sess); let attrs = tcx.hir_attrs(owner); let mut levels = LintLevelsBuilder { @@ -167,7 +166,7 @@ fn shallow_lint_levels_on(tcx: TyCtxt<'_>, owner: hir::OwnerId) -> ShallowLintLe }, warn_about_weird_lints: false, store, - registered_tools: &tcx.registered_tools(()), + registered_tools: tcx.registered_tools(()), }; if owner == hir::CRATE_OWNER_ID { @@ -548,10 +547,6 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { self.features } - pub(crate) fn lint_store(&self) -> &LintStore { - self.store - } - fn current_specs(&self) -> &FxHashMap<LintId, LevelAndSource> { self.provider.current_specs() } @@ -609,7 +604,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { let orig_level = level; let lint_flag_val = Symbol::intern(lint_name); - let Ok(ids) = self.store.find_lints(&lint_name) else { + let Ok(ids) = self.store.find_lints(lint_name) else { // errors already handled above continue; }; @@ -633,7 +628,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { /// (e.g. if a forbid was already inserted on the same scope), then emits a /// diagnostic with no change to `specs`. fn insert_spec(&mut self, id: LintId, (mut level, src): LevelAndSource) { - let (old_level, old_src) = self.provider.get_lint_level(id.lint, &self.sess); + let (old_level, old_src) = self.provider.get_lint_level(id.lint, self.sess); if let Level::Expect(id) = &mut level && let LintExpectationId::Stable { .. } = id { @@ -741,7 +736,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { if attr.has_name(sym::doc) && attr .meta_item_list() - .map_or(false, |l| ast::attr::list_contains_name(&l, sym::hidden)) + .is_some_and(|l| ast::attr::list_contains_name(&l, sym::hidden)) { self.insert(LintId::of(MISSING_DOCS), (Level::Allow, LintLevelSource::Default)); continue; @@ -933,12 +928,12 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { DeprecatedLintName { name, suggestion: sp, - replace: &new_lint_name, + replace: new_lint_name, }, ); let src = LintLevelSource::Node { - name: Symbol::intern(&new_lint_name), + name: Symbol::intern(new_lint_name), span: sp, reason, }; @@ -1082,7 +1077,6 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { GateIssue::Language, lint_from_cli, ); - lint }, ); return false; @@ -1099,8 +1093,6 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { /// Used to emit a lint-related diagnostic based on the current state of /// this lint context. /// - /// Return value of the `decorate` closure is ignored, see [`struct_lint_level`] for a detailed explanation. - /// /// [`struct_lint_level`]: rustc_middle::lint::struct_lint_level#decorate-signature #[rustc_lint_diagnostics] #[track_caller] @@ -1109,9 +1101,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { lint: &'static Lint, span: Option<MultiSpan>, msg: impl Into<DiagnosticMessage>, - decorate: impl for<'a, 'b> FnOnce( - &'b mut DiagnosticBuilder<'a, ()>, - ) -> &'b mut DiagnosticBuilder<'a, ()>, + decorate: impl for<'a, 'b> FnOnce(&'b mut DiagnosticBuilder<'a, ()>), ) { let (level, src) = self.lint_level(lint); struct_lint_level(self.sess, lint, level, src, span, msg, decorate) @@ -1126,7 +1116,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { ) { let (level, src) = self.lint_level(lint); struct_lint_level(self.sess, lint, level, src, Some(span), decorate.msg(), |lint| { - decorate.decorate_lint(lint) + decorate.decorate_lint(lint); }); } @@ -1134,7 +1124,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { pub fn emit_lint(&self, lint: &'static Lint, decorate: impl for<'a> DecorateLint<'a, ()>) { let (level, src) = self.lint_level(lint); struct_lint_level(self.sess, lint, level, src, None, decorate.msg(), |lint| { - decorate.decorate_lint(lint) + decorate.decorate_lint(lint); }); } } diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 54adedd3c..066c88cc6 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -27,8 +27,8 @@ #![allow(rustc::potential_query_instability)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] -#![cfg_attr(not(bootstrap), doc(rust_logo))] -#![cfg_attr(not(bootstrap), feature(rustdoc_internals))] +#![doc(rust_logo)] +#![feature(rustdoc_internals)] #![feature(array_windows)] #![feature(box_patterns)] #![feature(control_flow_enum)] @@ -39,6 +39,7 @@ #![feature(min_specialization)] #![feature(never_type)] #![feature(rustc_attrs)] +#![feature(trait_upcasting)] #![recursion_limit = "256"] #![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::diagnostic_outside_of_impl)] @@ -85,18 +86,14 @@ mod redundant_semicolon; mod reference_casting; mod traits; mod types; +mod unit_bindings; mod unused; pub use array_into_iter::ARRAY_INTO_ITER; -use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage}; -use rustc_fluent_macro::fluent_messages; use rustc_hir::def_id::LocalModDefId; 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, -}; use array_into_iter::ArrayIntoIter; use async_fn_in_trait::AsyncFnInTrait; @@ -123,6 +120,7 @@ use redundant_semicolon::*; use reference_casting::*; use traits::*; use types::*; +use unit_bindings::*; use unused::*; /// Useful for other parts of the compiler / Clippy. @@ -136,7 +134,7 @@ pub use rustc_session::lint::Level::{self, *}; pub use rustc_session::lint::{BufferedEarlyLint, FutureIncompatibleInfo, Lint, LintId}; pub use rustc_session::lint::{LintPass, LintVec}; -fluent_messages! { "../messages.ftl" } +rustc_fluent_macro::fluent_messages! { "../messages.ftl" } pub fn provide(providers: &mut Providers) { levels::provide(providers); @@ -179,7 +177,6 @@ early_lint_methods!( IncompleteInternalFeatures: IncompleteInternalFeatures, RedundantSemicolons: RedundantSemicolons, UnusedDocComment: UnusedDocComment, - UnexpectedCfgs: UnexpectedCfgs, ] ] ); @@ -203,6 +200,7 @@ late_lint_methods!( InvalidReferenceCasting: InvalidReferenceCasting, // Depends on referenced function signatures in expressions UnusedResults: UnusedResults, + UnitBindings: UnitBindings, NonUpperCaseGlobals: NonUpperCaseGlobals, NonShorthandFieldPatterns: NonShorthandFieldPatterns, UnusedAllocation: UnusedAllocation, @@ -511,6 +509,11 @@ fn register_builtins(store: &mut LintStore) { "converted into hard error, see PR #104616 \ <https://github.com/rust-lang/rust/pull/104616> for more information", ); + store.register_removed( + "implied_bounds_entailment", + "converted into hard error, see PR #117984 \ + <https://github.com/rust-lang/rust/pull/117984> for more information", + ); } fn register_internals(store: &mut LintStore) { diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 756899e50..9c0d3be03 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -134,12 +134,8 @@ pub struct BuiltinMissingDebugImpl<'a> { // Needed for def_path_str impl<'a> DecorateLint<'a, ()> for BuiltinMissingDebugImpl<'_> { - fn decorate_lint<'b>( - self, - diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>, - ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> { + fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>) { diag.set_arg("debug", self.tcx.def_path_str(self.def_id)); - diag } fn msg(&self) -> DiagnosticMessage { @@ -243,17 +239,13 @@ pub struct BuiltinUngatedAsyncFnTrackCaller<'a> { } impl<'a> DecorateLint<'a, ()> for BuiltinUngatedAsyncFnTrackCaller<'_> { - fn decorate_lint<'b>( - self, - diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>, - ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> { + fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>) { diag.span_label(self.label, fluent::lint_label); rustc_session::parse::add_feature_diagnostics( diag, - &self.parse_sess, + self.parse_sess, sym::async_fn_track_caller, ); - diag } fn msg(&self) -> DiagnosticMessage { @@ -433,10 +425,7 @@ pub struct BuiltinUnpermittedTypeInit<'a> { } impl<'a> DecorateLint<'a, ()> for BuiltinUnpermittedTypeInit<'_> { - fn decorate_lint<'b>( - self, - diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>, - ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> { + fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>) { diag.set_arg("ty", self.ty); diag.span_label(self.label, fluent::lint_builtin_unpermitted_type_init_label); if let InhabitedPredicate::True = self.ty.inhabited_predicate(self.tcx) { @@ -447,7 +436,6 @@ impl<'a> DecorateLint<'a, ()> for BuiltinUnpermittedTypeInit<'_> { ); } self.sub.add_to_diagnostic(diag); - diag } fn msg(&self) -> rustc_errors::DiagnosticMessage { @@ -553,33 +541,21 @@ pub enum BuiltinSpecialModuleNameUsed { Main, } -#[derive(LintDiagnostic)] -#[diag(lint_builtin_unexpected_cli_config_name)] -#[help] -pub struct BuiltinUnexpectedCliConfigName { - pub name: Symbol, -} - -#[derive(LintDiagnostic)] -#[diag(lint_builtin_unexpected_cli_config_value)] -#[help] -pub struct BuiltinUnexpectedCliConfigValue { - pub name: Symbol, - pub value: Symbol, -} - // deref_into_dyn_supertrait.rs #[derive(LintDiagnostic)] #[diag(lint_supertrait_as_deref_target)] pub struct SupertraitAsDerefTarget<'a> { - pub t: Ty<'a>, + pub self_ty: Ty<'a>, + pub supertrait_principal: PolyExistentialTraitRef<'a>, pub target_principal: PolyExistentialTraitRef<'a>, + #[label] + pub label: Span, #[subdiagnostic] - pub label: Option<SupertraitAsDerefTargetLabel>, + pub label2: Option<SupertraitAsDerefTargetLabel>, } #[derive(Subdiagnostic)] -#[label(lint_label)] +#[label(lint_label2)] pub struct SupertraitAsDerefTargetLabel { #[primary_span] pub label: Span, @@ -1170,10 +1146,7 @@ pub struct NonFmtPanicUnused { // Used because of two suggestions based on one Option<Span> impl<'a> DecorateLint<'a, ()> for NonFmtPanicUnused { - fn decorate_lint<'b>( - self, - diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>, - ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> { + fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>) { diag.set_arg("count", self.count); diag.note(fluent::lint_note); if let Some(span) = self.suggestion { @@ -1190,7 +1163,6 @@ impl<'a> DecorateLint<'a, ()> for NonFmtPanicUnused { Applicability::MachineApplicable, ); } - diag } fn msg(&self) -> rustc_errors::DiagnosticMessage { @@ -1369,12 +1341,9 @@ pub struct DropTraitConstraintsDiag<'a> { // Needed for def_path_str impl<'a> DecorateLint<'a, ()> for DropTraitConstraintsDiag<'_> { - fn decorate_lint<'b>( - self, - diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>, - ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> { + fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>) { diag.set_arg("predicate", self.predicate); - diag.set_arg("needs_drop", self.tcx.def_path_str(self.def_id)) + diag.set_arg("needs_drop", self.tcx.def_path_str(self.def_id)); } fn msg(&self) -> rustc_errors::DiagnosticMessage { @@ -1389,11 +1358,8 @@ pub struct DropGlue<'a> { // Needed for def_path_str impl<'a> DecorateLint<'a, ()> for DropGlue<'_> { - fn decorate_lint<'b>( - self, - diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>, - ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> { - diag.set_arg("needs_drop", self.tcx.def_path_str(self.def_id)) + fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>) { + diag.set_arg("needs_drop", self.tcx.def_path_str(self.def_id)); } fn msg(&self) -> rustc_errors::DiagnosticMessage { @@ -1585,6 +1551,76 @@ pub enum InvalidNanComparisonsSuggestion { Spanless, } +#[derive(LintDiagnostic)] +pub enum AmbiguousWidePointerComparisons<'a> { + #[diag(lint_ambiguous_wide_pointer_comparisons)] + Spanful { + #[subdiagnostic] + addr_suggestion: AmbiguousWidePointerComparisonsAddrSuggestion<'a>, + #[subdiagnostic] + addr_metadata_suggestion: Option<AmbiguousWidePointerComparisonsAddrMetadataSuggestion<'a>>, + }, + #[diag(lint_ambiguous_wide_pointer_comparisons)] + #[help(lint_addr_metadata_suggestion)] + #[help(lint_addr_suggestion)] + Spanless, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion( + lint_addr_metadata_suggestion, + style = "verbose", + applicability = "machine-applicable" +)] +pub struct AmbiguousWidePointerComparisonsAddrMetadataSuggestion<'a> { + pub ne: &'a str, + pub deref_left: &'a str, + pub deref_right: &'a str, + #[suggestion_part(code = "{ne}std::ptr::eq({deref_left}")] + pub left: Span, + #[suggestion_part(code = ", {deref_right}")] + pub middle: Span, + #[suggestion_part(code = ")")] + pub right: Span, +} + +#[derive(Subdiagnostic)] +pub enum AmbiguousWidePointerComparisonsAddrSuggestion<'a> { + #[multipart_suggestion( + lint_addr_suggestion, + style = "verbose", + applicability = "machine-applicable" + )] + AddrEq { + ne: &'a str, + deref_left: &'a str, + deref_right: &'a str, + #[suggestion_part(code = "{ne}std::ptr::addr_eq({deref_left}")] + left: Span, + #[suggestion_part(code = ", {deref_right}")] + middle: Span, + #[suggestion_part(code = ")")] + right: Span, + }, + #[multipart_suggestion( + lint_addr_suggestion, + style = "verbose", + applicability = "machine-applicable" + )] + Cast { + deref_left: &'a str, + deref_right: &'a str, + #[suggestion_part(code = "{deref_left}")] + left_before: Option<Span>, + #[suggestion_part(code = " as *const ()")] + left: Span, + #[suggestion_part(code = "{deref_right}")] + right_before: Option<Span>, + #[suggestion_part(code = " as *const ()")] + right: Span, + }, +} + pub struct ImproperCTypes<'a> { pub ty: Ty<'a>, pub desc: &'a str, @@ -1596,10 +1632,7 @@ pub struct ImproperCTypes<'a> { // Used because of the complexity of Option<DiagnosticMessage>, DiagnosticMessage, and Option<Span> impl<'a> DecorateLint<'a, ()> for ImproperCTypes<'_> { - fn decorate_lint<'b>( - self, - diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>, - ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> { + fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>) { diag.set_arg("ty", self.ty); diag.set_arg("desc", self.desc); diag.span_label(self.label, fluent::lint_label); @@ -1610,7 +1643,6 @@ impl<'a> DecorateLint<'a, ()> for ImproperCTypes<'_> { if let Some(note) = self.span_note { diag.span_note(note, fluent::lint_note); } - diag } fn msg(&self) -> rustc_errors::DiagnosticMessage { @@ -1743,10 +1775,7 @@ pub enum UnusedDefSuggestion { // Needed because of def_path_str impl<'a> DecorateLint<'a, ()> for UnusedDef<'_, '_> { - fn decorate_lint<'b>( - self, - diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>, - ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> { + fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>) { diag.set_arg("pre", self.pre); diag.set_arg("post", self.post); diag.set_arg("def", self.cx.tcx.def_path_str(self.def_id)); @@ -1757,7 +1786,6 @@ impl<'a> DecorateLint<'a, ()> for UnusedDef<'_, '_> { if let Some(sugg) = self.suggestion { diag.subdiagnostic(sugg); } - diag } fn msg(&self) -> rustc_errors::DiagnosticMessage { @@ -1830,18 +1858,21 @@ pub struct AsyncFnInTraitDiag { } impl<'a> DecorateLint<'a, ()> for AsyncFnInTraitDiag { - fn decorate_lint<'b>( - self, - diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>, - ) -> &'b mut rustc_errors::DiagnosticBuilder<'a, ()> { + fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>) { diag.note(fluent::lint_note); if let Some(sugg) = self.sugg { diag.multipart_suggestion(fluent::lint_suggestion, sugg, Applicability::MaybeIncorrect); } - diag } fn msg(&self) -> rustc_errors::DiagnosticMessage { fluent::lint_async_fn_in_trait } } + +#[derive(LintDiagnostic)] +#[diag(lint_unit_bindings)] +pub struct UnitBindingsDiag { + #[label] + pub label: Span, +} diff --git a/compiler/rustc_lint/src/non_ascii_idents.rs b/compiler/rustc_lint/src/non_ascii_idents.rs index 62bb8c2c6..4f92fcd71 100644 --- a/compiler/rustc_lint/src/non_ascii_idents.rs +++ b/compiler/rustc_lint/src/non_ascii_idents.rs @@ -204,7 +204,7 @@ impl EarlyLintPass for NonAsciiIdents { // Get the skeleton as a `Symbol`. skeleton_buf.clear(); - skeleton_buf.extend(skeleton(&symbol_str)); + skeleton_buf.extend(skeleton(symbol_str)); let skeleton_sym = if *symbol_str == *skeleton_buf { symbol } else { diff --git a/compiler/rustc_lint/src/non_fmt_panic.rs b/compiler/rustc_lint/src/non_fmt_panic.rs index b218cc578..9fcd70ba0 100644 --- a/compiler/rustc_lint/src/non_fmt_panic.rs +++ b/compiler/rustc_lint/src/non_fmt_panic.rs @@ -126,7 +126,7 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc lint.note(fluent::lint_more_info_note); if !is_arg_inside_call(arg_span, span) { // No clue where this argument is coming from. - return lint; + return; } if arg_macro.is_some_and(|id| cx.tcx.is_diagnostic_item(sym::format_macro, id)) { // A case of `panic!(format!(..))`. @@ -154,17 +154,13 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc let infcx = cx.tcx.infer_ctxt().build(); let suggest_display = is_str - || cx - .tcx - .get_diagnostic_item(sym::Display) - .map(|t| infcx.type_implements_trait(t, [ty], cx.param_env).may_apply()) - == Some(true); + || cx.tcx.get_diagnostic_item(sym::Display).is_some_and(|t| { + infcx.type_implements_trait(t, [ty], cx.param_env).may_apply() + }); let suggest_debug = !suggest_display - && cx - .tcx - .get_diagnostic_item(sym::Debug) - .map(|t| infcx.type_implements_trait(t, [ty], cx.param_env).may_apply()) - == Some(true); + && cx.tcx.get_diagnostic_item(sym::Debug).is_some_and(|t| { + infcx.type_implements_trait(t, [ty], cx.param_env).may_apply() + }); let suggest_panic_any = !is_str && panic == sym::std_panic_macro; @@ -211,7 +207,6 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc } } } - lint }); } diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index 66dc726df..59f27a88a 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -334,7 +334,7 @@ impl<'tcx> LateLintPass<'tcx> for NonSnakeCase { let crate_ident = if let Some(name) = &cx.tcx.sess.opts.crate_name { Some(Ident::from_str(name)) } else { - attr::find_by_name(&cx.tcx.hir().attrs(hir::CRATE_HIR_ID), sym::crate_name) + attr::find_by_name(cx.tcx.hir().attrs(hir::CRATE_HIR_ID), sym::crate_name) .and_then(|attr| attr.meta()) .and_then(|meta| { meta.name_value_literal().and_then(|lit| { @@ -473,7 +473,7 @@ impl NonUpperCaseGlobals { fn check_upper_case(cx: &LateContext<'_>, sort: &str, ident: &Ident) { let name = ident.name.as_str(); if name.chars().any(|c| c.is_lowercase()) { - let uc = NonSnakeCase::to_snake_case(&name).to_uppercase(); + let uc = NonSnakeCase::to_snake_case(name).to_uppercase(); // We cannot provide meaningful suggestions // if the characters are in the category of "Lowercase Letter". let sub = if *name != uc { @@ -520,7 +520,7 @@ impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals { fn check_pat(&mut self, cx: &LateContext<'_>, p: &hir::Pat<'_>) { // Lint for constants that look like binding identifiers (#7526) - if let PatKind::Path(hir::QPath::Resolved(None, ref path)) = p.kind { + if let PatKind::Path(hir::QPath::Resolved(None, path)) = p.kind { if let Res::Def(DefKind::Const, _) = path.res { if path.segments.len() == 1 { NonUpperCaseGlobals::check_upper_case( @@ -534,9 +534,9 @@ impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals { } fn check_generic_param(&mut self, cx: &LateContext<'_>, param: &hir::GenericParam<'_>) { - if let GenericParamKind::Const { .. } = param.kind { - // `rustc_host` params are explicitly allowed to be lowercase. - if cx.tcx.has_attr(param.def_id, sym::rustc_host) { + if let GenericParamKind::Const { is_host_effect, .. } = param.kind { + // `host` params are explicitly allowed to be lowercase. + if is_host_effect { return; } NonUpperCaseGlobals::check_upper_case(cx, "const parameter", ¶m.name.ident()); diff --git a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs index c24846ca9..44b23b8bd 100644 --- a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs +++ b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs @@ -113,7 +113,7 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound { for (assoc_pred, assoc_pred_span) in cx .tcx .explicit_item_bounds(proj.projection_ty.def_id) - .iter_instantiated_copied(cx.tcx, &proj.projection_ty.args) + .iter_instantiated_copied(cx.tcx, proj.projection_ty.args) { let assoc_pred = assoc_pred.fold_with(proj_replacer); let Ok(assoc_pred) = traits::fully_normalize( diff --git a/compiler/rustc_lint/src/pass_by_value.rs b/compiler/rustc_lint/src/pass_by_value.rs index cad2cd7fa..fce750c9b 100644 --- a/compiler/rustc_lint/src/pass_by_value.rs +++ b/compiler/rustc_lint/src/pass_by_value.rs @@ -28,7 +28,7 @@ impl<'tcx> LateLintPass<'tcx> for PassByValue { return; } } - if let Some(t) = path_for_pass_by_value(cx, &inner_ty) { + if let Some(t) = path_for_pass_by_value(cx, inner_ty) { cx.emit_spanned_lint( PASS_BY_VALUE, ty.span, diff --git a/compiler/rustc_lint/src/reference_casting.rs b/compiler/rustc_lint/src/reference_casting.rs index d44691b5e..96290288f 100644 --- a/compiler/rustc_lint/src/reference_casting.rs +++ b/compiler/rustc_lint/src/reference_casting.rs @@ -37,59 +37,73 @@ declare_lint_pass!(InvalidReferenceCasting => [INVALID_REFERENCE_CASTING]); impl<'tcx> LateLintPass<'tcx> for InvalidReferenceCasting { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - let Some((is_assignment, e)) = is_operation_we_care_about(cx, expr) else { - return; - }; - - let init = cx.expr_or_init(e); - - let Some(ty_has_interior_mutability) = is_cast_from_const_to_mut(cx, init) else { - return; - }; - let orig_cast = if init.span != e.span { Some(init.span) } else { None }; - let ty_has_interior_mutability = ty_has_interior_mutability.then_some(()); - - cx.emit_spanned_lint( - INVALID_REFERENCE_CASTING, - expr.span, - if is_assignment { - InvalidReferenceCastingDiag::AssignToRef { orig_cast, ty_has_interior_mutability } - } else { - InvalidReferenceCastingDiag::BorrowAsMut { orig_cast, ty_has_interior_mutability } - }, - ); + if let Some((e, pat)) = borrow_or_assign(cx, expr) { + if matches!(pat, PatternKind::Borrow { mutbl: Mutability::Mut } | PatternKind::Assign) { + let init = cx.expr_or_init(e); + + let Some(ty_has_interior_mutability) = is_cast_from_ref_to_mut_ptr(cx, init) else { + return; + }; + let orig_cast = if init.span != e.span { Some(init.span) } else { None }; + let ty_has_interior_mutability = ty_has_interior_mutability.then_some(()); + + cx.emit_spanned_lint( + INVALID_REFERENCE_CASTING, + expr.span, + if pat == PatternKind::Assign { + InvalidReferenceCastingDiag::AssignToRef { + orig_cast, + ty_has_interior_mutability, + } + } else { + InvalidReferenceCastingDiag::BorrowAsMut { + orig_cast, + ty_has_interior_mutability, + } + }, + ); + } + } } } -fn is_operation_we_care_about<'tcx>( +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum PatternKind { + Borrow { mutbl: Mutability }, + Assign, +} + +fn borrow_or_assign<'tcx>( cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>, -) -> Option<(bool, &'tcx Expr<'tcx>)> { - fn deref_assign_or_addr_of<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<(bool, &'tcx Expr<'tcx>)> { - // &mut <expr> - let inner = if let ExprKind::AddrOf(_, Mutability::Mut, expr) = expr.kind { - expr +) -> Option<(&'tcx Expr<'tcx>, PatternKind)> { + fn deref_assign_or_addr_of<'tcx>( + expr: &'tcx Expr<'tcx>, + ) -> Option<(&'tcx Expr<'tcx>, PatternKind)> { + // &(mut) <expr> + let (inner, pat) = if let ExprKind::AddrOf(_, mutbl, expr) = expr.kind { + (expr, PatternKind::Borrow { mutbl }) // <expr> = ... } else if let ExprKind::Assign(expr, _, _) = expr.kind { - expr + (expr, PatternKind::Assign) // <expr> += ... } else if let ExprKind::AssignOp(_, expr, _) = expr.kind { - expr + (expr, PatternKind::Assign) } else { return None; }; - if let ExprKind::Unary(UnOp::Deref, e) = &inner.kind { - Some((!matches!(expr.kind, ExprKind::AddrOf(..)), e)) - } else { - None - } + // *<inner> + let ExprKind::Unary(UnOp::Deref, e) = &inner.kind else { + return None; + }; + Some((e, pat)) } fn ptr_write<'tcx>( cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>, - ) -> Option<(bool, &'tcx Expr<'tcx>)> { + ) -> Option<(&'tcx Expr<'tcx>, PatternKind)> { if let ExprKind::Call(path, [arg_ptr, _arg_val]) = e.kind && let ExprKind::Path(ref qpath) = path.kind && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() @@ -98,7 +112,7 @@ fn is_operation_we_care_about<'tcx>( Some(sym::ptr_write | sym::ptr_write_volatile | sym::ptr_write_unaligned) ) { - Some((true, arg_ptr)) + Some((arg_ptr, PatternKind::Assign)) } else { None } @@ -107,13 +121,10 @@ fn is_operation_we_care_about<'tcx>( deref_assign_or_addr_of(e).or_else(|| ptr_write(cx, e)) } -fn is_cast_from_const_to_mut<'tcx>( +fn is_cast_from_ref_to_mut_ptr<'tcx>( cx: &LateContext<'tcx>, orig_expr: &'tcx Expr<'tcx>, ) -> Option<bool> { - let mut need_check_freeze = false; - let mut e = orig_expr; - let end_ty = cx.typeck_results().node_type(orig_expr.hir_id); // Bail out early if the end type is **not** a mutable pointer. @@ -121,6 +132,28 @@ fn is_cast_from_const_to_mut<'tcx>( return None; } + let (e, need_check_freeze) = peel_casts(cx, orig_expr); + + let start_ty = cx.typeck_results().node_type(e.hir_id); + if let ty::Ref(_, inner_ty, Mutability::Not) = start_ty.kind() { + // If an UnsafeCell method is involved, we need to additionally check the + // inner type for the presence of the Freeze trait (ie does NOT contain + // an UnsafeCell), since in that case we would incorrectly lint on valid casts. + // + // Except on the presence of non concrete skeleton types (ie generics) + // since there is no way to make it safe for arbitrary types. + let inner_ty_has_interior_mutability = + !inner_ty.is_freeze(cx.tcx, cx.param_env) && inner_ty.has_concrete_skeleton(); + (!need_check_freeze || !inner_ty_has_interior_mutability) + .then_some(inner_ty_has_interior_mutability) + } else { + None + } +} + +fn peel_casts<'tcx>(cx: &LateContext<'tcx>, mut e: &'tcx Expr<'tcx>) -> (&'tcx Expr<'tcx>, bool) { + let mut gone_trough_unsafe_cell_raw_get = false; + loop { e = e.peel_blocks(); // <expr> as ... @@ -145,27 +178,18 @@ fn is_cast_from_const_to_mut<'tcx>( ) { if cx.tcx.is_diagnostic_item(sym::unsafe_cell_raw_get, def_id) { - need_check_freeze = true; + gone_trough_unsafe_cell_raw_get = true; } arg } else { - break; + let init = cx.expr_or_init(e); + if init.hir_id != e.hir_id { + init + } else { + break; + } }; } - let start_ty = cx.typeck_results().node_type(e.hir_id); - if let ty::Ref(_, inner_ty, Mutability::Not) = start_ty.kind() { - // If an UnsafeCell method is involved we need to additionaly check the - // inner type for the presence of the Freeze trait (ie does NOT contain - // an UnsafeCell), since in that case we would incorrectly lint on valid casts. - // - // We also consider non concrete skeleton types (ie generics) - // to be an issue since there is no way to make it safe for abitrary types. - let inner_ty_has_interior_mutability = - !inner_ty.is_freeze(cx.tcx, cx.param_env) && inner_ty.has_concrete_skeleton(); - (!need_check_freeze || !inner_ty_has_interior_mutability) - .then_some(inner_ty_has_interior_mutability) - } else { - None - } + (e, gone_trough_unsafe_cell_raw_get) } diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index c04053d18..6dade43a1 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -1,12 +1,13 @@ use crate::{ fluent_generated as fluent, lints::{ - AtomicOrderingFence, AtomicOrderingLoad, AtomicOrderingStore, ImproperCTypes, - InvalidAtomicOrderingDiag, InvalidNanComparisons, InvalidNanComparisonsSuggestion, - OnlyCastu8ToChar, OverflowingBinHex, OverflowingBinHexSign, OverflowingBinHexSignBitSub, - OverflowingBinHexSub, OverflowingInt, OverflowingIntHelp, OverflowingLiteral, - OverflowingUInt, RangeEndpointOutOfRange, UnusedComparisons, UseInclusiveRange, - VariantSizeDifferencesDiag, + AmbiguousWidePointerComparisons, AmbiguousWidePointerComparisonsAddrMetadataSuggestion, + AmbiguousWidePointerComparisonsAddrSuggestion, AtomicOrderingFence, AtomicOrderingLoad, + AtomicOrderingStore, ImproperCTypes, InvalidAtomicOrderingDiag, InvalidNanComparisons, + InvalidNanComparisonsSuggestion, OnlyCastu8ToChar, OverflowingBinHex, + OverflowingBinHexSign, OverflowingBinHexSignBitSub, OverflowingBinHexSub, OverflowingInt, + OverflowingIntHelp, OverflowingLiteral, OverflowingUInt, RangeEndpointOutOfRange, + UnusedComparisons, UseInclusiveRange, VariantSizeDifferencesDiag, }, }; use crate::{LateContext, LateLintPass, LintContext}; @@ -17,10 +18,10 @@ use rustc_errors::DiagnosticMessage; use rustc_hir as hir; use rustc_hir::{is_range_literal, Expr, ExprKind, Node}; use rustc_middle::ty::layout::{IntegerExt, LayoutOf, SizeSkeleton}; -use rustc_middle::ty::GenericArgsRef; use rustc_middle::ty::{ self, AdtKind, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, }; +use rustc_middle::ty::{GenericArgsRef, TypeAndMut}; use rustc_span::def_id::LocalDefId; use rustc_span::source_map; use rustc_span::symbol::sym; @@ -28,6 +29,7 @@ use rustc_span::{Span, Symbol}; use rustc_target::abi::{Abi, Size, WrappingRange}; use rustc_target::abi::{Integer, TagEncoding, Variants}; use rustc_target::spec::abi::Abi as SpecAbi; +use rustc_type_ir::DynKind; use std::iter; use std::ops::ControlFlow; @@ -136,6 +138,37 @@ declare_lint! { "detects invalid floating point NaN comparisons" } +declare_lint! { + /// The `ambiguous_wide_pointer_comparisons` lint checks comparison + /// of `*const/*mut ?Sized` as the operands. + /// + /// ### Example + /// + /// ```rust + /// # struct A; + /// # struct B; + /// + /// # trait T {} + /// # impl T for A {} + /// # impl T for B {} + /// + /// let ab = (A, B); + /// let a = &ab.0 as *const dyn T; + /// let b = &ab.1 as *const dyn T; + /// + /// let _ = a == b; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The comparison includes metadata which may not be expected. + AMBIGUOUS_WIDE_POINTER_COMPARISONS, + Warn, + "detects ambiguous wide pointer comparisons" +} + #[derive(Copy, Clone)] pub struct TypeLimits { /// Id of the last visited negated expression @@ -144,7 +177,12 @@ pub struct TypeLimits { negated_expr_span: Option<Span>, } -impl_lint_pass!(TypeLimits => [UNUSED_COMPARISONS, OVERFLOWING_LITERALS, INVALID_NAN_COMPARISONS]); +impl_lint_pass!(TypeLimits => [ + UNUSED_COMPARISONS, + OVERFLOWING_LITERALS, + INVALID_NAN_COMPARISONS, + AMBIGUOUS_WIDE_POINTER_COMPARISONS +]); impl TypeLimits { pub fn new() -> TypeLimits { @@ -164,7 +202,7 @@ fn lint_overflowing_range_endpoint<'tcx>( ) -> bool { // Look past casts to support cases like `0..256 as u8` let (expr, lit_span) = if let Node::Expr(par_expr) = - cx.tcx.hir().get(cx.tcx.hir().parent_id(expr.hir_id)) + cx.tcx.hir_node(cx.tcx.hir().parent_id(expr.hir_id)) && let ExprKind::Cast(_, _) = par_expr.kind { (par_expr, expr.span) @@ -175,7 +213,7 @@ fn lint_overflowing_range_endpoint<'tcx>( // We only want to handle exclusive (`..`) ranges, // which are represented as `ExprKind::Struct`. let par_id = cx.tcx.hir().parent_id(expr.hir_id); - let Node::ExprField(field) = cx.tcx.hir().get(par_id) else { return false }; + let Node::ExprField(field) = cx.tcx.hir_node(par_id) else { return false }; let Node::Expr(struct_expr) = cx.tcx.hir().get_parent(field.hir_id) else { return false }; if !is_range_literal(struct_expr) { return false; @@ -460,7 +498,7 @@ fn lint_uint_literal<'tcx>( }; if lit_val < min || lit_val > max { let parent_id = cx.tcx.hir().parent_id(e.hir_id); - if let Node::Expr(par_e) = cx.tcx.hir().get(parent_id) { + if let Node::Expr(par_e) = cx.tcx.hir_node(parent_id) { match par_e.kind { hir::ExprKind::Cast(..) => { if let ty::Char = cx.typeck_results().expr_ty(par_e).kind() { @@ -620,10 +658,110 @@ fn lint_nan<'tcx>( cx.emit_spanned_lint(INVALID_NAN_COMPARISONS, e.span, lint); } +fn lint_wide_pointer<'tcx>( + cx: &LateContext<'tcx>, + e: &'tcx hir::Expr<'tcx>, + binop: hir::BinOpKind, + l: &'tcx hir::Expr<'tcx>, + r: &'tcx hir::Expr<'tcx>, +) { + let ptr_unsized = |mut ty: Ty<'tcx>| -> Option<(usize, bool)> { + let mut refs = 0; + // here we remove any "implicit" references and count the number + // of them to correctly suggest the right number of deref + while let ty::Ref(_, inner_ty, _) = ty.kind() { + ty = *inner_ty; + refs += 1; + } + match ty.kind() { + ty::RawPtr(TypeAndMut { mutbl: _, ty }) => (!ty.is_sized(cx.tcx, cx.param_env)) + .then(|| (refs, matches!(ty.kind(), ty::Dynamic(_, _, DynKind::Dyn)))), + _ => None, + } + }; + + // PartialEq::{eq,ne} takes references, remove any explicit references + let l = l.peel_borrows(); + let r = r.peel_borrows(); + + let Some(l_ty) = cx.typeck_results().expr_ty_opt(l) else { + return; + }; + let Some(r_ty) = cx.typeck_results().expr_ty_opt(r) else { + return; + }; + + let Some((l_ty_refs, l_inner_ty_is_dyn)) = ptr_unsized(l_ty) else { + return; + }; + let Some((r_ty_refs, r_inner_ty_is_dyn)) = ptr_unsized(r_ty) else { + return; + }; + + let (Some(l_span), Some(r_span)) = + (l.span.find_ancestor_inside(e.span), r.span.find_ancestor_inside(e.span)) + else { + return cx.emit_spanned_lint( + AMBIGUOUS_WIDE_POINTER_COMPARISONS, + e.span, + AmbiguousWidePointerComparisons::Spanless, + ); + }; + + let ne = if binop == hir::BinOpKind::Ne { "!" } else { "" }; + let is_eq_ne = matches!(binop, hir::BinOpKind::Eq | hir::BinOpKind::Ne); + let is_dyn_comparison = l_inner_ty_is_dyn && r_inner_ty_is_dyn; + + let left = e.span.shrink_to_lo().until(l_span.shrink_to_lo()); + let middle = l_span.shrink_to_hi().until(r_span.shrink_to_lo()); + let right = r_span.shrink_to_hi().until(e.span.shrink_to_hi()); + + let deref_left = &*"*".repeat(l_ty_refs); + let deref_right = &*"*".repeat(r_ty_refs); + + cx.emit_spanned_lint( + AMBIGUOUS_WIDE_POINTER_COMPARISONS, + e.span, + AmbiguousWidePointerComparisons::Spanful { + addr_metadata_suggestion: (is_eq_ne && !is_dyn_comparison).then(|| { + AmbiguousWidePointerComparisonsAddrMetadataSuggestion { + ne, + deref_left, + deref_right, + left, + middle, + right, + } + }), + addr_suggestion: if is_eq_ne { + AmbiguousWidePointerComparisonsAddrSuggestion::AddrEq { + ne, + deref_left, + deref_right, + left, + middle, + right, + } + } else { + AmbiguousWidePointerComparisonsAddrSuggestion::Cast { + deref_left, + deref_right, + // those two Options are required for correctness as having + // an empty span and an empty suggestion is not permitted + left_before: (l_ty_refs != 0).then_some(left), + right_before: (r_ty_refs != 0).then(|| r_span.shrink_to_lo()), + left: l_span.shrink_to_hi(), + right, + } + }, + }, + ); +} + impl<'tcx> LateLintPass<'tcx> for TypeLimits { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx hir::Expr<'tcx>) { match e.kind { - hir::ExprKind::Unary(hir::UnOp::Neg, ref expr) => { + hir::ExprKind::Unary(hir::UnOp::Neg, expr) => { // Propagate negation, if the negation itself isn't negated if self.negated_expr_id != Some(e.hir_id) { self.negated_expr_id = Some(expr.hir_id); @@ -632,14 +770,30 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits { } hir::ExprKind::Binary(binop, ref l, ref r) => { if is_comparison(binop) { - if !check_limits(cx, binop, &l, &r) { + if !check_limits(cx, binop, l, r) { cx.emit_spanned_lint(UNUSED_COMPARISONS, e.span, UnusedComparisons); } else { lint_nan(cx, e, binop, l, r); + lint_wide_pointer(cx, e, binop.node, l, r); } } } - hir::ExprKind::Lit(ref lit) => lint_literal(cx, self, e, lit), + hir::ExprKind::Lit(lit) => lint_literal(cx, self, e, lit), + hir::ExprKind::Call(path, [l, r]) + if let ExprKind::Path(ref qpath) = path.kind + && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() + && let Some(diag_item) = cx.tcx.get_diagnostic_name(def_id) + && let Some(binop) = partialeq_binop(diag_item) => + { + lint_wide_pointer(cx, e, binop, l, r); + } + hir::ExprKind::MethodCall(_, l, [r], _) + if let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id) + && let Some(diag_item) = cx.tcx.get_diagnostic_name(def_id) + && let Some(binop) = partialeq_binop(diag_item) => + { + lint_wide_pointer(cx, e, binop, l, r); + } _ => {} }; @@ -685,7 +839,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits { ty::Int(int_ty) => { let (min, max) = int_ty_range(int_ty); let lit_val: i128 = match lit.kind { - hir::ExprKind::Lit(ref li) => match li.node { + hir::ExprKind::Lit(li) => match li.node { ast::LitKind::Int( v, ast::LitIntType::Signed(_) | ast::LitIntType::Unsuffixed, @@ -699,7 +853,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits { ty::Uint(uint_ty) => { let (min, max): (u128, u128) = uint_ty_range(uint_ty); let lit_val: u128 = match lit.kind { - hir::ExprKind::Lit(ref li) => match li.node { + hir::ExprKind::Lit(li) => match li.node { ast::LitKind::Int(v, _) => v, _ => return true, }, @@ -722,6 +876,16 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits { | hir::BinOpKind::Gt ) } + + fn partialeq_binop(diag_item: Symbol) -> Option<hir::BinOpKind> { + if diag_item == sym::cmp_partialeq_eq { + Some(hir::BinOpKind::Eq) + } else if diag_item == sym::cmp_partialeq_ne { + Some(hir::BinOpKind::Ne) + } else { + None + } + } } } @@ -1015,7 +1179,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { // We can't completely trust `repr(C)` markings, so make sure the fields are actually safe. let mut all_phantom = !variant.fields.is_empty(); for field in &variant.fields { - all_phantom &= match self.check_field_type_for_ffi(cache, &field, args) { + all_phantom &= match self.check_field_type_for_ffi(cache, field, args) { FfiSafe => false, // `()` fields are FFI-safe! FfiUnsafe { ty, .. } if ty.is_unit() => false, @@ -1234,7 +1398,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { }; } - let sig = tcx.erase_late_bound_regions(sig); + let sig = tcx.instantiate_bound_regions_with_erased(sig); for arg in sig.inputs() { match self.check_type_for_ffi(cache, *arg) { FfiSafe => {} @@ -1391,7 +1555,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { /// types that have external ABIs, as these still need checked. fn check_fn(&mut self, def_id: LocalDefId, decl: &'tcx hir::FnDecl<'_>) { let sig = self.cx.tcx.fn_sig(def_id).instantiate_identity(); - let sig = self.cx.tcx.erase_late_bound_regions(sig); + let sig = self.cx.tcx.instantiate_bound_regions_with_erased(sig); for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) { for (fn_ptr_ty, span) in self.find_fn_ptr_ty_with_external_abi(input_hir, *input_ty) { @@ -1399,7 +1563,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } } - if let hir::FnRetTy::Return(ref ret_hir) = decl.output { + if let hir::FnRetTy::Return(ret_hir) = decl.output { for (fn_ptr_ty, span) in self.find_fn_ptr_ty_with_external_abi(ret_hir, sig.output()) { self.check_type_for_ffi_and_report_errors(span, fn_ptr_ty, false, true); } @@ -1409,13 +1573,13 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { /// Check if a function's argument types and result type are "ffi-safe". fn check_foreign_fn(&mut self, def_id: LocalDefId, decl: &'tcx hir::FnDecl<'_>) { let sig = self.cx.tcx.fn_sig(def_id).instantiate_identity(); - let sig = self.cx.tcx.erase_late_bound_regions(sig); + let sig = self.cx.tcx.instantiate_bound_regions_with_erased(sig); for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) { self.check_type_for_ffi_and_report_errors(input_hir.span, *input_ty, false, false); } - if let hir::FnRetTy::Return(ref ret_hir) = decl.output { + if let hir::FnRetTy::Return(ret_hir) = decl.output { self.check_type_for_ffi_and_report_errors(ret_hir.span, sig.output(), false, true); } } @@ -1487,13 +1651,13 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDeclarations { let abi = cx.tcx.hir().get_foreign_abi(it.hir_id()); match it.kind { - hir::ForeignItemKind::Fn(ref decl, _, _) if !vis.is_internal_abi(abi) => { + hir::ForeignItemKind::Fn(decl, _, _) if !vis.is_internal_abi(abi) => { vis.check_foreign_fn(it.owner_id.def_id, decl); } - hir::ForeignItemKind::Static(ref ty, _) if !vis.is_internal_abi(abi) => { + hir::ForeignItemKind::Static(ty, _) if !vis.is_internal_abi(abi) => { vis.check_foreign_static(it.owner_id, ty.span); } - hir::ForeignItemKind::Fn(ref decl, _, _) => vis.check_fn(it.owner_id.def_id, decl), + hir::ForeignItemKind::Fn(decl, _, _) => vis.check_fn(it.owner_id.def_id, decl), hir::ForeignItemKind::Static(..) | hir::ForeignItemKind::Type => (), } } @@ -1705,7 +1869,7 @@ impl InvalidAtomicOrdering { sym::AtomicI64, sym::AtomicI128, ]; - if let ExprKind::MethodCall(ref method_path, _, args, _) = &expr.kind + if let ExprKind::MethodCall(method_path, _, args, _) = &expr.kind && recognized_names.contains(&method_path.ident.name) && let Some(m_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) && let Some(impl_did) = cx.tcx.impl_of_method(m_def_id) @@ -1766,7 +1930,7 @@ impl InvalidAtomicOrdering { } fn check_memory_fence(cx: &LateContext<'_>, expr: &Expr<'_>) { - if let ExprKind::Call(ref func, ref args) = expr.kind + if let ExprKind::Call(func, args) = expr.kind && let ExprKind::Path(ref func_qpath) = func.kind && let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id() && matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::fence | sym::compiler_fence)) diff --git a/compiler/rustc_lint/src/unit_bindings.rs b/compiler/rustc_lint/src/unit_bindings.rs new file mode 100644 index 000000000..c80889f3a --- /dev/null +++ b/compiler/rustc_lint/src/unit_bindings.rs @@ -0,0 +1,72 @@ +use crate::lints::UnitBindingsDiag; +use crate::{LateLintPass, LintContext}; +use rustc_hir as hir; +use rustc_middle::ty::Ty; + +declare_lint! { + /// The `unit_bindings` lint detects cases where bindings are useless because they have + /// the unit type `()` as their inferred type. The lint is suppressed if the user explicitly + /// annotates the let binding with the unit type `()`, or if the let binding uses an underscore + /// wildcard pattern, i.e. `let _ = expr`, or if the binding is produced from macro expansions. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(unit_bindings)] + /// + /// fn foo() { + /// println!("do work"); + /// } + /// + /// pub fn main() { + /// let x = foo(); // useless binding + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Creating a local binding with the unit type `()` does not do much and can be a sign of a + /// user error, such as in this example: + /// + /// ```rust,no_run + /// fn main() { + /// let mut x = [1, 2, 3]; + /// x[0] = 5; + /// let y = x.sort(); // useless binding as `sort` returns `()` and not the sorted array. + /// println!("{:?}", y); // prints "()" + /// } + /// ``` + pub UNIT_BINDINGS, + Allow, + "binding is useless because it has the unit `()` type" +} + +declare_lint_pass!(UnitBindings => [UNIT_BINDINGS]); + +impl<'tcx> LateLintPass<'tcx> for UnitBindings { + fn check_local(&mut self, cx: &crate::LateContext<'tcx>, local: &'tcx hir::Local<'tcx>) { + // Suppress warning if user: + // - explicitly ascribes a type to the pattern + // - explicitly wrote `let pat = ();` + // - explicitly wrote `let () = init;`. + if !local.span.from_expansion() + && let Some(tyck_results) = cx.maybe_typeck_results() + && let Some(init) = local.init + && let init_ty = tyck_results.expr_ty(init) + && let local_ty = tyck_results.node_type(local.hir_id) + && init_ty == Ty::new_unit(cx.tcx) + && local_ty == Ty::new_unit(cx.tcx) + && local.ty.is_none() + && !matches!(init.kind, hir::ExprKind::Tup([])) + && !matches!(local.pat.kind, hir::PatKind::Tuple([], ..)) + { + cx.emit_spanned_lint( + UNIT_BINDINGS, + local.span, + UnitBindingsDiag { label: local.pat.span }, + ); + } + } +} diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 355855b8e..34cdee4ec 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -111,7 +111,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { } if let hir::ExprKind::Match(await_expr, _arms, hir::MatchSource::AwaitDesugar) = expr.kind - && let ty = cx.typeck_results().expr_ty(&await_expr) + && let ty = cx.typeck_results().expr_ty(await_expr) && let ty::Alias(ty::Opaque, ty::AliasTy { def_id: future_def_id, .. }) = ty.kind() && cx.tcx.ty_is_opaque_future(ty) && let async_fn_def_id = cx.tcx.parent(*future_def_id) @@ -132,9 +132,9 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { return; } - let ty = cx.typeck_results().expr_ty(&expr); + let ty = cx.typeck_results().expr_ty(expr); - let must_use_result = is_ty_must_use(cx, ty, &expr, expr.span); + let must_use_result = is_ty_must_use(cx, ty, expr, expr.span); let type_lint_emitted_or_suppressed = match must_use_result { Some(path) => { emit_must_use_untranslated(cx, &path, "", "", 1, false, expr_is_from_block); @@ -211,7 +211,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { expr_is_from_block: bool, ) -> bool { let maybe_def_id = match expr.kind { - hir::ExprKind::Call(ref callee, _) => { + hir::ExprKind::Call(callee, _) => { match callee.kind { hir::ExprKind::Path(ref qpath) => { match cx.qpath_res(qpath, callee.hir_id) { @@ -251,6 +251,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { /// The root of the normal must_use lint with an optional message. Def(Span, DefId, Option<Symbol>), Boxed(Box<Self>), + Pinned(Box<Self>), Opaque(Box<Self>), TraitObject(Box<Self>), TupleElement(Vec<(usize, Self)>), @@ -284,8 +285,13 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { is_ty_must_use(cx, boxed_ty, expr, span) .map(|inner| MustUsePath::Boxed(Box::new(inner))) } + ty::Adt(def, args) if cx.tcx.lang_items().pin_type() == Some(def.did()) => { + let pinned_ty = args.type_at(0); + is_ty_must_use(cx, pinned_ty, expr, span) + .map(|inner| MustUsePath::Pinned(Box::new(inner))) + } ty::Adt(def, _) => is_def_must_use(cx, def.did(), span), - ty::Alias(ty::Opaque, ty::AliasTy { def_id: def, .. }) => { + ty::Alias(ty::Opaque | ty::Projection, ty::AliasTy { def_id: def, .. }) => { elaborate( cx.tcx, cx.tcx.explicit_item_bounds(def).instantiate_identity_iter_copied(), @@ -425,6 +431,18 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { expr_is_from_block, ); } + MustUsePath::Pinned(path) => { + let descr_pre = &format!("{descr_pre}pinned "); + emit_must_use_untranslated( + cx, + path, + descr_pre, + descr_post, + plural_len, + true, + expr_is_from_block, + ); + } MustUsePath::Opaque(path) => { let descr_pre = &format!("{descr_pre}implementer{plural_suffix} of "); emit_must_use_untranslated( @@ -638,7 +656,7 @@ trait UnusedDelimLint { ) -> bool { if followed_by_else { match inner.kind { - ast::ExprKind::Binary(op, ..) if op.node.lazy() => return true, + ast::ExprKind::Binary(op, ..) if op.node.is_lazy() => return true, _ if classify::expr_trailing_brace(inner).is_some() => return true, _ => {} } @@ -674,7 +692,7 @@ trait UnusedDelimLint { innermost = match &innermost.kind { ExprKind::AddrOf(_, _, expr) => expr, _ => { - if parser::contains_exterior_struct_lit(&innermost) { + if parser::contains_exterior_struct_lit(innermost) { return true; } else { break; @@ -703,7 +721,7 @@ trait UnusedDelimLint { return matches!(rhs.kind, ExprKind::Block(..)); } - _ => return parser::contains_exterior_struct_lit(&inner), + _ => return parser::contains_exterior_struct_lit(inner), } } } @@ -878,7 +896,7 @@ trait UnusedDelimLint { }; self.check_unused_delims_expr( cx, - &value, + value, ctx, followed_by_block, left_pos, @@ -901,7 +919,7 @@ trait UnusedDelimLint { StmtKind::Expr(ref expr) => { self.check_unused_delims_expr( cx, - &expr, + expr, UnusedDelimsCtx::BlockRetValue, false, None, @@ -998,7 +1016,7 @@ impl UnusedDelimLint for UnusedParens { rustc_span::source_map::Spanned { node, .. }, _, _, - ) if node.lazy())) + ) if node.is_lazy())) { self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos, is_kw) } @@ -1095,15 +1113,17 @@ impl EarlyLintPass for UnusedParens { } ExprKind::Match(ref _expr, ref arm) => { for a in arm { - self.check_unused_delims_expr( - cx, - &a.body, - UnusedDelimsCtx::MatchArmExpr, - false, - None, - None, - true, - ); + if let Some(body) = &a.body { + self.check_unused_delims_expr( + cx, + body, + UnusedDelimsCtx::MatchArmExpr, + false, + None, + None, + true, + ); + } } } _ => {} @@ -1136,7 +1156,7 @@ impl EarlyLintPass for UnusedParens { // Do not lint on `(..)` as that will result in the other arms being useless. Paren(_) // The other cases do not contain sub-patterns. - | Wild | Rest | Lit(..) | MacCall(..) | Range(..) | Ident(.., None) | Path(..) => {}, + | Wild | Never | Rest | Lit(..) | MacCall(..) | Range(..) | Ident(.., None) | Path(..) => {}, // These are list-like patterns; parens can always be removed. TupleStruct(_, _, ps) | Tuple(ps) | Slice(ps) | Or(ps) => for p in ps { self.check_unused_parens_pat(cx, p, false, false, keep_space); |