summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_lint/src
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--compiler/rustc_lint/src/array_into_iter.rs65
-rw-r--r--compiler/rustc_lint/src/builtin.rs1049
-rw-r--r--compiler/rustc_lint/src/context.rs224
-rw-r--r--compiler/rustc_lint/src/early.rs36
-rw-r--r--compiler/rustc_lint/src/enum_intrinsics_non_enums.rs26
-rw-r--r--compiler/rustc_lint/src/errors.rs150
-rw-r--r--compiler/rustc_lint/src/expect.rs14
-rw-r--r--compiler/rustc_lint/src/for_loops_over_fallibles.rs183
-rw-r--r--compiler/rustc_lint/src/hidden_unicode_codepoints.rs98
-rw-r--r--compiler/rustc_lint/src/internal.rs233
-rw-r--r--compiler/rustc_lint/src/late.rs84
-rw-r--r--compiler/rustc_lint/src/let_underscore.rs168
-rw-r--r--compiler/rustc_lint/src/levels.rs1037
-rw-r--r--compiler/rustc_lint/src/lib.rs68
-rw-r--r--compiler/rustc_lint/src/methods.rs37
-rw-r--r--compiler/rustc_lint/src/non_ascii_idents.rs68
-rw-r--r--compiler/rustc_lint/src/non_fmt_panic.rs113
-rw-r--r--compiler/rustc_lint/src/nonstandard_style.rs136
-rw-r--r--compiler/rustc_lint/src/noop_method_call.rs30
-rw-r--r--compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs163
-rw-r--r--compiler/rustc_lint/src/pass_by_value.rs18
-rw-r--r--compiler/rustc_lint/src/passes.rs14
-rw-r--r--compiler/rustc_lint/src/redundant_semicolon.rs19
-rw-r--r--compiler/rustc_lint/src/traits.rs36
-rw-r--r--compiler/rustc_lint/src/types.rs442
-rw-r--r--compiler/rustc_lint/src/unused.rs255
26 files changed, 2998 insertions, 1768 deletions
diff --git a/compiler/rustc_lint/src/array_into_iter.rs b/compiler/rustc_lint/src/array_into_iter.rs
index 121fefdc6..abebc533c 100644
--- a/compiler/rustc_lint/src/array_into_iter.rs
+++ b/compiler/rustc_lint/src/array_into_iter.rs
@@ -61,7 +61,7 @@ impl<'tcx> LateLintPass<'tcx> for ArrayIntoIter {
}
// We only care about method call expressions.
- if let hir::ExprKind::MethodCall(call, args, _) = &expr.kind {
+ if let hir::ExprKind::MethodCall(call, receiver_arg, ..) = &expr.kind {
if call.ident.name != sym::into_iter {
return;
}
@@ -75,7 +75,6 @@ impl<'tcx> LateLintPass<'tcx> for ArrayIntoIter {
};
// As this is a method call expression, we have at least one argument.
- let receiver_arg = &args[0];
let receiver_ty = cx.typeck_results().expr_ty(receiver_arg);
let adjustments = cx.typeck_results().expr_adjustments(receiver_arg);
@@ -119,37 +118,41 @@ impl<'tcx> LateLintPass<'tcx> for ArrayIntoIter {
// to an array or to a slice.
_ => bug!("array type coerced to something other than array or slice"),
};
- cx.struct_span_lint(ARRAY_INTO_ITER, call.ident.span, |lint| {
- let mut diag = lint.build(fluent::lint::array_into_iter);
- diag.set_arg("target", target);
- diag.span_suggestion(
- call.ident.span,
- fluent::lint::use_iter_suggestion,
- "iter",
- Applicability::MachineApplicable,
- );
- if self.for_expr_span == expr.span {
+ cx.struct_span_lint(
+ ARRAY_INTO_ITER,
+ call.ident.span,
+ fluent::lint_array_into_iter,
+ |diag| {
+ diag.set_arg("target", target);
diag.span_suggestion(
- receiver_arg.span.shrink_to_hi().to(expr.span.shrink_to_hi()),
- fluent::lint::remove_into_iter_suggestion,
- "",
- Applicability::MaybeIncorrect,
+ call.ident.span,
+ fluent::use_iter_suggestion,
+ "iter",
+ Applicability::MachineApplicable,
);
- } else if receiver_ty.is_array() {
- diag.multipart_suggestion(
- fluent::lint::use_explicit_into_iter_suggestion,
- vec![
- (expr.span.shrink_to_lo(), "IntoIterator::into_iter(".into()),
- (
- receiver_arg.span.shrink_to_hi().to(expr.span.shrink_to_hi()),
- ")".into(),
- ),
- ],
- Applicability::MaybeIncorrect,
- );
- }
- diag.emit();
- })
+ if self.for_expr_span == expr.span {
+ diag.span_suggestion(
+ receiver_arg.span.shrink_to_hi().to(expr.span.shrink_to_hi()),
+ fluent::remove_into_iter_suggestion,
+ "",
+ Applicability::MaybeIncorrect,
+ );
+ } else if receiver_ty.is_array() {
+ diag.multipart_suggestion(
+ fluent::use_explicit_into_iter_suggestion,
+ vec![
+ (expr.span.shrink_to_lo(), "IntoIterator::into_iter(".into()),
+ (
+ receiver_arg.span.shrink_to_hi().to(expr.span.shrink_to_hi()),
+ ")".into(),
+ ),
+ ],
+ Applicability::MaybeIncorrect,
+ );
+ }
+ diag
+ },
+ )
}
}
}
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index bd58021f7..d425adf47 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -21,6 +21,7 @@
//! `late_lint_methods!` invocation in `lib.rs`.
use crate::{
+ errors::BuiltinEllpisisInclusiveRangePatterns,
types::{transparent_newtype_field, CItemKind},
EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext,
};
@@ -32,8 +33,8 @@ use rustc_ast_pretty::pprust::{self, expr_to_string};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_errors::{
- fluent, Applicability, Diagnostic, DiagnosticMessage, DiagnosticStyledString,
- LintDiagnosticBuilder, MultiSpan,
+ fluent, Applicability, DelayDm, Diagnostic, DiagnosticBuilder, DiagnosticMessage,
+ DiagnosticStyledString, MultiSpan,
};
use rustc_feature::{deprecated_attributes, AttributeGate, BuiltinAttribute, GateIssue, Stability};
use rustc_hir as hir;
@@ -45,8 +46,7 @@ use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::layout::{LayoutError, LayoutOf};
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::subst::GenericArgKind;
-use rustc_middle::ty::Instance;
-use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_middle::ty::{self, Instance, Ty, TyCtxt, VariantDef};
use rustc_session::lint::{BuiltinLintDiagnostics, FutureIncompatibilityReason};
use rustc_span::edition::Edition;
use rustc_span::source_map::Spanned;
@@ -58,7 +58,6 @@ use rustc_trait_selection::traits::{self, misc::can_type_implement_copy};
use crate::nonstandard_style::{method_context, MethodLateContext};
use std::fmt::Write;
-use tracing::{debug, trace};
// hardwired lints from librustc_middle
pub use rustc_session::lint::builtin::*;
@@ -98,30 +97,31 @@ 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 {
- if let ast::ExprKind::Lit(ref lit) = pierce_parens(cond).kind {
- if let ast::LitKind::Bool(true) = lit.kind {
- if !lit.span.from_expansion() {
- let condition_span = e.span.with_hi(cond.span.hi());
- cx.struct_span_lint(WHILE_TRUE, condition_span, |lint| {
- lint.build(fluent::lint::builtin_while_true)
- .span_suggestion_short(
- condition_span,
- fluent::lint::suggestion,
- format!(
- "{}loop",
- label.map_or_else(String::new, |label| format!(
- "{}: ",
- label.ident,
- ))
- ),
- Applicability::MachineApplicable,
- )
- .emit();
- })
- }
- }
- }
+ 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 condition_span = e.span.with_hi(cond.span.hi());
+ cx.struct_span_lint(
+ WHILE_TRUE,
+ condition_span,
+ fluent::lint_builtin_while_true,
+ |lint| {
+ lint.span_suggestion_short(
+ condition_span,
+ fluent::suggestion,
+ format!(
+ "{}loop",
+ label.map_or_else(String::new, |label| format!(
+ "{}: ",
+ label.ident,
+ ))
+ ),
+ Applicability::MachineApplicable,
+ )
+ },
+ )
}
}
}
@@ -157,9 +157,12 @@ impl BoxPointers {
for leaf in ty.walk() {
if let GenericArgKind::Type(leaf_ty) = leaf.unpack() {
if leaf_ty.is_box() {
- cx.struct_span_lint(BOX_POINTERS, span, |lint| {
- lint.build(fluent::lint::builtin_box_pointers).set_arg("ty", ty).emit();
- });
+ cx.struct_span_lint(
+ BOX_POINTERS,
+ span,
+ fluent::lint_builtin_box_pointers,
+ |lint| lint.set_arg("ty", ty),
+ );
}
}
}
@@ -174,7 +177,7 @@ impl<'tcx> LateLintPass<'tcx> for BoxPointers {
| hir::ItemKind::Enum(..)
| hir::ItemKind::Struct(..)
| hir::ItemKind::Union(..) => {
- self.check_heap_type(cx, it.span, cx.tcx.type_of(it.def_id))
+ self.check_heap_type(cx, it.span, cx.tcx.type_of(it.owner_id))
}
_ => (),
}
@@ -258,28 +261,21 @@ impl<'tcx> LateLintPass<'tcx> for NonShorthandFieldPatterns {
if cx.tcx.find_field_index(ident, &variant)
== Some(cx.tcx.field_index(fieldpat.hir_id, cx.typeck_results()))
{
- cx.struct_span_lint(NON_SHORTHAND_FIELD_PATTERNS, fieldpat.span, |lint| {
- let binding = match binding_annot {
- hir::BindingAnnotation::Unannotated => None,
- hir::BindingAnnotation::Mutable => Some("mut"),
- hir::BindingAnnotation::Ref => Some("ref"),
- hir::BindingAnnotation::RefMut => Some("ref mut"),
- };
- let suggested_ident = if let Some(binding) = binding {
- format!("{} {}", binding, ident)
- } else {
- ident.to_string()
- };
- lint.build(fluent::lint::builtin_non_shorthand_field_patterns)
- .set_arg("ident", ident.clone())
- .span_suggestion(
+ cx.struct_span_lint(
+ NON_SHORTHAND_FIELD_PATTERNS,
+ fieldpat.span,
+ fluent::lint_builtin_non_shorthand_field_patterns,
+ |lint| {
+ let suggested_ident =
+ format!("{}{}", binding_annot.prefix_str(), ident);
+ lint.set_arg("ident", ident.clone()).span_suggestion(
fieldpat.span,
- fluent::lint::suggestion,
+ fluent::suggestion,
suggested_ident,
Applicability::MachineApplicable,
)
- .emit();
- });
+ },
+ );
}
}
}
@@ -319,14 +315,17 @@ impl UnsafeCode {
&self,
cx: &EarlyContext<'_>,
span: Span,
- decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a, ()>),
+ msg: impl Into<DiagnosticMessage>,
+ decorate: impl for<'a, 'b> FnOnce(
+ &'b mut DiagnosticBuilder<'a, ()>,
+ ) -> &'b mut DiagnosticBuilder<'a, ()>,
) {
// This comes from a macro that has `#[allow_internal_unsafe]`.
if span.allows_unsafe() {
return;
}
- cx.struct_span_lint(UNSAFE_CODE, span, decorate);
+ cx.struct_span_lint(UNSAFE_CODE, span, msg, decorate);
}
fn report_overridden_symbol_name(
@@ -335,8 +334,8 @@ impl UnsafeCode {
span: Span,
msg: DiagnosticMessage,
) {
- self.report_unsafe(cx, span, |lint| {
- lint.build(msg).note(fluent::lint::builtin_overridden_symbol_name).emit();
+ self.report_unsafe(cx, span, msg, |lint| {
+ lint.note(fluent::lint_builtin_overridden_symbol_name)
})
}
@@ -346,8 +345,8 @@ impl UnsafeCode {
span: Span,
msg: DiagnosticMessage,
) {
- self.report_unsafe(cx, span, |lint| {
- lint.build(msg).note(fluent::lint::builtin_overridden_symbol_section).emit();
+ self.report_unsafe(cx, span, msg, |lint| {
+ lint.note(fluent::lint_builtin_overridden_symbol_section)
})
}
}
@@ -355,8 +354,8 @@ impl UnsafeCode {
impl EarlyLintPass for UnsafeCode {
fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &ast::Attribute) {
if attr.has_name(sym::allow_internal_unsafe) {
- self.report_unsafe(cx, attr.span, |lint| {
- lint.build(fluent::lint::builtin_allow_internal_unsafe).emit();
+ self.report_unsafe(cx, attr.span, fluent::lint_builtin_allow_internal_unsafe, |lint| {
+ lint
});
}
}
@@ -365,31 +364,27 @@ impl EarlyLintPass for UnsafeCode {
if let ast::ExprKind::Block(ref blk, _) = e.kind {
// Don't warn about generated blocks; that'll just pollute the output.
if blk.rules == ast::BlockCheckMode::Unsafe(ast::UserProvided) {
- self.report_unsafe(cx, blk.span, |lint| {
- lint.build(fluent::lint::builtin_unsafe_block).emit();
- });
+ self.report_unsafe(cx, blk.span, fluent::lint_builtin_unsafe_block, |lint| lint);
}
}
}
fn check_item(&mut self, cx: &EarlyContext<'_>, it: &ast::Item) {
match it.kind {
- ast::ItemKind::Trait(box ast::Trait { unsafety: ast::Unsafe::Yes(_), .. }) => self
- .report_unsafe(cx, it.span, |lint| {
- lint.build(fluent::lint::builtin_unsafe_trait).emit();
- }),
+ ast::ItemKind::Trait(box ast::Trait { unsafety: ast::Unsafe::Yes(_), .. }) => {
+ self.report_unsafe(cx, it.span, fluent::lint_builtin_unsafe_trait, |lint| lint)
+ }
- ast::ItemKind::Impl(box ast::Impl { unsafety: ast::Unsafe::Yes(_), .. }) => self
- .report_unsafe(cx, it.span, |lint| {
- lint.build(fluent::lint::builtin_unsafe_impl).emit();
- }),
+ ast::ItemKind::Impl(box ast::Impl { unsafety: ast::Unsafe::Yes(_), .. }) => {
+ self.report_unsafe(cx, it.span, fluent::lint_builtin_unsafe_impl, |lint| lint)
+ }
ast::ItemKind::Fn(..) => {
if let Some(attr) = cx.sess().find_by_name(&it.attrs, sym::no_mangle) {
self.report_overridden_symbol_name(
cx,
attr.span,
- fluent::lint::builtin_no_mangle_fn,
+ fluent::lint_builtin_no_mangle_fn,
);
}
@@ -397,7 +392,7 @@ impl EarlyLintPass for UnsafeCode {
self.report_overridden_symbol_name(
cx,
attr.span,
- fluent::lint::builtin_export_name_fn,
+ fluent::lint_builtin_export_name_fn,
);
}
@@ -405,7 +400,7 @@ impl EarlyLintPass for UnsafeCode {
self.report_overridden_symbol_section(
cx,
attr.span,
- fluent::lint::builtin_link_section_fn,
+ fluent::lint_builtin_link_section_fn,
);
}
}
@@ -415,7 +410,7 @@ impl EarlyLintPass for UnsafeCode {
self.report_overridden_symbol_name(
cx,
attr.span,
- fluent::lint::builtin_no_mangle_static,
+ fluent::lint_builtin_no_mangle_static,
);
}
@@ -423,7 +418,7 @@ impl EarlyLintPass for UnsafeCode {
self.report_overridden_symbol_name(
cx,
attr.span,
- fluent::lint::builtin_export_name_static,
+ fluent::lint_builtin_export_name_static,
);
}
@@ -431,7 +426,7 @@ impl EarlyLintPass for UnsafeCode {
self.report_overridden_symbol_section(
cx,
attr.span,
- fluent::lint::builtin_link_section_static,
+ fluent::lint_builtin_link_section_static,
);
}
}
@@ -446,14 +441,14 @@ impl EarlyLintPass for UnsafeCode {
self.report_overridden_symbol_name(
cx,
attr.span,
- fluent::lint::builtin_no_mangle_method,
+ fluent::lint_builtin_no_mangle_method,
);
}
if let Some(attr) = cx.sess().find_by_name(&it.attrs, sym::export_name) {
self.report_overridden_symbol_name(
cx,
attr.span,
- fluent::lint::builtin_export_name_method,
+ fluent::lint_builtin_export_name_method,
);
}
}
@@ -471,13 +466,11 @@ impl EarlyLintPass for UnsafeCode {
{
let msg = match ctxt {
FnCtxt::Foreign => return,
- FnCtxt::Free => fluent::lint::builtin_decl_unsafe_fn,
- FnCtxt::Assoc(_) if body.is_none() => fluent::lint::builtin_decl_unsafe_method,
- FnCtxt::Assoc(_) => fluent::lint::builtin_impl_unsafe_method,
+ FnCtxt::Free => fluent::lint_builtin_decl_unsafe_fn,
+ FnCtxt::Assoc(_) if body.is_none() => fluent::lint_builtin_decl_unsafe_method,
+ FnCtxt::Assoc(_) => fluent::lint_builtin_impl_unsafe_method,
};
- self.report_unsafe(cx, span, |lint| {
- lint.build(msg).emit();
- });
+ self.report_unsafe(cx, span, msg, |lint| lint);
}
}
}
@@ -570,7 +563,7 @@ impl MissingDoc {
// It's an option so the crate root can also use this function (it doesn't
// have a `NodeId`).
if def_id != CRATE_DEF_ID {
- if !cx.access_levels.is_exported(def_id) {
+ if !cx.effective_visibilities.is_exported(def_id) {
return;
}
}
@@ -578,12 +571,12 @@ impl MissingDoc {
let attrs = cx.tcx.hir().attrs(cx.tcx.hir().local_def_id_to_hir_id(def_id));
let has_doc = attrs.iter().any(has_doc);
if !has_doc {
- cx.struct_span_lint(MISSING_DOCS, cx.tcx.def_span(def_id), |lint| {
- lint.build(fluent::lint::builtin_missing_doc)
- .set_arg("article", article)
- .set_arg("desc", desc)
- .emit();
- });
+ cx.struct_span_lint(
+ MISSING_DOCS,
+ cx.tcx.def_span(def_id),
+ fluent::lint_builtin_missing_doc,
+ |lint| lint.set_arg("article", article).set_arg("desc", desc),
+ );
}
}
}
@@ -613,9 +606,9 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
match it.kind {
hir::ItemKind::Trait(..) => {
// Issue #11592: traits are always considered exported, even when private.
- if cx.tcx.visibility(it.def_id)
+ if cx.tcx.visibility(it.owner_id)
== ty::Visibility::Restricted(
- cx.tcx.parent_module_from_def_id(it.def_id).to_def_id(),
+ cx.tcx.parent_module_from_def_id(it.owner_id.def_id).to_def_id(),
)
{
return;
@@ -634,15 +627,15 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
_ => return,
};
- let (article, desc) = cx.tcx.article_and_description(it.def_id.to_def_id());
+ let (article, desc) = cx.tcx.article_and_description(it.owner_id.to_def_id());
- self.check_missing_docs_attrs(cx, it.def_id, article, desc);
+ self.check_missing_docs_attrs(cx, it.owner_id.def_id, article, desc);
}
fn check_trait_item(&mut self, cx: &LateContext<'_>, trait_item: &hir::TraitItem<'_>) {
- let (article, desc) = cx.tcx.article_and_description(trait_item.def_id.to_def_id());
+ let (article, desc) = cx.tcx.article_and_description(trait_item.owner_id.to_def_id());
- self.check_missing_docs_attrs(cx, trait_item.def_id, article, desc);
+ self.check_missing_docs_attrs(cx, trait_item.owner_id.def_id, article, desc);
}
fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) {
@@ -669,13 +662,13 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
}
}
- let (article, desc) = cx.tcx.article_and_description(impl_item.def_id.to_def_id());
- self.check_missing_docs_attrs(cx, impl_item.def_id, article, desc);
+ let (article, desc) = cx.tcx.article_and_description(impl_item.owner_id.to_def_id());
+ self.check_missing_docs_attrs(cx, impl_item.owner_id.def_id, article, desc);
}
fn check_foreign_item(&mut self, cx: &LateContext<'_>, foreign_item: &hir::ForeignItem<'_>) {
- let (article, desc) = cx.tcx.article_and_description(foreign_item.def_id.to_def_id());
- self.check_missing_docs_attrs(cx, foreign_item.def_id, article, desc);
+ let (article, desc) = cx.tcx.article_and_description(foreign_item.owner_id.to_def_id());
+ self.check_missing_docs_attrs(cx, foreign_item.owner_id.def_id, article, desc);
}
fn check_field_def(&mut self, cx: &LateContext<'_>, sf: &hir::FieldDef<'_>) {
@@ -728,7 +721,7 @@ declare_lint_pass!(MissingCopyImplementations => [MISSING_COPY_IMPLEMENTATIONS])
impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations {
fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
- if !cx.access_levels.is_reachable(item.def_id) {
+ if !cx.effective_visibilities.is_reachable(item.owner_id.def_id) {
return;
}
let (def, ty) = match item.kind {
@@ -736,21 +729,21 @@ impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations {
if !ast_generics.params.is_empty() {
return;
}
- let def = cx.tcx.adt_def(item.def_id);
+ let def = cx.tcx.adt_def(item.owner_id);
(def, cx.tcx.mk_adt(def, cx.tcx.intern_substs(&[])))
}
hir::ItemKind::Union(_, ref ast_generics) => {
if !ast_generics.params.is_empty() {
return;
}
- let def = cx.tcx.adt_def(item.def_id);
+ let def = cx.tcx.adt_def(item.owner_id);
(def, cx.tcx.mk_adt(def, cx.tcx.intern_substs(&[])))
}
hir::ItemKind::Enum(_, ref ast_generics) => {
if !ast_generics.params.is_empty() {
return;
}
- let def = cx.tcx.adt_def(item.def_id);
+ let def = cx.tcx.adt_def(item.owner_id);
(def, cx.tcx.mk_adt(def, cx.tcx.intern_substs(&[])))
}
_ => return,
@@ -759,7 +752,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations {
return;
}
let param_env = ty::ParamEnv::empty();
- if ty.is_copy_modulo_regions(cx.tcx.at(item.span), param_env) {
+ if ty.is_copy_modulo_regions(cx.tcx, param_env) {
return;
}
if can_type_implement_copy(
@@ -770,9 +763,12 @@ impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations {
)
.is_ok()
{
- cx.struct_span_lint(MISSING_COPY_IMPLEMENTATIONS, item.span, |lint| {
- lint.build(fluent::lint::builtin_missing_copy_impl).emit();
- })
+ cx.struct_span_lint(
+ MISSING_COPY_IMPLEMENTATIONS,
+ item.span,
+ fluent::lint_builtin_missing_copy_impl,
+ |lint| lint,
+ )
}
}
}
@@ -818,7 +814,7 @@ impl_lint_pass!(MissingDebugImplementations => [MISSING_DEBUG_IMPLEMENTATIONS]);
impl<'tcx> LateLintPass<'tcx> for MissingDebugImplementations {
fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
- if !cx.access_levels.is_reachable(item.def_id) {
+ if !cx.effective_visibilities.is_reachable(item.owner_id.def_id) {
return;
}
@@ -845,12 +841,13 @@ impl<'tcx> LateLintPass<'tcx> for MissingDebugImplementations {
debug!("{:?}", self.impling_types);
}
- if !self.impling_types.as_ref().unwrap().contains(&item.def_id) {
- cx.struct_span_lint(MISSING_DEBUG_IMPLEMENTATIONS, item.span, |lint| {
- lint.build(fluent::lint::builtin_missing_debug_impl)
- .set_arg("debug", cx.tcx.def_path_str(debug))
- .emit();
- });
+ if !self.impling_types.as_ref().unwrap().contains(&item.owner_id.def_id) {
+ cx.struct_span_lint(
+ MISSING_DEBUG_IMPLEMENTATIONS,
+ item.span,
+ fluent::lint_builtin_missing_debug_impl,
+ |lint| lint.set_arg("debug", cx.tcx.def_path_str(debug)),
+ );
}
}
}
@@ -918,24 +915,26 @@ impl EarlyLintPass for AnonymousParameters {
for arg in sig.decl.inputs.iter() {
if let ast::PatKind::Ident(_, ident, None) = arg.pat.kind {
if ident.name == kw::Empty {
- cx.struct_span_lint(ANONYMOUS_PARAMETERS, arg.pat.span, |lint| {
- let ty_snip = cx.sess().source_map().span_to_snippet(arg.ty.span);
+ let ty_snip = cx.sess().source_map().span_to_snippet(arg.ty.span);
- let (ty_snip, appl) = if let Ok(ref snip) = ty_snip {
- (snip.as_str(), Applicability::MachineApplicable)
- } else {
- ("<type>", Applicability::HasPlaceholders)
- };
-
- lint.build(fluent::lint::builtin_anonymous_params)
- .span_suggestion(
+ let (ty_snip, appl) = if let Ok(ref snip) = ty_snip {
+ (snip.as_str(), Applicability::MachineApplicable)
+ } else {
+ ("<type>", Applicability::HasPlaceholders)
+ };
+ cx.struct_span_lint(
+ ANONYMOUS_PARAMETERS,
+ arg.pat.span,
+ fluent::lint_builtin_anonymous_params,
+ |lint| {
+ lint.span_suggestion(
arg.pat.span,
- fluent::lint::suggestion,
+ fluent::suggestion,
format!("_: {}", ty_snip),
appl,
)
- .emit();
- })
+ },
+ )
}
}
}
@@ -970,38 +969,44 @@ impl EarlyLintPass for DeprecatedAttr {
_,
) = gate
{
- cx.struct_span_lint(DEPRECATED, attr.span, |lint| {
- // FIXME(davidtwco) translatable deprecated attr
- lint.build(fluent::lint::builtin_deprecated_attr_link)
- .set_arg("name", name)
- .set_arg("reason", reason)
- .set_arg("link", link)
- .span_suggestion_short(
- attr.span,
- suggestion.map(|s| s.into()).unwrap_or(
- fluent::lint::builtin_deprecated_attr_default_suggestion,
- ),
- "",
- Applicability::MachineApplicable,
- )
- .emit();
- });
+ // FIXME(davidtwco) translatable deprecated attr
+ cx.struct_span_lint(
+ DEPRECATED,
+ attr.span,
+ fluent::lint_builtin_deprecated_attr_link,
+ |lint| {
+ lint.set_arg("name", name)
+ .set_arg("reason", reason)
+ .set_arg("link", link)
+ .span_suggestion_short(
+ attr.span,
+ suggestion.map(|s| s.into()).unwrap_or(
+ fluent::lint_builtin_deprecated_attr_default_suggestion,
+ ),
+ "",
+ Applicability::MachineApplicable,
+ )
+ },
+ );
}
return;
}
}
if attr.has_name(sym::no_start) || attr.has_name(sym::crate_id) {
- cx.struct_span_lint(DEPRECATED, attr.span, |lint| {
- lint.build(fluent::lint::builtin_deprecated_attr_used)
- .set_arg("name", pprust::path_to_string(&attr.get_normal_item().path))
- .span_suggestion_short(
- attr.span,
- fluent::lint::builtin_deprecated_attr_default_suggestion,
- "",
- Applicability::MachineApplicable,
- )
- .emit();
- });
+ cx.struct_span_lint(
+ DEPRECATED,
+ attr.span,
+ fluent::lint_builtin_deprecated_attr_used,
+ |lint| {
+ lint.set_arg("name", pprust::path_to_string(&attr.get_normal_item().path))
+ .span_suggestion_short(
+ attr.span,
+ fluent::lint_builtin_deprecated_attr_default_suggestion,
+ "",
+ Applicability::MachineApplicable,
+ )
+ },
+ );
}
}
}
@@ -1028,20 +1033,21 @@ fn warn_if_doc(cx: &EarlyContext<'_>, node_span: Span, node_kind: &str, attrs: &
let span = sugared_span.take().unwrap_or(attr.span);
if is_doc_comment || attr.has_name(sym::doc) {
- cx.struct_span_lint(UNUSED_DOC_COMMENTS, span, |lint| {
- let mut err = lint.build(fluent::lint::builtin_unused_doc_comment);
- err.set_arg("kind", node_kind);
- err.span_label(node_span, fluent::lint::label);
- match attr.kind {
- AttrKind::DocComment(CommentKind::Line, _) | AttrKind::Normal(..) => {
- err.help(fluent::lint::plain_help);
- }
- AttrKind::DocComment(CommentKind::Block, _) => {
- err.help(fluent::lint::block_help);
- }
- }
- err.emit();
- });
+ cx.struct_span_lint(
+ UNUSED_DOC_COMMENTS,
+ span,
+ fluent::lint_builtin_unused_doc_comment,
+ |lint| {
+ lint.set_arg("kind", node_kind).span_label(node_span, fluent::label).help(
+ match attr.kind {
+ AttrKind::DocComment(CommentKind::Line, _) | AttrKind::Normal(..) => {
+ fluent::plain_help
+ }
+ AttrKind::DocComment(CommentKind::Block, _) => fluent::block_help,
+ },
+ )
+ },
+ );
}
}
}
@@ -1155,18 +1161,21 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems {
match param.kind {
GenericParamKind::Lifetime { .. } => {}
GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => {
- cx.struct_span_lint(NO_MANGLE_GENERIC_ITEMS, span, |lint| {
- lint.build(fluent::lint::builtin_no_mangle_generic)
- .span_suggestion_short(
+ cx.struct_span_lint(
+ NO_MANGLE_GENERIC_ITEMS,
+ span,
+ fluent::lint_builtin_no_mangle_generic,
+ |lint| {
+ lint.span_suggestion_short(
no_mangle_attr.span,
- fluent::lint::suggestion,
+ fluent::suggestion,
"",
// Use of `#[no_mangle]` suggests FFI intent; correct
// fix may be to monomorphize source by hand
Applicability::MaybeIncorrect,
)
- .emit();
- });
+ },
+ );
break;
}
}
@@ -1182,27 +1191,29 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems {
if cx.sess().contains_name(attrs, sym::no_mangle) {
// Const items do not refer to a particular location in memory, and therefore
// don't have anything to attach a symbol to
- cx.struct_span_lint(NO_MANGLE_CONST_ITEMS, it.span, |lint| {
- let mut err = lint.build(fluent::lint::builtin_const_no_mangle);
-
- // account for "pub const" (#45562)
- let start = cx
- .tcx
- .sess
- .source_map()
- .span_to_snippet(it.span)
- .map(|snippet| snippet.find("const").unwrap_or(0))
- .unwrap_or(0) as u32;
- // `const` is 5 chars
- let const_span = it.span.with_hi(BytePos(it.span.lo().0 + start + 5));
- err.span_suggestion(
- const_span,
- fluent::lint::suggestion,
- "pub static",
- Applicability::MachineApplicable,
- );
- err.emit();
- });
+ cx.struct_span_lint(
+ NO_MANGLE_CONST_ITEMS,
+ it.span,
+ fluent::lint_builtin_const_no_mangle,
+ |lint| {
+ // account for "pub const" (#45562)
+ let start = cx
+ .tcx
+ .sess
+ .source_map()
+ .span_to_snippet(it.span)
+ .map(|snippet| snippet.find("const").unwrap_or(0))
+ .unwrap_or(0) as u32;
+ // `const` is 5 chars
+ let const_span = it.span.with_hi(BytePos(it.span.lo().0 + start + 5));
+ lint.span_suggestion(
+ const_span,
+ fluent::suggestion,
+ "pub static",
+ Applicability::MachineApplicable,
+ )
+ },
+ );
}
}
hir::ItemKind::Impl(hir::Impl { generics, items, .. }) => {
@@ -1215,7 +1226,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems {
check_no_mangle_on_generic_fn(
no_mangle_attr,
Some(generics),
- cx.tcx.hir().get_generics(it.id.def_id).unwrap(),
+ cx.tcx.hir().get_generics(it.id.owner_id.def_id).unwrap(),
it.span,
);
}
@@ -1262,9 +1273,12 @@ impl<'tcx> LateLintPass<'tcx> for MutableTransmutes {
get_transmute_from_to(cx, expr).map(|(ty1, ty2)| (ty1.kind(), ty2.kind()))
{
if to_mt == hir::Mutability::Mut && from_mt == hir::Mutability::Not {
- cx.struct_span_lint(MUTABLE_TRANSMUTES, expr.span, |lint| {
- lint.build(fluent::lint::builtin_mutable_transmutes).emit();
- });
+ cx.struct_span_lint(
+ MUTABLE_TRANSMUTES,
+ expr.span,
+ fluent::lint_builtin_mutable_transmutes,
+ |lint| lint,
+ );
}
}
@@ -1312,9 +1326,12 @@ impl<'tcx> LateLintPass<'tcx> for UnstableFeatures {
if attr.has_name(sym::feature) {
if let Some(items) = attr.meta_item_list() {
for item in items {
- cx.struct_span_lint(UNSTABLE_FEATURES, item.span(), |lint| {
- lint.build(fluent::lint::builtin_unstable_features).emit();
- });
+ cx.struct_span_lint(
+ UNSTABLE_FEATURES,
+ item.span(),
+ fluent::lint_builtin_unstable_features,
+ |lint| lint,
+ );
}
}
}
@@ -1368,26 +1385,26 @@ impl UnreachablePub {
exportable: bool,
) {
let mut applicability = Applicability::MachineApplicable;
- if cx.tcx.visibility(def_id).is_public() && !cx.access_levels.is_reachable(def_id) {
+ if cx.tcx.visibility(def_id).is_public() && !cx.effective_visibilities.is_reachable(def_id)
+ {
if vis_span.from_expansion() {
applicability = Applicability::MaybeIncorrect;
}
let def_span = cx.tcx.def_span(def_id);
- cx.struct_span_lint(UNREACHABLE_PUB, def_span, |lint| {
- let mut err = lint.build(fluent::lint::builtin_unreachable_pub);
- err.set_arg("what", what);
-
- err.span_suggestion(
- vis_span,
- fluent::lint::suggestion,
- "pub(crate)",
- applicability,
- );
- if exportable {
- err.help(fluent::lint::help);
- }
- err.emit();
- });
+ cx.struct_span_lint(
+ UNREACHABLE_PUB,
+ def_span,
+ fluent::lint_builtin_unreachable_pub,
+ |lint| {
+ lint.set_arg("what", what);
+
+ lint.span_suggestion(vis_span, fluent::suggestion, "pub(crate)", applicability);
+ if exportable {
+ lint.help(fluent::help);
+ }
+ lint
+ },
+ );
}
}
}
@@ -1398,11 +1415,11 @@ impl<'tcx> LateLintPass<'tcx> for UnreachablePub {
if let hir::ItemKind::Use(_, hir::UseKind::ListStem) = &item.kind {
return;
}
- self.perform_lint(cx, "item", item.def_id, item.vis_span, true);
+ self.perform_lint(cx, "item", item.owner_id.def_id, item.vis_span, true);
}
fn check_foreign_item(&mut self, cx: &LateContext<'_>, foreign_item: &hir::ForeignItem<'tcx>) {
- self.perform_lint(cx, "item", foreign_item.def_id, foreign_item.vis_span, true);
+ self.perform_lint(cx, "item", foreign_item.owner_id.def_id, foreign_item.vis_span, true);
}
fn check_field_def(&mut self, cx: &LateContext<'_>, field: &hir::FieldDef<'_>) {
@@ -1412,8 +1429,8 @@ impl<'tcx> LateLintPass<'tcx> for UnreachablePub {
fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) {
// Only lint inherent impl items.
- if cx.tcx.associated_item(impl_item.def_id).trait_item_def_id.is_none() {
- self.perform_lint(cx, "item", impl_item.def_id, impl_item.vis_span, false);
+ if cx.tcx.associated_item(impl_item.owner_id).trait_item_def_id.is_none() {
+ self.perform_lint(cx, "item", impl_item.owner_id.def_id, impl_item.vis_span, false);
}
}
}
@@ -1474,9 +1491,9 @@ impl TypeAliasBounds {
impl Visitor<'_> for WalkAssocTypes<'_> {
fn visit_qpath(&mut self, qpath: &hir::QPath<'_>, id: hir::HirId, span: Span) {
if TypeAliasBounds::is_type_variable_assoc(qpath) {
- self.err.span_help(span, fluent::lint::builtin_type_alias_bounds_help);
+ self.err.span_help(span, fluent::lint_builtin_type_alias_bounds_help);
}
- intravisit::walk_qpath(self, qpath, id, span)
+ intravisit::walk_qpath(self, qpath, id)
}
}
@@ -1517,36 +1534,34 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds {
let mut suggested_changing_assoc_types = false;
if !where_spans.is_empty() {
- cx.lint(TYPE_ALIAS_BOUNDS, |lint| {
- let mut err = lint.build(fluent::lint::builtin_type_alias_where_clause);
- err.set_span(where_spans);
- err.span_suggestion(
+ cx.lint(TYPE_ALIAS_BOUNDS, fluent::lint_builtin_type_alias_where_clause, |lint| {
+ lint.set_span(where_spans);
+ lint.span_suggestion(
type_alias_generics.where_clause_span,
- fluent::lint::suggestion,
+ fluent::suggestion,
"",
Applicability::MachineApplicable,
);
if !suggested_changing_assoc_types {
- TypeAliasBounds::suggest_changing_assoc_types(ty, &mut err);
+ TypeAliasBounds::suggest_changing_assoc_types(ty, lint);
suggested_changing_assoc_types = true;
}
- err.emit();
+ lint
});
}
if !inline_spans.is_empty() {
- cx.lint(TYPE_ALIAS_BOUNDS, |lint| {
- let mut err = lint.build(fluent::lint::builtin_type_alias_generic_bounds);
- err.set_span(inline_spans);
- err.multipart_suggestion(
- fluent::lint::suggestion,
+ cx.lint(TYPE_ALIAS_BOUNDS, fluent::lint_builtin_type_alias_generic_bounds, |lint| {
+ lint.set_span(inline_spans);
+ lint.multipart_suggestion(
+ fluent::suggestion,
inline_sugg,
Applicability::MachineApplicable,
);
if !suggested_changing_assoc_types {
- TypeAliasBounds::suggest_changing_assoc_types(ty, &mut err);
+ TypeAliasBounds::suggest_changing_assoc_types(ty, lint);
}
- err.emit();
+ lint
});
}
}
@@ -1624,7 +1639,7 @@ impl<'tcx> LateLintPass<'tcx> for TrivialConstraints {
use rustc_middle::ty::PredicateKind::*;
if cx.tcx.features().trivial_bounds {
- let predicates = cx.tcx.predicates_of(item.def_id);
+ 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",
@@ -1645,12 +1660,15 @@ impl<'tcx> LateLintPass<'tcx> for TrivialConstraints {
TypeWellFormedFromEnv(..) => continue,
};
if predicate.is_global() {
- cx.struct_span_lint(TRIVIAL_BOUNDS, span, |lint| {
- lint.build(fluent::lint::builtin_trivial_bounds)
- .set_arg("predicate_kind_name", predicate_kind_name)
- .set_arg("predicate", predicate)
- .emit();
- });
+ cx.struct_span_lint(
+ TRIVIAL_BOUNDS,
+ span,
+ fluent::lint_builtin_trivial_bounds,
+ |lint| {
+ lint.set_arg("predicate_kind_name", predicate_kind_name)
+ .set_arg("predicate", predicate)
+ },
+ );
}
}
}
@@ -1750,8 +1768,8 @@ impl EarlyLintPass for EllipsisInclusiveRangePatterns {
};
if let Some((start, end, join)) = endpoints {
- let msg = fluent::lint::builtin_ellipsis_inclusive_range_patterns;
- let suggestion = fluent::lint::suggestion;
+ let msg = fluent::lint_builtin_ellipsis_inclusive_range_patterns;
+ let suggestion = fluent::suggestion;
if parenthesise {
self.node_id = Some(pat.id);
let end = expr_to_string(&end);
@@ -1760,55 +1778,37 @@ impl EarlyLintPass for EllipsisInclusiveRangePatterns {
None => format!("&(..={})", end),
};
if join.edition() >= Edition::Edition2021 {
- let mut err = cx.sess().struct_span_err_with_code(
- pat.span,
- msg,
- rustc_errors::error_code!(E0783),
- );
- err.span_suggestion(
- pat.span,
- suggestion,
+ cx.sess().emit_err(BuiltinEllpisisInclusiveRangePatterns {
+ span: pat.span,
+ suggestion: pat.span,
replace,
- Applicability::MachineApplicable,
- )
- .emit();
+ });
} else {
- cx.struct_span_lint(ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, pat.span, |lint| {
- lint.build(msg)
- .span_suggestion(
- pat.span,
- suggestion,
- replace,
- Applicability::MachineApplicable,
- )
- .emit();
+ cx.struct_span_lint(ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, pat.span, msg, |lint| {
+ lint.span_suggestion(
+ pat.span,
+ suggestion,
+ replace,
+ Applicability::MachineApplicable,
+ )
});
}
} else {
let replace = "..=";
if join.edition() >= Edition::Edition2021 {
- let mut err = cx.sess().struct_span_err_with_code(
- pat.span,
- msg,
- rustc_errors::error_code!(E0783),
- );
- err.span_suggestion_short(
- join,
- suggestion,
- replace,
- Applicability::MachineApplicable,
- )
- .emit();
+ cx.sess().emit_err(BuiltinEllpisisInclusiveRangePatterns {
+ span: pat.span,
+ suggestion: join,
+ replace: replace.to_string(),
+ });
} else {
- cx.struct_span_lint(ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, join, |lint| {
- lint.build(msg)
- .span_suggestion_short(
- join,
- suggestion,
- replace,
- Applicability::MachineApplicable,
- )
- .emit();
+ cx.struct_span_lint(ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, join, msg, |lint| {
+ lint.span_suggestion_short(
+ join,
+ suggestion,
+ replace,
+ Applicability::MachineApplicable,
+ )
});
}
};
@@ -1864,7 +1864,7 @@ declare_lint! {
}
pub struct UnnameableTestItems {
- boundary: Option<LocalDefId>, // Id of the item under which things are not nameable
+ boundary: Option<hir::OwnerId>, // Id of the item under which things are not nameable
items_nameable: bool,
}
@@ -1882,21 +1882,24 @@ impl<'tcx> LateLintPass<'tcx> for UnnameableTestItems {
if let hir::ItemKind::Mod(..) = it.kind {
} else {
self.items_nameable = false;
- self.boundary = Some(it.def_id);
+ self.boundary = Some(it.owner_id);
}
return;
}
let attrs = cx.tcx.hir().attrs(it.hir_id());
if let Some(attr) = cx.sess().find_by_name(attrs, sym::rustc_test_marker) {
- cx.struct_span_lint(UNNAMEABLE_TEST_ITEMS, attr.span, |lint| {
- lint.build(fluent::lint::builtin_unnameable_test_items).emit();
- });
+ cx.struct_span_lint(
+ UNNAMEABLE_TEST_ITEMS,
+ attr.span,
+ fluent::lint_builtin_unnameable_test_items,
+ |lint| lint,
+ );
}
}
fn check_item_post(&mut self, _cx: &LateContext<'_>, it: &hir::Item<'_>) {
- if !self.items_nameable && self.boundary == Some(it.def_id) {
+ if !self.items_nameable && self.boundary == Some(it.owner_id) {
self.items_nameable = true;
}
}
@@ -2007,23 +2010,24 @@ impl KeywordIdents {
return;
}
- cx.struct_span_lint(KEYWORD_IDENTS, ident.span, |lint| {
- lint.build(fluent::lint::builtin_keyword_idents)
- .set_arg("kw", ident.clone())
- .set_arg("next", next_edition)
- .span_suggestion(
+ cx.struct_span_lint(
+ KEYWORD_IDENTS,
+ ident.span,
+ fluent::lint_builtin_keyword_idents,
+ |lint| {
+ lint.set_arg("kw", ident.clone()).set_arg("next", next_edition).span_suggestion(
ident.span,
- fluent::lint::suggestion,
+ fluent::suggestion,
format!("r#{}", ident),
Applicability::MachineApplicable,
)
- .emit();
- });
+ },
+ );
}
}
impl EarlyLintPass for KeywordIdents {
- fn check_mac_def(&mut self, cx: &EarlyContext<'_>, mac_def: &ast::MacroDef, _id: ast::NodeId) {
+ fn check_mac_def(&mut self, cx: &EarlyContext<'_>, mac_def: &ast::MacroDef) {
self.check_tokens(cx, mac_def.body.inner_tokens());
}
fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &ast::MacCall) {
@@ -2039,13 +2043,13 @@ declare_lint_pass!(ExplicitOutlivesRequirements => [EXPLICIT_OUTLIVES_REQUIREMEN
impl ExplicitOutlivesRequirements {
fn lifetimes_outliving_lifetime<'tcx>(
inferred_outlives: &'tcx [(ty::Predicate<'tcx>, Span)],
- index: u32,
+ 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 {
- ty::ReEarlyBound(ebr) if ebr.index == index => Some(b),
+ ty::ReEarlyBound(ebr) if ebr.def_id == def_id => Some(b),
_ => None,
},
_ => None,
@@ -2082,8 +2086,12 @@ impl ExplicitOutlivesRequirements {
.filter_map(|(i, bound)| {
if let hir::GenericBound::Outlives(lifetime) = bound {
let is_inferred = match tcx.named_region(lifetime.hir_id) {
- Some(Region::EarlyBound(index, ..)) => inferred_outlives.iter().any(|r| {
- if let ty::ReEarlyBound(ebr) = **r { ebr.index == index } else { false }
+ Some(Region::EarlyBound(def_id)) => inferred_outlives.iter().any(|r| {
+ if let ty::ReEarlyBound(ebr) = **r {
+ ebr.def_id == def_id
+ } else {
+ false
+ }
}),
_ => false,
};
@@ -2157,7 +2165,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
use rustc_middle::middle::resolve_lifetime::Region;
- let def_id = item.def_id;
+ let def_id = item.owner_id.def_id;
if let hir::ItemKind::Struct(_, ref hir_generics)
| hir::ItemKind::Enum(_, ref hir_generics)
| hir::ItemKind::Union(_, ref hir_generics) = item.kind
@@ -2177,11 +2185,14 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements {
for (i, where_predicate) in hir_generics.predicates.iter().enumerate() {
let (relevant_lifetimes, bounds, span, in_where_clause) = match where_predicate {
hir::WherePredicate::RegionPredicate(predicate) => {
- if let Some(Region::EarlyBound(index, ..)) =
+ if let Some(Region::EarlyBound(region_def_id)) =
cx.tcx.named_region(predicate.lifetime.hir_id)
{
(
- Self::lifetimes_outliving_lifetime(inferred_outlives, index),
+ Self::lifetimes_outliving_lifetime(
+ inferred_outlives,
+ region_def_id,
+ ),
&predicate.bounds,
predicate.span,
predicate.in_where_clause,
@@ -2262,19 +2273,21 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements {
}
if !lint_spans.is_empty() {
- cx.struct_span_lint(EXPLICIT_OUTLIVES_REQUIREMENTS, lint_spans.clone(), |lint| {
- lint.build(fluent::lint::builtin_explicit_outlives)
- .set_arg("count", bound_count)
- .multipart_suggestion(
- fluent::lint::suggestion,
+ cx.struct_span_lint(
+ EXPLICIT_OUTLIVES_REQUIREMENTS,
+ lint_spans.clone(),
+ fluent::lint_builtin_explicit_outlives,
+ |lint| {
+ lint.set_arg("count", bound_count).multipart_suggestion(
+ fluent::suggestion,
lint_spans
.into_iter()
.map(|span| (span, String::new()))
.collect::<Vec<_>>(),
Applicability::MachineApplicable,
)
- .emit();
- });
+ },
+ );
}
}
}
@@ -2321,18 +2334,24 @@ impl EarlyLintPass for IncompleteFeatures {
.chain(features.declared_lib_features.iter().map(|(name, span)| (name, span)))
.filter(|(&name, _)| features.incomplete(name))
.for_each(|(&name, &span)| {
- cx.struct_span_lint(INCOMPLETE_FEATURES, span, |lint| {
- let mut builder = lint.build(fluent::lint::builtin_incomplete_features);
- builder.set_arg("name", name);
- if let Some(n) = rustc_feature::find_feature_issue(name, GateIssue::Language) {
- builder.set_arg("n", n);
- builder.note(fluent::lint::note);
- }
- if HAS_MIN_FEATURES.contains(&name) {
- builder.help(fluent::lint::help);
- }
- builder.emit();
- })
+ cx.struct_span_lint(
+ INCOMPLETE_FEATURES,
+ span,
+ fluent::lint_builtin_incomplete_features,
+ |lint| {
+ lint.set_arg("name", name);
+ if let Some(n) =
+ rustc_feature::find_feature_issue(name, GateIssue::Language)
+ {
+ lint.set_arg("n", n);
+ lint.note(fluent::note);
+ }
+ if HAS_MIN_FEATURES.contains(&name) {
+ lint.help(fluent::help);
+ }
+ lint
+ },
+ )
});
}
}
@@ -2419,13 +2438,13 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
_ => {}
}
}
- } else if let hir::ExprKind::MethodCall(_, ref args, _) = expr.kind {
+ } else if let hir::ExprKind::MethodCall(_, receiver, ..) = expr.kind {
// Find problematic calls to `MaybeUninit::assume_init`.
let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?;
if cx.tcx.is_diagnostic_item(sym::assume_init, def_id) {
// This is a call to *some* method named `assume_init`.
// See if the `self` parameter is one of the dangerous constructors.
- if let hir::ExprKind::Call(ref path_expr, _) = args[0].kind {
+ if let hir::ExprKind::Call(ref path_expr, _) = receiver.kind {
if let hir::ExprKind::Path(ref qpath) = path_expr.kind {
let def_id = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id()?;
match cx.tcx.get_diagnostic_name(def_id) {
@@ -2441,12 +2460,27 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
None
}
- /// Test if this enum has several actually "existing" variants.
- /// Zero-sized uninhabited variants do not always have a tag assigned and thus do not "exist".
- fn is_multi_variant<'tcx>(adt: ty::AdtDef<'tcx>) -> bool {
- // As an approximation, we only count dataless variants. Those are definitely inhabited.
- let existing_variants = adt.variants().iter().filter(|v| v.fields.is_empty()).count();
- existing_variants > 1
+ fn variant_find_init_error<'tcx>(
+ cx: &LateContext<'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))
+ } else {
+ // Just forward.
+ (msg, span)
+ }
+ })
+ })
}
/// Return `Some` only if we are sure this type does *not*
@@ -2475,7 +2509,16 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
Char if init == InitKind::Uninit => {
Some(("characters must be a valid Unicode codepoint".to_string(), None))
}
- // Recurse and checks for some compound types.
+ 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))
+ }
+ RawPtr(_) if init == InitKind::Uninit => {
+ Some(("raw pointers must not be uninitialized".to_string(), None))
+ }
+ // 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;
@@ -2483,7 +2526,11 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
// We exploit here that `layout_scalar_valid_range` will never
// return `Bound::Excluded`. (And we have tests checking that we
// handle the attribute correctly.)
- (Bound::Included(lo), _) if lo > 0 => {
+ // 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(_))
@@ -2499,50 +2546,65 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
}
_ => {}
}
- // Now, recurse.
- match adt_def.variants().len() {
- 0 => Some(("enums with no variants have no valid value".to_string(), None)),
- 1 => {
- // Struct, or enum with exactly one variant.
- // Proceed recursively, check all fields.
- let variant = &adt_def.variant(VariantIdx::from_u32(0));
- 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 {} field)",
- adt_def.descr()
- )
- .unwrap();
- (msg, Some(span))
- } else {
- // Just forward.
- (msg, span)
- }
- },
- )
- })
- }
- // Multi-variant enum.
- _ => {
- if init == InitKind::Uninit && is_multi_variant(*adt_def) {
- let span = cx.tcx.def_span(adt_def.did());
- Some((
- "enums have to be initialized to a variant".to_string(),
- Some(span),
- ))
- } else {
- // In principle, for zero-initialization we could figure out which variant corresponds
- // to tag 0, and check that... but for now we just accept all zero-initializations.
- None
- }
+ // Handle structs.
+ if adt_def.is_struct() {
+ return variant_find_init_error(
+ cx,
+ adt_def.non_enum_variant(),
+ substs,
+ "struct field",
+ init,
+ );
+ }
+ // And now, enums.
+ let span = cx.tcx.def_span(adt_def.did());
+ let mut potential_variants = adt_def.variants().iter().filter_map(|variant| {
+ let definitely_inhabited = match variant
+ .inhabited_predicate(cx.tcx, *adt_def)
+ .subst(cx.tcx, substs)
+ .apply_any_module(cx.tcx, cx.param_env)
+ {
+ // Entirely skip uninhbaited variants.
+ Some(false) => return None,
+ // Forward the others, but remember which ones are definitely inhabited.
+ Some(true) => true,
+ None => false,
+ };
+ 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)));
+ };
+ // 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,
+ &first_variant.0,
+ substs,
+ "field of the only potentially inhabited enum variant",
+ init,
+ );
+ };
+ // So we have at least two potentially inhabited variants.
+ // If we can prove that we have at least two *definitely* inhabited variants,
+ // then we have a tag and hence leaving this uninit is definitely disallowed.
+ // (Leaving it zeroed could be okay, depending on which variant is encoded as zero tag.)
+ if init == InitKind::Uninit {
+ let definitely_inhabited = (first_variant.1 as usize)
+ + (second_variant.1 as usize)
+ + potential_variants
+ .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),
+ ));
}
}
+ // We couldn't find anything wrong here.
+ None
}
Tuple(..) => {
// Proceed recursively, check all fields.
@@ -2571,28 +2633,37 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
with_no_trimmed_paths!(ty_find_init_error(cx, conjured_ty, init))
{
// FIXME(davidtwco): make translatable
- cx.struct_span_lint(INVALID_VALUE, expr.span, |lint| {
- let mut err = lint.build(&format!(
- "the type `{}` does not permit {}",
- conjured_ty,
- match init {
- InitKind::Zeroed => "zero-initialization",
- InitKind::Uninit => "being left uninitialized",
- },
- ));
- err.span_label(expr.span, "this code causes undefined behavior when executed");
- err.span_label(
- expr.span,
- "help: use `MaybeUninit<T>` instead, \
+ cx.struct_span_lint(
+ INVALID_VALUE,
+ expr.span,
+ DelayDm(|| {
+ format!(
+ "the type `{}` does not permit {}",
+ conjured_ty,
+ match init {
+ InitKind::Zeroed => "zero-initialization",
+ InitKind::Uninit => "being left uninitialized",
+ },
+ )
+ }),
+ |lint| {
+ lint.span_label(
+ expr.span,
+ "this code causes undefined behavior when executed",
+ );
+ lint.span_label(
+ expr.span,
+ "help: use `MaybeUninit<T>` instead, \
and only call `assume_init` after initialization is done",
- );
- if let Some(span) = span {
- err.span_note(span, &msg);
- } else {
- err.note(&msg);
- }
- err.emit();
- });
+ );
+ if let Some(span) = span {
+ lint.span_note(span, &msg);
+ } else {
+ lint.note(&msg);
+ }
+ lint
+ },
+ );
}
}
}
@@ -2673,7 +2744,7 @@ impl ClashingExternDeclarations {
/// Insert a new foreign item into the seen set. If a symbol with the same name already exists
/// for the item, return its HirId without updating the set.
fn insert(&mut self, tcx: TyCtxt<'_>, fi: &hir::ForeignItem<'_>) -> Option<HirId> {
- let did = fi.def_id.to_def_id();
+ let did = fi.owner_id.to_def_id();
let instance = Instance::new(did, ty::List::identity_for_item(tcx, did));
let name = Symbol::intern(tcx.symbol_name(instance).name);
if let Some(&hir_id) = self.seen_decls.get(&name) {
@@ -2691,14 +2762,14 @@ impl ClashingExternDeclarations {
/// symbol's name.
fn name_of_extern_decl(tcx: TyCtxt<'_>, fi: &hir::ForeignItem<'_>) -> SymbolName {
if let Some((overridden_link_name, overridden_link_name_span)) =
- tcx.codegen_fn_attrs(fi.def_id).link_name.map(|overridden_link_name| {
+ tcx.codegen_fn_attrs(fi.owner_id).link_name.map(|overridden_link_name| {
// FIXME: Instead of searching through the attributes again to get span
// information, we could have codegen_fn_attrs also give span information back for
// where the attribute was defined. However, until this is found to be a
// bottleneck, this does just fine.
(
overridden_link_name,
- tcx.get_attr(fi.def_id.to_def_id(), sym::link_name).unwrap().span,
+ tcx.get_attr(fi.owner_id.to_def_id(), sym::link_name).unwrap().span,
)
})
{
@@ -2915,10 +2986,10 @@ impl<'tcx> LateLintPass<'tcx> for ClashingExternDeclarations {
let tcx = cx.tcx;
if let Some(existing_hid) = self.insert(tcx, this_fi) {
let existing_decl_ty = tcx.type_of(tcx.hir().local_def_id(existing_hid));
- let this_decl_ty = tcx.type_of(this_fi.def_id);
+ let this_decl_ty = tcx.type_of(this_fi.owner_id);
debug!(
"ClashingExternDeclarations: Comparing existing {:?}: {:?} to this {:?}: {:?}",
- existing_hid, existing_decl_ty, this_fi.def_id, this_decl_ty
+ existing_hid, existing_decl_ty, this_fi.owner_id, this_decl_ty
);
// Check that the declarations match.
if !Self::structurally_same_type(
@@ -2938,31 +3009,29 @@ impl<'tcx> LateLintPass<'tcx> for ClashingExternDeclarations {
SymbolName::Link(_, annot_span) => fi.span.to(annot_span),
};
// Finally, emit the diagnostic.
+
+ let msg = if orig.get_name() == this_fi.ident.name {
+ fluent::lint_builtin_clashing_extern_same_name
+ } else {
+ fluent::lint_builtin_clashing_extern_diff_name
+ };
tcx.struct_span_lint_hir(
CLASHING_EXTERN_DECLARATIONS,
this_fi.hir_id(),
get_relevant_span(this_fi),
+ msg,
|lint| {
let mut expected_str = DiagnosticStyledString::new();
expected_str.push(existing_decl_ty.fn_sig(tcx).to_string(), false);
let mut found_str = DiagnosticStyledString::new();
found_str.push(this_decl_ty.fn_sig(tcx).to_string(), true);
- lint.build(if orig.get_name() == this_fi.ident.name {
- fluent::lint::builtin_clashing_extern_same_name
- } else {
- fluent::lint::builtin_clashing_extern_diff_name
- })
- .set_arg("this_fi", this_fi.ident.name)
- .set_arg("orig", orig.get_name())
- .span_label(
- get_relevant_span(orig_fi),
- fluent::lint::previous_decl_label,
- )
- .span_label(get_relevant_span(this_fi), fluent::lint::mismatch_label)
- // FIXME(davidtwco): translatable expected/found
- .note_expected_found(&"", expected_str, &"", found_str)
- .emit();
+ lint.set_arg("this_fi", this_fi.ident.name)
+ .set_arg("orig", orig.get_name())
+ .span_label(get_relevant_span(orig_fi), fluent::previous_decl_label)
+ .span_label(get_relevant_span(this_fi), fluent::mismatch_label)
+ // FIXME(davidtwco): translatable expected/found
+ .note_expected_found(&"", expected_str, &"", found_str)
},
);
}
@@ -3043,11 +3112,12 @@ impl<'tcx> LateLintPass<'tcx> for DerefNullPtr {
if let rustc_hir::ExprKind::Unary(rustc_hir::UnOp::Deref, expr_deref) = expr.kind {
if is_null_ptr(cx, expr_deref) {
- cx.struct_span_lint(DEREF_NULLPTR, expr.span, |lint| {
- let mut err = lint.build(fluent::lint::builtin_deref_nullptr);
- err.span_label(expr.span, fluent::lint::label);
- err.emit();
- });
+ cx.struct_span_lint(
+ DEREF_NULLPTR,
+ expr.span,
+ fluent::lint_builtin_deref_nullptr,
+ |lint| lint.span_label(expr.span, fluent::label),
+ );
}
}
}
@@ -3060,6 +3130,7 @@ declare_lint! {
/// ### Example
///
/// ```rust,compile_fail
+ /// # #![feature(asm_experimental_arch)]
/// use std::arch::asm;
///
/// fn main() {
@@ -3157,9 +3228,8 @@ impl<'tcx> LateLintPass<'tcx> for NamedAsmLabels {
cx.lookup_with_diagnostics(
NAMED_ASM_LABELS,
Some(target_spans),
- |diag| {
- diag.build(fluent::lint::builtin_asm_labels).emit();
- },
+ fluent::lint_builtin_asm_labels,
+ |lint| lint,
BuiltinLintDiagnostics::NamedAsmLabel(
"only local labels of the form `<number>:` should be used in inline asm"
.to_string(),
@@ -3170,3 +3240,118 @@ impl<'tcx> LateLintPass<'tcx> for NamedAsmLabels {
}
}
}
+
+declare_lint! {
+ /// The `special_module_name` lint detects module
+ /// declarations for files that have a special meaning.
+ ///
+ /// ### Example
+ ///
+ /// ```rust,compile_fail
+ /// mod lib;
+ ///
+ /// fn main() {
+ /// lib::run();
+ /// }
+ /// ```
+ ///
+ /// {{produces}}
+ ///
+ /// ### Explanation
+ ///
+ /// Cargo recognizes `lib.rs` and `main.rs` as the root of a
+ /// library or binary crate, so declaring them as modules
+ /// will lead to miscompilation of the crate unless configured
+ /// explicitly.
+ ///
+ /// To access a library from a binary target within the same crate,
+ /// use `your_crate_name::` as the path instead of `lib::`:
+ ///
+ /// ```rust,compile_fail
+ /// // bar/src/lib.rs
+ /// fn run() {
+ /// // ...
+ /// }
+ ///
+ /// // bar/src/main.rs
+ /// fn main() {
+ /// bar::run();
+ /// }
+ /// ```
+ ///
+ /// Binary targets cannot be used as libraries and so declaring
+ /// one as a module is not allowed.
+ pub SPECIAL_MODULE_NAME,
+ Warn,
+ "module declarations for files with a special meaning",
+}
+
+declare_lint_pass!(SpecialModuleName => [SPECIAL_MODULE_NAME]);
+
+impl EarlyLintPass for SpecialModuleName {
+ fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &ast::Crate) {
+ for item in &krate.items {
+ if let ast::ItemKind::Mod(
+ _,
+ ast::ModKind::Unloaded | ast::ModKind::Loaded(_, ast::Inline::No, _),
+ ) = item.kind
+ {
+ if item.attrs.iter().any(|a| a.has_name(sym::path)) {
+ continue;
+ }
+
+ match item.ident.name.as_str() {
+ "lib" => cx.struct_span_lint(SPECIAL_MODULE_NAME, item.span, "found module declaration for lib.rs", |lint| {
+ lint
+ .note("lib.rs is the root of this crate's library target")
+ .help("to refer to it from other targets, use the library's name as the path")
+ }),
+ "main" => cx.struct_span_lint(SPECIAL_MODULE_NAME, item.span, "found module declaration for main.rs", |lint| {
+ lint
+ .note("a binary crate cannot be used as library")
+ }),
+ _ => continue
+ }
+ }
+ }
+ }
+}
+
+pub use rustc_session::lint::builtin::UNEXPECTED_CFGS;
+
+declare_lint_pass!(UnexpectedCfgs => [UNEXPECTED_CFGS]);
+
+impl EarlyLintPass for UnexpectedCfgs {
+ fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) {
+ let cfg = &cx.sess().parse_sess.config;
+ let check_cfg = &cx.sess().parse_sess.check_config;
+ for &(name, value) in cfg {
+ if let Some(names_valid) = &check_cfg.names_valid {
+ if !names_valid.contains(&name) {
+ cx.lookup(
+ UNEXPECTED_CFGS,
+ None::<MultiSpan>,
+ fluent::lint_builtin_unexpected_cli_config_name,
+ |diag| diag.help(fluent::help).set_arg("name", name),
+ );
+ }
+ }
+ if let Some(value) = value {
+ if let Some(values) = &check_cfg.values_valid.get(&name) {
+ if !values.contains(&value) {
+ cx.lookup(
+ UNEXPECTED_CFGS,
+ None::<MultiSpan>,
+ fluent::lint_builtin_unexpected_cli_config_value,
+ |diag| {
+ diag.help(fluent::help)
+ .set_arg("name", name)
+ .set_arg("value", value)
+ },
+ );
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs
index b95fc341d..cec0003ff 100644
--- a/compiler/rustc_lint/src/context.rs
+++ b/compiler/rustc_lint/src/context.rs
@@ -16,20 +16,22 @@
use self::TargetLint::*;
+use crate::errors::{
+ CheckNameDeprecated, CheckNameUnknown, CheckNameUnknownTool, CheckNameWarning, RequestedLevel,
+ UnsupportedGroup,
+};
use crate::levels::LintLevelsBuilder;
use crate::passes::{EarlyLintPassObject, LateLintPassObject};
use rustc_ast::util::unicode::TEXT_FLOW_CONTROL_CHARS;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::sync;
-use rustc_errors::{add_elided_lifetime_in_path_suggestion, struct_span_err};
-use rustc_errors::{
- Applicability, DecorateLint, LintDiagnosticBuilder, MultiSpan, SuggestionStyle,
-};
+use rustc_errors::{add_elided_lifetime_in_path_suggestion, DiagnosticBuilder, DiagnosticMessage};
+use rustc_errors::{Applicability, DecorateLint, MultiSpan, SuggestionStyle};
use rustc_hir as hir;
use rustc_hir::def::Res;
use rustc_hir::def_id::{CrateNum, DefId};
use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData};
-use rustc_middle::middle::privacy::AccessLevels;
+use rustc_middle::middle::privacy::EffectiveVisibilities;
use rustc_middle::middle::stability;
use rustc_middle::ty::layout::{LayoutError, LayoutOfHelpers, TyAndLayout};
use rustc_middle::ty::print::with_no_trimmed_paths;
@@ -39,14 +41,17 @@ use rustc_session::lint::{FutureIncompatibleInfo, Level, Lint, LintBuffer, LintI
use rustc_session::Session;
use rustc_span::lev_distance::find_best_match_for_name;
use rustc_span::symbol::{sym, Ident, Symbol};
-use rustc_span::{BytePos, Span, DUMMY_SP};
+use rustc_span::{BytePos, Span};
use rustc_target::abi;
-use tracing::debug;
use std::cell::Cell;
use std::iter;
use std::slice;
+type EarlyLintPassFactory = dyn Fn() -> EarlyLintPassObject + sync::Send + sync::Sync;
+type LateLintPassFactory =
+ dyn for<'tcx> Fn(TyCtxt<'tcx>) -> LateLintPassObject<'tcx> + sync::Send + sync::Sync;
+
/// Information about the registered lints.
///
/// This is basically the subset of `Context` that we can
@@ -61,11 +66,11 @@ pub struct LintStore {
/// interior mutability, we don't enforce this (and lints should, in theory,
/// be compatible with being constructed more than once, though not
/// necessarily in a sane manner. This is safe though.)
- pub pre_expansion_passes: Vec<Box<dyn Fn() -> EarlyLintPassObject + sync::Send + sync::Sync>>,
- pub early_passes: Vec<Box<dyn Fn() -> EarlyLintPassObject + sync::Send + sync::Sync>>,
- pub late_passes: Vec<Box<dyn Fn() -> LateLintPassObject + sync::Send + sync::Sync>>,
+ pub pre_expansion_passes: Vec<Box<EarlyLintPassFactory>>,
+ pub early_passes: Vec<Box<EarlyLintPassFactory>>,
+ pub late_passes: Vec<Box<LateLintPassFactory>>,
/// This is unique in that we construct them per-module, so not once.
- pub late_module_passes: Vec<Box<dyn Fn() -> LateLintPassObject + sync::Send + sync::Sync>>,
+ pub late_module_passes: Vec<Box<LateLintPassFactory>>,
/// Lints indexed by name.
by_name: FxHashMap<String, TargetLint>,
@@ -183,14 +188,20 @@ impl LintStore {
pub fn register_late_pass(
&mut self,
- pass: impl Fn() -> LateLintPassObject + 'static + sync::Send + sync::Sync,
+ pass: impl for<'tcx> Fn(TyCtxt<'tcx>) -> LateLintPassObject<'tcx>
+ + 'static
+ + sync::Send
+ + sync::Sync,
) {
self.late_passes.push(Box::new(pass));
}
pub fn register_late_mod_pass(
&mut self,
- pass: impl Fn() -> LateLintPassObject + 'static + sync::Send + sync::Sync,
+ pass: impl for<'tcx> Fn(TyCtxt<'tcx>) -> LateLintPassObject<'tcx>
+ + 'static
+ + sync::Send
+ + sync::Sync,
) {
self.late_module_passes.push(Box::new(pass));
}
@@ -326,68 +337,41 @@ impl LintStore {
) {
let (tool_name, lint_name_only) = parse_lint_and_tool_name(lint_name);
if lint_name_only == crate::WARNINGS.name_lower() && matches!(level, Level::ForceWarn(_)) {
- struct_span_err!(
- sess,
- DUMMY_SP,
- E0602,
- "`{}` lint group is not supported with ´--force-warn´",
- crate::WARNINGS.name_lower()
- )
- .emit();
+ sess.emit_err(UnsupportedGroup { lint_group: crate::WARNINGS.name_lower() });
return;
}
- let db = match self.check_lint_name(lint_name_only, tool_name, registered_tools) {
- CheckLintNameResult::Ok(_) => None,
- CheckLintNameResult::Warning(ref msg, _) => Some(sess.struct_warn(msg)),
+ let lint_name = lint_name.to_string();
+ match self.check_lint_name(lint_name_only, tool_name, registered_tools) {
+ CheckLintNameResult::Warning(msg, _) => {
+ sess.emit_warning(CheckNameWarning {
+ msg,
+ sub: RequestedLevel { level, lint_name },
+ });
+ }
CheckLintNameResult::NoLint(suggestion) => {
- let mut err =
- struct_span_err!(sess, DUMMY_SP, E0602, "unknown lint: `{}`", lint_name);
-
- if let Some(suggestion) = suggestion {
- err.help(&format!("did you mean: `{}`", suggestion));
+ sess.emit_err(CheckNameUnknown {
+ lint_name: lint_name.clone(),
+ suggestion,
+ sub: RequestedLevel { level, lint_name },
+ });
+ }
+ CheckLintNameResult::Tool(result) => {
+ if let Err((Some(_), new_name)) = result {
+ sess.emit_warning(CheckNameDeprecated {
+ lint_name: lint_name.clone(),
+ new_name,
+ sub: RequestedLevel { level, lint_name },
+ });
}
-
- Some(err.forget_guarantee())
}
- CheckLintNameResult::Tool(result) => match result {
- Err((Some(_), new_name)) => Some(sess.struct_warn(&format!(
- "lint name `{}` is deprecated \
- and does not have an effect anymore. \
- Use: {}",
- lint_name, new_name
- ))),
- _ => None,
- },
- CheckLintNameResult::NoTool => Some(
- struct_span_err!(
- sess,
- DUMMY_SP,
- E0602,
- "unknown lint tool: `{}`",
- tool_name.unwrap()
- )
- .forget_guarantee(),
- ),
+ CheckLintNameResult::NoTool => {
+ sess.emit_err(CheckNameUnknownTool {
+ tool_name: tool_name.unwrap(),
+ sub: RequestedLevel { level, lint_name },
+ });
+ }
+ _ => {}
};
-
- if let Some(mut db) = db {
- let msg = format!(
- "requested on the command line with `{} {}`",
- match level {
- Level::Allow => "-A",
- Level::Warn => "-W",
- Level::ForceWarn(_) => "--force-warn",
- Level::Deny => "-D",
- Level::Forbid => "-F",
- Level::Expect(_) => {
- unreachable!("lints with the level of `expect` should not run this code");
- }
- },
- lint_name
- );
- db.note(&msg);
- db.emit();
- }
}
/// True if this symbol represents a lint group name.
@@ -440,7 +424,7 @@ impl LintStore {
None => {
// 1. The tool is currently running, so this lint really doesn't exist.
// FIXME: should this handle tools that never register a lint, like rustfmt?
- tracing::debug!("lints={:?}", self.by_name.keys().collect::<Vec<_>>());
+ debug!("lints={:?}", self.by_name.keys().collect::<Vec<_>>());
let tool_prefix = format!("{}::", tool_name);
return if self.by_name.keys().any(|lint| lint.starts_with(&tool_prefix)) {
self.no_lint_suggestion(&complete_name)
@@ -533,7 +517,7 @@ impl LintStore {
CheckLintNameResult::Tool(Err((Some(slice::from_ref(id)), complete_name)))
}
Some(other) => {
- tracing::debug!("got renamed lint {:?}", other);
+ debug!("got renamed lint {:?}", other);
CheckLintNameResult::NoLint(None)
}
}
@@ -558,7 +542,7 @@ pub struct LateContext<'tcx> {
pub param_env: ty::ParamEnv<'tcx>,
/// Items accessible from the crate being checked.
- pub access_levels: &'tcx AccessLevels,
+ pub effective_visibilities: &'tcx EffectiveVisibilities,
/// The store of registered lints and the lint levels.
pub lint_store: &'tcx LintStore,
@@ -574,7 +558,7 @@ pub struct LateContext<'tcx> {
/// Context for lint checking of the AST, after expansion, before lowering to HIR.
pub struct EarlyContext<'a> {
- pub builder: LintLevelsBuilder<'a>,
+ pub builder: LintLevelsBuilder<'a, crate::levels::TopDown>,
pub buffered: LintBuffer,
}
@@ -582,7 +566,7 @@ pub trait LintPassObject: Sized {}
impl LintPassObject for EarlyLintPassObject {}
-impl LintPassObject for LateLintPassObject {}
+impl LintPassObject for LateLintPassObject<'_> {}
pub trait LintContext: Sized {
type PassObject: LintPassObject;
@@ -590,17 +574,23 @@ pub trait LintContext: Sized {
fn sess(&self) -> &Session;
fn lints(&self) -> &LintStore;
+ /// Emit a lint at the appropriate level, with an optional associated span and an existing diagnostic.
+ ///
+ /// Return value of the `decorate` closure is ignored, see [`struct_lint_level`] for a detailed explanation.
+ ///
+ /// [`struct_lint_level`]: rustc_middle::lint::struct_lint_level#decorate-signature
fn lookup_with_diagnostics(
&self,
lint: &'static Lint,
span: Option<impl Into<MultiSpan>>,
- decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a, ()>),
+ msg: impl Into<DiagnosticMessage>,
+ decorate: impl for<'a, 'b> FnOnce(
+ &'b mut DiagnosticBuilder<'a, ()>,
+ ) -> &'b mut DiagnosticBuilder<'a, ()>,
diagnostic: BuiltinLintDiagnostics,
) {
- self.lookup(lint, span, |lint| {
- // We first generate a blank diagnostic.
- let mut db = lint.build("");
-
+ // We first generate a blank diagnostic.
+ self.lookup(lint, span, msg,|db| {
// Now, set up surrounding context.
let sess = self.sess();
match diagnostic {
@@ -674,7 +664,7 @@ pub trait LintContext: Sized {
) => {
add_elided_lifetime_in_path_suggestion(
sess.source_map(),
- &mut db,
+ db,
n,
path_span,
incl_angl_brckt,
@@ -710,7 +700,7 @@ pub trait LintContext: Sized {
}
}
BuiltinLintDiagnostics::DeprecatedMacro(suggestion, span) => {
- stability::deprecation_suggestion(&mut db, "macro", suggestion, span)
+ stability::deprecation_suggestion(db, "macro", suggestion, span)
}
BuiltinLintDiagnostics::UnusedDocComment(span) => {
db.span_label(span, "rustdoc does not generate documentation for macro invocations");
@@ -865,9 +855,14 @@ pub trait LintContext: Sized {
if let Some(positional_arg_to_replace) = position_sp_to_replace {
let name = if is_formatting_arg { named_arg_name + "$" } else { named_arg_name };
-
+ let span_to_replace = if let Ok(positional_arg_content) =
+ self.sess().source_map().span_to_snippet(positional_arg_to_replace) && positional_arg_content.starts_with(':') {
+ positional_arg_to_replace.shrink_to_lo()
+ } else {
+ positional_arg_to_replace
+ };
db.span_suggestion_verbose(
- positional_arg_to_replace,
+ span_to_replace,
"use the named argument by name to avoid ambiguity",
name,
Applicability::MaybeIncorrect,
@@ -876,17 +871,25 @@ pub trait LintContext: Sized {
}
}
// Rewrap `db`, and pass control to the user.
- decorate(LintDiagnosticBuilder::new(db));
+ decorate(db)
});
}
// FIXME: These methods should not take an Into<MultiSpan> -- instead, callers should need to
// set the span in their `decorate` function (preferably using set_span).
+ /// Emit a lint at the appropriate level, with an optional associated span.
+ ///
+ /// Return value of the `decorate` closure is ignored, see [`struct_lint_level`] for a detailed explanation.
+ ///
+ /// [`struct_lint_level`]: rustc_middle::lint::struct_lint_level#decorate-signature
fn lookup<S: Into<MultiSpan>>(
&self,
lint: &'static Lint,
span: Option<S>,
- decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a, ()>),
+ msg: impl Into<DiagnosticMessage>,
+ decorate: impl for<'a, 'b> FnOnce(
+ &'b mut DiagnosticBuilder<'a, ()>,
+ ) -> &'b mut DiagnosticBuilder<'a, ()>,
);
/// Emit a lint at `span` from a lint struct (some type that implements `DecorateLint`,
@@ -897,31 +900,48 @@ pub trait LintContext: Sized {
span: S,
decorator: impl for<'a> DecorateLint<'a, ()>,
) {
- self.lookup(lint, Some(span), |diag| decorator.decorate_lint(diag));
+ self.lookup(lint, Some(span), decorator.msg(), |diag| decorator.decorate_lint(diag));
}
+ /// Emit a lint at the appropriate level, with an associated span.
+ ///
+ /// Return value of the `decorate` closure is ignored, see [`struct_lint_level`] for a detailed explanation.
+ ///
+ /// [`struct_lint_level`]: rustc_middle::lint::struct_lint_level#decorate-signature
fn struct_span_lint<S: Into<MultiSpan>>(
&self,
lint: &'static Lint,
span: S,
- decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a, ()>),
+ msg: impl Into<DiagnosticMessage>,
+ decorate: impl for<'a, 'b> FnOnce(
+ &'b mut DiagnosticBuilder<'a, ()>,
+ ) -> &'b mut DiagnosticBuilder<'a, ()>,
) {
- self.lookup(lint, Some(span), decorate);
+ self.lookup(lint, Some(span), msg, decorate);
}
/// Emit a lint from a lint struct (some type that implements `DecorateLint`, typically
/// generated by `#[derive(LintDiagnostic)]`).
fn emit_lint(&self, lint: &'static Lint, decorator: impl for<'a> DecorateLint<'a, ()>) {
- self.lookup(lint, None as Option<Span>, |diag| decorator.decorate_lint(diag));
+ self.lookup(lint, None as Option<Span>, decorator.msg(), |diag| {
+ decorator.decorate_lint(diag)
+ });
}
/// Emit a lint at the appropriate level, with no associated span.
+ ///
+ /// Return value of the `decorate` closure is ignored, see [`struct_lint_level`] for a detailed explanation.
+ ///
+ /// [`struct_lint_level`]: rustc_middle::lint::struct_lint_level#decorate-signature
fn lint(
&self,
lint: &'static Lint,
- decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a, ()>),
+ msg: impl Into<DiagnosticMessage>,
+ decorate: impl for<'a, 'b> FnOnce(
+ &'b mut DiagnosticBuilder<'a, ()>,
+ ) -> &'b mut DiagnosticBuilder<'a, ()>,
) {
- self.lookup(lint, None as Option<Span>, decorate);
+ self.lookup(lint, None as Option<Span>, msg, decorate);
}
/// This returns the lint level for the given lint at the current location.
@@ -968,8 +988,8 @@ impl<'a> EarlyContext<'a> {
}
}
-impl LintContext for LateContext<'_> {
- type PassObject = LateLintPassObject;
+impl<'tcx> LintContext for LateContext<'tcx> {
+ type PassObject = LateLintPassObject<'tcx>;
/// Gets the overall compiler `Session` object.
fn sess(&self) -> &Session {
@@ -984,13 +1004,16 @@ impl LintContext for LateContext<'_> {
&self,
lint: &'static Lint,
span: Option<S>,
- decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a, ()>),
+ msg: impl Into<DiagnosticMessage>,
+ decorate: impl for<'a, 'b> FnOnce(
+ &'b mut DiagnosticBuilder<'a, ()>,
+ ) -> &'b mut DiagnosticBuilder<'a, ()>,
) {
let hir_id = self.last_node_with_lint_attrs;
match span {
- Some(s) => self.tcx.struct_span_lint_hir(lint, hir_id, s, decorate),
- None => self.tcx.struct_lint_node(lint, hir_id, decorate),
+ Some(s) => self.tcx.struct_span_lint_hir(lint, hir_id, s, msg, decorate),
+ None => self.tcx.struct_lint_node(lint, hir_id, msg, decorate),
}
}
@@ -1015,9 +1038,12 @@ impl LintContext for EarlyContext<'_> {
&self,
lint: &'static Lint,
span: Option<S>,
- decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a, ()>),
+ msg: impl Into<DiagnosticMessage>,
+ decorate: impl for<'a, 'b> FnOnce(
+ &'b mut DiagnosticBuilder<'a, ()>,
+ ) -> &'b mut DiagnosticBuilder<'a, ()>,
) {
- self.builder.struct_lint(lint, span.map(|s| s.into()), decorate)
+ self.builder.struct_lint(lint, span.map(|s| s.into()), msg, decorate)
}
fn get_lint_level(&self, lint: &'static Lint) -> Level {
@@ -1057,7 +1083,7 @@ impl<'tcx> LateContext<'tcx> {
.filter(|typeck_results| typeck_results.hir_owner == id.owner)
.or_else(|| {
if self.tcx.has_typeck_results(id.owner.to_def_id()) {
- Some(self.tcx.typeck(id.owner))
+ Some(self.tcx.typeck(id.owner.def_id))
} else {
None
}
diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs
index d13711c3a..aee870dd2 100644
--- a/compiler/rustc_lint/src/early.rs
+++ b/compiler/rustc_lint/src/early.rs
@@ -26,7 +26,6 @@ use rustc_span::symbol::Ident;
use rustc_span::Span;
use std::slice;
-use tracing::debug;
macro_rules! run_early_pass { ($cx:expr, $f:ident, $($args:expr),*) => ({
$cx.pass.$f(&$cx.context, $($args),*);
@@ -44,9 +43,8 @@ impl<'a, T: EarlyLintPass> EarlyContextAndPass<'a, T> {
self.context.lookup_with_diagnostics(
lint_id.lint,
Some(span),
- |lint| {
- lint.build(&msg).emit();
- },
+ msg,
+ |lint| lint,
diagnostic,
);
}
@@ -60,6 +58,7 @@ impl<'a, T: EarlyLintPass> EarlyContextAndPass<'a, T> {
F: FnOnce(&mut Self),
{
let is_crate_node = id == ast::CRATE_NODE_ID;
+ debug!(?id);
let push = self.context.builder.push(attrs, is_crate_node, None);
self.check_id(id);
@@ -101,6 +100,12 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T>
run_early_pass!(self, check_pat_post, p);
}
+ fn visit_pat_field(&mut self, field: &'a ast::PatField) {
+ self.with_lint_attrs(field.id, &field.attrs, |cx| {
+ ast_visit::walk_pat_field(cx, field);
+ });
+ }
+
fn visit_anon_const(&mut self, c: &'a ast::AnonConst) {
self.check_id(c.id);
ast_visit::walk_anon_const(self, c);
@@ -142,7 +147,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);
self.check_id(id);
- ast_visit::walk_fn(self, fk, span);
+ ast_visit::walk_fn(self, fk);
// Explicitly check for lints associated with 'closure_id', since
// it does not have a corresponding AST node
@@ -219,9 +224,10 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T>
}
fn visit_generic_param(&mut self, param: &'a ast::GenericParam) {
- run_early_pass!(self, check_generic_param, param);
- self.check_id(param.id);
- ast_visit::walk_generic_param(self, param);
+ self.with_lint_attrs(param.id, &param.attrs, |cx| {
+ run_early_pass!(cx, check_generic_param, param);
+ ast_visit::walk_generic_param(cx, param);
+ });
}
fn visit_generics(&mut self, g: &'a ast::Generics) {
@@ -233,9 +239,9 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T>
ast_visit::walk_where_predicate(self, p);
}
- fn visit_poly_trait_ref(&mut self, t: &'a ast::PolyTraitRef, m: &'a ast::TraitBoundModifier) {
- run_early_pass!(self, check_poly_trait_ref, t, m);
- ast_visit::walk_poly_trait_ref(self, t, m);
+ fn visit_poly_trait_ref(&mut self, t: &'a ast::PolyTraitRef) {
+ run_early_pass!(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) {
@@ -260,9 +266,9 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T>
ast_visit::walk_path(self, p);
}
- fn visit_path_segment(&mut self, path_span: Span, s: &'a ast::PathSegment) {
+ fn visit_path_segment(&mut self, s: &'a ast::PathSegment) {
self.check_id(s.id);
- ast_visit::walk_path_segment(self, path_span, s);
+ ast_visit::walk_path_segment(self, s);
}
fn visit_attribute(&mut self, attr: &'a ast::Attribute) {
@@ -270,7 +276,7 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T>
}
fn visit_mac_def(&mut self, mac: &'a ast::MacroDef, id: ast::NodeId) {
- run_early_pass!(self, check_mac_def, mac, id);
+ run_early_pass!(self, check_mac_def, mac);
self.check_id(id);
}
@@ -403,7 +409,7 @@ pub fn check_ast_node<'a>(
if sess.opts.unstable_opts.no_interleave_lints {
for (i, pass) in passes.iter_mut().enumerate() {
buffered =
- sess.prof.extra_verbose_generic_activity("run_lint", pass.name()).run(|| {
+ sess.prof.verbose_generic_activity_with_arg("run_lint", pass.name()).run(|| {
early_lint_node(
sess,
!pre_expansion && i == 0,
diff --git a/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs b/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs
index f41ee6404..f9d746622 100644
--- a/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs
+++ b/compiler/rustc_lint/src/enum_intrinsics_non_enums.rs
@@ -50,26 +50,24 @@ fn enforce_mem_discriminant(
) {
let ty_param = cx.typeck_results().node_substs(func_expr.hir_id).type_at(0);
if is_non_enum(ty_param) {
- cx.struct_span_lint(ENUM_INTRINSICS_NON_ENUMS, expr_span, |builder| {
- builder
- .build(fluent::lint::enum_intrinsics_mem_discriminant)
- .set_arg("ty_param", ty_param)
- .span_note(args_span, fluent::lint::note)
- .emit();
- });
+ cx.struct_span_lint(
+ ENUM_INTRINSICS_NON_ENUMS,
+ expr_span,
+ fluent::lint_enum_intrinsics_mem_discriminant,
+ |lint| lint.set_arg("ty_param", ty_param).span_note(args_span, fluent::note),
+ );
}
}
fn enforce_mem_variant_count(cx: &LateContext<'_>, func_expr: &hir::Expr<'_>, span: Span) {
let ty_param = cx.typeck_results().node_substs(func_expr.hir_id).type_at(0);
if is_non_enum(ty_param) {
- cx.struct_span_lint(ENUM_INTRINSICS_NON_ENUMS, span, |builder| {
- builder
- .build(fluent::lint::enum_intrinsics_mem_variant)
- .set_arg("ty_param", ty_param)
- .note(fluent::lint::note)
- .emit();
- });
+ cx.struct_span_lint(
+ ENUM_INTRINSICS_NON_ENUMS,
+ span,
+ fluent::lint_enum_intrinsics_mem_variant,
+ |lint| lint.set_arg("ty_param", ty_param).note(fluent::note),
+ );
}
}
diff --git a/compiler/rustc_lint/src/errors.rs b/compiler/rustc_lint/src/errors.rs
new file mode 100644
index 000000000..a49d1bdac
--- /dev/null
+++ b/compiler/rustc_lint/src/errors.rs
@@ -0,0 +1,150 @@
+use rustc_errors::{
+ fluent, AddToDiagnostic, Diagnostic, ErrorGuaranteed, Handler, IntoDiagnostic,
+ SubdiagnosticMessage,
+};
+use rustc_macros::{Diagnostic, Subdiagnostic};
+use rustc_session::lint::Level;
+use rustc_span::{Span, Symbol};
+
+#[derive(Diagnostic)]
+#[diag(lint_overruled_attribute, code = "E0453")]
+pub struct OverruledAttribute {
+ #[primary_span]
+ pub span: Span,
+ #[label]
+ pub overruled: Span,
+ pub lint_level: String,
+ pub lint_source: Symbol,
+ #[subdiagnostic]
+ pub sub: OverruledAttributeSub,
+}
+//
+pub enum OverruledAttributeSub {
+ DefaultSource { id: String },
+ NodeSource { span: Span, reason: Option<Symbol> },
+ CommandLineSource,
+}
+
+impl AddToDiagnostic for OverruledAttributeSub {
+ fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
+ where
+ F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
+ {
+ match self {
+ OverruledAttributeSub::DefaultSource { id } => {
+ diag.note(fluent::lint_default_source);
+ diag.set_arg("id", id);
+ }
+ OverruledAttributeSub::NodeSource { span, reason } => {
+ diag.span_label(span, fluent::lint_node_source);
+ if let Some(rationale) = reason {
+ diag.note(rationale.as_str());
+ }
+ }
+ OverruledAttributeSub::CommandLineSource => {
+ diag.note(fluent::lint_command_line_source);
+ }
+ }
+ }
+}
+
+#[derive(Diagnostic)]
+#[diag(lint_malformed_attribute, code = "E0452")]
+pub struct MalformedAttribute {
+ #[primary_span]
+ pub span: Span,
+ #[subdiagnostic]
+ pub sub: MalformedAttributeSub,
+}
+
+#[derive(Subdiagnostic)]
+pub enum MalformedAttributeSub {
+ #[label(lint_bad_attribute_argument)]
+ BadAttributeArgument(#[primary_span] Span),
+ #[label(lint_reason_must_be_string_literal)]
+ ReasonMustBeStringLiteral(#[primary_span] Span),
+ #[label(lint_reason_must_come_last)]
+ ReasonMustComeLast(#[primary_span] Span),
+}
+
+#[derive(Diagnostic)]
+#[diag(lint_unknown_tool_in_scoped_lint, code = "E0710")]
+pub struct UnknownToolInScopedLint {
+ #[primary_span]
+ pub span: Option<Span>,
+ pub tool_name: Symbol,
+ pub lint_name: String,
+ #[help]
+ pub is_nightly_build: Option<()>,
+}
+
+#[derive(Diagnostic)]
+#[diag(lint_builtin_ellipsis_inclusive_range_patterns, code = "E0783")]
+pub struct BuiltinEllpisisInclusiveRangePatterns {
+ #[primary_span]
+ pub span: Span,
+ #[suggestion_short(code = "{replace}", applicability = "machine-applicable")]
+ pub suggestion: Span,
+ pub replace: String,
+}
+
+#[derive(Subdiagnostic)]
+#[note(lint_requested_level)]
+pub struct RequestedLevel {
+ pub level: Level,
+ pub lint_name: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(lint_unsupported_group, code = "E0602")]
+pub struct UnsupportedGroup {
+ pub lint_group: String,
+}
+
+pub struct CheckNameUnknown {
+ pub lint_name: String,
+ pub suggestion: Option<Symbol>,
+ pub sub: RequestedLevel,
+}
+
+impl IntoDiagnostic<'_> for CheckNameUnknown {
+ fn into_diagnostic(
+ self,
+ handler: &Handler,
+ ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> {
+ let mut diag = handler.struct_err(fluent::lint_check_name_unknown);
+ diag.code(rustc_errors::error_code!(E0602));
+ if let Some(suggestion) = self.suggestion {
+ diag.help(fluent::help);
+ diag.set_arg("suggestion", suggestion);
+ }
+ diag.set_arg("lint_name", self.lint_name);
+ diag.subdiagnostic(self.sub);
+ diag
+ }
+}
+
+#[derive(Diagnostic)]
+#[diag(lint_check_name_unknown_tool, code = "E0602")]
+pub struct CheckNameUnknownTool {
+ pub tool_name: Symbol,
+ #[subdiagnostic]
+ pub sub: RequestedLevel,
+}
+
+#[derive(Diagnostic)]
+#[diag(lint_check_name_warning)]
+pub struct CheckNameWarning {
+ pub msg: String,
+ #[subdiagnostic]
+ pub sub: RequestedLevel,
+}
+
+#[derive(Diagnostic)]
+#[diag(lint_check_name_deprecated)]
+pub struct CheckNameDeprecated {
+ pub lint_name: String,
+ pub new_name: String,
+ #[subdiagnostic]
+ pub sub: RequestedLevel,
+}
diff --git a/compiler/rustc_lint/src/expect.rs b/compiler/rustc_lint/src/expect.rs
index 699e81543..cf8f31bcb 100644
--- a/compiler/rustc_lint/src/expect.rs
+++ b/compiler/rustc_lint/src/expect.rs
@@ -16,8 +16,10 @@ fn check_expectations(tcx: TyCtxt<'_>, tool_filter: Option<Symbol>) {
return;
}
+ let lint_expectations = tcx.lint_expectations(());
let fulfilled_expectations = tcx.sess.diagnostic().steal_fulfilled_expectation_ids();
- let lint_expectations = &tcx.lint_levels(()).lint_expectations;
+
+ tracing::debug!(?lint_expectations, ?fulfilled_expectations);
for (id, expectation) in lint_expectations {
// This check will always be true, since `lint_expectations` only
@@ -43,17 +45,17 @@ fn emit_unfulfilled_expectation_lint(
builtin::UNFULFILLED_LINT_EXPECTATIONS,
hir_id,
expectation.emission_span,
- |diag| {
- let mut diag = diag.build(fluent::lint::expectation);
+ fluent::lint_expectation,
+ |lint| {
if let Some(rationale) = expectation.reason {
- diag.note(rationale.as_str());
+ lint.note(rationale.as_str());
}
if expectation.is_unfulfilled_lint_expectations {
- diag.note(fluent::lint::note);
+ lint.note(fluent::note);
}
- diag.emit();
+ lint
},
);
}
diff --git a/compiler/rustc_lint/src/for_loops_over_fallibles.rs b/compiler/rustc_lint/src/for_loops_over_fallibles.rs
new file mode 100644
index 000000000..ed8d424e0
--- /dev/null
+++ b/compiler/rustc_lint/src/for_loops_over_fallibles.rs
@@ -0,0 +1,183 @@
+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.
+ ///
+ /// ### Example
+ ///
+ /// ```rust
+ /// let opt = Some(1);
+ /// for x in opt { /* ... */}
+ /// ```
+ ///
+ /// {{produces}}
+ ///
+ /// ### Explanation
+ ///
+ /// Both `Option` and `Result` implement `IntoIterator` trait, which allows using them in a `for` loop.
+ /// `for` loop over `Option` or `Result` will iterate either 0 (if the value is `None`/`Err(_)`)
+ /// or 1 time (if the value is `Some(_)`/`Ok(_)`). This is not very useful and is more clearly expressed
+ /// via `if let`.
+ ///
+ /// `for` loop can also be accidentally written with the intention to call a function multiple times,
+ /// while the function returns `Some(_)`, in these cases `while let` loop should be used instead.
+ ///
+ /// The "intended" use of `IntoIterator` implementations for `Option` and `Result` is passing them to
+ /// generic code that expects something implementing `IntoIterator`. For example using `.chain(option)`
+ /// to optionally add a value to an iterator.
+ pub FOR_LOOPS_OVER_FALLIBLES,
+ Warn,
+ "for-looping over an `Option` or a `Result`, which is more clearly expressed as an `if let`"
+}
+
+declare_lint_pass!(ForLoopsOverFallibles => [FOR_LOOPS_OVER_FALLIBLES]);
+
+impl<'tcx> LateLintPass<'tcx> for ForLoopsOverFallibles {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+ let Some((pat, arg)) = extract_for_loop(expr) else { return };
+
+ let ty = cx.typeck_results().expr_ty(arg);
+
+ let &ty::Adt(adt, substs) = ty.kind() else { return };
+
+ let (article, ty, var) = match adt.did() {
+ did if cx.tcx.is_diagnostic_item(sym::Option, did) => ("an", "Option", "Some"),
+ did if cx.tcx.is_diagnostic_item(sym::Result, did) => ("a", "Result", "Ok"),
+ _ => return,
+ };
+
+ let msg = DelayDm(|| {
+ format!(
+ "for loop over {article} `{ty}`. This is more readably written as an `if let` statement",
+ )
+ });
+
+ cx.struct_span_lint(FOR_LOOPS_OVER_FALLIBLES, arg.span, msg, |lint| {
+ if let Some(recv) = extract_iterator_next_call(cx, arg)
+ && let Ok(recv_snip) = cx.sess().source_map().span_to_snippet(recv.span)
+ {
+ lint.span_suggestion(
+ recv.span.between(arg.span.shrink_to_hi()),
+ format!("to iterate over `{recv_snip}` remove the call to `next`"),
+ ".by_ref()",
+ Applicability::MaybeIncorrect
+ );
+ } else {
+ lint.multipart_suggestion_verbose(
+ format!("to check pattern in a loop use `while let`"),
+ vec![
+ // NB can't use `until` here because `expr.span` and `pat.span` have different syntax contexts
+ (expr.span.with_hi(pat.span.lo()), format!("while let {var}(")),
+ (pat.span.between(arg.span), format!(") = ")),
+ ],
+ Applicability::MaybeIncorrect
+ );
+ }
+
+ if suggest_question_mark(cx, adt, substs, expr.span) {
+ lint.span_suggestion(
+ arg.span.shrink_to_hi(),
+ "consider unwrapping the `Result` with `?` to iterate over its contents",
+ "?",
+ Applicability::MaybeIncorrect,
+ );
+ }
+
+ lint.multipart_suggestion_verbose(
+ "consider using `if let` to clear intent",
+ vec![
+ // NB can't use `until` here because `expr.span` and `pat.span` have different syntax contexts
+ (expr.span.with_hi(pat.span.lo()), format!("if let {var}(")),
+ (pat.span.between(arg.span), format!(") = ")),
+ ],
+ Applicability::MaybeIncorrect,
+ )
+ })
+ }
+}
+
+fn extract_for_loop<'tcx>(expr: &Expr<'tcx>) -> Option<(&'tcx Pat<'tcx>, &'tcx Expr<'tcx>)> {
+ if let hir::ExprKind::DropTemps(e) = expr.kind
+ && let hir::ExprKind::Match(iterexpr, [arm], hir::MatchSource::ForLoopDesugar) = e.kind
+ && let hir::ExprKind::Call(_, [arg]) = iterexpr.kind
+ && let hir::ExprKind::Loop(block, ..) = arm.body.kind
+ && let [stmt] = block.stmts
+ && let hir::StmtKind::Expr(e) = stmt.kind
+ && let hir::ExprKind::Match(_, [_, some_arm], _) = e.kind
+ && let hir::PatKind::Struct(_, [field], _) = some_arm.pat.kind
+ {
+ Some((field.pat, arg))
+ } else {
+ None
+ }
+}
+
+fn extract_iterator_next_call<'tcx>(
+ cx: &LateContext<'_>,
+ expr: &Expr<'tcx>,
+) -> Option<&'tcx Expr<'tcx>> {
+ // This won't work for `Iterator::next(iter)`, is this an issue?
+ if let hir::ExprKind::MethodCall(_, recv, _, _) = expr.kind
+ && cx.typeck_results().type_dependent_def_id(expr.hir_id) == cx.tcx.lang_items().next_fn()
+ {
+ Some(recv)
+ } else {
+ return None
+ }
+}
+
+fn suggest_question_mark<'tcx>(
+ cx: &LateContext<'tcx>,
+ adt: ty::AdtDef<'tcx>,
+ substs: &List<ty::GenericArg<'tcx>>,
+ span: Span,
+) -> bool {
+ let Some(body_id) = cx.enclosing_body else { return false };
+ let Some(into_iterator_did) = cx.tcx.get_diagnostic_item(sym::IntoIterator) else { return false };
+
+ if !cx.tcx.is_diagnostic_item(sym::Result, adt.did()) {
+ return false;
+ }
+
+ // Check that the function/closure/constant we are in has a `Result` type.
+ // Otherwise suggesting using `?` may not be a good idea.
+ {
+ let ty = cx.typeck_results().expr_ty(&cx.tcx.hir().body(body_id).value);
+ let ty::Adt(ret_adt, ..) = ty.kind() else { return false };
+ if !cx.tcx.is_diagnostic_item(sym::Result, ret_adt.did()) {
+ return false;
+ }
+ }
+
+ 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(
+ &infcx,
+ 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 fe2712525..7e884e990 100644
--- a/compiler/rustc_lint/src/hidden_unicode_codepoints.rs
+++ b/compiler/rustc_lint/src/hidden_unicode_codepoints.rs
@@ -60,52 +60,56 @@ impl HiddenUnicodeCodepoints {
})
.collect();
- cx.struct_span_lint(TEXT_DIRECTION_CODEPOINT_IN_LITERAL, span, |lint| {
- let mut err = lint.build(fluent::lint::hidden_unicode_codepoints);
- err.set_arg("label", label);
- err.set_arg("count", spans.len());
- err.span_label(span, fluent::lint::label);
- err.note(fluent::lint::note);
- if point_at_inner_spans {
- for (c, span) in &spans {
- err.span_label(*span, format!("{:?}", c));
+ cx.struct_span_lint(
+ TEXT_DIRECTION_CODEPOINT_IN_LITERAL,
+ span,
+ fluent::lint_hidden_unicode_codepoints,
+ |lint| {
+ lint.set_arg("label", label);
+ lint.set_arg("count", spans.len());
+ lint.span_label(span, fluent::label);
+ lint.note(fluent::note);
+ if point_at_inner_spans {
+ for (c, span) in &spans {
+ lint.span_label(*span, format!("{:?}", c));
+ }
}
- }
- if point_at_inner_spans && !spans.is_empty() {
- err.multipart_suggestion_with_style(
- fluent::lint::suggestion_remove,
- spans.iter().map(|(_, span)| (*span, "".to_string())).collect(),
- Applicability::MachineApplicable,
- SuggestionStyle::HideCodeAlways,
- );
- err.multipart_suggestion(
- fluent::lint::suggestion_escape,
- spans
- .into_iter()
- .map(|(c, span)| {
- let c = format!("{:?}", c);
- (span, c[1..c.len() - 1].to_string())
- })
- .collect(),
- Applicability::MachineApplicable,
- );
- } else {
- // FIXME: in other suggestions we've reversed the inner spans of doc comments. We
- // should do the same here to provide the same good suggestions as we do for
- // literals above.
- err.set_arg(
- "escaped",
- spans
- .into_iter()
- .map(|(c, _)| format!("{:?}", c))
- .collect::<Vec<String>>()
- .join(", "),
- );
- err.note(fluent::lint::suggestion_remove);
- err.note(fluent::lint::no_suggestion_note_escape);
- }
- err.emit();
- });
+ if point_at_inner_spans && !spans.is_empty() {
+ lint.multipart_suggestion_with_style(
+ fluent::suggestion_remove,
+ spans.iter().map(|(_, span)| (*span, "".to_string())).collect(),
+ Applicability::MachineApplicable,
+ SuggestionStyle::HideCodeAlways,
+ );
+ lint.multipart_suggestion(
+ fluent::suggestion_escape,
+ spans
+ .into_iter()
+ .map(|(c, span)| {
+ let c = format!("{:?}", c);
+ (span, c[1..c.len() - 1].to_string())
+ })
+ .collect(),
+ Applicability::MachineApplicable,
+ );
+ } else {
+ // FIXME: in other suggestions we've reversed the inner spans of doc comments. We
+ // should do the same here to provide the same good suggestions as we do for
+ // literals above.
+ lint.set_arg(
+ "escaped",
+ spans
+ .into_iter()
+ .map(|(c, _)| format!("{:?}", c))
+ .collect::<Vec<String>>()
+ .join(", "),
+ );
+ lint.note(fluent::suggestion_remove);
+ lint.note(fluent::no_suggestion_note_escape);
+ }
+ lint
+ },
+ );
}
}
impl EarlyLintPass for HiddenUnicodeCodepoints {
@@ -120,8 +124,8 @@ 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, kind, span }) => {
- let text = token.symbol;
+ ast::ExprKind::Lit(ast::Lit { token_lit, kind, span }) => {
+ let text = token_lit.symbol;
if !contains_text_flow_control_chars(text.as_str()) {
return;
}
diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs
index c26d78247..11e4650cb 100644
--- a/compiler/rustc_lint/src/internal.rs
+++ b/compiler/rustc_lint/src/internal.rs
@@ -12,7 +12,6 @@ use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::hygiene::{ExpnKind, MacroKind};
use rustc_span::symbol::{kw, sym, Symbol};
use rustc_span::Span;
-use tracing::debug;
declare_tool_lint! {
pub rustc::DEFAULT_HASH_TYPES,
@@ -35,13 +34,16 @@ impl LateLintPass<'_> for DefaultHashTypes {
Some(sym::HashSet) => "FxHashSet",
_ => return,
};
- cx.struct_span_lint(DEFAULT_HASH_TYPES, path.span, |lint| {
- lint.build(fluent::lint::default_hash_types)
- .set_arg("preferred", replace)
- .set_arg("used", cx.tcx.item_name(def_id))
- .note(fluent::lint::note)
- .emit();
- });
+ cx.struct_span_lint(
+ DEFAULT_HASH_TYPES,
+ path.span,
+ fluent::lint_default_hash_types,
+ |lint| {
+ lint.set_arg("preferred", replace)
+ .set_arg("used", cx.tcx.item_name(def_id))
+ .note(fluent::note)
+ },
+ );
}
}
@@ -52,7 +54,7 @@ fn typeck_results_of_method_fn<'tcx>(
expr: &Expr<'_>,
) -> Option<(Span, DefId, ty::subst::SubstsRef<'tcx>)> {
match expr.kind {
- ExprKind::MethodCall(segment, _, _)
+ ExprKind::MethodCall(segment, ..)
if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) =>
{
Some((segment.ident.span, def_id, cx.typeck_results().node_substs(expr.hir_id)))
@@ -81,12 +83,12 @@ impl LateLintPass<'_> for QueryStability {
if let Ok(Some(instance)) = ty::Instance::resolve(cx.tcx, cx.param_env, def_id, substs) {
let def_id = instance.def_id();
if cx.tcx.has_attr(def_id, sym::rustc_lint_query_instability) {
- cx.struct_span_lint(POTENTIAL_QUERY_INSTABILITY, span, |lint| {
- lint.build(fluent::lint::query_instability)
- .set_arg("query", cx.tcx.item_name(def_id))
- .note(fluent::lint::note)
- .emit();
- })
+ cx.struct_span_lint(
+ POTENTIAL_QUERY_INSTABILITY,
+ span,
+ fluent::lint_query_instability,
+ |lint| lint.set_arg("query", cx.tcx.item_name(def_id)).note(fluent::note),
+ )
}
}
}
@@ -119,21 +121,19 @@ impl<'tcx> LateLintPass<'tcx> for TyTyKind {
_: rustc_hir::HirId,
) {
if let Some(segment) = path.segments.iter().nth_back(1)
- && let Some(res) = &segment.res
- && lint_ty_kind_usage(cx, res)
+ && lint_ty_kind_usage(cx, &segment.res)
{
let span = path.span.with_hi(
segment.args.map_or(segment.ident.span, |a| a.span_ext).hi()
);
- cx.struct_span_lint(USAGE_OF_TY_TYKIND, path.span, |lint| {
- lint.build(fluent::lint::tykind_kind)
+ cx.struct_span_lint(USAGE_OF_TY_TYKIND, path.span, fluent::lint_tykind_kind, |lint| {
+ lint
.span_suggestion(
span,
- fluent::lint::suggestion,
+ fluent::suggestion,
"ty",
Applicability::MaybeIncorrect, // ty maybe needs an import
)
- .emit();
});
}
}
@@ -142,85 +142,85 @@ impl<'tcx> LateLintPass<'tcx> for TyTyKind {
match &ty.kind {
TyKind::Path(QPath::Resolved(_, path)) => {
if lint_ty_kind_usage(cx, &path.res) {
- cx.struct_span_lint(USAGE_OF_TY_TYKIND, path.span, |lint| {
- let hir = cx.tcx.hir();
- match hir.find(hir.get_parent_node(ty.hir_id)) {
- Some(Node::Pat(Pat {
- kind:
- PatKind::Path(qpath)
- | PatKind::TupleStruct(qpath, ..)
- | PatKind::Struct(qpath, ..),
- ..
- })) => {
- if let QPath::TypeRelative(qpath_ty, ..) = qpath
- && qpath_ty.hir_id == ty.hir_id
- {
- lint.build(fluent::lint::tykind_kind)
- .span_suggestion(
- path.span,
- fluent::lint::suggestion,
- "ty",
- Applicability::MaybeIncorrect, // ty maybe needs an import
- )
- .emit();
- return;
- }
+ let hir = cx.tcx.hir();
+ let span = match hir.find(hir.get_parent_node(ty.hir_id)) {
+ Some(Node::Pat(Pat {
+ kind:
+ PatKind::Path(qpath)
+ | PatKind::TupleStruct(qpath, ..)
+ | PatKind::Struct(qpath, ..),
+ ..
+ })) => {
+ if let QPath::TypeRelative(qpath_ty, ..) = qpath
+ && qpath_ty.hir_id == ty.hir_id
+ {
+ Some(path.span)
+ } else {
+ None
}
- Some(Node::Expr(Expr {
- kind: ExprKind::Path(qpath),
- ..
- })) => {
- if let QPath::TypeRelative(qpath_ty, ..) = qpath
- && qpath_ty.hir_id == ty.hir_id
- {
- lint.build(fluent::lint::tykind_kind)
- .span_suggestion(
- path.span,
- fluent::lint::suggestion,
- "ty",
- Applicability::MaybeIncorrect, // ty maybe needs an import
- )
- .emit();
- return;
- }
+ }
+ Some(Node::Expr(Expr {
+ kind: ExprKind::Path(qpath),
+ ..
+ })) => {
+ if let QPath::TypeRelative(qpath_ty, ..) = qpath
+ && qpath_ty.hir_id == ty.hir_id
+ {
+ Some(path.span)
+ } else {
+ None
}
- // Can't unify these two branches because qpath below is `&&` and above is `&`
- // and `A | B` paths don't play well together with adjustments, apparently.
- Some(Node::Expr(Expr {
- kind: ExprKind::Struct(qpath, ..),
- ..
- })) => {
- if let QPath::TypeRelative(qpath_ty, ..) = qpath
- && qpath_ty.hir_id == ty.hir_id
- {
- lint.build(fluent::lint::tykind_kind)
- .span_suggestion(
- path.span,
- fluent::lint::suggestion,
- "ty",
- Applicability::MaybeIncorrect, // ty maybe needs an import
- )
- .emit();
- return;
- }
+ }
+ // Can't unify these two branches because qpath below is `&&` and above is `&`
+ // and `A | B` paths don't play well together with adjustments, apparently.
+ Some(Node::Expr(Expr {
+ kind: ExprKind::Struct(qpath, ..),
+ ..
+ })) => {
+ if let QPath::TypeRelative(qpath_ty, ..) = qpath
+ && qpath_ty.hir_id == ty.hir_id
+ {
+ Some(path.span)
+ } else {
+ None
}
- _ => {}
}
- lint.build(fluent::lint::tykind).help(fluent::lint::help).emit();
- })
+ _ => None
+ };
+
+ match span {
+ Some(span) => {
+ cx.struct_span_lint(
+ USAGE_OF_TY_TYKIND,
+ path.span,
+ fluent::lint_tykind_kind,
+ |lint| lint.span_suggestion(
+ span,
+ fluent::suggestion,
+ "ty",
+ Applicability::MaybeIncorrect, // ty maybe needs an import
+ )
+ )
+ },
+ None => cx.struct_span_lint(
+ USAGE_OF_TY_TYKIND,
+ path.span,
+ fluent::lint_tykind,
+ |lint| lint.help(fluent::help)
+ )
+ }
} else if !ty.span.from_expansion() && let Some(t) = is_ty_or_ty_ctxt(cx, &path) {
if path.segments.len() > 1 {
- cx.struct_span_lint(USAGE_OF_QUALIFIED_TY, path.span, |lint| {
- lint.build(fluent::lint::ty_qualified)
+ cx.struct_span_lint(USAGE_OF_QUALIFIED_TY, path.span, fluent::lint_ty_qualified, |lint| {
+ lint
.set_arg("ty", t.clone())
.span_suggestion(
path.span,
- fluent::lint::suggestion,
+ fluent::suggestion,
t,
// The import probably needs to be changed
Applicability::MaybeIncorrect,
)
- .emit();
})
}
}
@@ -246,7 +246,7 @@ fn is_ty_or_ty_ctxt(cx: &LateContext<'_>, path: &Path<'_>) -> Option<String> {
}
}
// Only lint on `&Ty` and `&TyCtxt` if it is used outside of a trait.
- Res::SelfTy { trait_: None, alias_to: Some((did, _)) } => {
+ Res::SelfTyAlias { alias_to: did, is_trait_impl: false, .. } => {
if let ty::Adt(adt, substs) = cx.tcx.type_of(did).kind() {
if let Some(name @ (sym::Ty | sym::TyCtxt)) = cx.tcx.get_diagnostic_name(adt.did())
{
@@ -310,11 +310,8 @@ impl EarlyLintPass for LintPassImpl {
cx.struct_span_lint(
LINT_PASS_IMPL_WITHOUT_MACRO,
lint_pass.path.span,
- |lint| {
- lint.build(fluent::lint::lintpass_by_hand)
- .help(fluent::lint::help)
- .emit();
- },
+ fluent::lint_lintpass_by_hand,
+ |lint| lint.help(fluent::help),
)
}
}
@@ -351,12 +348,12 @@ impl<'tcx> LateLintPass<'tcx> for ExistingDocKeyword {
if is_doc_keyword(v) {
return;
}
- cx.struct_span_lint(EXISTING_DOC_KEYWORD, attr.span, |lint| {
- lint.build(fluent::lint::non_existant_doc_keyword)
- .set_arg("keyword", v)
- .help(fluent::lint::help)
- .emit();
- });
+ cx.struct_span_lint(
+ EXISTING_DOC_KEYWORD,
+ attr.span,
+ fluent::lint_non_existant_doc_keyword,
+ |lint| lint.set_arg("keyword", v).help(fluent::help),
+ );
}
}
}
@@ -374,7 +371,7 @@ declare_tool_lint! {
declare_tool_lint! {
pub rustc::DIAGNOSTIC_OUTSIDE_OF_IMPL,
Allow,
- "prevent creation of diagnostics outside of `SessionDiagnostic`/`AddSubdiagnostic` impls",
+ "prevent creation of diagnostics outside of `IntoDiagnostic`/`AddToDiagnostic` impls",
report_in_external_macro: true
}
@@ -393,24 +390,33 @@ impl LateLintPass<'_> for Diagnostics {
return;
}
+ let mut found_parent_with_attr = false;
let mut found_impl = false;
- for (_, parent) in cx.tcx.hir().parent_iter(expr.hir_id) {
+ for (hir_id, parent) in cx.tcx.hir().parent_iter(expr.hir_id) {
+ if let Some(owner_did) = hir_id.as_owner() {
+ found_parent_with_attr = found_parent_with_attr
+ || cx.tcx.has_attr(owner_did.to_def_id(), sym::rustc_lint_diagnostics);
+ }
+
debug!(?parent);
if let Node::Item(Item { kind: ItemKind::Impl(impl_), .. }) = parent &&
let Impl { of_trait: Some(of_trait), .. } = impl_ &&
let Some(def_id) = of_trait.trait_def_id() &&
let Some(name) = cx.tcx.get_diagnostic_name(def_id) &&
- matches!(name, sym::SessionDiagnostic | sym::AddSubdiagnostic | sym::DecorateLint)
+ matches!(name, sym::IntoDiagnostic | sym::AddToDiagnostic | sym::DecorateLint)
{
found_impl = true;
break;
}
}
debug!(?found_impl);
- if !found_impl {
- cx.struct_span_lint(DIAGNOSTIC_OUTSIDE_OF_IMPL, span, |lint| {
- lint.build(fluent::lint::diag_out_of_impl).emit();
- })
+ if !found_parent_with_attr && !found_impl {
+ cx.struct_span_lint(
+ DIAGNOSTIC_OUTSIDE_OF_IMPL,
+ span,
+ fluent::lint_diag_out_of_impl,
+ |lint| lint,
+ )
}
let mut found_diagnostic_message = false;
@@ -425,10 +431,13 @@ impl LateLintPass<'_> for Diagnostics {
}
}
debug!(?found_diagnostic_message);
- if !found_diagnostic_message {
- cx.struct_span_lint(UNTRANSLATABLE_DIAGNOSTIC, span, |lint| {
- lint.build(fluent::lint::untranslatable_diag).emit();
- })
+ if !found_parent_with_attr && !found_diagnostic_message {
+ cx.struct_span_lint(
+ UNTRANSLATABLE_DIAGNOSTIC,
+ span,
+ fluent::lint_untranslatable_diag,
+ |lint| lint,
+ )
}
}
}
@@ -460,8 +469,8 @@ impl LateLintPass<'_> for BadOptAccess {
let Some(literal) = item.literal() &&
let ast::LitKind::Str(val, _) = literal.kind
{
- cx.struct_span_lint(BAD_OPT_ACCESS, expr.span, |lint| {
- lint.build(val.as_str()).emit(); }
+ 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 a329b3751..303fcb1a1 100644
--- a/compiler/rustc_lint/src/late.rs
+++ b/compiler/rustc_lint/src/late.rs
@@ -24,13 +24,11 @@ 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::symbol::Symbol;
use rustc_span::Span;
use std::any::Any;
use std::cell::Cell;
use std::slice;
-use tracing::debug;
/// Extract the `LintStore` from the query context.
/// This function exists because we've erased `LintStore` as `dyn Any` in the context.
@@ -78,8 +76,8 @@ impl<'tcx, T: LateLintPass<'tcx>> LateContextAndPass<'tcx, T> {
self.context.param_env = old_param_env;
}
- fn process_mod(&mut self, m: &'tcx hir::Mod<'tcx>, s: Span, n: hir::HirId) {
- lint_callback!(self, check_mod, m, s, n);
+ fn process_mod(&mut self, m: &'tcx hir::Mod<'tcx>, n: hir::HirId) {
+ lint_callback!(self, check_mod, m, n);
hir_visit::walk_mod(self, m, n);
}
}
@@ -189,19 +187,12 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas
let old_cached_typeck_results = self.context.cached_typeck_results.take();
let body = self.context.tcx.hir().body(body_id);
lint_callback!(self, check_fn, fk, decl, body, span, id);
- hir_visit::walk_fn(self, fk, decl, body_id, span, id);
+ hir_visit::walk_fn(self, fk, decl, body_id, id);
self.context.enclosing_body = old_enclosing_body;
self.context.cached_typeck_results.set(old_cached_typeck_results);
}
- fn visit_variant_data(
- &mut self,
- s: &'tcx hir::VariantData<'tcx>,
- _: Symbol,
- _: &'tcx hir::Generics<'tcx>,
- _: hir::HirId,
- _: Span,
- ) {
+ fn visit_variant_data(&mut self, s: &'tcx hir::VariantData<'tcx>) {
lint_callback!(self, check_struct_def, s);
hir_visit::walk_struct_def(self, s);
}
@@ -213,15 +204,10 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas
})
}
- fn visit_variant(
- &mut self,
- v: &'tcx hir::Variant<'tcx>,
- g: &'tcx hir::Generics<'tcx>,
- item_id: hir::HirId,
- ) {
+ fn visit_variant(&mut self, v: &'tcx hir::Variant<'tcx>) {
self.with_lint_attrs(v.id, |cx| {
lint_callback!(cx, check_variant, v);
- hir_visit::walk_variant(cx, v, g, item_id);
+ hir_visit::walk_variant(cx, v);
})
}
@@ -234,9 +220,9 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas
hir_visit::walk_inf(self, inf);
}
- fn visit_mod(&mut self, m: &'tcx hir::Mod<'tcx>, s: Span, n: hir::HirId) {
+ fn visit_mod(&mut self, m: &'tcx hir::Mod<'tcx>, _: Span, n: hir::HirId) {
if !self.context.only_module {
- self.process_mod(m, s, n);
+ self.process_mod(m, n);
}
}
@@ -272,13 +258,9 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas
hir_visit::walk_where_predicate(self, p);
}
- fn visit_poly_trait_ref(
- &mut self,
- t: &'tcx hir::PolyTraitRef<'tcx>,
- m: hir::TraitBoundModifier,
- ) {
- lint_callback!(self, check_poly_trait_ref, t, m);
- hir_visit::walk_poly_trait_ref(self, t, m);
+ fn visit_poly_trait_ref(&mut self, t: &'tcx hir::PolyTraitRef<'tcx>) {
+ lint_callback!(self, check_poly_trait_ref, t);
+ hir_visit::walk_poly_trait_ref(self, t);
}
fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) {
@@ -320,12 +302,12 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas
}
}
-struct LateLintPassObjects<'a> {
- lints: &'a mut [LateLintPassObject],
+struct LateLintPassObjects<'a, 'tcx> {
+ lints: &'a mut [LateLintPassObject<'tcx>],
}
#[allow(rustc::lint_pass_impl_without_macro)]
-impl LintPass for LateLintPassObjects<'_> {
+impl LintPass for LateLintPassObjects<'_, '_> {
fn name(&self) -> &'static str {
panic!()
}
@@ -343,7 +325,7 @@ macro_rules! expand_late_lint_pass_impl_methods {
macro_rules! late_lint_pass_impl {
([], [$hir:tt], $methods:tt) => {
- impl<$hir> LateLintPass<$hir> for LateLintPassObjects<'_> {
+ impl<$hir> LateLintPass<$hir> for LateLintPassObjects<'_, $hir> {
expand_late_lint_pass_impl_methods!([$hir], $methods);
}
};
@@ -356,14 +338,14 @@ fn late_lint_mod_pass<'tcx, T: LateLintPass<'tcx>>(
module_def_id: LocalDefId,
pass: T,
) {
- let access_levels = &tcx.privacy_access_levels(());
+ let effective_visibilities = &tcx.effective_visibilities(());
let context = LateContext {
tcx,
enclosing_body: None,
cached_typeck_results: Cell::new(None),
param_env: ty::ParamEnv::empty(),
- access_levels,
+ 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,
@@ -372,8 +354,8 @@ fn late_lint_mod_pass<'tcx, T: LateLintPass<'tcx>>(
let mut cx = LateContextAndPass { context, pass };
- let (module, span, hir_id) = tcx.hir().get_module(module_def_id);
- cx.process_mod(module, span, hir_id);
+ let (module, _span, hir_id) = tcx.hir().get_module(module_def_id);
+ cx.process_mod(module, hir_id);
// Visit the crate attributes
if hir_id == hir::CRATE_HIR_ID {
@@ -396,7 +378,7 @@ pub fn late_lint_mod<'tcx, T: LateLintPass<'tcx>>(
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)()).collect();
+ 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[..] });
@@ -404,14 +386,14 @@ pub fn late_lint_mod<'tcx, T: LateLintPass<'tcx>>(
}
fn late_lint_pass_crate<'tcx, T: LateLintPass<'tcx>>(tcx: TyCtxt<'tcx>, pass: T) {
- let access_levels = &tcx.privacy_access_levels(());
+ let effective_visibilities = &tcx.effective_visibilities(());
let context = LateContext {
tcx,
enclosing_body: None,
cached_typeck_results: Cell::new(None),
param_env: ty::ParamEnv::empty(),
- access_levels,
+ effective_visibilities,
lint_store: unerased_lint_store(tcx),
last_node_with_lint_attrs: hir::CRATE_HIR_ID,
generics: None,
@@ -432,7 +414,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)()).collect::<Vec<_>>();
+ 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() {
@@ -442,20 +425,23 @@ fn late_lint_crate<'tcx, T: LateLintPass<'tcx>>(tcx: TyCtxt<'tcx>, builtin_lints
late_lint_pass_crate(tcx, builtin_lints);
} else {
for pass in &mut passes {
- tcx.sess.prof.extra_verbose_generic_activity("run_late_lint", pass.name()).run(|| {
- late_lint_pass_crate(tcx, LateLintPassObjects { lints: slice::from_mut(pass) });
- });
+ 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)()).collect();
+ unerased_lint_store(tcx).late_module_passes.iter().map(|pass| (pass)(tcx)).collect();
for pass in &mut passes {
- tcx.sess.prof.extra_verbose_generic_activity("run_late_module_lint", pass.name()).run(
- || {
+ 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) });
- },
- );
+ });
}
}
}
diff --git a/compiler/rustc_lint/src/let_underscore.rs b/compiler/rustc_lint/src/let_underscore.rs
new file mode 100644
index 000000000..78f355ec3
--- /dev/null
+++ b/compiler/rustc_lint/src/let_underscore.rs
@@ -0,0 +1,168 @@
+use crate::{LateContext, LateLintPass, LintContext};
+use rustc_errors::{Applicability, DiagnosticBuilder, MultiSpan};
+use rustc_hir as hir;
+use rustc_middle::ty;
+use rustc_span::Symbol;
+
+declare_lint! {
+ /// The `let_underscore_drop` lint checks for statements which don't bind
+ /// an expression which has a non-trivial Drop implementation to anything,
+ /// causing the expression to be dropped immediately instead of at end of
+ /// scope.
+ ///
+ /// ### Example
+ /// ```
+ /// struct SomeStruct;
+ /// impl Drop for SomeStruct {
+ /// fn drop(&mut self) {
+ /// println!("Dropping SomeStruct");
+ /// }
+ /// }
+ ///
+ /// fn main() {
+ /// #[warn(let_underscore_drop)]
+ /// // SomeStuct is dropped immediately instead of at end of scope,
+ /// // so "Dropping SomeStruct" is printed before "end of main".
+ /// // The order of prints would be reversed if SomeStruct was bound to
+ /// // a name (such as "_foo").
+ /// let _ = SomeStruct;
+ /// println!("end of main");
+ /// }
+ /// ```
+ ///
+ /// {{produces}}
+ ///
+ /// ### Explanation
+ ///
+ /// Statements which assign an expression to an underscore causes the
+ /// expression to immediately drop instead of extending the expression's
+ /// lifetime to the end of the scope. This is usually unintended,
+ /// especially for types like `MutexGuard`, which are typically used to
+ /// lock a mutex for the duration of an entire scope.
+ ///
+ /// If you want to extend the expression's lifetime to the end of the scope,
+ /// assign an underscore-prefixed name (such as `_foo`) to the expression.
+ /// If you do actually want to drop the expression immediately, then
+ /// calling `std::mem::drop` on the expression is clearer and helps convey
+ /// intent.
+ pub LET_UNDERSCORE_DROP,
+ Allow,
+ "non-binding let on a type that implements `Drop`"
+}
+
+declare_lint! {
+ /// The `let_underscore_lock` lint checks for statements which don't bind
+ /// a mutex to anything, causing the lock to be released immediately instead
+ /// of at end of scope, which is typically incorrect.
+ ///
+ /// ### Example
+ /// ```compile_fail
+ /// use std::sync::{Arc, Mutex};
+ /// use std::thread;
+ /// let data = Arc::new(Mutex::new(0));
+ ///
+ /// thread::spawn(move || {
+ /// // The lock is immediately released instead of at the end of the
+ /// // scope, which is probably not intended.
+ /// let _ = data.lock().unwrap();
+ /// println!("doing some work");
+ /// let mut lock = data.lock().unwrap();
+ /// *lock += 1;
+ /// });
+ /// ```
+ ///
+ /// {{produces}}
+ ///
+ /// ### Explanation
+ ///
+ /// Statements which assign an expression to an underscore causes the
+ /// expression to immediately drop instead of extending the expression's
+ /// lifetime to the end of the scope. This is usually unintended,
+ /// especially for types like `MutexGuard`, which are typically used to
+ /// lock a mutex for the duration of an entire scope.
+ ///
+ /// If you want to extend the expression's lifetime to the end of the scope,
+ /// assign an underscore-prefixed name (such as `_foo`) to the expression.
+ /// If you do actually want to drop the expression immediately, then
+ /// calling `std::mem::drop` on the expression is clearer and helps convey
+ /// intent.
+ pub LET_UNDERSCORE_LOCK,
+ Deny,
+ "non-binding let on a synchronization lock"
+}
+
+declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_DROP, LET_UNDERSCORE_LOCK]);
+
+const SYNC_GUARD_SYMBOLS: [Symbol; 3] = [
+ rustc_span::sym::MutexGuard,
+ rustc_span::sym::RwLockReadGuard,
+ rustc_span::sym::RwLockWriteGuard,
+];
+
+impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
+ fn check_local(&mut self, cx: &LateContext<'_>, local: &hir::Local<'_>) {
+ if !matches!(local.pat.kind, hir::PatKind::Wild) {
+ return;
+ }
+ if let Some(init) = local.init {
+ let init_ty = cx.typeck_results().expr_ty(init);
+ // If the type has a trivial Drop implementation, then it doesn't
+ // matter that we drop the value immediately.
+ if !init_ty.needs_drop(cx.tcx, cx.param_env) {
+ return;
+ }
+ let is_sync_lock = match init_ty.kind() {
+ ty::Adt(adt, _) => SYNC_GUARD_SYMBOLS
+ .iter()
+ .any(|guard_symbol| cx.tcx.is_diagnostic_item(*guard_symbol, adt.did())),
+ _ => false,
+ };
+
+ if is_sync_lock {
+ let mut span = MultiSpan::from_spans(vec![local.pat.span, init.span]);
+ span.push_span_label(
+ local.pat.span,
+ "this lock is not assigned to a binding and is immediately dropped".to_string(),
+ );
+ span.push_span_label(
+ init.span,
+ "this binding will immediately drop the value assigned to it".to_string(),
+ );
+ cx.struct_span_lint(
+ LET_UNDERSCORE_LOCK,
+ span,
+ "non-binding let on a synchronization lock",
+ |lint| build_lint(lint, local, init.span),
+ )
+ } else {
+ cx.struct_span_lint(
+ LET_UNDERSCORE_DROP,
+ local.span,
+ "non-binding let on a type that implements `Drop`",
+ |lint| build_lint(lint, local, init.span),
+ )
+ }
+ }
+ }
+}
+
+fn build_lint<'a, 'b>(
+ lint: &'a mut DiagnosticBuilder<'b, ()>,
+ local: &hir::Local<'_>,
+ init_span: rustc_span::Span,
+) -> &'a mut DiagnosticBuilder<'b, ()> {
+ lint.span_suggestion_verbose(
+ local.pat.span,
+ "consider binding to an unused variable to avoid immediately dropping the value",
+ "_unused",
+ Applicability::MachineApplicable,
+ )
+ .multipart_suggestion(
+ "consider immediately dropping the value",
+ vec![
+ (local.span.until(init_span), "drop(".to_string()),
+ (init_span.shrink_to_hi(), ")".to_string()),
+ ],
+ Applicability::MachineApplicable,
+ )
+}
diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs
index 00e96f20d..db0a3419e 100644
--- a/compiler/rustc_lint/src/levels.rs
+++ b/compiler/rustc_lint/src/levels.rs
@@ -3,13 +3,15 @@ use crate::late::unerased_lint_store;
use rustc_ast as ast;
use rustc_ast_pretty::pprust;
use rustc_data_structures::fx::FxHashMap;
-use rustc_errors::{struct_span_err, Applicability, Diagnostic, LintDiagnosticBuilder, MultiSpan};
+use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, DiagnosticMessage, MultiSpan};
use rustc_hir as hir;
-use rustc_hir::{intravisit, HirId};
+use rustc_hir::intravisit::{self, Visitor};
+use rustc_hir::HirId;
+use rustc_index::vec::IndexVec;
use rustc_middle::hir::nested_filter;
use rustc_middle::lint::{
- struct_lint_level, LevelAndSource, LintExpectation, LintLevelMap, LintLevelSets,
- LintLevelSource, LintSet, LintStackIndex, COMMAND_LINE,
+ reveal_actual_level, struct_lint_level, LevelAndSource, LintExpectation, LintLevelSource,
+ ShallowLintLevelMap,
};
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{RegisteredTools, TyCtxt};
@@ -21,49 +23,414 @@ use rustc_session::parse::{add_feature_diagnostics, feature_err};
use rustc_session::Session;
use rustc_span::symbol::{sym, Symbol};
use rustc_span::{Span, DUMMY_SP};
-use tracing::debug;
-fn lint_levels(tcx: TyCtxt<'_>, (): ()) -> LintLevelMap {
- let store = unerased_lint_store(tcx);
- let levels =
- LintLevelsBuilder::new(tcx.sess, false, &store, &tcx.resolutions(()).registered_tools);
- let mut builder = LintLevelMapBuilder { levels, tcx };
- let krate = tcx.hir().krate();
+use crate::errors::{
+ MalformedAttribute, MalformedAttributeSub, OverruledAttribute, OverruledAttributeSub,
+ UnknownToolInScopedLint,
+};
+
+/// Collection of lint levels for the whole crate.
+/// This is used by AST-based lints, which do not
+/// wait until we have built HIR to be emitted.
+#[derive(Debug)]
+struct LintLevelSets {
+ /// Linked list of specifications.
+ list: IndexVec<LintStackIndex, LintSet>,
+}
+
+rustc_index::newtype_index! {
+ struct LintStackIndex {
+ ENCODABLE = custom, // we don't need encoding
+ const COMMAND_LINE = 0,
+ }
+}
+
+/// Specifications found at this position in the stack. This map only represents the lints
+/// found for one set of attributes (like `shallow_lint_levels_on` does).
+///
+/// We store the level specifications as a linked list.
+/// Each `LintSet` represents a set of attributes on the same AST node.
+/// The `parent` forms a linked list that matches the AST tree.
+/// This way, walking the linked list is equivalent to walking the AST bottom-up
+/// to find the specifications for a given lint.
+#[derive(Debug)]
+struct LintSet {
+ // -A,-W,-D flags, a `Symbol` for the flag itself and `Level` for which
+ // flag.
+ specs: FxHashMap<LintId, LevelAndSource>,
+ parent: LintStackIndex,
+}
- builder.levels.id_to_set.reserve(krate.owners.len() + 1);
+impl LintLevelSets {
+ fn new() -> Self {
+ LintLevelSets { list: IndexVec::new() }
+ }
- let push =
- builder.levels.push(tcx.hir().attrs(hir::CRATE_HIR_ID), true, Some(hir::CRATE_HIR_ID));
+ fn get_lint_level(
+ &self,
+ lint: &'static Lint,
+ idx: LintStackIndex,
+ aux: Option<&FxHashMap<LintId, LevelAndSource>>,
+ sess: &Session,
+ ) -> LevelAndSource {
+ let lint = LintId::of(lint);
+ let (level, mut src) = self.raw_lint_id_level(lint, idx, aux);
+ let level = reveal_actual_level(level, &mut src, sess, lint, |id| {
+ self.raw_lint_id_level(id, idx, aux)
+ });
+ (level, src)
+ }
+
+ fn raw_lint_id_level(
+ &self,
+ id: LintId,
+ mut idx: LintStackIndex,
+ aux: Option<&FxHashMap<LintId, LevelAndSource>>,
+ ) -> (Option<Level>, LintLevelSource) {
+ if let Some(specs) = aux {
+ if let Some(&(level, src)) = specs.get(&id) {
+ return (Some(level), src);
+ }
+ }
+ loop {
+ let LintSet { ref specs, parent } = self.list[idx];
+ if let Some(&(level, src)) = specs.get(&id) {
+ return (Some(level), src);
+ }
+ if idx == COMMAND_LINE {
+ return (None, LintLevelSource::Default);
+ }
+ idx = parent;
+ }
+ }
+}
- builder.levels.register_id(hir::CRATE_HIR_ID);
+fn lint_expectations(tcx: TyCtxt<'_>, (): ()) -> Vec<(LintExpectationId, LintExpectation)> {
+ let store = unerased_lint_store(tcx);
+
+ let mut builder = LintLevelsBuilder {
+ sess: tcx.sess,
+ provider: QueryMapExpectationsWrapper {
+ tcx,
+ cur: hir::CRATE_HIR_ID,
+ specs: ShallowLintLevelMap::default(),
+ expectations: Vec::new(),
+ unstable_to_stable_ids: FxHashMap::default(),
+ empty: FxHashMap::default(),
+ },
+ warn_about_weird_lints: false,
+ store,
+ registered_tools: &tcx.resolutions(()).registered_tools,
+ };
+
+ builder.add_command_line();
+ builder.add_id(hir::CRATE_HIR_ID);
tcx.hir().walk_toplevel_module(&mut builder);
- builder.levels.pop(push);
- builder.levels.update_unstable_expectation_ids();
- builder.levels.build_map()
+ tcx.sess.diagnostic().update_unstable_expectation_id(&builder.provider.unstable_to_stable_ids);
+
+ builder.provider.expectations
}
-pub struct LintLevelsBuilder<'s> {
- sess: &'s Session,
- lint_expectations: Vec<(LintExpectationId, LintExpectation)>,
- /// Each expectation has a stable and an unstable identifier. This map
- /// is used to map from unstable to stable [`LintExpectationId`]s.
- expectation_id_map: FxHashMap<LintExpectationId, LintExpectationId>,
+#[instrument(level = "trace", skip(tcx), ret)]
+fn shallow_lint_levels_on(tcx: TyCtxt<'_>, owner: hir::OwnerId) -> ShallowLintLevelMap {
+ let store = unerased_lint_store(tcx);
+ let attrs = tcx.hir_attrs(owner);
+
+ let mut levels = LintLevelsBuilder {
+ sess: tcx.sess,
+ provider: LintLevelQueryMap {
+ tcx,
+ cur: owner.into(),
+ specs: ShallowLintLevelMap::default(),
+ empty: FxHashMap::default(),
+ attrs,
+ },
+ warn_about_weird_lints: false,
+ store,
+ registered_tools: &tcx.resolutions(()).registered_tools,
+ };
+
+ if owner == hir::CRATE_OWNER_ID {
+ levels.add_command_line();
+ }
+
+ match attrs.map.range(..) {
+ // There is only something to do if there are attributes at all.
+ [] => {}
+ // Most of the time, there is only one attribute. Avoid fetching HIR in that case.
+ [(local_id, _)] => levels.add_id(HirId { owner, local_id: *local_id }),
+ // 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) {
+ 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),
+ hir::OwnerNode::ImplItem(item) => levels.visit_impl_item(item),
+ hir::OwnerNode::Crate(mod_) => {
+ levels.add_id(hir::CRATE_HIR_ID);
+ levels.visit_mod(mod_, mod_.spans.inner_span, hir::CRATE_HIR_ID)
+ }
+ },
+ }
+
+ let specs = levels.provider.specs;
+
+ #[cfg(debug_assertions)]
+ for (_, v) in specs.specs.iter() {
+ debug_assert!(!v.is_empty());
+ }
+
+ specs
+}
+
+pub struct TopDown {
sets: LintLevelSets,
- id_to_set: FxHashMap<HirId, LintStackIndex>,
cur: LintStackIndex,
+}
+
+pub trait LintLevelsProvider {
+ fn current_specs(&self) -> &FxHashMap<LintId, LevelAndSource>;
+ fn insert(&mut self, id: LintId, lvl: LevelAndSource);
+ fn get_lint_level(&self, lint: &'static Lint, sess: &Session) -> LevelAndSource;
+ fn push_expectation(&mut self, _id: LintExpectationId, _expectation: LintExpectation) {}
+}
+
+impl LintLevelsProvider for TopDown {
+ fn current_specs(&self) -> &FxHashMap<LintId, LevelAndSource> {
+ &self.sets.list[self.cur].specs
+ }
+
+ fn insert(&mut self, id: LintId, lvl: LevelAndSource) {
+ self.sets.list[self.cur].specs.insert(id, lvl);
+ }
+
+ fn get_lint_level(&self, lint: &'static Lint, sess: &Session) -> LevelAndSource {
+ self.sets.get_lint_level(lint, self.cur, Some(self.current_specs()), sess)
+ }
+}
+
+struct LintLevelQueryMap<'tcx> {
+ tcx: TyCtxt<'tcx>,
+ cur: HirId,
+ specs: ShallowLintLevelMap,
+ /// Empty hash map to simplify code.
+ empty: FxHashMap<LintId, LevelAndSource>,
+ attrs: &'tcx hir::AttributeMap<'tcx>,
+}
+
+impl LintLevelsProvider for LintLevelQueryMap<'_> {
+ fn current_specs(&self) -> &FxHashMap<LintId, LevelAndSource> {
+ self.specs.specs.get(&self.cur.local_id).unwrap_or(&self.empty)
+ }
+ fn insert(&mut self, id: LintId, lvl: LevelAndSource) {
+ self.specs.specs.get_mut_or_insert_default(self.cur.local_id).insert(id, lvl);
+ }
+ fn get_lint_level(&self, lint: &'static Lint, _: &Session) -> LevelAndSource {
+ self.specs.lint_level_id_at_node(self.tcx, LintId::of(lint), self.cur)
+ }
+}
+
+struct QueryMapExpectationsWrapper<'tcx> {
+ tcx: TyCtxt<'tcx>,
+ cur: HirId,
+ specs: ShallowLintLevelMap,
+ expectations: Vec<(LintExpectationId, LintExpectation)>,
+ unstable_to_stable_ids: FxHashMap<LintExpectationId, LintExpectationId>,
+ /// Empty hash map to simplify code.
+ empty: FxHashMap<LintId, LevelAndSource>,
+}
+
+impl LintLevelsProvider for QueryMapExpectationsWrapper<'_> {
+ fn current_specs(&self) -> &FxHashMap<LintId, LevelAndSource> {
+ self.specs.specs.get(&self.cur.local_id).unwrap_or(&self.empty)
+ }
+ fn insert(&mut self, id: LintId, lvl: LevelAndSource) {
+ let specs = self.specs.specs.get_mut_or_insert_default(self.cur.local_id);
+ specs.clear();
+ specs.insert(id, lvl);
+ }
+ fn get_lint_level(&self, lint: &'static Lint, _: &Session) -> LevelAndSource {
+ self.specs.lint_level_id_at_node(self.tcx, LintId::of(lint), self.cur)
+ }
+ fn push_expectation(&mut self, id: LintExpectationId, expectation: LintExpectation) {
+ let LintExpectationId::Stable { attr_id: Some(attr_id), hir_id, attr_index, .. } = id else { bug!("unstable expectation id should already be mapped") };
+ let key = LintExpectationId::Unstable { attr_id, lint_index: None };
+
+ if !self.unstable_to_stable_ids.contains_key(&key) {
+ self.unstable_to_stable_ids.insert(
+ key,
+ LintExpectationId::Stable { hir_id, attr_index, lint_index: None, attr_id: None },
+ );
+ }
+
+ self.expectations.push((id.normalize(), expectation));
+ }
+}
+
+impl<'tcx> LintLevelsBuilder<'_, LintLevelQueryMap<'tcx>> {
+ fn add_id(&mut self, hir_id: HirId) {
+ self.provider.cur = hir_id;
+ self.add(
+ self.provider.attrs.get(hir_id.local_id),
+ hir_id == hir::CRATE_HIR_ID,
+ Some(hir_id),
+ );
+ }
+}
+
+impl<'tcx> Visitor<'tcx> for LintLevelsBuilder<'_, LintLevelQueryMap<'tcx>> {
+ type NestedFilter = nested_filter::OnlyBodies;
+
+ fn nested_visit_map(&mut self) -> Self::Map {
+ self.provider.tcx.hir()
+ }
+
+ fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
+ self.add_id(param.hir_id);
+ intravisit::walk_param(self, param);
+ }
+
+ fn visit_item(&mut self, it: &'tcx hir::Item<'tcx>) {
+ self.add_id(it.hir_id());
+ intravisit::walk_item(self, it);
+ }
+
+ fn visit_foreign_item(&mut self, it: &'tcx hir::ForeignItem<'tcx>) {
+ self.add_id(it.hir_id());
+ intravisit::walk_foreign_item(self, it);
+ }
+
+ fn visit_stmt(&mut self, e: &'tcx hir::Stmt<'tcx>) {
+ // We will call `add_id` when we walk
+ // the `StmtKind`. The outer statement itself doesn't
+ // define the lint levels.
+ intravisit::walk_stmt(self, e);
+ }
+
+ fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) {
+ self.add_id(e.hir_id);
+ intravisit::walk_expr(self, e);
+ }
+
+ fn visit_field_def(&mut self, s: &'tcx hir::FieldDef<'tcx>) {
+ self.add_id(s.hir_id);
+ intravisit::walk_field_def(self, s);
+ }
+
+ fn visit_variant(&mut self, v: &'tcx hir::Variant<'tcx>) {
+ self.add_id(v.id);
+ intravisit::walk_variant(self, v);
+ }
+
+ fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>) {
+ self.add_id(l.hir_id);
+ intravisit::walk_local(self, l);
+ }
+
+ fn visit_arm(&mut self, a: &'tcx hir::Arm<'tcx>) {
+ self.add_id(a.hir_id);
+ intravisit::walk_arm(self, a);
+ }
+
+ fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) {
+ self.add_id(trait_item.hir_id());
+ intravisit::walk_trait_item(self, trait_item);
+ }
+
+ fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
+ self.add_id(impl_item.hir_id());
+ intravisit::walk_impl_item(self, impl_item);
+ }
+}
+
+impl<'tcx> LintLevelsBuilder<'_, QueryMapExpectationsWrapper<'tcx>> {
+ fn add_id(&mut self, hir_id: HirId) {
+ self.provider.cur = hir_id;
+ self.add(self.provider.tcx.hir().attrs(hir_id), hir_id == hir::CRATE_HIR_ID, Some(hir_id));
+ }
+}
+
+impl<'tcx> Visitor<'tcx> for LintLevelsBuilder<'_, QueryMapExpectationsWrapper<'tcx>> {
+ type NestedFilter = nested_filter::All;
+
+ fn nested_visit_map(&mut self) -> Self::Map {
+ self.provider.tcx.hir()
+ }
+
+ fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
+ self.add_id(param.hir_id);
+ intravisit::walk_param(self, param);
+ }
+
+ fn visit_item(&mut self, it: &'tcx hir::Item<'tcx>) {
+ self.add_id(it.hir_id());
+ intravisit::walk_item(self, it);
+ }
+
+ fn visit_foreign_item(&mut self, it: &'tcx hir::ForeignItem<'tcx>) {
+ self.add_id(it.hir_id());
+ intravisit::walk_foreign_item(self, it);
+ }
+
+ fn visit_stmt(&mut self, e: &'tcx hir::Stmt<'tcx>) {
+ // We will call `add_id` when we walk
+ // the `StmtKind`. The outer statement itself doesn't
+ // define the lint levels.
+ intravisit::walk_stmt(self, e);
+ }
+
+ fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) {
+ self.add_id(e.hir_id);
+ intravisit::walk_expr(self, e);
+ }
+
+ fn visit_field_def(&mut self, s: &'tcx hir::FieldDef<'tcx>) {
+ self.add_id(s.hir_id);
+ intravisit::walk_field_def(self, s);
+ }
+
+ fn visit_variant(&mut self, v: &'tcx hir::Variant<'tcx>) {
+ self.add_id(v.id);
+ intravisit::walk_variant(self, v);
+ }
+
+ fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>) {
+ self.add_id(l.hir_id);
+ intravisit::walk_local(self, l);
+ }
+
+ fn visit_arm(&mut self, a: &'tcx hir::Arm<'tcx>) {
+ self.add_id(a.hir_id);
+ intravisit::walk_arm(self, a);
+ }
+
+ fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) {
+ self.add_id(trait_item.hir_id());
+ intravisit::walk_trait_item(self, trait_item);
+ }
+
+ fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
+ self.add_id(impl_item.hir_id());
+ intravisit::walk_impl_item(self, impl_item);
+ }
+}
+
+pub struct LintLevelsBuilder<'s, P> {
+ sess: &'s Session,
+ provider: P,
warn_about_weird_lints: bool,
store: &'s LintStore,
registered_tools: &'s RegisteredTools,
}
-pub struct BuilderPush {
+pub(crate) struct BuilderPush {
prev: LintStackIndex,
- pub changed: bool,
}
-impl<'s> LintLevelsBuilder<'s> {
- pub fn new(
+impl<'s> LintLevelsBuilder<'s, TopDown> {
+ pub(crate) fn new(
sess: &'s Session,
warn_about_weird_lints: bool,
store: &'s LintStore,
@@ -71,20 +438,74 @@ impl<'s> LintLevelsBuilder<'s> {
) -> Self {
let mut builder = LintLevelsBuilder {
sess,
- lint_expectations: Default::default(),
- expectation_id_map: Default::default(),
- sets: LintLevelSets::new(),
- cur: COMMAND_LINE,
- id_to_set: Default::default(),
+ provider: TopDown { sets: LintLevelSets::new(), cur: COMMAND_LINE },
warn_about_weird_lints,
store,
registered_tools,
};
- builder.process_command_line(sess, store);
- assert_eq!(builder.sets.list.len(), 1);
+ builder.process_command_line();
+ assert_eq!(builder.provider.sets.list.len(), 1);
builder
}
+ fn process_command_line(&mut self) {
+ self.provider.cur = self
+ .provider
+ .sets
+ .list
+ .push(LintSet { specs: FxHashMap::default(), parent: COMMAND_LINE });
+ self.add_command_line();
+ }
+
+ /// Pushes a list of AST lint attributes onto this context.
+ ///
+ /// This function will return a `BuilderPush` object which should be passed
+ /// to `pop` when this scope for the attributes provided is exited.
+ ///
+ /// This function will perform a number of tasks:
+ ///
+ /// * It'll validate all lint-related attributes in `attrs`
+ /// * It'll mark all lint-related attributes as used
+ /// * Lint levels will be updated based on the attributes provided
+ /// * Lint attributes are validated, e.g., a `#[forbid]` can't be switched to
+ /// `#[allow]`
+ ///
+ /// Don't forget to call `pop`!
+ pub(crate) fn push(
+ &mut self,
+ attrs: &[ast::Attribute],
+ is_crate_node: bool,
+ source_hir_id: Option<HirId>,
+ ) -> BuilderPush {
+ let prev = self.provider.cur;
+ self.provider.cur =
+ self.provider.sets.list.push(LintSet { specs: FxHashMap::default(), parent: prev });
+
+ self.add(attrs, is_crate_node, source_hir_id);
+
+ if self.provider.current_specs().is_empty() {
+ self.provider.sets.list.pop();
+ self.provider.cur = prev;
+ }
+
+ BuilderPush { prev }
+ }
+
+ /// Called after `push` when the scope of a set of attributes are exited.
+ pub(crate) fn pop(&mut self, push: BuilderPush) {
+ self.provider.cur = push.prev;
+ std::mem::forget(push);
+ }
+}
+
+#[cfg(debug_assertions)]
+impl Drop for BuilderPush {
+ fn drop(&mut self) {
+ panic!("Found a `push` without a `pop`.");
+ }
+}
+
+impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
pub(crate) fn sess(&self) -> &Session {
self.sess
}
@@ -94,24 +515,20 @@ impl<'s> LintLevelsBuilder<'s> {
}
fn current_specs(&self) -> &FxHashMap<LintId, LevelAndSource> {
- &self.sets.list[self.cur].specs
+ self.provider.current_specs()
}
- fn current_specs_mut(&mut self) -> &mut FxHashMap<LintId, LevelAndSource> {
- &mut self.sets.list[self.cur].specs
+ fn insert(&mut self, id: LintId, lvl: LevelAndSource) {
+ self.provider.insert(id, lvl)
}
- fn process_command_line(&mut self, sess: &Session, store: &LintStore) {
- self.sets.lint_cap = sess.opts.lint_cap.unwrap_or(Level::Forbid);
-
- self.cur =
- self.sets.list.push(LintSet { specs: FxHashMap::default(), parent: COMMAND_LINE });
- for &(ref lint_name, level) in &sess.opts.lint_opts {
- store.check_lint_name_cmdline(sess, &lint_name, level, self.registered_tools);
+ fn add_command_line(&mut self) {
+ for &(ref lint_name, level) in &self.sess.opts.lint_opts {
+ self.store.check_lint_name_cmdline(self.sess, &lint_name, level, self.registered_tools);
let orig_level = level;
let lint_flag_val = Symbol::intern(lint_name);
- let Ok(ids) = store.find_lints(&lint_name) else {
+ let Ok(ids) = self.store.find_lints(&lint_name) else {
// errors handled in check_lint_name_cmdline above
continue
};
@@ -125,7 +542,7 @@ impl<'s> LintLevelsBuilder<'s> {
if self.check_gated_lint(id, DUMMY_SP) {
let src = LintLevelSource::CommandLine(lint_flag_val, orig_level);
- self.current_specs_mut().insert(id, (level, src));
+ self.insert(id, (level, src));
}
}
}
@@ -134,9 +551,11 @@ impl<'s> LintLevelsBuilder<'s> {
/// Attempts to insert the `id` to `level_src` map entry. If unsuccessful
/// (e.g. if a forbid was already inserted on the same scope), then emits a
/// diagnostic with no change to `specs`.
- fn insert_spec(&mut self, id: LintId, (level, src): LevelAndSource) {
- let (old_level, old_src) =
- self.sets.get_lint_level(id.lint, self.cur, Some(self.current_specs()), &self.sess);
+ fn insert_spec(&mut self, id: LintId, (mut level, src): LevelAndSource) {
+ let (old_level, old_src) = self.provider.get_lint_level(id.lint, &self.sess);
+ if let Level::Expect(id) = &mut level && let LintExpectationId::Stable { .. } = id {
+ *id = id.normalize();
+ }
// Setting to a non-forbid level is an error if the lint previously had
// a forbid level. Note that this is not necessarily true even with a
// `#[forbid(..)]` attribute present, as that is overridden by `--cap-lints`.
@@ -154,7 +573,7 @@ impl<'s> LintLevelsBuilder<'s> {
let id_name = id.lint.name_lower();
let fcw_warning = match old_src {
LintLevelSource::Default => false,
- LintLevelSource::Node(symbol, _, _) => self.store.is_lint_group(symbol),
+ LintLevelSource::Node { name, .. } => self.store.is_lint_group(name),
LintLevelSource::CommandLine(symbol, _) => self.store.is_lint_group(symbol),
};
debug!(
@@ -174,8 +593,8 @@ impl<'s> LintLevelsBuilder<'s> {
id.to_string()
));
}
- LintLevelSource::Node(_, forbid_source_span, reason) => {
- diag.span_label(forbid_source_span, "`forbid` level set here");
+ LintLevelSource::Node { span, reason, .. } => {
+ diag.span_label(span, "`forbid` level set here");
if let Some(rationale) = reason {
diag.note(rationale.as_str());
}
@@ -186,28 +605,35 @@ impl<'s> LintLevelsBuilder<'s> {
}
};
if !fcw_warning {
- let mut diag_builder = struct_span_err!(
- self.sess,
- src.span(),
- E0453,
- "{}({}) incompatible with previous forbid",
- level.as_str(),
- src.name(),
- );
- decorate_diag(&mut diag_builder);
- diag_builder.emit();
+ self.sess.emit_err(OverruledAttribute {
+ span: src.span(),
+ overruled: src.span(),
+ lint_level: level.as_str().to_string(),
+ lint_source: src.name(),
+ sub: match old_src {
+ LintLevelSource::Default => {
+ OverruledAttributeSub::DefaultSource { id: id.to_string() }
+ }
+ LintLevelSource::Node { span, reason, .. } => {
+ OverruledAttributeSub::NodeSource { span, reason }
+ }
+ LintLevelSource::CommandLine(_, _) => {
+ OverruledAttributeSub::CommandLineSource
+ }
+ },
+ });
} else {
self.struct_lint(
FORBIDDEN_LINT_GROUPS,
Some(src.span().into()),
- |diag_builder| {
- let mut diag_builder = diag_builder.build(&format!(
- "{}({}) incompatible with previous forbid",
- level.as_str(),
- src.name(),
- ));
- decorate_diag(&mut diag_builder);
- diag_builder.emit();
+ format!(
+ "{}({}) incompatible with previous forbid",
+ level.as_str(),
+ src.name(),
+ ),
+ |lint| {
+ decorate_diag(lint);
+ lint
},
);
}
@@ -230,46 +656,21 @@ impl<'s> LintLevelsBuilder<'s> {
match (old_level, level) {
// If the new level is an expectation store it in `ForceWarn`
- (Level::ForceWarn(_), Level::Expect(expectation_id)) => self
- .current_specs_mut()
- .insert(id, (Level::ForceWarn(Some(expectation_id)), old_src)),
- // Keep `ForceWarn` level but drop the expectation
- (Level::ForceWarn(_), _) => {
- self.current_specs_mut().insert(id, (Level::ForceWarn(None), old_src))
+ (Level::ForceWarn(_), Level::Expect(expectation_id)) => {
+ self.insert(id, (Level::ForceWarn(Some(expectation_id)), old_src))
}
+ // Keep `ForceWarn` level but drop the expectation
+ (Level::ForceWarn(_), _) => self.insert(id, (Level::ForceWarn(None), old_src)),
// Set the lint level as normal
- _ => self.current_specs_mut().insert(id, (level, src)),
+ _ => self.insert(id, (level, src)),
};
}
- /// Pushes a list of AST lint attributes onto this context.
- ///
- /// This function will return a `BuilderPush` object which should be passed
- /// to `pop` when this scope for the attributes provided is exited.
- ///
- /// This function will perform a number of tasks:
- ///
- /// * It'll validate all lint-related attributes in `attrs`
- /// * It'll mark all lint-related attributes as used
- /// * Lint levels will be updated based on the attributes provided
- /// * Lint attributes are validated, e.g., a `#[forbid]` can't be switched to
- /// `#[allow]`
- ///
- /// Don't forget to call `pop`!
- pub(crate) fn push(
- &mut self,
- attrs: &[ast::Attribute],
- is_crate_node: bool,
- source_hir_id: Option<HirId>,
- ) -> BuilderPush {
- let prev = self.cur;
- self.cur = self.sets.list.push(LintSet { specs: FxHashMap::default(), parent: prev });
-
+ fn add(&mut self, attrs: &[ast::Attribute], is_crate_node: bool, source_hir_id: Option<HirId>) {
let sess = self.sess;
- let bad_attr = |span| struct_span_err!(sess, span, E0452, "malformed lint attribute input");
for (attr_index, attr) in attrs.iter().enumerate() {
if attr.has_name(sym::automatically_derived) {
- self.current_specs_mut().insert(
+ self.insert(
LintId::of(SINGLE_USE_LIFETIMES),
(Level::Allow, LintLevelSource::Default),
);
@@ -280,7 +681,17 @@ impl<'s> LintLevelsBuilder<'s> {
None => continue,
// This is the only lint level with a `LintExpectationId` that can be created from an attribute
Some(Level::Expect(unstable_id)) if let Some(hir_id) = source_hir_id => {
- let stable_id = self.create_stable_id(unstable_id, hir_id, attr_index);
+ let LintExpectationId::Unstable { attr_id, lint_index } = unstable_id
+ else { bug!("stable id Level::from_attr") };
+
+ let stable_id = LintExpectationId::Stable {
+ hir_id,
+ attr_index: attr_index.try_into().unwrap(),
+ lint_index,
+ // we pass the previous unstable attr_id such that we can trace the ast id when building a map
+ // to go from unstable to stable id.
+ attr_id: Some(attr_id),
+ };
Level::Expect(stable_id)
}
@@ -317,20 +728,27 @@ impl<'s> LintLevelsBuilder<'s> {
}
reason = Some(rationale);
} else {
- bad_attr(name_value.span)
- .span_label(name_value.span, "reason must be a string literal")
- .emit();
+ sess.emit_err(MalformedAttribute {
+ span: name_value.span,
+ sub: MalformedAttributeSub::ReasonMustBeStringLiteral(
+ name_value.span,
+ ),
+ });
}
// found reason, reslice meta list to exclude it
metas.pop().unwrap();
} else {
- bad_attr(item.span)
- .span_label(item.span, "bad attribute argument")
- .emit();
+ sess.emit_err(MalformedAttribute {
+ span: item.span,
+ sub: MalformedAttributeSub::BadAttributeArgument(item.span),
+ });
}
}
ast::MetaItemKind::List(_) => {
- bad_attr(item.span).span_label(item.span, "bad attribute argument").emit();
+ sess.emit_err(MalformedAttribute {
+ span: item.span,
+ sub: MalformedAttributeSub::BadAttributeArgument(item.span),
+ });
}
}
}
@@ -348,20 +766,21 @@ impl<'s> LintLevelsBuilder<'s> {
let meta_item = match li {
ast::NestedMetaItem::MetaItem(meta_item) if meta_item.is_word() => meta_item,
_ => {
- let mut err = bad_attr(sp);
- let mut add_label = true;
if let Some(item) = li.meta_item() {
if let ast::MetaItemKind::NameValue(_) = item.kind {
if item.path == sym::reason {
- err.span_label(sp, "reason in lint attribute must come last");
- add_label = false;
+ sess.emit_err(MalformedAttribute {
+ span: sp,
+ sub: MalformedAttributeSub::ReasonMustComeLast(sp),
+ });
+ continue;
}
}
}
- if add_label {
- err.span_label(sp, "bad attribute argument");
- }
- err.emit();
+ sess.emit_err(MalformedAttribute {
+ span: sp,
+ sub: MalformedAttributeSub::BadAttributeArgument(sp),
+ });
continue;
}
};
@@ -387,7 +806,7 @@ impl<'s> LintLevelsBuilder<'s> {
[lint] => *lint == LintId::of(UNFULFILLED_LINT_EXPECTATIONS),
_ => false,
};
- self.lint_expectations.push((
+ self.provider.push_expectation(
expect_id,
LintExpectation::new(
reason,
@@ -395,13 +814,19 @@ impl<'s> LintLevelsBuilder<'s> {
is_unfulfilled_lint_expectations,
tool_name,
),
- ));
+ );
}
- let src = LintLevelSource::Node(
- meta_item.path.segments.last().expect("empty lint name").ident.name,
- sp,
+ let src = LintLevelSource::Node {
+ name: meta_item
+ .path
+ .segments
+ .last()
+ .expect("empty lint name")
+ .ident
+ .name,
+ span: sp,
reason,
- );
+ };
for &id in *ids {
if self.check_gated_lint(id, attr.span) {
self.insert_spec(id, (level, src));
@@ -414,65 +839,60 @@ impl<'s> LintLevelsBuilder<'s> {
Ok(ids) => {
let complete_name =
&format!("{}::{}", tool_ident.unwrap().name, name);
- let src = LintLevelSource::Node(
- Symbol::intern(complete_name),
- sp,
+ let src = LintLevelSource::Node {
+ name: Symbol::intern(complete_name),
+ span: sp,
reason,
- );
- for id in ids {
- self.insert_spec(*id, (level, src));
+ };
+ for &id in ids {
+ if self.check_gated_lint(id, attr.span) {
+ self.insert_spec(id, (level, src));
+ }
}
if let Level::Expect(expect_id) = level {
- self.lint_expectations.push((
+ self.provider.push_expectation(
expect_id,
LintExpectation::new(reason, sp, false, tool_name),
- ));
+ );
}
}
Err((Some(ids), ref new_lint_name)) => {
let lint = builtin::RENAMED_AND_REMOVED_LINTS;
- let (lvl, src) = self.sets.get_lint_level(
- lint,
- self.cur,
- Some(self.current_specs()),
- &sess,
- );
+ let (lvl, src) = self.provider.get_lint_level(lint, &sess);
struct_lint_level(
self.sess,
lint,
lvl,
src,
Some(sp.into()),
+ format!(
+ "lint name `{}` is deprecated \
+ and may not have an effect in the future.",
+ name
+ ),
|lint| {
- let msg = format!(
- "lint name `{}` is deprecated \
- and may not have an effect in the future.",
- name
- );
- lint.build(&msg)
- .span_suggestion(
- sp,
- "change it to",
- new_lint_name,
- Applicability::MachineApplicable,
- )
- .emit();
+ lint.span_suggestion(
+ sp,
+ "change it to",
+ new_lint_name,
+ Applicability::MachineApplicable,
+ )
},
);
- let src = LintLevelSource::Node(
- Symbol::intern(&new_lint_name),
- sp,
+ let src = LintLevelSource::Node {
+ name: Symbol::intern(&new_lint_name),
+ span: sp,
reason,
- );
+ };
for id in ids {
self.insert_spec(*id, (level, src));
}
if let Level::Expect(expect_id) = level {
- self.lint_expectations.push((
+ self.provider.push_expectation(
expect_id,
LintExpectation::new(reason, sp, false, tool_name),
- ));
+ );
}
}
Err((None, _)) => {
@@ -485,22 +905,12 @@ impl<'s> LintLevelsBuilder<'s> {
}
&CheckLintNameResult::NoTool => {
- let mut err = struct_span_err!(
- sess,
- tool_ident.map_or(DUMMY_SP, |ident| ident.span),
- E0710,
- "unknown tool name `{}` found in scoped lint: `{}::{}`",
- tool_name.unwrap(),
- tool_name.unwrap(),
- pprust::path_to_string(&meta_item.path),
- );
- if sess.is_nightly_build() {
- err.help(&format!(
- "add `#![register_tool({})]` to the crate root",
- tool_name.unwrap()
- ));
- }
- err.emit();
+ sess.emit_err(UnknownToolInScopedLint {
+ span: tool_ident.map(|ident| ident.span),
+ tool_name: tool_name.unwrap(),
+ lint_name: pprust::path_to_string(&meta_item.path),
+ is_nightly_build: sess.is_nightly_build().then_some(()),
+ });
continue;
}
@@ -508,57 +918,54 @@ impl<'s> LintLevelsBuilder<'s> {
CheckLintNameResult::Warning(msg, renamed) => {
let lint = builtin::RENAMED_AND_REMOVED_LINTS;
- let (renamed_lint_level, src) = self.sets.get_lint_level(
- lint,
- self.cur,
- Some(self.current_specs()),
- &sess,
- );
+ let (renamed_lint_level, src) = self.provider.get_lint_level(lint, &sess);
struct_lint_level(
self.sess,
lint,
renamed_lint_level,
src,
Some(sp.into()),
+ msg,
|lint| {
- let mut err = lint.build(msg);
if let Some(new_name) = &renamed {
- err.span_suggestion(
+ lint.span_suggestion(
sp,
"use the new name",
new_name,
Applicability::MachineApplicable,
);
}
- err.emit();
+ lint
},
);
}
CheckLintNameResult::NoLint(suggestion) => {
let lint = builtin::UNKNOWN_LINTS;
- let (level, src) = self.sets.get_lint_level(
- lint,
- self.cur,
- Some(self.current_specs()),
+ let (level, src) = self.provider.get_lint_level(lint, self.sess);
+ let name = if let Some(tool_ident) = tool_ident {
+ format!("{}::{}", tool_ident.name, name)
+ } else {
+ name.to_string()
+ };
+ struct_lint_level(
self.sess,
+ lint,
+ level,
+ src,
+ Some(sp.into()),
+ format!("unknown lint: `{}`", name),
+ |lint| {
+ if let Some(suggestion) = suggestion {
+ lint.span_suggestion(
+ sp,
+ "did you mean",
+ suggestion,
+ Applicability::MaybeIncorrect,
+ );
+ }
+ lint
+ },
);
- struct_lint_level(self.sess, lint, level, src, Some(sp.into()), |lint| {
- let name = if let Some(tool_ident) = tool_ident {
- format!("{}::{}", tool_ident.name, name)
- } else {
- name.to_string()
- };
- let mut db = lint.build(format!("unknown lint: `{}`", name));
- if let Some(suggestion) = suggestion {
- db.span_suggestion(
- sp,
- "did you mean",
- suggestion,
- Applicability::MachineApplicable,
- );
- }
- db.emit();
- });
}
}
// If this lint was renamed, apply the new lint instead of ignoring the attribute.
@@ -570,17 +977,21 @@ impl<'s> LintLevelsBuilder<'s> {
if let CheckLintNameResult::Ok(ids) =
self.store.check_lint_name(&new_name, None, self.registered_tools)
{
- let src = LintLevelSource::Node(Symbol::intern(&new_name), sp, reason);
+ let src = LintLevelSource::Node {
+ name: Symbol::intern(&new_name),
+ span: sp,
+ reason,
+ };
for &id in ids {
if self.check_gated_lint(id, attr.span) {
self.insert_spec(id, (level, src));
}
}
if let Level::Expect(expect_id) = level {
- self.lint_expectations.push((
+ self.provider.push_expectation(
expect_id,
LintExpectation::new(reason, sp, false, tool_name),
- ));
+ );
}
} else {
panic!("renamed lint does not exist: {}", new_name);
@@ -595,219 +1006,87 @@ impl<'s> LintLevelsBuilder<'s> {
continue;
}
- let LintLevelSource::Node(lint_attr_name, lint_attr_span, _) = *src else {
+ let LintLevelSource::Node { name: lint_attr_name, span: lint_attr_span, .. } = *src else {
continue
};
let lint = builtin::UNUSED_ATTRIBUTES;
- let (lint_level, lint_src) =
- self.sets.get_lint_level(lint, self.cur, Some(self.current_specs()), self.sess);
+ let (lint_level, lint_src) = self.provider.get_lint_level(lint, &self.sess);
struct_lint_level(
self.sess,
lint,
lint_level,
lint_src,
Some(lint_attr_span.into()),
- |lint| {
- let mut db = lint.build(&format!(
- "{}({}) is ignored unless specified at crate level",
- level.as_str(),
- lint_attr_name
- ));
- db.emit();
- },
+ format!(
+ "{}({}) is ignored unless specified at crate level",
+ level.as_str(),
+ lint_attr_name
+ ),
+ |lint| lint,
);
// don't set a separate error for every lint in the group
break;
}
}
-
- if self.current_specs().is_empty() {
- self.sets.list.pop();
- self.cur = prev;
- }
-
- BuilderPush { prev, changed: prev != self.cur }
- }
-
- fn create_stable_id(
- &mut self,
- unstable_id: LintExpectationId,
- hir_id: HirId,
- attr_index: usize,
- ) -> LintExpectationId {
- let stable_id =
- LintExpectationId::Stable { hir_id, attr_index: attr_index as u16, lint_index: None };
-
- self.expectation_id_map.insert(unstable_id, stable_id);
-
- stable_id
}
/// Checks if the lint is gated on a feature that is not enabled.
///
/// Returns `true` if the lint's feature is enabled.
+ // FIXME only emit this once for each attribute, instead of repeating it 4 times for
+ // pre-expansion lints, post-expansion lints, `shallow_lint_levels_on` and `lint_expectations`.
fn check_gated_lint(&self, lint_id: LintId, span: Span) -> bool {
if let Some(feature) = lint_id.lint.feature_gate {
if !self.sess.features_untracked().enabled(feature) {
let lint = builtin::UNKNOWN_LINTS;
let (level, src) = self.lint_level(builtin::UNKNOWN_LINTS);
- struct_lint_level(self.sess, lint, level, src, Some(span.into()), |lint_db| {
- let mut db =
- lint_db.build(&format!("unknown lint: `{}`", lint_id.lint.name_lower()));
- db.note(&format!("the `{}` lint is unstable", lint_id.lint.name_lower(),));
- add_feature_diagnostics(&mut db, &self.sess.parse_sess, feature);
- db.emit();
- });
+ struct_lint_level(
+ self.sess,
+ lint,
+ level,
+ src,
+ Some(span.into()),
+ format!("unknown lint: `{}`", lint_id.lint.name_lower()),
+ |lint| {
+ lint.note(
+ &format!("the `{}` lint is unstable", lint_id.lint.name_lower(),),
+ );
+ add_feature_diagnostics(lint, &self.sess.parse_sess, feature);
+ lint
+ },
+ );
return false;
}
}
true
}
- /// Called after `push` when the scope of a set of attributes are exited.
- pub fn pop(&mut self, push: BuilderPush) {
- self.cur = push.prev;
- }
-
/// Find the lint level for a lint.
- pub fn lint_level(&self, lint: &'static Lint) -> (Level, LintLevelSource) {
- self.sets.get_lint_level(lint, self.cur, None, self.sess)
+ pub fn lint_level(&self, lint: &'static Lint) -> LevelAndSource {
+ self.provider.get_lint_level(lint, self.sess)
}
/// Used to emit a lint-related diagnostic based on the current state of
/// this lint context.
- pub fn struct_lint(
+ ///
+ /// 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
+ pub(crate) fn struct_lint(
&self,
lint: &'static Lint,
span: Option<MultiSpan>,
- decorate: impl for<'a> FnOnce(LintDiagnosticBuilder<'a, ()>),
+ msg: impl Into<DiagnosticMessage>,
+ decorate: impl for<'a, 'b> FnOnce(
+ &'b mut DiagnosticBuilder<'a, ()>,
+ ) -> &'b mut DiagnosticBuilder<'a, ()>,
) {
let (level, src) = self.lint_level(lint);
- struct_lint_level(self.sess, lint, level, src, span, decorate)
- }
-
- /// Registers the ID provided with the current set of lints stored in
- /// this context.
- pub fn register_id(&mut self, id: HirId) {
- self.id_to_set.insert(id, self.cur);
- }
-
- fn update_unstable_expectation_ids(&self) {
- self.sess.diagnostic().update_unstable_expectation_id(&self.expectation_id_map);
- }
-
- pub fn build_map(self) -> LintLevelMap {
- LintLevelMap {
- sets: self.sets,
- id_to_set: self.id_to_set,
- lint_expectations: self.lint_expectations,
- }
- }
-}
-
-struct LintLevelMapBuilder<'tcx> {
- levels: LintLevelsBuilder<'tcx>,
- tcx: TyCtxt<'tcx>,
-}
-
-impl LintLevelMapBuilder<'_> {
- fn with_lint_attrs<F>(&mut self, id: hir::HirId, f: F)
- where
- F: FnOnce(&mut Self),
- {
- let is_crate_hir = id == hir::CRATE_HIR_ID;
- let attrs = self.tcx.hir().attrs(id);
- let push = self.levels.push(attrs, is_crate_hir, Some(id));
-
- if push.changed {
- self.levels.register_id(id);
- }
- f(self);
- self.levels.pop(push);
- }
-}
-
-impl<'tcx> intravisit::Visitor<'tcx> for LintLevelMapBuilder<'tcx> {
- type NestedFilter = nested_filter::All;
-
- fn nested_visit_map(&mut self) -> Self::Map {
- self.tcx.hir()
- }
-
- fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
- self.with_lint_attrs(param.hir_id, |builder| {
- intravisit::walk_param(builder, param);
- });
- }
-
- fn visit_item(&mut self, it: &'tcx hir::Item<'tcx>) {
- self.with_lint_attrs(it.hir_id(), |builder| {
- intravisit::walk_item(builder, it);
- });
- }
-
- fn visit_foreign_item(&mut self, it: &'tcx hir::ForeignItem<'tcx>) {
- self.with_lint_attrs(it.hir_id(), |builder| {
- intravisit::walk_foreign_item(builder, it);
- })
- }
-
- fn visit_stmt(&mut self, e: &'tcx hir::Stmt<'tcx>) {
- // We will call `with_lint_attrs` when we walk
- // the `StmtKind`. The outer statement itself doesn't
- // define the lint levels.
- intravisit::walk_stmt(self, e);
- }
-
- fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) {
- self.with_lint_attrs(e.hir_id, |builder| {
- intravisit::walk_expr(builder, e);
- })
- }
-
- fn visit_field_def(&mut self, s: &'tcx hir::FieldDef<'tcx>) {
- self.with_lint_attrs(s.hir_id, |builder| {
- intravisit::walk_field_def(builder, s);
- })
- }
-
- fn visit_variant(
- &mut self,
- v: &'tcx hir::Variant<'tcx>,
- g: &'tcx hir::Generics<'tcx>,
- item_id: hir::HirId,
- ) {
- self.with_lint_attrs(v.id, |builder| {
- intravisit::walk_variant(builder, v, g, item_id);
- })
- }
-
- fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>) {
- self.with_lint_attrs(l.hir_id, |builder| {
- intravisit::walk_local(builder, l);
- })
- }
-
- fn visit_arm(&mut self, a: &'tcx hir::Arm<'tcx>) {
- self.with_lint_attrs(a.hir_id, |builder| {
- intravisit::walk_arm(builder, a);
- })
- }
-
- fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) {
- self.with_lint_attrs(trait_item.hir_id(), |builder| {
- intravisit::walk_trait_item(builder, trait_item);
- });
- }
-
- fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
- self.with_lint_attrs(impl_item.hir_id(), |builder| {
- intravisit::walk_impl_item(builder, impl_item);
- });
+ struct_lint_level(self.sess, lint, level, src, span, msg, decorate)
}
}
-pub fn provide(providers: &mut Providers) {
- providers.lint_levels = lint_levels;
+pub(crate) fn provide(providers: &mut Providers) {
+ *providers = Providers { shallow_lint_levels_on, lint_expectations, ..*providers };
}
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index 389a0b5d1..5288fc542 100644
--- a/compiler/rustc_lint/src/lib.rs
+++ b/compiler/rustc_lint/src/lib.rs
@@ -34,7 +34,7 @@
#![feature(iter_intersperse)]
#![feature(iter_order_by)]
#![feature(let_chains)]
-#![feature(let_else)]
+#![feature(min_specialization)]
#![feature(never_type)]
#![recursion_limit = "256"]
@@ -42,22 +42,28 @@
extern crate rustc_middle;
#[macro_use]
extern crate rustc_session;
+#[macro_use]
+extern crate tracing;
mod array_into_iter;
pub mod builtin;
mod context;
mod early;
mod enum_intrinsics_non_enums;
+mod errors;
mod expect;
+mod for_loops_over_fallibles;
pub mod hidden_unicode_codepoints;
mod internal;
mod late;
+mod let_underscore;
mod levels;
mod methods;
mod non_ascii_idents;
mod non_fmt_panic;
mod nonstandard_style;
mod noop_method_call;
+mod opaque_hidden_inferred_bound;
mod pass_by_value;
mod passes;
mod redundant_semicolon;
@@ -81,13 +87,16 @@ use rustc_span::Span;
use array_into_iter::ArrayIntoIter;
use builtin::*;
use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums;
+use for_loops_over_fallibles::*;
use hidden_unicode_codepoints::*;
use internal::*;
+use let_underscore::*;
use methods::*;
use non_ascii_idents::*;
use non_fmt_panic::NonPanicFmt;
use nonstandard_style::*;
use noop_method_call::*;
+use opaque_hidden_inferred_bound::*;
use pass_by_value::*;
use redundant_semicolon::*;
use traits::*;
@@ -130,6 +139,7 @@ macro_rules! early_lint_passes {
UnusedBraces: UnusedBraces,
UnusedImportBraces: UnusedImportBraces,
UnsafeCode: UnsafeCode,
+ SpecialModuleName: SpecialModuleName,
AnonymousParameters: AnonymousParameters,
EllipsisInclusiveRangePatterns: EllipsisInclusiveRangePatterns::default(),
NonCamelCaseTypes: NonCamelCaseTypes,
@@ -140,6 +150,7 @@ macro_rules! early_lint_passes {
IncompleteFeatures: IncompleteFeatures,
RedundantSemicolons: RedundantSemicolons,
UnusedDocComment: UnusedDocComment,
+ UnexpectedCfgs: UnexpectedCfgs,
]
);
};
@@ -179,12 +190,14 @@ macro_rules! late_lint_mod_passes {
$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,
@@ -199,7 +212,7 @@ macro_rules! late_lint_mod_passes {
TypeLimits: TypeLimits::new(),
NonSnakeCase: NonSnakeCase,
InvalidNoMangleItems: InvalidNoMangleItems,
- // Depends on access levels
+ // Depends on effective visibilities
UnreachablePub: UnreachablePub,
ExplicitOutlivesRequirements: ExplicitOutlivesRequirements,
InvalidValue: InvalidValue,
@@ -215,6 +228,7 @@ macro_rules! late_lint_mod_passes {
EnumIntrinsicsNonEnums: EnumIntrinsicsNonEnums,
InvalidAtomicOrdering: InvalidAtomicOrdering,
NamedAsmLabels: NamedAsmLabels,
+ OpaqueHiddenInferredBound: OpaqueHiddenInferredBound,
]
);
};
@@ -252,26 +266,41 @@ fn register_builtins(store: &mut LintStore, no_interleave_lints: bool) {
)
}
- macro_rules! register_pass {
+ 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_passes {
+ 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_pass!($method, $passes, $constructor);
+ 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_passes, register_pre_expansion_pass);
- early_lint_passes!(register_passes, register_early_pass);
- late_lint_passes!(register_passes, register_late_pass);
- late_lint_mod_passes!(register_passes, register_late_mod_pass);
+ 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());
@@ -311,6 +340,8 @@ fn register_builtins(store: &mut LintStore, no_interleave_lints: bool) {
REDUNDANT_SEMICOLONS
);
+ add_lint_group!("let_underscore", LET_UNDERSCORE_DROP, LET_UNDERSCORE_LOCK);
+
add_lint_group!(
"rust_2018_idioms",
BARE_TRAIT_OBJECTS,
@@ -494,25 +525,30 @@ fn register_builtins(store: &mut LintStore, no_interleave_lints: bool) {
"now allowed, see issue #59159 \
<https://github.com/rust-lang/rust/issues/59159> for more information",
);
+ store.register_removed(
+ "const_err",
+ "converted into hard error, see issue #71800 \
+ <https://github.com/rust-lang/rust/issues/71800> for more information",
+ );
}
fn register_internals(store: &mut LintStore) {
store.register_lints(&LintPassImpl::get_lints());
store.register_early_pass(|| Box::new(LintPassImpl));
store.register_lints(&DefaultHashTypes::get_lints());
- store.register_late_pass(|| Box::new(DefaultHashTypes));
+ store.register_late_pass(|_| Box::new(DefaultHashTypes));
store.register_lints(&QueryStability::get_lints());
- store.register_late_pass(|| Box::new(QueryStability));
+ store.register_late_pass(|_| Box::new(QueryStability));
store.register_lints(&ExistingDocKeyword::get_lints());
- store.register_late_pass(|| Box::new(ExistingDocKeyword));
+ store.register_late_pass(|_| Box::new(ExistingDocKeyword));
store.register_lints(&TyTyKind::get_lints());
- store.register_late_pass(|| Box::new(TyTyKind));
+ store.register_late_pass(|_| Box::new(TyTyKind));
store.register_lints(&Diagnostics::get_lints());
- store.register_late_pass(|| Box::new(Diagnostics));
+ store.register_late_pass(|_| Box::new(Diagnostics));
store.register_lints(&BadOptAccess::get_lints());
- store.register_late_pass(|| Box::new(BadOptAccess));
+ store.register_late_pass(|_| Box::new(BadOptAccess));
store.register_lints(&PassByValue::get_lints());
- store.register_late_pass(|| Box::new(PassByValue));
+ store.register_late_pass(|_| Box::new(PassByValue));
// FIXME(davidtwco): deliberately do not include `UNTRANSLATABLE_DIAGNOSTIC` and
// `DIAGNOSTIC_OUTSIDE_OF_IMPL` here because `-Wrustc::internal` is provided to every crate and
// these lints will trigger all of the time - change this once migration to diagnostic structs
diff --git a/compiler/rustc_lint/src/methods.rs b/compiler/rustc_lint/src/methods.rs
index ff5a01749..e2d7d5b49 100644
--- a/compiler/rustc_lint/src/methods.rs
+++ b/compiler/rustc_lint/src/methods.rs
@@ -44,9 +44,13 @@ fn in_macro(span: Span) -> bool {
fn first_method_call<'tcx>(
expr: &'tcx Expr<'tcx>,
-) -> Option<(&'tcx PathSegment<'tcx>, &'tcx [Expr<'tcx>])> {
- if let ExprKind::MethodCall(path, args, _) = &expr.kind {
- if args.iter().any(|e| e.span.from_expansion()) { None } else { Some((path, *args)) }
+) -> Option<(&'tcx PathSegment<'tcx>, &'tcx Expr<'tcx>)> {
+ if let ExprKind::MethodCall(path, receiver, args, ..) = &expr.kind {
+ if args.iter().any(|e| e.span.from_expansion()) || receiver.span.from_expansion() {
+ None
+ } else {
+ Some((path, *receiver))
+ }
} else {
None
}
@@ -59,15 +63,13 @@ impl<'tcx> LateLintPass<'tcx> for TemporaryCStringAsPtr {
}
match first_method_call(expr) {
- Some((path, args)) if path.ident.name == sym::as_ptr => {
- let unwrap_arg = &args[0];
+ Some((path, unwrap_arg)) if path.ident.name == sym::as_ptr => {
let as_ptr_span = path.ident.span;
match first_method_call(unwrap_arg) {
- Some((path, args))
+ Some((path, receiver))
if path.ident.name == sym::unwrap || path.ident.name == sym::expect =>
{
- let source_arg = &args[0];
- lint_cstring_as_ptr(cx, as_ptr_span, source_arg, unwrap_arg);
+ lint_cstring_as_ptr(cx, as_ptr_span, receiver, unwrap_arg);
}
_ => return,
}
@@ -88,14 +90,17 @@ fn lint_cstring_as_ptr(
if cx.tcx.is_diagnostic_item(sym::Result, def.did()) {
if let ty::Adt(adt, _) = substs.type_at(0).kind() {
if cx.tcx.is_diagnostic_item(sym::cstring_type, adt.did()) {
- cx.struct_span_lint(TEMPORARY_CSTRING_AS_PTR, as_ptr_span, |diag| {
- diag.build(fluent::lint::cstring_ptr)
- .span_label(as_ptr_span, fluent::lint::as_ptr_label)
- .span_label(unwrap.span, fluent::lint::unwrap_label)
- .note(fluent::lint::note)
- .help(fluent::lint::help)
- .emit();
- });
+ cx.struct_span_lint(
+ TEMPORARY_CSTRING_AS_PTR,
+ as_ptr_span,
+ fluent::lint_cstring_ptr,
+ |diag| {
+ diag.span_label(as_ptr_span, fluent::as_ptr_label)
+ .span_label(unwrap.span, fluent::unwrap_label)
+ .note(fluent::note)
+ .help(fluent::help)
+ },
+ );
}
}
}
diff --git a/compiler/rustc_lint/src/non_ascii_idents.rs b/compiler/rustc_lint/src/non_ascii_idents.rs
index 764003e61..dea9506ac 100644
--- a/compiler/rustc_lint/src/non_ascii_idents.rs
+++ b/compiler/rustc_lint/src/non_ascii_idents.rs
@@ -180,15 +180,21 @@ impl EarlyLintPass for NonAsciiIdents {
continue;
}
has_non_ascii_idents = true;
- cx.struct_span_lint(NON_ASCII_IDENTS, sp, |lint| {
- lint.build(fluent::lint::identifier_non_ascii_char).emit();
- });
+ cx.struct_span_lint(
+ NON_ASCII_IDENTS,
+ sp,
+ fluent::lint_identifier_non_ascii_char,
+ |lint| lint,
+ );
if check_uncommon_codepoints
&& !symbol_str.chars().all(GeneralSecurityProfile::identifier_allowed)
{
- cx.struct_span_lint(UNCOMMON_CODEPOINTS, sp, |lint| {
- lint.build(fluent::lint::identifier_uncommon_codepoints).emit();
- })
+ cx.struct_span_lint(
+ UNCOMMON_CODEPOINTS,
+ sp,
+ fluent::lint_identifier_uncommon_codepoints,
+ |lint| lint,
+ )
}
}
@@ -216,13 +222,16 @@ impl EarlyLintPass for NonAsciiIdents {
.entry(skeleton_sym)
.and_modify(|(existing_symbol, existing_span, existing_is_ascii)| {
if !*existing_is_ascii || !is_ascii {
- cx.struct_span_lint(CONFUSABLE_IDENTS, sp, |lint| {
- lint.build(fluent::lint::confusable_identifier_pair)
- .set_arg("existing_sym", *existing_symbol)
- .set_arg("sym", symbol)
- .span_label(*existing_span, fluent::lint::label)
- .emit();
- });
+ cx.struct_span_lint(
+ CONFUSABLE_IDENTS,
+ sp,
+ fluent::lint_confusable_identifier_pair,
+ |lint| {
+ lint.set_arg("existing_sym", *existing_symbol)
+ .set_arg("sym", symbol)
+ .span_label(*existing_span, fluent::label)
+ },
+ );
}
if *existing_is_ascii && !is_ascii {
*existing_symbol = symbol;
@@ -322,22 +331,25 @@ impl EarlyLintPass for NonAsciiIdents {
}
for ((sp, ch_list), script_set) in lint_reports {
- cx.struct_span_lint(MIXED_SCRIPT_CONFUSABLES, sp, |lint| {
- let mut includes = String::new();
- for (idx, ch) in ch_list.into_iter().enumerate() {
- if idx != 0 {
- includes += ", ";
+ cx.struct_span_lint(
+ MIXED_SCRIPT_CONFUSABLES,
+ sp,
+ fluent::lint_mixed_script_confusables,
+ |lint| {
+ let mut includes = String::new();
+ for (idx, ch) in ch_list.into_iter().enumerate() {
+ if idx != 0 {
+ includes += ", ";
+ }
+ let char_info = format!("'{}' (U+{:04X})", ch, ch as u32);
+ includes += &char_info;
}
- let char_info = format!("'{}' (U+{:04X})", ch, ch as u32);
- includes += &char_info;
- }
- lint.build(fluent::lint::mixed_script_confusables)
- .set_arg("set", script_set.to_string())
- .set_arg("includes", includes)
- .note(fluent::lint::includes_note)
- .note(fluent::lint::note)
- .emit();
- });
+ lint.set_arg("set", script_set.to_string())
+ .set_arg("includes", includes)
+ .note(fluent::includes_note)
+ .note(fluent::note)
+ },
+ );
}
}
}
diff --git a/compiler/rustc_lint/src/non_fmt_panic.rs b/compiler/rustc_lint/src/non_fmt_panic.rs
index cdad2d2e8..6ad2e0294 100644
--- a/compiler/rustc_lint/src/non_fmt_panic.rs
+++ b/compiler/rustc_lint/src/non_fmt_panic.rs
@@ -119,22 +119,20 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc
arg_span = expn.call_site;
}
- cx.struct_span_lint(NON_FMT_PANICS, arg_span, |lint| {
- let mut l = lint.build(fluent::lint::non_fmt_panic);
- l.set_arg("name", symbol);
- l.note(fluent::lint::note);
- l.note(fluent::lint::more_info_note);
+ cx.struct_span_lint(NON_FMT_PANICS, arg_span, fluent::lint_non_fmt_panic, |lint| {
+ lint.set_arg("name", symbol);
+ lint.note(fluent::note);
+ lint.note(fluent::more_info_note);
if !is_arg_inside_call(arg_span, span) {
// No clue where this argument is coming from.
- l.emit();
- return;
+ return lint;
}
if arg_macro.map_or(false, |id| cx.tcx.is_diagnostic_item(sym::format_macro, id)) {
// A case of `panic!(format!(..))`.
- l.note(fluent::lint::supports_fmt_note);
+ lint.note(fluent::supports_fmt_note);
if let Some((open, close, _)) = find_delimiters(cx, arg_span) {
- l.multipart_suggestion(
- fluent::lint::supports_fmt_suggestion,
+ lint.multipart_suggestion(
+ fluent::supports_fmt_suggestion,
vec![
(arg_span.until(open.shrink_to_hi()), "".into()),
(close.until(arg_span.shrink_to_hi()), "".into()),
@@ -153,21 +151,19 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc
Some(ty_def) if cx.tcx.is_diagnostic_item(sym::String, ty_def.did()),
);
- let (suggest_display, suggest_debug) = cx.tcx.infer_ctxt().enter(|infcx| {
- let 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);
- let debug = !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);
- (display, debug)
- });
+ 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);
+ 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);
let suggest_panic_any = !is_str && panic == sym::std_panic_macro;
@@ -180,17 +176,17 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc
};
if suggest_display {
- l.span_suggestion_verbose(
+ lint.span_suggestion_verbose(
arg_span.shrink_to_lo(),
- fluent::lint::display_suggestion,
+ fluent::display_suggestion,
"\"{}\", ",
fmt_applicability,
);
} else if suggest_debug {
- l.set_arg("ty", ty);
- l.span_suggestion_verbose(
+ lint.set_arg("ty", ty);
+ lint.span_suggestion_verbose(
arg_span.shrink_to_lo(),
- fluent::lint::debug_suggestion,
+ fluent::debug_suggestion,
"\"{:?}\", ",
fmt_applicability,
);
@@ -198,9 +194,9 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc
if suggest_panic_any {
if let Some((open, close, del)) = find_delimiters(cx, span) {
- l.set_arg("already_suggested", suggest_display || suggest_debug);
- l.multipart_suggestion(
- fluent::lint::panic_suggestion,
+ lint.set_arg("already_suggested", suggest_display || suggest_debug);
+ lint.multipart_suggestion(
+ fluent::panic_suggestion,
if del == '(' {
vec![(span.until(open), "std::panic::panic_any".into())]
} else {
@@ -214,7 +210,7 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc
}
}
}
- l.emit();
+ lint
});
}
@@ -258,25 +254,24 @@ fn check_panic_str<'tcx>(
.map(|span| fmt_span.from_inner(InnerSpan::new(span.start, span.end)))
.collect(),
};
- cx.struct_span_lint(NON_FMT_PANICS, arg_spans, |lint| {
- let mut l = lint.build(fluent::lint::non_fmt_panic_unused);
- l.set_arg("count", n_arguments);
- l.note(fluent::lint::note);
+ cx.struct_span_lint(NON_FMT_PANICS, arg_spans, fluent::lint_non_fmt_panic_unused, |lint| {
+ lint.set_arg("count", n_arguments);
+ lint.note(fluent::note);
if is_arg_inside_call(arg.span, span) {
- l.span_suggestion(
+ lint.span_suggestion(
arg.span.shrink_to_hi(),
- fluent::lint::add_args_suggestion,
+ fluent::add_args_suggestion,
", ...",
Applicability::HasPlaceholders,
);
- l.span_suggestion(
+ lint.span_suggestion(
arg.span.shrink_to_lo(),
- fluent::lint::add_fmt_suggestion,
+ fluent::add_fmt_suggestion,
"\"{}\", ",
Applicability::MachineApplicable,
);
}
- l.emit();
+ lint
});
} else {
let brace_spans: Option<Vec<_>> =
@@ -287,20 +282,24 @@ fn check_panic_str<'tcx>(
.collect()
});
let count = brace_spans.as_ref().map(|v| v.len()).unwrap_or(/* any number >1 */ 2);
- cx.struct_span_lint(NON_FMT_PANICS, brace_spans.unwrap_or_else(|| vec![span]), |lint| {
- let mut l = lint.build(fluent::lint::non_fmt_panic_braces);
- l.set_arg("count", count);
- l.note(fluent::lint::note);
- if is_arg_inside_call(arg.span, span) {
- l.span_suggestion(
- arg.span.shrink_to_lo(),
- fluent::lint::suggestion,
- "\"{}\", ",
- Applicability::MachineApplicable,
- );
- }
- l.emit();
- });
+ cx.struct_span_lint(
+ NON_FMT_PANICS,
+ brace_spans.unwrap_or_else(|| vec![span]),
+ fluent::lint_non_fmt_panic_braces,
+ |lint| {
+ lint.set_arg("count", count);
+ lint.note(fluent::note);
+ if is_arg_inside_call(arg.span, span) {
+ lint.span_suggestion(
+ arg.span.shrink_to_lo(),
+ fluent::suggestion,
+ "\"{}\", ",
+ Applicability::MachineApplicable,
+ );
+ }
+ lint
+ },
+ );
}
}
diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs
index 8d04d68bf..7e50801f8 100644
--- a/compiler/rustc_lint/src/nonstandard_style.rs
+++ b/compiler/rustc_lint/src/nonstandard_style.rs
@@ -136,26 +136,30 @@ impl NonCamelCaseTypes {
let name = ident.name.as_str();
if !is_camel_case(name) {
- cx.struct_span_lint(NON_CAMEL_CASE_TYPES, ident.span, |lint| {
- let mut err = lint.build(fluent::lint::non_camel_case_type);
- let cc = to_camel_case(name);
- // We cannot provide meaningful suggestions
- // if the characters are in the category of "Lowercase Letter".
- if *name != cc {
- err.span_suggestion(
- ident.span,
- fluent::lint::suggestion,
- to_camel_case(name),
- Applicability::MaybeIncorrect,
- );
- } else {
- err.span_label(ident.span, fluent::lint::label);
- }
+ cx.struct_span_lint(
+ NON_CAMEL_CASE_TYPES,
+ ident.span,
+ fluent::lint_non_camel_case_type,
+ |lint| {
+ let cc = to_camel_case(name);
+ // We cannot provide meaningful suggestions
+ // if the characters are in the category of "Lowercase Letter".
+ if *name != cc {
+ lint.span_suggestion(
+ ident.span,
+ fluent::suggestion,
+ to_camel_case(name),
+ Applicability::MaybeIncorrect,
+ );
+ } else {
+ lint.span_label(ident.span, fluent::label);
+ }
- err.set_arg("sort", sort);
- err.set_arg("name", name);
- err.emit();
- })
+ lint.set_arg("sort", sort);
+ lint.set_arg("name", name);
+ lint
+ },
+ )
}
}
}
@@ -183,7 +187,7 @@ impl EarlyLintPass for NonCamelCaseTypes {
}
fn check_trait_item(&mut self, cx: &EarlyContext<'_>, it: &ast::AssocItem) {
- if let ast::AssocItemKind::TyAlias(..) = it.kind {
+ if let ast::AssocItemKind::Type(..) = it.kind {
self.check_case(cx, "associated type", &it.ident);
}
}
@@ -280,9 +284,8 @@ impl NonSnakeCase {
let name = ident.name.as_str();
if !is_snake_case(name) {
- cx.struct_span_lint(NON_SNAKE_CASE, ident.span, |lint| {
+ cx.struct_span_lint(NON_SNAKE_CASE, ident.span, fluent::lint_non_snake_case, |lint| {
let sc = NonSnakeCase::to_snake_case(name);
- let mut err = lint.build(fluent::lint::non_snake_case);
// We cannot provide meaningful suggestions
// if the characters are in the category of "Uppercase Letter".
if name != sc {
@@ -295,45 +298,39 @@ impl NonSnakeCase {
// Instead, recommend renaming the identifier entirely or, if permitted,
// escaping it to create a raw identifier.
if sc_ident.name.can_be_raw() {
- (fluent::lint::rename_or_convert_suggestion, sc_ident.to_string())
+ (fluent::rename_or_convert_suggestion, sc_ident.to_string())
} else {
- err.note(fluent::lint::cannot_convert_note);
- (fluent::lint::rename_suggestion, String::new())
+ lint.note(fluent::cannot_convert_note);
+ (fluent::rename_suggestion, String::new())
}
} else {
- (fluent::lint::convert_suggestion, sc.clone())
+ (fluent::convert_suggestion, sc.clone())
};
- err.span_suggestion(
+ lint.span_suggestion(
ident.span,
message,
suggestion,
Applicability::MaybeIncorrect,
);
} else {
- err.help(fluent::lint::help);
+ lint.help(fluent::help);
}
} else {
- err.span_label(ident.span, fluent::lint::label);
+ lint.span_label(ident.span, fluent::label);
}
- err.set_arg("sort", sort);
- err.set_arg("name", name);
- err.set_arg("sc", sc);
- err.emit();
+ lint.set_arg("sort", sort);
+ lint.set_arg("name", name);
+ lint.set_arg("sc", sc);
+ lint
});
}
}
}
impl<'tcx> LateLintPass<'tcx> for NonSnakeCase {
- fn check_mod(
- &mut self,
- cx: &LateContext<'_>,
- _: &'tcx hir::Mod<'tcx>,
- _: Span,
- id: hir::HirId,
- ) {
+ fn check_mod(&mut self, cx: &LateContext<'_>, _: &'tcx hir::Mod<'tcx>, id: hir::HirId) {
if id != hir::CRATE_HIR_ID {
return;
}
@@ -437,19 +434,14 @@ impl<'tcx> LateLintPass<'tcx> for NonSnakeCase {
fn check_pat(&mut self, cx: &LateContext<'_>, p: &hir::Pat<'_>) {
if let PatKind::Binding(_, hid, ident, _) = p.kind {
- if let hir::Node::Pat(parent_pat) = cx.tcx.hir().get(cx.tcx.hir().get_parent_node(hid))
+ if let hir::Node::PatField(field) = cx.tcx.hir().get(cx.tcx.hir().get_parent_node(hid))
{
- if let PatKind::Struct(_, field_pats, _) = &parent_pat.kind {
- if field_pats
- .iter()
- .any(|field| !field.is_shorthand && field.pat.hir_id == p.hir_id)
- {
- // Only check if a new name has been introduced, to avoid warning
- // on both the struct definition and this pattern.
- self.check_snake_case(cx, "variable", &ident);
- }
- return;
+ if !field.is_shorthand {
+ // Only check if a new name has been introduced, to avoid warning
+ // on both the struct definition and this pattern.
+ self.check_snake_case(cx, "variable", &ident);
}
+ return;
}
self.check_snake_case(cx, "variable", &ident);
}
@@ -489,26 +481,30 @@ impl NonUpperCaseGlobals {
fn check_upper_case(cx: &LateContext<'_>, sort: &str, ident: &Ident) {
let name = ident.name.as_str();
if name.chars().any(|c| c.is_lowercase()) {
- cx.struct_span_lint(NON_UPPER_CASE_GLOBALS, ident.span, |lint| {
- let uc = NonSnakeCase::to_snake_case(&name).to_uppercase();
- let mut err = lint.build(fluent::lint::non_upper_case_global);
- // We cannot provide meaningful suggestions
- // if the characters are in the category of "Lowercase Letter".
- if *name != uc {
- err.span_suggestion(
- ident.span,
- fluent::lint::suggestion,
- uc,
- Applicability::MaybeIncorrect,
- );
- } else {
- err.span_label(ident.span, fluent::lint::label);
- }
+ cx.struct_span_lint(
+ NON_UPPER_CASE_GLOBALS,
+ ident.span,
+ fluent::lint_non_upper_case_global,
+ |lint| {
+ let uc = NonSnakeCase::to_snake_case(&name).to_uppercase();
+ // We cannot provide meaningful suggestions
+ // if the characters are in the category of "Lowercase Letter".
+ if *name != uc {
+ lint.span_suggestion(
+ ident.span,
+ fluent::suggestion,
+ uc,
+ Applicability::MaybeIncorrect,
+ );
+ } else {
+ lint.span_label(ident.span, fluent::label);
+ }
- err.set_arg("sort", sort);
- err.set_arg("name", name);
- err.emit();
- })
+ lint.set_arg("sort", sort);
+ lint.set_arg("name", name);
+ lint
+ },
+ )
}
}
}
diff --git a/compiler/rustc_lint/src/noop_method_call.rs b/compiler/rustc_lint/src/noop_method_call.rs
index 11a752ff0..2ef425a10 100644
--- a/compiler/rustc_lint/src/noop_method_call.rs
+++ b/compiler/rustc_lint/src/noop_method_call.rs
@@ -1,5 +1,4 @@
use crate::context::LintContext;
-use crate::rustc_middle::ty::TypeVisitable;
use crate::LateContext;
use crate::LateLintPass;
use rustc_errors::fluent;
@@ -41,12 +40,12 @@ declare_lint_pass!(NoopMethodCall => [NOOP_METHOD_CALL]);
impl<'tcx> LateLintPass<'tcx> for NoopMethodCall {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
// We only care about method calls.
- let ExprKind::MethodCall(call, elements, _) = &expr.kind else {
+ let ExprKind::MethodCall(call, receiver, ..) = &expr.kind else {
return
};
// We only care about method calls corresponding to the `Clone`, `Deref` and `Borrow`
// traits and ignore any other method call.
- let (trait_id, did) = match cx.typeck_results().type_dependent_def(expr.hir_id) {
+ let did = match cx.typeck_results().type_dependent_def(expr.hir_id) {
// Verify we are dealing with a method/associated function.
Some((DefKind::AssocFn, did)) => match cx.tcx.trait_of_item(did) {
// Check that we're dealing with a trait method for one of the traits we care about.
@@ -56,21 +55,17 @@ impl<'tcx> LateLintPass<'tcx> for NoopMethodCall {
Some(sym::Borrow | sym::Clone | sym::Deref)
) =>
{
- (trait_id, did)
+ did
}
_ => return,
},
_ => return,
};
- let substs = cx.typeck_results().node_substs(expr.hir_id);
- if substs.needs_subst() {
- // We can't resolve on types that require monomorphization, so we don't handle them if
- // we need to perform substitution.
- return;
- }
- let param_env = cx.tcx.param_env(trait_id);
+ let substs = cx
+ .tcx
+ .normalize_erasing_regions(cx.param_env, cx.typeck_results().node_substs(expr.hir_id));
// Resolve the trait method instance.
- let Ok(Some(i)) = ty::Instance::resolve(cx.tcx, param_env, did, substs) else {
+ let Ok(Some(i)) = ty::Instance::resolve(cx.tcx, cx.param_env, did, substs) else {
return
};
// (Re)check that it implements the noop diagnostic.
@@ -81,7 +76,6 @@ impl<'tcx> LateLintPass<'tcx> for NoopMethodCall {
) {
return;
}
- let receiver = &elements[0];
let receiver_ty = cx.typeck_results().expr_ty(receiver);
let expr_ty = cx.typeck_results().expr_ty_adjusted(expr);
if receiver_ty != expr_ty {
@@ -91,13 +85,11 @@ impl<'tcx> LateLintPass<'tcx> for NoopMethodCall {
}
let expr_span = expr.span;
let span = expr_span.with_lo(receiver.span.hi());
- cx.struct_span_lint(NOOP_METHOD_CALL, span, |lint| {
- lint.build(fluent::lint::noop_method_call)
- .set_arg("method", call.ident.name)
+ cx.struct_span_lint(NOOP_METHOD_CALL, span, fluent::lint_noop_method_call, |lint| {
+ lint.set_arg("method", call.ident.name)
.set_arg("receiver_ty", receiver_ty)
- .span_label(span, fluent::lint::label)
- .note(fluent::lint::note)
- .emit();
+ .span_label(span, fluent::label)
+ .note(fluent::note)
});
}
}
diff --git a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs
new file mode 100644
index 000000000..00bf287ba
--- /dev/null
+++ b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs
@@ -0,0 +1,163 @@
+use rustc_hir as hir;
+use rustc_infer::infer::TyCtxtInferExt;
+use rustc_macros::{LintDiagnostic, Subdiagnostic};
+use rustc_middle::ty::{
+ self, fold::BottomUpFolder, print::TraitPredPrintModifiersAndPath, Ty, TypeFoldable,
+};
+use rustc_span::Span;
+use rustc_trait_selection::traits;
+use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
+
+use crate::{LateContext, LateLintPass, LintContext};
+
+declare_lint! {
+ /// The `opaque_hidden_inferred_bound` lint detects cases in which nested
+ /// `impl Trait` in associated type bounds are not written generally enough
+ /// to satisfy the bounds of the associated type.
+ ///
+ /// ### Explanation
+ ///
+ /// This functionality was removed in #97346, but then rolled back in #99860
+ /// because it caused regressions.
+ ///
+ /// We plan on reintroducing this as a hard error, but in the mean time,
+ /// this lint serves to warn and suggest fixes for any use-cases which rely
+ /// on this behavior.
+ ///
+ /// ### Example
+ ///
+ /// ```
+ /// trait Trait {
+ /// type Assoc: Send;
+ /// }
+ ///
+ /// struct Struct;
+ ///
+ /// impl Trait for Struct {
+ /// type Assoc = i32;
+ /// }
+ ///
+ /// fn test() -> impl Trait<Assoc = impl Sized> {
+ /// Struct
+ /// }
+ /// ```
+ ///
+ /// {{produces}}
+ ///
+ /// In this example, `test` declares that the associated type `Assoc` for
+ /// `impl Trait` is `impl Sized`, which does not satisfy the `Send` bound
+ /// on the associated type.
+ ///
+ /// Although the hidden type, `i32` does satisfy this bound, we do not
+ /// consider the return type to be well-formed with this lint. It can be
+ /// fixed by changing `impl Sized` into `impl Sized + Send`.
+ pub OPAQUE_HIDDEN_INFERRED_BOUND,
+ Warn,
+ "detects the use of nested `impl Trait` types in associated type bounds that are not general enough"
+}
+
+declare_lint_pass!(OpaqueHiddenInferredBound => [OPAQUE_HIDDEN_INFERRED_BOUND]);
+
+impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound {
+ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
+ let hir::ItemKind::OpaqueTy(_) = &item.kind else { return; };
+ let def_id = item.owner_id.def_id.to_def_id();
+ let infcx = &cx.tcx.infer_ctxt().build();
+ // For every projection predicate in the opaque type's explicit bounds,
+ // check that the type that we're assigning actually satisfies the bounds
+ // of the associated type.
+ for &(pred, pred_span) in cx.tcx.explicit_item_bounds(def_id) {
+ // 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 {
+ continue;
+ };
+ // Only check types, since those are the only things that may
+ // have opaques in them anyways.
+ let Some(proj_term) = proj.term.ty() else { continue };
+
+ let proj_ty =
+ cx.tcx.mk_projection(proj.projection_ty.item_def_id, proj.projection_ty.substs);
+ // For every instance of the projection type in the bounds,
+ // replace them with the term we're assigning to the associated
+ // type in our opaque type.
+ let proj_replacer = &mut BottomUpFolder {
+ tcx: cx.tcx,
+ ty_op: |ty| if ty == proj_ty { proj_term } else { ty },
+ lt_op: |lt| lt,
+ ct_op: |ct| ct,
+ };
+ // For example, in `impl Trait<Assoc = impl Send>`, for all of the bounds on `Assoc`,
+ // e.g. `type Assoc: OtherTrait`, replace `<impl Trait as Trait>::Assoc: OtherTrait`
+ // with `impl Send: OtherTrait`.
+ for (assoc_pred, assoc_pred_span) in cx
+ .tcx
+ .bound_explicit_item_bounds(proj.projection_ty.item_def_id)
+ .subst_iter_copied(cx.tcx, &proj.projection_ty.substs)
+ {
+ let assoc_pred = assoc_pred.fold_with(proj_replacer);
+ let Ok(assoc_pred) = traits::fully_normalize(infcx, traits::ObligationCause::dummy(), cx.param_env, assoc_pred) else {
+ continue;
+ };
+ // If that predicate doesn't hold modulo regions (but passed during type-check),
+ // 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(
+ traits::ObligationCause::dummy(),
+ cx.param_env,
+ assoc_pred,
+ )) {
+ // 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(),
+ })
+ }
+ _ => None,
+ };
+ cx.emit_spanned_lint(
+ OPAQUE_HIDDEN_INFERRED_BOUND,
+ pred_span,
+ OpaqueHiddenInferredBoundLint {
+ ty: cx.tcx.mk_opaque(
+ def_id,
+ ty::InternalSubsts::identity_for_item(cx.tcx, def_id),
+ ),
+ proj_ty: proj_term,
+ assoc_pred_span,
+ add_bound,
+ },
+ );
+ }
+ }
+ }
+ }
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_opaque_hidden_inferred_bound)]
+struct OpaqueHiddenInferredBoundLint<'tcx> {
+ ty: Ty<'tcx>,
+ proj_ty: Ty<'tcx>,
+ #[label(specifically)]
+ assoc_pred_span: Span,
+ #[subdiagnostic]
+ add_bound: Option<AddBound<'tcx>>,
+}
+
+#[derive(Subdiagnostic)]
+#[suggestion_verbose(
+ lint_opaque_hidden_inferred_bound_sugg,
+ applicability = "machine-applicable",
+ code = " + {trait_ref}"
+)]
+struct AddBound<'tcx> {
+ #[primary_span]
+ suggest_span: Span,
+ #[skip_arg]
+ trait_ref: TraitPredPrintModifiersAndPath<'tcx>,
+}
diff --git a/compiler/rustc_lint/src/pass_by_value.rs b/compiler/rustc_lint/src/pass_by_value.rs
index af5e5faf1..01bface71 100644
--- a/compiler/rustc_lint/src/pass_by_value.rs
+++ b/compiler/rustc_lint/src/pass_by_value.rs
@@ -29,18 +29,20 @@ impl<'tcx> LateLintPass<'tcx> for PassByValue {
}
}
if let Some(t) = path_for_pass_by_value(cx, &inner_ty) {
- cx.struct_span_lint(PASS_BY_VALUE, ty.span, |lint| {
- lint.build(fluent::lint::pass_by_value)
- .set_arg("ty", t.clone())
- .span_suggestion(
+ cx.struct_span_lint(
+ PASS_BY_VALUE,
+ ty.span,
+ fluent::lint_pass_by_value,
+ |lint| {
+ lint.set_arg("ty", t.clone()).span_suggestion(
ty.span,
- fluent::lint::suggestion,
+ fluent::suggestion,
t,
// Changing type of function argument
Applicability::MaybeIncorrect,
)
- .emit();
- })
+ },
+ )
}
}
_ => {}
@@ -56,7 +58,7 @@ fn path_for_pass_by_value(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> Option<Stri
let path_segment = path.segments.last().unwrap();
return Some(format!("{}{}", name, gen_args(cx, path_segment)));
}
- Res::SelfTy { trait_: None, alias_to: Some((did, _)) } => {
+ Res::SelfTyAlias { alias_to: did, is_trait_impl: false, .. } => {
if let ty::Adt(adt, substs) = cx.tcx.type_of(did).kind() {
if cx.tcx.has_attr(adt.did(), sym::rustc_pass_by_value) {
return Some(cx.tcx.def_path_str_with_substs(adt.did(), substs));
diff --git a/compiler/rustc_lint/src/passes.rs b/compiler/rustc_lint/src/passes.rs
index cb7bd407e..1c6a057d1 100644
--- a/compiler/rustc_lint/src/passes.rs
+++ b/compiler/rustc_lint/src/passes.rs
@@ -16,7 +16,7 @@ macro_rules! late_lint_methods {
fn check_body_post(a: &$hir hir::Body<$hir>);
fn check_crate();
fn check_crate_post();
- fn check_mod(a: &$hir hir::Mod<$hir>, b: Span, c: hir::HirId);
+ fn check_mod(a: &$hir hir::Mod<$hir>, b: hir::HirId);
fn check_foreign_item(a: &$hir hir::ForeignItem<$hir>);
fn check_item(a: &$hir hir::Item<$hir>);
fn check_item_post(a: &$hir hir::Item<$hir>);
@@ -31,7 +31,7 @@ macro_rules! late_lint_methods {
fn check_ty(a: &$hir hir::Ty<$hir>);
fn check_generic_param(a: &$hir hir::GenericParam<$hir>);
fn check_generics(a: &$hir hir::Generics<$hir>);
- fn check_poly_trait_ref(a: &$hir hir::PolyTraitRef<$hir>, b: hir::TraitBoundModifier);
+ fn check_poly_trait_ref(a: &$hir hir::PolyTraitRef<$hir>);
fn check_fn(
a: rustc_hir::intravisit::FnKind<$hir>,
b: &$hir hir::FnDecl<$hir>,
@@ -156,14 +156,13 @@ macro_rules! early_lint_methods {
fn check_generic_arg(a: &ast::GenericArg);
fn check_generic_param(a: &ast::GenericParam);
fn check_generics(a: &ast::Generics);
- fn check_poly_trait_ref(a: &ast::PolyTraitRef,
- b: &ast::TraitBoundModifier);
+ fn check_poly_trait_ref(a: &ast::PolyTraitRef);
fn check_fn(a: rustc_ast::visit::FnKind<'_>, c: Span, d_: ast::NodeId);
fn check_trait_item(a: &ast::AssocItem);
fn check_impl_item(a: &ast::AssocItem);
fn check_variant(a: &ast::Variant);
fn check_attribute(a: &ast::Attribute);
- fn check_mac_def(a: &ast::MacroDef, b: ast::NodeId);
+ fn check_mac_def(a: &ast::MacroDef);
fn check_mac(a: &ast::MacCall);
/// Called when entering a syntax node that can have lint attributes such
@@ -244,6 +243,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 + sync::Sync + 'static>;
-pub type LateLintPassObject =
- Box<dyn for<'tcx> LateLintPass<'tcx> + sync::Send + sync::Sync + 'static>;
+pub type EarlyLintPassObject = Box<dyn EarlyLintPass + sync::Send + 'static>;
+pub type LateLintPassObject<'tcx> = Box<dyn LateLintPass<'tcx> + sync::Send + 'tcx>;
diff --git a/compiler/rustc_lint/src/redundant_semicolon.rs b/compiler/rustc_lint/src/redundant_semicolon.rs
index 26f413453..3521de7fc 100644
--- a/compiler/rustc_lint/src/redundant_semicolon.rs
+++ b/compiler/rustc_lint/src/redundant_semicolon.rs
@@ -48,11 +48,18 @@ fn maybe_lint_redundant_semis(cx: &EarlyContext<'_>, seq: &mut Option<(Span, boo
return;
}
- cx.struct_span_lint(REDUNDANT_SEMICOLONS, span, |lint| {
- lint.build(fluent::lint::redundant_semicolons)
- .set_arg("multiple", multiple)
- .span_suggestion(span, fluent::lint::suggestion, "", Applicability::MaybeIncorrect)
- .emit();
- });
+ cx.struct_span_lint(
+ REDUNDANT_SEMICOLONS,
+ span,
+ fluent::lint_redundant_semicolons,
+ |lint| {
+ lint.set_arg("multiple", multiple).span_suggestion(
+ span,
+ fluent::suggestion,
+ "",
+ Applicability::MaybeIncorrect,
+ )
+ },
+ );
}
}
diff --git a/compiler/rustc_lint/src/traits.rs b/compiler/rustc_lint/src/traits.rs
index df1587c59..f22f38aa2 100644
--- a/compiler/rustc_lint/src/traits.rs
+++ b/compiler/rustc_lint/src/traits.rs
@@ -89,7 +89,7 @@ impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
use rustc_middle::ty::PredicateKind::*;
- let predicates = cx.tcx.explicit_predicates_of(item.def_id);
+ 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 {
continue
@@ -100,15 +100,18 @@ impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints {
if trait_predicate.trait_ref.self_ty().is_impl_trait() {
continue;
}
- cx.struct_span_lint(DROP_BOUNDS, span, |lint| {
- let Some(needs_drop) = cx.tcx.get_diagnostic_item(sym::needs_drop) else {
- return
- };
- lint.build(fluent::lint::drop_trait_constraints)
- .set_arg("predicate", predicate)
- .set_arg("needs_drop", cx.tcx.def_path_str(needs_drop))
- .emit();
- });
+ let Some(needs_drop) = cx.tcx.get_diagnostic_item(sym::needs_drop) else {
+ continue;
+ };
+ cx.struct_span_lint(
+ DROP_BOUNDS,
+ span,
+ fluent::lint_drop_trait_constraints,
+ |lint| {
+ lint.set_arg("predicate", predicate)
+ .set_arg("needs_drop", cx.tcx.def_path_str(needs_drop))
+ },
+ );
}
}
}
@@ -119,14 +122,11 @@ impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints {
};
for bound in &bounds[..] {
let def_id = bound.trait_ref.trait_def_id();
- if cx.tcx.lang_items().drop_trait() == def_id {
- cx.struct_span_lint(DYN_DROP, bound.span, |lint| {
- let Some(needs_drop) = cx.tcx.get_diagnostic_item(sym::needs_drop) else {
- return
- };
- lint.build(fluent::lint::drop_glue)
- .set_arg("needs_drop", cx.tcx.def_path_str(needs_drop))
- .emit();
+ if cx.tcx.lang_items().drop_trait() == def_id
+ && let Some(needs_drop) = cx.tcx.get_diagnostic_item(sym::needs_drop)
+ {
+ cx.struct_span_lint(DYN_DROP, bound.span, fluent::lint_drop_glue, |lint| {
+ lint.set_arg("needs_drop", cx.tcx.def_path_str(needs_drop))
});
}
}
diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs
index 5c07afeb7..37caab2da 100644
--- a/compiler/rustc_lint/src/types.rs
+++ b/compiler/rustc_lint/src/types.rs
@@ -11,7 +11,7 @@ use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::{self, AdtKind, DefIdTree, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable};
use rustc_span::source_map;
use rustc_span::symbol::sym;
-use rustc_span::{Span, Symbol, DUMMY_SP};
+use rustc_span::{Span, Symbol};
use rustc_target::abi::{Abi, WrappingRange};
use rustc_target::abi::{Integer, TagEncoding, Variants};
use rustc_target::spec::abi::Abi as SpecAbi;
@@ -19,7 +19,6 @@ use rustc_target::spec::abi::Abi as SpecAbi;
use std::cmp;
use std::iter;
use std::ops::ControlFlow;
-use tracing::debug;
declare_lint! {
/// The `unused_comparisons` lint detects comparisons made useless by
@@ -117,55 +116,70 @@ impl TypeLimits {
}
}
-/// Attempts to special-case the overflowing literal lint when it occurs as a range endpoint.
-/// Returns `true` iff the lint was overridden.
+/// Attempts to special-case the overflowing literal lint when it occurs as a range endpoint (`expr..MAX+1`).
+/// Returns `true` iff the lint was emitted.
fn lint_overflowing_range_endpoint<'tcx>(
cx: &LateContext<'tcx>,
lit: &hir::Lit,
lit_val: u128,
max: u128,
expr: &'tcx hir::Expr<'tcx>,
- parent_expr: &'tcx hir::Expr<'tcx>,
ty: &str,
) -> bool {
// We only want to handle exclusive (`..`) ranges,
// which are represented as `ExprKind::Struct`.
- let mut overwritten = false;
- if let ExprKind::Struct(_, eps, _) = &parent_expr.kind {
- if eps.len() != 2 {
- return false;
- }
- // We can suggest using an inclusive range
- // (`..=`) instead only if it is the `end` that is
- // overflowing and only by 1.
- if eps[1].expr.hir_id == expr.hir_id && lit_val - 1 == max {
- cx.struct_span_lint(OVERFLOWING_LITERALS, parent_expr.span, |lint| {
- let mut err = lint.build(fluent::lint::range_endpoint_out_of_range);
- err.set_arg("ty", ty);
- if let Ok(start) = cx.sess().source_map().span_to_snippet(eps[0].span) {
- use ast::{LitIntType, LitKind};
- // We need to preserve the literal's suffix,
- // as it may determine typing information.
- let suffix = match lit.node {
- LitKind::Int(_, LitIntType::Signed(s)) => s.name_str(),
- LitKind::Int(_, LitIntType::Unsigned(s)) => s.name_str(),
- LitKind::Int(_, LitIntType::Unsuffixed) => "",
- _ => bug!(),
- };
- let suggestion = format!("{}..={}{}", start, lit_val - 1, suffix);
- err.span_suggestion(
- parent_expr.span,
- fluent::lint::suggestion,
- suggestion,
- Applicability::MachineApplicable,
- );
- err.emit();
- overwritten = true;
- }
- });
- }
+ let par_id = cx.tcx.hir().get_parent_node(expr.hir_id);
+ let Node::ExprField(field) = cx.tcx.hir().get(par_id) else { return false };
+ let field_par_id = cx.tcx.hir().get_parent_node(field.hir_id);
+ let Node::Expr(struct_expr) = cx.tcx.hir().get(field_par_id) else { return false };
+ if !is_range_literal(struct_expr) {
+ return false;
+ };
+ let ExprKind::Struct(_, eps, _) = &struct_expr.kind else { return false };
+ if eps.len() != 2 {
+ return false;
}
- overwritten
+
+ // We can suggest using an inclusive range
+ // (`..=`) instead only if it is the `end` that is
+ // overflowing and only by 1.
+ if !(eps[1].expr.hir_id == expr.hir_id && lit_val - 1 == max) {
+ return false;
+ };
+ let Ok(start) = cx.sess().source_map().span_to_snippet(eps[0].span) else { return false };
+
+ cx.struct_span_lint(
+ OVERFLOWING_LITERALS,
+ struct_expr.span,
+ fluent::lint_range_endpoint_out_of_range,
+ |lint| {
+ use ast::{LitIntType, LitKind};
+
+ lint.set_arg("ty", ty);
+
+ // We need to preserve the literal's suffix,
+ // as it may determine typing information.
+ let suffix = match lit.node {
+ LitKind::Int(_, LitIntType::Signed(s)) => s.name_str(),
+ LitKind::Int(_, LitIntType::Unsigned(s)) => s.name_str(),
+ LitKind::Int(_, LitIntType::Unsuffixed) => "",
+ _ => bug!(),
+ };
+ let suggestion = format!("{}..={}{}", start, lit_val - 1, suffix);
+ lint.span_suggestion(
+ struct_expr.span,
+ fluent::suggestion,
+ suggestion,
+ Applicability::MachineApplicable,
+ );
+
+ lint
+ },
+ );
+
+ // We've just emitted a lint, special cased for `(...)..MAX+1` ranges,
+ // return `true` so the callers don't also emit a lint
+ true
}
// For `isize` & `usize`, be conservative with the warnings, so that the
@@ -216,52 +230,58 @@ fn report_bin_hex_error(
negative: bool,
) {
let size = Integer::from_attr(&cx.tcx, ty).size();
- cx.struct_span_lint(OVERFLOWING_LITERALS, expr.span, |lint| {
- let (t, actually) = match ty {
- attr::IntType::SignedInt(t) => {
- let actually = if negative {
- -(size.sign_extend(val) as i128)
- } else {
- size.sign_extend(val) as i128
- };
- (t.name_str(), actually.to_string())
- }
- attr::IntType::UnsignedInt(t) => {
- let actually = size.truncate(val);
- (t.name_str(), actually.to_string())
- }
- };
- let mut err = lint.build(fluent::lint::overflowing_bin_hex);
- if negative {
- // If the value is negative,
- // emits a note about the value itself, apart from the literal.
- err.note(fluent::lint::negative_note);
- err.note(fluent::lint::negative_becomes_note);
- } else {
- err.note(fluent::lint::positive_note);
- }
- if let Some(sugg_ty) =
- get_type_suggestion(cx.typeck_results().node_type(expr.hir_id), val, negative)
- {
- err.set_arg("suggestion_ty", sugg_ty);
- if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') {
- let (sans_suffix, _) = repr_str.split_at(pos);
- err.span_suggestion(
- expr.span,
- fluent::lint::suggestion,
- format!("{}{}", sans_suffix, sugg_ty),
- Applicability::MachineApplicable,
- );
+ cx.struct_span_lint(
+ OVERFLOWING_LITERALS,
+ expr.span,
+ fluent::lint_overflowing_bin_hex,
+ |lint| {
+ let (t, actually) = match ty {
+ attr::IntType::SignedInt(t) => {
+ let actually = if negative {
+ -(size.sign_extend(val) as i128)
+ } else {
+ size.sign_extend(val) as i128
+ };
+ (t.name_str(), actually.to_string())
+ }
+ attr::IntType::UnsignedInt(t) => {
+ let actually = size.truncate(val);
+ (t.name_str(), actually.to_string())
+ }
+ };
+
+ if negative {
+ // If the value is negative,
+ // emits a note about the value itself, apart from the literal.
+ lint.note(fluent::negative_note);
+ lint.note(fluent::negative_becomes_note);
} else {
- err.help(fluent::lint::help);
+ lint.note(fluent::positive_note);
}
- }
- err.set_arg("ty", t);
- err.set_arg("lit", repr_str);
- err.set_arg("dec", val);
- err.set_arg("actually", actually);
- err.emit();
- });
+ if let Some(sugg_ty) =
+ get_type_suggestion(cx.typeck_results().node_type(expr.hir_id), val, negative)
+ {
+ lint.set_arg("suggestion_ty", sugg_ty);
+ if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') {
+ let (sans_suffix, _) = repr_str.split_at(pos);
+ lint.span_suggestion(
+ expr.span,
+ fluent::suggestion,
+ format!("{}{}", sans_suffix, sugg_ty),
+ Applicability::MachineApplicable,
+ );
+ } else {
+ lint.help(fluent::help);
+ }
+ }
+ lint.set_arg("ty", t)
+ .set_arg("lit", repr_str)
+ .set_arg("dec", val)
+ .set_arg("actually", actually);
+
+ lint
+ },
+ );
}
// This function finds the next fitting type and generates a suggestion string.
@@ -339,38 +359,32 @@ fn lint_int_literal<'tcx>(
return;
}
- let par_id = cx.tcx.hir().get_parent_node(e.hir_id);
- if let Node::Expr(par_e) = cx.tcx.hir().get(par_id) {
- if let hir::ExprKind::Struct(..) = par_e.kind {
- if is_range_literal(par_e)
- && lint_overflowing_range_endpoint(cx, lit, v, max, e, par_e, t.name_str())
- {
- // The overflowing literal lint was overridden.
- return;
- }
- }
+ if lint_overflowing_range_endpoint(cx, lit, v, max, e, t.name_str()) {
+ // The overflowing literal lint was emited by `lint_overflowing_range_endpoint`.
+ return;
}
- cx.struct_span_lint(OVERFLOWING_LITERALS, e.span, |lint| {
- let mut err = lint.build(fluent::lint::overflowing_int);
- err.set_arg("ty", t.name_str());
- err.set_arg(
- "lit",
- cx.sess()
- .source_map()
- .span_to_snippet(lit.span)
- .expect("must get snippet from literal"),
- );
- err.set_arg("min", min);
- err.set_arg("max", max);
- err.note(fluent::lint::note);
+ cx.struct_span_lint(OVERFLOWING_LITERALS, e.span, fluent::lint_overflowing_int, |lint| {
+ lint.set_arg("ty", t.name_str())
+ .set_arg(
+ "lit",
+ cx.sess()
+ .source_map()
+ .span_to_snippet(lit.span)
+ .expect("must get snippet from literal"),
+ )
+ .set_arg("min", min)
+ .set_arg("max", max)
+ .note(fluent::note);
+
if let Some(sugg_ty) =
get_type_suggestion(cx.typeck_results().node_type(e.hir_id), v, negative)
{
- err.set_arg("suggestion_ty", sugg_ty);
- err.help(fluent::lint::help);
+ lint.set_arg("suggestion_ty", sugg_ty);
+ lint.help(fluent::help);
}
- err.emit();
+
+ lint
});
}
}
@@ -395,29 +409,29 @@ fn lint_uint_literal<'tcx>(
match par_e.kind {
hir::ExprKind::Cast(..) => {
if let ty::Char = cx.typeck_results().expr_ty(par_e).kind() {
- cx.struct_span_lint(OVERFLOWING_LITERALS, par_e.span, |lint| {
- lint.build(fluent::lint::only_cast_u8_to_char)
- .span_suggestion(
+ cx.struct_span_lint(
+ OVERFLOWING_LITERALS,
+ par_e.span,
+ fluent::lint_only_cast_u8_to_char,
+ |lint| {
+ lint.span_suggestion(
par_e.span,
- fluent::lint::suggestion,
+ fluent::suggestion,
format!("'\\u{{{:X}}}'", lit_val),
Applicability::MachineApplicable,
)
- .emit();
- });
- return;
- }
- }
- hir::ExprKind::Struct(..) if is_range_literal(par_e) => {
- let t = t.name_str();
- if lint_overflowing_range_endpoint(cx, lit, lit_val, max, e, par_e, t) {
- // The overflowing literal lint was overridden.
+ },
+ );
return;
}
}
_ => {}
}
}
+ 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`.
+ return;
+ }
if let Some(repr_str) = get_bin_hex_repr(cx, lit) {
report_bin_hex_error(
cx,
@@ -429,9 +443,8 @@ fn lint_uint_literal<'tcx>(
);
return;
}
- cx.struct_span_lint(OVERFLOWING_LITERALS, e.span, |lint| {
- lint.build(fluent::lint::overflowing_uint)
- .set_arg("ty", t.name_str())
+ cx.struct_span_lint(OVERFLOWING_LITERALS, e.span, fluent::lint_overflowing_uint, |lint| {
+ lint.set_arg("ty", t.name_str())
.set_arg(
"lit",
cx.sess()
@@ -441,8 +454,7 @@ fn lint_uint_literal<'tcx>(
)
.set_arg("min", min)
.set_arg("max", max)
- .note(fluent::lint::note)
- .emit();
+ .note(fluent::note)
});
}
}
@@ -472,19 +484,22 @@ fn lint_literal<'tcx>(
_ => bug!(),
};
if is_infinite == Ok(true) {
- cx.struct_span_lint(OVERFLOWING_LITERALS, e.span, |lint| {
- lint.build(fluent::lint::overflowing_literal)
- .set_arg("ty", t.name_str())
- .set_arg(
- "lit",
- cx.sess()
- .source_map()
- .span_to_snippet(lit.span)
- .expect("must get snippet from literal"),
- )
- .note(fluent::lint::note)
- .emit();
- });
+ cx.struct_span_lint(
+ OVERFLOWING_LITERALS,
+ e.span,
+ fluent::lint_overflowing_literal,
+ |lint| {
+ lint.set_arg("ty", t.name_str())
+ .set_arg(
+ "lit",
+ cx.sess()
+ .source_map()
+ .span_to_snippet(lit.span)
+ .expect("must get snippet from literal"),
+ )
+ .note(fluent::note)
+ },
+ );
}
}
_ => {}
@@ -502,9 +517,12 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits {
}
hir::ExprKind::Binary(binop, ref l, ref r) => {
if is_comparison(binop) && !check_limits(cx, binop, &l, &r) {
- cx.struct_span_lint(UNUSED_COMPARISONS, e.span, |lint| {
- lint.build(fluent::lint::unused_comparisons).emit();
- });
+ cx.struct_span_lint(
+ UNUSED_COMPARISONS,
+ e.span,
+ fluent::lint_unused_comparisons,
+ |lint| lint,
+ );
}
}
hir::ExprKind::Lit(ref lit) => lint_literal(cx, self, e, lit),
@@ -724,7 +742,7 @@ fn get_nullable_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'t
Some(match *ty.kind() {
ty::Adt(field_def, field_substs) => {
let inner_field_ty = {
- let first_non_zst_ty = field_def
+ let mut first_non_zst_ty = field_def
.variants()
.iter()
.filter_map(|v| transparent_newtype_field(cx.tcx, v));
@@ -734,7 +752,7 @@ fn get_nullable_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'t
"Wrong number of fields for transparent type"
);
first_non_zst_ty
- .last()
+ .next_back()
.expect("No non-zst fields in transparent type.")
.ty(tcx, field_substs)
};
@@ -824,8 +842,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
self.emit_ffi_unsafe_type_lint(
ty,
sp,
- fluent::lint::improper_ctypes_array_reason,
- Some(fluent::lint::improper_ctypes_array_help),
+ fluent::lint_improper_ctypes_array_reason,
+ Some(fluent::lint_improper_ctypes_array_help),
);
true
} else {
@@ -868,7 +886,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
} else {
// All fields are ZSTs; this means that the type should behave
// like (), which is FFI-unsafe
- FfiUnsafe { ty, reason: fluent::lint::improper_ctypes_struct_zst, help: None }
+ FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_struct_zst, help: None }
}
} else {
// We can't completely trust repr(C) markings; make sure the fields are
@@ -882,7 +900,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
FfiPhantom(..) if def.is_enum() => {
return FfiUnsafe {
ty,
- reason: fluent::lint::improper_ctypes_enum_phantomdata,
+ reason: fluent::lint_improper_ctypes_enum_phantomdata,
help: None,
};
}
@@ -913,12 +931,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
match *ty.kind() {
ty::Adt(def, substs) => {
if def.is_box() && matches!(self.mode, CItemKind::Definition) {
- if ty.boxed_ty().is_sized(tcx.at(DUMMY_SP), self.cx.param_env) {
+ if ty.boxed_ty().is_sized(tcx, self.cx.param_env) {
return FfiSafe;
} else {
return FfiUnsafe {
ty,
- reason: fluent::lint::improper_ctypes_box,
+ reason: fluent::lint_improper_ctypes_box,
help: None,
};
}
@@ -932,14 +950,14 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
return FfiUnsafe {
ty,
reason: if def.is_struct() {
- fluent::lint::improper_ctypes_struct_layout_reason
+ fluent::lint_improper_ctypes_struct_layout_reason
} else {
- fluent::lint::improper_ctypes_union_layout_reason
+ fluent::lint_improper_ctypes_union_layout_reason
},
help: if def.is_struct() {
- Some(fluent::lint::improper_ctypes_struct_layout_help)
+ Some(fluent::lint_improper_ctypes_struct_layout_help)
} else {
- Some(fluent::lint::improper_ctypes_union_layout_help)
+ Some(fluent::lint_improper_ctypes_union_layout_help)
},
};
}
@@ -950,9 +968,9 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
return FfiUnsafe {
ty,
reason: if def.is_struct() {
- fluent::lint::improper_ctypes_struct_non_exhaustive
+ fluent::lint_improper_ctypes_struct_non_exhaustive
} else {
- fluent::lint::improper_ctypes_union_non_exhaustive
+ fluent::lint_improper_ctypes_union_non_exhaustive
},
help: None,
};
@@ -962,14 +980,14 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
return FfiUnsafe {
ty,
reason: if def.is_struct() {
- fluent::lint::improper_ctypes_struct_fieldless_reason
+ fluent::lint_improper_ctypes_struct_fieldless_reason
} else {
- fluent::lint::improper_ctypes_union_fieldless_reason
+ fluent::lint_improper_ctypes_union_fieldless_reason
},
help: if def.is_struct() {
- Some(fluent::lint::improper_ctypes_struct_fieldless_help)
+ Some(fluent::lint_improper_ctypes_struct_fieldless_help)
} else {
- Some(fluent::lint::improper_ctypes_union_fieldless_help)
+ Some(fluent::lint_improper_ctypes_union_fieldless_help)
},
};
}
@@ -990,8 +1008,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
if repr_nullable_ptr(self.cx, ty, self.mode).is_none() {
return FfiUnsafe {
ty,
- reason: fluent::lint::improper_ctypes_enum_repr_reason,
- help: Some(fluent::lint::improper_ctypes_enum_repr_help),
+ reason: fluent::lint_improper_ctypes_enum_repr_reason,
+ help: Some(fluent::lint_improper_ctypes_enum_repr_help),
};
}
}
@@ -999,7 +1017,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
if def.is_variant_list_non_exhaustive() && !def.did().is_local() {
return FfiUnsafe {
ty,
- reason: fluent::lint::improper_ctypes_non_exhaustive,
+ reason: fluent::lint_improper_ctypes_non_exhaustive,
help: None,
};
}
@@ -1010,7 +1028,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
if is_non_exhaustive && !variant.def_id.is_local() {
return FfiUnsafe {
ty,
- reason: fluent::lint::improper_ctypes_non_exhaustive_variant,
+ reason: fluent::lint_improper_ctypes_non_exhaustive_variant,
help: None,
};
}
@@ -1028,12 +1046,12 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
ty::Char => FfiUnsafe {
ty,
- reason: fluent::lint::improper_ctypes_char_reason,
- help: Some(fluent::lint::improper_ctypes_char_help),
+ reason: fluent::lint_improper_ctypes_char_reason,
+ help: Some(fluent::lint_improper_ctypes_char_help),
},
ty::Int(ty::IntTy::I128) | ty::Uint(ty::UintTy::U128) => {
- FfiUnsafe { ty, reason: fluent::lint::improper_ctypes_128bit, help: None }
+ FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_128bit, help: None }
}
// Primitive types with a stable representation.
@@ -1041,30 +1059,30 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
ty::Slice(_) => FfiUnsafe {
ty,
- reason: fluent::lint::improper_ctypes_slice_reason,
- help: Some(fluent::lint::improper_ctypes_slice_help),
+ reason: fluent::lint_improper_ctypes_slice_reason,
+ help: Some(fluent::lint_improper_ctypes_slice_help),
},
ty::Dynamic(..) => {
- FfiUnsafe { ty, reason: fluent::lint::improper_ctypes_dyn, help: None }
+ FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_dyn, help: None }
}
ty::Str => FfiUnsafe {
ty,
- reason: fluent::lint::improper_ctypes_str_reason,
- help: Some(fluent::lint::improper_ctypes_str_help),
+ reason: fluent::lint_improper_ctypes_str_reason,
+ help: Some(fluent::lint_improper_ctypes_str_help),
},
ty::Tuple(..) => FfiUnsafe {
ty,
- reason: fluent::lint::improper_ctypes_tuple_reason,
- help: Some(fluent::lint::improper_ctypes_tuple_help),
+ reason: fluent::lint_improper_ctypes_tuple_reason,
+ help: Some(fluent::lint_improper_ctypes_tuple_help),
},
ty::RawPtr(ty::TypeAndMut { ty, .. }) | ty::Ref(_, ty, _)
if {
matches!(self.mode, CItemKind::Definition)
- && ty.is_sized(self.cx.tcx.at(DUMMY_SP), self.cx.param_env)
+ && ty.is_sized(self.cx.tcx, self.cx.param_env)
} =>
{
FfiSafe
@@ -1089,8 +1107,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
if self.is_internal_abi(sig.abi()) {
return FfiUnsafe {
ty,
- reason: fluent::lint::improper_ctypes_fnptr_reason,
- help: Some(fluent::lint::improper_ctypes_fnptr_help),
+ reason: fluent::lint_improper_ctypes_fnptr_reason,
+ help: Some(fluent::lint_improper_ctypes_fnptr_help),
};
}
@@ -1121,7 +1139,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
// While opaque types are checked for earlier, if a projection in a struct field
// normalizes to an opaque type, then it will reach this branch.
ty::Opaque(..) => {
- FfiUnsafe { ty, reason: fluent::lint::improper_ctypes_opaque, help: None }
+ FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_opaque, help: None }
}
// `extern "C" fn` functions can have type parameters, which may or may not be FFI-safe,
@@ -1155,25 +1173,24 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
CItemKind::Definition => IMPROPER_CTYPES_DEFINITIONS,
};
- self.cx.struct_span_lint(lint, sp, |lint| {
+ self.cx.struct_span_lint(lint, sp, fluent::lint_improper_ctypes, |lint| {
let item_description = match self.mode {
CItemKind::Declaration => "block",
CItemKind::Definition => "fn",
};
- let mut diag = lint.build(fluent::lint::improper_ctypes);
- diag.set_arg("ty", ty);
- diag.set_arg("desc", item_description);
- diag.span_label(sp, fluent::lint::label);
+ lint.set_arg("ty", ty);
+ lint.set_arg("desc", item_description);
+ lint.span_label(sp, fluent::label);
if let Some(help) = help {
- diag.help(help);
+ lint.help(help);
}
- diag.note(note);
+ lint.note(note);
if let ty::Adt(def, _) = ty.kind() {
if let Some(sp) = self.cx.tcx.hir().span_if_local(def.did()) {
- diag.span_note(sp, fluent::lint::note);
+ lint.span_note(sp, fluent::note);
}
}
- diag.emit();
+ lint
});
}
@@ -1207,7 +1224,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
}
if let Some(ty) = ty.visit_with(&mut ProhibitOpaqueTypes { cx: self.cx }).break_value() {
- self.emit_ffi_unsafe_type_lint(ty, sp, fluent::lint::improper_ctypes_opaque, None);
+ self.emit_ffi_unsafe_type_lint(ty, sp, fluent::lint_improper_ctypes_opaque, None);
true
} else {
false
@@ -1252,7 +1269,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
self.emit_ffi_unsafe_type_lint(
ty,
sp,
- fluent::lint::improper_ctypes_only_phantomdata,
+ fluent::lint_improper_ctypes_only_phantomdata,
None,
);
}
@@ -1343,7 +1360,7 @@ declare_lint_pass!(VariantSizeDifferences => [VARIANT_SIZE_DIFFERENCES]);
impl<'tcx> LateLintPass<'tcx> for VariantSizeDifferences {
fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) {
if let hir::ItemKind::Enum(ref enum_definition, _) = it.kind {
- let t = cx.tcx.type_of(it.def_id);
+ let t = cx.tcx.type_of(it.owner_id);
let ty = cx.tcx.erase_regions(t);
let Ok(layout) = cx.layout_of(ty) else { return };
let Variants::Multiple {
@@ -1386,11 +1403,8 @@ impl<'tcx> LateLintPass<'tcx> for VariantSizeDifferences {
cx.struct_span_lint(
VARIANT_SIZE_DIFFERENCES,
enum_definition.variants[largest_index].span,
- |lint| {
- lint.build(fluent::lint::variant_size_differences)
- .set_arg("largest", largest)
- .emit();
- },
+ fluent::lint_variant_size_differences,
+ |lint| lint.set_arg("largest", largest),
);
}
}
@@ -1463,7 +1477,7 @@ impl InvalidAtomicOrdering {
sym::AtomicI64,
sym::AtomicI128,
];
- if let ExprKind::MethodCall(ref method_path, args, _) = &expr.kind
+ if let ExprKind::MethodCall(ref method_path, _, args, _) = &expr.kind
&& recognized_names.contains(&method_path.ident.name)
&& let Some(m_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
&& let Some(impl_did) = cx.tcx.impl_of_method(m_def_id)
@@ -1498,25 +1512,16 @@ impl InvalidAtomicOrdering {
fn check_atomic_load_store(cx: &LateContext<'_>, expr: &Expr<'_>) {
if let Some((method, args)) = Self::inherent_atomic_method_call(cx, expr, &[sym::load, sym::store])
- && let Some((ordering_arg, invalid_ordering)) = match method {
- sym::load => Some((&args[1], sym::Release)),
- sym::store => Some((&args[2], sym::Acquire)),
+ && let Some((ordering_arg, invalid_ordering, msg)) = match method {
+ sym::load => Some((&args[0], sym::Release, fluent::lint_atomic_ordering_load)),
+ sym::store => Some((&args[1], sym::Acquire, fluent::lint_atomic_ordering_store)),
_ => None,
}
&& let Some(ordering) = Self::match_ordering(cx, ordering_arg)
&& (ordering == invalid_ordering || ordering == sym::AcqRel)
{
- cx.struct_span_lint(INVALID_ATOMIC_ORDERING, ordering_arg.span, |diag| {
- if method == sym::load {
- diag.build(fluent::lint::atomic_ordering_load)
- .help(fluent::lint::help)
- .emit()
- } else {
- debug_assert_eq!(method, sym::store);
- diag.build(fluent::lint::atomic_ordering_store)
- .help(fluent::lint::help)
- .emit();
- }
+ cx.struct_span_lint(INVALID_ATOMIC_ORDERING, ordering_arg.span, msg, |lint| {
+ lint.help(fluent::help)
});
}
}
@@ -1528,10 +1533,9 @@ impl InvalidAtomicOrdering {
&& matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::fence | sym::compiler_fence))
&& Self::match_ordering(cx, &args[0]) == Some(sym::Relaxed)
{
- cx.struct_span_lint(INVALID_ATOMIC_ORDERING, args[0].span, |diag| {
- diag.build(fluent::lint::atomic_ordering_fence)
- .help(fluent::lint::help)
- .emit();
+ cx.struct_span_lint(INVALID_ATOMIC_ORDERING, args[0].span, fluent::lint_atomic_ordering_fence, |lint| {
+ lint
+ .help(fluent::help)
});
}
}
@@ -1541,8 +1545,8 @@ impl InvalidAtomicOrdering {
else {return };
let fail_order_arg = match method {
- sym::fetch_update => &args[2],
- sym::compare_exchange | sym::compare_exchange_weak => &args[4],
+ sym::fetch_update => &args[1],
+ sym::compare_exchange | sym::compare_exchange_weak => &args[3],
_ => return,
};
@@ -1550,7 +1554,7 @@ impl InvalidAtomicOrdering {
if matches!(fail_ordering, sym::Release | sym::AcqRel) {
#[derive(LintDiagnostic)]
- #[lint(lint::atomic_ordering_invalid)]
+ #[diag(lint_atomic_ordering_invalid)]
#[help]
struct InvalidAtomicOrderingDiag {
method: Symbol,
diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs
index b6cf18291..46706e498 100644
--- a/compiler/rustc_lint/src/unused.rs
+++ b/compiler/rustc_lint/src/unused.rs
@@ -7,11 +7,12 @@ use rustc_errors::{fluent, pluralize, Applicability, MultiSpan};
use rustc_hir as hir;
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_span::symbol::Symbol;
use rustc_span::symbol::{kw, sym};
-use rustc_span::{BytePos, Span, DUMMY_SP};
+use rustc_span::{BytePos, Span};
declare_lint! {
/// The `unused_must_use` lint detects unused result of a type flagged as
@@ -154,24 +155,22 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
};
if let Some(must_use_op) = must_use_op {
- cx.struct_span_lint(UNUSED_MUST_USE, expr.span, |lint| {
- lint.build(fluent::lint::unused_op)
- .set_arg("op", must_use_op)
- .span_label(expr.span, fluent::lint::label)
+ cx.struct_span_lint(UNUSED_MUST_USE, expr.span, fluent::lint_unused_op, |lint| {
+ lint.set_arg("op", must_use_op)
+ .span_label(expr.span, fluent::label)
.span_suggestion_verbose(
expr.span.shrink_to_lo(),
- fluent::lint::suggestion,
+ fluent::suggestion,
"let _ = ",
Applicability::MachineApplicable,
)
- .emit();
});
op_warned = true;
}
if !(type_permits_lack_of_use || fn_warned || op_warned) {
- cx.struct_span_lint(UNUSED_RESULTS, s.span, |lint| {
- lint.build(fluent::lint::unused_result).set_arg("ty", ty).emit();
+ cx.struct_span_lint(UNUSED_RESULTS, s.span, fluent::lint_unused_result, |lint| {
+ lint.set_arg("ty", ty)
});
}
@@ -206,10 +205,13 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
ty::Adt(def, _) => check_must_use_def(cx, def.did(), span, descr_pre, descr_post),
ty::Opaque(def, _) => {
let mut has_emitted = false;
- for &(predicate, _) in cx.tcx.explicit_item_bounds(def) {
+ for obligation in elaborate_predicates_with_span(
+ cx.tcx,
+ cx.tcx.explicit_item_bounds(def).iter().cloned(),
+ ) {
// We only look at the `DefId`, so it is safe to skip the binder here.
if let ty::PredicateKind::Trait(ref poly_trait_predicate) =
- predicate.kind().skip_binder()
+ obligation.predicate.kind().skip_binder()
{
let def_id = poly_trait_predicate.trait_ref.def_id;
let descr_pre =
@@ -222,7 +224,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
}
has_emitted
}
- ty::Dynamic(binder, _) => {
+ ty::Dynamic(binder, _, _) => {
let mut has_emitted = false;
for predicate in binder.iter() {
if let ty::ExistentialPredicate::Trait(ref trait_ref) =
@@ -267,29 +269,35 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
}
},
ty::Closure(..) => {
- cx.struct_span_lint(UNUSED_MUST_USE, span, |lint| {
- // FIXME(davidtwco): this isn't properly translatable becauses of the
- // pre/post strings
- lint.build(fluent::lint::unused_closure)
- .set_arg("count", plural_len)
- .set_arg("pre", descr_pre)
- .set_arg("post", descr_post)
- .note(fluent::lint::note)
- .emit();
- });
+ cx.struct_span_lint(
+ UNUSED_MUST_USE,
+ span,
+ fluent::lint_unused_closure,
+ |lint| {
+ // FIXME(davidtwco): this isn't properly translatable because of the
+ // pre/post strings
+ lint.set_arg("count", plural_len)
+ .set_arg("pre", descr_pre)
+ .set_arg("post", descr_post)
+ .note(fluent::note)
+ },
+ );
true
}
ty::Generator(..) => {
- cx.struct_span_lint(UNUSED_MUST_USE, span, |lint| {
- // FIXME(davidtwco): this isn't properly translatable becauses of the
- // pre/post strings
- lint.build(fluent::lint::unused_generator)
- .set_arg("count", plural_len)
- .set_arg("pre", descr_pre)
- .set_arg("post", descr_post)
- .note(fluent::lint::note)
- .emit();
- });
+ cx.struct_span_lint(
+ UNUSED_MUST_USE,
+ span,
+ fluent::lint_unused_generator,
+ |lint| {
+ // FIXME(davidtwco): this isn't properly translatable because of the
+ // pre/post strings
+ lint.set_arg("count", plural_len)
+ .set_arg("pre", descr_pre)
+ .set_arg("post", descr_post)
+ .note(fluent::note)
+ },
+ );
true
}
_ => false,
@@ -309,18 +317,17 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
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, |lint| {
- // FIXME(davidtwco): this isn't properly translatable becauses of the pre/post
+ 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
- let mut err = lint.build(fluent::lint::unused_def);
- err.set_arg("pre", descr_pre_path);
- err.set_arg("post", descr_post_path);
- err.set_arg("def", cx.tcx.def_path_str(def_id));
+ 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() {
- err.note(note.as_str());
+ lint.note(note.as_str());
}
- err.emit();
+ lint
});
true
} else {
@@ -357,25 +364,34 @@ impl<'tcx> LateLintPass<'tcx> for PathStatements {
fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) {
if let hir::StmtKind::Semi(expr) = s.kind {
if let hir::ExprKind::Path(_) = expr.kind {
- cx.struct_span_lint(PATH_STATEMENTS, s.span, |lint| {
- let ty = cx.typeck_results().expr_ty(expr);
- if ty.needs_drop(cx.tcx, cx.param_env) {
- let mut lint = lint.build(fluent::lint::path_statement_drop);
- if let Ok(snippet) = cx.sess().source_map().span_to_snippet(expr.span) {
- lint.span_suggestion(
- s.span,
- fluent::lint::suggestion,
- format!("drop({});", snippet),
- Applicability::MachineApplicable,
- );
- } else {
- lint.span_help(s.span, fluent::lint::suggestion);
- }
- lint.emit();
- } else {
- lint.build(fluent::lint::path_statement_no_effect).emit();
- }
- });
+ let ty = cx.typeck_results().expr_ty(expr);
+ if ty.needs_drop(cx.tcx, cx.param_env) {
+ cx.struct_span_lint(
+ PATH_STATEMENTS,
+ s.span,
+ fluent::lint_path_statement_drop,
+ |lint| {
+ if let Ok(snippet) = cx.sess().source_map().span_to_snippet(expr.span) {
+ lint.span_suggestion(
+ s.span,
+ fluent::suggestion,
+ format!("drop({});", snippet),
+ Applicability::MachineApplicable,
+ );
+ } else {
+ lint.span_help(s.span, fluent::suggestion);
+ }
+ lint
+ },
+ );
+ } else {
+ cx.struct_span_lint(
+ PATH_STATEMENTS,
+ s.span,
+ fluent::lint_path_statement_no_effect,
+ |lint| lint,
+ );
+ }
}
}
}
@@ -504,23 +520,23 @@ trait UnusedDelimLint {
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 value.span.from_expansion() || start.from_expansion() || end.from_expansion() {
- (
- value.span.with_hi(value.span.lo() + BytePos(1)),
- value.span.with_lo(value.span.hi() - BytePos(1)),
- )
+ 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()),
+ ))
} else {
- (value.span.with_hi(start.lo()), value.span.with_lo(end.hi()))
+ None
}
}
ast::ExprKind::Paren(ref expr) => {
- if value.span.from_expansion() || expr.span.from_expansion() {
- (
- value.span.with_hi(value.span.lo() + BytePos(1)),
- value.span.with_lo(value.span.hi() - BytePos(1)),
- )
+ let expr_span = expr.span.find_ancestor_inside(value.span);
+ if let Some(expr_span) = expr_span {
+ Some((value.span.with_hi(expr_span.lo()), value.span.with_lo(expr_span.hi())))
} else {
- (value.span.with_hi(expr.span.lo()), value.span.with_lo(expr.span.hi()))
+ None
}
}
_ => return,
@@ -529,36 +545,37 @@ trait UnusedDelimLint {
left_pos.map_or(false, |s| s >= value.span.lo()),
right_pos.map_or(false, |s| s <= value.span.hi()),
);
- self.emit_unused_delims(cx, spans, ctx.into(), keep_space);
+ self.emit_unused_delims(cx, value.span, spans, ctx.into(), keep_space);
}
fn emit_unused_delims(
&self,
cx: &EarlyContext<'_>,
- spans: (Span, Span),
+ value_span: Span,
+ spans: Option<(Span, Span)>,
msg: &str,
keep_space: (bool, bool),
) {
- // FIXME(flip1995): Quick and dirty fix for #70814. This should be fixed in rustdoc
- // properly.
- if spans.0 == DUMMY_SP || spans.1 == DUMMY_SP {
- return;
- }
-
- cx.struct_span_lint(self.lint(), MultiSpan::from(vec![spans.0, spans.1]), |lint| {
- let replacement = vec![
- (spans.0, if keep_space.0 { " ".into() } else { "".into() }),
- (spans.1, if keep_space.1 { " ".into() } else { "".into() }),
- ];
- lint.build(fluent::lint::unused_delim)
- .set_arg("delim", Self::DELIM_STR)
- .set_arg("item", msg)
- .multipart_suggestion(
- fluent::lint::suggestion,
+ let primary_span = if let Some((lo, hi)) = spans {
+ MultiSpan::from(vec![lo, hi])
+ } else {
+ MultiSpan::from(value_span)
+ };
+ cx.struct_span_lint(self.lint(), primary_span, fluent::lint_unused_delim, |lint| {
+ 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() }),
+ ];
+ lint.multipart_suggestion(
+ fluent::suggestion,
replacement,
Applicability::MachineApplicable,
- )
- .emit();
+ );
+ }
+ lint
});
}
@@ -606,8 +623,7 @@ trait UnusedDelimLint {
ref call_or_other => {
let (args_to_check, ctx) = match *call_or_other {
Call(_, ref args) => (&args[..], UnusedDelimsCtx::FunctionArg),
- // first "argument" is self (which sometimes needs delims)
- MethodCall(_, ref args, _) => (&args[1..], UnusedDelimsCtx::MethodArg),
+ MethodCall(_, _, ref args, _) => (&args[..], UnusedDelimsCtx::MethodArg),
// actual catch-all arm
_ => {
return;
@@ -750,7 +766,7 @@ impl UnusedParens {
avoid_or: bool,
avoid_mut: bool,
) {
- use ast::{BindingMode, Mutability, PatKind};
+ use ast::{BindingAnnotation, PatKind};
if let PatKind::Paren(inner) = &value.kind {
match inner.kind {
@@ -762,19 +778,18 @@ impl UnusedParens {
// Avoid `p0 | .. | pn` if we should.
PatKind::Or(..) if avoid_or => return,
// Avoid `mut x` and `mut x @ p` if we should:
- PatKind::Ident(BindingMode::ByValue(Mutability::Mut), ..) if avoid_mut => return,
+ PatKind::Ident(BindingAnnotation::MUT, ..) if avoid_mut => {
+ return;
+ }
// Otherwise proceed with linting.
_ => {}
}
- let spans = if value.span.from_expansion() || inner.span.from_expansion() {
- (
- value.span.with_hi(value.span.lo() + BytePos(1)),
- value.span.with_lo(value.span.hi() - BytePos(1)),
- )
+ let spans = if let Some(inner) = inner.span.find_ancestor_inside(value.span) {
+ Some((value.span.with_hi(inner.lo()), value.span.with_lo(inner.hi())))
} else {
- (value.span.with_hi(inner.span.lo()), value.span.with_lo(inner.span.hi()))
+ None
};
- self.emit_unused_delims(cx, spans, "pattern", (false, false));
+ self.emit_unused_delims(cx, value.span, spans, "pattern", (false, false));
}
}
}
@@ -879,15 +894,12 @@ impl EarlyLintPass for UnusedParens {
);
}
_ => {
- let spans = if ty.span.from_expansion() || r.span.from_expansion() {
- (
- ty.span.with_hi(ty.span.lo() + BytePos(1)),
- ty.span.with_lo(ty.span.hi() - BytePos(1)),
- )
+ let spans = if let Some(r) = r.span.find_ancestor_inside(ty.span) {
+ Some((ty.span.with_hi(r.lo()), ty.span.with_lo(r.hi())))
} else {
- (ty.span.with_hi(r.span.lo()), ty.span.with_lo(r.span.hi()))
+ None
};
- self.emit_unused_delims(cx, spans, "type", (false, false));
+ self.emit_unused_delims(cx, ty.span, spans, "type", (false, false));
}
}
}
@@ -1131,9 +1143,12 @@ impl UnusedImportBraces {
ast::UseTreeKind::Nested(_) => return,
};
- cx.struct_span_lint(UNUSED_IMPORT_BRACES, item.span, |lint| {
- lint.build(fluent::lint::unused_import_braces).set_arg("node", node_name).emit();
- });
+ cx.struct_span_lint(
+ UNUSED_IMPORT_BRACES,
+ item.span,
+ fluent::lint_unused_import_braces,
+ |lint| lint.set_arg("node", node_name),
+ );
}
}
}
@@ -1182,15 +1197,17 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAllocation {
for adj in cx.typeck_results().expr_adjustments(e) {
if let adjustment::Adjust::Borrow(adjustment::AutoBorrow::Ref(_, m)) = adj.kind {
- cx.struct_span_lint(UNUSED_ALLOCATION, e.span, |lint| {
- lint.build(match m {
- adjustment::AutoBorrowMutability::Not => fluent::lint::unused_allocation,
+ cx.struct_span_lint(
+ UNUSED_ALLOCATION,
+ e.span,
+ match m {
+ adjustment::AutoBorrowMutability::Not => fluent::lint_unused_allocation,
adjustment::AutoBorrowMutability::Mut { .. } => {
- fluent::lint::unused_allocation_mut
+ fluent::lint_unused_allocation_mut
}
- })
- .emit();
- });
+ },
+ |lint| lint,
+ );
}
}
}