summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_lint
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_lint')
-rw-r--r--compiler/rustc_lint/src/builtin.rs288
-rw-r--r--compiler/rustc_lint/src/context.rs25
-rw-r--r--compiler/rustc_lint/src/deref_into_dyn_supertrait.rs92
-rw-r--r--compiler/rustc_lint/src/early.rs193
-rw-r--r--compiler/rustc_lint/src/errors.rs2
-rw-r--r--compiler/rustc_lint/src/for_loops_over_fallibles.rs11
-rw-r--r--compiler/rustc_lint/src/hidden_unicode_codepoints.rs15
-rw-r--r--compiler/rustc_lint/src/internal.rs12
-rw-r--r--compiler/rustc_lint/src/late.rs132
-rw-r--r--compiler/rustc_lint/src/let_underscore.rs5
-rw-r--r--compiler/rustc_lint/src/levels.rs7
-rw-r--r--compiler/rustc_lint/src/lib.rs278
-rw-r--r--compiler/rustc_lint/src/non_fmt_panic.rs23
-rw-r--r--compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs33
-rw-r--r--compiler/rustc_lint/src/pass_by_value.rs4
-rw-r--r--compiler/rustc_lint/src/passes.rs27
-rw-r--r--compiler/rustc_lint/src/traits.rs3
-rw-r--r--compiler/rustc_lint/src/types.rs51
-rw-r--r--compiler/rustc_lint/src/unused.rs447
19 files changed, 898 insertions, 750 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, &param.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, &param.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, &param.pat, true, false);
+ self.check_unused_parens_pat(cx, &param.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;