summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_lint
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_lint')
-rw-r--r--compiler/rustc_lint/src/array_into_iter.rs3
-rw-r--r--compiler/rustc_lint/src/builtin.rs197
-rw-r--r--compiler/rustc_lint/src/context.rs135
-rw-r--r--compiler/rustc_lint/src/early.rs30
-rw-r--r--compiler/rustc_lint/src/errors.rs162
-rw-r--r--compiler/rustc_lint/src/hidden_unicode_codepoints.rs4
-rw-r--r--compiler/rustc_lint/src/internal.rs18
-rw-r--r--compiler/rustc_lint/src/late.rs59
-rw-r--r--compiler/rustc_lint/src/let_underscore.rs175
-rw-r--r--compiler/rustc_lint/src/levels.rs132
-rw-r--r--compiler/rustc_lint/src/lib.rs55
-rw-r--r--compiler/rustc_lint/src/methods.rs18
-rw-r--r--compiler/rustc_lint/src/nonstandard_style.rs25
-rw-r--r--compiler/rustc_lint/src/noop_method_call.rs3
-rw-r--r--compiler/rustc_lint/src/passes.rs14
-rw-r--r--compiler/rustc_lint/src/types.rs115
-rw-r--r--compiler/rustc_lint/src/unused.rs102
17 files changed, 853 insertions, 394 deletions
diff --git a/compiler/rustc_lint/src/array_into_iter.rs b/compiler/rustc_lint/src/array_into_iter.rs
index 121fefdc6..b97f8acb3 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);
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index bd58021f7..0ff2ef5cd 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,
};
@@ -58,7 +59,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::*;
@@ -259,17 +259,8 @@ impl<'tcx> LateLintPass<'tcx> for NonShorthandFieldPatterns {
== 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()
- };
+ let suggested_ident =
+ format!("{}{}", binding_annot.prefix_str(), ident);
lint.build(fluent::lint::builtin_non_shorthand_field_patterns)
.set_arg("ident", ident.clone())
.span_suggestion(
@@ -1476,7 +1467,7 @@ impl TypeAliasBounds {
if TypeAliasBounds::is_type_variable_assoc(qpath) {
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)
}
}
@@ -1760,18 +1751,11 @@ 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)
@@ -1787,18 +1771,11 @@ impl EarlyLintPass for EllipsisInclusiveRangePatterns {
} 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)
@@ -2023,7 +2000,7 @@ impl KeywordIdents {
}
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 +2016,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 +2059,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,
};
@@ -2177,11 +2158,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,
@@ -2419,13 +2403,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) {
@@ -2475,6 +2459,15 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
Char if init == InitKind::Uninit => {
Some(("characters must be a valid Unicode codepoint".to_string(), None))
}
+ 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.
Adt(adt_def, substs) if !adt_def.is_union() => {
// First check if this ADT has a layout attribute (like `NonNull` and friends).
@@ -3170,3 +3163,117 @@ 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 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, |lint| {
+ lint.build("found module declaration for lib.rs")
+ .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")
+ .emit()
+ }),
+ "main" => cx.struct_span_lint(SPECIAL_MODULE_NAME, item.span, |lint| {
+ lint.build("found module declaration for main.rs")
+ .note("a binary crate cannot be used as library")
+ .emit()
+ }),
+ _ => 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>, |diag| {
+ diag.build(fluent::lint::builtin_unexpected_cli_config_name)
+ .help(fluent::lint::help)
+ .set_arg("name", name)
+ .emit();
+ });
+ }
+ }
+ 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>, |diag| {
+ diag.build(fluent::lint::builtin_unexpected_cli_config_value)
+ .help(fluent::lint::help)
+ .set_arg("name", name)
+ .set_arg("value", value)
+ .emit();
+ });
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs
index b95fc341d..7ca6ec5d9 100644
--- a/compiler/rustc_lint/src/context.rs
+++ b/compiler/rustc_lint/src/context.rs
@@ -16,12 +16,16 @@
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::add_elided_lifetime_in_path_suggestion;
use rustc_errors::{
Applicability, DecorateLint, LintDiagnosticBuilder, MultiSpan, SuggestionStyle,
};
@@ -39,14 +43,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 +68,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 +190,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 +339,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 +426,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 +519,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)
}
}
@@ -582,7 +568,7 @@ pub trait LintPassObject: Sized {}
impl LintPassObject for EarlyLintPassObject {}
-impl LintPassObject for LateLintPassObject {}
+impl LintPassObject for LateLintPassObject<'_> {}
pub trait LintContext: Sized {
type PassObject: LintPassObject;
@@ -865,9 +851,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,
@@ -968,8 +959,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 {
diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs
index d13711c3a..96ecd79a6 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),*);
@@ -45,7 +44,7 @@ impl<'a, T: EarlyLintPass> EarlyContextAndPass<'a, T> {
lint_id.lint,
Some(span),
|lint| {
- lint.build(&msg).emit();
+ lint.build(msg).emit();
},
diagnostic,
);
@@ -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);
}
diff --git a/compiler/rustc_lint/src/errors.rs b/compiler/rustc_lint/src/errors.rs
new file mode 100644
index 000000000..5c183d409
--- /dev/null
+++ b/compiler/rustc_lint/src/errors.rs
@@ -0,0 +1,162 @@
+use rustc_errors::{fluent, AddSubdiagnostic, ErrorGuaranteed, Handler};
+use rustc_macros::{SessionDiagnostic, SessionSubdiagnostic};
+use rustc_session::{lint::Level, SessionDiagnostic};
+use rustc_span::{Span, Symbol};
+
+#[derive(SessionDiagnostic)]
+#[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 AddSubdiagnostic for OverruledAttributeSub {
+ fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) {
+ 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(SessionDiagnostic)]
+#[diag(lint::malformed_attribute, code = "E0452")]
+pub struct MalformedAttribute {
+ #[primary_span]
+ pub span: Span,
+ #[subdiagnostic]
+ pub sub: MalformedAttributeSub,
+}
+
+#[derive(SessionSubdiagnostic)]
+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(SessionDiagnostic)]
+#[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(SessionDiagnostic)]
+#[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,
+}
+
+pub struct RequestedLevel {
+ pub level: Level,
+ pub lint_name: String,
+}
+
+impl AddSubdiagnostic for RequestedLevel {
+ fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) {
+ diag.note(fluent::lint::requested_level);
+ diag.set_arg(
+ "level",
+ match self.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");
+ }
+ },
+ );
+ diag.set_arg("lint_name", self.lint_name);
+ }
+}
+
+#[derive(SessionDiagnostic)]
+#[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 SessionDiagnostic<'_> 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::lint::help);
+ diag.set_arg("suggestion", suggestion);
+ }
+ diag.set_arg("lint_name", self.lint_name);
+ diag.subdiagnostic(self.sub);
+ diag
+ }
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(lint::check_name_unknown_tool, code = "E0602")]
+pub struct CheckNameUnknownTool {
+ pub tool_name: Symbol,
+ #[subdiagnostic]
+ pub sub: RequestedLevel,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(lint::check_name_warning)]
+pub struct CheckNameWarning {
+ pub msg: String,
+ #[subdiagnostic]
+ pub sub: RequestedLevel,
+}
+
+#[derive(SessionDiagnostic)]
+#[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/hidden_unicode_codepoints.rs b/compiler/rustc_lint/src/hidden_unicode_codepoints.rs
index fe2712525..8f2222132 100644
--- a/compiler/rustc_lint/src/hidden_unicode_codepoints.rs
+++ b/compiler/rustc_lint/src/hidden_unicode_codepoints.rs
@@ -120,8 +120,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..dd1fc5916 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,
@@ -52,7 +51,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)))
@@ -119,8 +118,7 @@ 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()
@@ -393,8 +391,14 @@ 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_ &&
@@ -407,7 +411,7 @@ impl LateLintPass<'_> for Diagnostics {
}
}
debug!(?found_impl);
- if !found_impl {
+ if !found_parent_with_attr && !found_impl {
cx.struct_span_lint(DIAGNOSTIC_OUTSIDE_OF_IMPL, span, |lint| {
lint.build(fluent::lint::diag_out_of_impl).emit();
})
@@ -425,7 +429,7 @@ impl LateLintPass<'_> for Diagnostics {
}
}
debug!(?found_diagnostic_message);
- if !found_diagnostic_message {
+ if !found_parent_with_attr && !found_diagnostic_message {
cx.struct_span_lint(UNTRANSLATABLE_DIAGNOSTIC, span, |lint| {
lint.build(fluent::lint::untranslatable_diag).emit();
})
diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs
index a329b3751..da6f1c5ee 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);
}
};
@@ -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[..] });
@@ -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() {
@@ -448,7 +431,7 @@ fn late_lint_crate<'tcx, T: LateLintPass<'tcx>>(tcx: TyCtxt<'tcx>, 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();
for pass in &mut passes {
tcx.sess.prof.extra_verbose_generic_activity("run_late_module_lint", pass.name()).run(
diff --git a/compiler/rustc_lint/src/let_underscore.rs b/compiler/rustc_lint/src/let_underscore.rs
new file mode 100644
index 000000000..7e885e6c5
--- /dev/null
+++ b/compiler/rustc_lint/src/let_underscore.rs
@@ -0,0 +1,175 @@
+use crate::{LateContext, LateLintPass, LintContext};
+use rustc_errors::{Applicability, LintDiagnosticBuilder, 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, |lint| {
+ build_and_emit_lint(
+ lint,
+ local,
+ init.span,
+ "non-binding let on a synchronization lock",
+ )
+ })
+ } else {
+ cx.struct_span_lint(LET_UNDERSCORE_DROP, local.span, |lint| {
+ build_and_emit_lint(
+ lint,
+ local,
+ init.span,
+ "non-binding let on a type that implements `Drop`",
+ );
+ })
+ }
+ }
+ }
+}
+
+fn build_and_emit_lint(
+ lint: LintDiagnosticBuilder<'_, ()>,
+ local: &hir::Local<'_>,
+ init_span: rustc_span::Span,
+ msg: &str,
+) {
+ lint.build(msg)
+ .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,
+ )
+ .emit();
+}
diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs
index 00e96f20d..1e16ac51e 100644
--- a/compiler/rustc_lint/src/levels.rs
+++ b/compiler/rustc_lint/src/levels.rs
@@ -3,7 +3,7 @@ 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, LintDiagnosticBuilder, MultiSpan};
use rustc_hir as hir;
use rustc_hir::{intravisit, HirId};
use rustc_middle::hir::nested_filter;
@@ -21,7 +21,11 @@ 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;
+
+use crate::errors::{
+ MalformedAttribute, MalformedAttributeSub, OverruledAttribute, OverruledAttributeSub,
+ UnknownToolInScopedLint,
+};
fn lint_levels(tcx: TyCtxt<'_>, (): ()) -> LintLevelMap {
let store = unerased_lint_store(tcx);
@@ -186,16 +190,26 @@ 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(_, forbid_source_span, reason) => {
+ OverruledAttributeSub::NodeSource {
+ span: forbid_source_span,
+ reason,
+ }
+ }
+ LintLevelSource::CommandLine(_, _) => {
+ OverruledAttributeSub::CommandLineSource
+ }
+ },
+ });
} else {
self.struct_lint(
FORBIDDEN_LINT_GROUPS,
@@ -266,7 +280,6 @@ impl<'s> LintLevelsBuilder<'s> {
self.cur = self.sets.list.push(LintSet { specs: FxHashMap::default(), parent: prev });
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(
@@ -317,20 +330,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 +368,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;
}
};
@@ -419,8 +440,10 @@ impl<'s> LintLevelsBuilder<'s> {
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((
@@ -485,22 +508,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;
}
@@ -766,20 +779,21 @@ impl<'tcx> intravisit::Visitor<'tcx> for LintLevelMapBuilder<'tcx> {
})
}
+ fn visit_expr_field(&mut self, field: &'tcx hir::ExprField<'tcx>) {
+ self.with_lint_attrs(field.hir_id, |builder| {
+ intravisit::walk_expr_field(builder, field);
+ })
+ }
+
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,
- ) {
+ fn visit_variant(&mut self, v: &'tcx hir::Variant<'tcx>) {
self.with_lint_attrs(v.id, |builder| {
- intravisit::walk_variant(builder, v, g, item_id);
+ intravisit::walk_variant(builder, v);
})
}
@@ -806,6 +820,18 @@ impl<'tcx> intravisit::Visitor<'tcx> for LintLevelMapBuilder<'tcx> {
intravisit::walk_impl_item(builder, impl_item);
});
}
+
+ fn visit_pat_field(&mut self, field: &'tcx hir::PatField<'tcx>) {
+ self.with_lint_attrs(field.hir_id, |builder| {
+ intravisit::walk_pat_field(builder, field);
+ })
+ }
+
+ fn visit_generic_param(&mut self, p: &'tcx hir::GenericParam<'tcx>) {
+ self.with_lint_attrs(p.hir_id, |builder| {
+ intravisit::walk_generic_param(builder, p);
+ });
+ }
}
pub fn provide(providers: &mut Providers) {
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index 389a0b5d1..752a751f6 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)]
+#![cfg_attr(bootstrap, feature(let_else))]
#![feature(never_type)]
#![recursion_limit = "256"]
@@ -42,16 +42,20 @@
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;
pub mod hidden_unicode_codepoints;
mod internal;
mod late;
+mod let_underscore;
mod levels;
mod methods;
mod non_ascii_idents;
@@ -83,6 +87,7 @@ use builtin::*;
use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums;
use hidden_unicode_codepoints::*;
use internal::*;
+use let_underscore::*;
use methods::*;
use non_ascii_idents::*;
use non_fmt_panic::NonPanicFmt;
@@ -130,6 +135,7 @@ macro_rules! early_lint_passes {
UnusedBraces: UnusedBraces,
UnusedImportBraces: UnusedImportBraces,
UnsafeCode: UnsafeCode,
+ SpecialModuleName: SpecialModuleName,
AnonymousParameters: AnonymousParameters,
EllipsisInclusiveRangePatterns: EllipsisInclusiveRangePatterns::default(),
NonCamelCaseTypes: NonCamelCaseTypes,
@@ -140,6 +146,7 @@ macro_rules! early_lint_passes {
IncompleteFeatures: IncompleteFeatures,
RedundantSemicolons: RedundantSemicolons,
UnusedDocComment: UnusedDocComment,
+ UnexpectedCfgs: UnexpectedCfgs,
]
);
};
@@ -185,6 +192,7 @@ macro_rules! late_lint_mod_passes {
VariantSizeDifferences: VariantSizeDifferences,
BoxPointers: BoxPointers,
PathStatements: PathStatements,
+ LetUnderscore: LetUnderscore,
// Depends on referenced function signatures in expressions
UnusedResults: UnusedResults,
NonUpperCaseGlobals: NonUpperCaseGlobals,
@@ -252,26 +260,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 +334,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,
@@ -500,19 +525,19 @@ 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..5f7f03480 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,
}
diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs
index 8d04d68bf..768ad8483 100644
--- a/compiler/rustc_lint/src/nonstandard_style.rs
+++ b/compiler/rustc_lint/src/nonstandard_style.rs
@@ -327,13 +327,7 @@ impl NonSnakeCase {
}
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 +431,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);
}
diff --git a/compiler/rustc_lint/src/noop_method_call.rs b/compiler/rustc_lint/src/noop_method_call.rs
index 11a752ff0..d1449496d 100644
--- a/compiler/rustc_lint/src/noop_method_call.rs
+++ b/compiler/rustc_lint/src/noop_method_call.rs
@@ -41,7 +41,7 @@ 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`
@@ -81,7 +81,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 {
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/types.rs b/compiler/rustc_lint/src/types.rs
index 5c07afeb7..4fb6d65a6 100644
--- a/compiler/rustc_lint/src/types.rs
+++ b/compiler/rustc_lint/src/types.rs
@@ -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
@@ -125,45 +124,51 @@ fn lint_overflowing_range_endpoint<'tcx>(
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 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;
+ }
+
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;
- }
- });
- }
+ // 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, struct_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(
+ struct_expr.span,
+ fluent::lint::suggestion,
+ suggestion,
+ Applicability::MachineApplicable,
+ );
+ err.emit();
+ overwritten = true;
+ }
+ });
}
overwritten
}
@@ -339,16 +344,9 @@ 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 overridden.
+ return;
}
cx.struct_span_lint(OVERFLOWING_LITERALS, e.span, |lint| {
@@ -408,16 +406,13 @@ fn lint_uint_literal<'tcx>(
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 overridden.
+ return;
+ }
if let Some(repr_str) = get_bin_hex_repr(cx, lit) {
report_bin_hex_error(
cx,
@@ -724,7 +719,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 +729,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)
};
@@ -1463,7 +1458,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)
@@ -1499,8 +1494,8 @@ 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)),
+ sym::load => Some((&args[0], sym::Release)),
+ sym::store => Some((&args[1], sym::Acquire)),
_ => None,
}
&& let Some(ordering) = Self::match_ordering(cx, ordering_arg)
@@ -1541,8 +1536,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 +1545,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..8c9ceb711 100644
--- a/compiler/rustc_lint/src/unused.rs
+++ b/compiler/rustc_lint/src/unused.rs
@@ -11,7 +11,7 @@ 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
@@ -222,7 +222,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) =
@@ -268,7 +268,7 @@ 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
+ // FIXME(davidtwco): this isn't properly translatable because of the
// pre/post strings
lint.build(fluent::lint::unused_closure)
.set_arg("count", plural_len)
@@ -281,7 +281,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
}
ty::Generator(..) => {
cx.struct_span_lint(UNUSED_MUST_USE, span, |lint| {
- // FIXME(davidtwco): this isn't properly translatable becauses of the
+ // FIXME(davidtwco): this isn't properly translatable because of the
// pre/post strings
lint.build(fluent::lint::unused_generator)
.set_arg("count", plural_len)
@@ -310,7 +310,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
) -> 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
+ // 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);
@@ -504,23 +504,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 +529,38 @@ 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(
+ 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, |lint| {
+ let mut db = lint.build(fluent::lint::unused_delim);
+ db.set_arg("delim", Self::DELIM_STR);
+ db.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() }),
+ ];
+ db.multipart_suggestion(
fluent::lint::suggestion,
replacement,
Applicability::MachineApplicable,
- )
- .emit();
+ );
+ }
+ db.emit();
});
}
@@ -750,7 +752,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 +764,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 +880,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));
}
}
}