Auto merge of #3494 - rust-lang:rustup-2024-04-20, r=RalfJung
Automatic Rustup
This commit is contained in:
commit
9713ff45ab
@ -5598,6 +5598,7 @@ dependencies = [
|
||||
"lazy_static",
|
||||
"miropt-test-tools",
|
||||
"regex",
|
||||
"rustc-hash",
|
||||
"semver",
|
||||
"termcolor",
|
||||
"walkdir",
|
||||
|
@ -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),
|
||||
|
@ -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
|
||||
|
@ -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) => {
|
||||
|
@ -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) => {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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>;
|
||||
|
||||
|
@ -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)))
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
|
@ -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`).
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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]);
|
||||
}
|
||||
|
@ -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,
|
||||
},
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 }
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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)]
|
||||
|
@ -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)),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
|
@ -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]
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
///
|
||||
|
@ -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()
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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());
|
||||
|
@ -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),
|
||||
&[
|
||||
|
@ -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(
|
||||
|
@ -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 => {
|
||||
|
@ -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
|
||||
|
@ -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`.
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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,
|
||||
|
@ -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",
|
||||
]
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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),
|
||||
}
|
||||
|
||||
|
10
src/doc/unstable-book/src/compiler-flags/wasm-c-abi.md
Normal file
10
src/doc/unstable-book/src/compiler-flags/wasm-c-abi.md
Normal 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.
|
@ -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))
|
||||
}
|
||||
|
@ -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"),
|
||||
};
|
||||
|
@ -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>\
|
||||
|
@ -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
|
@ -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: |
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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)]
|
||||
|
@ -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")
|
||||
|
@ -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>,
|
||||
|
@ -1,5 +1,6 @@
|
||||
pub mod git_hook;
|
||||
pub mod intellij;
|
||||
pub mod toolchain;
|
||||
pub mod vscode;
|
||||
|
||||
use std::path::Path;
|
||||
|
75
src/tools/clippy/clippy_dev/src/setup/toolchain.rs
Normal file
75
src/tools/clippy/clippy_dev/src/setup/toolchain.rs
Normal 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();
|
||||
}
|
@ -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`"
|
||||
));
|
||||
});
|
||||
},
|
||||
|
@ -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 {
|
||||
|
@ -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) {
|
||||
|
@ -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, [], _) => {
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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>,
|
||||
|
@ -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,
|
||||
);
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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));
|
||||
|
@ -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())
|
||||
},
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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?
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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"]
|
||||
|
@ -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)]
|
||||
|
@ -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>
|
||||
|
||||
|
@ -0,0 +1 @@
|
||||
allowed-prefixes = ["bar"]
|
@ -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() {}
|
@ -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
|
||||
|
@ -0,0 +1 @@
|
||||
allowed-prefixes = ["..", "bar"]
|
@ -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() {}
|
@ -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
Loading…
x
Reference in New Issue
Block a user