summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_lint/src
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_lint/src')
-rw-r--r--compiler/rustc_lint/src/array_into_iter.rs2
-rw-r--r--compiler/rustc_lint/src/builtin.rs29
-rw-r--r--compiler/rustc_lint/src/context.rs170
-rw-r--r--compiler/rustc_lint/src/deref_into_dyn_supertrait.rs2
-rw-r--r--compiler/rustc_lint/src/early.rs1
-rw-r--r--compiler/rustc_lint/src/errors.rs52
-rw-r--r--compiler/rustc_lint/src/foreign_modules.rs7
-rw-r--r--compiler/rustc_lint/src/invalid_from_utf8.rs25
-rw-r--r--compiler/rustc_lint/src/late.rs29
-rw-r--r--compiler/rustc_lint/src/levels.rs136
-rw-r--r--compiler/rustc_lint/src/lib.rs17
-rw-r--r--compiler/rustc_lint/src/lints.rs88
-rw-r--r--compiler/rustc_lint/src/noop_method_call.rs6
-rw-r--r--compiler/rustc_lint/src/passes.rs155
-rw-r--r--compiler/rustc_lint/src/ptr_nulls.rs52
-rw-r--r--compiler/rustc_lint/src/reference_casting.rs187
-rw-r--r--compiler/rustc_lint/src/tests.rs2
-rw-r--r--compiler/rustc_lint/src/traits.rs2
-rw-r--r--compiler/rustc_lint/src/types.rs18
-rw-r--r--compiler/rustc_lint/src/unused.rs41
20 files changed, 597 insertions, 424 deletions
diff --git a/compiler/rustc_lint/src/array_into_iter.rs b/compiler/rustc_lint/src/array_into_iter.rs
index d0967ba56..814991cd8 100644
--- a/compiler/rustc_lint/src/array_into_iter.rs
+++ b/compiler/rustc_lint/src/array_into_iter.rs
@@ -34,8 +34,8 @@ declare_lint! {
Warn,
"detects calling `into_iter` on arrays in Rust 2015 and 2018",
@future_incompatible = FutureIncompatibleInfo {
- reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2021/IntoIterator-for-arrays.html>",
reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2021),
+ reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2021/IntoIterator-for-arrays.html>",
};
}
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index 4b6917fdf..536f78a73 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -41,7 +41,6 @@ use crate::{
},
EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext,
};
-use hir::IsAsync;
use rustc_ast::attr;
use rustc_ast::tokenstream::{TokenStream, TokenTree};
use rustc_ast::visit::{FnCtxt, FnKind};
@@ -845,8 +844,8 @@ declare_lint! {
Warn,
"detects anonymous parameters",
@future_incompatible = FutureIncompatibleInfo {
- reference: "issue #41686 <https://github.com/rust-lang/rust/issues/41686>",
reason: FutureIncompatibilityReason::EditionError(Edition::Edition2018),
+ reference: "issue #41686 <https://github.com/rust-lang/rust/issues/41686>",
};
}
@@ -1001,8 +1000,22 @@ impl EarlyLintPass for UnusedDocComment {
warn_if_doc(cx, arm_span, "match arms", &arm.attrs);
}
+ fn check_pat(&mut self, cx: &EarlyContext<'_>, pat: &ast::Pat) {
+ if let ast::PatKind::Struct(_, _, fields, _) = &pat.kind {
+ for field in fields {
+ warn_if_doc(cx, field.span, "pattern fields", &field.attrs);
+ }
+ }
+ }
+
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
warn_if_doc(cx, expr.span, "expressions", &expr.attrs);
+
+ if let ExprKind::Struct(s) = &expr.kind {
+ for field in &s.fields {
+ warn_if_doc(cx, field.span, "expression fields", &field.attrs);
+ }
+ }
}
fn check_generic_param(&mut self, cx: &EarlyContext<'_>, param: &ast::GenericParam) {
@@ -1280,7 +1293,7 @@ impl<'tcx> LateLintPass<'tcx> for UngatedAsyncFnTrackCaller {
span: Span,
def_id: LocalDefId,
) {
- if fn_kind.asyncness() == IsAsync::Async
+ if fn_kind.asyncness().is_async()
&& !cx.tcx.features().async_fn_track_caller
// Now, check if the function has the `#[track_caller]` attribute
&& let Some(attr) = cx.tcx.get_attr(def_id, sym::track_caller)
@@ -1441,13 +1454,13 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds {
fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
let hir::ItemKind::TyAlias(hir_ty, type_alias_generics) = &item.kind else { return };
- if cx.tcx.features().lazy_type_alias {
- // Bounds of lazy type aliases are respected.
+ // Bounds of lazy type aliases and TAITs are respected.
+ if cx.tcx.type_alias_is_lazy(item.owner_id) {
return;
}
let ty = cx.tcx.type_of(item.owner_id).skip_binder();
- if ty.has_opaque_types() || ty.has_inherent_projections() {
+ if ty.has_inherent_projections() {
// Bounds of type aliases that contain opaque types or inherent projections are respected.
// E.g: `type X = impl Trait;`, `type X = (impl Trait, Y);`, `type X = Type::Inherent;`.
return;
@@ -1656,8 +1669,8 @@ declare_lint! {
Warn,
"`...` range patterns are deprecated",
@future_incompatible = FutureIncompatibleInfo {
- reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2021/warnings-promoted-to-error.html>",
reason: FutureIncompatibilityReason::EditionError(Edition::Edition2021),
+ reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2021/warnings-promoted-to-error.html>",
};
}
@@ -1791,8 +1804,8 @@ declare_lint! {
Allow,
"detects edition keywords being used as an identifier",
@future_incompatible = FutureIncompatibleInfo {
- reference: "issue #49716 <https://github.com/rust-lang/rust/issues/49716>",
reason: FutureIncompatibilityReason::EditionError(Edition::Edition2018),
+ reference: "issue #49716 <https://github.com/rust-lang/rust/issues/49716>",
};
}
diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs
index f73797415..3c5cde430 100644
--- a/compiler/rustc_lint/src/context.rs
+++ b/compiler/rustc_lint/src/context.rs
@@ -16,10 +16,6 @@
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;
@@ -124,9 +120,10 @@ pub enum CheckLintNameResult<'a> {
NoLint(Option<Symbol>),
/// The lint refers to a tool that has not been registered.
NoTool,
- /// The lint is either renamed or removed. This is the warning
- /// message, and an optional new name (`None` if removed).
- Warning(String, Option<String>),
+ /// The lint has been renamed to a new name.
+ Renamed(String),
+ /// The lint has been removed due to the given reason.
+ Removed(String),
/// The lint is from a tool. If the Option is None, then either
/// the lint does not exist in the tool or the code was not
/// compiled with the tool and therefore the lint was never
@@ -329,51 +326,6 @@ impl LintStore {
}
}
- /// Checks the validity of lint names derived from the command line.
- pub fn check_lint_name_cmdline(
- &self,
- sess: &Session,
- lint_name: &str,
- level: Level,
- registered_tools: &RegisteredTools,
- ) {
- 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(_)) {
- sess.emit_err(UnsupportedGroup { lint_group: crate::WARNINGS.name_lower() });
- return;
- }
- 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) => {
- sess.emit_err(CheckNameUnknown {
- lint_name: lint_name.clone(),
- suggestion,
- sub: RequestedLevel { level, lint_name },
- });
- }
- CheckLintNameResult::Tool(Err((Some(_), new_name))) => {
- sess.emit_warning(CheckNameDeprecated {
- lint_name: lint_name.clone(),
- new_name,
- sub: RequestedLevel { level, lint_name },
- });
- }
- CheckLintNameResult::NoTool => {
- sess.emit_err(CheckNameUnknownTool {
- tool_name: tool_name.unwrap(),
- sub: RequestedLevel { level, lint_name },
- });
- }
- _ => {}
- };
- }
-
/// True if this symbol represents a lint group name.
pub fn is_lint_group(&self, lint_name: Symbol) -> bool {
debug!(
@@ -445,14 +397,8 @@ impl LintStore {
}
}
match self.by_name.get(&complete_name) {
- Some(Renamed(new_name, _)) => CheckLintNameResult::Warning(
- format!("lint `{complete_name}` has been renamed to `{new_name}`"),
- Some(new_name.to_owned()),
- ),
- Some(Removed(reason)) => CheckLintNameResult::Warning(
- format!("lint `{complete_name}` has been removed: {reason}"),
- None,
- ),
+ Some(Renamed(new_name, _)) => CheckLintNameResult::Renamed(new_name.to_string()),
+ Some(Removed(reason)) => CheckLintNameResult::Removed(reason.to_string()),
None => match self.lint_groups.get(&*complete_name) {
// If neither the lint, nor the lint group exists check if there is a `clippy::`
// variant of this lint
@@ -966,6 +912,14 @@ pub trait LintContext: Sized {
Applicability::MachineApplicable
);
}
+ BuiltinLintDiagnostics::AssociatedConstElidedLifetime { elided, span } => {
+ db.span_suggestion_verbose(
+ if elided { span.shrink_to_hi() } else { span },
+ "use the `'static` lifetime",
+ if elided { "'static " } else { "'static" },
+ Applicability::MachineApplicable
+ );
+ }
}
// Rewrap `db`, and pass control to the user.
decorate(db)
@@ -1361,6 +1315,91 @@ impl<'tcx> LateContext<'tcx> {
tcx.try_normalize_erasing_regions(self.param_env, proj).ok()
})
}
+
+ /// If the given expression is a local binding, find the initializer expression.
+ /// If that initializer expression is another local binding, find its initializer again.
+ ///
+ /// This process repeats as long as possible (but usually no more than once).
+ /// Type-check adjustments are not taken in account in this function.
+ ///
+ /// Examples:
+ /// ```
+ /// let abc = 1;
+ /// let def = abc + 2;
+ /// // ^^^^^^^ output
+ /// let def = def;
+ /// dbg!(def);
+ /// // ^^^ input
+ /// ```
+ pub fn expr_or_init<'a>(&self, mut expr: &'a hir::Expr<'tcx>) -> &'a hir::Expr<'tcx> {
+ expr = expr.peel_blocks();
+
+ while let hir::ExprKind::Path(ref qpath) = expr.kind
+ && let Some(parent_node) = match self.qpath_res(qpath, expr.hir_id) {
+ Res::Local(hir_id) => self.tcx.hir().find_parent(hir_id),
+ _ => None,
+ }
+ && let Some(init) = match parent_node {
+ hir::Node::Expr(expr) => Some(expr),
+ hir::Node::Local(hir::Local { init, .. }) => *init,
+ _ => None
+ }
+ {
+ expr = init.peel_blocks();
+ }
+ expr
+ }
+
+ /// If the given expression is a local binding, find the initializer expression.
+ /// If that initializer expression is another local or **outside** (`const`/`static`)
+ /// binding, find its initializer again.
+ ///
+ /// This process repeats as long as possible (but usually no more than once).
+ /// Type-check adjustments are not taken in account in this function.
+ ///
+ /// Examples:
+ /// ```
+ /// const ABC: i32 = 1;
+ /// // ^ output
+ /// let def = ABC;
+ /// dbg!(def);
+ /// // ^^^ input
+ ///
+ /// // or...
+ /// let abc = 1;
+ /// let def = abc + 2;
+ /// // ^^^^^^^ output
+ /// dbg!(def);
+ /// // ^^^ input
+ /// ```
+ pub fn expr_or_init_with_outside_body<'a>(
+ &self,
+ mut expr: &'a hir::Expr<'tcx>,
+ ) -> &'a hir::Expr<'tcx> {
+ expr = expr.peel_blocks();
+
+ while let hir::ExprKind::Path(ref qpath) = expr.kind
+ && let Some(parent_node) = match self.qpath_res(qpath, expr.hir_id) {
+ Res::Local(hir_id) => self.tcx.hir().find_parent(hir_id),
+ Res::Def(_, def_id) => self.tcx.hir().get_if_local(def_id),
+ _ => None,
+ }
+ && let Some(init) = match parent_node {
+ hir::Node::Expr(expr) => Some(expr),
+ hir::Node::Local(hir::Local { init, .. }) => *init,
+ hir::Node::Item(item) => match item.kind {
+ hir::ItemKind::Const(.., body_id) | hir::ItemKind::Static(.., body_id) => {
+ Some(self.tcx.hir().body(body_id).value)
+ }
+ _ => None
+ }
+ _ => None
+ }
+ {
+ expr = init.peel_blocks();
+ }
+ expr
+ }
}
impl<'tcx> abi::HasDataLayout for LateContext<'tcx> {
@@ -1392,14 +1431,3 @@ impl<'tcx> LayoutOfHelpers<'tcx> for LateContext<'tcx> {
err
}
}
-
-pub fn parse_lint_and_tool_name(lint_name: &str) -> (Option<Symbol>, &str) {
- match lint_name.split_once("::") {
- Some((tool_name, lint_name)) => {
- let tool_name = Symbol::intern(tool_name);
-
- (Some(tool_name), lint_name)
- }
- None => (None, lint_name),
- }
-}
diff --git a/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs b/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs
index 851c6493d..9be2edf84 100644
--- a/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs
+++ b/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs
@@ -5,6 +5,7 @@ use crate::{
use rustc_hir as hir;
use rustc_middle::{traits::util::supertraits, ty};
+use rustc_session::lint::FutureIncompatibilityReason;
use rustc_span::sym;
declare_lint! {
@@ -48,6 +49,7 @@ declare_lint! {
Warn,
"`Deref` implementation usage with a supertrait trait object for output might be shadowed in the future",
@future_incompatible = FutureIncompatibleInfo {
+ reason: FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps,
reference: "issue #89460 <https://github.com/rust-lang/rust/issues/89460>",
};
}
diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs
index 211ea8f43..d102e3a6c 100644
--- a/compiler/rustc_lint/src/early.rs
+++ b/compiler/rustc_lint/src/early.rs
@@ -228,6 +228,7 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T>
}) => self.check_id(closure_id),
_ => {}
}
+ lint_callback!(self, check_expr_post, e);
}
fn visit_generic_arg(&mut self, arg: &'a ast::GenericArg) {
diff --git a/compiler/rustc_lint/src/errors.rs b/compiler/rustc_lint/src/errors.rs
index 68167487a..eccea35c7 100644
--- a/compiler/rustc_lint/src/errors.rs
+++ b/compiler/rustc_lint/src/errors.rs
@@ -1,7 +1,5 @@
use crate::fluent_generated as fluent;
-use rustc_errors::{
- AddToDiagnostic, Diagnostic, ErrorGuaranteed, Handler, IntoDiagnostic, SubdiagnosticMessage,
-};
+use rustc_errors::{AddToDiagnostic, Diagnostic, SubdiagnosticMessage};
use rustc_macros::{Diagnostic, Subdiagnostic};
use rustc_session::lint::Level;
use rustc_span::{Span, Symbol};
@@ -91,9 +89,9 @@ pub struct BuiltinEllipsisInclusiveRangePatterns {
#[derive(Subdiagnostic)]
#[note(lint_requested_level)]
-pub struct RequestedLevel {
+pub struct RequestedLevel<'a> {
pub level: Level,
- pub lint_name: String,
+ pub lint_name: &'a str,
}
#[derive(Diagnostic)]
@@ -102,50 +100,10 @@ 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::lint_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 struct CheckNameUnknownTool<'a> {
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,
+ pub sub: RequestedLevel<'a>,
}
diff --git a/compiler/rustc_lint/src/foreign_modules.rs b/compiler/rustc_lint/src/foreign_modules.rs
index 7b291d558..e1df69bda 100644
--- a/compiler/rustc_lint/src/foreign_modules.rs
+++ b/compiler/rustc_lint/src/foreign_modules.rs
@@ -5,19 +5,18 @@ use rustc_hir::def::DefKind;
use rustc_middle::query::Providers;
use rustc_middle::ty::layout::LayoutError;
use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
-use rustc_session::lint::{lint_array, LintArray};
use rustc_span::{sym, Span, Symbol};
use rustc_target::abi::FIRST_VARIANT;
use crate::lints::{BuiltinClashingExtern, BuiltinClashingExternSub};
-use crate::types;
+use crate::{types, LintVec};
pub(crate) fn provide(providers: &mut Providers) {
*providers = Providers { clashing_extern_declarations, ..*providers };
}
-pub(crate) fn get_lints() -> LintArray {
- lint_array!(CLASHING_EXTERN_DECLARATIONS)
+pub(crate) fn get_lints() -> LintVec {
+ vec![CLASHING_EXTERN_DECLARATIONS]
}
fn clashing_extern_declarations(tcx: TyCtxt<'_>, (): ()) {
diff --git a/compiler/rustc_lint/src/invalid_from_utf8.rs b/compiler/rustc_lint/src/invalid_from_utf8.rs
index 3291286ad..e398059ad 100644
--- a/compiler/rustc_lint/src/invalid_from_utf8.rs
+++ b/compiler/rustc_lint/src/invalid_from_utf8.rs
@@ -1,6 +1,6 @@
use std::str::Utf8Error;
-use rustc_ast::{BorrowKind, LitKind};
+use rustc_ast::LitKind;
use rustc_hir::{Expr, ExprKind};
use rustc_span::source_map::Spanned;
use rustc_span::sym;
@@ -11,7 +11,7 @@ use crate::{LateContext, LateLintPass, LintContext};
declare_lint! {
/// The `invalid_from_utf8_unchecked` lint checks for calls to
/// `std::str::from_utf8_unchecked` and `std::str::from_utf8_unchecked_mut`
- /// with an invalid UTF-8 literal.
+ /// with a known invalid UTF-8 value.
///
/// ### Example
///
@@ -36,7 +36,7 @@ declare_lint! {
declare_lint! {
/// The `invalid_from_utf8` lint checks for calls to
/// `std::str::from_utf8` and `std::str::from_utf8_mut`
- /// with an invalid UTF-8 literal.
+ /// with a known invalid UTF-8 value.
///
/// ### Example
///
@@ -67,8 +67,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidFromUtf8 {
&& [sym::str_from_utf8, sym::str_from_utf8_mut,
sym::str_from_utf8_unchecked, sym::str_from_utf8_unchecked_mut].contains(&diag_item)
{
- let lint = |utf8_error: Utf8Error| {
- let label = arg.span;
+ let lint = |label, utf8_error: Utf8Error| {
let method = diag_item.as_str().strip_prefix("str_").unwrap();
let method = format!("std::str::{method}");
let valid_up_to = utf8_error.valid_up_to();
@@ -78,22 +77,26 @@ impl<'tcx> LateLintPass<'tcx> for InvalidFromUtf8 {
if is_unchecked_variant { INVALID_FROM_UTF8_UNCHECKED } else { INVALID_FROM_UTF8 },
expr.span,
if is_unchecked_variant {
- InvalidFromUtf8Diag::Unchecked { method, valid_up_to, label }
+ InvalidFromUtf8Diag::Unchecked { method, valid_up_to, label }
} else {
- InvalidFromUtf8Diag::Checked { method, valid_up_to, label }
+ InvalidFromUtf8Diag::Checked { method, valid_up_to, label }
}
)
};
- match &arg.kind {
+ let mut init = cx.expr_or_init_with_outside_body(arg);
+ while let ExprKind::AddrOf(.., inner) = init.kind {
+ init = cx.expr_or_init_with_outside_body(inner);
+ }
+ match init.kind {
ExprKind::Lit(Spanned { node: lit, .. }) => {
if let LitKind::ByteStr(bytes, _) = &lit
&& let Err(utf8_error) = std::str::from_utf8(bytes)
{
- lint(utf8_error);
+ lint(init.span, utf8_error);
}
},
- ExprKind::AddrOf(BorrowKind::Ref, _, Expr { kind: ExprKind::Array(args), .. }) => {
+ ExprKind::Array(args) => {
let elements = args.iter().map(|e|{
match &e.kind {
ExprKind::Lit(Spanned { node: lit, .. }) => match lit {
@@ -108,7 +111,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidFromUtf8 {
if let Some(elements) = elements
&& let Err(utf8_error) = std::str::from_utf8(&elements)
{
- lint(utf8_error);
+ lint(init.span, utf8_error);
}
}
_ => {}
diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs
index 73af51d9e..6c8b60c8d 100644
--- a/compiler/rustc_lint/src/late.rs
+++ b/compiler/rustc_lint/src/late.rs
@@ -21,7 +21,6 @@ use rustc_data_structures::sync::join;
use rustc_hir as hir;
use rustc_hir::def_id::{LocalDefId, LocalModDefId};
use rustc_hir::intravisit as hir_visit;
-use rustc_hir::intravisit::Visitor;
use rustc_middle::hir::nested_filter;
use rustc_middle::ty::{self, TyCtxt};
use rustc_session::lint::LintPass;
@@ -61,6 +60,9 @@ impl<'tcx, T: LateLintPass<'tcx>> LateContextAndPass<'tcx, T> {
self.context.last_node_with_lint_attrs = id;
debug!("late context: enter_attrs({:?})", attrs);
lint_callback!(self, enter_lint_attrs, attrs);
+ for attr in attrs {
+ lint_callback!(self, check_attribute, attr);
+ }
f(self);
debug!("late context: exit_attrs({:?})", attrs);
lint_callback!(self, exit_lint_attrs, attrs);
@@ -157,6 +159,10 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas
hir_visit::walk_pat(self, p);
}
+ fn visit_expr_field(&mut self, field: &'tcx hir::ExprField<'tcx>) {
+ self.with_lint_attrs(field.hir_id, |cx| hir_visit::walk_expr_field(cx, field))
+ }
+
fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) {
ensure_sufficient_stack(|| {
self.with_lint_attrs(e.hir_id, |cx| {
@@ -377,20 +383,18 @@ fn late_lint_mod_inner<'tcx, T: LateLintPass<'tcx>>(
let (module, _span, hir_id) = tcx.hir().get_module(module_def_id);
- // There is no module lint that will have the crate itself as an item, so check it here.
- if hir_id == hir::CRATE_HIR_ID {
- lint_callback!(cx, check_crate,);
- }
+ cx.with_lint_attrs(hir_id, |cx| {
+ // There is no module lint that will have the crate itself as an item, so check it here.
+ if hir_id == hir::CRATE_HIR_ID {
+ lint_callback!(cx, check_crate,);
+ }
- cx.process_mod(module, hir_id);
+ cx.process_mod(module, hir_id);
- // Visit the crate attributes
- if hir_id == hir::CRATE_HIR_ID {
- for attr in tcx.hir().attrs(hir::CRATE_HIR_ID).iter() {
- cx.visit_attribute(attr)
+ if hir_id == hir::CRATE_HIR_ID {
+ lint_callback!(cx, check_crate_post,);
}
- lint_callback!(cx, check_crate_post,);
- }
+ });
}
fn late_lint_crate<'tcx>(tcx: TyCtxt<'tcx>) {
@@ -431,7 +435,6 @@ fn late_lint_crate_inner<'tcx, T: LateLintPass<'tcx>>(
// item), warn for it here.
lint_callback!(cx, check_crate,);
tcx.hir().walk_toplevel_module(cx);
- tcx.hir().walk_attributes(cx);
lint_callback!(cx, check_crate_post,);
})
}
diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs
index 1f4e5fa4d..ba521b969 100644
--- a/compiler/rustc_lint/src/levels.rs
+++ b/compiler/rustc_lint/src/levels.rs
@@ -1,18 +1,23 @@
+use crate::errors::{CheckNameUnknownTool, RequestedLevel, UnsupportedGroup};
+use crate::lints::{
+ DeprecatedLintNameFromCommandLine, RemovedLintFromCommandLine, RenamedLintFromCommandLine,
+ UnknownLintFromCommandLine,
+};
use crate::{
builtin::MISSING_DOCS,
context::{CheckLintNameResult, LintStore},
fluent_generated as fluent,
late::unerased_lint_store,
lints::{
- DeprecatedLintName, IgnoredUnlessCrateSpecified, OverruledAttributeLint,
- RenamedOrRemovedLint, RenamedOrRemovedLintSuggestion, UnknownLint, UnknownLintSuggestion,
+ DeprecatedLintName, IgnoredUnlessCrateSpecified, OverruledAttributeLint, RemovedLint,
+ RenamedLint, RenamedLintSuggestion, UnknownLint, UnknownLintSuggestion,
},
};
use rustc_ast as ast;
use rustc_ast_pretty::pprust;
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{DecorateLint, DiagnosticBuilder, DiagnosticMessage, MultiSpan};
-use rustc_feature::Features;
+use rustc_feature::{Features, GateIssue};
use rustc_hir as hir;
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::HirId;
@@ -24,12 +29,14 @@ use rustc_middle::lint::{
};
use rustc_middle::query::Providers;
use rustc_middle::ty::{RegisteredTools, TyCtxt};
-use rustc_session::lint::builtin::{RENAMED_AND_REMOVED_LINTS, UNKNOWN_LINTS, UNUSED_ATTRIBUTES};
use rustc_session::lint::{
- builtin::{self, FORBIDDEN_LINT_GROUPS, SINGLE_USE_LIFETIMES, UNFULFILLED_LINT_EXPECTATIONS},
+ builtin::{
+ self, FORBIDDEN_LINT_GROUPS, RENAMED_AND_REMOVED_LINTS, SINGLE_USE_LIFETIMES,
+ UNFULFILLED_LINT_EXPECTATIONS, UNKNOWN_LINTS, UNUSED_ATTRIBUTES,
+ },
Level, Lint, LintExpectationId, LintId,
};
-use rustc_session::parse::{add_feature_diagnostics, feature_err};
+use rustc_session::parse::feature_err;
use rustc_session::Session;
use rustc_span::symbol::{sym, Symbol};
use rustc_span::{Span, DUMMY_SP};
@@ -331,6 +338,11 @@ impl<'tcx> Visitor<'tcx> for LintLevelsBuilder<'_, LintLevelQueryMap<'tcx>> {
intravisit::walk_expr(self, e);
}
+ fn visit_expr_field(&mut self, f: &'tcx hir::ExprField<'tcx>) {
+ self.add_id(f.hir_id);
+ intravisit::walk_expr_field(self, f);
+ }
+
fn visit_field_def(&mut self, s: &'tcx hir::FieldDef<'tcx>) {
self.add_id(s.hir_id);
intravisit::walk_field_def(self, s);
@@ -550,12 +562,55 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
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);
+ // Checks the validity of lint names derived from the command line.
+ 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(_))
+ {
+ self.sess.emit_err(UnsupportedGroup { lint_group: crate::WARNINGS.name_lower() });
+ }
+ match self.store.check_lint_name(lint_name_only, tool_name, self.registered_tools) {
+ CheckLintNameResult::Renamed(ref replace) => {
+ let name = lint_name.as_str();
+ let suggestion = RenamedLintSuggestion::WithoutSpan { replace };
+ let requested_level = RequestedLevel { level, lint_name };
+ let lint = RenamedLintFromCommandLine { name, suggestion, requested_level };
+ self.emit_lint(RENAMED_AND_REMOVED_LINTS, lint);
+ }
+ CheckLintNameResult::Removed(ref reason) => {
+ let name = lint_name.as_str();
+ let requested_level = RequestedLevel { level, lint_name };
+ let lint = RemovedLintFromCommandLine { name, reason, requested_level };
+ self.emit_lint(RENAMED_AND_REMOVED_LINTS, lint);
+ }
+ CheckLintNameResult::NoLint(suggestion) => {
+ let name = lint_name.clone();
+ let suggestion =
+ suggestion.map(|replace| UnknownLintSuggestion::WithoutSpan { replace });
+ let requested_level = RequestedLevel { level, lint_name };
+ let lint = UnknownLintFromCommandLine { name, suggestion, requested_level };
+ self.emit_lint(UNKNOWN_LINTS, lint);
+ }
+ CheckLintNameResult::Tool(Err((Some(_), ref replace))) => {
+ let name = lint_name.clone();
+ let requested_level = RequestedLevel { level, lint_name };
+ let lint = DeprecatedLintNameFromCommandLine { name, replace, requested_level };
+ self.emit_lint(RENAMED_AND_REMOVED_LINTS, lint);
+ }
+ CheckLintNameResult::NoTool => {
+ self.sess.emit_err(CheckNameUnknownTool {
+ tool_name: tool_name.unwrap(),
+ sub: RequestedLevel { level, lint_name },
+ });
+ }
+ _ => {}
+ };
+
let orig_level = level;
let lint_flag_val = Symbol::intern(lint_name);
let Ok(ids) = self.store.find_lints(&lint_name) else {
- // errors handled in check_lint_name_cmdline above
+ // errors already handled above
continue;
};
for id in ids {
@@ -566,7 +621,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
continue;
}
- if self.check_gated_lint(id, DUMMY_SP) {
+ if self.check_gated_lint(id, DUMMY_SP, true) {
let src = LintLevelSource::CommandLine(lint_flag_val, orig_level);
self.insert(id, (level, src));
}
@@ -837,7 +892,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
reason,
};
for &id in *ids {
- if self.check_gated_lint(id, attr.span) {
+ if self.check_gated_lint(id, attr.span, false) {
self.insert_spec(id, (level, src));
}
}
@@ -854,7 +909,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
reason,
};
for &id in ids {
- if self.check_gated_lint(id, attr.span) {
+ if self.check_gated_lint(id, attr.span, false) {
self.insert_spec(id, (level, src));
}
}
@@ -913,37 +968,37 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
_ if !self.warn_about_weird_lints => {}
- CheckLintNameResult::Warning(msg, renamed) => {
+ CheckLintNameResult::Renamed(ref replace) => {
let suggestion =
- renamed.as_ref().map(|replace| RenamedOrRemovedLintSuggestion {
- suggestion: sp,
- replace: replace.as_str(),
- });
- self.emit_spanned_lint(
- RENAMED_AND_REMOVED_LINTS,
- sp.into(),
- RenamedOrRemovedLint { msg, suggestion },
- );
+ RenamedLintSuggestion::WithSpan { suggestion: sp, replace };
+ let name = tool_ident.map(|tool| format!("{tool}::{name}")).unwrap_or(name);
+ let lint = RenamedLint { name: name.as_str(), suggestion };
+ self.emit_spanned_lint(RENAMED_AND_REMOVED_LINTS, sp.into(), lint);
+ }
+
+ CheckLintNameResult::Removed(ref reason) => {
+ let name = tool_ident.map(|tool| format!("{tool}::{name}")).unwrap_or(name);
+ let lint = RemovedLint { name: name.as_str(), reason };
+ self.emit_spanned_lint(RENAMED_AND_REMOVED_LINTS, sp.into(), lint);
}
+
CheckLintNameResult::NoLint(suggestion) => {
let name = if let Some(tool_ident) = tool_ident {
format!("{}::{}", tool_ident.name, name)
} else {
name.to_string()
};
- let suggestion = suggestion
- .map(|replace| UnknownLintSuggestion { suggestion: sp, replace });
- self.emit_spanned_lint(
- UNKNOWN_LINTS,
- sp.into(),
- UnknownLint { name, suggestion },
- );
+ let suggestion = suggestion.map(|replace| {
+ UnknownLintSuggestion::WithSpan { suggestion: sp, replace }
+ });
+ let lint = UnknownLint { name, suggestion };
+ self.emit_spanned_lint(UNKNOWN_LINTS, sp.into(), lint);
}
}
// If this lint was renamed, apply the new lint instead of ignoring the attribute.
// This happens outside of the match because the new lint should be applied even if
// we don't warn about the name change.
- if let CheckLintNameResult::Warning(_, Some(new_name)) = lint_result {
+ if let CheckLintNameResult::Renamed(new_name) = lint_result {
// Ignore any errors or warnings that happen because the new name is inaccurate
// NOTE: `new_name` already includes the tool name, so we don't have to add it again.
if let CheckLintNameResult::Ok(ids) =
@@ -955,7 +1010,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
reason,
};
for &id in ids {
- if self.check_gated_lint(id, attr.span) {
+ if self.check_gated_lint(id, attr.span, false) {
self.insert_spec(id, (level, src));
}
}
@@ -1000,7 +1055,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
// 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`.
#[track_caller]
- fn check_gated_lint(&self, lint_id: LintId, span: Span) -> bool {
+ fn check_gated_lint(&self, lint_id: LintId, span: Span, lint_from_cli: bool) -> bool {
if let Some(feature) = lint_id.lint.feature_gate {
if !self.features.enabled(feature) {
let lint = builtin::UNKNOWN_LINTS;
@@ -1015,7 +1070,13 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
|lint| {
lint.set_arg("name", lint_id.lint.name_lower());
lint.note(fluent::lint_note);
- add_feature_diagnostics(lint, &self.sess.parse_sess, feature);
+ rustc_session::parse::add_feature_diagnostics_for_issue(
+ lint,
+ &self.sess.parse_sess,
+ feature,
+ GateIssue::Language,
+ lint_from_cli,
+ );
lint
},
);
@@ -1076,3 +1137,14 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
pub(crate) fn provide(providers: &mut Providers) {
*providers = Providers { shallow_lint_levels_on, lint_expectations, ..*providers };
}
+
+pub fn parse_lint_and_tool_name(lint_name: &str) -> (Option<Symbol>, &str) {
+ match lint_name.split_once("::") {
+ Some((tool_name, lint_name)) => {
+ let tool_name = Symbol::intern(tool_name);
+
+ (Some(tool_name), lint_name)
+ }
+ None => (None, lint_name),
+ }
+}
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index 585b10e79..72c103f2d 100644
--- a/compiler/rustc_lint/src/lib.rs
+++ b/compiler/rustc_lint/src/lib.rs
@@ -40,7 +40,7 @@
#![recursion_limit = "256"]
#![deny(rustc::untranslatable_diagnostic)]
#![deny(rustc::diagnostic_outside_of_impl)]
-#![cfg_attr(not(bootstrap), allow(internal_features))]
+#![allow(internal_features)]
#[macro_use]
extern crate rustc_middle;
@@ -86,18 +86,14 @@ mod unused;
pub use array_into_iter::ARRAY_INTO_ITER;
-use rustc_ast as ast;
use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage};
use rustc_fluent_macro::fluent_messages;
-use rustc_hir as hir;
-use rustc_hir::def_id::{LocalDefId, LocalModDefId};
+use rustc_hir::def_id::LocalModDefId;
use rustc_middle::query::Providers;
use rustc_middle::ty::TyCtxt;
use rustc_session::lint::builtin::{
BARE_TRAIT_OBJECTS, ELIDED_LIFETIMES_IN_PATHS, EXPLICIT_OUTLIVES_REQUIREMENTS,
};
-use rustc_span::symbol::Ident;
-use rustc_span::Span;
use array_into_iter::ArrayIntoIter;
use builtin::*;
@@ -134,7 +130,7 @@ pub use late::{check_crate, late_lint_mod, unerased_lint_store};
pub use passes::{EarlyLintPass, LateLintPass};
pub use rustc_session::lint::Level::{self, *};
pub use rustc_session::lint::{BufferedEarlyLint, FutureIncompatibleInfo, Lint, LintId};
-pub use rustc_session::lint::{LintArray, LintPass};
+pub use rustc_session::lint::{LintPass, LintVec};
fluent_messages! { "../messages.ftl" }
@@ -200,7 +196,7 @@ late_lint_methods!(
BoxPointers: BoxPointers,
PathStatements: PathStatements,
LetUnderscore: LetUnderscore,
- InvalidReferenceCasting: InvalidReferenceCasting::default(),
+ InvalidReferenceCasting: InvalidReferenceCasting,
// Depends on referenced function signatures in expressions
UnusedResults: UnusedResults,
NonUpperCaseGlobals: NonUpperCaseGlobals,
@@ -500,6 +496,11 @@ fn register_builtins(store: &mut LintStore) {
"converted into hard error, see issue #82523 \
<https://github.com/rust-lang/rust/issues/82523> for more information",
);
+ store.register_removed(
+ "private_in_public",
+ "replaced with another group of lints, see RFC \
+ <https://rust-lang.github.io/rfcs/2145-type-privacy.html> for more information",
+ );
}
fn register_internals(store: &mut LintStore) {
diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs
index 25982a458..c091c260a 100644
--- a/compiler/rustc_lint/src/lints.rs
+++ b/compiler/rustc_lint/src/lints.rs
@@ -2,6 +2,7 @@
#![allow(rustc::diagnostic_outside_of_impl)]
use std::num::NonZeroU32;
+use crate::errors::RequestedLevel;
use crate::fluent_generated as fluent;
use rustc_errors::{
AddToDiagnostic, Applicability, DecorateLint, DiagnosticMessage, DiagnosticStyledString,
@@ -634,6 +635,8 @@ pub enum PtrNullChecksDiag<'a> {
#[label]
label: Span,
},
+ #[diag(lint_ptr_null_checks_fn_ret)]
+ FnRet { fn_name: Ident },
}
// for_loops_over_fallibles.rs
@@ -764,11 +767,13 @@ pub enum InvalidFromUtf8Diag {
#[derive(LintDiagnostic)]
pub enum InvalidReferenceCastingDiag {
#[diag(lint_invalid_reference_casting_borrow_as_mut)]
+ #[note(lint_invalid_reference_casting_note_book)]
BorrowAsMut {
#[label]
orig_cast: Option<Span>,
},
#[diag(lint_invalid_reference_casting_assign_to_ref)]
+ #[note(lint_invalid_reference_casting_note_book)]
AssignToRef {
#[label]
orig_cast: Option<Span>,
@@ -1010,21 +1015,60 @@ pub struct DeprecatedLintName<'a> {
pub replace: &'a str,
}
-// FIXME: Non-translatable msg
#[derive(LintDiagnostic)]
-#[diag(lint_renamed_or_removed_lint)]
-pub struct RenamedOrRemovedLint<'a> {
- pub msg: &'a str,
+#[diag(lint_deprecated_lint_name)]
+#[help]
+pub struct DeprecatedLintNameFromCommandLine<'a> {
+ pub name: String,
+ pub replace: &'a str,
+ #[subdiagnostic]
+ pub requested_level: RequestedLevel<'a>,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_renamed_lint)]
+pub struct RenamedLint<'a> {
+ pub name: &'a str,
#[subdiagnostic]
- pub suggestion: Option<RenamedOrRemovedLintSuggestion<'a>>,
+ pub suggestion: RenamedLintSuggestion<'a>,
}
#[derive(Subdiagnostic)]
-#[suggestion(lint_suggestion, code = "{replace}", applicability = "machine-applicable")]
-pub struct RenamedOrRemovedLintSuggestion<'a> {
- #[primary_span]
- pub suggestion: Span,
- pub replace: &'a str,
+pub enum RenamedLintSuggestion<'a> {
+ #[suggestion(lint_suggestion, code = "{replace}", applicability = "machine-applicable")]
+ WithSpan {
+ #[primary_span]
+ suggestion: Span,
+ replace: &'a str,
+ },
+ #[help(lint_help)]
+ WithoutSpan { replace: &'a str },
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_renamed_lint)]
+pub struct RenamedLintFromCommandLine<'a> {
+ pub name: &'a str,
+ #[subdiagnostic]
+ pub suggestion: RenamedLintSuggestion<'a>,
+ #[subdiagnostic]
+ pub requested_level: RequestedLevel<'a>,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_removed_lint)]
+pub struct RemovedLint<'a> {
+ pub name: &'a str,
+ pub reason: &'a str,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_removed_lint)]
+pub struct RemovedLintFromCommandLine<'a> {
+ pub name: &'a str,
+ pub reason: &'a str,
+ #[subdiagnostic]
+ pub requested_level: RequestedLevel<'a>,
}
#[derive(LintDiagnostic)]
@@ -1036,11 +1080,25 @@ pub struct UnknownLint {
}
#[derive(Subdiagnostic)]
-#[suggestion(lint_suggestion, code = "{replace}", applicability = "maybe-incorrect")]
-pub struct UnknownLintSuggestion {
- #[primary_span]
- pub suggestion: Span,
- pub replace: Symbol,
+pub enum UnknownLintSuggestion {
+ #[suggestion(lint_suggestion, code = "{replace}", applicability = "maybe-incorrect")]
+ WithSpan {
+ #[primary_span]
+ suggestion: Span,
+ replace: Symbol,
+ },
+ #[help(lint_help)]
+ WithoutSpan { replace: Symbol },
+}
+
+#[derive(LintDiagnostic)]
+#[diag(lint_unknown_lint, code = "E0602")]
+pub struct UnknownLintFromCommandLine<'a> {
+ pub name: String,
+ #[subdiagnostic]
+ pub suggestion: Option<UnknownLintSuggestion>,
+ #[subdiagnostic]
+ pub requested_level: RequestedLevel<'a>,
}
#[derive(LintDiagnostic)]
diff --git a/compiler/rustc_lint/src/noop_method_call.rs b/compiler/rustc_lint/src/noop_method_call.rs
index bc0b9d6d8..cfbca6efb 100644
--- a/compiler/rustc_lint/src/noop_method_call.rs
+++ b/compiler/rustc_lint/src/noop_method_call.rs
@@ -98,6 +98,12 @@ impl<'tcx> LateLintPass<'tcx> for NoopMethodCall {
let Ok(Some(i)) = ty::Instance::resolve(cx.tcx, cx.param_env, did, args) else { return };
// (Re)check that it implements the noop diagnostic.
let Some(name) = cx.tcx.get_diagnostic_name(i.def_id()) else { return };
+ if !matches!(
+ name,
+ sym::noop_method_borrow | sym::noop_method_clone | sym::noop_method_deref
+ ) {
+ return;
+ }
let receiver_ty = cx.typeck_results().expr_ty(receiver);
let expr_ty = cx.typeck_results().expr_ty_adjusted(expr);
diff --git a/compiler/rustc_lint/src/passes.rs b/compiler/rustc_lint/src/passes.rs
index 16964565b..508f3e1ec 100644
--- a/compiler/rustc_lint/src/passes.rs
+++ b/compiler/rustc_lint/src/passes.rs
@@ -1,58 +1,53 @@
use crate::context::{EarlyContext, LateContext};
-use rustc_ast as ast;
-use rustc_hir as hir;
use rustc_session::lint::builtin::HardwiredLints;
use rustc_session::lint::LintPass;
-use rustc_span::def_id::LocalDefId;
-use rustc_span::symbol::Ident;
-use rustc_span::Span;
#[macro_export]
macro_rules! late_lint_methods {
($macro:path, $args:tt) => (
$macro!($args, [
- fn check_body(a: &'tcx hir::Body<'tcx>);
- fn check_body_post(a: &'tcx hir::Body<'tcx>);
+ fn check_body(a: &'tcx rustc_hir::Body<'tcx>);
+ fn check_body_post(a: &'tcx rustc_hir::Body<'tcx>);
fn check_crate();
fn check_crate_post();
- fn check_mod(a: &'tcx hir::Mod<'tcx>, b: hir::HirId);
- fn check_foreign_item(a: &'tcx hir::ForeignItem<'tcx>);
- fn check_item(a: &'tcx hir::Item<'tcx>);
- fn check_item_post(a: &'tcx hir::Item<'tcx>);
- fn check_local(a: &'tcx hir::Local<'tcx>);
- fn check_block(a: &'tcx hir::Block<'tcx>);
- fn check_block_post(a: &'tcx hir::Block<'tcx>);
- fn check_stmt(a: &'tcx hir::Stmt<'tcx>);
- fn check_arm(a: &'tcx hir::Arm<'tcx>);
- fn check_pat(a: &'tcx hir::Pat<'tcx>);
- fn check_expr(a: &'tcx hir::Expr<'tcx>);
- fn check_expr_post(a: &'tcx hir::Expr<'tcx>);
- fn check_ty(a: &'tcx hir::Ty<'tcx>);
- fn check_generic_param(a: &'tcx hir::GenericParam<'tcx>);
- fn check_generics(a: &'tcx hir::Generics<'tcx>);
- fn check_poly_trait_ref(a: &'tcx hir::PolyTraitRef<'tcx>);
+ fn check_mod(a: &'tcx rustc_hir::Mod<'tcx>, b: rustc_hir::HirId);
+ fn check_foreign_item(a: &'tcx rustc_hir::ForeignItem<'tcx>);
+ fn check_item(a: &'tcx rustc_hir::Item<'tcx>);
+ fn check_item_post(a: &'tcx rustc_hir::Item<'tcx>);
+ fn check_local(a: &'tcx rustc_hir::Local<'tcx>);
+ fn check_block(a: &'tcx rustc_hir::Block<'tcx>);
+ fn check_block_post(a: &'tcx rustc_hir::Block<'tcx>);
+ fn check_stmt(a: &'tcx rustc_hir::Stmt<'tcx>);
+ fn check_arm(a: &'tcx rustc_hir::Arm<'tcx>);
+ fn check_pat(a: &'tcx rustc_hir::Pat<'tcx>);
+ fn check_expr(a: &'tcx rustc_hir::Expr<'tcx>);
+ fn check_expr_post(a: &'tcx rustc_hir::Expr<'tcx>);
+ fn check_ty(a: &'tcx rustc_hir::Ty<'tcx>);
+ fn check_generic_param(a: &'tcx rustc_hir::GenericParam<'tcx>);
+ fn check_generics(a: &'tcx rustc_hir::Generics<'tcx>);
+ fn check_poly_trait_ref(a: &'tcx rustc_hir::PolyTraitRef<'tcx>);
fn check_fn(
a: rustc_hir::intravisit::FnKind<'tcx>,
- b: &'tcx hir::FnDecl<'tcx>,
- c: &'tcx hir::Body<'tcx>,
- d: Span,
- e: LocalDefId);
- fn check_trait_item(a: &'tcx hir::TraitItem<'tcx>);
- fn check_impl_item(a: &'tcx hir::ImplItem<'tcx>);
- fn check_impl_item_post(a: &'tcx hir::ImplItem<'tcx>);
- fn check_struct_def(a: &'tcx hir::VariantData<'tcx>);
- fn check_field_def(a: &'tcx hir::FieldDef<'tcx>);
- fn check_variant(a: &'tcx hir::Variant<'tcx>);
- fn check_path(a: &hir::Path<'tcx>, b: hir::HirId);
- fn check_attribute(a: &'tcx ast::Attribute);
+ b: &'tcx rustc_hir::FnDecl<'tcx>,
+ c: &'tcx rustc_hir::Body<'tcx>,
+ d: rustc_span::Span,
+ e: rustc_span::def_id::LocalDefId);
+ fn check_trait_item(a: &'tcx rustc_hir::TraitItem<'tcx>);
+ fn check_impl_item(a: &'tcx rustc_hir::ImplItem<'tcx>);
+ fn check_impl_item_post(a: &'tcx rustc_hir::ImplItem<'tcx>);
+ fn check_struct_def(a: &'tcx rustc_hir::VariantData<'tcx>);
+ fn check_field_def(a: &'tcx rustc_hir::FieldDef<'tcx>);
+ fn check_variant(a: &'tcx rustc_hir::Variant<'tcx>);
+ fn check_path(a: &rustc_hir::Path<'tcx>, b: rustc_hir::HirId);
+ fn check_attribute(a: &'tcx rustc_ast::Attribute);
/// Called when entering a syntax node that can have lint attributes such
/// as `#[allow(...)]`. Called with *all* the attributes of that node.
- fn enter_lint_attrs(a: &'tcx [ast::Attribute]);
+ fn enter_lint_attrs(a: &'tcx [rustc_ast::Attribute]);
/// Counterpart to `enter_lint_attrs`.
- fn exit_lint_attrs(a: &'tcx [ast::Attribute]);
+ fn exit_lint_attrs(a: &'tcx [rustc_ast::Attribute]);
]);
)
}
@@ -90,8 +85,8 @@ macro_rules! expand_combined_late_lint_pass_method {
#[macro_export]
macro_rules! expand_combined_late_lint_pass_methods {
($passes:tt, [$($(#[$attr:meta])* fn $name:ident($($param:ident: $arg:ty),*);)*]) => (
- $(fn $name(&mut self, context: &LateContext<'tcx>, $($param: $arg),*) {
- expand_combined_late_lint_pass_method!($passes, self, $name, (context, $($param),*));
+ $(fn $name(&mut self, context: &$crate::LateContext<'tcx>, $($param: $arg),*) {
+ $crate::expand_combined_late_lint_pass_method!($passes, self, $name, (context, $($param),*));
})*
)
}
@@ -116,19 +111,19 @@ macro_rules! declare_combined_late_lint_pass {
}
}
- $v fn get_lints() -> LintArray {
+ $v fn get_lints() -> $crate::LintVec {
let mut lints = Vec::new();
$(lints.extend_from_slice(&$pass::get_lints());)*
lints
}
}
- impl<'tcx> LateLintPass<'tcx> for $name {
- expand_combined_late_lint_pass_methods!([$($pass),*], $methods);
+ impl<'tcx> $crate::LateLintPass<'tcx> for $name {
+ $crate::expand_combined_late_lint_pass_methods!([$($pass),*], $methods);
}
#[allow(rustc::lint_pass_impl_without_macro)]
- impl LintPass for $name {
+ impl $crate::LintPass for $name {
fn name(&self) -> &'static str {
panic!()
}
@@ -140,41 +135,45 @@ macro_rules! declare_combined_late_lint_pass {
macro_rules! early_lint_methods {
($macro:path, $args:tt) => (
$macro!($args, [
- fn check_param(a: &ast::Param);
- fn check_ident(a: Ident);
- fn check_crate(a: &ast::Crate);
- fn check_crate_post(a: &ast::Crate);
- fn check_item(a: &ast::Item);
- fn check_item_post(a: &ast::Item);
- fn check_local(a: &ast::Local);
- fn check_block(a: &ast::Block);
- fn check_stmt(a: &ast::Stmt);
- fn check_arm(a: &ast::Arm);
- fn check_pat(a: &ast::Pat);
- fn check_pat_post(a: &ast::Pat);
- fn check_expr(a: &ast::Expr);
- fn check_ty(a: &ast::Ty);
- 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);
- 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);
- fn check_mac(a: &ast::MacCall);
+ fn check_param(a: &rustc_ast::Param);
+ fn check_ident(a: rustc_span::symbol::Ident);
+ fn check_crate(a: &rustc_ast::Crate);
+ fn check_crate_post(a: &rustc_ast::Crate);
+ fn check_item(a: &rustc_ast::Item);
+ fn check_item_post(a: &rustc_ast::Item);
+ fn check_local(a: &rustc_ast::Local);
+ fn check_block(a: &rustc_ast::Block);
+ fn check_stmt(a: &rustc_ast::Stmt);
+ fn check_arm(a: &rustc_ast::Arm);
+ fn check_pat(a: &rustc_ast::Pat);
+ fn check_pat_post(a: &rustc_ast::Pat);
+ fn check_expr(a: &rustc_ast::Expr);
+ fn check_expr_post(a: &rustc_ast::Expr);
+ fn check_ty(a: &rustc_ast::Ty);
+ fn check_generic_arg(a: &rustc_ast::GenericArg);
+ fn check_generic_param(a: &rustc_ast::GenericParam);
+ fn check_generics(a: &rustc_ast::Generics);
+ fn check_poly_trait_ref(a: &rustc_ast::PolyTraitRef);
+ fn check_fn(
+ a: rustc_ast::visit::FnKind<'_>,
+ c: rustc_span::Span,
+ d_: rustc_ast::NodeId);
+ fn check_trait_item(a: &rustc_ast::AssocItem);
+ fn check_impl_item(a: &rustc_ast::AssocItem);
+ fn check_variant(a: &rustc_ast::Variant);
+ fn check_attribute(a: &rustc_ast::Attribute);
+ fn check_mac_def(a: &rustc_ast::MacroDef);
+ fn check_mac(a: &rustc_ast::MacCall);
/// Called when entering a syntax node that can have lint attributes such
/// as `#[allow(...)]`. Called with *all* the attributes of that node.
- fn enter_lint_attrs(a: &[ast::Attribute]);
+ fn enter_lint_attrs(a: &[rustc_ast::Attribute]);
/// Counterpart to `enter_lint_attrs`.
- fn exit_lint_attrs(a: &[ast::Attribute]);
+ fn exit_lint_attrs(a: &[rustc_ast::Attribute]);
- fn enter_where_predicate(a: &ast::WherePredicate);
- fn exit_where_predicate(a: &ast::WherePredicate);
+ fn enter_where_predicate(a: &rustc_ast::WherePredicate);
+ fn exit_where_predicate(a: &rustc_ast::WherePredicate);
]);
)
}
@@ -201,8 +200,8 @@ macro_rules! expand_combined_early_lint_pass_method {
#[macro_export]
macro_rules! expand_combined_early_lint_pass_methods {
($passes:tt, [$($(#[$attr:meta])* fn $name:ident($($param:ident: $arg:ty),*);)*]) => (
- $(fn $name(&mut self, context: &EarlyContext<'_>, $($param: $arg),*) {
- expand_combined_early_lint_pass_method!($passes, self, $name, (context, $($param),*));
+ $(fn $name(&mut self, context: &$crate::EarlyContext<'_>, $($param: $arg),*) {
+ $crate::expand_combined_early_lint_pass_method!($passes, self, $name, (context, $($param),*));
})*
)
}
@@ -227,19 +226,19 @@ macro_rules! declare_combined_early_lint_pass {
}
}
- $v fn get_lints() -> LintArray {
+ $v fn get_lints() -> $crate::LintVec {
let mut lints = Vec::new();
$(lints.extend_from_slice(&$pass::get_lints());)*
lints
}
}
- impl EarlyLintPass for $name {
- expand_combined_early_lint_pass_methods!([$($pass),*], $methods);
+ impl $crate::EarlyLintPass for $name {
+ $crate::expand_combined_early_lint_pass_methods!([$($pass),*], $methods);
}
#[allow(rustc::lint_pass_impl_without_macro)]
- impl LintPass for $name {
+ impl $crate::LintPass for $name {
fn name(&self) -> &'static str {
panic!()
}
diff --git a/compiler/rustc_lint/src/ptr_nulls.rs b/compiler/rustc_lint/src/ptr_nulls.rs
index 02aff9103..0de72d8d3 100644
--- a/compiler/rustc_lint/src/ptr_nulls.rs
+++ b/compiler/rustc_lint/src/ptr_nulls.rs
@@ -31,12 +31,30 @@ declare_lint! {
declare_lint_pass!(PtrNullChecks => [USELESS_PTR_NULL_CHECKS]);
-/// This function detects and returns the original expression from a series of consecutive casts,
-/// ie. `(my_fn as *const _ as *mut _).cast_mut()` would return the expression for `my_fn`.
-fn ptr_cast_chain<'a>(cx: &'a LateContext<'_>, mut e: &'a Expr<'a>) -> Option<&'a Expr<'a>> {
+/// This function checks if the expression is from a series of consecutive casts,
+/// ie. `(my_fn as *const _ as *mut _).cast_mut()` and whether the original expression is either
+/// a fn ptr, a reference, or a function call whose definition is
+/// annotated with `#![rustc_never_returns_null_ptr]`.
+/// If this situation is present, the function returns the appropriate diagnostic.
+fn incorrect_check<'a, 'tcx: 'a>(
+ cx: &'a LateContext<'tcx>,
+ mut e: &'a Expr<'a>,
+) -> Option<PtrNullChecksDiag<'tcx>> {
let mut had_at_least_one_cast = false;
loop {
e = e.peel_blocks();
+ if let ExprKind::MethodCall(_, _expr, [], _) = e.kind
+ && let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
+ && cx.tcx.has_attr(def_id, sym::rustc_never_returns_null_ptr)
+ && let Some(fn_name) = cx.tcx.opt_item_ident(def_id) {
+ return Some(PtrNullChecksDiag::FnRet { fn_name });
+ } else if let ExprKind::Call(path, _args) = e.kind
+ && let ExprKind::Path(ref qpath) = path.kind
+ && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
+ && cx.tcx.has_attr(def_id, sym::rustc_never_returns_null_ptr)
+ && let Some(fn_name) = cx.tcx.opt_item_ident(def_id) {
+ return Some(PtrNullChecksDiag::FnRet { fn_name });
+ }
e = if let ExprKind::Cast(expr, t) = e.kind
&& let TyKind::Ptr(_) = t.kind {
had_at_least_one_cast = true;
@@ -46,33 +64,21 @@ fn ptr_cast_chain<'a>(cx: &'a LateContext<'_>, mut e: &'a Expr<'a>) -> Option<&'
&& matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::ptr_cast | sym::ptr_cast_mut)) {
had_at_least_one_cast = true;
expr
- } else if let ExprKind::Call(path, [arg]) = e.kind
- && let ExprKind::Path(ref qpath) = path.kind
- && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
- && matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::ptr_from_ref | sym::ptr_from_mut)) {
- had_at_least_one_cast = true;
- arg
} else if had_at_least_one_cast {
- return Some(e);
+ let orig_ty = cx.typeck_results().expr_ty(e);
+ return if orig_ty.is_fn() {
+ Some(PtrNullChecksDiag::FnPtr { orig_ty, label: e.span })
+ } else if orig_ty.is_ref() {
+ Some(PtrNullChecksDiag::Ref { orig_ty, label: e.span })
+ } else {
+ None
+ };
} else {
return None;
};
}
}
-fn incorrect_check<'a>(cx: &LateContext<'a>, expr: &Expr<'_>) -> Option<PtrNullChecksDiag<'a>> {
- let expr = ptr_cast_chain(cx, expr)?;
-
- let orig_ty = cx.typeck_results().expr_ty(expr);
- if orig_ty.is_fn() {
- Some(PtrNullChecksDiag::FnPtr { orig_ty, label: expr.span })
- } else if orig_ty.is_ref() {
- Some(PtrNullChecksDiag::Ref { orig_ty, label: expr.span })
- } else {
- None
- }
-}
-
impl<'tcx> LateLintPass<'tcx> for PtrNullChecks {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
match expr.kind {
diff --git a/compiler/rustc_lint/src/reference_casting.rs b/compiler/rustc_lint/src/reference_casting.rs
index 2577cabb3..39def599b 100644
--- a/compiler/rustc_lint/src/reference_casting.rs
+++ b/compiler/rustc_lint/src/reference_casting.rs
@@ -1,8 +1,7 @@
use rustc_ast::Mutability;
-use rustc_data_structures::fx::FxHashMap;
-use rustc_hir::{def::Res, Expr, ExprKind, HirId, Local, QPath, StmtKind, UnOp};
+use rustc_hir::{Expr, ExprKind, UnOp};
use rustc_middle::ty::{self, TypeAndMut};
-use rustc_span::{sym, Span};
+use rustc_span::sym;
use crate::{lints::InvalidReferenceCastingDiag, LateContext, LateLintPass, LintContext};
@@ -34,51 +33,18 @@ declare_lint! {
"casts of `&T` to `&mut T` without interior mutability"
}
-#[derive(Default)]
-pub struct InvalidReferenceCasting {
- casted: FxHashMap<HirId, Span>,
-}
-
-impl_lint_pass!(InvalidReferenceCasting => [INVALID_REFERENCE_CASTING]);
+declare_lint_pass!(InvalidReferenceCasting => [INVALID_REFERENCE_CASTING]);
impl<'tcx> LateLintPass<'tcx> for InvalidReferenceCasting {
- fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx rustc_hir::Stmt<'tcx>) {
- let StmtKind::Local(local) = stmt.kind else {
- return;
- };
- let Local { init: Some(init), els: None, .. } = local else {
- return;
- };
-
- if is_cast_from_const_to_mut(cx, init) {
- self.casted.insert(local.pat.hir_id, init.span);
- }
- }
-
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
- // &mut <expr>
- let inner = if let ExprKind::AddrOf(_, Mutability::Mut, expr) = expr.kind {
- expr
- // <expr> = ...
- } else if let ExprKind::Assign(expr, _, _) = expr.kind {
- expr
- // <expr> += ...
- } else if let ExprKind::AssignOp(_, expr, _) = expr.kind {
- expr
- } else {
+ let Some((is_assignment, e)) = is_operation_we_care_about(cx, expr) else {
return;
};
- let ExprKind::Unary(UnOp::Deref, e) = &inner.kind else {
- return;
- };
+ let init = cx.expr_or_init(e);
- let orig_cast = if is_cast_from_const_to_mut(cx, e) {
- None
- } else if let ExprKind::Path(QPath::Resolved(_, path)) = e.kind
- && let Res::Local(hir_id) = &path.res
- && let Some(orig_cast) = self.casted.get(hir_id) {
- Some(*orig_cast)
+ let orig_cast = if is_cast_from_const_to_mut(cx, init) {
+ if init.span != e.span { Some(init.span) } else { None }
} else {
return;
};
@@ -86,84 +52,113 @@ impl<'tcx> LateLintPass<'tcx> for InvalidReferenceCasting {
cx.emit_spanned_lint(
INVALID_REFERENCE_CASTING,
expr.span,
- if matches!(expr.kind, ExprKind::AddrOf(..)) {
- InvalidReferenceCastingDiag::BorrowAsMut { orig_cast }
- } else {
+ if is_assignment {
InvalidReferenceCastingDiag::AssignToRef { orig_cast }
+ } else {
+ InvalidReferenceCastingDiag::BorrowAsMut { orig_cast }
},
);
}
}
-fn is_cast_from_const_to_mut<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
- let e = e.peel_blocks();
-
- fn from_casts<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
- // <expr> as *mut ...
- let mut e = if let ExprKind::Cast(e, t) = e.kind
- && let ty::RawPtr(TypeAndMut { mutbl: Mutability::Mut, .. }) = cx.typeck_results().node_type(t.hir_id).kind() {
- e
- // <expr>.cast_mut()
- } else if let ExprKind::MethodCall(_, expr, [], _) = e.kind
- && let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
- && cx.tcx.is_diagnostic_item(sym::ptr_cast_mut, def_id) {
+fn is_operation_we_care_about<'tcx>(
+ cx: &LateContext<'tcx>,
+ e: &'tcx Expr<'tcx>,
+) -> Option<(bool, &'tcx Expr<'tcx>)> {
+ fn deref_assign_or_addr_of<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<(bool, &'tcx Expr<'tcx>)> {
+ // &mut <expr>
+ let inner = if let ExprKind::AddrOf(_, Mutability::Mut, expr) = expr.kind {
+ expr
+ // <expr> = ...
+ } else if let ExprKind::Assign(expr, _, _) = expr.kind {
+ expr
+ // <expr> += ...
+ } else if let ExprKind::AssignOp(_, expr, _) = expr.kind {
expr
} else {
return None;
};
- let mut had_at_least_one_cast = false;
- loop {
- e = e.peel_blocks();
- // <expr> as *mut/const ... or <expr> as <uint>
- e = if let ExprKind::Cast(expr, t) = e.kind
- && matches!(cx.typeck_results().node_type(t.hir_id).kind(), ty::RawPtr(_) | ty::Uint(_)) {
- had_at_least_one_cast = true;
- expr
- // <expr>.cast(), <expr>.cast_mut() or <expr>.cast_const()
- } else if let ExprKind::MethodCall(_, expr, [], _) = e.kind
- && let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
- && matches!(
- cx.tcx.get_diagnostic_name(def_id),
- Some(sym::ptr_cast | sym::const_ptr_cast | sym::ptr_cast_mut | sym::ptr_cast_const)
- )
- {
- had_at_least_one_cast = true;
- expr
- // ptr::from_ref(<expr>)
- } else if let ExprKind::Call(path, [arg]) = e.kind
- && let ExprKind::Path(ref qpath) = path.kind
- && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
- && cx.tcx.is_diagnostic_item(sym::ptr_from_ref, def_id) {
- return Some(arg);
- } else if had_at_least_one_cast {
- return Some(e);
- } else {
- return None;
- };
+ if let ExprKind::Unary(UnOp::Deref, e) = &inner.kind {
+ Some((!matches!(expr.kind, ExprKind::AddrOf(..)), e))
+ } else {
+ None
}
}
- fn from_transmute<'tcx>(
+ fn ptr_write<'tcx>(
cx: &LateContext<'tcx>,
e: &'tcx Expr<'tcx>,
- ) -> Option<&'tcx Expr<'tcx>> {
- // mem::transmute::<_, *mut _>(<expr>)
- if let ExprKind::Call(path, [arg]) = e.kind
+ ) -> Option<(bool, &'tcx Expr<'tcx>)> {
+ if let ExprKind::Call(path, [arg_ptr, _arg_val]) = e.kind
&& let ExprKind::Path(ref qpath) = path.kind
&& let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
- && cx.tcx.is_diagnostic_item(sym::transmute, def_id)
- && let ty::RawPtr(TypeAndMut { mutbl: Mutability::Mut, .. }) = cx.typeck_results().node_type(e.hir_id).kind() {
- Some(arg)
+ && matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::ptr_write | sym::ptr_write_volatile | sym::ptr_write_unaligned))
+ {
+ Some((true, arg_ptr))
} else {
None
}
}
- let Some(e) = from_casts(cx, e).or_else(|| from_transmute(cx, e)) else {
+ deref_assign_or_addr_of(e).or_else(|| ptr_write(cx, e))
+}
+
+fn is_cast_from_const_to_mut<'tcx>(cx: &LateContext<'tcx>, orig_expr: &'tcx Expr<'tcx>) -> bool {
+ let mut need_check_freeze = false;
+ let mut e = orig_expr;
+
+ let end_ty = cx.typeck_results().node_type(orig_expr.hir_id);
+
+ // Bail out early if the end type is **not** a mutable pointer.
+ if !matches!(end_ty.kind(), ty::RawPtr(TypeAndMut { ty: _, mutbl: Mutability::Mut })) {
return false;
- };
+ }
+
+ loop {
+ e = e.peel_blocks();
+ // <expr> as ...
+ e = if let ExprKind::Cast(expr, _) = e.kind {
+ expr
+ // <expr>.cast(), <expr>.cast_mut() or <expr>.cast_const()
+ } else if let ExprKind::MethodCall(_, expr, [], _) = e.kind
+ && let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
+ && matches!(
+ cx.tcx.get_diagnostic_name(def_id),
+ Some(sym::ptr_cast | sym::const_ptr_cast | sym::ptr_cast_mut | sym::ptr_cast_const)
+ )
+ {
+ expr
+ // ptr::from_ref(<expr>), UnsafeCell::raw_get(<expr>) or mem::transmute<_, _>(<expr>)
+ } else if let ExprKind::Call(path, [arg]) = e.kind
+ && let ExprKind::Path(ref qpath) = path.kind
+ && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
+ && matches!(
+ cx.tcx.get_diagnostic_name(def_id),
+ Some(sym::ptr_from_ref | sym::unsafe_cell_raw_get | sym::transmute)
+ )
+ {
+ if cx.tcx.is_diagnostic_item(sym::unsafe_cell_raw_get, def_id) {
+ need_check_freeze = true;
+ }
+ arg
+ } else {
+ break;
+ };
+ }
- let e = e.peel_blocks();
- matches!(cx.typeck_results().node_type(e.hir_id).kind(), ty::Ref(_, _, Mutability::Not))
+ let start_ty = cx.typeck_results().node_type(e.hir_id);
+ if let ty::Ref(_, inner_ty, Mutability::Not) = start_ty.kind() {
+ // If an UnsafeCell method is involved we need to additionaly check the
+ // inner type for the presence of the Freeze trait (ie does NOT contain
+ // an UnsafeCell), since in that case we would incorrectly lint on valid casts.
+ //
+ // We also consider non concrete skeleton types (ie generics)
+ // to be an issue since there is no way to make it safe for abitrary types.
+ !need_check_freeze
+ || inner_ty.is_freeze(cx.tcx, cx.param_env)
+ || !inner_ty.has_concrete_skeleton()
+ } else {
+ false
+ }
}
diff --git a/compiler/rustc_lint/src/tests.rs b/compiler/rustc_lint/src/tests.rs
index fc9d6f636..4fd054cb7 100644
--- a/compiler/rustc_lint/src/tests.rs
+++ b/compiler/rustc_lint/src/tests.rs
@@ -1,4 +1,4 @@
-use crate::context::parse_lint_and_tool_name;
+use crate::levels::parse_lint_and_tool_name;
use rustc_span::{create_default_session_globals_then, Symbol};
#[test]
diff --git a/compiler/rustc_lint/src/traits.rs b/compiler/rustc_lint/src/traits.rs
index 56508a2a6..e812493b3 100644
--- a/compiler/rustc_lint/src/traits.rs
+++ b/compiler/rustc_lint/src/traits.rs
@@ -96,7 +96,7 @@ impl<'tcx> LateLintPass<'tcx> for DropTraitConstraints {
};
let def_id = trait_predicate.trait_ref.def_id;
if cx.tcx.lang_items().drop_trait() == Some(def_id) {
- // Explicitly allow `impl Drop`, a drop-guards-as-Voldemort-type pattern.
+ // Explicitly allow `impl Drop`, a drop-guards-as-unnameable-type pattern.
if trait_predicate.trait_ref.self_ty().is_impl_trait() {
continue;
}
diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs
index 1ba746edd..44cf1591c 100644
--- a/compiler/rustc_lint/src/types.rs
+++ b/compiler/rustc_lint/src/types.rs
@@ -804,7 +804,7 @@ pub(crate) fn nonnull_optimization_guaranteed<'tcx>(
tcx.has_attr(def.did(), sym::rustc_nonnull_optimization_guaranteed)
}
-/// `repr(transparent)` structs can have a single non-ZST field, this function returns that
+/// `repr(transparent)` structs can have a single non-1-ZST field, this function returns that
/// field.
pub fn transparent_newtype_field<'a, 'tcx>(
tcx: TyCtxt<'tcx>,
@@ -813,8 +813,8 @@ pub fn transparent_newtype_field<'a, 'tcx>(
let param_env = tcx.param_env(variant.def_id);
variant.fields.iter().find(|field| {
let field_ty = tcx.type_of(field.did).instantiate_identity();
- let is_zst = tcx.layout_of(param_env.and(field_ty)).is_ok_and(|layout| layout.is_zst());
- !is_zst
+ let is_1zst = tcx.layout_of(param_env.and(field_ty)).is_ok_and(|layout| layout.is_1zst());
+ !is_1zst
})
}
@@ -917,13 +917,18 @@ pub(crate) fn repr_nullable_ptr<'tcx>(
// At this point, the field's type is known to be nonnull and the parent enum is Option-like.
// If the computed size for the field and the enum are different, the nonnull optimization isn't
// being applied (and we've got a problem somewhere).
- let compute_size_skeleton = |t| SizeSkeleton::compute(t, tcx, param_env).unwrap();
- if !compute_size_skeleton(ty).same_size(compute_size_skeleton(field_ty)) {
+ let compute_size_skeleton = |t| SizeSkeleton::compute(t, tcx, param_env).ok();
+ if !compute_size_skeleton(ty)?.same_size(compute_size_skeleton(field_ty)?) {
bug!("improper_ctypes: Option nonnull optimization not applied?");
}
// Return the nullable type this Option-like enum can be safely represented with.
- let field_ty_abi = &tcx.layout_of(param_env.and(field_ty)).unwrap().abi;
+ let field_ty_layout = tcx.layout_of(param_env.and(field_ty));
+ if field_ty_layout.is_err() && !field_ty.has_non_region_param() {
+ bug!("should be able to compute the layout of non-polymorphic type");
+ }
+
+ let field_ty_abi = &field_ty_layout.ok()?.abi;
if let Abi::Scalar(field_ty_scalar) = field_ty_abi {
match field_ty_scalar.valid_range(&tcx) {
WrappingRange { start: 0, end }
@@ -1266,7 +1271,6 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
| ty::Closure(..)
| ty::Generator(..)
| ty::GeneratorWitness(..)
- | ty::GeneratorWitnessMIR(..)
| ty::Placeholder(..)
| ty::FnDef(..) => bug!("unexpected type in foreign function: {:?}", ty),
}
diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs
index 6041f8075..d5beff4f1 100644
--- a/compiler/rustc_lint/src/unused.rs
+++ b/compiler/rustc_lint/src/unused.rs
@@ -816,8 +816,7 @@ trait UnusedDelimLint {
let (value, ctx, followed_by_block, left_pos, right_pos, is_kw) = match e.kind {
// Do not lint `unused_braces` in `if let` expressions.
If(ref cond, ref block, _)
- if !matches!(cond.kind, Let(_, _, _))
- || Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX =>
+ if !matches!(cond.kind, Let(..)) || Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX =>
{
let left = e.span.lo() + rustc_span::BytePos(2);
let right = block.span.lo();
@@ -826,8 +825,7 @@ trait UnusedDelimLint {
// Do not lint `unused_braces` in `while let` expressions.
While(ref cond, ref block, ..)
- if !matches!(cond.kind, Let(_, _, _))
- || Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX =>
+ if !matches!(cond.kind, Let(..)) || Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX =>
{
let left = e.span.lo() + rustc_span::BytePos(5);
let right = block.span.lo();
@@ -955,11 +953,14 @@ declare_lint! {
pub struct UnusedParens {
with_self_ty_parens: bool,
+ /// `1 as (i32) < 2` parses to ExprKind::Lt
+ /// `1 as i32 < 2` parses to i32::<2[missing angle bracket]
+ parens_in_cast_in_lt: Vec<ast::NodeId>,
}
impl UnusedParens {
pub fn new() -> Self {
- Self { with_self_ty_parens: false }
+ Self { with_self_ty_parens: false, parens_in_cast_in_lt: Vec::new() }
}
}
@@ -1000,7 +1001,7 @@ impl UnusedDelimLint for UnusedParens {
self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos, is_kw)
}
}
- ast::ExprKind::Let(_, ref expr, _) => {
+ ast::ExprKind::Let(_, ref expr, _, _) => {
self.check_unused_delims_expr(
cx,
expr,
@@ -1055,8 +1056,16 @@ impl UnusedParens {
impl EarlyLintPass for UnusedParens {
#[inline]
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
+ if let ExprKind::Binary(op, lhs, _rhs) = &e.kind &&
+ (op.node == ast::BinOpKind::Lt || op.node == ast::BinOpKind::Shl) &&
+ let ExprKind::Cast(_expr, ty) = &lhs.kind &&
+ let ast::TyKind::Paren(_) = &ty.kind
+ {
+ self.parens_in_cast_in_lt.push(ty.id);
+ }
+
match e.kind {
- ExprKind::Let(ref pat, _, _) | ExprKind::ForLoop(ref pat, ..) => {
+ ExprKind::Let(ref pat, _, _, _) | ExprKind::ForLoop(ref pat, ..) => {
self.check_unused_parens_pat(cx, pat, false, false, (true, true));
}
// We ignore parens in cases like `if (((let Some(0) = Some(1))))` because we already
@@ -1101,6 +1110,17 @@ impl EarlyLintPass for UnusedParens {
<Self as UnusedDelimLint>::check_expr(self, cx, e)
}
+ fn check_expr_post(&mut self, _cx: &EarlyContext<'_>, e: &ast::Expr) {
+ if let ExprKind::Binary(op, lhs, _rhs) = &e.kind &&
+ (op.node == ast::BinOpKind::Lt || op.node == ast::BinOpKind::Shl) &&
+ let ExprKind::Cast(_expr, ty) = &lhs.kind &&
+ let ast::TyKind::Paren(_) = &ty.kind
+ {
+ let id = self.parens_in_cast_in_lt.pop().expect("check_expr and check_expr_post must balance");
+ assert_eq!(id, ty.id, "check_expr, check_ty, and check_expr_post are called, in that order, by the visitor");
+ }
+ }
+
fn check_pat(&mut self, cx: &EarlyContext<'_>, p: &ast::Pat) {
use ast::{Mutability, PatKind::*};
let keep_space = (false, false);
@@ -1141,6 +1161,11 @@ impl EarlyLintPass for UnusedParens {
}
fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) {
+ if let ast::TyKind::Paren(_) = ty.kind &&
+ Some(&ty.id) == self.parens_in_cast_in_lt.last()
+ {
+ return;
+ }
match &ty.kind {
ast::TyKind::Array(_, len) => {
self.check_unused_delims_expr(
@@ -1284,7 +1309,7 @@ impl UnusedDelimLint for UnusedBraces {
}
}
}
- ast::ExprKind::Let(_, ref expr, _) => {
+ ast::ExprKind::Let(_, ref expr, _, _) => {
self.check_unused_delims_expr(
cx,
expr,