diff options
Diffstat (limited to '')
20 files changed, 944 insertions, 800 deletions
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index d425adf47..c6c7caa7c 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -25,6 +25,7 @@ use crate::{ types::{transparent_newtype_field, CItemKind}, EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext, }; +use hir::IsAsync; use rustc_ast::attr; use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_ast::visit::{FnCtxt, FnKind}; @@ -40,7 +41,10 @@ use rustc_feature::{deprecated_attributes, AttributeGate, BuiltinAttribute, Gate use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdSet, CRATE_DEF_ID}; -use rustc_hir::{ForeignItemKind, GenericParamKind, HirId, PatKind, PredicateOrigin}; +use rustc_hir::intravisit::FnKind as HirFnKind; +use rustc_hir::{ + Body, FnDecl, ForeignItemKind, GenericParamKind, HirId, Node, PatKind, PredicateOrigin, +}; use rustc_index::vec::Idx; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::layout::{LayoutError, LayoutOf}; @@ -52,7 +56,7 @@ use rustc_span::edition::Edition; use rustc_span::source_map::Spanned; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{BytePos, InnerSpan, Span}; -use rustc_target::abi::VariantIdx; +use rustc_target::abi::{Abi, VariantIdx}; use rustc_trait_selection::traits::{self, misc::can_type_implement_copy}; use crate::nonstandard_style::{method_context, MethodLateContext}; @@ -98,9 +102,10 @@ fn pierce_parens(mut expr: &ast::Expr) -> &ast::Expr { impl EarlyLintPass for WhileTrue { fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { if let ast::ExprKind::While(cond, _, label) = &e.kind - && let ast::ExprKind::Lit(ref lit) = pierce_parens(cond).kind - && let ast::LitKind::Bool(true) = lit.kind - && !lit.span.from_expansion() + && let cond = pierce_parens(cond) + && let ast::ExprKind::Lit(token_lit) = cond.kind + && let token::Lit { kind: token::Bool, symbol: kw::True, .. } = token_lit + && !cond.span.from_expansion() { let condition_span = e.span.with_hi(cond.span.hi()); cx.struct_span_lint( @@ -185,9 +190,8 @@ impl<'tcx> LateLintPass<'tcx> for BoxPointers { // If it's a struct, we also have to check the fields' types match it.kind { hir::ItemKind::Struct(ref struct_def, _) | hir::ItemKind::Union(ref struct_def, _) => { - for struct_field in struct_def.fields() { - let def_id = cx.tcx.hir().local_def_id(struct_field.hir_id); - self.check_heap_type(cx, struct_field.span, cx.tcx.type_of(def_id)); + for field in struct_def.fields() { + self.check_heap_type(cx, field.span, cx.tcx.type_of(field.def_id)); } } _ => (), @@ -259,7 +263,7 @@ impl<'tcx> LateLintPass<'tcx> for NonShorthandFieldPatterns { } if let PatKind::Binding(binding_annot, _, ident, None) = fieldpat.pat.kind { if cx.tcx.find_field_index(ident, &variant) - == Some(cx.tcx.field_index(fieldpat.hir_id, cx.typeck_results())) + == Some(cx.typeck_results().field_index(fieldpat.hir_id)) { cx.struct_span_lint( NON_SHORTHAND_FIELD_PATTERNS, @@ -673,13 +677,12 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { fn check_field_def(&mut self, cx: &LateContext<'_>, sf: &hir::FieldDef<'_>) { if !sf.is_positional() { - let def_id = cx.tcx.hir().local_def_id(sf.hir_id); - self.check_missing_docs_attrs(cx, def_id, "a", "struct field") + self.check_missing_docs_attrs(cx, sf.def_id, "a", "struct field") } } fn check_variant(&mut self, cx: &LateContext<'_>, v: &hir::Variant<'_>) { - self.check_missing_docs_attrs(cx, cx.tcx.hir().local_def_id(v.id), "a", "variant"); + self.check_missing_docs_attrs(cx, v.def_id, "a", "variant"); } } @@ -1269,10 +1272,10 @@ declare_lint_pass!(MutableTransmutes => [MUTABLE_TRANSMUTES]); impl<'tcx> LateLintPass<'tcx> for MutableTransmutes { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) { - if let Some((&ty::Ref(_, _, from_mt), &ty::Ref(_, _, to_mt))) = + if let Some((&ty::Ref(_, _, from_mutbl), &ty::Ref(_, _, to_mutbl))) = get_transmute_from_to(cx, expr).map(|(ty1, ty2)| (ty1.kind(), ty2.kind())) { - if to_mt == hir::Mutability::Mut && from_mt == hir::Mutability::Not { + if from_mutbl < to_mutbl { cx.struct_span_lint( MUTABLE_TRANSMUTES, expr.span, @@ -1339,6 +1342,72 @@ impl<'tcx> LateLintPass<'tcx> for UnstableFeatures { } declare_lint! { + /// The `ungated_async_fn_track_caller` lint warns when the + /// `#[track_caller]` attribute is used on an async function, method, or + /// closure, without enabling the corresponding unstable feature flag. + /// + /// ### Example + /// + /// ```rust + /// #[track_caller] + /// async fn foo() {} + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The attribute must be used in conjunction with the + /// [`closure_track_caller` feature flag]. Otherwise, the `#[track_caller]` + /// annotation will function as as no-op. + /// + /// [`closure_track_caller` feature flag]: https://doc.rust-lang.org/beta/unstable-book/language-features/closure-track-caller.html + UNGATED_ASYNC_FN_TRACK_CALLER, + Warn, + "enabling track_caller on an async fn is a no-op unless the closure_track_caller feature is enabled" +} + +declare_lint_pass!( + /// Explains corresponding feature flag must be enabled for the `#[track_caller] attribute to + /// do anything + UngatedAsyncFnTrackCaller => [UNGATED_ASYNC_FN_TRACK_CALLER] +); + +impl<'tcx> LateLintPass<'tcx> for UngatedAsyncFnTrackCaller { + fn check_fn( + &mut self, + cx: &LateContext<'_>, + fn_kind: HirFnKind<'_>, + _: &'tcx FnDecl<'_>, + _: &'tcx Body<'_>, + span: Span, + hir_id: HirId, + ) { + if fn_kind.asyncness() == IsAsync::Async + && !cx.tcx.features().closure_track_caller + && let attrs = cx.tcx.hir().attrs(hir_id) + // Now, check if the function has the `#[track_caller]` attribute + && let Some(attr) = attrs.iter().find(|attr| attr.has_name(sym::track_caller)) + { + cx.struct_span_lint( + UNGATED_ASYNC_FN_TRACK_CALLER, + attr.span, + fluent::lint_ungated_async_fn_track_caller, + |lint| { + lint.span_label(span, fluent::label); + rustc_session::parse::add_feature_diagnostics( + lint, + &cx.tcx.sess.parse_sess, + sym::closure_track_caller, + ); + lint + }, + ); + } + } +} + +declare_lint! { /// The `unreachable_pub` lint triggers for `pub` items not reachable from /// the crate root. /// @@ -1423,8 +1492,11 @@ impl<'tcx> LateLintPass<'tcx> for UnreachablePub { } fn check_field_def(&mut self, cx: &LateContext<'_>, field: &hir::FieldDef<'_>) { - let def_id = cx.tcx.hir().local_def_id(field.hir_id); - self.perform_lint(cx, "field", def_id, field.vis_span, false); + let map = cx.tcx.hir(); + if matches!(map.get(map.get_parent_node(field.hir_id)), Node::Variant(_)) { + return; + } + self.perform_lint(cx, "field", field.def_id, field.vis_span, false); } fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) { @@ -1636,19 +1708,20 @@ 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::TypeVisitable; + use rustc_middle::ty::Clause; use rustc_middle::ty::PredicateKind::*; if cx.tcx.features().trivial_bounds { let predicates = cx.tcx.predicates_of(item.owner_id); for &(predicate, span) in predicates.predicates { let predicate_kind_name = match predicate.kind().skip_binder() { - Trait(..) => "trait", - TypeOutlives(..) | - RegionOutlives(..) => "lifetime", + Clause(Clause::Trait(..)) => "trait", + Clause(Clause::TypeOutlives(..)) | + Clause(Clause::RegionOutlives(..)) => "lifetime", // Ignore projections, as they can only be global // if the trait bound is global - Projection(..) | + Clause(Clause::Projection(..)) | // Ignore bounds that a user can't type WellFormed(..) | ObjectSafe(..) | @@ -1657,6 +1730,7 @@ impl<'tcx> LateLintPass<'tcx> for TrivialConstraints { Coerce(..) | ConstEvaluatable(..) | ConstEquate(..) | + Ambiguous | TypeWellFormedFromEnv(..) => continue, }; if predicate.is_global() { @@ -2028,10 +2102,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.inner_tokens()); + self.check_tokens(cx, mac_def.body.tokens.clone()); } fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &ast::MacCall) { - self.check_tokens(cx, mac.args.inner_tokens()); + self.check_tokens(cx, mac.args.tokens.clone()); } fn check_ident(&mut self, cx: &EarlyContext<'_>, ident: Ident) { self.check_ident_token(cx, UnderMacro(false), ident); @@ -2042,13 +2116,13 @@ declare_lint_pass!(ExplicitOutlivesRequirements => [EXPLICIT_OUTLIVES_REQUIREMEN impl ExplicitOutlivesRequirements { fn lifetimes_outliving_lifetime<'tcx>( - inferred_outlives: &'tcx [(ty::Predicate<'tcx>, Span)], + inferred_outlives: &'tcx [(ty::Clause<'tcx>, Span)], def_id: DefId, ) -> Vec<ty::Region<'tcx>> { inferred_outlives .iter() - .filter_map(|(pred, _)| match pred.kind().skip_binder() { - ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate(a, b)) => match *a { + .filter_map(|(clause, _)| match *clause { + ty::Clause::RegionOutlives(ty::OutlivesPredicate(a, b)) => match *a { ty::ReEarlyBound(ebr) if ebr.def_id == def_id => Some(b), _ => None, }, @@ -2058,13 +2132,13 @@ impl ExplicitOutlivesRequirements { } fn lifetimes_outliving_type<'tcx>( - inferred_outlives: &'tcx [(ty::Predicate<'tcx>, Span)], + inferred_outlives: &'tcx [(ty::Clause<'tcx>, Span)], index: u32, ) -> Vec<ty::Region<'tcx>> { inferred_outlives .iter() - .filter_map(|(pred, _)| match pred.kind().skip_binder() { - ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(a, b)) => { + .filter_map(|(clause, _)| match *clause { + ty::Clause::TypeOutlives(ty::OutlivesPredicate(a, b)) => { a.is_param(index).then_some(b) } _ => None, @@ -2405,8 +2479,34 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue { } /// Information about why a type cannot be initialized this way. - /// Contains an error message and optionally a span to point at. - type InitError = (String, Option<Span>); + struct InitError { + message: String, + /// Spans from struct fields and similar that can be obtained from just the type. + span: Option<Span>, + /// Used to report a trace through adts. + nested: Option<Box<InitError>>, + } + impl InitError { + fn spanned(self, span: Span) -> InitError { + Self { span: Some(span), ..self } + } + + fn nested(self, nested: impl Into<Option<InitError>>) -> InitError { + assert!(self.nested.is_none()); + Self { nested: nested.into().map(Box::new), ..self } + } + } + + impl<'a> From<&'a str> for InitError { + fn from(s: &'a str) -> Self { + s.to_owned().into() + } + } + impl From<String> for InitError { + fn from(message: String) -> Self { + Self { message, span: None, nested: None } + } + } /// Test if this constant is all-0. fn is_zero(expr: &hir::Expr<'_>) -> bool { @@ -2462,25 +2562,54 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue { fn variant_find_init_error<'tcx>( cx: &LateContext<'tcx>, + ty: Ty<'tcx>, variant: &VariantDef, substs: ty::SubstsRef<'tcx>, descr: &str, init: InitKind, ) -> Option<InitError> { - variant.fields.iter().find_map(|field| { - ty_find_init_error(cx, field.ty(cx.tcx, substs), init).map(|(mut msg, span)| { - if span.is_none() { - // Point to this field, should be helpful for figuring - // out where the source of the error is. - let span = cx.tcx.def_span(field.did); - write!(&mut msg, " (in this {descr})").unwrap(); - (msg, Some(span)) + let mut field_err = variant.fields.iter().find_map(|field| { + ty_find_init_error(cx, field.ty(cx.tcx, substs), init).map(|mut err| { + if !field.did.is_local() { + err + } else if err.span.is_none() { + err.span = Some(cx.tcx.def_span(field.did)); + write!(&mut err.message, " (in this {descr})").unwrap(); + err } else { - // Just forward. - (msg, span) + InitError::from(format!("in this {descr}")) + .spanned(cx.tcx.def_span(field.did)) + .nested(err) } }) - }) + }); + + // Check if this ADT has a constrained layout (like `NonNull` and friends). + if let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(ty)) { + if let Abi::Scalar(scalar) | Abi::ScalarPair(scalar, _) = &layout.abi { + let range = scalar.valid_range(cx); + let msg = if !range.contains(0) { + "must be non-null" + } else if init == InitKind::Uninit && !scalar.is_always_valid(cx) { + // Prefer reporting on the fields over the entire struct for uninit, + // as the information bubbles out and it may be unclear why the type can't + // be null from just its outside signature. + + "must be initialized inside its custom valid range" + } else { + return field_err; + }; + if let Some(field_err) = &mut field_err { + // Most of the time, if the field error is the same as the struct error, + // the struct error only happens because of the field error. + if field_err.message.contains(msg) { + field_err.message = format!("because {}", field_err.message); + } + } + return Some(InitError::from(format!("`{ty}` {msg}")).nested(field_err)); + } + } + field_err } /// Return `Some` only if we are sure this type does *not* @@ -2493,63 +2622,36 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue { use rustc_type_ir::sty::TyKind::*; match ty.kind() { // Primitive types that don't like 0 as a value. - Ref(..) => Some(("references must be non-null".to_string(), None)), - Adt(..) if ty.is_box() => Some(("`Box` must be non-null".to_string(), None)), - FnPtr(..) => Some(("function pointers must be non-null".to_string(), None)), - Never => Some(("the `!` type has no valid value".to_string(), None)), + Ref(..) => Some("references must be non-null".into()), + Adt(..) if ty.is_box() => Some("`Box` must be non-null".into()), + FnPtr(..) => Some("function pointers must be non-null".into()), + Never => Some("the `!` type has no valid value".into()), RawPtr(tm) if matches!(tm.ty.kind(), Dynamic(..)) => // raw ptr to dyn Trait { - Some(("the vtable of a wide raw pointer must be non-null".to_string(), None)) + Some("the vtable of a wide raw pointer must be non-null".into()) } // Primitive types with other constraints. Bool if init == InitKind::Uninit => { - Some(("booleans must be either `true` or `false`".to_string(), None)) + Some("booleans must be either `true` or `false`".into()) } Char if init == InitKind::Uninit => { - Some(("characters must be a valid Unicode codepoint".to_string(), None)) + Some("characters must be a valid Unicode codepoint".into()) } Int(_) | Uint(_) if init == InitKind::Uninit => { - Some(("integers must not be uninitialized".to_string(), None)) - } - Float(_) if init == InitKind::Uninit => { - Some(("floats must not be uninitialized".to_string(), None)) + Some("integers must be initialized".into()) } + Float(_) if init == InitKind::Uninit => Some("floats must be initialized".into()), RawPtr(_) if init == InitKind::Uninit => { - Some(("raw pointers must not be uninitialized".to_string(), None)) + Some("raw pointers must be initialized".into()) } // Recurse and checks for some compound types. (but not unions) Adt(adt_def, substs) if !adt_def.is_union() => { - // First check if this ADT has a layout attribute (like `NonNull` and friends). - use std::ops::Bound; - match cx.tcx.layout_scalar_valid_range(adt_def.did()) { - // We exploit here that `layout_scalar_valid_range` will never - // return `Bound::Excluded`. (And we have tests checking that we - // handle the attribute correctly.) - // We don't add a span since users cannot declare such types anyway. - (Bound::Included(lo), Bound::Included(hi)) if 0 < lo && lo < hi => { - return Some((format!("`{}` must be non-null", ty), None)); - } - (Bound::Included(lo), Bound::Unbounded) if 0 < lo => { - return Some((format!("`{}` must be non-null", ty), None)); - } - (Bound::Included(_), _) | (_, Bound::Included(_)) - if init == InitKind::Uninit => - { - return Some(( - format!( - "`{}` must be initialized inside its custom valid range", - ty, - ), - None, - )); - } - _ => {} - } // Handle structs. if adt_def.is_struct() { return variant_find_init_error( cx, + ty, adt_def.non_enum_variant(), substs, "struct field", @@ -2573,13 +2675,14 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue { Some((variant, definitely_inhabited)) }); let Some(first_variant) = potential_variants.next() else { - return Some(("enums with no inhabited variants have no valid value".to_string(), Some(span))); + return Some(InitError::from("enums with no inhabited variants have no valid value").spanned(span)); }; // So we have at least one potentially inhabited variant. Might we have two? let Some(second_variant) = potential_variants.next() else { // There is only one potentially inhabited variant. So we can recursively check that variant! return variant_find_init_error( cx, + ty, &first_variant.0, substs, "field of the only potentially inhabited enum variant", @@ -2597,10 +2700,9 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue { .filter(|(_variant, definitely_inhabited)| *definitely_inhabited) .count(); if definitely_inhabited > 1 { - return Some(( - "enums with multiple inhabited variants have to be initialized to a variant".to_string(), - Some(span), - )); + return Some(InitError::from( + "enums with multiple inhabited variants have to be initialized to a variant", + ).spanned(span)); } } // We couldn't find anything wrong here. @@ -2629,8 +2731,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue { // using zeroed or uninitialized memory. // We are extremely conservative with what we warn about. let conjured_ty = cx.typeck_results().expr_ty(expr); - if let Some((msg, span)) = - with_no_trimmed_paths!(ty_find_init_error(cx, conjured_ty, init)) + if let Some(mut err) = with_no_trimmed_paths!(ty_find_init_error(cx, conjured_ty, init)) { // FIXME(davidtwco): make translatable cx.struct_span_lint( @@ -2656,10 +2757,17 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue { "help: use `MaybeUninit<T>` instead, \ and only call `assume_init` after initialization is done", ); - if let Some(span) = span { - lint.span_note(span, &msg); - } else { - lint.note(&msg); + loop { + if let Some(span) = err.span { + lint.span_note(span, &err.message); + } else { + lint.note(&err.message); + } + if let Some(e) = err.nested { + err = *e; + } else { + break; + } } lint }, diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index cec0003ff..e6a0d7e60 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -206,7 +206,7 @@ impl LintStore { self.late_module_passes.push(Box::new(pass)); } - // Helper method for register_early/late_pass + /// Helper method for register_early/late_pass pub fn register_lints(&mut self, lints: &[&'static Lint]) { for lint in lints { self.lints.push(lint); @@ -579,6 +579,7 @@ pub trait LintContext: Sized { /// 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_with_diagnostics( &self, lint: &'static Lint, @@ -882,6 +883,7 @@ pub trait LintContext: Sized { /// 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>>( &self, lint: &'static Lint, @@ -908,6 +910,7 @@ pub trait LintContext: Sized { /// 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>>( &self, lint: &'static Lint, @@ -933,6 +936,7 @@ pub trait LintContext: Sized { /// 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, @@ -1155,7 +1159,7 @@ impl<'tcx> LateContext<'tcx> { fn print_dyn_existential( self, - _predicates: &'tcx ty::List<ty::Binder<'tcx, ty::ExistentialPredicate<'tcx>>>, + _predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>, ) -> Result<Self::DynExistential, Self::Error> { Ok(()) } @@ -1241,6 +1245,23 @@ impl<'tcx> LateContext<'tcx> { AbsolutePathPrinter { tcx: self.tcx }.print_def_path(def_id, &[]).unwrap() } + + /// Returns the associated type `name` for `self_ty` as an implementation of `trait_id`. + /// Do not invoke without first verifying that the type implements the trait. + pub fn get_associated_type( + &self, + self_ty: Ty<'tcx>, + trait_id: DefId, + name: &str, + ) -> Option<Ty<'tcx>> { + let tcx = self.tcx; + tcx.associated_items(trait_id) + .find_by_name_and_kind(tcx, Ident::from_str(name), ty::AssocKind::Type, trait_id) + .and_then(|assoc| { + let proj = tcx.mk_projection(assoc.def_id, tcx.mk_substs_trait(self_ty, [])); + tcx.try_normalize_erasing_regions(self.param_env, proj).ok() + }) + } } impl<'tcx> abi::HasDataLayout for LateContext<'tcx> { diff --git a/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs b/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs new file mode 100644 index 000000000..1d29a234a --- /dev/null +++ b/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs @@ -0,0 +1,92 @@ +use crate::{LateContext, LateLintPass, LintContext}; + +use rustc_errors::DelayDm; +use rustc_hir as hir; +use rustc_middle::{traits::util::supertraits, ty}; +use rustc_span::sym; + +declare_lint! { + /// The `deref_into_dyn_supertrait` lint is output whenever there is a use of the + /// `Deref` implementation with a `dyn SuperTrait` type as `Output`. + /// + /// These implementations will become shadowed when the `trait_upcasting` feature is stabilized. + /// The `deref` functions will no longer be called implicitly, so there might be behavior change. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(deref_into_dyn_supertrait)] + /// #![allow(dead_code)] + /// + /// use core::ops::Deref; + /// + /// trait A {} + /// trait B: A {} + /// impl<'a> Deref for dyn 'a + B { + /// type Target = dyn A; + /// fn deref(&self) -> &Self::Target { + /// todo!() + /// } + /// } + /// + /// fn take_a(_: &dyn A) { } + /// + /// fn take_b(b: &dyn B) { + /// take_a(b); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// The dyn upcasting coercion feature adds new coercion rules, taking priority + /// over certain other coercion rules, which will cause some behavior change. + pub DEREF_INTO_DYN_SUPERTRAIT, + Warn, + "`Deref` implementation usage with a supertrait trait object for output might be shadowed in the future", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #89460 <https://github.com/rust-lang/rust/issues/89460>", + }; +} + +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>) { + // `Deref` is being implemented for `t` + if let hir::ItemKind::Impl(impl_) = item.kind + && let Some(trait_) = &impl_.of_trait + && let t = cx.tcx.type_of(item.owner_id) + && 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() + // `<T as Deref>::Target` is `dyn target_principal` + && let Some(target) = cx.get_associated_type(t, 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) + { + cx.struct_span_lint( + DEREF_INTO_DYN_SUPERTRAIT, + cx.tcx.def_span(item.owner_id.def_id), + DelayDm(|| { + format!( + "`{t}` implements `Deref` with supertrait `{target_principal}` as target" + ) + }), + |lint| { + if let Some(target_span) = impl_.items.iter().find_map(|i| (i.ident.name == sym::Target).then_some(i.span)) { + lint.span_label(target_span, "target type is set here"); + } + + lint + }, + ) + } + } +} diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index aee870dd2..52363b0be 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -20,23 +20,23 @@ use rustc_ast::ptr::P; use rustc_ast::visit::{self as ast_visit, Visitor}; use rustc_ast::{self as ast, walk_list, HasAttrs}; use rustc_middle::ty::RegisteredTools; -use rustc_session::lint::{BufferedEarlyLint, LintBuffer, LintPass}; +use rustc_session::lint::{BufferedEarlyLint, LintBuffer}; use rustc_session::Session; use rustc_span::symbol::Ident; use rustc_span::Span; -use std::slice; - -macro_rules! run_early_pass { ($cx:expr, $f:ident, $($args:expr),*) => ({ - $cx.pass.$f(&$cx.context, $($args),*); +macro_rules! run_early_passes { ($cx:expr, $f:ident, $($args:expr),*) => ({ + for pass in $cx.passes.iter_mut() { + pass.$f(&$cx.context, $($args),*); + } }) } -pub struct EarlyContextAndPass<'a, T: EarlyLintPass> { +pub struct EarlyContextAndPasses<'a> { context: EarlyContext<'a>, - pass: T, + passes: Vec<EarlyLintPassObject>, } -impl<'a, T: EarlyLintPass> EarlyContextAndPass<'a, T> { +impl<'a> EarlyContextAndPasses<'a> { fn 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; @@ -63,27 +63,27 @@ impl<'a, T: EarlyLintPass> EarlyContextAndPass<'a, T> { self.check_id(id); debug!("early context: enter_attrs({:?})", attrs); - run_early_pass!(self, enter_lint_attrs, attrs); + run_early_passes!(self, enter_lint_attrs, attrs); f(self); debug!("early context: exit_attrs({:?})", attrs); - run_early_pass!(self, exit_lint_attrs, attrs); + run_early_passes!(self, exit_lint_attrs, attrs); self.context.builder.pop(push); } } -impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> { +impl<'a> ast_visit::Visitor<'a> for EarlyContextAndPasses<'a> { fn visit_param(&mut self, param: &'a ast::Param) { self.with_lint_attrs(param.id, ¶m.attrs, |cx| { - run_early_pass!(cx, check_param, param); + run_early_passes!(cx, check_param, param); ast_visit::walk_param(cx, param); }); } fn visit_item(&mut self, it: &'a ast::Item) { self.with_lint_attrs(it.id, &it.attrs, |cx| { - run_early_pass!(cx, check_item, it); + run_early_passes!(cx, check_item, it); ast_visit::walk_item(cx, it); - run_early_pass!(cx, check_item_post, it); + run_early_passes!(cx, check_item_post, it); }) } @@ -94,10 +94,10 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> } fn visit_pat(&mut self, p: &'a ast::Pat) { - run_early_pass!(self, check_pat, p); + run_early_passes!(self, check_pat, p); self.check_id(p.id); ast_visit::walk_pat(self, p); - run_early_pass!(self, check_pat_post, p); + run_early_passes!(self, check_pat_post, p); } fn visit_pat_field(&mut self, field: &'a ast::PatField) { @@ -113,7 +113,7 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> fn visit_expr(&mut self, e: &'a ast::Expr) { self.with_lint_attrs(e.id, &e.attrs, |cx| { - run_early_pass!(cx, check_expr, e); + run_early_passes!(cx, check_expr, e); ast_visit::walk_expr(cx, e); }) } @@ -134,7 +134,7 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> // Note that statements get their attributes from // the AST struct that they wrap (e.g. an item) self.with_lint_attrs(s.id, s.attrs(), |cx| { - run_early_pass!(cx, check_stmt, s); + run_early_passes!(cx, check_stmt, s); cx.check_id(s.id); }); // The visitor for the AST struct wrapped @@ -145,7 +145,7 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> } fn visit_fn(&mut self, fk: ast_visit::FnKind<'a>, span: Span, id: ast::NodeId) { - run_early_pass!(self, check_fn, fk, span, id); + run_early_passes!(self, check_fn, fk, span, id); self.check_id(id); ast_visit::walk_fn(self, fk); @@ -159,8 +159,8 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> } fn visit_variant_data(&mut self, s: &'a ast::VariantData) { - if let Some(ctor_hir_id) = s.ctor_id() { - self.check_id(ctor_hir_id); + if let Some(ctor_node_id) = s.ctor_node_id() { + self.check_id(ctor_node_id); } ast_visit::walk_struct_def(self, s); } @@ -173,37 +173,37 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> fn visit_variant(&mut self, v: &'a ast::Variant) { self.with_lint_attrs(v.id, &v.attrs, |cx| { - run_early_pass!(cx, check_variant, v); + run_early_passes!(cx, check_variant, v); ast_visit::walk_variant(cx, v); }) } fn visit_ty(&mut self, t: &'a ast::Ty) { - run_early_pass!(self, check_ty, t); + run_early_passes!(self, check_ty, t); self.check_id(t.id); ast_visit::walk_ty(self, t); } fn visit_ident(&mut self, ident: Ident) { - run_early_pass!(self, check_ident, ident); + run_early_passes!(self, check_ident, ident); } fn visit_local(&mut self, l: &'a ast::Local) { self.with_lint_attrs(l.id, &l.attrs, |cx| { - run_early_pass!(cx, check_local, l); + run_early_passes!(cx, check_local, l); ast_visit::walk_local(cx, l); }) } fn visit_block(&mut self, b: &'a ast::Block) { - run_early_pass!(self, check_block, b); + run_early_passes!(self, check_block, b); self.check_id(b.id); ast_visit::walk_block(self, b); } fn visit_arm(&mut self, a: &'a ast::Arm) { self.with_lint_attrs(a.id, &a.attrs, |cx| { - run_early_pass!(cx, check_arm, a); + run_early_passes!(cx, check_arm, a); ast_visit::walk_arm(cx, a); }) } @@ -212,26 +212,29 @@ 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 match e.kind { - ast::ExprKind::Closure(_, _, ast::Async::Yes { closure_id, .. }, ..) + ast::ExprKind::Closure(box ast::Closure { + asyncness: ast::Async::Yes { closure_id, .. }, + .. + }) | ast::ExprKind::Async(_, closure_id, ..) => self.check_id(closure_id), _ => {} } } fn visit_generic_arg(&mut self, arg: &'a ast::GenericArg) { - run_early_pass!(self, check_generic_arg, arg); + run_early_passes!(self, check_generic_arg, arg); ast_visit::walk_generic_arg(self, arg); } fn visit_generic_param(&mut self, param: &'a ast::GenericParam) { self.with_lint_attrs(param.id, ¶m.attrs, |cx| { - run_early_pass!(cx, check_generic_param, param); + run_early_passes!(cx, check_generic_param, param); ast_visit::walk_generic_param(cx, param); }); } fn visit_generics(&mut self, g: &'a ast::Generics) { - run_early_pass!(self, check_generics, g); + run_early_passes!(self, check_generics, g); ast_visit::walk_generics(self, g); } @@ -240,18 +243,18 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> } fn visit_poly_trait_ref(&mut self, t: &'a ast::PolyTraitRef) { - run_early_pass!(self, check_poly_trait_ref, t); + run_early_passes!(self, check_poly_trait_ref, t); ast_visit::walk_poly_trait_ref(self, t); } fn visit_assoc_item(&mut self, item: &'a ast::AssocItem, ctxt: ast_visit::AssocCtxt) { self.with_lint_attrs(item.id, &item.attrs, |cx| match ctxt { ast_visit::AssocCtxt::Trait => { - run_early_pass!(cx, check_trait_item, item); + run_early_passes!(cx, check_trait_item, item); ast_visit::walk_assoc_item(cx, item, ctxt); } ast_visit::AssocCtxt::Impl => { - run_early_pass!(cx, check_impl_item, item); + run_early_passes!(cx, check_impl_item, item); ast_visit::walk_assoc_item(cx, item, ctxt); } }); @@ -272,51 +275,20 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> } fn visit_attribute(&mut self, attr: &'a ast::Attribute) { - run_early_pass!(self, check_attribute, attr); + run_early_passes!(self, check_attribute, attr); } fn visit_mac_def(&mut self, mac: &'a ast::MacroDef, id: ast::NodeId) { - run_early_pass!(self, check_mac_def, mac); + run_early_passes!(self, check_mac_def, mac); self.check_id(id); } fn visit_mac_call(&mut self, mac: &'a ast::MacCall) { - run_early_pass!(self, check_mac, mac); + run_early_passes!(self, check_mac, mac); ast_visit::walk_mac(self, mac); } } -struct EarlyLintPassObjects<'a> { - lints: &'a mut [EarlyLintPassObject], -} - -#[allow(rustc::lint_pass_impl_without_macro)] -impl LintPass for EarlyLintPassObjects<'_> { - fn name(&self) -> &'static str { - panic!() - } -} - -macro_rules! expand_early_lint_pass_impl_methods { - ([$($(#[$attr:meta])* fn $name:ident($($param:ident: $arg:ty),*);)*]) => ( - $(fn $name(&mut self, context: &EarlyContext<'_>, $($param: $arg),*) { - for obj in self.lints.iter_mut() { - obj.$name(context, $($param),*); - } - })* - ) -} - -macro_rules! early_lint_pass_impl { - ([], [$($methods:tt)*]) => ( - impl EarlyLintPass for EarlyLintPassObjects<'_> { - expand_early_lint_pass_impl_methods!([$($methods)*]); - } - ) -} - -crate::early_lint_methods!(early_lint_pass_impl, []); - /// Early lints work on different nodes - either on the crate root, or on freshly loaded modules. /// This trait generalizes over those nodes. pub trait EarlyCheckNode<'a>: Copy { @@ -324,7 +296,7 @@ pub trait EarlyCheckNode<'a>: Copy { fn attrs<'b>(self) -> &'b [ast::Attribute] where 'a: 'b; - fn check<'b>(self, cx: &mut EarlyContextAndPass<'b, impl EarlyLintPass>) + fn check<'b>(self, cx: &mut EarlyContextAndPasses<'b>) where 'a: 'b; } @@ -339,13 +311,13 @@ impl<'a> EarlyCheckNode<'a> for &'a ast::Crate { { &self.attrs } - fn check<'b>(self, cx: &mut EarlyContextAndPass<'b, impl EarlyLintPass>) + fn check<'b>(self, cx: &mut EarlyContextAndPasses<'b>) where 'a: 'b, { - run_early_pass!(cx, check_crate, self); + run_early_passes!(cx, check_crate, self); ast_visit::walk_crate(cx, self); - run_early_pass!(cx, check_crate_post, self); + run_early_passes!(cx, check_crate_post, self); } } @@ -359,7 +331,7 @@ impl<'a> EarlyCheckNode<'a> for (ast::NodeId, &'a [ast::Attribute], &'a [P<ast:: { self.1 } - fn check<'b>(self, cx: &mut EarlyContextAndPass<'b, impl EarlyLintPass>) + fn check<'b>(self, cx: &mut EarlyContextAndPasses<'b>) where 'a: 'b, { @@ -368,87 +340,36 @@ impl<'a> EarlyCheckNode<'a> for (ast::NodeId, &'a [ast::Attribute], &'a [P<ast:: } } -fn early_lint_node<'a>( - sess: &Session, - warn_about_weird_lints: bool, - lint_store: &LintStore, - registered_tools: &RegisteredTools, - buffered: LintBuffer, - pass: impl EarlyLintPass, - check_node: impl EarlyCheckNode<'a>, -) -> LintBuffer { - let mut cx = EarlyContextAndPass { - context: EarlyContext::new( - sess, - warn_about_weird_lints, - lint_store, - registered_tools, - buffered, - ), - pass, - }; - - cx.with_lint_attrs(check_node.id(), check_node.attrs(), |cx| check_node.check(cx)); - cx.context.buffered -} - pub fn check_ast_node<'a>( sess: &Session, pre_expansion: bool, lint_store: &LintStore, registered_tools: &RegisteredTools, lint_buffer: Option<LintBuffer>, - builtin_lints: impl EarlyLintPass, + builtin_lints: impl EarlyLintPass + 'static, check_node: impl EarlyCheckNode<'a>, ) { let passes = if pre_expansion { &lint_store.pre_expansion_passes } else { &lint_store.early_passes }; - let mut passes: Vec<_> = passes.iter().map(|p| (p)()).collect(); - let mut buffered = lint_buffer.unwrap_or_default(); - - if sess.opts.unstable_opts.no_interleave_lints { - for (i, pass) in passes.iter_mut().enumerate() { - buffered = - sess.prof.verbose_generic_activity_with_arg("run_lint", pass.name()).run(|| { - early_lint_node( - sess, - !pre_expansion && i == 0, - lint_store, - registered_tools, - buffered, - EarlyLintPassObjects { lints: slice::from_mut(pass) }, - check_node, - ) - }); - } - } else { - buffered = early_lint_node( + let mut passes: Vec<EarlyLintPassObject> = passes.iter().map(|p| (p)()).collect(); + passes.push(Box::new(builtin_lints)); + + let mut cx = EarlyContextAndPasses { + context: EarlyContext::new( sess, !pre_expansion, lint_store, registered_tools, - buffered, - builtin_lints, - check_node, - ); - - if !passes.is_empty() { - buffered = early_lint_node( - sess, - false, - lint_store, - registered_tools, - buffered, - EarlyLintPassObjects { lints: &mut passes[..] }, - check_node, - ); - } - } + lint_buffer.unwrap_or_default(), + ), + passes, + }; + cx.with_lint_attrs(check_node.id(), check_node.attrs(), |cx| check_node.check(cx)); // All of the buffered lints should have been emitted at this point. // If not, that means that we somehow buffered a lint for a node id // that was not lint-checked (perhaps it doesn't exist?). This is a bug. - for (id, lints) in buffered.map { + for (id, lints) in cx.context.buffered.map { for early_lint in lints { sess.delay_span_bug( early_lint.span, diff --git a/compiler/rustc_lint/src/errors.rs b/compiler/rustc_lint/src/errors.rs index a49d1bdac..1a769893f 100644 --- a/compiler/rustc_lint/src/errors.rs +++ b/compiler/rustc_lint/src/errors.rs @@ -83,7 +83,7 @@ pub struct UnknownToolInScopedLint { pub struct BuiltinEllpisisInclusiveRangePatterns { #[primary_span] pub span: Span, - #[suggestion_short(code = "{replace}", applicability = "machine-applicable")] + #[suggestion(style = "short", code = "{replace}", applicability = "machine-applicable")] pub suggestion: Span, pub replace: String, } diff --git a/compiler/rustc_lint/src/for_loops_over_fallibles.rs b/compiler/rustc_lint/src/for_loops_over_fallibles.rs index ed8d424e0..418785015 100644 --- a/compiler/rustc_lint/src/for_loops_over_fallibles.rs +++ b/compiler/rustc_lint/src/for_loops_over_fallibles.rs @@ -3,11 +3,9 @@ use crate::{LateContext, LateLintPass, LintContext}; use hir::{Expr, Pat}; use rustc_errors::{Applicability, DelayDm}; use rustc_hir as hir; -use rustc_infer::traits::TraitEngine; use rustc_infer::{infer::TyCtxtInferExt, traits::ObligationCause}; use rustc_middle::ty::{self, List}; use rustc_span::{sym, Span}; -use rustc_trait_selection::traits::TraitEngineExt; declare_lint! { /// The `for_loops_over_fallibles` lint checks for `for` loops over `Option` or `Result` values. @@ -160,24 +158,19 @@ fn suggest_question_mark<'tcx>( let ty = substs.type_at(0); let infcx = cx.tcx.infer_ctxt().build(); - let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx); - let cause = ObligationCause::new( span, body_id.hir_id, rustc_infer::traits::ObligationCauseCode::MiscObligation, ); - fulfill_cx.register_bound( + let errors = rustc_trait_selection::traits::fully_solve_bound( &infcx, + cause, ty::ParamEnv::empty(), // Erase any region vids from the type, which may not be resolved infcx.tcx.erase_regions(ty), into_iterator_did, - cause, ); - // Select all, including ambiguous predicates - let errors = fulfill_cx.select_all_or_error(&infcx); - errors.is_empty() } diff --git a/compiler/rustc_lint/src/hidden_unicode_codepoints.rs b/compiler/rustc_lint/src/hidden_unicode_codepoints.rs index 7e884e990..7106e75db 100644 --- a/compiler/rustc_lint/src/hidden_unicode_codepoints.rs +++ b/compiler/rustc_lint/src/hidden_unicode_codepoints.rs @@ -123,23 +123,22 @@ impl EarlyLintPass for HiddenUnicodeCodepoints { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) { // byte strings are already handled well enough by `EscapeError::NonAsciiCharInByteString` - let (text, span, padding) = match &expr.kind { - ast::ExprKind::Lit(ast::Lit { token_lit, kind, span }) => { + match &expr.kind { + ast::ExprKind::Lit(token_lit) => { let text = token_lit.symbol; if !contains_text_flow_control_chars(text.as_str()) { return; } - let padding = match kind { + let padding = match token_lit.kind { // account for `"` or `'` - ast::LitKind::Str(_, ast::StrStyle::Cooked) | ast::LitKind::Char(_) => 1, + ast::token::LitKind::Str | ast::token::LitKind::Char => 1, // account for `r###"` - ast::LitKind::Str(_, ast::StrStyle::Raw(val)) => *val as u32 + 2, + ast::token::LitKind::StrRaw(n) => n as u32 + 2, _ => return, }; - (text, span, padding) + self.lint_text_direction_codepoint(cx, text, expr.span, padding, true, "literal"); } - _ => return, + _ => {} }; - self.lint_text_direction_codepoint(cx, text, *span, padding, true, "literal"); } } diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index 11e4650cb..4f92661db 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -117,7 +117,7 @@ impl<'tcx> LateLintPass<'tcx> for TyTyKind { fn check_path( &mut self, cx: &LateContext<'tcx>, - path: &'tcx rustc_hir::Path<'tcx>, + path: &rustc_hir::Path<'tcx>, _: rustc_hir::HirId, ) { if let Some(segment) = path.segments.iter().nth_back(1) @@ -272,11 +272,7 @@ fn gen_args(segment: &PathSegment<'_>) -> String { .args .iter() .filter_map(|arg| { - if let GenericArg::Lifetime(lt) = arg { - Some(lt.name.ident().to_string()) - } else { - None - } + if let GenericArg::Lifetime(lt) = arg { Some(lt.ident.to_string()) } else { None } }) .collect::<Vec<_>>(); @@ -466,8 +462,8 @@ impl LateLintPass<'_> for BadOptAccess { let Some(attr) = cx.tcx.get_attr(field.did, sym::rustc_lint_opt_deny_field_access) && let Some(items) = attr.meta_item_list() && let Some(item) = items.first() && - let Some(literal) = item.literal() && - let ast::LitKind::Str(val, _) = literal.kind + let Some(lit) = item.lit() && + let ast::LitKind::Str(val, _) = lit.kind { cx.struct_span_lint(BAD_OPT_ACCESS, expr.span, val.as_str(), |lint| lint diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs index 303fcb1a1..8a50cb1f1 100644 --- a/compiler/rustc_lint/src/late.rs +++ b/compiler/rustc_lint/src/late.rs @@ -23,12 +23,10 @@ use rustc_hir::intravisit as hir_visit; use rustc_hir::intravisit::Visitor; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, TyCtxt}; -use rustc_session::lint::LintPass; use rustc_span::Span; use std::any::Any; use std::cell::Cell; -use std::slice; /// Extract the `LintStore` from the query context. /// This function exists because we've erased `LintStore` as `dyn Any` in the context. @@ -38,15 +36,17 @@ pub fn unerased_lint_store(tcx: TyCtxt<'_>) -> &LintStore { } macro_rules! lint_callback { ($cx:expr, $f:ident, $($args:expr),*) => ({ - $cx.pass.$f(&$cx.context, $($args),*); + for pass in $cx.passes.iter_mut() { + pass.$f(&$cx.context, $($args),*); + } }) } -struct LateContextAndPass<'tcx, T: LateLintPass<'tcx>> { +struct LateContextAndPasses<'tcx> { context: LateContext<'tcx>, - pass: T, + passes: Vec<LateLintPassObject<'tcx>>, } -impl<'tcx, T: LateLintPass<'tcx>> LateContextAndPass<'tcx, T> { +impl<'tcx> LateContextAndPasses<'tcx> { /// Merge the lints specified by any lint attributes into the /// current lint context, call the provided function, then reset the /// lints in effect to their previous state. @@ -82,7 +82,7 @@ impl<'tcx, T: LateLintPass<'tcx>> LateContextAndPass<'tcx, T> { } } -impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPass<'tcx, T> { +impl<'tcx> hir_visit::Visitor<'tcx> for LateContextAndPasses<'tcx> { type NestedFilter = nested_filter::All; /// Because lints are scoped lexically, we want to walk nested @@ -205,7 +205,7 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas } fn visit_variant(&mut self, v: &'tcx hir::Variant<'tcx>) { - self.with_lint_attrs(v.id, |cx| { + self.with_lint_attrs(v.hir_id, |cx| { lint_callback!(cx, check_variant, v); hir_visit::walk_variant(cx, v); }) @@ -292,7 +292,7 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas hir_visit::walk_lifetime(self, lt); } - fn visit_path(&mut self, p: &'tcx hir::Path<'tcx>, id: hir::HirId) { + fn visit_path(&mut self, p: &hir::Path<'tcx>, id: hir::HirId) { lint_callback!(self, check_path, p, id); hir_visit::walk_path(self, p); } @@ -302,57 +302,28 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas } } -struct LateLintPassObjects<'a, 'tcx> { - lints: &'a mut [LateLintPassObject<'tcx>], -} - -#[allow(rustc::lint_pass_impl_without_macro)] -impl LintPass for LateLintPassObjects<'_, '_> { - fn name(&self) -> &'static str { - panic!() - } -} - -macro_rules! expand_late_lint_pass_impl_methods { - ([$hir:tt], [$($(#[$attr:meta])* fn $name:ident($($param:ident: $arg:ty),*);)*]) => ( - $(fn $name(&mut self, context: &LateContext<$hir>, $($param: $arg),*) { - for obj in self.lints.iter_mut() { - obj.$name(context, $($param),*); - } - })* - ) -} - -macro_rules! late_lint_pass_impl { - ([], [$hir:tt], $methods:tt) => { - impl<$hir> LateLintPass<$hir> for LateLintPassObjects<'_, $hir> { - expand_late_lint_pass_impl_methods!([$hir], $methods); - } - }; -} - -crate::late_lint_methods!(late_lint_pass_impl, [], ['tcx]); - -fn late_lint_mod_pass<'tcx, T: LateLintPass<'tcx>>( +pub(super) fn late_lint_mod<'tcx, T: LateLintPass<'tcx> + 'tcx>( tcx: TyCtxt<'tcx>, module_def_id: LocalDefId, - pass: T, + builtin_lints: T, ) { - let effective_visibilities = &tcx.effective_visibilities(()); - let context = LateContext { tcx, enclosing_body: None, cached_typeck_results: Cell::new(None), param_env: ty::ParamEnv::empty(), - effective_visibilities, + 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), generics: None, only_module: true, }; - let mut cx = LateContextAndPass { context, pass }; + let mut passes: Vec<_> = + unerased_lint_store(tcx).late_module_passes.iter().map(|pass| (pass)(tcx)).collect(); + passes.push(Box::new(builtin_lints)); + + let mut cx = LateContextAndPasses { context, passes }; let (module, _span, hir_id) = tcx.hir().get_module(module_def_id); cx.process_mod(module, hir_id); @@ -365,46 +336,28 @@ fn late_lint_mod_pass<'tcx, T: LateLintPass<'tcx>>( } } -pub fn late_lint_mod<'tcx, T: LateLintPass<'tcx>>( - tcx: TyCtxt<'tcx>, - module_def_id: LocalDefId, - builtin_lints: T, -) { - if tcx.sess.opts.unstable_opts.no_interleave_lints { - // These passes runs in late_lint_crate with -Z no_interleave_lints - return; - } - - late_lint_mod_pass(tcx, module_def_id, builtin_lints); - - let mut passes: Vec<_> = - unerased_lint_store(tcx).late_module_passes.iter().map(|pass| (pass)(tcx)).collect(); - - if !passes.is_empty() { - late_lint_mod_pass(tcx, module_def_id, LateLintPassObjects { lints: &mut passes[..] }); - } -} - -fn late_lint_pass_crate<'tcx, T: LateLintPass<'tcx>>(tcx: TyCtxt<'tcx>, pass: T) { - let effective_visibilities = &tcx.effective_visibilities(()); - +fn late_lint_crate<'tcx, T: LateLintPass<'tcx> + 'tcx>(tcx: TyCtxt<'tcx>, builtin_lints: T) { let context = LateContext { tcx, enclosing_body: None, cached_typeck_results: Cell::new(None), param_env: ty::ParamEnv::empty(), - effective_visibilities, + effective_visibilities: &tcx.effective_visibilities(()), lint_store: unerased_lint_store(tcx), last_node_with_lint_attrs: hir::CRATE_HIR_ID, generics: None, only_module: false, }; - let mut cx = LateContextAndPass { context, pass }; + let mut passes = + unerased_lint_store(tcx).late_passes.iter().map(|p| (p)(tcx)).collect::<Vec<_>>(); + passes.push(Box::new(builtin_lints)); + + let mut cx = LateContextAndPasses { context, passes }; // Visit the whole crate. cx.with_lint_attrs(hir::CRATE_HIR_ID, |cx| { - // since the root module isn't visited as an item (because it isn't an + // Since the root module isn't visited as an item (because it isn't an // item), warn for it here. lint_callback!(cx, check_crate,); tcx.hir().walk_toplevel_module(cx); @@ -413,41 +366,8 @@ fn late_lint_pass_crate<'tcx, T: LateLintPass<'tcx>>(tcx: TyCtxt<'tcx>, pass: T) }) } -fn late_lint_crate<'tcx, T: LateLintPass<'tcx>>(tcx: TyCtxt<'tcx>, builtin_lints: T) { - let mut passes = - unerased_lint_store(tcx).late_passes.iter().map(|p| (p)(tcx)).collect::<Vec<_>>(); - - if !tcx.sess.opts.unstable_opts.no_interleave_lints { - if !passes.is_empty() { - late_lint_pass_crate(tcx, LateLintPassObjects { lints: &mut passes[..] }); - } - - late_lint_pass_crate(tcx, builtin_lints); - } else { - for pass in &mut passes { - tcx.sess.prof.verbose_generic_activity_with_arg("run_late_lint", pass.name()).run( - || { - late_lint_pass_crate(tcx, LateLintPassObjects { lints: slice::from_mut(pass) }); - }, - ); - } - - let mut passes: Vec<_> = - unerased_lint_store(tcx).late_module_passes.iter().map(|pass| (pass)(tcx)).collect(); - - for pass in &mut passes { - tcx.sess - .prof - .verbose_generic_activity_with_arg("run_late_module_lint", pass.name()) - .run(|| { - late_lint_pass_crate(tcx, LateLintPassObjects { lints: slice::from_mut(pass) }); - }); - } - } -} - /// Performs lint checking on a crate. -pub fn check_crate<'tcx, T: LateLintPass<'tcx>>( +pub fn check_crate<'tcx, T: LateLintPass<'tcx> + 'tcx>( tcx: TyCtxt<'tcx>, builtin_lints: impl FnOnce() -> T + Send, ) { diff --git a/compiler/rustc_lint/src/let_underscore.rs b/compiler/rustc_lint/src/let_underscore.rs index 78f355ec3..04d844d21 100644 --- a/compiler/rustc_lint/src/let_underscore.rs +++ b/compiler/rustc_lint/src/let_underscore.rs @@ -11,7 +11,8 @@ declare_lint! { /// scope. /// /// ### Example - /// ``` + /// + /// ```rust /// struct SomeStruct; /// impl Drop for SomeStruct { /// fn drop(&mut self) { @@ -56,7 +57,7 @@ declare_lint! { /// of at end of scope, which is typically incorrect. /// /// ### Example - /// ```compile_fail + /// ```rust,compile_fail /// use std::sync::{Arc, Mutex}; /// use std::thread; /// let data = Arc::new(Mutex::new(0)); diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index db0a3419e..847c356b8 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -163,7 +163,7 @@ fn shallow_lint_levels_on(tcx: TyCtxt<'_>, owner: hir::OwnerId) -> ShallowLintLe // Otherwise, we need to visit the attributes in source code order, so we fetch HIR and do // a standard visit. // FIXME(#102522) Just iterate on attrs once that iteration order matches HIR's. - _ => match tcx.hir().expect_owner(owner) { + _ => match tcx.hir().owner(owner) { hir::OwnerNode::Item(item) => levels.visit_item(item), hir::OwnerNode::ForeignItem(item) => levels.visit_foreign_item(item), hir::OwnerNode::TraitItem(item) => levels.visit_trait_item(item), @@ -320,7 +320,7 @@ impl<'tcx> Visitor<'tcx> for LintLevelsBuilder<'_, LintLevelQueryMap<'tcx>> { } fn visit_variant(&mut self, v: &'tcx hir::Variant<'tcx>) { - self.add_id(v.id); + self.add_id(v.hir_id); intravisit::walk_variant(self, v); } @@ -392,7 +392,7 @@ impl<'tcx> Visitor<'tcx> for LintLevelsBuilder<'_, QueryMapExpectationsWrapper<' } fn visit_variant(&mut self, v: &'tcx hir::Variant<'tcx>) { - self.add_id(v.id); + self.add_id(v.hir_id); intravisit::walk_variant(self, v); } @@ -1073,6 +1073,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { /// 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] pub(crate) fn struct_lint( &self, lint: &'static Lint, diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 5288fc542..771628553 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -36,6 +36,7 @@ #![feature(let_chains)] #![feature(min_specialization)] #![feature(never_type)] +#![feature(rustc_attrs)] #![recursion_limit = "256"] #[macro_use] @@ -48,6 +49,7 @@ extern crate tracing; mod array_into_iter; pub mod builtin; mod context; +mod deref_into_dyn_supertrait; mod early; mod enum_intrinsics_non_enums; mod errors; @@ -86,6 +88,7 @@ use rustc_span::Span; use array_into_iter::ArrayIntoIter; use builtin::*; +use deref_into_dyn_supertrait::*; use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums; use for_loops_over_fallibles::*; use hidden_unicode_codepoints::*; @@ -124,131 +127,117 @@ fn lint_mod(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { late::late_lint_mod(tcx, module_def_id, BuiltinCombinedModuleLateLintPass::new()); } -macro_rules! pre_expansion_lint_passes { - ($macro:path, $args:tt) => { - $macro!($args, [KeywordIdents: KeywordIdents,]); - }; -} - -macro_rules! early_lint_passes { - ($macro:path, $args:tt) => { - $macro!( - $args, - [ - UnusedParens: UnusedParens, - UnusedBraces: UnusedBraces, - UnusedImportBraces: UnusedImportBraces, - UnsafeCode: UnsafeCode, - SpecialModuleName: SpecialModuleName, - AnonymousParameters: AnonymousParameters, - EllipsisInclusiveRangePatterns: EllipsisInclusiveRangePatterns::default(), - NonCamelCaseTypes: NonCamelCaseTypes, - DeprecatedAttr: DeprecatedAttr::new(), - WhileTrue: WhileTrue, - NonAsciiIdents: NonAsciiIdents, - HiddenUnicodeCodepoints: HiddenUnicodeCodepoints, - IncompleteFeatures: IncompleteFeatures, - RedundantSemicolons: RedundantSemicolons, - UnusedDocComment: UnusedDocComment, - UnexpectedCfgs: UnexpectedCfgs, - ] - ); - }; -} - -macro_rules! declare_combined_early_pass { - ([$name:ident], $passes:tt) => ( - early_lint_methods!(declare_combined_early_lint_pass, [pub $name, $passes]); - ) -} - -pre_expansion_lint_passes!(declare_combined_early_pass, [BuiltinCombinedPreExpansionLintPass]); -early_lint_passes!(declare_combined_early_pass, [BuiltinCombinedEarlyLintPass]); - -macro_rules! late_lint_passes { - ($macro:path, $args:tt) => { - $macro!( - $args, - [ - // Tracks state across modules - UnnameableTestItems: UnnameableTestItems::new(), - // Tracks attributes of parents - MissingDoc: MissingDoc::new(), - // Builds a global list of all impls of `Debug`. - // FIXME: Turn the computation of types which implement Debug into a query - // and change this to a module lint pass - MissingDebugImplementations: MissingDebugImplementations::default(), - // Keeps a global list of foreign declarations. - ClashingExternDeclarations: ClashingExternDeclarations::new(), - ] - ); - }; -} - -macro_rules! late_lint_mod_passes { - ($macro:path, $args:tt) => { - $macro!( - $args, - [ - ForLoopsOverFallibles: ForLoopsOverFallibles, - HardwiredLints: HardwiredLints, - ImproperCTypesDeclarations: ImproperCTypesDeclarations, - ImproperCTypesDefinitions: ImproperCTypesDefinitions, - VariantSizeDifferences: VariantSizeDifferences, - BoxPointers: BoxPointers, - PathStatements: PathStatements, - LetUnderscore: LetUnderscore, - // Depends on referenced function signatures in expressions - UnusedResults: UnusedResults, - NonUpperCaseGlobals: NonUpperCaseGlobals, - NonShorthandFieldPatterns: NonShorthandFieldPatterns, - UnusedAllocation: UnusedAllocation, - // Depends on types used in type definitions - MissingCopyImplementations: MissingCopyImplementations, - // Depends on referenced function signatures in expressions - MutableTransmutes: MutableTransmutes, - TypeAliasBounds: TypeAliasBounds, - TrivialConstraints: TrivialConstraints, - TypeLimits: TypeLimits::new(), - NonSnakeCase: NonSnakeCase, - InvalidNoMangleItems: InvalidNoMangleItems, - // Depends on effective visibilities - UnreachablePub: UnreachablePub, - ExplicitOutlivesRequirements: ExplicitOutlivesRequirements, - InvalidValue: InvalidValue, - DerefNullPtr: DerefNullPtr, - // May Depend on constants elsewhere - UnusedBrokenConst: UnusedBrokenConst, - UnstableFeatures: UnstableFeatures, - ArrayIntoIter: ArrayIntoIter::default(), - DropTraitConstraints: DropTraitConstraints, - TemporaryCStringAsPtr: TemporaryCStringAsPtr, - NonPanicFmt: NonPanicFmt, - NoopMethodCall: NoopMethodCall, - EnumIntrinsicsNonEnums: EnumIntrinsicsNonEnums, - InvalidAtomicOrdering: InvalidAtomicOrdering, - NamedAsmLabels: NamedAsmLabels, - OpaqueHiddenInferredBound: OpaqueHiddenInferredBound, - ] - ); - }; -} - -macro_rules! declare_combined_late_pass { - ([$v:vis $name:ident], $passes:tt) => ( - late_lint_methods!(declare_combined_late_lint_pass, [$v $name, $passes], ['tcx]); - ) -} +early_lint_methods!( + declare_combined_early_lint_pass, + [ + pub BuiltinCombinedPreExpansionLintPass, + [ + KeywordIdents: KeywordIdents, + ] + ] +); + +early_lint_methods!( + declare_combined_early_lint_pass, + [ + pub BuiltinCombinedEarlyLintPass, + [ + UnusedParens: UnusedParens, + UnusedBraces: UnusedBraces, + UnusedImportBraces: UnusedImportBraces, + UnsafeCode: UnsafeCode, + SpecialModuleName: SpecialModuleName, + AnonymousParameters: AnonymousParameters, + EllipsisInclusiveRangePatterns: EllipsisInclusiveRangePatterns::default(), + NonCamelCaseTypes: NonCamelCaseTypes, + DeprecatedAttr: DeprecatedAttr::new(), + WhileTrue: WhileTrue, + NonAsciiIdents: NonAsciiIdents, + HiddenUnicodeCodepoints: HiddenUnicodeCodepoints, + IncompleteFeatures: IncompleteFeatures, + RedundantSemicolons: RedundantSemicolons, + UnusedDocComment: UnusedDocComment, + UnexpectedCfgs: UnexpectedCfgs, + ] + ] +); // FIXME: Make a separate lint type which do not require typeck tables -late_lint_passes!(declare_combined_late_pass, [pub BuiltinCombinedLateLintPass]); - -late_lint_mod_passes!(declare_combined_late_pass, [BuiltinCombinedModuleLateLintPass]); - -pub fn new_lint_store(no_interleave_lints: bool, internal_lints: bool) -> LintStore { +late_lint_methods!( + declare_combined_late_lint_pass, + [ + pub BuiltinCombinedLateLintPass, + [ + // Tracks state across modules + UnnameableTestItems: UnnameableTestItems::new(), + // Tracks attributes of parents + MissingDoc: MissingDoc::new(), + // Builds a global list of all impls of `Debug`. + // FIXME: Turn the computation of types which implement Debug into a query + // and change this to a module lint pass + MissingDebugImplementations: MissingDebugImplementations::default(), + // Keeps a global list of foreign declarations. + ClashingExternDeclarations: ClashingExternDeclarations::new(), + ] + ], + ['tcx] +); + +late_lint_methods!( + declare_combined_late_lint_pass, + [ + BuiltinCombinedModuleLateLintPass, + [ + ForLoopsOverFallibles: ForLoopsOverFallibles, + DerefIntoDynSupertrait: DerefIntoDynSupertrait, + HardwiredLints: HardwiredLints, + ImproperCTypesDeclarations: ImproperCTypesDeclarations, + ImproperCTypesDefinitions: ImproperCTypesDefinitions, + VariantSizeDifferences: VariantSizeDifferences, + BoxPointers: BoxPointers, + PathStatements: PathStatements, + LetUnderscore: LetUnderscore, + // Depends on referenced function signatures in expressions + UnusedResults: UnusedResults, + NonUpperCaseGlobals: NonUpperCaseGlobals, + NonShorthandFieldPatterns: NonShorthandFieldPatterns, + UnusedAllocation: UnusedAllocation, + // Depends on types used in type definitions + MissingCopyImplementations: MissingCopyImplementations, + // Depends on referenced function signatures in expressions + MutableTransmutes: MutableTransmutes, + TypeAliasBounds: TypeAliasBounds, + TrivialConstraints: TrivialConstraints, + TypeLimits: TypeLimits::new(), + NonSnakeCase: NonSnakeCase, + InvalidNoMangleItems: InvalidNoMangleItems, + // Depends on effective visibilities + UnreachablePub: UnreachablePub, + ExplicitOutlivesRequirements: ExplicitOutlivesRequirements, + InvalidValue: InvalidValue, + DerefNullPtr: DerefNullPtr, + // May Depend on constants elsewhere + UnusedBrokenConst: UnusedBrokenConst, + UnstableFeatures: UnstableFeatures, + UngatedAsyncFnTrackCaller: UngatedAsyncFnTrackCaller, + ArrayIntoIter: ArrayIntoIter::default(), + DropTraitConstraints: DropTraitConstraints, + TemporaryCStringAsPtr: TemporaryCStringAsPtr, + NonPanicFmt: NonPanicFmt, + NoopMethodCall: NoopMethodCall, + EnumIntrinsicsNonEnums: EnumIntrinsicsNonEnums, + InvalidAtomicOrdering: InvalidAtomicOrdering, + NamedAsmLabels: NamedAsmLabels, + OpaqueHiddenInferredBound: OpaqueHiddenInferredBound, + ] + ], + ['tcx] +); + +pub fn new_lint_store(internal_lints: bool) -> LintStore { let mut lint_store = LintStore::new(); - register_builtins(&mut lint_store, no_interleave_lints); + register_builtins(&mut lint_store); if internal_lints { register_internals(&mut lint_store); } @@ -259,54 +248,17 @@ pub fn new_lint_store(no_interleave_lints: bool, internal_lints: bool) -> LintSt /// Tell the `LintStore` about all the built-in lints (the ones /// defined in this crate and the ones defined in /// `rustc_session::lint::builtin`). -fn register_builtins(store: &mut LintStore, no_interleave_lints: bool) { +fn register_builtins(store: &mut LintStore) { macro_rules! add_lint_group { ($name:expr, $($lint:ident),*) => ( store.register_group(false, $name, None, vec![$(LintId::of($lint)),*]); ) } - macro_rules! register_early_pass { - ($method:ident, $ty:ident, $constructor:expr) => { - store.register_lints(&$ty::get_lints()); - store.$method(|| Box::new($constructor)); - }; - } - - macro_rules! register_late_pass { - ($method:ident, $ty:ident, $constructor:expr) => { - store.register_lints(&$ty::get_lints()); - store.$method(|_| Box::new($constructor)); - }; - } - - macro_rules! register_early_passes { - ($method:ident, [$($passes:ident: $constructor:expr,)*]) => ( - $( - register_early_pass!($method, $passes, $constructor); - )* - ) - } - - macro_rules! register_late_passes { - ($method:ident, [$($passes:ident: $constructor:expr,)*]) => ( - $( - register_late_pass!($method, $passes, $constructor); - )* - ) - } - - if no_interleave_lints { - pre_expansion_lint_passes!(register_early_passes, register_pre_expansion_pass); - early_lint_passes!(register_early_passes, register_early_pass); - late_lint_passes!(register_late_passes, register_late_pass); - late_lint_mod_passes!(register_late_passes, register_late_mod_pass); - } else { - store.register_lints(&BuiltinCombinedPreExpansionLintPass::get_lints()); - store.register_lints(&BuiltinCombinedEarlyLintPass::get_lints()); - store.register_lints(&BuiltinCombinedModuleLateLintPass::get_lints()); - store.register_lints(&BuiltinCombinedLateLintPass::get_lints()); - } + store.register_lints(&BuiltinCombinedPreExpansionLintPass::get_lints()); + store.register_lints(&BuiltinCombinedEarlyLintPass::get_lints()); + store.register_lints(&BuiltinCombinedModuleLateLintPass::get_lints()); + store.register_lints(&BuiltinCombinedLateLintPass::get_lints()); add_lint_group!( "nonstandard_style", diff --git a/compiler/rustc_lint/src/non_fmt_panic.rs b/compiler/rustc_lint/src/non_fmt_panic.rs index 6ad2e0294..c1820ac4d 100644 --- a/compiler/rustc_lint/src/non_fmt_panic.rs +++ b/compiler/rustc_lint/src/non_fmt_panic.rs @@ -5,7 +5,6 @@ use rustc_hir as hir; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::lint::in_external_macro; use rustc_middle::ty; -use rustc_middle::ty::subst::InternalSubsts; use rustc_parse_format::{ParseMode, Parser, Piece}; use rustc_session::lint::FutureIncompatibilityReason; use rustc_span::edition::Edition; @@ -148,22 +147,22 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc ty::Ref(_, r, _) if *r.kind() == ty::Str, ) || matches!( ty.ty_adt_def(), - Some(ty_def) if cx.tcx.is_diagnostic_item(sym::String, ty_def.did()), + Some(ty_def) if Some(ty_def.did()) == cx.tcx.lang_items().string(), ); 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, InternalSubsts::empty(), cx.param_env) - .may_apply() - }) == Some(true); + || cx + .tcx + .get_diagnostic_item(sym::Display) + .map(|t| infcx.type_implements_trait(t, [ty], cx.param_env).may_apply()) + == Some(true); let suggest_debug = !suggest_display - && cx.tcx.get_diagnostic_item(sym::Debug).map(|t| { - infcx - .type_implements_trait(t, ty, InternalSubsts::empty(), cx.param_env) - .may_apply() - }) == Some(true); + && cx + .tcx + .get_diagnostic_item(sym::Debug) + .map(|t| infcx.type_implements_trait(t, [ty], cx.param_env).may_apply()) + == Some(true); let suggest_panic_any = !is_str && panic == sym::std_panic_macro; diff --git a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs index 00bf287ba..03d6f4fd9 100644 --- a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs +++ b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs @@ -26,19 +26,23 @@ declare_lint! { /// /// ### Example /// - /// ``` + /// ```rust + /// trait Duh {} + /// + /// impl Duh for i32 {} + /// /// trait Trait { - /// type Assoc: Send; + /// type Assoc: Duh; /// } /// /// struct Struct; /// - /// impl Trait for Struct { - /// type Assoc = i32; + /// impl<F: Duh> Trait for F { + /// type Assoc = F; /// } /// /// fn test() -> impl Trait<Assoc = impl Sized> { - /// Struct + /// 42 /// } /// ``` /// @@ -70,7 +74,7 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound { // 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()); - let ty::PredicateKind::Projection(proj) = predicate else { + let ty::PredicateKind::Clause(ty::Clause::Projection(proj)) = predicate else { continue; }; // Only check types, since those are the only things that may @@ -104,6 +108,7 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound { // then we must've taken advantage of the hack in `project_and_unify_types` where // we replace opaques with inference vars. Emit a warning! if !infcx.predicate_must_hold_modulo_regions(&traits::Obligation::new( + cx.tcx, traits::ObligationCause::dummy(), cx.param_env, assoc_pred, @@ -111,12 +116,13 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound { // If it's a trait bound and an opaque that doesn't satisfy it, // then we can emit a suggestion to add the bound. let add_bound = match (proj_term.kind(), assoc_pred.kind().skip_binder()) { - (ty::Opaque(def_id, _), ty::PredicateKind::Trait(trait_pred)) => { - Some(AddBound { - suggest_span: cx.tcx.def_span(*def_id).shrink_to_hi(), - trait_ref: trait_pred.print_modifiers_and_trait_path(), - }) - } + ( + ty::Opaque(def_id, _), + ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)), + ) => Some(AddBound { + suggest_span: cx.tcx.def_span(*def_id).shrink_to_hi(), + trait_ref: trait_pred.print_modifiers_and_trait_path(), + }), _ => None, }; cx.emit_spanned_lint( @@ -150,8 +156,9 @@ struct OpaqueHiddenInferredBoundLint<'tcx> { } #[derive(Subdiagnostic)] -#[suggestion_verbose( +#[suggestion( lint_opaque_hidden_inferred_bound_sugg, + style = "verbose", applicability = "machine-applicable", code = " + {trait_ref}" )] diff --git a/compiler/rustc_lint/src/pass_by_value.rs b/compiler/rustc_lint/src/pass_by_value.rs index 01bface71..0fa81b7e4 100644 --- a/compiler/rustc_lint/src/pass_by_value.rs +++ b/compiler/rustc_lint/src/pass_by_value.rs @@ -10,7 +10,7 @@ declare_tool_lint! { /// The `rustc_pass_by_value` lint marks a type with `#[rustc_pass_by_value]` requiring it to /// always be passed by value. This is usually used for types that are thin wrappers around /// references, so there is no benefit to an extra layer of indirection. (Example: `Ty` which - /// is a reference to an `Interned<TyS>`) + /// is a reference to an `Interned<TyKind>`) pub rustc::PASS_BY_VALUE, Warn, "pass by reference of a type flagged as `#[rustc_pass_by_value]`", @@ -78,7 +78,7 @@ fn gen_args(cx: &LateContext<'_>, segment: &PathSegment<'_>) -> String { .args .iter() .map(|arg| match arg { - GenericArg::Lifetime(lt) => lt.name.ident().to_string(), + GenericArg::Lifetime(lt) => lt.to_string(), GenericArg::Type(ty) => { cx.tcx.sess.source_map().span_to_snippet(ty.span).unwrap_or_else(|_| "_".into()) } diff --git a/compiler/rustc_lint/src/passes.rs b/compiler/rustc_lint/src/passes.rs index 1c6a057d1..2f5398613 100644 --- a/compiler/rustc_lint/src/passes.rs +++ b/compiler/rustc_lint/src/passes.rs @@ -1,7 +1,6 @@ use crate::context::{EarlyContext, LateContext}; use rustc_ast as ast; -use rustc_data_structures::sync; use rustc_hir as hir; use rustc_session::lint::builtin::HardwiredLints; use rustc_session::lint::LintPass; @@ -44,7 +43,7 @@ macro_rules! late_lint_methods { fn check_struct_def(a: &$hir hir::VariantData<$hir>); fn check_field_def(a: &$hir hir::FieldDef<$hir>); fn check_variant(a: &$hir hir::Variant<$hir>); - fn check_path(a: &$hir hir::Path<$hir>, b: hir::HirId); + fn check_path(a: &hir::Path<$hir>, b: hir::HirId); fn check_attribute(a: &$hir ast::Attribute); /// Called when entering a syntax node that can have lint attributes such @@ -66,16 +65,10 @@ macro_rules! late_lint_methods { // FIXME: eliminate the duplication with `Visitor`. But this also // contains a few lint-specific methods with no equivalent in `Visitor`. -macro_rules! expand_lint_pass_methods { - ($context:ty, [$($(#[$attr:meta])* fn $name:ident($($param:ident: $arg:ty),*);)*]) => ( - $(#[inline(always)] fn $name(&mut self, _: $context, $(_: $arg),*) {})* - ) -} - macro_rules! declare_late_lint_pass { - ([], [$hir:tt], [$($methods:tt)*]) => ( + ([], [$hir:tt], [$($(#[$attr:meta])* fn $name:ident($($param:ident: $arg:ty),*);)*]) => ( pub trait LateLintPass<$hir>: LintPass { - expand_lint_pass_methods!(&LateContext<$hir>, [$($methods)*]); + $(#[inline(always)] fn $name(&mut self, _: &LateContext<$hir>, $(_: $arg),*) {})* } ) } @@ -175,16 +168,10 @@ macro_rules! early_lint_methods { ) } -macro_rules! expand_early_lint_pass_methods { - ($context:ty, [$($(#[$attr:meta])* fn $name:ident($($param:ident: $arg:ty),*);)*]) => ( - $(#[inline(always)] fn $name(&mut self, _: $context, $(_: $arg),*) {})* - ) -} - macro_rules! declare_early_lint_pass { - ([], [$($methods:tt)*]) => ( + ([], [$($(#[$attr:meta])* fn $name:ident($($param:ident: $arg:ty),*);)*]) => ( pub trait EarlyLintPass: LintPass { - expand_early_lint_pass_methods!(&EarlyContext<'_>, [$($methods)*]); + $(#[inline(always)] fn $name(&mut self, _: &EarlyContext<'_>, $(_: $arg),*) {})* } ) } @@ -243,5 +230,5 @@ macro_rules! declare_combined_early_lint_pass { } /// A lint pass boxed up as a trait object. -pub type EarlyLintPassObject = Box<dyn EarlyLintPass + sync::Send + 'static>; -pub type LateLintPassObject<'tcx> = Box<dyn LateLintPass<'tcx> + sync::Send + 'tcx>; +pub type EarlyLintPassObject = Box<dyn EarlyLintPass + 'static>; +pub type LateLintPassObject<'tcx> = Box<dyn LateLintPass<'tcx> + 'tcx>; diff --git a/compiler/rustc_lint/src/traits.rs b/compiler/rustc_lint/src/traits.rs index f22f38aa2..1b21c2dac 100644 --- a/compiler/rustc_lint/src/traits.rs +++ b/compiler/rustc_lint/src/traits.rs @@ -87,11 +87,12 @@ declare_lint_pass!( impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) { + use rustc_middle::ty::Clause; use rustc_middle::ty::PredicateKind::*; let predicates = cx.tcx.explicit_predicates_of(item.owner_id); for &(predicate, span) in predicates.predicates { - let Trait(trait_predicate) = predicate.kind().skip_binder() else { + let Clause(Clause::Trait(trait_predicate)) = predicate.kind().skip_binder() else { continue }; let def_id = trait_predicate.trait_ref.def_id; diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 37caab2da..297b509d4 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -12,7 +12,7 @@ use rustc_middle::ty::{self, AdtKind, DefIdTree, Ty, TyCtxt, TypeSuperVisitable, use rustc_span::source_map; use rustc_span::symbol::sym; use rustc_span::{Span, Symbol}; -use rustc_target::abi::{Abi, WrappingRange}; +use rustc_target::abi::{Abi, Size, WrappingRange}; use rustc_target::abi::{Integer, TagEncoding, Variants}; use rustc_target::spec::abi::Abi as SpecAbi; @@ -225,11 +225,11 @@ fn report_bin_hex_error( cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: attr::IntType, + size: Size, repr_str: String, val: u128, negative: bool, ) { - let size = Integer::from_attr(&cx.tcx, ty).size(); cx.struct_span_lint( OVERFLOWING_LITERALS, expr.span, @@ -352,6 +352,7 @@ fn lint_int_literal<'tcx>( cx, e, attr::IntType::SignedInt(ty::ast_int_ty(t)), + Integer::from_int_ty(cx, t).size(), repr_str, v, negative, @@ -360,7 +361,7 @@ fn lint_int_literal<'tcx>( } if lint_overflowing_range_endpoint(cx, lit, v, max, e, t.name_str()) { - // The overflowing literal lint was emited by `lint_overflowing_range_endpoint`. + // The overflowing literal lint was emitted by `lint_overflowing_range_endpoint`. return; } @@ -429,7 +430,7 @@ fn lint_uint_literal<'tcx>( } } if lint_overflowing_range_endpoint(cx, lit, lit_val, max, e, t.name_str()) { - // The overflowing literal lint was emited by `lint_overflowing_range_endpoint`. + // The overflowing literal lint was emitted by `lint_overflowing_range_endpoint`. return; } if let Some(repr_str) = get_bin_hex_repr(cx, lit) { @@ -437,6 +438,7 @@ fn lint_uint_literal<'tcx>( cx, e, attr::IntType::UnsignedInt(ty::ast_uint_ty(t)), + Integer::from_uint_ty(cx, t).size(), repr_str, lit_val, false, @@ -1195,35 +1197,30 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> { } fn check_for_opaque_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool { - struct ProhibitOpaqueTypes<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - } - - impl<'a, 'tcx> ty::visit::TypeVisitor<'tcx> for ProhibitOpaqueTypes<'a, 'tcx> { + struct ProhibitOpaqueTypes; + impl<'tcx> ty::visit::TypeVisitor<'tcx> for ProhibitOpaqueTypes { type BreakTy = Ty<'tcx>; fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { - match ty.kind() { - ty::Opaque(..) => ControlFlow::Break(ty), - // Consider opaque types within projections FFI-safe if they do not normalize - // to more opaque types. - ty::Projection(..) => { - let ty = self.cx.tcx.normalize_erasing_regions(self.cx.param_env, ty); - - // If `ty` is an opaque type directly then `super_visit_with` won't invoke - // this function again. - if ty.has_opaque_types() { - self.visit_ty(ty) - } else { - ControlFlow::CONTINUE - } - } - _ => ty.super_visit_with(self), + if !ty.has_opaque_types() { + return ControlFlow::CONTINUE; + } + + if let ty::Opaque(..) = ty.kind() { + ControlFlow::Break(ty) + } else { + ty.super_visit_with(self) } } } - if let Some(ty) = ty.visit_with(&mut ProhibitOpaqueTypes { cx: self.cx }).break_value() { + if let Some(ty) = self + .cx + .tcx + .normalize_erasing_regions(self.cx.param_env, ty) + .visit_with(&mut ProhibitOpaqueTypes) + .break_value() + { self.emit_ffi_unsafe_type_lint(ty, sp, fluent::lint_improper_ctypes_opaque, None); true } else { @@ -1381,7 +1378,7 @@ impl<'tcx> LateLintPass<'tcx> for VariantSizeDifferences { let (largest, slargest, largest_index) = iter::zip(enum_definition.variants, variants) .map(|(variant, variant_layout)| { // Subtract the size of the enum tag. - let bytes = variant_layout.size().bytes().saturating_sub(tag_size); + let bytes = variant_layout.size.bytes().saturating_sub(tag_size); debug!("- variant `{}` is {} bytes large", variant.ident, bytes); bytes diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 46706e498..b5db94f8c 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -9,10 +9,11 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_infer::traits::util::elaborate_predicates_with_span; use rustc_middle::ty::adjustment; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, DefIdTree, Ty}; use rustc_span::symbol::Symbol; use rustc_span::symbol::{kw, sym}; use rustc_span::{BytePos, Span}; +use std::iter; declare_lint! { /// The `unused_must_use` lint detects unused result of a type flagged as @@ -87,40 +88,45 @@ declare_lint_pass!(UnusedResults => [UNUSED_MUST_USE, UNUSED_RESULTS]); impl<'tcx> LateLintPass<'tcx> for UnusedResults { fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) { - let expr = match s.kind { - hir::StmtKind::Semi(ref expr) => &**expr, - _ => return, - }; + let hir::StmtKind::Semi(expr) = s.kind else { return; }; if let hir::ExprKind::Ret(..) = expr.kind { return; } + if let hir::ExprKind::Match(await_expr, _arms, hir::MatchSource::AwaitDesugar) = expr.kind + && let ty = cx.typeck_results().expr_ty(&await_expr) + && let ty::Opaque(future_def_id, _) = ty.kind() + && cx.tcx.ty_is_opaque_future(ty) + // FIXME: This also includes non-async fns that return `impl Future`. + && let async_fn_def_id = cx.tcx.parent(*future_def_id) + && check_must_use_def( + cx, + async_fn_def_id, + expr.span, + "output of future returned by ", + "", + ) + { + // We have a bare `foo().await;` on an opaque type from an async function that was + // annotated with `#[must_use]`. + return; + } + let ty = cx.typeck_results().expr_ty(&expr); - let type_permits_lack_of_use = check_must_use_ty(cx, ty, &expr, s.span, "", "", 1); - let mut fn_warned = false; - let mut op_warned = false; - let maybe_def_id = match expr.kind { - hir::ExprKind::Call(ref callee, _) => { - match callee.kind { - hir::ExprKind::Path(ref qpath) => { - match cx.qpath_res(qpath, callee.hir_id) { - Res::Def(DefKind::Fn | DefKind::AssocFn, def_id) => Some(def_id), - // `Res::Local` if it was a closure, for which we - // do not currently support must-use linting - _ => None, - } - } - _ => None, - } + 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); + true } - hir::ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id), - _ => None, + None => false, }; - if let Some(def_id) = maybe_def_id { - fn_warned = check_must_use_def(cx, def_id, s.span, "return value of ", ""); - } else if type_permits_lack_of_use { + + let fn_warned = check_fn_must_use(cx, expr); + + if !fn_warned && type_lint_emitted_or_suppressed { // We don't warn about unused unit or uninhabited types. // (See https://github.com/rust-lang/rust/issues/43806 for details.) return; @@ -154,6 +160,8 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { _ => None, }; + let mut op_warned = false; + if let Some(must_use_op) = must_use_op { cx.struct_span_lint(UNUSED_MUST_USE, expr.span, fluent::lint_unused_op, |lint| { lint.set_arg("op", must_use_op) @@ -168,110 +176,240 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { op_warned = true; } - if !(type_permits_lack_of_use || fn_warned || op_warned) { + if !(type_lint_emitted_or_suppressed || fn_warned || op_warned) { cx.struct_span_lint(UNUSED_RESULTS, s.span, fluent::lint_unused_result, |lint| { lint.set_arg("ty", ty) }); } - // Returns whether an error has been emitted (and thus another does not need to be later). - fn check_must_use_ty<'tcx>( + fn check_fn_must_use(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { + let maybe_def_id = match expr.kind { + hir::ExprKind::Call(ref callee, _) => { + match callee.kind { + hir::ExprKind::Path(ref qpath) => { + match cx.qpath_res(qpath, callee.hir_id) { + Res::Def(DefKind::Fn | DefKind::AssocFn, def_id) => Some(def_id), + // `Res::Local` if it was a closure, for which we + // do not currently support must-use linting + _ => None, + } + } + _ => None, + } + } + hir::ExprKind::MethodCall(..) => { + cx.typeck_results().type_dependent_def_id(expr.hir_id) + } + _ => None, + }; + if let Some(def_id) = maybe_def_id { + check_must_use_def(cx, def_id, expr.span, "return value of ", "") + } else { + false + } + } + + /// A path through a type to a must_use source. Contains useful info for the lint. + #[derive(Debug)] + enum MustUsePath { + /// Suppress must_use checking. + Suppressed, + /// The root of the normal must_use lint with an optional message. + Def(Span, DefId, Option<Symbol>), + Boxed(Box<Self>), + Opaque(Box<Self>), + TraitObject(Box<Self>), + TupleElement(Vec<(usize, Self)>), + Array(Box<Self>, u64), + /// The root of the unused_closures lint. + Closure(Span), + /// The root of the unused_generators lint. + Generator(Span), + } + + #[instrument(skip(cx, expr), level = "debug", ret)] + fn is_ty_must_use<'tcx>( cx: &LateContext<'tcx>, ty: Ty<'tcx>, expr: &hir::Expr<'_>, span: Span, - descr_pre: &str, - descr_post: &str, - plural_len: usize, - ) -> bool { + ) -> Option<MustUsePath> { if ty.is_unit() - || cx.tcx.is_ty_uninhabited_from( + || !ty.is_inhabited_from( + cx.tcx, cx.tcx.parent_module(expr.hir_id).to_def_id(), - ty, cx.param_env, ) { - return true; + return Some(MustUsePath::Suppressed); } - let plural_suffix = pluralize!(plural_len); - match *ty.kind() { ty::Adt(..) if ty.is_box() => { let boxed_ty = ty.boxed_ty(); - let descr_pre = &format!("{}boxed ", descr_pre); - check_must_use_ty(cx, boxed_ty, expr, span, descr_pre, descr_post, plural_len) + is_ty_must_use(cx, boxed_ty, expr, span) + .map(|inner| MustUsePath::Boxed(Box::new(inner))) } - ty::Adt(def, _) => check_must_use_def(cx, def.did(), span, descr_pre, descr_post), + ty::Adt(def, _) => is_def_must_use(cx, def.did(), span), ty::Opaque(def, _) => { - let mut has_emitted = false; - for obligation in elaborate_predicates_with_span( + elaborate_predicates_with_span( cx.tcx, cx.tcx.explicit_item_bounds(def).iter().cloned(), - ) { + ) + .filter_map(|obligation| { // We only look at the `DefId`, so it is safe to skip the binder here. - if let ty::PredicateKind::Trait(ref poly_trait_predicate) = - obligation.predicate.kind().skip_binder() + if let ty::PredicateKind::Clause(ty::Clause::Trait( + ref poly_trait_predicate, + )) = obligation.predicate.kind().skip_binder() { let def_id = poly_trait_predicate.trait_ref.def_id; - let descr_pre = - &format!("{}implementer{} of ", descr_pre, plural_suffix,); - if check_must_use_def(cx, def_id, span, descr_pre, descr_post) { - has_emitted = true; - break; - } + + is_def_must_use(cx, def_id, span) + } else { + None } - } - has_emitted + }) + .map(|inner| MustUsePath::Opaque(Box::new(inner))) + .next() } - ty::Dynamic(binder, _, _) => { - let mut has_emitted = false; - for predicate in binder.iter() { + ty::Dynamic(binders, _, _) => binders + .iter() + .filter_map(|predicate| { if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() { let def_id = trait_ref.def_id; - let descr_post = - &format!(" trait object{}{}", plural_suffix, descr_post,); - if check_must_use_def(cx, def_id, span, descr_pre, descr_post) { - has_emitted = true; - break; - } + is_def_must_use(cx, def_id, span) + } else { + None } - } - has_emitted - } - ty::Tuple(ref tys) => { - let mut has_emitted = false; - let comps = if let hir::ExprKind::Tup(comps) = expr.kind { - debug_assert_eq!(comps.len(), tys.len()); - comps + .map(|inner| MustUsePath::TraitObject(Box::new(inner))) + }) + .next(), + ty::Tuple(tys) => { + let elem_exprs = if let hir::ExprKind::Tup(elem_exprs) = expr.kind { + debug_assert_eq!(elem_exprs.len(), tys.len()); + elem_exprs } else { &[] }; - for (i, ty) in tys.iter().enumerate() { - let descr_post = &format!(" in tuple element {}", i); - let e = comps.get(i).unwrap_or(expr); - let span = e.span; - if check_must_use_ty(cx, ty, e, span, descr_pre, descr_post, plural_len) { - has_emitted = true; - } + + // Default to `expr`. + let elem_exprs = elem_exprs.iter().chain(iter::repeat(expr)); + + let nested_must_use = tys + .iter() + .zip(elem_exprs) + .enumerate() + .filter_map(|(i, (ty, expr))| { + is_ty_must_use(cx, ty, expr, expr.span).map(|path| (i, path)) + }) + .collect::<Vec<_>>(); + + if !nested_must_use.is_empty() { + Some(MustUsePath::TupleElement(nested_must_use)) + } else { + None } - has_emitted } ty::Array(ty, len) => match len.try_eval_usize(cx.tcx, cx.param_env) { // If the array is empty we don't lint, to avoid false positives - Some(0) | None => false, + Some(0) | None => None, // If the array is definitely non-empty, we can do `#[must_use]` checking. - Some(n) => { - let descr_pre = &format!("{}array{} of ", descr_pre, plural_suffix,); - check_must_use_ty(cx, ty, expr, span, descr_pre, descr_post, n as usize + 1) - } + Some(len) => is_ty_must_use(cx, ty, expr, span) + .map(|inner| MustUsePath::Array(Box::new(inner), len)), }, - ty::Closure(..) => { + ty::Closure(..) => Some(MustUsePath::Closure(span)), + ty::Generator(def_id, ..) => { + // async fn should be treated as "implementor of `Future`" + let must_use = if cx.tcx.generator_is_async(def_id) { + let def_id = cx.tcx.lang_items().future_trait().unwrap(); + is_def_must_use(cx, def_id, span) + .map(|inner| MustUsePath::Opaque(Box::new(inner))) + } else { + None + }; + must_use.or(Some(MustUsePath::Generator(span))) + } + _ => None, + } + } + + fn is_def_must_use(cx: &LateContext<'_>, def_id: DefId, span: Span) -> Option<MustUsePath> { + if let Some(attr) = cx.tcx.get_attr(def_id, sym::must_use) { + // check for #[must_use = "..."] + let reason = attr.value_str(); + Some(MustUsePath::Def(span, def_id, reason)) + } else { + None + } + } + + // Returns whether further errors should be suppressed because either a lint has been emitted or the type should be ignored. + fn check_must_use_def( + cx: &LateContext<'_>, + def_id: DefId, + span: Span, + descr_pre_path: &str, + descr_post_path: &str, + ) -> bool { + is_def_must_use(cx, def_id, span) + .map(|must_use_path| { + emit_must_use_untranslated( + cx, + &must_use_path, + descr_pre_path, + descr_post_path, + 1, + ) + }) + .is_some() + } + + #[instrument(skip(cx), level = "debug")] + fn emit_must_use_untranslated( + cx: &LateContext<'_>, + path: &MustUsePath, + descr_pre: &str, + descr_post: &str, + plural_len: usize, + ) { + let plural_suffix = pluralize!(plural_len); + + match path { + MustUsePath::Suppressed => {} + MustUsePath::Boxed(path) => { + let descr_pre = &format!("{}boxed ", descr_pre); + emit_must_use_untranslated(cx, path, descr_pre, descr_post, plural_len); + } + MustUsePath::Opaque(path) => { + let descr_pre = &format!("{}implementer{} of ", descr_pre, plural_suffix); + emit_must_use_untranslated(cx, path, descr_pre, descr_post, plural_len); + } + MustUsePath::TraitObject(path) => { + let descr_post = &format!(" trait object{}{}", plural_suffix, descr_post); + emit_must_use_untranslated(cx, path, descr_pre, descr_post, plural_len); + } + MustUsePath::TupleElement(elems) => { + for (index, path) in elems { + let descr_post = &format!(" in tuple element {}", index); + emit_must_use_untranslated(cx, path, descr_pre, descr_post, plural_len); + } + } + MustUsePath::Array(path, len) => { + let descr_pre = &format!("{}array{} of ", descr_pre, plural_suffix); + emit_must_use_untranslated( + cx, + path, + descr_pre, + descr_post, + plural_len.saturating_add(usize::try_from(*len).unwrap_or(usize::MAX)), + ); + } + MustUsePath::Closure(span) => { cx.struct_span_lint( UNUSED_MUST_USE, - span, + *span, fluent::lint_unused_closure, |lint| { // FIXME(davidtwco): this isn't properly translatable because of the @@ -282,12 +420,11 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { .note(fluent::note) }, ); - true } - ty::Generator(..) => { + MustUsePath::Generator(span) => { cx.struct_span_lint( UNUSED_MUST_USE, - span, + *span, fluent::lint_unused_generator, |lint| { // FIXME(davidtwco): this isn't properly translatable because of the @@ -298,40 +435,20 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { .note(fluent::note) }, ); - true } - _ => false, - } - } - - // Returns whether an error has been emitted (and thus another does not need to be later). - // FIXME: Args desc_{pre,post}_path could be made lazy by taking Fn() -> &str, but this - // would make calling it a big awkward. Could also take String (so args are moved), but - // this would still require a copy into the format string, which would only be executed - // when needed. - fn check_must_use_def( - cx: &LateContext<'_>, - def_id: DefId, - span: Span, - descr_pre_path: &str, - descr_post_path: &str, - ) -> bool { - if let Some(attr) = cx.tcx.get_attr(def_id, sym::must_use) { - cx.struct_span_lint(UNUSED_MUST_USE, span, fluent::lint_unused_def, |lint| { - // FIXME(davidtwco): this isn't properly translatable because of the pre/post - // strings - lint.set_arg("pre", descr_pre_path); - lint.set_arg("post", descr_post_path); - lint.set_arg("def", cx.tcx.def_path_str(def_id)); - // check for #[must_use = "..."] - if let Some(note) = attr.value_str() { - lint.note(note.as_str()); - } - lint - }); - true - } else { - false + MustUsePath::Def(span, def_id, reason) => { + cx.struct_span_lint(UNUSED_MUST_USE, *span, fluent::lint_unused_def, |lint| { + // FIXME(davidtwco): this isn't properly translatable because of the pre/post + // strings + lint.set_arg("pre", descr_pre); + lint.set_arg("post", descr_post); + lint.set_arg("def", cx.tcx.def_path_str(*def_id)); + if let Some(note) = reason { + lint.note(note.as_str()); + } + lint + }); + } } } } @@ -516,17 +633,36 @@ trait UnusedDelimLint { left_pos: Option<BytePos>, right_pos: Option<BytePos>, ) { + // If `value` has `ExprKind::Err`, unused delim lint can be broken. + // For example, the following code caused ICE. + // This is because the `ExprKind::Call` in `value` has `ExprKind::Err` as its argument + // and this leads to wrong spans. #104897 + // + // ``` + // fn f(){(print!(รก + // ``` + use rustc_ast::visit::{walk_expr, Visitor}; + struct ErrExprVisitor { + has_error: bool, + } + impl<'ast> Visitor<'ast> for ErrExprVisitor { + fn visit_expr(&mut self, expr: &'ast ast::Expr) { + if let ExprKind::Err = expr.kind { + self.has_error = true; + return; + } + walk_expr(self, expr) + } + } + let mut visitor = ErrExprVisitor { has_error: false }; + visitor.visit_expr(value); + if visitor.has_error { + return; + } let spans = match value.kind { - ast::ExprKind::Block(ref block, None) if block.stmts.len() > 0 => { - let start = block.stmts[0].span; - let end = block.stmts[block.stmts.len() - 1].span; - if let Some(start) = start.find_ancestor_inside(value.span) - && let Some(end) = end.find_ancestor_inside(value.span) - { - Some(( - value.span.with_hi(start.lo()), - value.span.with_lo(end.hi()), - )) + 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 } @@ -565,10 +701,24 @@ trait UnusedDelimLint { lint.set_arg("delim", Self::DELIM_STR); lint.set_arg("item", msg); if let Some((lo, hi)) = spans { - let replacement = vec![ - (lo, if keep_space.0 { " ".into() } else { "".into() }), - (hi, if keep_space.1 { " ".into() } else { "".into() }), - ]; + let sm = cx.sess().source_map(); + let lo_replace = + if keep_space.0 && + let Ok(snip) = sm.span_to_prev_source(lo) && !snip.ends_with(' ') { + " ".to_string() + } else { + "".to_string() + }; + + let hi_replace = + if keep_space.1 && + let Ok(snip) = sm.span_to_next_source(hi) && !snip.starts_with(' ') { + " ".to_string() + } else { + "".to_string() + }; + + let replacement = vec![(lo, lo_replace), (hi, hi_replace)]; lint.multipart_suggestion( fluent::suggestion, replacement, @@ -623,7 +773,7 @@ trait UnusedDelimLint { ref call_or_other => { let (args_to_check, ctx) = match *call_or_other { Call(_, ref args) => (&args[..], UnusedDelimsCtx::FunctionArg), - MethodCall(_, _, ref args, _) => (&args[..], UnusedDelimsCtx::MethodArg), + MethodCall(ref call) => (&call.args[..], UnusedDelimsCtx::MethodArg), // actual catch-all arm _ => { return; @@ -765,6 +915,7 @@ impl UnusedParens { value: &ast::Pat, avoid_or: bool, avoid_mut: bool, + keep_space: (bool, bool), ) { use ast::{BindingAnnotation, PatKind}; @@ -789,7 +940,7 @@ impl UnusedParens { } else { None }; - self.emit_unused_delims(cx, value.span, spans, "pattern", (false, false)); + self.emit_unused_delims(cx, value.span, spans, "pattern", keep_space); } } } @@ -798,7 +949,7 @@ impl EarlyLintPass for UnusedParens { fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { match e.kind { ExprKind::Let(ref pat, _, _) | ExprKind::ForLoop(ref pat, ..) => { - self.check_unused_parens_pat(cx, pat, false, false); + self.check_unused_parens_pat(cx, pat, false, false, (true, true)); } // We ignore parens in cases like `if (((let Some(0) = Some(1))))` because we already // handle a hard error for them during AST lowering in `lower_expr_mut`, but we still @@ -842,6 +993,7 @@ impl EarlyLintPass for UnusedParens { fn check_pat(&mut self, cx: &EarlyContext<'_>, p: &ast::Pat) { use ast::{Mutability, PatKind::*}; + let keep_space = (false, false); match &p.kind { // Do not lint on `(..)` as that will result in the other arms being useless. Paren(_) @@ -849,39 +1001,40 @@ impl EarlyLintPass for UnusedParens { | Wild | 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); + self.check_unused_parens_pat(cx, p, false, false, keep_space); }, Struct(_, _, fps, _) => for f in fps { - self.check_unused_parens_pat(cx, &f.pat, false, false); + self.check_unused_parens_pat(cx, &f.pat, false, false, keep_space); }, // Avoid linting on `i @ (p0 | .. | pn)` and `box (p0 | .. | pn)`, #64106. - Ident(.., Some(p)) | Box(p) => self.check_unused_parens_pat(cx, p, true, false), + Ident(.., Some(p)) | Box(p) => self.check_unused_parens_pat(cx, p, true, false, keep_space), // Avoid linting on `&(mut x)` as `&mut x` has a different meaning, #55342. // Also avoid linting on `& mut? (p0 | .. | pn)`, #64106. - Ref(p, m) => self.check_unused_parens_pat(cx, p, true, *m == Mutability::Not), + Ref(p, m) => self.check_unused_parens_pat(cx, p, true, *m == Mutability::Not, keep_space), } } fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) { if let StmtKind::Local(ref local) = s.kind { - self.check_unused_parens_pat(cx, &local.pat, true, false); + self.check_unused_parens_pat(cx, &local.pat, true, false, (false, false)); } <Self as UnusedDelimLint>::check_stmt(self, cx, s) } fn check_param(&mut self, cx: &EarlyContext<'_>, param: &ast::Param) { - self.check_unused_parens_pat(cx, ¶m.pat, true, false); + self.check_unused_parens_pat(cx, ¶m.pat, true, false, (false, false)); } fn check_arm(&mut self, cx: &EarlyContext<'_>, arm: &ast::Arm) { - self.check_unused_parens_pat(cx, &arm.pat, false, false); + self.check_unused_parens_pat(cx, &arm.pat, false, false, (false, false)); } fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) { if let ast::TyKind::Paren(r) = &ty.kind { match &r.kind { ast::TyKind::TraitObject(..) => {} + ast::TyKind::BareFn(b) if b.generic_params.len() > 0 => {} ast::TyKind::ImplTrait(_, bounds) if bounds.len() > 1 => {} ast::TyKind::Array(_, len) => { self.check_unused_delims_expr( @@ -1132,7 +1285,7 @@ impl UnusedImportBraces { // Trigger the lint if the nested item is a non-self single item let node_name = match items[0].0.kind { - ast::UseTreeKind::Simple(rename, ..) => { + ast::UseTreeKind::Simple(rename) => { let orig_ident = items[0].0.prefix.segments.last().unwrap().ident; if orig_ident.name == kw::SelfLower { return; diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 61ee467f5..82b837fba 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -605,7 +605,7 @@ declare_lint! { /// /// ### Example /// - /// ``` + /// ```rust /// #[warn(unused_tuple_struct_fields)] /// struct S(i32, i32, i32); /// let s = S(1, 2, 3); @@ -1154,7 +1154,7 @@ declare_lint! { /// /// ### Example /// - /// ```compile_fail + /// ```rust,compile_fail /// #[repr(packed)] /// pub struct Foo { /// field1: u64, @@ -2615,7 +2615,7 @@ declare_lint! { /// /// ### Example /// - /// ```compile_fail + /// ```rust,compile_fail /// # #![allow(unused)] /// enum E { /// A, @@ -3330,7 +3330,6 @@ declare_lint_pass! { UNUSED_TUPLE_STRUCT_FIELDS, NON_EXHAUSTIVE_OMITTED_PATTERNS, TEXT_DIRECTION_CODEPOINT_IN_COMMENT, - DEREF_INTO_DYN_SUPERTRAIT, DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME, DUPLICATE_MACRO_ATTRIBUTES, SUSPICIOUS_AUTO_TRAIT_IMPLS, @@ -3339,6 +3338,7 @@ declare_lint_pass! { FFI_UNWIND_CALLS, REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS, NAMED_ARGUMENTS_USED_POSITIONALLY, + IMPLIED_BOUNDS_ENTAILMENT, ] } @@ -3833,51 +3833,6 @@ declare_lint! { } declare_lint! { - /// The `deref_into_dyn_supertrait` lint is output whenever there is a use of the - /// `Deref` implementation with a `dyn SuperTrait` type as `Output`. - /// - /// These implementations will become shadowed when the `trait_upcasting` feature is stabilized. - /// The `deref` functions will no longer be called implicitly, so there might be behavior change. - /// - /// ### Example - /// - /// ```rust,compile_fail - /// #![deny(deref_into_dyn_supertrait)] - /// #![allow(dead_code)] - /// - /// use core::ops::Deref; - /// - /// trait A {} - /// trait B: A {} - /// impl<'a> Deref for dyn 'a + B { - /// type Target = dyn A; - /// fn deref(&self) -> &Self::Target { - /// todo!() - /// } - /// } - /// - /// fn take_a(_: &dyn A) { } - /// - /// fn take_b(b: &dyn B) { - /// take_a(b); - /// } - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// The dyn upcasting coercion feature adds new coercion rules, taking priority - /// over certain other coercion rules, which will cause some behavior change. - pub DEREF_INTO_DYN_SUPERTRAIT, - Warn, - "`Deref` implementation usage with a supertrait trait object for output might be shadowed in the future", - @future_incompatible = FutureIncompatibleInfo { - reference: "issue #89460 <https://github.com/rust-lang/rust/issues/89460>", - }; -} - -declare_lint! { /// The `duplicate_macro_attributes` lint detects when a `#[test]`-like built-in macro /// attribute is duplicated on an item. This lint may trigger on `bench`, `cfg_eval`, `test` /// and `test_case`. @@ -3986,7 +3941,7 @@ declare_lint! { /// /// ### Example /// - /// ``` + /// ```rust /// #![allow(test_unstable_lint)] /// ``` /// @@ -4071,3 +4026,44 @@ declare_lint! { Warn, "named arguments in format used positionally" } + +declare_lint! { + /// The `implied_bounds_entailment` lint detects cases where the arguments of an impl method + /// have stronger implied bounds than those from the trait method it's implementing. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(implied_bounds_entailment)] + /// + /// trait Trait { + /// fn get<'s>(s: &'s str, _: &'static &'static ()) -> &'static str; + /// } + /// + /// impl Trait for () { + /// fn get<'s>(s: &'s str, _: &'static &'s ()) -> &'static str { + /// s + /// } + /// } + /// + /// let val = <() as Trait>::get(&String::from("blah blah blah"), &&()); + /// println!("{}", val); + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Neither the trait method, which provides no implied bounds about `'s`, nor the impl, + /// requires the main function to prove that 's: 'static, but the impl method is allowed + /// to assume that `'s: 'static` within its own body. + /// + /// This can be used to implement an unsound API if used incorrectly. + pub IMPLIED_BOUNDS_ENTAILMENT, + Warn, + "impl method assumes more implied bounds than its corresponding trait method", + @future_incompatible = FutureIncompatibleInfo { + reference: "issue #105572 <https://github.com/rust-lang/rust/issues/105572>", + reason: FutureIncompatibilityReason::FutureReleaseError, + }; +} |