Auto merge of #3494 - rust-lang:rustup-2024-04-20, r=RalfJung

Automatic Rustup
This commit is contained in:
bors 2024-04-20 06:28:05 +00:00
commit 9713ff45ab
196 changed files with 2458 additions and 981 deletions

View File

@ -5598,6 +5598,7 @@ dependencies = [
"lazy_static",
"miropt-test-tools",
"regex",
"rustc-hash",
"semver",
"termcolor",
"walkdir",

View File

@ -20,10 +20,126 @@ impl<'hir> LoweringContext<'_, 'hir> {
let mut fmt = Cow::Borrowed(fmt);
if self.tcx.sess.opts.unstable_opts.flatten_format_args {
fmt = flatten_format_args(fmt);
fmt = inline_literals(fmt);
fmt = self.inline_literals(fmt);
}
expand_format_args(self, sp, &fmt, allow_const)
}
/// Try to convert a literal into an interned string
fn try_inline_lit(&self, lit: token::Lit) -> Option<Symbol> {
match LitKind::from_token_lit(lit) {
Ok(LitKind::Str(s, _)) => Some(s),
Ok(LitKind::Int(n, ty)) => {
match ty {
// unsuffixed integer literals are assumed to be i32's
LitIntType::Unsuffixed => {
(n <= i32::MAX as u128).then_some(Symbol::intern(&n.to_string()))
}
LitIntType::Signed(int_ty) => {
let max_literal = self.int_ty_max(int_ty);
(n <= max_literal).then_some(Symbol::intern(&n.to_string()))
}
LitIntType::Unsigned(uint_ty) => {
let max_literal = self.uint_ty_max(uint_ty);
(n <= max_literal).then_some(Symbol::intern(&n.to_string()))
}
}
}
_ => None,
}
}
/// Get the maximum value of int_ty. It is platform-dependent due to the byte size of isize
fn int_ty_max(&self, int_ty: IntTy) -> u128 {
match int_ty {
IntTy::Isize => self.tcx.data_layout.pointer_size.signed_int_max() as u128,
IntTy::I8 => i8::MAX as u128,
IntTy::I16 => i16::MAX as u128,
IntTy::I32 => i32::MAX as u128,
IntTy::I64 => i64::MAX as u128,
IntTy::I128 => i128::MAX as u128,
}
}
/// Get the maximum value of uint_ty. It is platform-dependent due to the byte size of usize
fn uint_ty_max(&self, uint_ty: UintTy) -> u128 {
match uint_ty {
UintTy::Usize => self.tcx.data_layout.pointer_size.unsigned_int_max(),
UintTy::U8 => u8::MAX as u128,
UintTy::U16 => u16::MAX as u128,
UintTy::U32 => u32::MAX as u128,
UintTy::U64 => u64::MAX as u128,
UintTy::U128 => u128::MAX as u128,
}
}
/// Inline literals into the format string.
///
/// Turns
///
/// `format_args!("Hello, {}! {} {}", "World", 123, x)`
///
/// into
///
/// `format_args!("Hello, World! 123 {}", x)`.
fn inline_literals<'fmt>(&self, mut fmt: Cow<'fmt, FormatArgs>) -> Cow<'fmt, FormatArgs> {
let mut was_inlined = vec![false; fmt.arguments.all_args().len()];
let mut inlined_anything = false;
for i in 0..fmt.template.len() {
let FormatArgsPiece::Placeholder(placeholder) = &fmt.template[i] else { continue };
let Ok(arg_index) = placeholder.argument.index else { continue };
let mut literal = None;
if let FormatTrait::Display = placeholder.format_trait
&& placeholder.format_options == Default::default()
&& let arg = fmt.arguments.all_args()[arg_index].expr.peel_parens_and_refs()
&& let ExprKind::Lit(lit) = arg.kind
{
literal = self.try_inline_lit(lit);
}
if let Some(literal) = literal {
// Now we need to mutate the outer FormatArgs.
// If this is the first time, this clones the outer FormatArgs.
let fmt = fmt.to_mut();
// Replace the placeholder with the literal.
fmt.template[i] = FormatArgsPiece::Literal(literal);
was_inlined[arg_index] = true;
inlined_anything = true;
}
}
// Remove the arguments that were inlined.
if inlined_anything {
let fmt = fmt.to_mut();
let mut remove = was_inlined;
// Don't remove anything that's still used.
for_all_argument_indexes(&mut fmt.template, |index| remove[*index] = false);
// Drop all the arguments that are marked for removal.
let mut remove_it = remove.iter();
fmt.arguments.all_args_mut().retain(|_| remove_it.next() != Some(&true));
// Calculate the mapping of old to new indexes for the remaining arguments.
let index_map: Vec<usize> = remove
.into_iter()
.scan(0, |i, remove| {
let mapped = *i;
*i += !remove as usize;
Some(mapped)
})
.collect();
// Correct the indexes that refer to arguments that have shifted position.
for_all_argument_indexes(&mut fmt.template, |index| *index = index_map[*index]);
}
fmt
}
}
/// Flattens nested `format_args!()` into one.
@ -103,82 +219,6 @@ fn flatten_format_args(mut fmt: Cow<'_, FormatArgs>) -> Cow<'_, FormatArgs> {
fmt
}
/// Inline literals into the format string.
///
/// Turns
///
/// `format_args!("Hello, {}! {} {}", "World", 123, x)`
///
/// into
///
/// `format_args!("Hello, World! 123 {}", x)`.
fn inline_literals(mut fmt: Cow<'_, FormatArgs>) -> Cow<'_, FormatArgs> {
let mut was_inlined = vec![false; fmt.arguments.all_args().len()];
let mut inlined_anything = false;
for i in 0..fmt.template.len() {
let FormatArgsPiece::Placeholder(placeholder) = &fmt.template[i] else { continue };
let Ok(arg_index) = placeholder.argument.index else { continue };
let mut literal = None;
if let FormatTrait::Display = placeholder.format_trait
&& placeholder.format_options == Default::default()
&& let arg = fmt.arguments.all_args()[arg_index].expr.peel_parens_and_refs()
&& let ExprKind::Lit(lit) = arg.kind
{
if let token::LitKind::Str | token::LitKind::StrRaw(_) = lit.kind
&& let Ok(LitKind::Str(s, _)) = LitKind::from_token_lit(lit)
{
literal = Some(s);
} else if let token::LitKind::Integer = lit.kind
&& let Ok(LitKind::Int(n, _)) = LitKind::from_token_lit(lit)
{
literal = Some(Symbol::intern(&n.to_string()));
}
}
if let Some(literal) = literal {
// Now we need to mutate the outer FormatArgs.
// If this is the first time, this clones the outer FormatArgs.
let fmt = fmt.to_mut();
// Replace the placeholder with the literal.
fmt.template[i] = FormatArgsPiece::Literal(literal);
was_inlined[arg_index] = true;
inlined_anything = true;
}
}
// Remove the arguments that were inlined.
if inlined_anything {
let fmt = fmt.to_mut();
let mut remove = was_inlined;
// Don't remove anything that's still used.
for_all_argument_indexes(&mut fmt.template, |index| remove[*index] = false);
// Drop all the arguments that are marked for removal.
let mut remove_it = remove.iter();
fmt.arguments.all_args_mut().retain(|_| remove_it.next() != Some(&true));
// Calculate the mapping of old to new indexes for the remaining arguments.
let index_map: Vec<usize> = remove
.into_iter()
.scan(0, |i, remove| {
let mapped = *i;
*i += !remove as usize;
Some(mapped)
})
.collect();
// Correct the indexes that refer to arguments that have shifted position.
for_all_argument_indexes(&mut fmt.template, |index| *index = index_map[*index]);
}
fmt
}
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
enum ArgumentType {
Format(FormatTrait),

View File

@ -0,0 +1,37 @@
From 0d741cf82c3c908616abd39dc84ebf7d8702e0c3 Mon Sep 17 00:00:00 2001
From: Chris Denton <chris@chrisdenton.dev>
Date: Tue, 16 Apr 2024 15:51:34 +0000
Subject: [PATCH] Revert use raw-dylib for Windows futex APIs
---
library/std/src/sys/pal/windows/c.rs | 14 +-------------
1 file changed, 1 insertion(+), 13 deletions(-)
diff --git a/library/std/src/sys/pal/windows/c.rs b/library/std/src/sys/pal/windows/c.rs
index 9d58ce05f01..1c828bac4b6 100644
--- a/library/std/src/sys/pal/windows/c.rs
+++ b/library/std/src/sys/pal/windows/c.rs
@@ -357,19 +357,7 @@ pub fn GetTempPath2W(bufferlength: u32, buffer: PWSTR) -> u32 {
}
#[cfg(not(target_vendor = "win7"))]
-// Use raw-dylib to import synchronization functions to workaround issues with the older mingw import library.
-#[cfg_attr(
- target_arch = "x86",
- link(
- name = "api-ms-win-core-synch-l1-2-0",
- kind = "raw-dylib",
- import_name_type = "undecorated"
- )
-)]
-#[cfg_attr(
- not(target_arch = "x86"),
- link(name = "api-ms-win-core-synch-l1-2-0", kind = "raw-dylib")
-)]
+#[link(name = "synchronization")]
extern "system" {
pub fn WaitOnAddress(
address: *const c_void,
--
2.42.0.windows.2

View File

@ -110,7 +110,7 @@ pub(crate) fn codegen_const_value<'tcx>(
if fx.clif_type(layout.ty).is_some() {
return CValue::const_val(fx, layout, int);
} else {
let raw_val = int.size().truncate(int.to_bits(int.size()).unwrap());
let raw_val = int.size().truncate(int.assert_bits(int.size()));
let val = match int.size().bytes() {
1 => fx.bcx.ins().iconst(types::I8, raw_val as i64),
2 => fx.bcx.ins().iconst(types::I16, raw_val as i64),
@ -491,27 +491,24 @@ pub(crate) fn mir_operand_get_const_val<'tcx>(
return None;
}
let scalar_int = mir_operand_get_const_val(fx, operand)?;
let scalar_int = match fx
.layout_of(*ty)
.size
.cmp(&scalar_int.size())
{
Ordering::Equal => scalar_int,
Ordering::Less => match ty.kind() {
ty::Uint(_) => ScalarInt::try_from_uint(
scalar_int.try_to_uint(scalar_int.size()).unwrap(),
fx.layout_of(*ty).size,
)
.unwrap(),
ty::Int(_) => ScalarInt::try_from_int(
scalar_int.try_to_int(scalar_int.size()).unwrap(),
fx.layout_of(*ty).size,
)
.unwrap(),
_ => unreachable!(),
},
Ordering::Greater => return None,
};
let scalar_int =
match fx.layout_of(*ty).size.cmp(&scalar_int.size()) {
Ordering::Equal => scalar_int,
Ordering::Less => match ty.kind() {
ty::Uint(_) => ScalarInt::try_from_uint(
scalar_int.assert_uint(scalar_int.size()),
fx.layout_of(*ty).size,
)
.unwrap(),
ty::Int(_) => ScalarInt::try_from_int(
scalar_int.assert_int(scalar_int.size()),
fx.layout_of(*ty).size,
)
.unwrap(),
_ => unreachable!(),
},
Ordering::Greater => return None,
};
computed_scalar_int = Some(scalar_int);
}
Rvalue::Use(operand) => {

View File

@ -326,7 +326,7 @@ impl<'tcx> CValue<'tcx> {
let val = match layout.ty.kind() {
ty::Uint(UintTy::U128) | ty::Int(IntTy::I128) => {
let const_val = const_val.to_bits(layout.size).unwrap();
let const_val = const_val.assert_bits(layout.size);
let lsb = fx.bcx.ins().iconst(types::I64, const_val as u64 as i64);
let msb = fx.bcx.ins().iconst(types::I64, (const_val >> 64) as u64 as i64);
fx.bcx.ins().iconcat(lsb, msb)
@ -338,7 +338,7 @@ impl<'tcx> CValue<'tcx> {
| ty::Ref(..)
| ty::RawPtr(..)
| ty::FnPtr(..) => {
let raw_val = const_val.size().truncate(const_val.to_bits(layout.size).unwrap());
let raw_val = const_val.size().truncate(const_val.assert_bits(layout.size));
fx.bcx.ins().iconst(clif_ty, raw_val as i64)
}
ty::Float(FloatTy::F32) => {

View File

@ -31,7 +31,7 @@ use rustc_span::Span;
use rustc_target::abi::{
self, call::FnAbi, Align, HasDataLayout, Size, TargetDataLayout, WrappingRange,
};
use rustc_target::spec::{HasTargetSpec, Target};
use rustc_target::spec::{HasTargetSpec, HasWasmCAbiOpt, Target, WasmCAbi};
use crate::common::{type_is_pointer, SignType, TypeReflection};
use crate::context::CodegenCx;
@ -2352,6 +2352,12 @@ impl<'tcx> HasTargetSpec for Builder<'_, '_, 'tcx> {
}
}
impl<'tcx> HasWasmCAbiOpt for Builder<'_, '_, 'tcx> {
fn wasm_c_abi_opt(&self) -> WasmCAbi {
self.cx.wasm_c_abi_opt()
}
}
pub trait ToGccComp {
fn to_gcc_comparison(&self) -> ComparisonOp;
}

View File

@ -20,7 +20,7 @@ use rustc_span::{source_map::respan, Span};
use rustc_target::abi::{
call::FnAbi, HasDataLayout, PointeeInfo, Size, TargetDataLayout, VariantIdx,
};
use rustc_target::spec::{HasTargetSpec, Target, TlsModel};
use rustc_target::spec::{HasTargetSpec, HasWasmCAbiOpt, Target, TlsModel, WasmCAbi};
use crate::callee::get_fn;
use crate::common::SignType;
@ -557,6 +557,12 @@ impl<'gcc, 'tcx> HasTargetSpec for CodegenCx<'gcc, 'tcx> {
}
}
impl<'gcc, 'tcx> HasWasmCAbiOpt for CodegenCx<'gcc, 'tcx> {
fn wasm_c_abi_opt(&self) -> WasmCAbi {
self.tcx.sess.opts.unstable_opts.wasm_c_abi
}
}
impl<'gcc, 'tcx> LayoutOfHelpers<'tcx> for CodegenCx<'gcc, 'tcx> {
type LayoutOfResult = TyAndLayout<'tcx>;

View File

@ -295,8 +295,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
&niche_start_val,
)?
.to_scalar()
.try_to_int()
.unwrap();
.assert_int();
Ok(Some((tag, tag_field)))
}
}

View File

@ -6,9 +6,10 @@ use std::assert_matches::assert_matches;
use either::{Either, Left, Right};
use rustc_hir::def::Namespace;
use rustc_middle::mir::interpret::ScalarSizeMismatch;
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter};
use rustc_middle::ty::{ConstInt, Ty, TyCtxt};
use rustc_middle::ty::{ConstInt, ScalarInt, Ty, TyCtxt};
use rustc_middle::{mir, ty};
use rustc_target::abi::{self, Abi, HasDataLayout, Size};
@ -210,6 +211,12 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> {
ImmTy { imm: Immediate::Uninit, layout }
}
#[inline]
pub fn from_scalar_int(s: ScalarInt, layout: TyAndLayout<'tcx>) -> Self {
assert_eq!(s.size(), layout.size);
Self::from_scalar(Scalar::from(s), layout)
}
#[inline]
pub fn try_from_uint(i: impl Into<u128>, layout: TyAndLayout<'tcx>) -> Option<Self> {
Some(Self::from_scalar(Scalar::try_from_uint(i, layout.size)?, layout))
@ -223,7 +230,6 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> {
pub fn try_from_int(i: impl Into<i128>, layout: TyAndLayout<'tcx>) -> Option<Self> {
Some(Self::from_scalar(Scalar::try_from_int(i, layout.size)?, layout))
}
#[inline]
pub fn from_int(i: impl Into<i128>, layout: TyAndLayout<'tcx>) -> Self {
Self::from_scalar(Scalar::from_int(i, layout.size), layout)
@ -242,6 +248,20 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> {
Self::from_scalar(Scalar::from_i8(c as i8), layout)
}
/// Return the immediate as a `ScalarInt`. Ensures that it has the size that the layout of the
/// immediate indicates.
#[inline]
pub fn to_scalar_int(&self) -> InterpResult<'tcx, ScalarInt> {
let s = self.to_scalar().to_scalar_int()?;
if s.size() != self.layout.size {
throw_ub!(ScalarSizeMismatch(ScalarSizeMismatch {
target_size: self.layout.size.bytes(),
data_size: s.size().bytes(),
}));
}
Ok(s)
}
#[inline]
pub fn to_const_int(self) -> ConstInt {
assert!(self.layout.ty.is_integral());

View File

@ -2,7 +2,7 @@ use rustc_apfloat::{Float, FloatConvert};
use rustc_middle::mir;
use rustc_middle::mir::interpret::{InterpResult, Scalar};
use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
use rustc_middle::ty::{self, FloatTy, Ty};
use rustc_middle::ty::{self, FloatTy, ScalarInt, Ty};
use rustc_span::symbol::sym;
use rustc_target::abi::Abi;
@ -146,14 +146,20 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
fn binary_int_op(
&self,
bin_op: mir::BinOp,
// passing in raw bits
l: u128,
left_layout: TyAndLayout<'tcx>,
r: u128,
right_layout: TyAndLayout<'tcx>,
left: &ImmTy<'tcx, M::Provenance>,
right: &ImmTy<'tcx, M::Provenance>,
) -> InterpResult<'tcx, (ImmTy<'tcx, M::Provenance>, bool)> {
use rustc_middle::mir::BinOp::*;
// This checks the size, so that we can just assert it below.
let l = left.to_scalar_int()?;
let r = right.to_scalar_int()?;
// Prepare to convert the values to signed or unsigned form.
let l_signed = || l.assert_int(left.layout.size);
let l_unsigned = || l.assert_uint(left.layout.size);
let r_signed = || r.assert_int(right.layout.size);
let r_unsigned = || r.assert_uint(right.layout.size);
let throw_ub_on_overflow = match bin_op {
AddUnchecked => Some(sym::unchecked_add),
SubUnchecked => Some(sym::unchecked_sub),
@ -165,69 +171,72 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
// Shift ops can have an RHS with a different numeric type.
if matches!(bin_op, Shl | ShlUnchecked | Shr | ShrUnchecked) {
let size = left_layout.size.bits();
let size = left.layout.size.bits();
// The shift offset is implicitly masked to the type size. (This is the one MIR operator
// that does *not* directly map to a single LLVM operation.) Compute how much we
// actually shift and whether there was an overflow due to shifting too much.
let (shift_amount, overflow) = if right_layout.abi.is_signed() {
let shift_amount = self.sign_extend(r, right_layout) as i128;
let (shift_amount, overflow) = if right.layout.abi.is_signed() {
let shift_amount = r_signed();
let overflow = shift_amount < 0 || shift_amount >= i128::from(size);
// Deliberately wrapping `as` casts: shift_amount *can* be negative, but the result
// of the `as` will be equal modulo `size` (since it is a power of two).
let masked_amount = (shift_amount as u128) % u128::from(size);
debug_assert_eq!(overflow, shift_amount != (masked_amount as i128));
assert_eq!(overflow, shift_amount != (masked_amount as i128));
(masked_amount, overflow)
} else {
let shift_amount = r;
let shift_amount = r_unsigned();
let masked_amount = shift_amount % u128::from(size);
(masked_amount, shift_amount != masked_amount)
};
let shift_amount = u32::try_from(shift_amount).unwrap(); // we masked so this will always fit
// Compute the shifted result.
let result = if left_layout.abi.is_signed() {
let l = self.sign_extend(l, left_layout) as i128;
let result = if left.layout.abi.is_signed() {
let l = l_signed();
let result = match bin_op {
Shl | ShlUnchecked => l.checked_shl(shift_amount).unwrap(),
Shr | ShrUnchecked => l.checked_shr(shift_amount).unwrap(),
_ => bug!(),
};
result as u128
ScalarInt::truncate_from_int(result, left.layout.size).0
} else {
match bin_op {
let l = l_unsigned();
let result = match bin_op {
Shl | ShlUnchecked => l.checked_shl(shift_amount).unwrap(),
Shr | ShrUnchecked => l.checked_shr(shift_amount).unwrap(),
_ => bug!(),
}
};
ScalarInt::truncate_from_uint(result, left.layout.size).0
};
let truncated = self.truncate(result, left_layout);
if overflow && let Some(intrinsic_name) = throw_ub_on_overflow {
throw_ub_custom!(
fluent::const_eval_overflow_shift,
val = if right_layout.abi.is_signed() {
(self.sign_extend(r, right_layout) as i128).to_string()
val = if right.layout.abi.is_signed() {
r_signed().to_string()
} else {
r.to_string()
r_unsigned().to_string()
},
name = intrinsic_name
);
}
return Ok((ImmTy::from_uint(truncated, left_layout), overflow));
return Ok((ImmTy::from_scalar_int(result, left.layout), overflow));
}
// For the remaining ops, the types must be the same on both sides
if left_layout.ty != right_layout.ty {
if left.layout.ty != right.layout.ty {
span_bug!(
self.cur_span(),
"invalid asymmetric binary op {bin_op:?}: {l:?} ({l_ty}), {r:?} ({r_ty})",
l_ty = left_layout.ty,
r_ty = right_layout.ty,
l_ty = left.layout.ty,
r_ty = right.layout.ty,
)
}
let size = left_layout.size;
let size = left.layout.size;
// Operations that need special treatment for signed integers
if left_layout.abi.is_signed() {
if left.layout.abi.is_signed() {
let op: Option<fn(&i128, &i128) -> bool> = match bin_op {
Lt => Some(i128::lt),
Le => Some(i128::le),
@ -236,18 +245,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
_ => None,
};
if let Some(op) = op {
let l = self.sign_extend(l, left_layout) as i128;
let r = self.sign_extend(r, right_layout) as i128;
return Ok((ImmTy::from_bool(op(&l, &r), *self.tcx), false));
return Ok((ImmTy::from_bool(op(&l_signed(), &r_signed()), *self.tcx), false));
}
if bin_op == Cmp {
let l = self.sign_extend(l, left_layout) as i128;
let r = self.sign_extend(r, right_layout) as i128;
return Ok(self.three_way_compare(l, r));
return Ok(self.three_way_compare(l_signed(), r_signed()));
}
let op: Option<fn(i128, i128) -> (i128, bool)> = match bin_op {
Div if r == 0 => throw_ub!(DivisionByZero),
Rem if r == 0 => throw_ub!(RemainderByZero),
Div if r.is_null() => throw_ub!(DivisionByZero),
Rem if r.is_null() => throw_ub!(RemainderByZero),
Div => Some(i128::overflowing_div),
Rem => Some(i128::overflowing_rem),
Add | AddUnchecked => Some(i128::overflowing_add),
@ -256,8 +261,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
_ => None,
};
if let Some(op) = op {
let l = self.sign_extend(l, left_layout) as i128;
let r = self.sign_extend(r, right_layout) as i128;
let l = l_signed();
let r = r_signed();
// We need a special check for overflowing Rem and Div since they are *UB*
// on overflow, which can happen with "int_min $OP -1".
@ -272,17 +277,19 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
}
let (result, oflo) = op(l, r);
// This may be out-of-bounds for the result type, so we have to truncate ourselves.
// This may be out-of-bounds for the result type, so we have to truncate.
// If that truncation loses any information, we have an overflow.
let result = result as u128;
let truncated = self.truncate(result, left_layout);
let overflow = oflo || self.sign_extend(truncated, left_layout) != result;
let (result, lossy) = ScalarInt::truncate_from_int(result, left.layout.size);
let overflow = oflo || lossy;
if overflow && let Some(intrinsic_name) = throw_ub_on_overflow {
throw_ub_custom!(fluent::const_eval_overflow, name = intrinsic_name);
}
return Ok((ImmTy::from_uint(truncated, left_layout), overflow));
return Ok((ImmTy::from_scalar_int(result, left.layout), overflow));
}
}
// From here on it's okay to treat everything as unsigned.
let l = l_unsigned();
let r = r_unsigned();
if bin_op == Cmp {
return Ok(self.three_way_compare(l, r));
@ -297,12 +304,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
Gt => ImmTy::from_bool(l > r, *self.tcx),
Ge => ImmTy::from_bool(l >= r, *self.tcx),
BitOr => ImmTy::from_uint(l | r, left_layout),
BitAnd => ImmTy::from_uint(l & r, left_layout),
BitXor => ImmTy::from_uint(l ^ r, left_layout),
BitOr => ImmTy::from_uint(l | r, left.layout),
BitAnd => ImmTy::from_uint(l & r, left.layout),
BitXor => ImmTy::from_uint(l ^ r, left.layout),
Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Rem | Div => {
assert!(!left_layout.abi.is_signed());
assert!(!left.layout.abi.is_signed());
let op: fn(u128, u128) -> (u128, bool) = match bin_op {
Add | AddUnchecked => u128::overflowing_add,
Sub | SubUnchecked => u128::overflowing_sub,
@ -316,21 +323,21 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let (result, oflo) = op(l, r);
// Truncate to target type.
// If that truncation loses any information, we have an overflow.
let truncated = self.truncate(result, left_layout);
let overflow = oflo || truncated != result;
let (result, lossy) = ScalarInt::truncate_from_uint(result, left.layout.size);
let overflow = oflo || lossy;
if overflow && let Some(intrinsic_name) = throw_ub_on_overflow {
throw_ub_custom!(fluent::const_eval_overflow, name = intrinsic_name);
}
return Ok((ImmTy::from_uint(truncated, left_layout), overflow));
return Ok((ImmTy::from_scalar_int(result, left.layout), overflow));
}
_ => span_bug!(
self.cur_span(),
"invalid binary op {:?}: {:?}, {:?} (both {})",
bin_op,
l,
r,
right_layout.ty,
left,
right,
right.layout.ty,
),
};
@ -427,9 +434,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
right.layout.ty
);
let l = left.to_scalar().to_bits(left.layout.size)?;
let r = right.to_scalar().to_bits(right.layout.size)?;
self.binary_int_op(bin_op, l, left.layout, r, right.layout)
self.binary_int_op(bin_op, left, right)
}
_ if left.layout.ty.is_any_ptr() => {
// The RHS type must be a `pointer` *or an integer type* (for `Offset`).

View File

@ -70,21 +70,21 @@ pub fn reverse_post_order<G: DirectedGraph + Successors>(
}
/// A "depth-first search" iterator for a directed graph.
pub struct DepthFirstSearch<'graph, G>
pub struct DepthFirstSearch<G>
where
G: ?Sized + DirectedGraph + Successors,
G: DirectedGraph + Successors,
{
graph: &'graph G,
graph: G,
stack: Vec<G::Node>,
visited: BitSet<G::Node>,
}
impl<'graph, G> DepthFirstSearch<'graph, G>
impl<G> DepthFirstSearch<G>
where
G: ?Sized + DirectedGraph + Successors,
G: DirectedGraph + Successors,
{
pub fn new(graph: &'graph G) -> Self {
Self { graph, stack: vec![], visited: BitSet::new_empty(graph.num_nodes()) }
pub fn new(graph: G) -> Self {
Self { stack: vec![], visited: BitSet::new_empty(graph.num_nodes()), graph }
}
/// Version of `push_start_node` that is convenient for chained
@ -125,9 +125,9 @@ where
}
}
impl<G> std::fmt::Debug for DepthFirstSearch<'_, G>
impl<G> std::fmt::Debug for DepthFirstSearch<G>
where
G: ?Sized + DirectedGraph + Successors,
G: DirectedGraph + Successors,
{
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut f = fmt.debug_set();
@ -138,9 +138,9 @@ where
}
}
impl<G> Iterator for DepthFirstSearch<'_, G>
impl<G> Iterator for DepthFirstSearch<G>
where
G: ?Sized + DirectedGraph + Successors,
G: DirectedGraph + Successors,
{
type Item = G::Node;

View File

@ -46,9 +46,35 @@ where
.is_some()
}
pub fn depth_first_search<G>(graph: &G, from: G::Node) -> iterate::DepthFirstSearch<'_, G>
pub fn depth_first_search<G>(graph: G, from: G::Node) -> iterate::DepthFirstSearch<G>
where
G: ?Sized + Successors,
G: Successors,
{
iterate::DepthFirstSearch::new(graph).with_start_node(from)
}
pub fn depth_first_search_as_undirected<G>(
graph: G,
from: G::Node,
) -> iterate::DepthFirstSearch<impl Successors<Node = G::Node>>
where
G: Successors + Predecessors,
{
struct AsUndirected<G>(G);
impl<G: DirectedGraph> DirectedGraph for AsUndirected<G> {
type Node = G::Node;
fn num_nodes(&self) -> usize {
self.0.num_nodes()
}
}
impl<G: Successors + Predecessors> Successors for AsUndirected<G> {
fn successors(&self, node: Self::Node) -> impl Iterator<Item = Self::Node> {
self.0.successors(node).chain(self.0.predecessors(node))
}
}
iterate::DepthFirstSearch::new(AsUndirected(graph)).with_start_node(from)
}

View File

@ -1,99 +1,235 @@
use crate::graph::{DirectedGraph, NumEdges, Successors};
use crate::graph::{DirectedGraph, NumEdges, Predecessors, Successors};
use rustc_index::{Idx, IndexVec};
#[cfg(test)]
mod tests;
pub struct VecGraph<N: Idx> {
/// Maps from a given node to an index where the set of successors
/// for that node starts. The index indexes into the `edges`
/// vector. To find the range for a given node, we look up the
/// start for that node and then the start for the next node
/// (i.e., with an index 1 higher) and get the range between the
/// two. This vector always has an extra entry so that this works
/// even for the max element.
/// A directed graph, efficient for cases where node indices are pre-existing.
///
/// If `BR` is true, the graph will store back-references, allowing you to get predecessors.
pub struct VecGraph<N: Idx, const BR: bool = false> {
// This is basically a `HashMap<N, (Vec<N>, If<BR, Vec<N>>)>` -- a map from a node index, to
// a list of targets of outgoing edges and (if enabled) a list of sources of incoming edges.
//
// However, it is condensed into two arrays as an optimization.
//
// `node_starts[n]` is the start of the list of targets of outgoing edges for node `n`.
// So you can get node's successors with `edge_targets[node_starts[n]..node_starts[n + 1]]`.
//
// If `BR` is true (back references are enabled), then `node_starts[n + edge_count]` is the
// start of the list of *sources* of incoming edges. You can get predecessors of a node
// similarly to its successors but offsetting by `edge_count`. `edge_count` is
// `edge_targets.len()/2` (again, in case BR is true) because half of the vec is back refs.
//
// All of this might be confusing, so here is an example graph and its representation:
//
// n3 ----+
// ^ | (if BR = true)
// | v outgoing edges incoming edges
// n0 -> n1 -> n2 ______________ __________________
// / \ / \
// node indices[1]: n0, n1, n2, n3, n0, n1, n2, n3, n/a
// vec indices: n0, n1, n2, n3, n4, n5, n6, n7, n8
// node_starts: [0, 1, 3, 4 4, 4, 5, 7, 8]
// | | | | | | | | |
// | | +---+ +---+ | +---+ |
// | | | | | | |
// v v v v v v v
// edge_targets: [n1, n2, n3, n2 n0, n1, n3, n1]
// / \____/ | | \____/ \
// n0->n1 / | | \ n3<-n1
// / n3->n2 [2] n1<-n0 [2] \
// n1->n2, n1->n3 n2<-n1, n2<-n3
//
// The incoming edges are basically stored in the same way as outgoing edges, but offset and
// the graph they store is the inverse of the original. Last index in the `node_starts` array
// always points to one-past-the-end, so that we don't need to bound check `node_starts[n + 1]`
//
// [1]: "node indices" are the indices a user of `VecGraph` might use,
// note that they are different from "vec indices",
// which are the real indices you need to index `node_starts`
//
// [2]: Note that even though n2 also points to here,
// the next index also points here, so n2 has no
// successors (`edge_targets[3..3] = []`).
// Similarly with n0 and incoming edges
//
// If this is still confusing... then sorry :(
//
/// Indices into `edge_targets` that signify a start of list of edges.
node_starts: IndexVec<N, usize>,
/// Targets (or sources for back refs) of edges
edge_targets: Vec<N>,
}
impl<N: Idx + Ord> VecGraph<N> {
impl<N: Idx + Ord, const BR: bool> VecGraph<N, BR> {
pub fn new(num_nodes: usize, mut edge_pairs: Vec<(N, N)>) -> Self {
let num_edges = edge_pairs.len();
let nodes_cap = match BR {
// +1 for special entry at the end, pointing one past the end of `edge_targets`
false => num_nodes + 1,
// *2 for back references
true => (num_nodes * 2) + 1,
};
let edges_cap = match BR {
false => num_edges,
// *2 for back references
true => num_edges * 2,
};
let mut node_starts = IndexVec::with_capacity(nodes_cap);
let mut edge_targets = Vec::with_capacity(edges_cap);
// Sort the edges by the source -- this is important.
edge_pairs.sort();
let num_edges = edge_pairs.len();
// Fill forward references
create_index(
num_nodes,
&mut edge_pairs.iter().map(|&(src, _)| src),
&mut edge_pairs.iter().map(|&(_, tgt)| tgt),
&mut edge_targets,
&mut node_starts,
);
// Store the *target* of each edge into `edge_targets`.
let edge_targets: Vec<N> = edge_pairs.iter().map(|&(_, target)| target).collect();
// Fill back references
if BR {
// Pop the special "last" entry, it will be replaced by first back ref
node_starts.pop();
// Create the *edge starts* array. We are iterating over the
// (sorted) edge pairs. We maintain the invariant that the
// length of the `node_starts` array is enough to store the
// current source node -- so when we see that the source node
// for an edge is greater than the current length, we grow the
// edge-starts array by just enough.
let mut node_starts = IndexVec::with_capacity(num_edges);
for (index, &(source, _)) in edge_pairs.iter().enumerate() {
// If we have a list like `[(0, x), (2, y)]`:
//
// - Start out with `node_starts` of `[]`
// - Iterate to `(0, x)` at index 0:
// - Push one entry because `node_starts.len()` (0) is <= the source (0)
// - Leaving us with `node_starts` of `[0]`
// - Iterate to `(2, y)` at index 1:
// - Push one entry because `node_starts.len()` (1) is <= the source (2)
// - Push one entry because `node_starts.len()` (2) is <= the source (2)
// - Leaving us with `node_starts` of `[0, 1, 1]`
// - Loop terminates
while node_starts.len() <= source.index() {
node_starts.push(index);
}
// Re-sort the edges so that they are sorted by target
edge_pairs.sort_by_key(|&(src, tgt)| (tgt, src));
create_index(
// Back essentially double the number of nodes
num_nodes * 2,
// NB: the source/target are switched here too
// NB: we double the key index, so that we can later use *2 to get the back references
&mut edge_pairs.iter().map(|&(_, tgt)| N::new(tgt.index() + num_nodes)),
&mut edge_pairs.iter().map(|&(src, _)| src),
&mut edge_targets,
&mut node_starts,
);
}
// Pad out the `node_starts` array so that it has `num_nodes +
// 1` entries. Continuing our example above, if `num_nodes` is
// be `3`, we would push one more index: `[0, 1, 1, 2]`.
//
// Interpretation of that vector:
//
// [0, 1, 1, 2]
// ---- range for N=2
// ---- range for N=1
// ---- range for N=0
while node_starts.len() <= num_nodes {
node_starts.push(edge_targets.len());
}
assert_eq!(node_starts.len(), num_nodes + 1);
Self { node_starts, edge_targets }
}
/// Gets the successors for `source` as a slice.
pub fn successors(&self, source: N) -> &[N] {
assert!(source.index() < self.num_nodes());
let start_index = self.node_starts[source];
let end_index = self.node_starts[source.plus(1)];
&self.edge_targets[start_index..end_index]
}
}
impl<N: Idx> DirectedGraph for VecGraph<N> {
impl<N: Idx + Ord> VecGraph<N, true> {
/// Gets the predecessors for `target` as a slice.
pub fn predecessors(&self, target: N) -> &[N] {
assert!(target.index() < self.num_nodes());
let target = N::new(target.index() + self.num_nodes());
let start_index = self.node_starts[target];
let end_index = self.node_starts[target.plus(1)];
&self.edge_targets[start_index..end_index]
}
}
/// Creates/initializes the index for the [`VecGraph`]. A helper for [`VecGraph::new`].
///
/// - `num_nodes` is the target number of nodes in the graph
/// - `sorted_edge_sources` are the edge sources, sorted
/// - `associated_edge_targets` are the edge *targets* in the same order as sources
/// - `edge_targets` is the vec of targets to be extended
/// - `node_starts` is the index to be filled
fn create_index<N: Idx + Ord>(
num_nodes: usize,
sorted_edge_sources: &mut dyn Iterator<Item = N>,
associated_edge_targets: &mut dyn Iterator<Item = N>,
edge_targets: &mut Vec<N>,
node_starts: &mut IndexVec<N, usize>,
) {
let offset = edge_targets.len();
// Store the *target* of each edge into `edge_targets`.
edge_targets.extend(associated_edge_targets);
// Create the *edge starts* array. We are iterating over the
// (sorted) edge pairs. We maintain the invariant that the
// length of the `node_starts` array is enough to store the
// current source node -- so when we see that the source node
// for an edge is greater than the current length, we grow the
// edge-starts array by just enough.
for (index, source) in sorted_edge_sources.enumerate() {
// If we have a list like `[(0, x), (2, y)]`:
//
// - Start out with `node_starts` of `[]`
// - Iterate to `(0, x)` at index 0:
// - Push one entry because `node_starts.len()` (0) is <= the source (0)
// - Leaving us with `node_starts` of `[0]`
// - Iterate to `(2, y)` at index 1:
// - Push one entry because `node_starts.len()` (1) is <= the source (2)
// - Push one entry because `node_starts.len()` (2) is <= the source (2)
// - Leaving us with `node_starts` of `[0, 1, 1]`
// - Loop terminates
while node_starts.len() <= source.index() {
node_starts.push(index + offset);
}
}
// Pad out the `node_starts` array so that it has `num_nodes +
// 1` entries. Continuing our example above, if `num_nodes` is
// be `3`, we would push one more index: `[0, 1, 1, 2]`.
//
// Interpretation of that vector:
//
// [0, 1, 1, 2]
// ---- range for N=2
// ---- range for N=1
// ---- range for N=0
while node_starts.len() <= num_nodes {
node_starts.push(edge_targets.len());
}
assert_eq!(node_starts.len(), num_nodes + 1);
}
impl<N: Idx, const BR: bool> DirectedGraph for VecGraph<N, BR> {
type Node = N;
fn num_nodes(&self) -> usize {
self.node_starts.len() - 1
match BR {
false => self.node_starts.len() - 1,
// If back refs are enabled, half of the array is said back refs
true => (self.node_starts.len() - 1) / 2,
}
}
}
impl<N: Idx> NumEdges for VecGraph<N> {
impl<N: Idx, const BR: bool> NumEdges for VecGraph<N, BR> {
fn num_edges(&self) -> usize {
self.edge_targets.len()
match BR {
false => self.edge_targets.len(),
// If back refs are enabled, half of the array is reversed edges for them
true => self.edge_targets.len() / 2,
}
}
}
impl<N: Idx + Ord> Successors for VecGraph<N> {
impl<N: Idx + Ord, const BR: bool> Successors for VecGraph<N, BR> {
fn successors(&self, node: N) -> impl Iterator<Item = Self::Node> {
self.successors(node).iter().cloned()
}
}
impl<N: Idx + Ord> Predecessors for VecGraph<N, true> {
fn predecessors(&self, node: Self::Node) -> impl Iterator<Item = Self::Node> {
self.predecessors(node).iter().cloned()
}
}

View File

@ -18,10 +18,18 @@ fn create_graph() -> VecGraph<usize> {
VecGraph::new(7, vec![(0, 1), (1, 2), (1, 3), (3, 4), (5, 1)])
}
fn create_graph_with_back_refs() -> VecGraph<usize, true> {
// Same as above
VecGraph::new(7, vec![(0, 1), (1, 2), (1, 3), (3, 4), (5, 1)])
}
#[test]
fn num_nodes() {
let graph = create_graph();
assert_eq!(graph.num_nodes(), 7);
let graph = create_graph_with_back_refs();
assert_eq!(graph.num_nodes(), 7);
}
#[test]
@ -34,6 +42,27 @@ fn successors() {
assert_eq!(graph.successors(4), &[] as &[usize]);
assert_eq!(graph.successors(5), &[1]);
assert_eq!(graph.successors(6), &[] as &[usize]);
let graph = create_graph_with_back_refs();
assert_eq!(graph.successors(0), &[1]);
assert_eq!(graph.successors(1), &[2, 3]);
assert_eq!(graph.successors(2), &[] as &[usize]);
assert_eq!(graph.successors(3), &[4]);
assert_eq!(graph.successors(4), &[] as &[usize]);
assert_eq!(graph.successors(5), &[1]);
assert_eq!(graph.successors(6), &[] as &[usize]);
}
#[test]
fn predecessors() {
let graph = create_graph_with_back_refs();
assert_eq!(graph.predecessors(0), &[]);
assert_eq!(graph.predecessors(1), &[0, 5]);
assert_eq!(graph.predecessors(2), &[1]);
assert_eq!(graph.predecessors(3), &[1]);
assert_eq!(graph.predecessors(4), &[3]);
assert_eq!(graph.predecessors(5), &[]);
assert_eq!(graph.predecessors(6), &[]);
}
#[test]
@ -41,4 +70,8 @@ fn dfs() {
let graph = create_graph();
let dfs: Vec<_> = graph::depth_first_search(&graph, 0).collect();
assert_eq!(dfs, vec![0, 1, 3, 4, 2]);
let graph = create_graph_with_back_refs();
let dfs: Vec<_> = graph::depth_first_search(&graph, 0).collect();
assert_eq!(dfs, vec![0, 1, 3, 4, 2]);
}

View File

@ -195,16 +195,19 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
}
Some(fn_def_id.to_def_id())
}
ItemKind::OpaqueTy(hir::OpaqueTy {
origin: hir::OpaqueTyOrigin::TyAlias { .. },
ItemKind::OpaqueTy(&hir::OpaqueTy {
origin: hir::OpaqueTyOrigin::TyAlias { parent, in_assoc_ty },
..
}) => {
let parent_id = tcx.hir().get_parent_item(hir_id);
assert_ne!(parent_id, hir::CRATE_OWNER_ID);
debug!("generics_of: parent of opaque ty {:?} is {:?}", def_id, parent_id);
if in_assoc_ty {
assert!(matches!(tcx.def_kind(parent), DefKind::AssocTy));
} else {
assert!(matches!(tcx.def_kind(parent), DefKind::TyAlias));
}
debug!("generics_of: parent of opaque ty {:?} is {:?}", def_id, parent);
// Opaque types are always nested within another item, and
// inherit the generics of the item.
Some(parent_id.to_def_id())
Some(parent.to_def_id())
}
_ => None,
},

View File

@ -279,13 +279,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
Entry::Occupied(mut entry) => {
debug!(" - composing on top of {:?}", entry.get());
match (&entry.get()[..], &adj[..]) {
// Applying any adjustment on top of a NeverToAny
// is a valid NeverToAny adjustment, because it can't
// be reached.
(&[Adjustment { kind: Adjust::NeverToAny, .. }], _) => return,
match (&mut entry.get_mut()[..], &adj[..]) {
(
&[
[Adjustment { kind: Adjust::NeverToAny, target }],
&[.., Adjustment { target: new_target, .. }],
) => {
// NeverToAny coercion can target any type, so instead of adding a new
// adjustment on top we can change the target.
//
// This is required for things like `a == a` (where `a: !`) to produce
// valid MIR -- we need borrow adjustment from things like `==` to change
// the type to `&!` (or `&()` depending on the fallback). This might be
// relevant even in unreachable code.
*target = new_target;
}
(
&mut [
Adjustment { kind: Adjust::Deref(_), .. },
Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), .. },
],
@ -294,11 +304,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.., // Any following adjustments are allowed.
],
) => {
// A reborrow has no effect before a dereference.
// A reborrow has no effect before a dereference, so we can safely replace adjustments.
*entry.get_mut() = adj;
}
// FIXME: currently we never try to compose autoderefs
// and ReifyFnPointer/UnsafeFnPointer, but we could.
_ => {
// FIXME: currently we never try to compose autoderefs
// and ReifyFnPointer/UnsafeFnPointer, but we could.
self.dcx().span_delayed_bug(
expr.span,
format!(
@ -308,9 +320,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
adj
),
);
*entry.get_mut() = adj;
}
}
*entry.get_mut() = adj;
}
}

View File

@ -382,7 +382,7 @@ impl<'tcx> MiniGraph<'tcx> {
edges.push((source_node, target_node));
},
);
let graph = VecGraph::new(nodes.len(), edges);
let graph = VecGraph::<_, false>::new(nodes.len(), edges);
let sccs = Sccs::new(&graph);
Self { nodes, sccs }
}

View File

@ -19,7 +19,9 @@ use rustc_span::edition::{Edition, DEFAULT_EDITION};
use rustc_span::source_map::{RealFileLoader, SourceMapInputs};
use rustc_span::symbol::sym;
use rustc_span::{FileName, SourceFileHashAlgorithm};
use rustc_target::spec::{CodeModel, LinkerFlavorCli, MergeFunctions, PanicStrategy, RelocModel};
use rustc_target::spec::{
CodeModel, LinkerFlavorCli, MergeFunctions, PanicStrategy, RelocModel, WasmCAbi,
};
use rustc_target::spec::{RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, TlsModel};
use std::collections::{BTreeMap, BTreeSet};
use std::num::NonZero;
@ -851,6 +853,7 @@ fn test_unstable_options_tracking_hash() {
tracked!(verify_llvm_ir, true);
tracked!(virtual_function_elimination, true);
tracked!(wasi_exec_model, Some(WasiExecModel::Reactor));
tracked!(wasm_c_abi, WasmCAbi::Spec);
// tidy-alphabetical-end
macro_rules! tracked_no_crate_hash {

View File

@ -88,6 +88,10 @@ pub enum TokenKind {
/// tokens.
UnknownPrefix,
/// Similar to the above, but *always* an error on every edition. This is used
/// for emoji identifier recovery, as those are not meant to be ever accepted.
InvalidPrefix,
/// Examples: `12u8`, `1.0e-40`, `b"123"`. Note that `_` is an invalid
/// suffix, but may be present here on string and float literals. Users of
/// this type will need to check for and reject that case.
@ -528,7 +532,7 @@ impl Cursor<'_> {
// Known prefixes must have been handled earlier. So if
// we see a prefix here, it is definitely an unknown prefix.
match self.first() {
'#' | '"' | '\'' => UnknownPrefix,
'#' | '"' | '\'' => InvalidPrefix,
_ => InvalidIdent,
}
}

View File

@ -87,7 +87,7 @@ impl<'tcx> ConstValue<'tcx> {
}
pub fn try_to_bits(&self, size: Size) -> Option<u128> {
self.try_to_scalar_int()?.to_bits(size).ok()
self.try_to_scalar_int()?.try_to_bits(size).ok()
}
pub fn try_to_bool(&self) -> Option<bool> {
@ -244,6 +244,8 @@ impl<'tcx> Const<'tcx> {
Const::Ty(c) => match c.kind() {
ty::ConstKind::Value(valtree) if c.ty().is_primitive() => {
// A valtree of a type where leaves directly represent the scalar const value.
// Just checking whether it is a leaf is insufficient as e.g. references are leafs
// but the leaf value is the value they point to, not the reference itself!
Some(valtree.unwrap_leaf().into())
}
_ => None,
@ -255,12 +257,22 @@ impl<'tcx> Const<'tcx> {
#[inline]
pub fn try_to_scalar_int(self) -> Option<ScalarInt> {
self.try_to_scalar()?.try_to_int().ok()
// This is equivalent to `self.try_to_scalar()?.try_to_int().ok()`, but measurably faster.
match self {
Const::Val(ConstValue::Scalar(Scalar::Int(x)), _) => Some(x),
Const::Ty(c) => match c.kind() {
ty::ConstKind::Value(valtree) if c.ty().is_primitive() => {
Some(valtree.unwrap_leaf())
}
_ => None,
},
_ => None,
}
}
#[inline]
pub fn try_to_bits(self, size: Size) -> Option<u128> {
self.try_to_scalar_int()?.to_bits(size).ok()
self.try_to_scalar_int()?.try_to_bits(size).ok()
}
#[inline]
@ -334,7 +346,7 @@ impl<'tcx> Const<'tcx> {
let int = self.try_eval_scalar_int(tcx, param_env)?;
let size =
tcx.layout_of(param_env.with_reveal_all_normalized(tcx).and(self.ty())).ok()?.size;
int.to_bits(size).ok()
int.try_to_bits(size).ok()
}
/// Panics if the value cannot be evaluated or doesn't contain a valid integer of the given type.

View File

@ -236,7 +236,7 @@ impl<Prov> Scalar<Prov> {
) -> Result<Either<u128, Pointer<Prov>>, ScalarSizeMismatch> {
assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST");
Ok(match self {
Scalar::Int(int) => Left(int.to_bits(target_size).map_err(|size| {
Scalar::Int(int) => Left(int.try_to_bits(target_size).map_err(|size| {
ScalarSizeMismatch { target_size: target_size.bytes(), data_size: size.bytes() }
})?),
Scalar::Ptr(ptr, sz) => {
@ -300,6 +300,11 @@ impl<'tcx, Prov: Provenance> Scalar<Prov> {
}
}
#[inline(always)]
pub fn to_scalar_int(self) -> InterpResult<'tcx, ScalarInt> {
self.try_to_int().map_err(|_| err_unsup!(ReadPointerAsInt(None)).into())
}
#[inline(always)]
#[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980)
pub fn assert_int(self) -> ScalarInt {
@ -311,16 +316,13 @@ impl<'tcx, Prov: Provenance> Scalar<Prov> {
#[inline]
pub fn to_bits(self, target_size: Size) -> InterpResult<'tcx, u128> {
assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST");
self.try_to_int()
.map_err(|_| err_unsup!(ReadPointerAsInt(None)))?
.to_bits(target_size)
.map_err(|size| {
err_ub!(ScalarSizeMismatch(ScalarSizeMismatch {
target_size: target_size.bytes(),
data_size: size.bytes(),
}))
.into()
})
self.to_scalar_int()?.try_to_bits(target_size).map_err(|size| {
err_ub!(ScalarSizeMismatch(ScalarSizeMismatch {
target_size: target_size.bytes(),
data_size: size.bytes(),
}))
.into()
})
}
#[inline(always)]

View File

@ -16,7 +16,7 @@ use rustc_hir::{BindingMode, ByRef, HirId, MatchSource, RangeEnd};
use rustc_index::newtype_index;
use rustc_index::IndexVec;
use rustc_middle::middle::region;
use rustc_middle::mir::interpret::{AllocId, Scalar};
use rustc_middle::mir::interpret::AllocId;
use rustc_middle::mir::{self, BinOp, BorrowKind, FakeReadCause, UnOp};
use rustc_middle::ty::adjustment::PointerCoercion;
use rustc_middle::ty::layout::IntegerExt;
@ -1006,18 +1006,17 @@ impl<'tcx> PatRangeBoundary<'tcx> {
// This code is hot when compiling matches with many ranges. So we
// special-case extraction of evaluated scalars for speed, for types where
// raw data comparisons are appropriate. E.g. `unicode-normalization` has
// unsigned int comparisons are appropriate. E.g. `unicode-normalization` has
// many ranges such as '\u{037A}'..='\u{037F}', and chars can be compared
// in this way.
(Finite(mir::Const::Ty(a)), Finite(mir::Const::Ty(b)))
if matches!(ty.kind(), ty::Uint(_) | ty::Char) =>
{
return Some(a.to_valtree().cmp(&b.to_valtree()));
(Finite(a), Finite(b)) if matches!(ty.kind(), ty::Uint(_) | ty::Char) => {
if let (Some(a), Some(b)) = (a.try_to_scalar_int(), b.try_to_scalar_int()) {
let sz = ty.primitive_size(tcx);
let a = a.assert_uint(sz);
let b = b.assert_uint(sz);
return Some(a.cmp(&b));
}
}
(
Finite(mir::Const::Val(mir::ConstValue::Scalar(Scalar::Int(a)), _)),
Finite(mir::Const::Val(mir::ConstValue::Scalar(Scalar::Int(b)), _)),
) if matches!(ty.kind(), ty::Uint(_) | ty::Char) => return Some(a.cmp(&b)),
_ => {}
}

View File

@ -409,7 +409,7 @@ impl<'tcx> Const<'tcx> {
let size =
tcx.layout_of(param_env.with_reveal_all_normalized(tcx).and(self.ty())).ok()?.size;
// if `ty` does not depend on generic parameters, use an empty param_env
int.to_bits(size).ok()
int.try_to_bits(size).ok()
}
#[inline]

View File

@ -126,7 +126,7 @@ impl IntoDiagArg for ConstInt {
///
/// This is a packed struct in order to allow this type to be optimally embedded in enums
/// (like Scalar).
#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[derive(Clone, Copy, Eq, PartialEq, Hash)]
#[repr(packed)]
pub struct ScalarInt {
/// The first `size` bytes of `data` are the value.
@ -167,9 +167,12 @@ impl<D: Decoder> Decodable<D> for ScalarInt {
impl ScalarInt {
pub const TRUE: ScalarInt = ScalarInt { data: 1_u128, size: NonZero::new(1).unwrap() };
pub const FALSE: ScalarInt = ScalarInt { data: 0_u128, size: NonZero::new(1).unwrap() };
fn raw(data: u128, size: Size) -> Self {
Self { data, size: NonZero::new(size.bytes() as u8).unwrap() }
}
#[inline]
pub fn size(self) -> Size {
Size::from_bytes(self.size.get())
@ -196,7 +199,7 @@ impl ScalarInt {
#[inline]
pub fn null(size: Size) -> Self {
Self { data: 0, size: NonZero::new(size.bytes() as u8).unwrap() }
Self::raw(0, size)
}
#[inline]
@ -207,11 +210,15 @@ impl ScalarInt {
#[inline]
pub fn try_from_uint(i: impl Into<u128>, size: Size) -> Option<Self> {
let data = i.into();
if size.truncate(data) == data {
Some(Self { data, size: NonZero::new(size.bytes() as u8).unwrap() })
} else {
None
}
if size.truncate(data) == data { Some(Self::raw(data, size)) } else { None }
}
/// Returns the truncated result, and whether truncation changed the value.
#[inline]
pub fn truncate_from_uint(i: impl Into<u128>, size: Size) -> (Self, bool) {
let data = i.into();
let r = Self::raw(size.truncate(data), size);
(r, r.data != data)
}
#[inline]
@ -220,26 +227,27 @@ impl ScalarInt {
// `into` performed sign extension, we have to truncate
let truncated = size.truncate(i as u128);
if size.sign_extend(truncated) as i128 == i {
Some(Self { data: truncated, size: NonZero::new(size.bytes() as u8).unwrap() })
Some(Self::raw(truncated, size))
} else {
None
}
}
/// Returns the truncated result, and whether truncation changed the value.
#[inline]
pub fn truncate_from_int(i: impl Into<i128>, size: Size) -> (Self, bool) {
let data = i.into();
let r = Self::raw(size.truncate(data as u128), size);
(r, size.sign_extend(r.data) as i128 != data)
}
#[inline]
pub fn try_from_target_usize(i: impl Into<u128>, tcx: TyCtxt<'_>) -> Option<Self> {
Self::try_from_uint(i, tcx.data_layout.pointer_size)
}
#[inline]
pub fn assert_bits(self, target_size: Size) -> u128 {
self.to_bits(target_size).unwrap_or_else(|size| {
bug!("expected int of size {}, but got size {}", target_size.bytes(), size.bytes())
})
}
#[inline]
pub fn to_bits(self, target_size: Size) -> Result<u128, Size> {
pub fn try_to_bits(self, target_size: Size) -> Result<u128, Size> {
assert_ne!(target_size.bytes(), 0, "you should never look at the bits of a ZST");
if target_size.bytes() == u64::from(self.size.get()) {
self.check_data();
@ -249,16 +257,28 @@ impl ScalarInt {
}
}
#[inline]
pub fn assert_bits(self, target_size: Size) -> u128 {
self.try_to_bits(target_size).unwrap_or_else(|size| {
bug!("expected int of size {}, but got size {}", target_size.bytes(), size.bytes())
})
}
/// Tries to convert the `ScalarInt` to an unsigned integer of the given size.
/// Fails if the size of the `ScalarInt` is not equal to `size` and returns the
/// `ScalarInt`s size in that case.
#[inline]
pub fn try_to_uint(self, size: Size) -> Result<u128, Size> {
self.to_bits(size)
self.try_to_bits(size)
}
#[inline]
pub fn assert_uint(self, size: Size) -> u128 {
self.assert_bits(size)
}
// Tries to convert the `ScalarInt` to `u8`. Fails if the `size` of the `ScalarInt`
// in not equal to `Size { raw: 1 }` and returns the `size` value of the `ScalarInt` in
// in not equal to 1 byte and returns the `size` value of the `ScalarInt` in
// that case.
#[inline]
pub fn try_to_u8(self) -> Result<u8, Size> {
@ -266,7 +286,7 @@ impl ScalarInt {
}
/// Tries to convert the `ScalarInt` to `u16`. Fails if the size of the `ScalarInt`
/// in not equal to `Size { raw: 2 }` and returns the `size` value of the `ScalarInt` in
/// in not equal to 2 bytes and returns the `size` value of the `ScalarInt` in
/// that case.
#[inline]
pub fn try_to_u16(self) -> Result<u16, Size> {
@ -274,7 +294,7 @@ impl ScalarInt {
}
/// Tries to convert the `ScalarInt` to `u32`. Fails if the `size` of the `ScalarInt`
/// in not equal to `Size { raw: 4 }` and returns the `size` value of the `ScalarInt` in
/// in not equal to 4 bytes and returns the `size` value of the `ScalarInt` in
/// that case.
#[inline]
pub fn try_to_u32(self) -> Result<u32, Size> {
@ -282,7 +302,7 @@ impl ScalarInt {
}
/// Tries to convert the `ScalarInt` to `u64`. Fails if the `size` of the `ScalarInt`
/// in not equal to `Size { raw: 8 }` and returns the `size` value of the `ScalarInt` in
/// in not equal to 8 bytes and returns the `size` value of the `ScalarInt` in
/// that case.
#[inline]
pub fn try_to_u64(self) -> Result<u64, Size> {
@ -290,7 +310,7 @@ impl ScalarInt {
}
/// Tries to convert the `ScalarInt` to `u128`. Fails if the `size` of the `ScalarInt`
/// in not equal to `Size { raw: 16 }` and returns the `size` value of the `ScalarInt` in
/// in not equal to 16 bytes and returns the `size` value of the `ScalarInt` in
/// that case.
#[inline]
pub fn try_to_u128(self) -> Result<u128, Size> {
@ -303,7 +323,7 @@ impl ScalarInt {
}
// Tries to convert the `ScalarInt` to `bool`. Fails if the `size` of the `ScalarInt`
// in not equal to `Size { raw: 1 }` or if the value is not 0 or 1 and returns the `size`
// in not equal to 1 byte or if the value is not 0 or 1 and returns the `size`
// value of the `ScalarInt` in that case.
#[inline]
pub fn try_to_bool(self) -> Result<bool, Size> {
@ -319,40 +339,46 @@ impl ScalarInt {
/// `ScalarInt`s size in that case.
#[inline]
pub fn try_to_int(self, size: Size) -> Result<i128, Size> {
let b = self.to_bits(size)?;
let b = self.try_to_bits(size)?;
Ok(size.sign_extend(b) as i128)
}
#[inline]
pub fn assert_int(self, size: Size) -> i128 {
let b = self.assert_bits(size);
size.sign_extend(b) as i128
}
/// Tries to convert the `ScalarInt` to i8.
/// Fails if the size of the `ScalarInt` is not equal to `Size { raw: 1 }`
/// Fails if the size of the `ScalarInt` is not equal to 1 byte
/// and returns the `ScalarInt`s size in that case.
pub fn try_to_i8(self) -> Result<i8, Size> {
self.try_to_int(Size::from_bits(8)).map(|v| i8::try_from(v).unwrap())
}
/// Tries to convert the `ScalarInt` to i16.
/// Fails if the size of the `ScalarInt` is not equal to `Size { raw: 2 }`
/// Fails if the size of the `ScalarInt` is not equal to 2 bytes
/// and returns the `ScalarInt`s size in that case.
pub fn try_to_i16(self) -> Result<i16, Size> {
self.try_to_int(Size::from_bits(16)).map(|v| i16::try_from(v).unwrap())
}
/// Tries to convert the `ScalarInt` to i32.
/// Fails if the size of the `ScalarInt` is not equal to `Size { raw: 4 }`
/// Fails if the size of the `ScalarInt` is not equal to 4 bytes
/// and returns the `ScalarInt`s size in that case.
pub fn try_to_i32(self) -> Result<i32, Size> {
self.try_to_int(Size::from_bits(32)).map(|v| i32::try_from(v).unwrap())
}
/// Tries to convert the `ScalarInt` to i64.
/// Fails if the size of the `ScalarInt` is not equal to `Size { raw: 8 }`
/// Fails if the size of the `ScalarInt` is not equal to 8 bytes
/// and returns the `ScalarInt`s size in that case.
pub fn try_to_i64(self) -> Result<i64, Size> {
self.try_to_int(Size::from_bits(64)).map(|v| i64::try_from(v).unwrap())
}
/// Tries to convert the `ScalarInt` to i128.
/// Fails if the size of the `ScalarInt` is not equal to `Size { raw: 16 }`
/// Fails if the size of the `ScalarInt` is not equal to 16 bytes
/// and returns the `ScalarInt`s size in that case.
pub fn try_to_i128(self) -> Result<i128, Size> {
self.try_to_int(Size::from_bits(128))
@ -366,7 +392,7 @@ impl ScalarInt {
#[inline]
pub fn try_to_float<F: Float>(self) -> Result<F, Size> {
// Going through `to_uint` to check size and truncation.
Ok(F::from_bits(self.to_bits(Size::from_bits(F::BITS))?))
Ok(F::from_bits(self.try_to_bits(Size::from_bits(F::BITS))?))
}
#[inline]
@ -415,7 +441,7 @@ macro_rules! try_from {
fn try_from(int: ScalarInt) -> Result<Self, Size> {
// The `unwrap` cannot fail because to_bits (if it succeeds)
// is guaranteed to return a value that fits into the size.
int.to_bits(Size::from_bytes(std::mem::size_of::<$ty>()))
int.try_to_bits(Size::from_bytes(std::mem::size_of::<$ty>()))
.map(|u| u.try_into().unwrap())
}
}
@ -450,7 +476,7 @@ impl TryFrom<ScalarInt> for char {
#[inline]
fn try_from(int: ScalarInt) -> Result<Self, Self::Error> {
let Ok(bits) = int.to_bits(Size::from_bytes(std::mem::size_of::<char>())) else {
let Ok(bits) = int.try_to_bits(Size::from_bytes(std::mem::size_of::<char>())) else {
return Err(CharTryFromScalarInt);
};
match char::from_u32(bits.try_into().unwrap()) {
@ -472,7 +498,7 @@ impl TryFrom<ScalarInt> for Half {
type Error = Size;
#[inline]
fn try_from(int: ScalarInt) -> Result<Self, Size> {
int.to_bits(Size::from_bytes(2)).map(Self::from_bits)
int.try_to_bits(Size::from_bytes(2)).map(Self::from_bits)
}
}
@ -488,7 +514,7 @@ impl TryFrom<ScalarInt> for Single {
type Error = Size;
#[inline]
fn try_from(int: ScalarInt) -> Result<Self, Size> {
int.to_bits(Size::from_bytes(4)).map(Self::from_bits)
int.try_to_bits(Size::from_bytes(4)).map(Self::from_bits)
}
}
@ -504,7 +530,7 @@ impl TryFrom<ScalarInt> for Double {
type Error = Size;
#[inline]
fn try_from(int: ScalarInt) -> Result<Self, Size> {
int.to_bits(Size::from_bytes(8)).map(Self::from_bits)
int.try_to_bits(Size::from_bytes(8)).map(Self::from_bits)
}
}
@ -520,7 +546,7 @@ impl TryFrom<ScalarInt> for Quad {
type Error = Size;
#[inline]
fn try_from(int: ScalarInt) -> Result<Self, Size> {
int.to_bits(Size::from_bytes(16)).map(Self::from_bits)
int.try_to_bits(Size::from_bytes(16)).map(Self::from_bits)
}
}

View File

@ -3,7 +3,7 @@ use crate::mir::interpret::Scalar;
use crate::ty::{self, Ty, TyCtxt};
use rustc_macros::{HashStable, TyDecodable, TyEncodable};
#[derive(Copy, Clone, Debug, Hash, TyEncodable, TyDecodable, Eq, PartialEq, Ord, PartialOrd)]
#[derive(Copy, Clone, Debug, Hash, TyEncodable, TyDecodable, Eq, PartialEq)]
#[derive(HashStable)]
/// This datastructure is used to represent the value of constants used in the type system.
///

View File

@ -15,7 +15,9 @@ use rustc_span::symbol::{sym, Symbol};
use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP};
use rustc_target::abi::call::FnAbi;
use rustc_target::abi::*;
use rustc_target::spec::{abi::Abi as SpecAbi, HasTargetSpec, PanicStrategy, Target};
use rustc_target::spec::{
abi::Abi as SpecAbi, HasTargetSpec, HasWasmCAbiOpt, PanicStrategy, Target, WasmCAbi,
};
use std::borrow::Cow;
use std::cmp;
@ -483,6 +485,12 @@ impl<'tcx> HasTargetSpec for TyCtxt<'tcx> {
}
}
impl<'tcx> HasWasmCAbiOpt for TyCtxt<'tcx> {
fn wasm_c_abi_opt(&self) -> WasmCAbi {
self.sess.opts.unstable_opts.wasm_c_abi
}
}
impl<'tcx> HasTyCtxt<'tcx> for TyCtxt<'tcx> {
#[inline]
fn tcx(&self) -> TyCtxt<'tcx> {
@ -528,6 +536,12 @@ impl<'tcx, T: HasTargetSpec> HasTargetSpec for LayoutCx<'tcx, T> {
}
}
impl<'tcx, T: HasWasmCAbiOpt> HasWasmCAbiOpt for LayoutCx<'tcx, T> {
fn wasm_c_abi_opt(&self) -> WasmCAbi {
self.tcx.wasm_c_abi_opt()
}
}
impl<'tcx, T: HasTyCtxt<'tcx>> HasTyCtxt<'tcx> for LayoutCx<'tcx, T> {
fn tcx(&self) -> TyCtxt<'tcx> {
self.tcx.tcx()

View File

@ -1023,7 +1023,13 @@ pub(crate) fn parse_float_into_scalar(
let num = num.as_str();
match float_ty {
// FIXME(f16_f128): When available, compare to the library parser as with `f32` and `f64`
ty::FloatTy::F16 => num.parse::<Half>().ok().map(Scalar::from_f16),
ty::FloatTy::F16 => {
let mut f = num.parse::<Half>().ok()?;
if neg {
f = -f;
}
Some(Scalar::from_f16(f))
}
ty::FloatTy::F32 => {
let Ok(rust_f) = num.parse::<f32>() else { return None };
let mut f = num
@ -1071,7 +1077,13 @@ pub(crate) fn parse_float_into_scalar(
Some(Scalar::from_f64(f))
}
// FIXME(f16_f128): When available, compare to the library parser as with `f32` and `f64`
ty::FloatTy::F128 => num.parse::<Quad>().ok().map(Scalar::from_f128),
ty::FloatTy::F128 => {
let mut f = num.parse::<Quad>().ok()?;
if neg {
f = -f;
}
Some(Scalar::from_f128(f))
}
}
}

View File

@ -796,7 +796,7 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> {
if let Some(ref value) = self.eval_operand(discr)
&& let Some(value_const) = self.use_ecx(|this| this.ecx.read_scalar(value))
&& let Ok(constant) = value_const.try_to_int()
&& let Ok(constant) = constant.to_bits(constant.size())
&& let Ok(constant) = constant.try_to_bits(constant.size())
{
// We managed to evaluate the discriminant, so we know we only need to visit
// one target.

View File

@ -369,8 +369,7 @@ impl<'tcx> SimplifyMatch<'tcx> for SimplifyToExp {
}
fn int_equal(l: ScalarInt, r: impl Into<u128>, size: Size) -> bool {
l.try_to_int(l.size()).unwrap()
== ScalarInt::try_from_uint(r, size).unwrap().try_to_int(size).unwrap()
l.assert_int(l.size()) == ScalarInt::try_from_uint(r, size).unwrap().assert_int(size)
}
// We first compare the two branches, and then the other branches need to fulfill the same conditions.

View File

@ -490,14 +490,14 @@ impl<'tcx> Validator<'_, 'tcx> {
}
_ => None,
};
match rhs_val.map(|x| x.try_to_uint(sz).unwrap()) {
match rhs_val.map(|x| x.assert_uint(sz)) {
// for the zero test, int vs uint does not matter
Some(x) if x != 0 => {} // okay
_ => return Err(Unpromotable), // value not known or 0 -- not okay
}
// Furthermore, for signed divison, we also have to exclude `int::MIN / -1`.
if lhs_ty.is_signed() {
match rhs_val.map(|x| x.try_to_int(sz).unwrap()) {
match rhs_val.map(|x| x.assert_int(sz)) {
Some(-1) | None => {
// The RHS is -1 or unknown, so we have to be careful.
// But is the LHS int::MIN?
@ -508,7 +508,7 @@ impl<'tcx> Validator<'_, 'tcx> {
_ => None,
};
let lhs_min = sz.signed_int_min();
match lhs_val.map(|x| x.try_to_int(sz).unwrap()) {
match lhs_val.map(|x| x.assert_int(sz)) {
Some(x) if x != lhs_min => {} // okay
_ => return Err(Unpromotable), // value not known or int::MIN -- not okay
}

View File

@ -204,6 +204,7 @@ impl<'psess, 'src> StringReader<'psess, 'src> {
self.ident(start)
}
rustc_lexer::TokenKind::InvalidIdent
| rustc_lexer::TokenKind::InvalidPrefix
// Do not recover an identifier with emoji if the codepoint is a confusable
// with a recoverable substitution token, like ``.
if !UNICODE_ARRAY
@ -301,7 +302,9 @@ impl<'psess, 'src> StringReader<'psess, 'src> {
rustc_lexer::TokenKind::Caret => token::BinOp(token::Caret),
rustc_lexer::TokenKind::Percent => token::BinOp(token::Percent),
rustc_lexer::TokenKind::Unknown | rustc_lexer::TokenKind::InvalidIdent => {
rustc_lexer::TokenKind::Unknown
| rustc_lexer::TokenKind::InvalidIdent
| rustc_lexer::TokenKind::InvalidPrefix => {
// Don't emit diagnostics for sequences of the same invalid token
if swallow_next_invalid > 0 {
swallow_next_invalid -= 1;

View File

@ -2880,7 +2880,7 @@ pub(crate) mod dep_tracking {
use rustc_feature::UnstableFeatures;
use rustc_span::edition::Edition;
use rustc_span::RealFileName;
use rustc_target::spec::{CodeModel, MergeFunctions, PanicStrategy, RelocModel};
use rustc_target::spec::{CodeModel, MergeFunctions, PanicStrategy, RelocModel, WasmCAbi};
use rustc_target::spec::{
RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, TargetTriple, TlsModel,
};
@ -2978,6 +2978,7 @@ pub(crate) mod dep_tracking {
Polonius,
InliningThreshold,
FunctionReturn,
WasmCAbi,
);
impl<T1, T2> DepTrackingHash for (T1, T2)

View File

@ -8,7 +8,9 @@ use rustc_data_structures::profiling::TimePassesFormat;
use rustc_data_structures::stable_hasher::Hash64;
use rustc_errors::ColorConfig;
use rustc_errors::{LanguageIdentifier, TerminalUrl};
use rustc_target::spec::{CodeModel, LinkerFlavorCli, MergeFunctions, PanicStrategy, SanitizerSet};
use rustc_target::spec::{
CodeModel, LinkerFlavorCli, MergeFunctions, PanicStrategy, SanitizerSet, WasmCAbi,
};
use rustc_target::spec::{
RelocModel, RelroLevel, SplitDebuginfo, StackProtector, TargetTriple, TlsModel,
};
@ -441,6 +443,7 @@ mod desc {
"either a boolean (`yes`, `no`, `on`, `off`, etc), or a non-negative number";
pub const parse_llvm_module_flag: &str = "<key>:<type>:<value>:<behavior>. Type must currently be `u32`. Behavior should be one of (`error`, `warning`, `require`, `override`, `append`, `appendunique`, `max`, `min`)";
pub const parse_function_return: &str = "`keep` or `thunk-extern`";
pub const parse_wasm_c_abi: &str = "`legacy` or `spec`";
}
mod parse {
@ -1433,6 +1436,15 @@ mod parse {
}
true
}
pub(crate) fn parse_wasm_c_abi(slot: &mut WasmCAbi, v: Option<&str>) -> bool {
match v {
Some("spec") => *slot = WasmCAbi::Spec,
Some("legacy") => *slot = WasmCAbi::Legacy,
_ => return false,
}
true
}
}
options! {
@ -2061,6 +2073,8 @@ written to standard error output)"),
Requires `-Clto[=[fat,yes]]`"),
wasi_exec_model: Option<WasiExecModel> = (None, parse_wasi_exec_model, [TRACKED],
"whether to build a wasi command or reactor"),
wasm_c_abi: WasmCAbi = (WasmCAbi::Legacy, parse_wasm_c_abi, [TRACKED],
"use spec-compliant C ABI for `wasm32-unknown-unknown` (default: legacy)"),
write_long_types_to_disk: bool = (true, parse_bool, [UNTRACKED],
"whether long type names should be written to files instead of being printed in errors"),
// tidy-alphabetical-end

View File

@ -1,6 +1,6 @@
use crate::abi::{self, Abi, Align, FieldsShape, Size};
use crate::abi::{HasDataLayout, TyAbiInterface, TyAndLayout};
use crate::spec::{self, HasTargetSpec};
use crate::spec::{self, HasTargetSpec, HasWasmCAbiOpt};
use rustc_span::Symbol;
use std::fmt;
use std::str::FromStr;
@ -829,7 +829,7 @@ impl<'a, Ty> FnAbi<'a, Ty> {
) -> Result<(), AdjustForForeignAbiError>
where
Ty: TyAbiInterface<'a, C> + Copy,
C: HasDataLayout + HasTargetSpec,
C: HasDataLayout + HasTargetSpec + HasWasmCAbiOpt,
{
if abi == spec::abi::Abi::X86Interrupt {
if let Some(arg) = self.args.first_mut() {
@ -886,7 +886,9 @@ impl<'a, Ty> FnAbi<'a, Ty> {
"sparc" => sparc::compute_abi_info(cx, self),
"sparc64" => sparc64::compute_abi_info(cx, self),
"nvptx64" => {
if cx.target_spec().adjust_abi(abi, self.c_variadic) == spec::abi::Abi::PtxKernel {
if cx.target_spec().adjust_abi(cx, abi, self.c_variadic)
== spec::abi::Abi::PtxKernel
{
nvptx64::compute_ptx_kernel_abi_info(cx, self)
} else {
nvptx64::compute_abi_info(self)
@ -895,7 +897,7 @@ impl<'a, Ty> FnAbi<'a, Ty> {
"hexagon" => hexagon::compute_abi_info(self),
"riscv32" | "riscv64" => riscv::compute_abi_info(cx, self),
"wasm32" | "wasm64" => {
if cx.target_spec().adjust_abi(abi, self.c_variadic) == spec::abi::Abi::Wasm {
if cx.target_spec().adjust_abi(cx, abi, self.c_variadic) == spec::abi::Abi::Wasm {
wasm::compute_wasm_abi_info(self)
} else {
wasm::compute_c_abi_info(cx, self)

View File

@ -37,7 +37,7 @@
use crate::abi::call::Conv;
use crate::abi::{Endian, Integer, Size, TargetDataLayout, TargetDataLayoutErrors};
use crate::json::{Json, ToJson};
use crate::spec::abi::{lookup as lookup_abi, Abi};
use crate::spec::abi::Abi;
use crate::spec::crt_objects::CrtObjects;
use rustc_fs_util::try_canonicalize;
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
@ -1915,6 +1915,19 @@ impl HasTargetSpec for Target {
}
}
/// Which C ABI to use for `wasm32-unknown-unknown`.
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub enum WasmCAbi {
/// Spec-compliant C ABI.
Spec,
/// Legacy ABI. Which is non-spec-compliant.
Legacy,
}
pub trait HasWasmCAbiOpt {
fn wasm_c_abi_opt(&self) -> WasmCAbi;
}
type StaticCow<T> = Cow<'static, T>;
/// Optional aspects of a target specification.
@ -2273,9 +2286,6 @@ pub struct TargetOptions {
/// distributed with the target, the sanitizer should still appear in this list for the target.
pub supported_sanitizers: SanitizerSet,
/// If present it's a default value to use for adjusting the C ABI.
pub default_adjusted_cabi: Option<Abi>,
/// Minimum number of bits in #[repr(C)] enum. Defaults to the size of c_int
pub c_enum_min_bits: Option<u64>,
@ -2507,7 +2517,6 @@ impl Default for TargetOptions {
// `Off` is supported by default, but targets can remove this manually, e.g. Windows.
supported_split_debuginfo: Cow::Borrowed(&[SplitDebuginfo::Off]),
supported_sanitizers: SanitizerSet::empty(),
default_adjusted_cabi: None,
c_enum_min_bits: None,
generate_arange_section: true,
supports_stack_protector: true,
@ -2538,9 +2547,21 @@ impl DerefMut for Target {
impl Target {
/// Given a function ABI, turn it into the correct ABI for this target.
pub fn adjust_abi(&self, abi: Abi, c_variadic: bool) -> Abi {
pub fn adjust_abi<C>(&self, cx: &C, abi: Abi, c_variadic: bool) -> Abi
where
C: HasWasmCAbiOpt,
{
match abi {
Abi::C { .. } => self.default_adjusted_cabi.unwrap_or(abi),
Abi::C { .. } => {
if self.arch == "wasm32"
&& self.os == "unknown"
&& cx.wasm_c_abi_opt() == WasmCAbi::Legacy
{
Abi::Wasm
} else {
abi
}
}
// On Windows, `extern "system"` behaves like msvc's `__stdcall`.
// `__stdcall` only applies on x86 and on non-variadic functions:
@ -3079,16 +3100,6 @@ impl Target {
}
}
} );
($key_name:ident, Option<Abi>) => ( {
let name = (stringify!($key_name)).replace("_", "-");
obj.remove(&name).and_then(|o| o.as_str().and_then(|s| {
match lookup_abi(s) {
Ok(abi) => base.$key_name = Some(abi),
_ => return Some(Err(format!("'{}' is not a valid value for abi", s))),
}
Some(Ok(()))
})).unwrap_or(Ok(()))
} );
($key_name:ident, TargetFamilies) => ( {
if let Some(value) = obj.remove("target-family") {
if let Some(v) = value.as_array() {
@ -3238,7 +3249,6 @@ impl Target {
key!(split_debuginfo, SplitDebuginfo)?;
key!(supported_split_debuginfo, fallible_list)?;
key!(supported_sanitizers, SanitizerSet)?;
key!(default_adjusted_cabi, Option<Abi>)?;
key!(generate_arange_section, bool);
key!(supports_stack_protector, bool);
key!(entry_name);
@ -3502,10 +3512,6 @@ impl ToJson for Target {
target_option_val!(entry_abi);
target_option_val!(supports_xray);
if let Some(abi) = self.default_adjusted_cabi {
d.insert("default-adjusted-cabi".into(), Abi::name(abi).to_json());
}
// Serializing `-Clink-self-contained` needs a dynamic key to support the
// backwards-compatible variants.
d.insert(self.link_self_contained.json_key().into(), self.link_self_contained.to_json());

View File

@ -10,23 +10,12 @@
//! This target is more or less managed by the Rust and WebAssembly Working
//! Group nowadays at <https://github.com/rustwasm>.
use crate::spec::abi::Abi;
use crate::spec::{base, Cc, LinkerFlavor, Target};
pub fn target() -> Target {
let mut options = base::wasm::options();
options.os = "unknown".into();
// This is a default for backwards-compatibility with the original
// definition of this target oh-so-long-ago. Once the "wasm" ABI is
// stable and the wasm-bindgen project has switched to using it then there's
// no need for this and it can be removed.
//
// Currently this is the reason that this target's ABI is mismatched with
// clang's ABI. This means that, in the limit, you can't merge C and Rust
// code on this target due to this ABI mismatch.
options.default_adjusted_cabi = Some(Abi::Wasm);
options.add_pre_link_args(
LinkerFlavor::WasmLld(Cc::No),
&[

View File

@ -641,7 +641,7 @@ pub fn compute_inherent_assoc_ty_args<'a, 'b, 'tcx>(
);
}
match selcx.infcx.at(&cause, param_env).eq(DefineOpaqueTypes::No, impl_ty, self_ty) {
match selcx.infcx.at(&cause, param_env).eq(DefineOpaqueTypes::Yes, impl_ty, self_ty) {
Ok(mut ok) => obligations.append(&mut ok.obligations),
Err(_) => {
tcx.dcx().span_bug(

View File

@ -420,7 +420,7 @@ pub(crate) mod rustc {
fn from_tag(tag: ScalarInt, tcx: TyCtxt<'tcx>) -> Self {
use rustc_target::abi::Endian;
let size = tag.size();
let bits = tag.to_bits(size).unwrap();
let bits = tag.assert_bits(size);
let bytes: [u8; 16];
let bytes = match tcx.data_layout.endian {
Endian::Little => {

View File

@ -322,7 +322,7 @@ fn fn_sig_for_fn_abi<'tcx>(
#[inline]
fn conv_from_spec_abi(tcx: TyCtxt<'_>, abi: SpecAbi, c_variadic: bool) -> Conv {
use rustc_target::spec::abi::Abi::*;
match tcx.sess.target.adjust_abi(abi, c_variadic) {
match tcx.sess.target.adjust_abi(&tcx, abi, c_variadic) {
RustIntrinsic | Rust | RustCall => Conv::Rust,
// This is intentionally not using `Conv::Cold`, as that has to preserve

View File

@ -517,11 +517,8 @@
#overflow-checks-std = rust.overflow-checks (boolean)
# Debuginfo level for most of Rust code, corresponds to the `-C debuginfo=N` option of `rustc`.
# `0` - no debug info
# `1` - line tables only - sufficient to generate backtraces that include line
# information and inlined functions, set breakpoints at source code
# locations, and step through execution in a debugger.
# `2` - full debug info with variable and type information
# See https://doc.rust-lang.org/rustc/codegen-options/index.html#debuginfo for available options.
#
# Can be overridden for specific subsets of Rust code (rustc, std or tools).
# Debuginfo for tests run with compiletest is not controlled by this option
# and needs to be enabled separately with `debuginfo-level-tests`.

View File

@ -34,9 +34,22 @@ where
/// Returns an iterator over the remaining elements of the original iterator
/// that are not going to be returned by this iterator. The returned
/// iterator will yield at most `N-1` elements.
///
/// # Example
/// ```
/// # // Also serves as a regression test for https://github.com/rust-lang/rust/issues/123333
/// # #![feature(iter_array_chunks)]
/// let x = [1,2,3,4,5].into_iter().array_chunks::<2>();
/// let mut rem = x.into_remainder().unwrap();
/// assert_eq!(rem.next(), Some(5));
/// assert_eq!(rem.next(), None);
/// ```
#[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "100450")]
#[inline]
pub fn into_remainder(self) -> Option<array::IntoIter<I::Item, N>> {
pub fn into_remainder(mut self) -> Option<array::IntoIter<I::Item, N>> {
if self.remainder.is_none() {
while let Some(_) = self.next() {}
}
self.remainder
}
}

View File

@ -277,6 +277,13 @@ fn default_hook(info: &PanicInfo<'_>) {
"note: run with `RUST_BACKTRACE=1` environment variable to display a \
backtrace"
);
if cfg!(miri) {
let _ = writeln!(
err,
"note: in Miri, you may have to set `-Zmiri-env-forward=RUST_BACKTRACE` \
for the environment variable to have an effect"
);
}
}
}
// If backtraces aren't supported or are forced-off, do nothing.

View File

@ -357,7 +357,19 @@ compat_fn_with_fallback! {
}
#[cfg(not(target_vendor = "win7"))]
#[link(name = "synchronization")]
// Use raw-dylib to import synchronization functions to workaround issues with the older mingw import library.
#[cfg_attr(
target_arch = "x86",
link(
name = "api-ms-win-core-synch-l1-2-0",
kind = "raw-dylib",
import_name_type = "undecorated"
)
)]
#[cfg_attr(
not(target_arch = "x86"),
link(name = "api-ms-win-core-synch-l1-2-0", kind = "raw-dylib")
)]
extern "system" {
pub fn WaitOnAddress(
address: *const c_void,

View File

@ -236,12 +236,6 @@ dependencies = [
"crypto-common",
]
[[package]]
name = "either"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "errno"
version = "0.3.8"
@ -448,26 +442,6 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "rayon"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1"
dependencies = [
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed"
dependencies = [
"crossbeam-deque",
"crossbeam-utils",
]
[[package]]
name = "redox_syscall"
version = "0.4.1"
@ -598,7 +572,6 @@ dependencies = [
"libc",
"ntapi",
"once_cell",
"rayon",
"windows",
]

View File

@ -64,7 +64,7 @@ walkdir = "2.4"
xz2 = "0.1"
# Dependencies needed by the build-metrics feature
sysinfo = { version = "0.30", optional = true }
sysinfo = { version = "0.30", default-features = false, optional = true }
[target.'cfg(windows)'.dependencies.junction]
version = "1.0.0"

View File

@ -55,6 +55,7 @@ pub enum DryRun {
pub enum DebuginfoLevel {
#[default]
None,
LineDirectivesOnly,
LineTablesOnly,
Limited,
Full,
@ -70,16 +71,22 @@ impl<'de> Deserialize<'de> for DebuginfoLevel {
use serde::de::Error;
Ok(match Deserialize::deserialize(deserializer)? {
StringOrInt::String("none") | StringOrInt::Int(0) => DebuginfoLevel::None,
StringOrInt::String("line-tables-only") => DebuginfoLevel::LineTablesOnly,
StringOrInt::String("limited") | StringOrInt::Int(1) => DebuginfoLevel::Limited,
StringOrInt::String("full") | StringOrInt::Int(2) => DebuginfoLevel::Full,
StringOrInt::String(s) if s == "none" => DebuginfoLevel::None,
StringOrInt::Int(0) => DebuginfoLevel::None,
StringOrInt::String(s) if s == "line-directives-only" => {
DebuginfoLevel::LineDirectivesOnly
}
StringOrInt::String(s) if s == "line-tables-only" => DebuginfoLevel::LineTablesOnly,
StringOrInt::String(s) if s == "limited" => DebuginfoLevel::Limited,
StringOrInt::Int(1) => DebuginfoLevel::Limited,
StringOrInt::String(s) if s == "full" => DebuginfoLevel::Full,
StringOrInt::Int(2) => DebuginfoLevel::Full,
StringOrInt::Int(n) => {
let other = serde::de::Unexpected::Signed(n);
return Err(D::Error::invalid_value(other, &"expected 0, 1, or 2"));
}
StringOrInt::String(s) => {
let other = serde::de::Unexpected::Str(s);
let other = serde::de::Unexpected::Str(&s);
return Err(D::Error::invalid_value(
other,
&"expected none, line-tables-only, limited, or full",
@ -95,6 +102,7 @@ impl Display for DebuginfoLevel {
use DebuginfoLevel::*;
f.write_str(match self {
None => "0",
LineDirectivesOnly => "line-directives-only",
LineTablesOnly => "line-tables-only",
Limited => "1",
Full => "2",
@ -1021,8 +1029,8 @@ impl RustOptimize {
#[derive(Deserialize)]
#[serde(untagged)]
enum StringOrInt<'a> {
String(&'a str),
enum StringOrInt {
String(String),
Int(i64),
}

View File

@ -0,0 +1,10 @@
# `wasm-c-abi`
This option controls whether Rust uses the spec-compliant C ABI when compiling
for the `wasm32-unknown-unknown` target.
This makes it possible to be ABI-compatible with all other spec-compliant Wasm
like Rusts `wasm32-wasi`.
This compiler flag is perma-unstable, as it will be enabled by default in the
future with no option to fall back to the old non-spec-compliant ABI.

View File

@ -600,13 +600,6 @@ impl Item {
}
}
pub(crate) fn const_stable_since(&self, tcx: TyCtxt<'_>) -> Option<StableSince> {
match self.const_stability(tcx)?.level {
StabilityLevel::Stable { since, .. } => Some(since),
StabilityLevel::Unstable { .. } => None,
}
}
pub(crate) fn is_non_exhaustive(&self) -> bool {
self.attrs.other_attrs.iter().any(|a| a.has_name(sym::non_exhaustive))
}

View File

@ -876,9 +876,10 @@ impl<'src> Classifier<'src> {
},
Some(c) => c,
},
TokenKind::RawIdent | TokenKind::UnknownPrefix | TokenKind::InvalidIdent => {
Class::Ident(self.new_span(before, text))
}
TokenKind::RawIdent
| TokenKind::UnknownPrefix
| TokenKind::InvalidPrefix
| TokenKind::InvalidIdent => Class::Ident(self.new_span(before, text)),
TokenKind::Lifetime { .. } => Class::Lifetime,
TokenKind::Eof => panic!("Eof in advance"),
};

View File

@ -996,32 +996,20 @@ fn assoc_method(
/// consequence of the above rules.
fn render_stability_since_raw_with_extra(
w: &mut Buffer,
ver: Option<StableSince>,
stable_version: Option<StableSince>,
const_stability: Option<ConstStability>,
containing_ver: Option<StableSince>,
containing_const_ver: Option<StableSince>,
extra_class: &str,
) -> bool {
let stable_version = if ver != containing_ver
&& let Some(ver) = &ver
{
since_to_string(ver)
} else {
None
};
let mut title = String::new();
let mut stability = String::new();
if let Some(ver) = stable_version {
stability.push_str(ver.as_str());
title.push_str(&format!("Stable since Rust version {ver}"));
if let Some(version) = stable_version.and_then(|version| since_to_string(&version)) {
stability.push_str(&version);
title.push_str(&format!("Stable since Rust version {version}"));
}
let const_title_and_stability = match const_stability {
Some(ConstStability { level: StabilityLevel::Stable { since, .. }, .. })
if Some(since) != containing_const_ver =>
{
Some(ConstStability { level: StabilityLevel::Stable { since, .. }, .. }) => {
since_to_string(&since)
.map(|since| (format!("const since {since}"), format!("const: {since}")))
}
@ -1076,17 +1064,8 @@ fn render_stability_since_raw(
w: &mut Buffer,
ver: Option<StableSince>,
const_stability: Option<ConstStability>,
containing_ver: Option<StableSince>,
containing_const_ver: Option<StableSince>,
) -> bool {
render_stability_since_raw_with_extra(
w,
ver,
const_stability,
containing_ver,
containing_const_ver,
"",
)
render_stability_since_raw_with_extra(w, ver, const_stability, "")
}
fn render_assoc_item(
@ -1585,7 +1564,6 @@ fn render_impl(
cx: &mut Context<'_>,
item: &clean::Item,
parent: &clean::Item,
containing_item: &clean::Item,
link: AssocItemLink<'_>,
render_mode: RenderMode,
is_default_item: bool,
@ -1681,7 +1659,7 @@ fn render_impl(
})
.map(|item| format!("{}.{name}", item.type_()));
write!(w, "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">");
render_rightside(w, cx, item, containing_item, render_mode);
render_rightside(w, cx, item, render_mode);
if trait_.is_some() {
// Anchors are only used on trait impls.
write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>");
@ -1703,7 +1681,7 @@ fn render_impl(
let source_id = format!("{item_type}.{name}");
let id = cx.derive_id(&source_id);
write!(w, "<section id=\"{id}\" class=\"{item_type}{in_trait_class}\">");
render_rightside(w, cx, item, containing_item, render_mode);
render_rightside(w, cx, item, render_mode);
if trait_.is_some() {
// Anchors are only used on trait impls.
write!(w, "<a href=\"#{id}\" class=\"anchor\">§</a>");
@ -1789,7 +1767,6 @@ fn render_impl(
cx,
trait_item,
if trait_.is_some() { &i.impl_item } else { parent },
parent,
link,
render_mode,
false,
@ -1805,7 +1782,6 @@ fn render_impl(
t: &clean::Trait,
i: &clean::Impl,
parent: &clean::Item,
containing_item: &clean::Item,
render_mode: RenderMode,
rendering_params: ImplRenderingParameters,
) {
@ -1833,7 +1809,6 @@ fn render_impl(
cx,
trait_item,
parent,
containing_item,
assoc_link,
render_mode,
true,
@ -1856,7 +1831,6 @@ fn render_impl(
t,
i.inner_impl(),
&i.impl_item,
parent,
render_mode,
rendering_params,
);
@ -1878,7 +1852,6 @@ fn render_impl(
cx,
i,
parent,
parent,
rendering_params.show_def_docs,
use_absolute,
aliases,
@ -1926,20 +1899,14 @@ fn render_impl(
// Render the items that appear on the right side of methods, impls, and
// associated types. For example "1.0.0 (const: 1.39.0) · source".
fn render_rightside(
w: &mut Buffer,
cx: &Context<'_>,
item: &clean::Item,
containing_item: &clean::Item,
render_mode: RenderMode,
) {
fn render_rightside(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, render_mode: RenderMode) {
let tcx = cx.tcx();
// FIXME: Once https://github.com/rust-lang/rust/issues/67792 is implemented, we can remove
// this condition.
let (const_stability, const_stable_since) = match render_mode {
RenderMode::Normal => (item.const_stability(tcx), containing_item.const_stable_since(tcx)),
RenderMode::ForDeref { .. } => (None, None),
let const_stability = match render_mode {
RenderMode::Normal => item.const_stability(tcx),
RenderMode::ForDeref { .. } => None,
};
let src_href = cx.src_href(item);
let has_src_ref = src_href.is_some();
@ -1949,8 +1916,6 @@ fn render_rightside(
&mut rightside,
item.stable_since(tcx),
const_stability,
containing_item.stable_since(tcx),
const_stable_since,
if has_src_ref { "" } else { " rightside" },
);
if let Some(link) = src_href {
@ -1972,7 +1937,6 @@ pub(crate) fn render_impl_summary(
cx: &mut Context<'_>,
i: &Impl,
parent: &clean::Item,
containing_item: &clean::Item,
show_def_docs: bool,
use_absolute: Option<bool>,
// This argument is used to reference same type with different paths to avoid duplication
@ -1987,7 +1951,7 @@ pub(crate) fn render_impl_summary(
format!(" data-aliases=\"{}\"", aliases.join(","))
};
write!(w, "<section id=\"{id}\" class=\"impl\"{aliases}>");
render_rightside(w, cx, &i.impl_item, containing_item, RenderMode::Normal);
render_rightside(w, cx, &i.impl_item, RenderMode::Normal);
write!(
w,
"<a href=\"#{id}\" class=\"anchor\">§</a>\

View File

@ -214,8 +214,6 @@ pub(super) fn print_item(cx: &mut Context<'_>, item: &clean::Item, buf: &mut Buf
&mut stability_since_raw,
item.stable_since(cx.tcx()),
item.const_stability(cx.tcx()),
None,
None,
);
let stability_since_raw: String = stability_since_raw.into_inner();
@ -825,7 +823,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
write!(w, "<details class=\"toggle{method_toggle_class}\" open><summary>");
}
write!(w, "<section id=\"{id}\" class=\"method\">");
render_rightside(w, cx, m, t, RenderMode::Normal);
render_rightside(w, cx, m, RenderMode::Normal);
write!(w, "<h4 class=\"code-header\">");
render_assoc_item(
w,
@ -1686,8 +1684,6 @@ fn item_variants(
w,
variant.stable_since(tcx),
variant.const_stability(tcx),
it.stable_since(tcx),
it.const_stable_since(tcx),
" rightside",
);
w.write_str("<h3 class=\"code-header\">");

@ -1 +1 @@
Subproject commit 6f06fe908a5ee0f415c187f868ea627e82efe07d
Subproject commit 80d5b607dde6ef97dfff4e23923822c01d2bb036

View File

@ -24,7 +24,7 @@ jobs:
node-version: '18.x'
- name: Install remark
run: npm install remark-cli remark-lint remark-lint-maximum-line-length remark-preset-lint-recommended remark-gfm
run: npm install remark-cli remark-lint remark-lint-maximum-line-length@^3.1.3 remark-preset-lint-recommended remark-gfm
- name: Install mdbook
run: |

View File

@ -5894,6 +5894,7 @@ Released 2018-09-13
[`allowed-dotfiles`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-dotfiles
[`allowed-duplicate-crates`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-duplicate-crates
[`allowed-idents-below-min-chars`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-idents-below-min-chars
[`allowed-prefixes`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-prefixes
[`allowed-scripts`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-scripts
[`allowed-wildcard-imports`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-wildcard-imports
[`arithmetic-side-effects-allowed`]: https://doc.rust-lang.org/clippy/lint_configuration.html#arithmetic-side-effects-allowed

View File

@ -18,17 +18,27 @@ category.
| `clippy::all` | all lints that are on by default (correctness, suspicious, style, complexity, perf) | **warn/deny** |
| `clippy::correctness` | code that is outright wrong or useless | **deny** |
| `clippy::suspicious` | code that is most likely wrong or useless | **warn** |
| `clippy::style` | code that should be written in a more idiomatic way | **warn** |
| `clippy::complexity` | code that does something simple but in a complex way | **warn** |
| `clippy::perf` | code that can be written to run faster | **warn** |
| `clippy::style` | code that should be written in a more idiomatic way | **warn** |
| `clippy::pedantic` | lints which are rather strict or might have false positives | allow |
| `clippy::pedantic` | lints which are rather strict or have occasional false positives | allow |
| `clippy::restriction` | lints which prevent the use of language and library features[^restrict] | allow |
| `clippy::nursery` | new lints that are still under development | allow |
| `clippy::cargo` | lints for the cargo manifest | allow | | allow |
| `clippy::cargo` | lints for the cargo manifest | allow |
More to come, please [file an
issue](https://github.com/rust-lang/rust-clippy/issues) if you have ideas!
More to come, please [file an issue](https://github.com/rust-lang/rust-clippy/issues) if you have ideas!
The [lint list](https://rust-lang.github.io/rust-clippy/master/index.html) also
contains "restriction lints", which are for things which are usually not
considered "bad", but may be useful to turn on in specific cases. These should
be used very selectively, if at all.
The `restriction` category should, *emphatically*, not be enabled as a whole. The contained
lints may lint against perfectly reasonable code, may not have an alternative suggestion,
and may contradict any other lints (including other categories). Lints should be considered
on a case-by-case basis before enabling.
[^restrict]: Some use cases for `restriction` lints include:
- Strict coding styles (e.g. [`clippy::else_if_without_else`]).
- Additional restrictions on CI (e.g. [`clippy::todo`]).
- Preventing panicking in certain functions (e.g. [`clippy::unwrap_used`]).
- Running a lint only on a subset of code (e.g. `#[forbid(clippy::float_arithmetic)]` on a module).
[`clippy::else_if_without_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#else_if_without_else
[`clippy::todo`]: https://rust-lang.github.io/rust-clippy/master/index.html#todo
[`clippy::unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used

View File

@ -18,7 +18,6 @@ because that's clearly a non-descriptive name.
- [Cargo lints](#cargo-lints)
- [Rustfix tests](#rustfix-tests)
- [Testing manually](#testing-manually)
- [Running directly](#running-directly)
- [Lint declaration](#lint-declaration)
- [Lint registration](#lint-registration)
- [Lint passes](#lint-passes)
@ -176,23 +175,26 @@ the tests.
Manually testing against an example file can be useful if you have added some
`println!`s and the test suite output becomes unreadable. To try Clippy with
your local modifications, run
your local modifications, run the following from the Clippy directory:
```
```bash
cargo dev lint input.rs
```
from the working copy root. With tests in place, let's have a look at
implementing our lint now.
To run Clippy on an existing project rather than a single file you can use
## Running directly
```bash
cargo dev lint /path/to/project
```
While it's easier to just use `cargo dev lint`, it might be desirable to get
`target/release/cargo-clippy` and `target/release/clippy-driver` to work as well in some cases.
By default, they don't work because clippy dynamically links rustc. To help them find rustc,
add the path printed by`rustc --print target-libdir` (ran inside this workspace so that the rustc version matches)
to your library search path.
On linux, this can be done by setting the `LD_LIBRARY_PATH` environment variable to that path.
Or set up a rustup toolchain that points to the local Clippy binaries
```bash
cargo dev setup toolchain
# Then in `/path/to/project` you can run
cargo +clippy clippy
```
## Lint declaration

View File

@ -164,6 +164,32 @@ configuration of Clippy. By default, any configuration will replace the default
* [`min_ident_chars`](https://rust-lang.github.io/rust-clippy/master/index.html#min_ident_chars)
## `allowed-prefixes`
List of prefixes to allow when determining whether an item's name ends with the module's name.
If the rest of an item's name is an allowed prefix (e.g. item `ToFoo` or `to_foo` in module `foo`),
then don't emit a warning.
#### Example
```toml
allowed-prefixes = [ "to", "from" ]
```
#### Noteworthy
- By default, the following prefixes are allowed: `to`, `as`, `into`, `from`, `try_into` and `try_from`
- PascalCase variant is included automatically for each snake_case variant (e.g. if `try_into` is included,
`TryInto` will also be included)
- Use `".."` as part of the list to indicate that the configured values should be appended to the
default configuration of Clippy. By default, any configuration will replace the default value
**Default Value:** `["to", "as", "into", "from", "try_into", "try_from"]`
---
**Affected lints:**
* [`module_name_repetitions`](https://rust-lang.github.io/rust-clippy/master/index.html#module_name_repetitions)
## `allowed-scripts`
The list of unicode scripts allowed to be used in the scope.

View File

@ -39,6 +39,7 @@ const DEFAULT_DOC_VALID_IDENTS: &[&str] = &[
];
const DEFAULT_DISALLOWED_NAMES: &[&str] = &["foo", "baz", "quux"];
const DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS: &[&str] = &["i", "j", "x", "y", "z", "w", "n"];
const DEFAULT_ALLOWED_PREFIXES: &[&str] = &["to", "as", "into", "from", "try_into", "try_from"];
/// Conf with parse errors
#[derive(Default)]
@ -589,6 +590,26 @@ define_Conf! {
/// 2. Paths with any segment that containing the word 'prelude'
/// are already allowed by default.
(allowed_wildcard_imports: FxHashSet<String> = FxHashSet::default()),
/// Lint: MODULE_NAME_REPETITIONS.
///
/// List of prefixes to allow when determining whether an item's name ends with the module's name.
/// If the rest of an item's name is an allowed prefix (e.g. item `ToFoo` or `to_foo` in module `foo`),
/// then don't emit a warning.
///
/// #### Example
///
/// ```toml
/// allowed-prefixes = [ "to", "from" ]
/// ```
///
/// #### Noteworthy
///
/// - By default, the following prefixes are allowed: `to`, `as`, `into`, `from`, `try_into` and `try_from`
/// - PascalCase variant is included automatically for each snake_case variant (e.g. if `try_into` is included,
/// `TryInto` will also be included)
/// - Use `".."` as part of the list to indicate that the configured values should be appended to the
/// default configuration of Clippy. By default, any configuration will replace the default value
(allowed_prefixes: Vec<String> = DEFAULT_ALLOWED_PREFIXES.iter().map(ToString::to_string).collect()),
}
/// Search for the configuration file.
@ -649,6 +670,7 @@ fn deserialize(file: &SourceFile) -> TryConf {
Ok(mut conf) => {
extend_vec_if_indicator_present(&mut conf.conf.doc_valid_idents, DEFAULT_DOC_VALID_IDENTS);
extend_vec_if_indicator_present(&mut conf.conf.disallowed_names, DEFAULT_DISALLOWED_NAMES);
extend_vec_if_indicator_present(&mut conf.conf.allowed_prefixes, DEFAULT_ALLOWED_PREFIXES);
// TODO: THIS SHOULD BE TESTED, this comment will be gone soon
if conf.conf.allowed_idents_below_min_chars.contains("..") {
conf.conf

View File

@ -35,7 +35,6 @@ struct FmtContext {
}
// the "main" function of cargo dev fmt
#[allow(clippy::missing_panics_doc)]
pub fn run(check: bool, verbose: bool) {
fn try_run(context: &FmtContext) -> Result<bool, CliError> {
let mut success = true;

View File

@ -9,6 +9,7 @@
unused_lifetimes,
unused_qualifications
)]
#![allow(clippy::missing_panics_doc)]
// The `rustc_driver` crate seems to be required in order to use the `rust_lexer` crate.
#[allow(unused_extern_crates)]

View File

@ -46,6 +46,13 @@ fn main() {
}
},
Some(("setup", sub_command)) => match sub_command.subcommand() {
Some(("git-hook", matches)) => {
if matches.get_flag("remove") {
setup::git_hook::remove_hook();
} else {
setup::git_hook::install_hook(matches.get_flag("force-override"));
}
},
Some(("intellij", matches)) => {
if matches.get_flag("remove") {
setup::intellij::remove_rustc_src();
@ -57,12 +64,12 @@ fn main() {
);
}
},
Some(("git-hook", matches)) => {
if matches.get_flag("remove") {
setup::git_hook::remove_hook();
} else {
setup::git_hook::install_hook(matches.get_flag("force-override"));
}
Some(("toolchain", matches)) => {
setup::toolchain::create(
matches.get_flag("force"),
matches.get_flag("release"),
matches.get_one::<String>("name").unwrap(),
);
},
Some(("vscode-tasks", matches)) => {
if matches.get_flag("remove") {
@ -210,6 +217,19 @@ fn get_clap_config() -> ArgMatches {
.about("Support for setting up your personal development environment")
.arg_required_else_help(true)
.subcommands([
Command::new("git-hook")
.about("Add a pre-commit git hook that formats your code to make it look pretty")
.args([
Arg::new("remove")
.long("remove")
.action(ArgAction::SetTrue)
.help("Remove the pre-commit hook added with 'cargo dev setup git-hook'"),
Arg::new("force-override")
.long("force-override")
.short('f')
.action(ArgAction::SetTrue)
.help("Forces the override of an existing git pre-commit hook"),
]),
Command::new("intellij")
.about("Alter dependencies so Intellij Rust can find rustc internals")
.args([
@ -225,18 +245,23 @@ fn get_clap_config() -> ArgMatches {
.conflicts_with("remove")
.required(true),
]),
Command::new("git-hook")
.about("Add a pre-commit git hook that formats your code to make it look pretty")
Command::new("toolchain")
.about("Install a rustup toolchain pointing to the local clippy build")
.args([
Arg::new("remove")
.long("remove")
.action(ArgAction::SetTrue)
.help("Remove the pre-commit hook added with 'cargo dev setup git-hook'"),
Arg::new("force-override")
.long("force-override")
Arg::new("force")
.long("force")
.short('f')
.action(ArgAction::SetTrue)
.help("Forces the override of an existing git pre-commit hook"),
.help("Override an existing toolchain"),
Arg::new("release")
.long("release")
.short('r')
.action(ArgAction::SetTrue)
.help("Point to --release clippy binaries"),
Arg::new("name")
.long("name")
.default_value("clippy")
.help("The name of the created toolchain"),
]),
Command::new("vscode-tasks")
.about("Add several tasks to vscode for formatting, validation and testing")

View File

@ -36,7 +36,6 @@ impl<T> Context for io::Result<T> {
/// # Errors
///
/// This function errors out if the files couldn't be created or written to.
#[allow(clippy::missing_panics_doc)]
pub fn create(
pass: &String,
lint_name: Option<&String>,

View File

@ -1,5 +1,6 @@
pub mod git_hook;
pub mod intellij;
pub mod toolchain;
pub mod vscode;
use std::path::Path;

View File

@ -0,0 +1,75 @@
use std::env::consts::EXE_SUFFIX;
use std::env::current_dir;
use std::ffi::OsStr;
use std::fs;
use std::path::{Path, PathBuf};
use walkdir::WalkDir;
use super::verify_inside_clippy_dir;
pub fn create(force: bool, release: bool, name: &str) {
if !verify_inside_clippy_dir() {
return;
}
let rustup_home = std::env::var("RUSTUP_HOME").unwrap();
let toolchain = std::env::var("RUSTUP_TOOLCHAIN").unwrap();
let src = PathBuf::from_iter([&rustup_home, "toolchains", &toolchain]);
let dest = PathBuf::from_iter([&rustup_home, "toolchains", name]);
if dest.exists() {
if force {
fs::remove_dir_all(&dest).unwrap();
} else {
println!("{} already exists, pass `--force` to override it", dest.display());
return;
}
}
for entry in WalkDir::new(&src) {
let entry = entry.unwrap();
let relative = entry.path().strip_prefix(&src).unwrap();
if relative.starts_with("bin")
&& matches!(
relative.file_stem().and_then(OsStr::to_str),
Some("cargo-clippy" | "clippy-driver")
)
{
continue;
}
let target = dest.join(relative);
if entry.file_type().is_dir() {
fs::create_dir(&target).unwrap();
} else {
fs::hard_link(entry.path(), target).unwrap();
}
}
symlink_bin("cargo-clippy", &dest, release);
symlink_bin("clippy-driver", &dest, release);
println!("Created toolchain {name}, use it in other projects with e.g. `cargo +{name} clippy`");
println!("Note: This will need to be re-run whenever the Clippy `rust-toolchain` changes");
}
fn symlink_bin(bin: &str, dest: &Path, release: bool) {
#[cfg(windows)]
use std::os::windows::fs::symlink_file as symlink;
#[cfg(not(windows))]
use std::os::unix::fs::symlink;
let profile = if release { "release" } else { "debug" };
let file_name = format!("{bin}{EXE_SUFFIX}");
let mut src = current_dir().unwrap();
src.extend(["target", profile, &file_name]);
let mut dest = dest.to_path_buf();
dest.extend(["bin", &file_name]);
symlink(src, dest).unwrap();
}

View File

@ -56,7 +56,12 @@ impl<'tcx> LateLintPass<'tcx> for ArcWithNonSendSync {
&& let Some(send) = cx.tcx.get_diagnostic_item(sym::Send)
&& let Some(sync) = cx.tcx.lang_items().sync_trait()
&& let [is_send, is_sync] = [send, sync].map(|id| implements_trait(cx, arg_ty, id, &[]))
&& !(is_send && is_sync)
&& let reason = match (is_send, is_sync) {
(false, false) => "neither `Send` nor `Sync`",
(false, true) => "not `Send`",
(true, false) => "not `Sync`",
_ => return,
}
&& !is_from_proc_macro(cx, expr)
{
span_lint_and_then(
@ -66,21 +71,12 @@ impl<'tcx> LateLintPass<'tcx> for ArcWithNonSendSync {
"usage of an `Arc` that is not `Send` and `Sync`",
|diag| {
with_forced_trimmed_paths!({
diag.note(format!("`Arc<{arg_ty}>` is not `Send` and `Sync` as:"));
if !is_send {
diag.note(format!("- the trait `Send` is not implemented for `{arg_ty}`"));
}
if !is_sync {
diag.note(format!("- the trait `Sync` is not implemented for `{arg_ty}`"));
}
diag.help("consider using an `Rc` instead. `Arc` does not provide benefits for non `Send` and `Sync` types");
diag.note("if you intend to use `Arc` with `Send` and `Sync` traits");
diag.note(format!(
"wrap the inner type with a `Mutex` or implement `Send` and `Sync` for `{arg_ty}`"
"`Arc<{arg_ty}>` is not `Send` and `Sync` as `{arg_ty}` is {reason}"
));
diag.help("if the `Arc` will not used be across threads replace it with an `Rc`");
diag.help(format!(
"otherwise make `{arg_ty}` `Send` and `Sync` or consider a wrapper type such as `Mutex`"
));
});
},

View File

@ -2,12 +2,12 @@ use super::DUPLICATED_ATTRIBUTES;
use clippy_utils::diagnostics::span_lint_and_then;
use rustc_ast::{Attribute, MetaItem};
use rustc_data_structures::fx::FxHashMap;
use rustc_lint::EarlyContext;
use rustc_lint::LateContext;
use rustc_span::{sym, Span};
use std::collections::hash_map::Entry;
fn emit_if_duplicated(
cx: &EarlyContext<'_>,
cx: &LateContext<'_>,
attr: &MetaItem,
attr_paths: &mut FxHashMap<String, Span>,
complete_path: String,
@ -26,7 +26,7 @@ fn emit_if_duplicated(
}
fn check_duplicated_attr(
cx: &EarlyContext<'_>,
cx: &LateContext<'_>,
attr: &MetaItem,
attr_paths: &mut FxHashMap<String, Span>,
parent: &mut Vec<String>,
@ -64,7 +64,7 @@ fn check_duplicated_attr(
}
}
pub fn check(cx: &EarlyContext<'_>, attrs: &[Attribute]) {
pub fn check(cx: &LateContext<'_>, attrs: &[Attribute]) {
let mut attr_paths = FxHashMap::default();
for attr in attrs {

View File

@ -17,7 +17,7 @@ mod useless_attribute;
mod utils;
use clippy_config::msrvs::Msrv;
use rustc_ast::{Attribute, Crate, MetaItemKind, NestedMetaItem};
use rustc_ast::{Attribute, MetaItemKind, NestedMetaItem};
use rustc_hir::{ImplItem, Item, ItemKind, TraitItem};
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, impl_lint_pass};
@ -534,11 +534,13 @@ declare_lint_pass!(Attributes => [
BLANKET_CLIPPY_RESTRICTION_LINTS,
SHOULD_PANIC_WITHOUT_EXPECT,
MIXED_ATTRIBUTES_STYLE,
DUPLICATED_ATTRIBUTES,
]);
impl<'tcx> LateLintPass<'tcx> for Attributes {
fn check_crate(&mut self, cx: &LateContext<'tcx>) {
blanket_clippy_restriction_lints::check_command_line(cx);
duplicated_attributes::check(cx, cx.tcx.hir().krate_attrs());
}
fn check_attribute(&mut self, cx: &LateContext<'tcx>, attr: &'tcx Attribute) {
@ -578,6 +580,7 @@ impl<'tcx> LateLintPass<'tcx> for Attributes {
_ => {},
}
mixed_attributes_style::check(cx, item.span, attrs);
duplicated_attributes::check(cx, attrs);
}
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) {
@ -606,17 +609,11 @@ impl_lint_pass!(EarlyAttributes => [
MAYBE_MISUSED_CFG,
DEPRECATED_CLIPPY_CFG_ATTR,
UNNECESSARY_CLIPPY_CFG,
DUPLICATED_ATTRIBUTES,
]);
impl EarlyLintPass for EarlyAttributes {
fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) {
duplicated_attributes::check(cx, &krate.attrs);
}
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &rustc_ast::Item) {
empty_line_after::check(cx, item);
duplicated_attributes::check(cx, &item.attrs);
}
fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {

View File

@ -346,11 +346,18 @@ fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
_ => None,
}
.and_then(|op| {
Some(format!(
"{}{op}{}",
snippet_opt(cx, lhs.span)?,
snippet_opt(cx, rhs.span)?
))
let lhs_snippet = snippet_opt(cx, lhs.span)?;
let rhs_snippet = snippet_opt(cx, rhs.span)?;
if !(lhs_snippet.starts_with('(') && lhs_snippet.ends_with(')')) {
if let (ExprKind::Cast(..), BinOpKind::Ge) = (&lhs.kind, binop.node) {
// e.g. `(a as u64) < b`. Without the parens the `<` is
// interpreted as a start of generic arguments for `u64`
return Some(format!("({lhs_snippet}){op}{rhs_snippet}"));
}
}
Some(format!("{lhs_snippet}{op}{rhs_snippet}"))
})
},
ExprKind::MethodCall(path, receiver, [], _) => {

View File

@ -754,11 +754,7 @@ impl_lint_pass!(Casts => [
impl<'tcx> LateLintPass<'tcx> for Casts {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if !in_external_macro(cx.sess(), expr.span) {
ptr_as_ptr::check(cx, expr, &self.msrv);
}
if expr.span.from_expansion() {
if in_external_macro(cx.sess(), expr.span) {
return;
}
@ -771,7 +767,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
cx.typeck_results().expr_ty(expr),
);
if unnecessary_cast::check(cx, expr, cast_expr, cast_from, cast_to) {
if !expr.span.from_expansion() && unnecessary_cast::check(cx, expr, cast_expr, cast_from, cast_to) {
return;
}
cast_slice_from_raw_parts::check(cx, expr, cast_expr, cast_to, &self.msrv);
@ -782,7 +778,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
fn_to_numeric_cast_with_truncation::check(cx, expr, cast_expr, cast_from, cast_to);
zero_ptr::check(cx, expr, cast_expr, cast_to_hir);
if cast_to.is_numeric() && !in_external_macro(cx.sess(), expr.span) {
if cast_to.is_numeric() {
cast_possible_truncation::check(cx, expr, cast_expr, cast_from, cast_to, cast_to_hir.span);
if cast_from.is_numeric() {
cast_possible_wrap::check(cx, expr, cast_from, cast_to);

View File

@ -1016,9 +1016,18 @@ fn report<'tcx>(
},
_ => (0, false),
};
let is_in_tuple = matches!(
get_parent_expr(cx, data.first_expr),
Some(Expr {
kind: ExprKind::Tup(..),
..
})
);
let sugg = if !snip_is_macro
&& (calls_field || expr.precedence().order() < precedence)
&& !has_enclosing_paren(&snip)
&& !is_in_tuple
{
format!("({snip})")
} else {

View File

@ -132,7 +132,7 @@ declare_clippy_lint! {
///
/// ### Why is this bad?
/// Deriving `serde::Deserialize` will create a constructor
/// that may violate invariants hold by another constructor.
/// that may violate invariants held by another constructor.
///
/// ### Example
/// ```rust,ignore

View File

@ -11,7 +11,7 @@ use super::{DocHeaders, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, MISSING_SAFETY_D
pub fn check(
cx: &LateContext<'_>,
owner_id: OwnerId,
sig: &FnSig<'_>,
sig: FnSig<'_>,
headers: DocHeaders,
body_id: Option<BodyId>,
panic_span: Option<Span>,

View File

@ -3,7 +3,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
use clippy_utils::macros::{is_panic, root_macro_call_first_node};
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::visitors::Visitable;
use clippy_utils::{is_entrypoint_fn, method_chain_args};
use clippy_utils::{is_entrypoint_fn, is_trait_impl_item, method_chain_args};
use pulldown_cmark::Event::{
Code, End, FootnoteReference, HardBreak, Html, Rule, SoftBreak, Start, TaskListMarker, Text,
};
@ -11,9 +11,8 @@ use pulldown_cmark::Tag::{BlockQuote, CodeBlock, Heading, Item, Link, Paragraph}
use pulldown_cmark::{BrokenLink, CodeBlockKind, CowStr, Options};
use rustc_ast::ast::Attribute;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir as hir;
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{AnonConst, Expr};
use rustc_hir::{AnonConst, Expr, ImplItemKind, ItemKind, Node, TraitItemKind, Unsafety};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::hir::nested_filter;
use rustc_middle::lint::in_external_macro;
@ -366,7 +365,6 @@ declare_clippy_lint! {
#[derive(Clone)]
pub struct Documentation {
valid_idents: FxHashSet<String>,
in_trait_impl: bool,
check_private_items: bool,
}
@ -374,7 +372,6 @@ impl Documentation {
pub fn new(valid_idents: &[String], check_private_items: bool) -> Self {
Self {
valid_idents: valid_idents.iter().cloned().collect(),
in_trait_impl: false,
check_private_items,
}
}
@ -394,36 +391,72 @@ impl_lint_pass!(Documentation => [
]);
impl<'tcx> LateLintPass<'tcx> for Documentation {
fn check_crate(&mut self, cx: &LateContext<'tcx>) {
let attrs = cx.tcx.hir().attrs(hir::CRATE_HIR_ID);
check_attrs(cx, &self.valid_idents, attrs);
}
fn check_variant(&mut self, cx: &LateContext<'tcx>, variant: &'tcx hir::Variant<'tcx>) {
let attrs = cx.tcx.hir().attrs(variant.hir_id);
check_attrs(cx, &self.valid_idents, attrs);
}
fn check_field_def(&mut self, cx: &LateContext<'tcx>, variant: &'tcx hir::FieldDef<'tcx>) {
let attrs = cx.tcx.hir().attrs(variant.hir_id);
check_attrs(cx, &self.valid_idents, attrs);
}
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
let attrs = cx.tcx.hir().attrs(item.hir_id());
fn check_attributes(&mut self, cx: &LateContext<'tcx>, attrs: &'tcx [Attribute]) {
let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else {
return;
};
match item.kind {
hir::ItemKind::Fn(ref sig, _, body_id) => {
if !(is_entrypoint_fn(cx, item.owner_id.to_def_id()) || in_external_macro(cx.tcx.sess, item.span)) {
let body = cx.tcx.hir().body(body_id);
match cx.tcx.hir_node(cx.last_node_with_lint_attrs) {
Node::Item(item) => match item.kind {
ItemKind::Fn(sig, _, body_id) => {
if !(is_entrypoint_fn(cx, item.owner_id.to_def_id()) || in_external_macro(cx.tcx.sess, item.span)) {
let body = cx.tcx.hir().body(body_id);
let panic_span = FindPanicUnwrap::find_span(cx, cx.tcx.typeck(item.owner_id), body.value);
let panic_span = FindPanicUnwrap::find_span(cx, cx.tcx.typeck(item.owner_id), body.value);
missing_headers::check(
cx,
item.owner_id,
sig,
headers,
Some(body_id),
panic_span,
self.check_private_items,
);
}
},
ItemKind::Trait(_, unsafety, ..) => match (headers.safety, unsafety) {
(false, Unsafety::Unsafe) => span_lint(
cx,
MISSING_SAFETY_DOC,
cx.tcx.def_span(item.owner_id),
"docs for unsafe trait missing `# Safety` section",
),
(true, Unsafety::Normal) => span_lint(
cx,
UNNECESSARY_SAFETY_DOC,
cx.tcx.def_span(item.owner_id),
"docs for safe trait have unnecessary `# Safety` section",
),
_ => (),
},
_ => (),
},
Node::TraitItem(trait_item) => {
if let TraitItemKind::Fn(sig, ..) = trait_item.kind
&& !in_external_macro(cx.tcx.sess, trait_item.span)
{
missing_headers::check(
cx,
item.owner_id,
trait_item.owner_id,
sig,
headers,
None,
None,
self.check_private_items,
);
}
},
Node::ImplItem(impl_item) => {
if let ImplItemKind::Fn(sig, body_id) = impl_item.kind
&& !in_external_macro(cx.tcx.sess, impl_item.span)
&& !is_trait_impl_item(cx, impl_item.hir_id())
{
let body = cx.tcx.hir().body(body_id);
let panic_span = FindPanicUnwrap::find_span(cx, cx.tcx.typeck(impl_item.owner_id), body.value);
missing_headers::check(
cx,
impl_item.owner_id,
sig,
headers,
Some(body_id),
@ -432,67 +465,7 @@ impl<'tcx> LateLintPass<'tcx> for Documentation {
);
}
},
hir::ItemKind::Impl(impl_) => {
self.in_trait_impl = impl_.of_trait.is_some();
},
hir::ItemKind::Trait(_, unsafety, ..) => match (headers.safety, unsafety) {
(false, hir::Unsafety::Unsafe) => span_lint(
cx,
MISSING_SAFETY_DOC,
cx.tcx.def_span(item.owner_id),
"docs for unsafe trait missing `# Safety` section",
),
(true, hir::Unsafety::Normal) => span_lint(
cx,
UNNECESSARY_SAFETY_DOC,
cx.tcx.def_span(item.owner_id),
"docs for safe trait have unnecessary `# Safety` section",
),
_ => (),
},
_ => (),
}
}
fn check_item_post(&mut self, _cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
if let hir::ItemKind::Impl { .. } = item.kind {
self.in_trait_impl = false;
}
}
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
let attrs = cx.tcx.hir().attrs(item.hir_id());
let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else {
return;
};
if let hir::TraitItemKind::Fn(ref sig, ..) = item.kind {
if !in_external_macro(cx.tcx.sess, item.span) {
missing_headers::check(cx, item.owner_id, sig, headers, None, None, self.check_private_items);
}
}
}
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
let attrs = cx.tcx.hir().attrs(item.hir_id());
let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else {
return;
};
if self.in_trait_impl || in_external_macro(cx.tcx.sess, item.span) {
return;
}
if let hir::ImplItemKind::Fn(ref sig, body_id) = item.kind {
let body = cx.tcx.hir().body(body_id);
let panic_span = FindPanicUnwrap::find_span(cx, cx.tcx.typeck(item.owner_id), body.value);
missing_headers::check(
cx,
item.owner_id,
sig,
headers,
Some(body_id),
panic_span,
self.check_private_items,
);
_ => {},
}
}
}

View File

@ -7,8 +7,8 @@ use clippy_utils::{is_expn_of, is_lint_allowed, path_to_local};
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::HirId;
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::HirId;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::nested_filter;
use rustc_middle::ty;

View File

@ -5,6 +5,7 @@ use clippy_utils::is_bool;
use clippy_utils::macros::span_is_local;
use clippy_utils::source::is_present_in_source;
use clippy_utils::str_utils::{camel_case_split, count_match_end, count_match_start, to_camel_case, to_snake_case};
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::{EnumDef, FieldDef, Item, ItemKind, OwnerId, Variant, VariantData};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::impl_lint_pass;
@ -147,6 +148,7 @@ pub struct ItemNameRepetitions {
struct_threshold: u64,
avoid_breaking_exported_api: bool,
allow_private_module_inception: bool,
allowed_prefixes: FxHashSet<String>,
}
impl ItemNameRepetitions {
@ -156,6 +158,7 @@ impl ItemNameRepetitions {
struct_threshold: u64,
avoid_breaking_exported_api: bool,
allow_private_module_inception: bool,
allowed_prefixes: &[String],
) -> Self {
Self {
modules: Vec::new(),
@ -163,8 +166,13 @@ impl ItemNameRepetitions {
struct_threshold,
avoid_breaking_exported_api,
allow_private_module_inception,
allowed_prefixes: allowed_prefixes.iter().map(|s| to_camel_case(s)).collect(),
}
}
fn is_allowed_prefix(&self, prefix: &str) -> bool {
self.allowed_prefixes.contains(prefix)
}
}
impl_lint_pass!(ItemNameRepetitions => [
@ -423,7 +431,9 @@ impl LateLintPass<'_> for ItemNameRepetitions {
_ => (),
}
}
if rmatching.char_count == nchars {
if rmatching.char_count == nchars
&& !self.is_allowed_prefix(&item_camel[..item_camel.len() - rmatching.byte_count])
{
span_lint(
cx,
MODULE_NAME_REPETITIONS,

View File

@ -17,7 +17,7 @@ declare_clippy_lint! {
/// `std::<float>::EPSILON`, etc.
///
/// ### Why is this bad?
/// All of these have been superceded by the associated constants on their respective types,
/// All of these have been superseded by the associated constants on their respective types,
/// such as `i128::MAX`. These legacy items may be deprecated in a future version of rust.
///
/// ### Example

View File

@ -594,6 +594,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
pub_underscore_fields_behavior,
ref allowed_duplicate_crates,
allow_comparison_to_zero,
ref allowed_prefixes,
blacklisted_names: _,
cyclomatic_complexity_threshold: _,
@ -864,6 +865,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
struct_field_name_threshold,
avoid_breaking_exported_api,
allow_private_module_inception,
allowed_prefixes,
))
});
store.register_early_pass(|| Box::new(tabs_in_doc_comments::TabsInDocComments));

View File

@ -291,10 +291,7 @@ fn elision_suggestions(
}) => {
// expand `&'a T` to `&'a T`
// ^^ ^^^
let span = cx
.sess()
.source_map()
.span_extend_while_whitespace(usage.ident.span);
let span = cx.sess().source_map().span_extend_while_whitespace(usage.ident.span);
(span, String::new())
},

View File

@ -1,13 +1,14 @@
use clippy_utils::sugg::Sugg;
use rustc_errors::Applicability;
use rustc_hir::def::Res;
use rustc_hir::{Arm, Expr, ExprKind, HirId, LangItem, MatchSource, Pat, PatKind, QPath};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::ty::GenericArgKind;
use rustc_session::declare_lint_pass;
use rustc_span::sym;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_opt;
use clippy_utils::higher::IfLetOrMatch;
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::implements_trait;
use clippy_utils::{in_constant, is_default_equivalent, peel_blocks, span_contains_comment};
@ -105,19 +106,39 @@ fn get_some_and_none_bodies<'tcx>(
}
}
fn handle_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
let ExprKind::Match(match_expr, [arm1, arm2], MatchSource::Normal | MatchSource::ForLoopDesugar) = expr.kind else {
return false;
#[allow(clippy::needless_pass_by_value)]
fn handle<'tcx>(cx: &LateContext<'tcx>, if_let_or_match: IfLetOrMatch<'tcx>, expr: &'tcx Expr<'tcx>) {
// Get expr_name ("if let" or "match" depending on kind of expression), the condition, the body for
// the some arm, the body for the none arm and the binding id of the some arm
let (expr_name, condition, body_some, body_none, binding_id) = match if_let_or_match {
IfLetOrMatch::Match(condition, [arm1, arm2], MatchSource::Normal | MatchSource::ForLoopDesugar)
// Make sure there are no guards to keep things simple
if arm1.guard.is_none()
&& arm2.guard.is_none()
// Get the some and none bodies and the binding id of the some arm
&& let Some(((body_some, binding_id), body_none)) = get_some_and_none_bodies(cx, arm1, arm2) =>
{
("match", condition, body_some, body_none, binding_id)
},
IfLetOrMatch::IfLet(condition, pat, if_expr, Some(else_expr), _)
if let Some(binding_id) = get_some(cx, pat) =>
{
("if let", condition, if_expr, else_expr, binding_id)
},
_ => {
// All other cases (match with number of arms != 2, if let without else, etc.)
return;
},
};
// We don't want conditions on the arms to simplify things.
if arm1.guard.is_none()
&& arm2.guard.is_none()
// We check that the returned type implements the `Default` trait.
&& let match_ty = cx.typeck_results().expr_ty(expr)
&& let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default)
&& implements_trait(cx, match_ty, default_trait_id, &[])
// We now get the bodies for both the `Some` and `None` arms.
&& let Some(((body_some, binding_id), body_none)) = get_some_and_none_bodies(cx, arm1, arm2)
// We check if the return type of the expression implements Default.
let expr_type = cx.typeck_results().expr_ty(expr);
if let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default)
&& implements_trait(cx, expr_type, default_trait_id, &[])
// We check if the initial condition implements Default.
&& let Some(condition_ty) = cx.typeck_results().expr_ty(condition).walk().nth(1)
&& let GenericArgKind::Type(condition_ty) = condition_ty.unpack()
&& implements_trait(cx, condition_ty, default_trait_id, &[])
// We check that the `Some(x) => x` doesn't do anything apart "returning" the value in `Some`.
&& let ExprKind::Path(QPath::Resolved(_, path)) = peel_blocks(body_some).kind
&& let Res::Local(local_id) = path.res
@ -125,8 +146,9 @@ fn handle_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
// We now check the `None` arm is calling a method equivalent to `Default::default`.
&& let body_none = peel_blocks(body_none)
&& is_default_equivalent(cx, body_none)
&& let Some(receiver) = Sugg::hir_opt(cx, match_expr).map(Sugg::maybe_par)
&& let Some(receiver) = Sugg::hir_opt(cx, condition).map(Sugg::maybe_par)
{
// Machine applicable only if there are no comments present
let applicability = if span_contains_comment(cx.sess().source_map(), expr.span) {
Applicability::MaybeIncorrect
} else {
@ -136,48 +158,12 @@ fn handle_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
cx,
MANUAL_UNWRAP_OR_DEFAULT,
expr.span,
"match can be simplified with `.unwrap_or_default()`",
format!("{expr_name} can be simplified with `.unwrap_or_default()`"),
"replace it with",
format!("{receiver}.unwrap_or_default()"),
applicability,
);
}
true
}
fn handle_if_let<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
if let ExprKind::If(cond, if_block, Some(else_expr)) = expr.kind
&& let ExprKind::Let(let_) = cond.kind
&& let ExprKind::Block(_, _) = else_expr.kind
// We check that the returned type implements the `Default` trait.
&& let match_ty = cx.typeck_results().expr_ty(expr)
&& let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default)
&& implements_trait(cx, match_ty, default_trait_id, &[])
&& let Some(binding_id) = get_some(cx, let_.pat)
// We check that the `Some(x) => x` doesn't do anything apart "returning" the value in `Some`.
&& let ExprKind::Path(QPath::Resolved(_, path)) = peel_blocks(if_block).kind
&& let Res::Local(local_id) = path.res
&& local_id == binding_id
// We now check the `None` arm is calling a method equivalent to `Default::default`.
&& let body_else = peel_blocks(else_expr)
&& is_default_equivalent(cx, body_else)
&& let Some(if_let_expr_snippet) = snippet_opt(cx, let_.init.span)
{
let applicability = if span_contains_comment(cx.sess().source_map(), expr.span) {
Applicability::MaybeIncorrect
} else {
Applicability::MachineApplicable
};
span_lint_and_sugg(
cx,
MANUAL_UNWRAP_OR_DEFAULT,
expr.span,
"if let can be simplified with `.unwrap_or_default()`",
"replace it with",
format!("{if_let_expr_snippet}.unwrap_or_default()"),
applicability,
);
}
}
impl<'tcx> LateLintPass<'tcx> for ManualUnwrapOrDefault {
@ -185,8 +171,9 @@ impl<'tcx> LateLintPass<'tcx> for ManualUnwrapOrDefault {
if expr.span.from_expansion() || in_constant(cx, expr.hir_id) {
return;
}
if !handle_match(cx, expr) {
handle_if_let(cx, expr);
// Call handle only if the expression is `if let` or `match`
if let Some(if_let_or_match) = IfLetOrMatch::parse(cx, expr) {
handle(cx, if_let_or_match, expr);
}
}
}

View File

@ -3938,7 +3938,6 @@ declare_clippy_lint! {
/// This lint cannot detect if the split is intentionally restricted to a single type of newline (`"\n"` or
/// `"\r\n"`), for example during the parsing of a specific file format in which precisely one newline type is
/// valid.
/// ```
#[clippy::version = "1.77.0"]
pub STR_SPLIT_AT_NEWLINE,
pedantic,

View File

@ -2,7 +2,8 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
use clippy_utils::source::{snippet, snippet_with_applicability};
use clippy_utils::sugg::deref_closure_args;
use clippy_utils::ty::is_type_lang_item;
use clippy_utils::{is_trait_method, strip_pat_refs};
use clippy_utils::{get_parent_expr, is_trait_method, strip_pat_refs};
use hir::ExprKind;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::PatKind;
@ -35,7 +36,7 @@ pub(super) fn check<'tcx>(
// suggest `any(|..| *..)` instead of `any(|..| **..)` for `find(|..| **..).is_some()`
let mut applicability = Applicability::MachineApplicable;
let any_search_snippet = if search_method == "find"
&& let hir::ExprKind::Closure(&hir::Closure { body, .. }) = search_arg.kind
&& let ExprKind::Closure(&hir::Closure { body, .. }) = search_arg.kind
&& let closure_body = cx.tcx.hir().body(body)
&& let Some(closure_arg) = closure_body.params.first()
{
@ -72,16 +73,24 @@ pub(super) fn check<'tcx>(
);
} else {
let iter = snippet(cx, search_recv.span, "..");
let sugg = if is_receiver_of_method_call(cx, expr) {
format!(
"(!{iter}.any({}))",
any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str)
)
} else {
format!(
"!{iter}.any({})",
any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str)
)
};
span_lint_and_sugg(
cx,
SEARCH_IS_SOME,
expr.span,
msg,
"consider using",
format!(
"!{iter}.any({})",
any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str)
),
sugg,
applicability,
);
}
@ -127,13 +136,18 @@ pub(super) fn check<'tcx>(
let string = snippet(cx, search_recv.span, "..");
let mut applicability = Applicability::MachineApplicable;
let find_arg = snippet_with_applicability(cx, search_arg.span, "..", &mut applicability);
let sugg = if is_receiver_of_method_call(cx, expr) {
format!("(!{string}.contains({find_arg}))")
} else {
format!("!{string}.contains({find_arg})")
};
span_lint_and_sugg(
cx,
SEARCH_IS_SOME,
expr.span,
msg,
"consider using",
format!("!{string}.contains({find_arg})"),
sugg,
applicability,
);
},
@ -142,3 +156,13 @@ pub(super) fn check<'tcx>(
}
}
}
fn is_receiver_of_method_call(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
if let Some(parent_expr) = get_parent_expr(cx, expr)
&& let ExprKind::MethodCall(_, receiver, ..) = parent_expr.kind
&& receiver.hir_id == expr.hir_id
{
return true;
}
false
}

View File

@ -23,7 +23,7 @@ use std::collections::VecDeque;
declare_clippy_lint! {
/// ### What it does
/// Checks for borrow operations (`&`) that used as a generic argument to a
/// Checks for borrow operations (`&`) that are used as a generic argument to a
/// function when the borrowed value could be used.
///
/// ### Why is this bad?

View File

@ -18,7 +18,7 @@ use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult, GlobalId};
use rustc_middle::ty::adjustment::Adjust;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_session::impl_lint_pass;
use rustc_span::{sym, DUMMY_SP, InnerSpan, Span};
use rustc_span::{sym, InnerSpan, Span, DUMMY_SP};
use rustc_target::abi::VariantIdx;
// FIXME: this is a correctness problem but there's no suitable
@ -297,12 +297,7 @@ impl NonCopyConst {
fn is_value_unfrozen_expr<'tcx>(&self, cx: &LateContext<'tcx>, hir_id: HirId, def_id: DefId, ty: Ty<'tcx>) -> bool {
let args = cx.typeck_results().node_args(hir_id);
let result = Self::const_eval_resolve(
cx.tcx,
cx.param_env,
ty::UnevaluatedConst::new(def_id, args),
DUMMY_SP,
);
let result = Self::const_eval_resolve(cx.tcx, cx.param_env, ty::UnevaluatedConst::new(def_id, args), DUMMY_SP);
self.is_value_unfrozen_raw(cx, result, ty)
}

View File

@ -300,11 +300,8 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes {
e.span,
"calling `as_bytes()` on `include_str!(..)`",
"consider using `include_bytes!(..)` instead",
snippet_with_applicability(cx, receiver.span.source_callsite(), r#""foo""#, &mut applicability).replacen(
"include_str",
"include_bytes",
1,
),
snippet_with_applicability(cx, receiver.span.source_callsite(), r#""foo""#, &mut applicability)
.replacen("include_str", "include_bytes", 1),
applicability,
);
} else if lit_content.as_str().is_ascii()

View File

@ -27,7 +27,7 @@ pub(super) fn check<'tcx>(
};
// FIXME: This can be simplified once `NonZero<T>` is stable.
let coercable_types = [
let coercible_types = [
("NonZeroU8", tcx.types.u8),
("NonZeroU16", tcx.types.u16),
("NonZeroU32", tcx.types.u32),
@ -44,7 +44,7 @@ pub(super) fn check<'tcx>(
let int_type = substs.type_at(0);
let Some(nonzero_alias) = coercable_types.iter().find_map(|(nonzero_alias, t)| {
let Some(nonzero_alias) = coercible_types.iter().find_map(|(nonzero_alias, t)| {
if *t == int_type && *t == from_ty {
Some(nonzero_alias)
} else {

View File

@ -1,10 +1,10 @@
use rustc_hir_typeck::cast::check_cast;
use super::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::sugg::Sugg;
use rustc_ast::ExprPrecedence;
use rustc_errors::Applicability;
use rustc_hir::{Expr, Node};
use rustc_hir_typeck::cast::check_cast;
use rustc_lint::LateContext;
use rustc_middle::ty::cast::CastKind;
use rustc_middle::ty::Ty;

View File

@ -709,8 +709,12 @@ pub fn eq_ty(l: &Ty, r: &Ty) -> bool {
(Tup(l), Tup(r)) => over(l, r, |l, r| eq_ty(l, r)),
(Path(lq, lp), Path(rq, rp)) => both(lq, rq, eq_qself) && eq_path(lp, rp),
(TraitObject(lg, ls), TraitObject(rg, rs)) => ls == rs && over(lg, rg, eq_generic_bound),
(ImplTrait(_, lg, lc), ImplTrait(_, rg, rc)) =>
over(lg, rg, eq_generic_bound) && both(lc, rc, |lc, rc| over(lc.0.as_slice(), rc.0.as_slice(), eq_precise_capture)),
(ImplTrait(_, lg, lc), ImplTrait(_, rg, rc)) => {
over(lg, rg, eq_generic_bound)
&& both(lc, rc, |lc, rc| {
over(lc.0.as_slice(), rc.0.as_slice(), eq_precise_capture)
})
},
(Typeof(l), Typeof(r)) => eq_expr(&l.value, &r.value),
(MacCall(l), MacCall(r)) => eq_mac_call(l, r),
_ => false,

View File

@ -3285,7 +3285,7 @@ fn maybe_get_relative_path(from: &DefPath, to: &DefPath, max_super: usize) -> St
Right(r) => Right(r.data),
});
// 2. for the remaning segments, construct relative path using only mod names and `super`
// 2. for the remaining segments, construct relative path using only mod names and `super`
let mut go_up_by = 0;
let mut path = Vec::new();
for el in unique_parts {

View File

@ -10,7 +10,7 @@ use rustc_hir as hir;
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::{Expr, FnDecl, LangItem, TyKind, Unsafety};
use rustc_infer::infer::type_variable::{TypeVariableOrigin};
use rustc_infer::infer::type_variable::TypeVariableOrigin;
use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::LateContext;
use rustc_middle::mir::interpret::Scalar;

View File

@ -90,7 +90,7 @@ fn expr_type_certainty(cx: &LateContext<'_>, expr: &Expr<'_>) -> Certainty {
if let Some(def_id) = adt_def_id(expr_ty) {
certainty.with_def_id(def_id)
} else {
certainty
certainty.clear_def_id()
}
}

View File

@ -26,12 +26,12 @@ use std::env::consts::EXE_SUFFIX;
use std::fmt::{self, Write as _};
use std::io::{self, ErrorKind};
use std::path::{Path, PathBuf};
use std::process::Command;
use std::process::{Command, ExitStatus};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::Duration;
use std::{env, fs, thread};
use cargo_metadata::diagnostic::{Diagnostic, DiagnosticLevel};
use cargo_metadata::diagnostic::Diagnostic;
use cargo_metadata::Message;
use rayon::prelude::*;
use serde::{Deserialize, Serialize};
@ -97,16 +97,43 @@ struct Crate {
options: Option<Vec<String>>,
}
/// A single emitted output from clippy being executed on a crate. It may either be a
/// `ClippyWarning`, or a `RustcIce` caused by a panic within clippy. A crate may have many
/// `ClippyWarning`s but a maximum of one `RustcIce` (at which point clippy halts execution).
#[derive(Debug)]
enum ClippyCheckOutput {
ClippyWarning(ClippyWarning),
RustcIce(RustcIce),
}
#[derive(Debug)]
struct RustcIce {
pub crate_name: String,
pub ice_content: String,
}
impl RustcIce {
pub fn from_stderr_and_status(crate_name: &str, status: ExitStatus, stderr: &str) -> Option<Self> {
if status.code().unwrap_or(0) == 101
/* ice exit status */
{
Some(Self {
crate_name: crate_name.to_owned(),
ice_content: stderr.to_owned(),
})
} else {
None
}
}
}
/// A single warning that clippy issued while checking a `Crate`
#[derive(Debug)]
struct ClippyWarning {
crate_name: String,
file: String,
line: usize,
column: usize,
lint_type: String,
message: String,
is_ice: bool,
}
#[allow(unused)]
@ -131,13 +158,11 @@ impl ClippyWarning {
};
Some(Self {
crate_name: crate_name.to_owned(),
file,
line: span.line_start,
column: span.column_start,
lint_type,
message: diag.message,
is_ice: diag.level == DiagnosticLevel::Ice,
})
}
@ -318,7 +343,7 @@ impl Crate {
config: &LintcheckConfig,
lint_filter: &[String],
server: &Option<LintcheckServer>,
) -> Vec<ClippyWarning> {
) -> Vec<ClippyCheckOutput> {
// advance the atomic index by one
let index = target_dir_index.fetch_add(1, Ordering::SeqCst);
// "loop" the index within 0..thread_limit
@ -342,9 +367,9 @@ impl Crate {
let shared_target_dir = clippy_project_root().join("target/lintcheck/shared_target_dir");
let mut cargo_clippy_args = if config.fix {
vec!["--fix", "--"]
vec!["--quiet", "--fix", "--"]
} else {
vec!["--", "--message-format=json", "--"]
vec!["--quiet", "--message-format=json", "--"]
};
let mut clippy_args = Vec::<&str>::new();
@ -435,14 +460,21 @@ impl Crate {
}
// get all clippy warnings and ICEs
let warnings: Vec<ClippyWarning> = Message::parse_stream(stdout.as_bytes())
let mut entries: Vec<ClippyCheckOutput> = Message::parse_stream(stdout.as_bytes())
.filter_map(|msg| match msg {
Ok(Message::CompilerMessage(message)) => ClippyWarning::new(message.message, &self.name, &self.version),
_ => None,
})
.map(ClippyCheckOutput::ClippyWarning)
.collect();
warnings
if let Some(ice) = RustcIce::from_stderr_and_status(&self.name, *status, &stderr) {
entries.push(ClippyCheckOutput::RustcIce(ice));
} else if !status.success() {
println!("non-ICE bad exit status for {} {}: {}", self.name, self.version, stderr);
}
entries
}
}
@ -642,7 +674,7 @@ fn main() {
LintcheckServer::spawn(recursive_options)
});
let mut clippy_warnings: Vec<ClippyWarning> = crates
let mut clippy_entries: Vec<ClippyCheckOutput> = crates
.par_iter()
.flat_map(|krate| {
krate.run_clippy_lints(
@ -658,7 +690,9 @@ fn main() {
.collect();
if let Some(server) = server {
clippy_warnings.extend(server.warnings());
let server_clippy_entries = server.warnings().map(ClippyCheckOutput::ClippyWarning);
clippy_entries.extend(server_clippy_entries);
}
// if we are in --fix mode, don't change the log files, terminate here
@ -666,20 +700,21 @@ fn main() {
return;
}
// split up warnings and ices
let mut warnings: Vec<ClippyWarning> = vec![];
let mut raw_ices: Vec<RustcIce> = vec![];
for entry in clippy_entries {
if let ClippyCheckOutput::ClippyWarning(x) = entry {
warnings.push(x);
} else if let ClippyCheckOutput::RustcIce(x) = entry {
raw_ices.push(x);
}
}
// generate some stats
let (stats_formatted, new_stats) = gather_stats(&clippy_warnings);
let (stats_formatted, new_stats) = gather_stats(&warnings);
// grab crashes/ICEs, save the crate name and the ice message
let ices: Vec<(&String, &String)> = clippy_warnings
.iter()
.filter(|warning| warning.is_ice)
.map(|w| (&w.crate_name, &w.message))
.collect();
let mut all_msgs: Vec<String> = clippy_warnings
.iter()
.map(|warn| warn.to_output(config.markdown))
.collect();
let mut all_msgs: Vec<String> = warnings.iter().map(|warn| warn.to_output(config.markdown)).collect();
all_msgs.sort();
all_msgs.push("\n\n### Stats:\n\n".into());
all_msgs.push(stats_formatted);
@ -693,11 +728,18 @@ fn main() {
}
write!(text, "{}", all_msgs.join("")).unwrap();
text.push_str("\n\n### ICEs:\n");
for (cratename, msg) in &ices {
let _: fmt::Result = write!(text, "{cratename}: '{msg}'");
for ice in &raw_ices {
let _: fmt::Result = write!(
text,
"{}:\n{}\n========================================\n\n",
ice.crate_name, ice.ice_content
);
}
println!("Writing logs to {}", config.lintcheck_results_path.display());
if !raw_ices.is_empty() {
println!("WARNING: at least one ICE reported, check log file");
}
fs::create_dir_all(config.lintcheck_results_path.parent().unwrap()).unwrap();
fs::write(&config.lintcheck_results_path, text).unwrap();

View File

@ -1,3 +1,3 @@
[toolchain]
channel = "nightly-2024-04-04"
channel = "nightly-2024-04-18"
components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]

View File

@ -5,7 +5,6 @@
//@normalize-stderr-test: "'rustc'" -> "'<unnamed>'"
//@normalize-stderr-test: "rustc 1\.\d+.* running on .*" -> "rustc <version> running on <target>"
//@normalize-stderr-test: "(?ms)query stack during panic:\n.*end of query stack\n" -> ""
//@normalize-stderr-test: "this compiler `.*` is outdated" -> "this compiler <version> is outdated"
#![deny(clippy::internal)]
#![allow(clippy::missing_clippy_version_attribute)]

View File

@ -4,10 +4,9 @@ note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
error: the compiler unexpectedly panicked. this is a bug.
note: it seems that this compiler <version> is outdated, a newer nightly should have been released in the meantime
|
= note: please consider running `rustup update nightly` to update the nightly channel and check if this problem still persists
= note: if the problem still persists, we would appreciate a bug report: https://github.com/rust-lang/rust-clippy/issues/new?template=ice.yml
note: we would appreciate a bug report: https://github.com/rust-lang/rust-clippy/issues/new?template=ice.yml
note: please make sure that you have updated to the latest nightly
note: rustc <version> running on <target>

View File

@ -0,0 +1 @@
allowed-prefixes = ["bar"]

View File

@ -0,0 +1,15 @@
#![warn(clippy::module_name_repetitions)]
#![allow(dead_code)]
mod foo {
// #12544 - shouldn't warn if item name consists only of an allowed prefix and a module name.
// In this test, allowed prefixes are configured to be ["bar"].
// this line should produce a warning:
pub fn to_foo() {}
// but this line shouldn't
pub fn bar_foo() {}
}
fn main() {}

View File

@ -0,0 +1,11 @@
error: item name ends with its containing module's name
--> tests/ui-toml/item_name_repetitions/allowed_prefixes/item_name_repetitions.rs:9:12
|
LL | pub fn to_foo() {}
| ^^^^^^
|
= note: `-D clippy::module-name-repetitions` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::module_name_repetitions)]`
error: aborting due to 1 previous error

View File

@ -0,0 +1 @@
allowed-prefixes = ["..", "bar"]

View File

@ -0,0 +1,21 @@
#![warn(clippy::module_name_repetitions)]
#![allow(dead_code)]
mod foo {
// #12544 - shouldn't warn if item name consists only of an allowed prefix and a module name.
// In this test, allowed prefixes are configured to be all of the default prefixes and ["bar"].
// this line should produce a warning:
pub fn something_foo() {}
// but none of the following should:
pub fn bar_foo() {}
pub fn to_foo() {}
pub fn as_foo() {}
pub fn into_foo() {}
pub fn from_foo() {}
pub fn try_into_foo() {}
pub fn try_from_foo() {}
}
fn main() {}

View File

@ -0,0 +1,11 @@
error: item name ends with its containing module's name
--> tests/ui-toml/item_name_repetitions/allowed_prefixes_extend/item_name_repetitions.rs:9:12
|
LL | pub fn something_foo() {}
| ^^^^^^^^^^^^^
|
= note: `-D clippy::module-name-repetitions` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::module_name_repetitions)]`
error: aborting due to 1 previous error

Some files were not shown because too many files have changed in this diff Show More