summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_lint/src/types.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_lint/src/types.rs')
-rw-r--r--compiler/rustc_lint/src/types.rs355
1 files changed, 144 insertions, 211 deletions
diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs
index 297b509d4..be47a3e23 100644
--- a/compiler/rustc_lint/src/types.rs
+++ b/compiler/rustc_lint/src/types.rs
@@ -1,11 +1,16 @@
+use crate::lints::{
+ AtomicOrderingFence, AtomicOrderingLoad, AtomicOrderingStore, ImproperCTypes,
+ InvalidAtomicOrderingDiag, OnlyCastu8ToChar, OverflowingBinHex, OverflowingBinHexSign,
+ OverflowingBinHexSub, OverflowingInt, OverflowingIntHelp, OverflowingLiteral, OverflowingUInt,
+ RangeEndpointOutOfRange, UnusedComparisons, VariantSizeDifferencesDiag,
+};
use crate::{LateContext, LateLintPass, LintContext};
use rustc_ast as ast;
use rustc_attr as attr;
use rustc_data_structures::fx::FxHashSet;
-use rustc_errors::{fluent, Applicability, DiagnosticMessage};
+use rustc_errors::{fluent, DiagnosticMessage};
use rustc_hir as hir;
use rustc_hir::{is_range_literal, Expr, ExprKind, Node};
-use rustc_macros::LintDiagnostic;
use rustc_middle::ty::layout::{IntegerExt, LayoutOf, SizeSkeleton};
use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::{self, AdtKind, DefIdTree, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable};
@@ -16,7 +21,6 @@ use rustc_target::abi::{Abi, Size, WrappingRange};
use rustc_target::abi::{Integer, TagEncoding, Variants};
use rustc_target::spec::abi::Abi as SpecAbi;
-use std::cmp;
use std::iter;
use std::ops::ControlFlow;
@@ -128,10 +132,9 @@ fn lint_overflowing_range_endpoint<'tcx>(
) -> 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 par_id = cx.tcx.hir().parent_id(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 };
+ let Node::Expr(struct_expr) = cx.tcx.hir().get_parent(field.hir_id) else { return false };
if !is_range_literal(struct_expr) {
return false;
};
@@ -148,32 +151,22 @@ fn lint_overflowing_range_endpoint<'tcx>(
};
let Ok(start) = cx.sess().source_map().span_to_snippet(eps[0].span) else { return false };
- cx.struct_span_lint(
+ use rustc_ast::{LitIntType, LitKind};
+ 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!(),
+ };
+ cx.emit_spanned_lint(
OVERFLOWING_LITERALS,
struct_expr.span,
- fluent::lint_range_endpoint_out_of_range,
- |lint| {
- use ast::{LitIntType, LitKind};
-
- lint.set_arg("ty", ty);
-
- // We need to preserve the literal's suffix,
- // as it may determine typing information.
- let suffix = match lit.node {
- LitKind::Int(_, LitIntType::Signed(s)) => s.name_str(),
- LitKind::Int(_, LitIntType::Unsigned(s)) => s.name_str(),
- LitKind::Int(_, LitIntType::Unsuffixed) => "",
- _ => bug!(),
- };
- let suggestion = format!("{}..={}{}", start, lit_val - 1, suffix);
- lint.span_suggestion(
- struct_expr.span,
- fluent::suggestion,
- suggestion,
- Applicability::MachineApplicable,
- );
-
- lint
+ RangeEndpointOutOfRange {
+ ty,
+ suggestion: struct_expr.span,
+ start,
+ literal: lit_val - 1,
+ suffix,
},
);
@@ -230,58 +223,37 @@ fn report_bin_hex_error(
val: u128,
negative: bool,
) {
- cx.struct_span_lint(
- OVERFLOWING_LITERALS,
- expr.span,
- fluent::lint_overflowing_bin_hex,
- |lint| {
- let (t, actually) = match ty {
- attr::IntType::SignedInt(t) => {
- let actually = if negative {
- -(size.sign_extend(val) as i128)
- } else {
- size.sign_extend(val) as i128
- };
- (t.name_str(), actually.to_string())
- }
- attr::IntType::UnsignedInt(t) => {
- let actually = size.truncate(val);
- (t.name_str(), actually.to_string())
- }
+ let (t, actually) = match ty {
+ attr::IntType::SignedInt(t) => {
+ let actually = if negative {
+ -(size.sign_extend(val) as i128)
+ } else {
+ size.sign_extend(val) as i128
};
-
- if negative {
- // If the value is negative,
- // emits a note about the value itself, apart from the literal.
- lint.note(fluent::negative_note);
- lint.note(fluent::negative_becomes_note);
+ (t.name_str(), actually.to_string())
+ }
+ attr::IntType::UnsignedInt(t) => {
+ let actually = size.truncate(val);
+ (t.name_str(), actually.to_string())
+ }
+ };
+ let sign =
+ if negative { OverflowingBinHexSign::Negative } else { OverflowingBinHexSign::Positive };
+ let sub = get_type_suggestion(cx.typeck_results().node_type(expr.hir_id), val, negative).map(
+ |suggestion_ty| {
+ if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') {
+ let (sans_suffix, _) = repr_str.split_at(pos);
+ OverflowingBinHexSub::Suggestion { span: expr.span, suggestion_ty, sans_suffix }
} else {
- lint.note(fluent::positive_note);
- }
- if let Some(sugg_ty) =
- get_type_suggestion(cx.typeck_results().node_type(expr.hir_id), val, negative)
- {
- lint.set_arg("suggestion_ty", sugg_ty);
- if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') {
- let (sans_suffix, _) = repr_str.split_at(pos);
- lint.span_suggestion(
- expr.span,
- fluent::suggestion,
- format!("{}{}", sans_suffix, sugg_ty),
- Applicability::MachineApplicable,
- );
- } else {
- lint.help(fluent::help);
- }
+ OverflowingBinHexSub::Help { suggestion_ty }
}
- lint.set_arg("ty", t)
- .set_arg("lit", repr_str)
- .set_arg("dec", val)
- .set_arg("actually", actually);
-
- lint
},
);
+ cx.emit_spanned_lint(
+ OVERFLOWING_LITERALS,
+ expr.span,
+ OverflowingBinHex { ty: t, lit: repr_str.clone(), dec: val, actually, sign, sub },
+ )
}
// This function finds the next fitting type and generates a suggestion string.
@@ -365,28 +337,19 @@ fn lint_int_literal<'tcx>(
return;
}
- cx.struct_span_lint(OVERFLOWING_LITERALS, e.span, fluent::lint_overflowing_int, |lint| {
- lint.set_arg("ty", t.name_str())
- .set_arg(
- "lit",
- cx.sess()
- .source_map()
- .span_to_snippet(lit.span)
- .expect("must get snippet from literal"),
- )
- .set_arg("min", min)
- .set_arg("max", max)
- .note(fluent::note);
-
- if let Some(sugg_ty) =
- get_type_suggestion(cx.typeck_results().node_type(e.hir_id), v, negative)
- {
- lint.set_arg("suggestion_ty", sugg_ty);
- lint.help(fluent::help);
- }
-
- lint
- });
+ let lit = cx
+ .sess()
+ .source_map()
+ .span_to_snippet(lit.span)
+ .expect("must get snippet from literal");
+ let help = get_type_suggestion(cx.typeck_results().node_type(e.hir_id), v, negative)
+ .map(|suggestion_ty| OverflowingIntHelp { suggestion_ty });
+
+ cx.emit_spanned_lint(
+ OVERFLOWING_LITERALS,
+ e.span,
+ OverflowingInt { ty: t.name_str(), lit, min, max, help },
+ );
}
}
@@ -405,23 +368,15 @@ fn lint_uint_literal<'tcx>(
_ => bug!(),
};
if lit_val < min || lit_val > max {
- let parent_id = cx.tcx.hir().get_parent_node(e.hir_id);
+ let parent_id = cx.tcx.hir().parent_id(e.hir_id);
if let Node::Expr(par_e) = cx.tcx.hir().get(parent_id) {
match par_e.kind {
hir::ExprKind::Cast(..) => {
if let ty::Char = cx.typeck_results().expr_ty(par_e).kind() {
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
OVERFLOWING_LITERALS,
par_e.span,
- fluent::lint_only_cast_u8_to_char,
- |lint| {
- lint.span_suggestion(
- par_e.span,
- fluent::suggestion,
- format!("'\\u{{{:X}}}'", lit_val),
- Applicability::MachineApplicable,
- )
- },
+ OnlyCastu8ToChar { span: par_e.span, literal: lit_val },
);
return;
}
@@ -445,19 +400,20 @@ fn lint_uint_literal<'tcx>(
);
return;
}
- cx.struct_span_lint(OVERFLOWING_LITERALS, e.span, fluent::lint_overflowing_uint, |lint| {
- lint.set_arg("ty", t.name_str())
- .set_arg(
- "lit",
- cx.sess()
- .source_map()
- .span_to_snippet(lit.span)
- .expect("must get snippet from literal"),
- )
- .set_arg("min", min)
- .set_arg("max", max)
- .note(fluent::note)
- });
+ cx.emit_spanned_lint(
+ OVERFLOWING_LITERALS,
+ e.span,
+ OverflowingUInt {
+ ty: t.name_str(),
+ lit: cx
+ .sess()
+ .source_map()
+ .span_to_snippet(lit.span)
+ .expect("must get snippet from literal"),
+ min,
+ max,
+ },
+ );
}
}
@@ -486,20 +442,16 @@ fn lint_literal<'tcx>(
_ => bug!(),
};
if is_infinite == Ok(true) {
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
OVERFLOWING_LITERALS,
e.span,
- fluent::lint_overflowing_literal,
- |lint| {
- lint.set_arg("ty", t.name_str())
- .set_arg(
- "lit",
- cx.sess()
- .source_map()
- .span_to_snippet(lit.span)
- .expect("must get snippet from literal"),
- )
- .note(fluent::note)
+ OverflowingLiteral {
+ ty: t.name_str(),
+ lit: cx
+ .sess()
+ .source_map()
+ .span_to_snippet(lit.span)
+ .expect("must get snippet from literal"),
},
);
}
@@ -519,19 +471,14 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits {
}
hir::ExprKind::Binary(binop, ref l, ref r) => {
if is_comparison(binop) && !check_limits(cx, binop, &l, &r) {
- cx.struct_span_lint(
- UNUSED_COMPARISONS,
- e.span,
- fluent::lint_unused_comparisons,
- |lint| lint,
- );
+ cx.emit_spanned_lint(UNUSED_COMPARISONS, e.span, UnusedComparisons);
}
}
hir::ExprKind::Lit(ref lit) => lint_literal(cx, self, e, lit),
_ => {}
};
- fn is_valid<T: cmp::PartialOrd>(binop: hir::BinOp, v: T, min: T, max: T) -> bool {
+ fn is_valid<T: PartialOrd>(binop: hir::BinOp, v: T, min: T, max: T) -> bool {
match binop.node {
hir::BinOpKind::Lt => v > min && v <= max,
hir::BinOpKind::Le => v >= min && v < max,
@@ -880,39 +827,39 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
) -> FfiResult<'tcx> {
use FfiResult::*;
- if def.repr().transparent() {
+ let transparent_safety = def.repr().transparent().then(|| {
// Can assume that at most one field is not a ZST, so only check
// that field's type for FFI-safety.
if let Some(field) = transparent_newtype_field(self.cx.tcx, variant) {
- self.check_field_type_for_ffi(cache, field, substs)
+ return self.check_field_type_for_ffi(cache, field, substs);
} else {
// All fields are ZSTs; this means that the type should behave
- // like (), which is FFI-unsafe
+ // like (), which is FFI-unsafe... except if all fields are PhantomData,
+ // which is tested for below
FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_struct_zst, help: None }
}
- } else {
- // We can't completely trust repr(C) markings; make sure the fields are
- // actually safe.
- let mut all_phantom = !variant.fields.is_empty();
- for field in &variant.fields {
- match self.check_field_type_for_ffi(cache, &field, substs) {
- FfiSafe => {
- all_phantom = false;
- }
- FfiPhantom(..) if def.is_enum() => {
- return FfiUnsafe {
- ty,
- reason: fluent::lint_improper_ctypes_enum_phantomdata,
- help: None,
- };
- }
- FfiPhantom(..) => {}
- r => return r,
+ });
+ // We can't completely trust repr(C) markings; make sure the fields are
+ // actually safe.
+ let mut all_phantom = !variant.fields.is_empty();
+ for field in &variant.fields {
+ match self.check_field_type_for_ffi(cache, &field, substs) {
+ FfiSafe => {
+ all_phantom = false;
+ }
+ FfiPhantom(..) if !def.repr().transparent() && def.is_enum() => {
+ return FfiUnsafe {
+ ty,
+ reason: fluent::lint_improper_ctypes_enum_phantomdata,
+ help: None,
+ };
}
+ FfiPhantom(..) => {}
+ r => return transparent_safety.unwrap_or(r),
}
-
- if all_phantom { FfiPhantom(ty) } else { FfiSafe }
}
+
+ if all_phantom { FfiPhantom(ty) } else { transparent_safety.unwrap_or(FfiSafe) }
}
/// Checks if the given type is "ffi-safe" (has a stable, well-defined
@@ -1140,18 +1087,20 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
// While opaque types are checked for earlier, if a projection in a struct field
// normalizes to an opaque type, then it will reach this branch.
- ty::Opaque(..) => {
+ ty::Alias(ty::Opaque, ..) => {
FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_opaque, help: None }
}
// `extern "C" fn` functions can have type parameters, which may or may not be FFI-safe,
// so they are currently ignored for the purposes of this lint.
- ty::Param(..) | ty::Projection(..) if matches!(self.mode, CItemKind::Definition) => {
+ ty::Param(..) | ty::Alias(ty::Projection, ..)
+ if matches!(self.mode, CItemKind::Definition) =>
+ {
FfiSafe
}
ty::Param(..)
- | ty::Projection(..)
+ | ty::Alias(ty::Projection, ..)
| ty::Infer(..)
| ty::Bound(..)
| ty::Error(_)
@@ -1174,26 +1123,21 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
CItemKind::Declaration => IMPROPER_CTYPES,
CItemKind::Definition => IMPROPER_CTYPES_DEFINITIONS,
};
-
- self.cx.struct_span_lint(lint, sp, fluent::lint_improper_ctypes, |lint| {
- let item_description = match self.mode {
- CItemKind::Declaration => "block",
- CItemKind::Definition => "fn",
+ let desc = match self.mode {
+ CItemKind::Declaration => "block",
+ CItemKind::Definition => "fn",
+ };
+ let span_note = if let ty::Adt(def, _) = ty.kind()
+ && let Some(sp) = self.cx.tcx.hir().span_if_local(def.did()) {
+ Some(sp)
+ } else {
+ None
};
- lint.set_arg("ty", ty);
- lint.set_arg("desc", item_description);
- lint.span_label(sp, fluent::label);
- if let Some(help) = help {
- lint.help(help);
- }
- lint.note(note);
- if let ty::Adt(def, _) = ty.kind() {
- if let Some(sp) = self.cx.tcx.hir().span_if_local(def.did()) {
- lint.span_note(sp, fluent::note);
- }
- }
- lint
- });
+ self.cx.emit_spanned_lint(
+ lint,
+ sp,
+ ImproperCTypes { ty, desc, label: sp, help, note, span_note },
+ );
}
fn check_for_opaque_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool {
@@ -1203,10 +1147,10 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
if !ty.has_opaque_types() {
- return ControlFlow::CONTINUE;
+ return ControlFlow::Continue(());
}
- if let ty::Opaque(..) = ty.kind() {
+ if let ty::Alias(ty::Opaque, ..) = ty.kind() {
ControlFlow::Break(ty)
} else {
ty.super_visit_with(self)
@@ -1397,11 +1341,10 @@ impl<'tcx> LateLintPass<'tcx> for VariantSizeDifferences {
// We only warn if the largest variant is at least thrice as large as
// the second-largest.
if largest > slargest * 3 && slargest > 0 {
- cx.struct_span_lint(
+ cx.emit_spanned_lint(
VARIANT_SIZE_DIFFERENCES,
enum_definition.variants[largest_index].span,
- fluent::lint_variant_size_differences,
- |lint| lint.set_arg("largest", largest),
+ VariantSizeDifferencesDiag { largest },
);
}
}
@@ -1509,17 +1452,19 @@ 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, msg)) = match method {
- sym::load => Some((&args[0], sym::Release, fluent::lint_atomic_ordering_load)),
- sym::store => Some((&args[1], sym::Acquire, fluent::lint_atomic_ordering_store)),
+ && let Some((ordering_arg, invalid_ordering)) = match method {
+ sym::load => Some((&args[0], sym::Release)),
+ sym::store => Some((&args[1], sym::Acquire)),
_ => None,
}
&& let Some(ordering) = Self::match_ordering(cx, ordering_arg)
&& (ordering == invalid_ordering || ordering == sym::AcqRel)
{
- cx.struct_span_lint(INVALID_ATOMIC_ORDERING, ordering_arg.span, msg, |lint| {
- lint.help(fluent::help)
- });
+ if method == sym::load {
+ cx.emit_spanned_lint(INVALID_ATOMIC_ORDERING, ordering_arg.span, AtomicOrderingLoad);
+ } else {
+ cx.emit_spanned_lint(INVALID_ATOMIC_ORDERING, ordering_arg.span, AtomicOrderingStore);
+ };
}
}
@@ -1530,10 +1475,7 @@ impl InvalidAtomicOrdering {
&& matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::fence | sym::compiler_fence))
&& Self::match_ordering(cx, &args[0]) == Some(sym::Relaxed)
{
- cx.struct_span_lint(INVALID_ATOMIC_ORDERING, args[0].span, fluent::lint_atomic_ordering_fence, |lint| {
- lint
- .help(fluent::help)
- });
+ cx.emit_spanned_lint(INVALID_ATOMIC_ORDERING, args[0].span, AtomicOrderingFence);
}
}
@@ -1550,15 +1492,6 @@ impl InvalidAtomicOrdering {
let Some(fail_ordering) = Self::match_ordering(cx, fail_order_arg) else { return };
if matches!(fail_ordering, sym::Release | sym::AcqRel) {
- #[derive(LintDiagnostic)]
- #[diag(lint_atomic_ordering_invalid)]
- #[help]
- struct InvalidAtomicOrderingDiag {
- method: Symbol,
- #[label]
- fail_order_arg_span: Span,
- }
-
cx.emit_spanned_lint(
INVALID_ATOMIC_ORDERING,
fail_order_arg.span,