static_assert_size!(Fn, 192);
static_assert_size!(ForeignItem, 96);
static_assert_size!(ForeignItemKind, 24);
+ static_assert_size!(GenericArg, 24);
static_assert_size!(GenericBound, 88);
static_assert_size!(Generics, 72);
static_assert_size!(Impl, 200);
static_assert_size!(ItemKind, 112);
static_assert_size!(Lit, 48);
static_assert_size!(LitKind, 24);
+ static_assert_size!(Local, 72);
+ static_assert_size!(Param, 40);
static_assert_size!(Pat, 120);
static_assert_size!(PatKind, 96);
static_assert_size!(Path, 40);
let op = match *op {
InlineAsmOperand::In { reg, ref expr } => hir::InlineAsmOperand::In {
reg: lower_reg(reg),
- expr: self.lower_expr_mut(expr),
+ expr: self.lower_expr(expr),
},
InlineAsmOperand::Out { reg, late, ref expr } => hir::InlineAsmOperand::Out {
reg: lower_reg(reg),
late,
- expr: expr.as_ref().map(|expr| self.lower_expr_mut(expr)),
+ expr: expr.as_ref().map(|expr| self.lower_expr(expr)),
},
InlineAsmOperand::InOut { reg, late, ref expr } => {
hir::InlineAsmOperand::InOut {
reg: lower_reg(reg),
late,
- expr: self.lower_expr_mut(expr),
+ expr: self.lower_expr(expr),
}
}
InlineAsmOperand::SplitInOut { reg, late, ref in_expr, ref out_expr } => {
hir::InlineAsmOperand::SplitInOut {
reg: lower_reg(reg),
late,
- in_expr: self.lower_expr_mut(in_expr),
- out_expr: out_expr.as_ref().map(|expr| self.lower_expr_mut(expr)),
+ in_expr: self.lower_expr(in_expr),
+ out_expr: out_expr.as_ref().map(|expr| self.lower_expr(expr)),
}
}
InlineAsmOperand::Const { ref anon_const } => {
ParenthesizedGenericArgs::Err,
ImplTraitContext::Disallowed(ImplTraitPosition::Path),
));
- let args = self.arena.alloc_from_iter(
- [&*receiver].into_iter().chain(args.iter()).map(|x| self.lower_expr_mut(x)),
- );
- hir::ExprKind::MethodCall(hir_seg, args, self.lower_span(span))
+ let receiver = self.lower_expr(receiver);
+ let args =
+ self.arena.alloc_from_iter(args.iter().map(|x| self.lower_expr_mut(x)));
+ hir::ExprKind::MethodCall(hir_seg, receiver, args, self.lower_span(span))
}
ExprKind::Binary(binop, ref lhs, ref rhs) => {
let binop = self.lower_binop(binop);
binding: hir::HirId,
attrs: AttrVec,
) -> hir::Expr<'hir> {
+ let hir_id = self.next_id();
+ let res = Res::Local(binding);
let expr_path = hir::ExprKind::Path(hir::QPath::Resolved(
None,
self.arena.alloc(hir::Path {
span: self.lower_span(span),
- res: Res::Local(binding),
- segments: arena_vec![self; hir::PathSegment::from_ident(ident)],
+ res,
+ segments: arena_vec![self; hir::PathSegment::new(ident, hir_id, res)],
}),
));
}
fn visit_path_segment(&mut self, path_span: Span, path_segment: &'hir PathSegment<'hir>) {
- if let Some(hir_id) = path_segment.hir_id {
- self.insert(path_span, hir_id, Node::PathSegment(path_segment));
- }
+ self.insert(path_span, path_segment.hir_id, Node::PathSegment(path_segment));
intravisit::walk_path_segment(self, path_span, path_segment);
}
self.with_lctx(CRATE_NODE_ID, |lctx| {
let module = lctx.lower_mod(&c.items, &c.spans);
lctx.lower_attrs(hir::CRATE_HIR_ID, &c.attrs);
- hir::OwnerNode::Crate(lctx.arena.alloc(module))
+ hir::OwnerNode::Crate(module)
})
}
}
impl<'hir> LoweringContext<'_, 'hir> {
- pub(super) fn lower_mod(&mut self, items: &[P<Item>], spans: &ModSpans) -> hir::Mod<'hir> {
- hir::Mod {
+ pub(super) fn lower_mod(
+ &mut self,
+ items: &[P<Item>],
+ spans: &ModSpans,
+ ) -> &'hir hir::Mod<'hir> {
+ self.arena.alloc(hir::Mod {
spans: hir::ModSpans {
inner_span: self.lower_span(spans.inner_span),
inject_use_span: self.lower_span(spans.inject_use_span),
},
item_ids: self.arena.alloc_from_iter(items.iter().flat_map(|x| self.lower_item_ref(x))),
- }
+ })
}
pub(super) fn lower_item_ref(&mut self, i: &Item) -> SmallVec<[hir::ItemId; 1]> {
params: &'hir [hir::Param<'hir>],
value: hir::Expr<'hir>,
) -> hir::BodyId {
- let body = hir::Body { generator_kind: self.generator_kind, params, value };
+ let body = hir::Body {
+ generator_kind: self.generator_kind,
+ params,
+ value: self.arena.alloc(value),
+ };
let id = body.id();
debug_assert_eq!(id.hir_id.owner, self.current_hir_id_owner);
self.bodies.push((id.hir_id.local_id, self.arena.alloc(body)));
GenericParamKind::Const { .. } => None,
GenericParamKind::Type { .. } => {
let def_id = self.local_def_id(id).to_def_id();
+ let hir_id = self.next_id();
+ let res = Res::Def(DefKind::TyParam, def_id);
let ty_path = self.arena.alloc(hir::Path {
span: param_span,
- res: Res::Def(DefKind::TyParam, def_id),
- segments: self.arena.alloc_from_iter([hir::PathSegment::from_ident(ident)]),
+ res,
+ segments: self
+ .arena
+ .alloc_from_iter([hir::PathSegment::new(ident, hir_id, res)]),
});
let ty_id = self.next_id();
let bounded_ty =
}
_ => {}
}
- GenericArg::Type(self.lower_ty_direct(&ty, itctx))
+ GenericArg::Type(self.lower_ty(&ty, itctx))
}
ast::GenericArg::Const(ct) => GenericArg::Const(ConstArg {
value: self.lower_anon_const(&ct),
return self.lower_path_ty(t, qself, path, ParamMode::Explicit, itctx);
}
TyKind::ImplicitSelf => {
+ let hir_id = self.lower_node_id(t.id);
let res = self.expect_full_res(t.id);
let res = self.lower_res(res);
hir::TyKind::Path(hir::QPath::Resolved(
None,
self.arena.alloc(hir::Path {
res,
- segments: arena_vec![self; hir::PathSegment::from_ident(
- Ident::with_dummy_span(kw::SelfUpper)
+ segments: arena_vec![self; hir::PathSegment::new(
+ Ident::with_dummy_span(kw::SelfUpper),
+ hir_id,
+ res
)],
span: self.lower_span(t.span),
}),
hir::PredicateOrigin::ImplTrait,
);
+ let hir_id = self.next_id();
+ let res = Res::Def(DefKind::TyParam, def_id.to_def_id());
let ty = hir::TyKind::Path(hir::QPath::Resolved(
None,
self.arena.alloc(hir::Path {
span: self.lower_span(span),
- res: Res::Def(DefKind::TyParam, def_id.to_def_id()),
- segments: arena_vec![self; hir::PathSegment::from_ident(self.lower_ident(ident))],
+ res,
+ segments:
+ arena_vec![self; hir::PathSegment::new(self.lower_ident(ident), hir_id, res)],
}),
));
lower_sub(self),
)
}
- Some(res) => hir::PatKind::Path(hir::QPath::Resolved(
- None,
- self.arena.alloc(hir::Path {
- span: self.lower_span(ident.span),
- res: self.lower_res(res),
- segments: arena_vec![self; hir::PathSegment::from_ident(self.lower_ident(ident))],
- }),
- )),
+ Some(res) => {
+ let hir_id = self.next_id();
+ let res = self.lower_res(res);
+ hir::PatKind::Path(hir::QPath::Resolved(
+ None,
+ self.arena.alloc(hir::Path {
+ span: self.lower_span(ident.span),
+ res,
+ segments: arena_vec![self; hir::PathSegment::new(self.lower_ident(ident), hir_id, res)],
+ }),
+ ))
+ }
}
}
}
let res = self.expect_full_res(segment.id);
- let id = self.lower_node_id(segment.id);
+ let hir_id = self.lower_node_id(segment.id);
debug!(
"lower_path_segment: ident={:?} original-id={:?} new-id={:?}",
- segment.ident, segment.id, id,
+ segment.ident, segment.id, hir_id,
);
hir::PathSegment {
ident: self.lower_ident(segment.ident),
- hir_id: Some(id),
- res: Some(self.lower_res(res)),
+ hir_id,
+ res: self.lower_res(res),
infer_args,
args: if generic_args.is_empty() && generic_args.span.is_empty() {
None
}
FnRetTy::Default(_) => self.arena.alloc(self.ty_tup(*span, &[])),
};
- let args = smallvec![GenericArg::Type(self.ty_tup(*inputs_span, inputs))];
+ let args = smallvec![GenericArg::Type(self.arena.alloc(self.ty_tup(*inputs_span, inputs)))];
let binding = self.output_ty_binding(output_ty.span, output_ty);
(
GenericArgsCtor {
let ty = place.ty(self.body, self.infcx.tcx).ty;
// If we're in pattern, we do nothing in favor of the previous suggestion (#80913).
- if is_loop_move & !in_pattern {
+ // Same for if we're in a loop, see #101119.
+ if is_loop_move & !in_pattern && !matches!(use_spans, UseSpans::ClosureUse { .. }) {
if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind() {
// We have a `&mut` ref, we need to reborrow on each iteration (#62112).
err.span_suggestion_verbose(
Applicability::MachineApplicable,
);
self.suggested = true;
- } else if let hir::ExprKind::MethodCall(_path, args @ [_, ..], sp) = expr.kind
- && let hir::ExprKind::Index(val, index) = args[0].kind
+ } else if let hir::ExprKind::MethodCall(_path, receiver, _, sp) = expr.kind
+ && let hir::ExprKind::Index(val, index) = receiver.kind
&& expr.span == self.assign_span
{
// val[index].path(args..);
".get_mut(".to_string(),
),
(
- index.span.shrink_to_hi().with_hi(args[0].span.hi()),
+ index.span.shrink_to_hi().with_hi(receiver.span.hi()),
").map(|val| val".to_string(),
),
(sp.shrink_to_hi(), ")".to_string()),
[
Expr {
kind:
- MethodCall(
- path_segment,
- _args,
- span,
- ),
+ MethodCall(path_segment, _, _, span),
hir_id,
..
},
_,
) = hir_map.body(fn_body_id).value.kind
{
- let opt_suggestions = path_segment
- .hir_id
- .map(|path_hir_id| self.infcx.tcx.typeck(path_hir_id.owner))
- .and_then(|typeck| typeck.type_dependent_def_id(*hir_id))
+ let opt_suggestions = self
+ .infcx
+ .tcx
+ .typeck(path_segment.hir_id.owner)
+ .type_dependent_def_id(*hir_id)
.and_then(|def_id| self.infcx.tcx.impl_of_method(def_id))
.map(|def_id| self.infcx.tcx.associated_items(def_id))
.map(|assoc_items| {
let mut closure_span = None::<rustc_span::Span>;
match expr.kind {
hir::ExprKind::MethodCall(.., args, _) => {
- // only the first closre parameter of the method. args[0] is MethodCall PathSegment
- for i in 1..args.len() {
+ for arg in args {
if let hir::ExprKind::Closure(hir::Closure {
capture_clause: hir::CaptureBy::Ref,
..
- }) = args[i].kind
+ }) = arg.kind
{
- closure_span = Some(args[i].span.shrink_to_lo());
+ closure_span = Some(arg.span.shrink_to_lo());
break;
}
}
use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
use rustc_hir::lang_items::LangItem;
use rustc_index::vec::Idx;
-use rustc_middle::mir::AssertKind;
-use rustc_middle::mir::{self, SwitchTargets};
+use rustc_middle::mir::{self, AssertKind, SwitchTargets};
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths};
use rustc_middle::ty::{self, Instance, Ty, TypeVisitable};
// All `#[rustc_do_not_const_check]` functions should be hooked here.
let def_id = instance.def_id();
- if Some(def_id) == self.tcx.lang_items().const_eval_select() {
- // redirect to const_eval_select_ct
- if let Some(const_eval_select) = self.tcx.lang_items().const_eval_select_ct() {
- return Ok(Some(
- ty::Instance::resolve(
- *self.tcx,
- ty::ParamEnv::reveal_all(),
- const_eval_select,
- instance.substs,
- )
- .unwrap()
- .unwrap(),
- ));
- }
- } else if Some(def_id) == self.tcx.lang_items().panic_display()
+ if Some(def_id) == self.tcx.lang_items().panic_display()
|| Some(def_id) == self.tcx.lang_items().begin_panic_fn()
{
// &str or &&str
use std::fmt::Debug;
use std::hash::Hash;
+use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
use rustc_middle::mir;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::def_id::DefId;
kind: Option<MemoryKind<Self::MemoryKind>>,
) -> InterpResult<'tcx, Cow<'b, Allocation<Self::Provenance, Self::AllocExtra>>>;
+ fn eval_inline_asm(
+ _ecx: &mut InterpCx<'mir, 'tcx, Self>,
+ _template: &'tcx [InlineAsmTemplatePiece],
+ _operands: &[mir::InlineAsmOperand<'tcx>],
+ _options: InlineAsmOptions,
+ ) -> InterpResult<'tcx> {
+ throw_unsup_format!("inline assembly is not supported")
+ }
+
/// Hook for performing extra checks on a memory read access.
///
/// Takes read-only access to the allocation so we can keep all the memory read
use std::borrow::Cow;
+use rustc_ast::ast::InlineAsmOptions;
use rustc_middle::ty::layout::{FnAbiOf, LayoutOf};
use rustc_middle::ty::Instance;
use rustc_middle::{
terminator.kind
),
- // Inline assembly can't be interpreted.
- InlineAsm { .. } => throw_unsup_format!("inline assembly is not supported"),
+ InlineAsm { template, ref operands, options, destination, .. } => {
+ M::eval_inline_asm(self, template, operands, options)?;
+ if options.contains(InlineAsmOptions::NORETURN) {
+ throw_ub_format!("returned from noreturn inline assembly");
+ }
+ self.go_to_block(
+ destination
+ .expect("InlineAsm terminators without noreturn must have a destination"),
+ )
+ }
}
Ok(())
///
/// **Belongs to the type namespace.**
PrimTy(hir::PrimTy),
+
/// The `Self` type, optionally with the [`DefId`] of the trait it belongs to and
/// optionally with the [`DefId`] of the item introducing the `Self` type alias.
///
/// const fn baz<T>() -> usize { 10 }
/// ```
/// We do however allow `Self` in repeat expression even if it is generic to not break code
- /// which already works on stable while causing the `const_evaluatable_unchecked` future compat lint:
+ /// which already works on stable while causing the `const_evaluatable_unchecked` future compat
+ /// lint:
/// ```
/// fn foo<T>() {
/// let _bar = [1_u8; std::mem::size_of::<*mut T>()];
/// from mentioning generics (i.e. when used in an anonymous constant).
alias_to: Option<(DefId, bool)>,
},
+
/// A tool attribute module; e.g., the `rustfmt` in `#[rustfmt::skip]`.
///
/// **Belongs to the type namespace.**
///
/// *See also [`Res::SelfTy`].*
SelfCtor(DefId),
+
/// A local variable or function parameter.
///
/// **Belongs to the value namespace.**
pub struct PathSegment<'hir> {
/// The identifier portion of this path segment.
pub ident: Ident,
- // `id` and `res` are optional. We currently only use these in save-analysis,
- // any path segments without these will not have save-analysis info and
- // therefore will not have 'jump to def' in IDEs, but otherwise will not be
- // affected. (In general, we don't bother to get the defs for synthesized
- // segments, only for segments which have come from the AST).
- pub hir_id: Option<HirId>,
- pub res: Option<Res>,
+ pub hir_id: HirId,
+ pub res: Res,
/// Type/lifetime parameters attached to this path. They come in
/// two flavors: `Path<A,B,C>` and `Path(A,B) -> C`. Note that
impl<'hir> PathSegment<'hir> {
/// Converts an identifier to the corresponding segment.
- pub fn from_ident(ident: Ident) -> PathSegment<'hir> {
- PathSegment { ident, hir_id: None, res: None, infer_args: true, args: None }
+ pub fn new(ident: Ident, hir_id: HirId, res: Res) -> PathSegment<'hir> {
+ PathSegment { ident, hir_id, res, infer_args: true, args: None }
}
pub fn invalid() -> Self {
- Self::from_ident(Ident::empty())
+ Self::new(Ident::empty(), HirId::INVALID, Res::Err)
}
pub fn args(&self) -> &GenericArgs<'hir> {
#[derive(Debug, HashStable_Generic)]
pub enum GenericArg<'hir> {
Lifetime(Lifetime),
- Type(Ty<'hir>),
+ Type(&'hir Ty<'hir>),
Const(ConstArg),
Infer(InferArg),
}
}
}
- pub fn id(&self) -> HirId {
+ pub fn hir_id(&self) -> HirId {
match self {
GenericArg::Lifetime(l) => l.hir_id,
GenericArg::Type(t) => t.hir_id,
Semi(&'hir Expr<'hir>),
}
-/// Represents a `let` statement (i.e., `let <pat>:<ty> = <expr>;`).
+/// Represents a `let` statement (i.e., `let <pat>:<ty> = <init>;`).
#[derive(Debug, HashStable_Generic)]
pub struct Local<'hir> {
pub pat: &'hir Pat<'hir>,
#[derive(Debug, HashStable_Generic)]
pub struct Body<'hir> {
pub params: &'hir [Param<'hir>],
- pub value: Expr<'hir>,
+ pub value: &'hir Expr<'hir>,
pub generator_kind: Option<GeneratorKind>,
}
///
/// The `PathSegment` represents the method name and its generic arguments
/// (within the angle brackets).
- /// The first element of the `&[Expr]` is the expression that evaluates
+ /// The `&Expr` is the expression that evaluates
/// to the object on which the method is being called on (the receiver),
- /// and the remaining elements are the rest of the arguments.
+ /// and the `&[Expr]` is the rest of the arguments.
/// Thus, `x.foo::<Bar, Baz>(a, b, c, d)` is represented as
- /// `ExprKind::MethodCall(PathSegment { foo, [Bar, Baz] }, [x, a, b, c, d], span)`.
+ /// `ExprKind::MethodCall(PathSegment { foo, [Bar, Baz] }, x, [a, b, c, d], span)`.
/// The final `Span` represents the span of the function and arguments
/// (e.g. `foo::<Bar, Baz>(a, b, c, d)` in `x.foo::<Bar, Baz>(a, b, c, d)`
///
/// the `hir_id` of the `MethodCall` node itself.
///
/// [`type_dependent_def_id`]: ../../rustc_middle/ty/struct.TypeckResults.html#method.type_dependent_def_id
- MethodCall(&'hir PathSegment<'hir>, &'hir [Expr<'hir>], Span),
+ MethodCall(&'hir PathSegment<'hir>, &'hir Expr<'hir>, &'hir [Expr<'hir>], Span),
/// A tuple (e.g., `(a, b, c, d)`).
Tup(&'hir [Expr<'hir>]),
/// A binary operation (e.g., `a + b`, `a * b`).
pub enum InlineAsmOperand<'hir> {
In {
reg: InlineAsmRegOrRegClass,
- expr: Expr<'hir>,
+ expr: &'hir Expr<'hir>,
},
Out {
reg: InlineAsmRegOrRegClass,
late: bool,
- expr: Option<Expr<'hir>>,
+ expr: Option<&'hir Expr<'hir>>,
},
InOut {
reg: InlineAsmRegOrRegClass,
late: bool,
- expr: Expr<'hir>,
+ expr: &'hir Expr<'hir>,
},
SplitInOut {
reg: InlineAsmRegOrRegClass,
late: bool,
- in_expr: Expr<'hir>,
- out_expr: Option<Expr<'hir>>,
+ in_expr: &'hir Expr<'hir>,
+ out_expr: Option<&'hir Expr<'hir>>,
},
Const {
anon_const: AnonConst,
/// A MBE macro definition (`macro_rules!` or `macro`).
Macro(ast::MacroDef, MacroKind),
/// A module.
- Mod(Mod<'hir>),
+ Mod(&'hir Mod<'hir>),
/// An external module, e.g. `extern { .. }`.
ForeignMod { abi: Abi, items: &'hir [ForeignItemRef] },
/// Module-level inline assembly (from `global_asm!`).
mod size_asserts {
use super::*;
// These are in alphabetical order, which is easy to maintain.
- static_assert_size!(Block<'static>, 48);
- static_assert_size!(Expr<'static>, 56);
- static_assert_size!(ForeignItem<'static>, 72);
+ static_assert_size!(Block<'_>, 48);
+ static_assert_size!(Body<'_>, 32);
+ static_assert_size!(Expr<'_>, 64);
+ static_assert_size!(ExprKind<'_>, 48);
+ static_assert_size!(FnDecl<'_>, 40);
+ static_assert_size!(ForeignItem<'_>, 72);
+ static_assert_size!(ForeignItemKind<'_>, 40);
+ static_assert_size!(GenericArg<'_>, 40);
static_assert_size!(GenericBound<'_>, 48);
- static_assert_size!(Generics<'static>, 56);
- static_assert_size!(ImplItem<'static>, 88);
- static_assert_size!(Impl<'static>, 80);
- static_assert_size!(Item<'static>, 80);
- static_assert_size!(Pat<'static>, 88);
- static_assert_size!(QPath<'static>, 24);
- static_assert_size!(TraitItem<'static>, 96);
- static_assert_size!(Ty<'static>, 72);
+ static_assert_size!(Generics<'_>, 56);
+ static_assert_size!(Impl<'_>, 80);
+ static_assert_size!(ImplItem<'_>, 88);
+ static_assert_size!(ImplItemKind<'_>, 40);
+ static_assert_size!(Item<'_>, 80);
+ static_assert_size!(ItemKind<'_>, 48);
+ static_assert_size!(Local<'_>, 64);
+ static_assert_size!(Param<'_>, 32);
+ static_assert_size!(Pat<'_>, 88);
+ static_assert_size!(PatKind<'_>, 64);
+ static_assert_size!(Path<'_>, 48);
+ static_assert_size!(PathSegment<'_>, 56);
+ static_assert_size!(QPath<'_>, 24);
+ static_assert_size!(Stmt<'_>, 32);
+ static_assert_size!(StmtKind<'_>, 16);
+ static_assert_size!(TraitItem<'_>, 96);
+ static_assert_size!(TraitItemKind<'_>, 56);
+ static_assert_size!(Ty<'_>, 72);
+ static_assert_size!(TyKind<'_>, 56);
}
}
impl HirId {
+ /// Signal local id which should never be used.
+ pub const INVALID: HirId = HirId { owner: CRATE_DEF_ID, local_id: ItemLocalId::INVALID };
+
#[inline]
pub fn expect_owner(self) -> LocalDefId {
assert_eq!(self.local_id.index(), 0);
segment: &'v PathSegment<'v>,
) {
visitor.visit_ident(segment.ident);
- walk_list!(visitor, visit_id, segment.hir_id);
+ visitor.visit_id(segment.hir_id);
if let Some(ref args) = segment.args {
visitor.visit_generic_args(path_span, args);
}
visitor.visit_expr(callee_expression);
walk_list!(visitor, visit_expr, arguments);
}
- ExprKind::MethodCall(ref segment, arguments, _) => {
+ ExprKind::MethodCall(ref segment, receiver, arguments, _) => {
visitor.visit_path_segment(expression.span, segment);
+ visitor.visit_expr(receiver);
walk_list!(visitor, visit_expr, arguments);
}
ExprKind::Binary(_, ref left_expression, ref right_expression) => {
DropInPlace, sym::drop_in_place, drop_in_place_fn, Target::Fn, GenericRequirement::Minimum(1);
Oom, sym::oom, oom, Target::Fn, GenericRequirement::None;
AllocLayout, sym::alloc_layout, alloc_layout, Target::Struct, GenericRequirement::None;
- ConstEvalSelect, sym::const_eval_select, const_eval_select, Target::Fn, GenericRequirement::Exact(4);
- ConstConstEvalSelect, sym::const_eval_select_ct,const_eval_select_ct, Target::Fn, GenericRequirement::Exact(4);
Start, sym::start, start_fn, Target::Fn, GenericRequirement::Exact(1);
self.print_call_post(args)
}
- fn print_expr_method_call(&mut self, segment: &hir::PathSegment<'_>, args: &[hir::Expr<'_>]) {
- let base_args = &args[1..];
- self.print_expr_maybe_paren(&args[0], parser::PREC_POSTFIX);
+ fn print_expr_method_call(
+ &mut self,
+ segment: &hir::PathSegment<'_>,
+ receiver: &hir::Expr<'_>,
+ args: &[hir::Expr<'_>],
+ ) {
+ let base_args = args;
+ self.print_expr_maybe_paren(&receiver, parser::PREC_POSTFIX);
self.word(".");
self.print_ident(segment.ident);
let generic_args = segment.args();
if !generic_args.args.is_empty() || !generic_args.bindings.is_empty() {
- self.print_generic_args(generic_args, segment.infer_args, true);
+ self.print_generic_args(generic_args, true);
}
self.print_call_post(base_args)
hir::ExprKind::Call(func, args) => {
self.print_expr_call(func, args);
}
- hir::ExprKind::MethodCall(segment, args, _) => {
- self.print_expr_method_call(segment, args);
+ hir::ExprKind::MethodCall(segment, receiver, args, _) => {
+ self.print_expr_method_call(segment, receiver, args);
}
hir::ExprKind::Binary(op, lhs, rhs) => {
self.print_expr_binary(op, lhs, rhs);
}
if segment.ident.name != kw::PathRoot {
self.print_ident(segment.ident);
- self.print_generic_args(segment.args(), segment.infer_args, colons_before_params);
+ self.print_generic_args(segment.args(), colons_before_params);
}
}
}
pub fn print_path_segment(&mut self, segment: &hir::PathSegment<'_>) {
if segment.ident.name != kw::PathRoot {
self.print_ident(segment.ident);
- self.print_generic_args(segment.args(), segment.infer_args, false);
+ self.print_generic_args(segment.args(), false);
}
}
}
if segment.ident.name != kw::PathRoot {
self.print_ident(segment.ident);
- self.print_generic_args(
- segment.args(),
- segment.infer_args,
- colons_before_params,
- );
+ self.print_generic_args(segment.args(), colons_before_params);
}
}
self.word("::");
let item_segment = path.segments.last().unwrap();
self.print_ident(item_segment.ident);
- self.print_generic_args(
- item_segment.args(),
- item_segment.infer_args,
- colons_before_params,
- )
+ self.print_generic_args(item_segment.args(), colons_before_params)
}
hir::QPath::TypeRelative(qself, item_segment) => {
// If we've got a compound-qualified-path, let's push an additional pair of angle
self.word("::");
self.print_ident(item_segment.ident);
- self.print_generic_args(
- item_segment.args(),
- item_segment.infer_args,
- colons_before_params,
- )
+ self.print_generic_args(item_segment.args(), colons_before_params)
}
hir::QPath::LangItem(lang_item, span, _) => {
self.word("#[lang = \"");
fn print_generic_args(
&mut self,
generic_args: &hir::GenericArgs<'_>,
- infer_args: bool,
colons_before_params: bool,
) {
if generic_args.parenthesized {
);
}
- // FIXME(eddyb): this would leak into error messages (e.g.,
- // "non-exhaustive patterns: `Some::<..>(_)` not covered").
- if infer_args && false {
- start_or_comma(self);
- self.word("..");
- }
-
for binding in generic_args.bindings {
start_or_comma(self);
self.print_type_binding(binding);
pub fn print_type_binding(&mut self, binding: &hir::TypeBinding<'_>) {
self.print_ident(binding.ident);
- self.print_generic_args(binding.gen_args, false, false);
+ self.print_generic_args(binding.gen_args, false);
self.space();
match binding.kind {
hir::TypeBindingKind::Equality { ref term } => {
contains_exterior_struct_lit(x)
}
- hir::ExprKind::MethodCall(.., exprs, _) => {
+ hir::ExprKind::MethodCall(_, receiver, ..) => {
// `X { y: 1 }.bar(...)`
- contains_exterior_struct_lit(&exprs[0])
+ contains_exterior_struct_lit(receiver)
}
_ => false,
}
}
}
- hir::ExprKind::MethodCall(segment, _, _) => {
+ hir::ExprKind::MethodCall(segment, ..) => {
if let Some(def_id) = self.typeck_results.type_dependent_def_id(expr.hir_id) {
let generics = tcx.generics_of(def_id);
let insertable: Option<_> = try {
None?
}
let substs = self.node_substs_opt(expr.hir_id)?;
- let span = tcx.hir().span(segment.hir_id?);
+ let span = tcx.hir().span(segment.hir_id);
let insert_span = segment.ident.span.shrink_to_hi().with_hi(span.hi());
InsertableGenericArgs {
insert_span,
path.segments
.iter()
.filter_map(move |segment| {
- let res = segment.res?;
+ let res = segment.res;
let generics_def_id = tcx.res_generics_def_id(res)?;
let generics = tcx.generics_of(generics_def_id);
if generics.has_impl_trait() {
return None;
}
- let span = tcx.hir().span(segment.hir_id?);
+ let span = tcx.hir().span(segment.hir_id);
let insert_span = segment.ident.span.shrink_to_hi().with_hi(span.hi());
Some(InsertableGenericArgs {
insert_span,
if !segment.infer_args || generics.has_impl_trait() {
None?;
}
- let span = tcx.hir().span(segment.hir_id?);
+ let span = tcx.hir().span(segment.hir_id);
let insert_span = segment.ident.span.shrink_to_hi().with_hi(span.hi());
InsertableGenericArgs { insert_span, substs, generics_def_id: def_id, def_id }
};
let generic_args = &generics.own_substs_no_defaults(tcx, substs)
[generics.own_counts().lifetimes..];
let span = match expr.kind {
- ExprKind::MethodCall(path, _, _) => path.ident.span,
+ ExprKind::MethodCall(path, ..) => path.ident.span,
_ => expr.span,
};
})
.any(|generics| generics.has_impl_trait())
};
- if let ExprKind::MethodCall(path, args, span) = expr.kind
+ if let ExprKind::MethodCall(path, receiver, args, span) = expr.kind
&& let Some(substs) = self.node_substs_opt(expr.hir_id)
&& substs.iter().any(|arg| self.generic_arg_contains_target(arg))
&& let Some(def_id) = self.typeck_results.type_dependent_def_id(expr.hir_id)
&& !has_impl_trait(def_id)
{
let successor =
- args.get(1).map_or_else(|| (")", span.hi()), |arg| (", ", arg.span.lo()));
+ args.get(0).map_or_else(|| (")", span.hi()), |arg| (", ", arg.span.lo()));
let substs = self.infcx.resolve_vars_if_possible(substs);
self.update_infer_source(InferSource {
span: path.ident.span,
kind: InferSourceKind::FullyQualifiedMethodCall {
- receiver: args.first().unwrap(),
+ receiver,
successor,
substs,
def_id,
}
hir::TyKind::Path(hir::QPath::Resolved(None, path)) => match &path.segments {
[segment]
- if segment
- .res
- .map(|res| {
- matches!(
- res,
- Res::SelfTy { trait_: _, alias_to: _ }
- | Res::Def(hir::def::DefKind::TyParam, _)
- )
- })
- .unwrap_or(false) =>
+ if matches!(
+ segment.res,
+ Res::SelfTy { trait_: _, alias_to: _ }
+ | Res::Def(hir::def::DefKind::TyParam, _)
+ ) =>
{
self.types.push(path.span);
}
}
if sess.opts.unstable_opts.hir_stats {
- hir_stats::print_ast_stats(&krate, "PRE EXPANSION AST STATS");
+ hir_stats::print_ast_stats(&krate, "PRE EXPANSION AST STATS", "ast-stats-1");
}
Ok(krate)
}
if sess.opts.unstable_opts.hir_stats {
- hir_stats::print_ast_stats(&krate, "POST EXPANSION AST STATS");
+ hir_stats::print_ast_stats(&krate, "POST EXPANSION AST STATS", "ast-stats-2");
}
resolver.resolve_crate(&krate);
}
// We only care about method call expressions.
- if let hir::ExprKind::MethodCall(call, args, _) = &expr.kind {
+ if let hir::ExprKind::MethodCall(call, receiver_arg, ..) = &expr.kind {
if call.ident.name != sym::into_iter {
return;
}
};
// As this is a method call expression, we have at least one argument.
- let receiver_arg = &args[0];
let receiver_ty = cx.typeck_results().expr_ty(receiver_arg);
let adjustments = cx.typeck_results().expr_adjustments(receiver_arg);
_ => {}
}
}
- } else if let hir::ExprKind::MethodCall(_, ref args, _) = expr.kind {
+ } else if let hir::ExprKind::MethodCall(_, receiver, ..) = expr.kind {
// Find problematic calls to `MaybeUninit::assume_init`.
let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?;
if cx.tcx.is_diagnostic_item(sym::assume_init, def_id) {
// This is a call to *some* method named `assume_init`.
// See if the `self` parameter is one of the dangerous constructors.
- if let hir::ExprKind::Call(ref path_expr, _) = args[0].kind {
+ if let hir::ExprKind::Call(ref path_expr, _) = receiver.kind {
if let hir::ExprKind::Path(ref qpath) = path_expr.kind {
let def_id = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id()?;
match cx.tcx.get_diagnostic_name(def_id) {
if let Some(positional_arg_to_replace) = position_sp_to_replace {
let name = if is_formatting_arg { named_arg_name + "$" } else { named_arg_name };
let span_to_replace = if let Ok(positional_arg_content) =
- self.sess().source_map().span_to_snippet(positional_arg_to_replace) && positional_arg_content.starts_with(":") {
+ self.sess().source_map().span_to_snippet(positional_arg_to_replace) && positional_arg_content.starts_with(':') {
positional_arg_to_replace.shrink_to_lo()
} else {
positional_arg_to_replace
expr: &Expr<'_>,
) -> Option<(Span, DefId, ty::subst::SubstsRef<'tcx>)> {
match expr.kind {
- ExprKind::MethodCall(segment, _, _)
+ ExprKind::MethodCall(segment, ..)
if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) =>
{
Some((segment.ident.span, def_id, cx.typeck_results().node_substs(expr.hir_id)))
_: rustc_hir::HirId,
) {
if let Some(segment) = path.segments.iter().nth_back(1)
- && let Some(res) = &segment.res
- && lint_ty_kind_usage(cx, res)
+ && lint_ty_kind_usage(cx, &segment.res)
{
let span = path.span.with_hi(
segment.args.map_or(segment.ident.span, |a| a.span_ext).hi()
fn first_method_call<'tcx>(
expr: &'tcx Expr<'tcx>,
-) -> Option<(&'tcx PathSegment<'tcx>, &'tcx [Expr<'tcx>])> {
- if let ExprKind::MethodCall(path, args, _) = &expr.kind {
- if args.iter().any(|e| e.span.from_expansion()) { None } else { Some((path, *args)) }
+) -> Option<(&'tcx PathSegment<'tcx>, &'tcx Expr<'tcx>)> {
+ if let ExprKind::MethodCall(path, receiver, args, ..) = &expr.kind {
+ if args.iter().any(|e| e.span.from_expansion()) || receiver.span.from_expansion() {
+ None
+ } else {
+ Some((path, *receiver))
+ }
} else {
None
}
}
match first_method_call(expr) {
- Some((path, args)) if path.ident.name == sym::as_ptr => {
- let unwrap_arg = &args[0];
+ Some((path, unwrap_arg)) if path.ident.name == sym::as_ptr => {
let as_ptr_span = path.ident.span;
match first_method_call(unwrap_arg) {
- Some((path, args))
+ Some((path, receiver))
if path.ident.name == sym::unwrap || path.ident.name == sym::expect =>
{
- let source_arg = &args[0];
- lint_cstring_as_ptr(cx, as_ptr_span, source_arg, unwrap_arg);
+ lint_cstring_as_ptr(cx, as_ptr_span, receiver, unwrap_arg);
}
_ => return,
}
impl<'tcx> LateLintPass<'tcx> for NoopMethodCall {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
// We only care about method calls.
- let ExprKind::MethodCall(call, elements, _) = &expr.kind else {
+ let ExprKind::MethodCall(call, receiver, ..) = &expr.kind else {
return
};
// We only care about method calls corresponding to the `Clone`, `Deref` and `Borrow`
) {
return;
}
- let receiver = &elements[0];
let receiver_ty = cx.typeck_results().expr_ty(receiver);
let expr_ty = cx.typeck_results().expr_ty_adjusted(expr);
if receiver_ty != expr_ty {
sym::AtomicI64,
sym::AtomicI128,
];
- if let ExprKind::MethodCall(ref method_path, args, _) = &expr.kind
+ if let ExprKind::MethodCall(ref method_path, _, args, _) = &expr.kind
&& recognized_names.contains(&method_path.ident.name)
&& let Some(m_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
&& let Some(impl_did) = cx.tcx.impl_of_method(m_def_id)
fn check_atomic_load_store(cx: &LateContext<'_>, expr: &Expr<'_>) {
if let Some((method, args)) = Self::inherent_atomic_method_call(cx, expr, &[sym::load, sym::store])
&& let Some((ordering_arg, invalid_ordering)) = match method {
- sym::load => Some((&args[1], sym::Release)),
- sym::store => Some((&args[2], sym::Acquire)),
+ sym::load => Some((&args[0], sym::Release)),
+ sym::store => Some((&args[1], sym::Acquire)),
_ => None,
}
&& let Some(ordering) = Self::match_ordering(cx, ordering_arg)
else {return };
let fail_order_arg = match method {
- sym::fetch_update => &args[2],
- sym::compare_exchange | sym::compare_exchange_weak => &args[4],
+ sym::fetch_update => &args[1],
+ sym::compare_exchange | sym::compare_exchange_weak => &args[3],
_ => return,
};
///
/// ### Example of drop reorder
///
- /// ```rust,compile_fail
+ /// ```rust,edition2018,compile_fail
/// #![deny(rust_2021_incompatible_closure_captures)]
/// # #![allow(unused)]
///
///
/// ### Example of auto-trait
///
- /// ```rust,compile_fail
+ /// ```rust,edition2018,compile_fail
/// #![deny(rust_2021_incompatible_closure_captures)]
/// use std::thread;
///
let tcx = self.tcx;
- let keys_and_jobs = tcx
- .mir_keys(())
- .iter()
- .filter_map(|&def_id| {
- let (encode_const, encode_opt) = should_encode_mir(tcx, def_id);
- if encode_const || encode_opt {
- Some((def_id, encode_const, encode_opt))
- } else {
- None
- }
- })
- .collect::<Vec<_>>();
- for (def_id, encode_const, encode_opt) in keys_and_jobs.into_iter() {
+ let keys_and_jobs = tcx.mir_keys(()).iter().filter_map(|&def_id| {
+ let (encode_const, encode_opt) = should_encode_mir(tcx, def_id);
+ if encode_const || encode_opt { Some((def_id, encode_const, encode_opt)) } else { None }
+ });
+ for (def_id, encode_const, encode_opt) in keys_and_jobs {
debug_assert!(encode_const || encode_opt);
debug!("EntryBuilder::encode_mir({:?})", def_id);
/// `ValTree` does not have this problem with representation, as it only contains integers or
/// lists of (nested) `ValTree`.
pub enum ValTree<'tcx> {
- /// ZSTs, integers, `bool`, `char` are represented as scalars.
+ /// integers, `bool`, `char` are represented as scalars.
/// See the `ScalarInt` documentation for how `ScalarInt` guarantees that equal values
/// of these types have the same representation.
Leaf(ScalarInt),
// dont use SliceOrStr for now
/// The fields of any kind of aggregate. Structs, tuples and arrays are represented by
/// listing their fields' values in order.
+ ///
/// Enums are represented by storing their discriminant as a field, followed by all
/// the fields of the variant.
+ ///
+ /// ZST types are represented as an empty slice.
Branch(&'tcx [ValTree<'tcx>]),
}
Ok(layout)
}
+#[derive(Clone, Copy)]
pub struct LayoutCx<'tcx, C> {
pub tcx: C,
pub param_env: ty::ParamEnv<'tcx>,
)
}
+// Handle safe Rust thin and fat pointers.
+pub fn adjust_for_rust_scalar<'tcx>(
+ cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
+ attrs: &mut ArgAttributes,
+ scalar: Scalar,
+ layout: TyAndLayout<'tcx>,
+ offset: Size,
+ is_return: bool,
+) {
+ // Booleans are always a noundef i1 that needs to be zero-extended.
+ if scalar.is_bool() {
+ attrs.ext(ArgExtension::Zext);
+ attrs.set(ArgAttribute::NoUndef);
+ return;
+ }
+
+ // Scalars which have invalid values cannot be undef.
+ if !scalar.is_always_valid(&cx) {
+ attrs.set(ArgAttribute::NoUndef);
+ }
+
+ // Only pointer types handled below.
+ let Scalar::Initialized { value: Pointer, valid_range} = scalar else { return };
+
+ if !valid_range.contains(0) {
+ attrs.set(ArgAttribute::NonNull);
+ }
+
+ if let Some(pointee) = layout.pointee_info_at(&cx, offset) {
+ if let Some(kind) = pointee.safe {
+ attrs.pointee_align = Some(pointee.align);
+
+ // `Box` (`UniqueBorrowed`) are not necessarily dereferenceable
+ // for the entire duration of the function as they can be deallocated
+ // at any time. Same for shared mutable references. If LLVM had a
+ // way to say "dereferenceable on entry" we could use it here.
+ attrs.pointee_size = match kind {
+ PointerKind::UniqueBorrowed
+ | PointerKind::UniqueBorrowedPinned
+ | PointerKind::Frozen => pointee.size,
+ PointerKind::SharedMutable | PointerKind::UniqueOwned => Size::ZERO,
+ };
+
+ // `Box`, `&T`, and `&mut T` cannot be undef.
+ // Note that this only applies to the value of the pointer itself;
+ // this attribute doesn't make it UB for the pointed-to data to be undef.
+ attrs.set(ArgAttribute::NoUndef);
+
+ // The aliasing rules for `Box<T>` are still not decided, but currently we emit
+ // `noalias` for it. This can be turned off using an unstable flag.
+ // See https://github.com/rust-lang/unsafe-code-guidelines/issues/326
+ let noalias_for_box = cx.tcx.sess.opts.unstable_opts.box_noalias.unwrap_or(true);
+
+ // `&mut` pointer parameters never alias other parameters,
+ // or mutable global data
+ //
+ // `&T` where `T` contains no `UnsafeCell<U>` is immutable,
+ // and can be marked as both `readonly` and `noalias`, as
+ // LLVM's definition of `noalias` is based solely on memory
+ // dependencies rather than pointer equality
+ //
+ // Due to past miscompiles in LLVM, we apply a separate NoAliasMutRef attribute
+ // for UniqueBorrowed arguments, so that the codegen backend can decide whether
+ // or not to actually emit the attribute. It can also be controlled with the
+ // `-Zmutable-noalias` debugging option.
+ let no_alias = match kind {
+ PointerKind::SharedMutable
+ | PointerKind::UniqueBorrowed
+ | PointerKind::UniqueBorrowedPinned => false,
+ PointerKind::UniqueOwned => noalias_for_box,
+ PointerKind::Frozen => !is_return,
+ };
+ if no_alias {
+ attrs.set(ArgAttribute::NoAlias);
+ }
+
+ if kind == PointerKind::Frozen && !is_return {
+ attrs.set(ArgAttribute::ReadOnly);
+ }
+
+ if kind == PointerKind::UniqueBorrowed && !is_return {
+ attrs.set(ArgAttribute::NoAliasMutRef);
+ }
+ }
+ }
+}
+
impl<'tcx> LayoutCx<'tcx, TyCtxt<'tcx>> {
// FIXME(eddyb) perhaps group the signature/type-containing (or all of them?)
// arguments of this method, into a separate `struct`.
use SpecAbi::*;
let rust_abi = matches!(sig.abi, RustIntrinsic | PlatformIntrinsic | Rust | RustCall);
- // Handle safe Rust thin and fat pointers.
- let adjust_for_rust_scalar = |attrs: &mut ArgAttributes,
- scalar: Scalar,
- layout: TyAndLayout<'tcx>,
- offset: Size,
- is_return: bool| {
- // Booleans are always a noundef i1 that needs to be zero-extended.
- if scalar.is_bool() {
- attrs.ext(ArgExtension::Zext);
- attrs.set(ArgAttribute::NoUndef);
- return;
- }
-
- // Scalars which have invalid values cannot be undef.
- if !scalar.is_always_valid(self) {
- attrs.set(ArgAttribute::NoUndef);
- }
-
- // Only pointer types handled below.
- let Scalar::Initialized { value: Pointer, valid_range} = scalar else { return };
-
- if !valid_range.contains(0) {
- attrs.set(ArgAttribute::NonNull);
- }
-
- if let Some(pointee) = layout.pointee_info_at(self, offset) {
- if let Some(kind) = pointee.safe {
- attrs.pointee_align = Some(pointee.align);
-
- // `Box` (`UniqueBorrowed`) are not necessarily dereferenceable
- // for the entire duration of the function as they can be deallocated
- // at any time. Same for shared mutable references. If LLVM had a
- // way to say "dereferenceable on entry" we could use it here.
- attrs.pointee_size = match kind {
- PointerKind::UniqueBorrowed
- | PointerKind::UniqueBorrowedPinned
- | PointerKind::Frozen => pointee.size,
- PointerKind::SharedMutable | PointerKind::UniqueOwned => Size::ZERO,
- };
-
- // `Box`, `&T`, and `&mut T` cannot be undef.
- // Note that this only applies to the value of the pointer itself;
- // this attribute doesn't make it UB for the pointed-to data to be undef.
- attrs.set(ArgAttribute::NoUndef);
-
- // The aliasing rules for `Box<T>` are still not decided, but currently we emit
- // `noalias` for it. This can be turned off using an unstable flag.
- // See https://github.com/rust-lang/unsafe-code-guidelines/issues/326
- let noalias_for_box =
- self.tcx().sess.opts.unstable_opts.box_noalias.unwrap_or(true);
-
- // `&mut` pointer parameters never alias other parameters,
- // or mutable global data
- //
- // `&T` where `T` contains no `UnsafeCell<U>` is immutable,
- // and can be marked as both `readonly` and `noalias`, as
- // LLVM's definition of `noalias` is based solely on memory
- // dependencies rather than pointer equality
- //
- // Due to past miscompiles in LLVM, we apply a separate NoAliasMutRef attribute
- // for UniqueBorrowed arguments, so that the codegen backend can decide whether
- // or not to actually emit the attribute. It can also be controlled with the
- // `-Zmutable-noalias` debugging option.
- let no_alias = match kind {
- PointerKind::SharedMutable
- | PointerKind::UniqueBorrowed
- | PointerKind::UniqueBorrowedPinned => false,
- PointerKind::UniqueOwned => noalias_for_box,
- PointerKind::Frozen => !is_return,
- };
- if no_alias {
- attrs.set(ArgAttribute::NoAlias);
- }
-
- if kind == PointerKind::Frozen && !is_return {
- attrs.set(ArgAttribute::ReadOnly);
- }
-
- if kind == PointerKind::UniqueBorrowed && !is_return {
- attrs.set(ArgAttribute::NoAliasMutRef);
- }
- }
- }
- };
-
let arg_of = |ty: Ty<'tcx>, arg_idx: Option<usize>| -> Result<_, FnAbiError<'tcx>> {
let is_return = arg_idx.is_none();
let mut arg = ArgAbi::new(self, layout, |layout, scalar, offset| {
let mut attrs = ArgAttributes::new();
- adjust_for_rust_scalar(&mut attrs, scalar, *layout, offset, is_return);
+ adjust_for_rust_scalar(*self, &mut attrs, scalar, *layout, offset, is_return);
attrs
});
let kind = match expr.kind {
// Here comes the interesting stuff:
- hir::ExprKind::MethodCall(segment, ref args, fn_span) => {
+ hir::ExprKind::MethodCall(segment, receiver, ref args, fn_span) => {
// Rewrite a.b(c) into UFCS form like Trait::b(a, c)
let expr = self.method_callee(expr, segment.ident.span, None);
// When we apply adjustments to the receiver, use the span of
// the overall method call for better diagnostics. args[0]
// is guaranteed to exist, since a method call always has a receiver.
- let old_adjustment_span = self.adjustment_span.replace((args[0].hir_id, expr_span));
+ let old_adjustment_span =
+ self.adjustment_span.replace((receiver.hir_id, expr_span));
info!("Using method span: {:?}", expr.span);
- let args = self.mirror_exprs(args);
+ let args = std::iter::once(receiver)
+ .chain(args.iter())
+ .map(|expr| self.mirror_expr(expr))
+ .collect();
self.adjustment_span = old_adjustment_span;
ExprKind::Call {
ty: expr.ty,
#![feature(trusted_step)]
#![feature(try_blocks)]
#![feature(yeet_expr)]
+#![feature(if_let_guard)]
#![recursion_limit = "256"]
#[macro_use]
use rustc_index::vec::IndexVec;
use rustc_middle::mir::visit::Visitor as _;
use rustc_middle::mir::{
- traversal, AnalysisPhase, Body, ConstQualifs, MirPass, MirPhase, Promoted, RuntimePhase,
+ traversal, AnalysisPhase, Body, ConstQualifs, Constant, LocalDecl, MirPass, MirPhase, Operand,
+ Place, ProjectionElem, Promoted, RuntimePhase, Rvalue, SourceInfo, Statement, StatementKind,
+ TerminatorKind,
};
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{self, TyCtxt, TypeVisitable};
+use rustc_span::sym;
#[macro_use]
mod pass_manager;
};
}
+fn remap_mir_for_const_eval_select<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ mut body: Body<'tcx>,
+ context: hir::Constness,
+) -> Body<'tcx> {
+ for bb in body.basic_blocks.as_mut().iter_mut() {
+ let terminator = bb.terminator.as_mut().expect("invalid terminator");
+ match terminator.kind {
+ TerminatorKind::Call {
+ func: Operand::Constant(box Constant { ref literal, .. }),
+ ref mut args,
+ destination,
+ target,
+ cleanup,
+ fn_span,
+ ..
+ } if let ty::FnDef(def_id, _) = *literal.ty().kind()
+ && tcx.item_name(def_id) == sym::const_eval_select
+ && tcx.is_intrinsic(def_id) =>
+ {
+ let [tupled_args, called_in_const, called_at_rt]: [_; 3] = std::mem::take(args).try_into().unwrap();
+ let ty = tupled_args.ty(&body.local_decls, tcx);
+ let fields = ty.tuple_fields();
+ let num_args = fields.len();
+ let func = if context == hir::Constness::Const { called_in_const } else { called_at_rt };
+ let (method, place): (fn(Place<'tcx>) -> Operand<'tcx>, Place<'tcx>) = match tupled_args {
+ Operand::Constant(_) => {
+ // there is no good way of extracting a tuple arg from a constant (const generic stuff)
+ // so we just create a temporary and deconstruct that.
+ let local = body.local_decls.push(LocalDecl::new(ty, fn_span));
+ bb.statements.push(Statement {
+ source_info: SourceInfo::outermost(fn_span),
+ kind: StatementKind::Assign(Box::new((local.into(), Rvalue::Use(tupled_args.clone())))),
+ });
+ (Operand::Move, local.into())
+ }
+ Operand::Move(place) => (Operand::Move, place),
+ Operand::Copy(place) => (Operand::Copy, place),
+ };
+ let place_elems = place.projection;
+ let arguments = (0..num_args).map(|x| {
+ let mut place_elems = place_elems.to_vec();
+ place_elems.push(ProjectionElem::Field(x.into(), fields[x]));
+ let projection = tcx.intern_place_elems(&place_elems);
+ let place = Place {
+ local: place.local,
+ projection,
+ };
+ method(place)
+ }).collect();
+ terminator.kind = TerminatorKind::Call { func, args: arguments, destination, target, cleanup, from_hir_call: false, fn_span };
+ }
+ _ => {}
+ }
+ }
+ body
+}
+
fn is_mir_available(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
let def_id = def_id.expect_local();
tcx.mir_keys(()).contains(&def_id)
.body_const_context(def.did)
.expect("mir_for_ctfe should not be used for runtime functions");
- let mut body = tcx.mir_drops_elaborated_and_const_checked(def).borrow().clone();
+ let body = tcx.mir_drops_elaborated_and_const_checked(def).borrow().clone();
+
+ let mut body = remap_mir_for_const_eval_select(tcx, body, hir::Constness::Const);
match context {
// Do not const prop functions, either they get executed at runtime or exported to metadata,
Some(other) => panic!("do not use `optimized_mir` for constants: {:?}", other),
}
debug!("about to call mir_drops_elaborated...");
- let mut body =
+ let body =
tcx.mir_drops_elaborated_and_const_checked(ty::WithOptConstParam::unknown(did)).steal();
+ let mut body = remap_mir_for_const_eval_select(tcx, body, hir::Constness::NotConst);
debug!("body: {:#?}", body);
run_optimization_passes(tcx, &mut body);
//! method in operand position, we treat it as a neighbor of the current
//! mono item. Calls are just a special case of that.
//!
-//! #### Closures
-//! In a way, closures are a simple case. Since every closure object needs to be
-//! constructed somewhere, we can reliably discover them by observing
-//! `RValue::Aggregate` expressions with `AggregateKind::Closure`. This is also
-//! true for closures inlined from other crates.
-//!
//! #### Drop glue
//! Drop glue mono items are introduced by MIR drop-statements. The
//! generated mono item will again have drop-glue item neighbors if the
mir::TerminatorKind::Call { ref func, .. } => {
let callee_ty = func.ty(self.body, tcx);
let callee_ty = self.monomorphize(callee_ty);
- visit_fn_use(self.tcx, callee_ty, true, source, &mut self.output);
+ visit_fn_use(self.tcx, callee_ty, true, source, &mut self.output)
}
mir::TerminatorKind::Drop { ref place, .. }
| mir::TerminatorKind::DropAndReplace { ref place, .. } => {
};
tcx.hir().walk_toplevel_module(&mut collector);
tcx.hir().walk_attributes(&mut collector);
- collector.print("HIR STATS");
+ collector.print("HIR STATS", "hir-stats");
}
-pub fn print_ast_stats(krate: &ast::Crate, title: &str) {
+pub fn print_ast_stats(krate: &ast::Crate, title: &str, prefix: &str) {
use rustc_ast::visit::Visitor;
let mut collector =
StatCollector { krate: None, nodes: FxHashMap::default(), seen: FxHashSet::default() };
collector.visit_crate(krate);
- collector.print(title);
+ collector.print(title, prefix);
}
impl<'k> StatCollector<'k> {
}
}
- fn print(&self, title: &str) {
+ fn print(&self, title: &str, prefix: &str) {
let mut nodes: Vec<_> = self.nodes.iter().collect();
nodes.sort_by_key(|&(_, ref node)| node.stats.count * node.stats.size);
let total_size = nodes.iter().map(|(_, node)| node.stats.count * node.stats.size).sum();
- eprintln!("\n{}\n", title);
-
- eprintln!("{:<18}{:>18}{:>14}{:>14}", "Name", "Accumulated Size", "Count", "Item Size");
- eprintln!("----------------------------------------------------------------");
+ eprintln!("{} {}", prefix, title);
+ eprintln!(
+ "{} {:<18}{:>18}{:>14}{:>14}",
+ prefix, "Name", "Accumulated Size", "Count", "Item Size"
+ );
+ eprintln!("{} ----------------------------------------------------------------", prefix);
let percent = |m, n| (m * 100) as f64 / n as f64;
for (label, node) in nodes {
let size = node.stats.count * node.stats.size;
eprintln!(
- "{:<18}{:>10} ({:4.1}%){:>14}{:>14}",
+ "{} {:<18}{:>10} ({:4.1}%){:>14}{:>14}",
+ prefix,
label,
to_readable_str(size),
percent(size, total_size),
for (label, subnode) in subnodes {
let size = subnode.count * subnode.size;
eprintln!(
- "- {:<18}{:>10} ({:4.1}%){:>14}",
+ "{} - {:<18}{:>10} ({:4.1}%){:>14}",
+ prefix,
label,
to_readable_str(size),
percent(size, total_size),
}
}
}
- eprintln!("----------------------------------------------------------------");
- eprintln!("{:<18}{:>10}\n", "Total", to_readable_str(total_size));
+ eprintln!("{} ----------------------------------------------------------------", prefix);
+ eprintln!("{} {:<18}{:>10}", prefix, "Total", to_readable_str(total_size));
+ eprintln!("{}", prefix);
}
}
+// Used to avoid boilerplate for types with many variants.
+macro_rules! record_variants {
+ (
+ ($self:ident, $val:expr, $kind:expr, $id:expr, $mod:ident, $ty:ty, $tykind:ident),
+ [$($variant:ident),*]
+ ) => {
+ match $kind {
+ $(
+ $mod::$tykind::$variant { .. } => {
+ $self.record_variant(stringify!($ty), stringify!($variant), $id, $val)
+ }
+ )*
+ }
+ };
+}
+
impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
fn visit_param(&mut self, param: &'v hir::Param<'v>) {
self.record("Param", Id::Node(param.hir_id), param);
}
fn visit_item(&mut self, i: &'v hir::Item<'v>) {
- self.record("Item", Id::Node(i.hir_id()), i);
+ record_variants!(
+ (self, i, i.kind, Id::Node(i.hir_id()), hir, Item, ItemKind),
+ [
+ ExternCrate,
+ Use,
+ Static,
+ Const,
+ Fn,
+ Macro,
+ Mod,
+ ForeignMod,
+ GlobalAsm,
+ TyAlias,
+ OpaqueTy,
+ Enum,
+ Struct,
+ Union,
+ Trait,
+ TraitAlias,
+ Impl
+ ]
+ );
hir_visit::walk_item(self, i)
}
+ fn visit_body(&mut self, b: &'v hir::Body<'v>) {
+ self.record("Body", Id::None, b);
+ hir_visit::walk_body(self, b);
+ }
+
+ fn visit_mod(&mut self, m: &'v hir::Mod<'v>, _s: Span, n: HirId) {
+ self.record("Mod", Id::None, m);
+ hir_visit::walk_mod(self, m, n)
+ }
+
fn visit_foreign_item(&mut self, i: &'v hir::ForeignItem<'v>) {
- self.record("ForeignItem", Id::Node(i.hir_id()), i);
+ record_variants!(
+ (self, i, i.kind, Id::Node(i.hir_id()), hir, ForeignItem, ForeignItemKind),
+ [Fn, Static, Type]
+ );
hir_visit::walk_foreign_item(self, i)
}
}
fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) {
- self.record("Stmt", Id::Node(s.hir_id), s);
+ record_variants!(
+ (self, s, s.kind, Id::Node(s.hir_id), hir, Stmt, StmtKind),
+ [Local, Item, Expr, Semi]
+ );
hir_visit::walk_stmt(self, s)
}
}
fn visit_pat(&mut self, p: &'v hir::Pat<'v>) {
- self.record("Pat", Id::Node(p.hir_id), p);
+ record_variants!(
+ (self, p, p.kind, Id::Node(p.hir_id), hir, Pat, PatKind),
+ [Wild, Binding, Struct, TupleStruct, Or, Path, Tuple, Box, Ref, Lit, Range, Slice]
+ );
hir_visit::walk_pat(self, p)
}
- fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
- self.record("Expr", Id::Node(ex.hir_id), ex);
- hir_visit::walk_expr(self, ex)
+ fn visit_pat_field(&mut self, f: &'v hir::PatField<'v>) {
+ self.record("PatField", Id::Node(f.hir_id), f);
+ hir_visit::walk_pat_field(self, f)
+ }
+
+ fn visit_expr(&mut self, e: &'v hir::Expr<'v>) {
+ record_variants!(
+ (self, e, e.kind, Id::Node(e.hir_id), hir, Expr, ExprKind),
+ [
+ Box, ConstBlock, Array, Call, MethodCall, Tup, Binary, Unary, Lit, Cast, Type,
+ DropTemps, Let, If, Loop, Match, Closure, Block, Assign, AssignOp, Field, Index,
+ Path, AddrOf, Break, Continue, Ret, InlineAsm, Struct, Repeat, Yield, Err
+ ]
+ );
+ hir_visit::walk_expr(self, e)
+ }
+
+ fn visit_let_expr(&mut self, lex: &'v hir::Let<'v>) {
+ self.record("Let", Id::Node(lex.hir_id), lex);
+ hir_visit::walk_let_expr(self, lex)
+ }
+
+ fn visit_expr_field(&mut self, f: &'v hir::ExprField<'v>) {
+ self.record("ExprField", Id::Node(f.hir_id), f);
+ hir_visit::walk_expr_field(self, f)
}
fn visit_ty(&mut self, t: &'v hir::Ty<'v>) {
- self.record("Ty", Id::Node(t.hir_id), t);
+ record_variants!(
+ (self, t, t.kind, Id::Node(t.hir_id), hir, Ty, TyKind),
+ [
+ Slice,
+ Array,
+ Ptr,
+ Rptr,
+ BareFn,
+ Never,
+ Tup,
+ Path,
+ OpaqueDef,
+ TraitObject,
+ Typeof,
+ Infer,
+ Err
+ ]
+ );
hir_visit::walk_ty(self, t)
}
+ fn visit_generic_param(&mut self, p: &'v hir::GenericParam<'v>) {
+ self.record("GenericParam", Id::Node(p.hir_id), p);
+ hir_visit::walk_generic_param(self, p)
+ }
+
+ fn visit_generics(&mut self, g: &'v hir::Generics<'v>) {
+ self.record("Generics", Id::None, g);
+ hir_visit::walk_generics(self, g)
+ }
+
+ fn visit_where_predicate(&mut self, p: &'v hir::WherePredicate<'v>) {
+ record_variants!(
+ (self, p, p, Id::None, hir, WherePredicate, WherePredicate),
+ [BoundPredicate, RegionPredicate, EqPredicate]
+ );
+ hir_visit::walk_where_predicate(self, p)
+ }
+
fn visit_fn(
&mut self,
fk: hir_visit::FnKind<'v>,
hir_visit::walk_fn(self, fk, fd, b, s, id)
}
- fn visit_where_predicate(&mut self, predicate: &'v hir::WherePredicate<'v>) {
- self.record("WherePredicate", Id::None, predicate);
- hir_visit::walk_where_predicate(self, predicate)
+ fn visit_use(&mut self, p: &'v hir::Path<'v>, hir_id: hir::HirId) {
+ // This is `visit_use`, but the type is `Path` so record it that way.
+ self.record("Path", Id::None, p);
+ hir_visit::walk_use(self, p, hir_id)
}
fn visit_trait_item(&mut self, ti: &'v hir::TraitItem<'v>) {
- self.record("TraitItem", Id::Node(ti.hir_id()), ti);
+ record_variants!(
+ (self, ti, ti.kind, Id::Node(ti.hir_id()), hir, TraitItem, TraitItemKind),
+ [Const, Fn, Type]
+ );
hir_visit::walk_trait_item(self, ti)
}
+ fn visit_trait_item_ref(&mut self, ti: &'v hir::TraitItemRef) {
+ self.record("TraitItemRef", Id::Node(ti.id.hir_id()), ti);
+ hir_visit::walk_trait_item_ref(self, ti)
+ }
+
fn visit_impl_item(&mut self, ii: &'v hir::ImplItem<'v>) {
- self.record("ImplItem", Id::Node(ii.hir_id()), ii);
+ record_variants!(
+ (self, ii, ii.kind, Id::Node(ii.hir_id()), hir, ImplItem, ImplItemKind),
+ [Const, Fn, TyAlias]
+ );
hir_visit::walk_impl_item(self, ii)
}
- fn visit_param_bound(&mut self, bounds: &'v hir::GenericBound<'v>) {
- self.record("GenericBound", Id::None, bounds);
- hir_visit::walk_param_bound(self, bounds)
+ fn visit_foreign_item_ref(&mut self, fi: &'v hir::ForeignItemRef) {
+ self.record("ForeignItemRef", Id::Node(fi.id.hir_id()), fi);
+ hir_visit::walk_foreign_item_ref(self, fi)
+ }
+
+ fn visit_impl_item_ref(&mut self, ii: &'v hir::ImplItemRef) {
+ self.record("ImplItemRef", Id::Node(ii.id.hir_id()), ii);
+ hir_visit::walk_impl_item_ref(self, ii)
+ }
+
+ fn visit_param_bound(&mut self, b: &'v hir::GenericBound<'v>) {
+ record_variants!(
+ (self, b, b, Id::None, hir, GenericBound, GenericBound),
+ [Trait, LangItemTrait, Outlives]
+ );
+ hir_visit::walk_param_bound(self, b)
}
fn visit_field_def(&mut self, s: &'v hir::FieldDef<'v>) {
hir_visit::walk_variant(self, v)
}
- fn visit_lifetime(&mut self, lifetime: &'v hir::Lifetime) {
- self.record("Lifetime", Id::Node(lifetime.hir_id), lifetime);
- hir_visit::walk_lifetime(self, lifetime)
- }
-
- fn visit_qpath(&mut self, qpath: &'v hir::QPath<'v>, id: hir::HirId, span: Span) {
- self.record("QPath", Id::None, qpath);
- hir_visit::walk_qpath(self, qpath, id, span)
+ fn visit_generic_arg(&mut self, ga: &'v hir::GenericArg<'v>) {
+ record_variants!(
+ (self, ga, ga, Id::Node(ga.hir_id()), hir, GenericArg, GenericArg),
+ [Lifetime, Type, Const, Infer]
+ );
+ match ga {
+ hir::GenericArg::Lifetime(lt) => self.visit_lifetime(lt),
+ hir::GenericArg::Type(ty) => self.visit_ty(ty),
+ hir::GenericArg::Const(ct) => self.visit_anon_const(&ct.value),
+ hir::GenericArg::Infer(inf) => self.visit_infer(inf),
+ }
}
fn visit_path(&mut self, path: &'v hir::Path<'v>, _id: hir::HirId) {
hir_visit::walk_path(self, path)
}
- // `PathSegment` has one inline use (in `ast::ExprKind::MethodCall`) and
- // one non-inline use (in `Path::segments`). The latter case is more common
- // than the former case, so we implement this visitor and tolerate the
- // double counting in the former case.
fn visit_path_segment(&mut self, path_span: Span, path_segment: &'v hir::PathSegment<'v>) {
self.record("PathSegment", Id::None, path_segment);
hir_visit::walk_path_segment(self, path_span, path_segment)
}
+ fn visit_generic_args(&mut self, sp: Span, ga: &'v hir::GenericArgs<'v>) {
+ self.record("GenericArgs", Id::None, ga);
+ hir_visit::walk_generic_args(self, sp, ga)
+ }
+
fn visit_assoc_type_binding(&mut self, type_binding: &'v hir::TypeBinding<'v>) {
self.record("TypeBinding", Id::Node(type_binding.hir_id), type_binding);
hir_visit::walk_assoc_type_binding(self, type_binding)
fn visit_attribute(&mut self, attr: &'v ast::Attribute) {
self.record("Attribute", Id::Attr(attr.id), attr);
}
-}
-// Used to avoid boilerplate for types with many variants.
-macro_rules! record_variants {
- (
- ($self:ident, $val:expr, $kind:expr, $ty:ty, $tykind:ident), // mandatory pieces
- [$($variant:ident),*]
- ) => {
- match $kind {
- $(
- ast::$tykind::$variant { .. } => {
- $self.record_variant(stringify!($ty), stringify!($variant), Id::None, $val)
- }
- )*
- }
- };
+ fn visit_inline_asm(&mut self, asm: &'v hir::InlineAsm<'v>, id: HirId) {
+ self.record("InlineAsm", Id::None, asm);
+ hir_visit::walk_inline_asm(self, asm, id);
+ }
}
impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
fn visit_foreign_item(&mut self, i: &'v ast::ForeignItem) {
record_variants!(
- (self, i, i.kind, ForeignItem, ForeignItemKind),
+ (self, i, i.kind, Id::None, ast, ForeignItem, ForeignItemKind),
[Static, Fn, TyAlias, MacCall]
);
ast_visit::walk_foreign_item(self, i)
fn visit_item(&mut self, i: &'v ast::Item) {
record_variants!(
- (self, i, i.kind, Item, ItemKind),
+ (self, i, i.kind, Id::None, ast, Item, ItemKind),
[
ExternCrate,
Use,
fn visit_stmt(&mut self, s: &'v ast::Stmt) {
record_variants!(
- (self, s, s.kind, Stmt, StmtKind),
+ (self, s, s.kind, Id::None, ast, Stmt, StmtKind),
[Local, Item, Expr, Semi, Empty, MacCall]
);
ast_visit::walk_stmt(self, s)
fn visit_pat(&mut self, p: &'v ast::Pat) {
record_variants!(
- (self, p, p.kind, Pat, PatKind),
+ (self, p, p.kind, Id::None, ast, Pat, PatKind),
[
Wild,
Ident,
fn visit_expr(&mut self, e: &'v ast::Expr) {
record_variants!(
- (self, e, e.kind, Expr, ExprKind),
+ (self, e, e.kind, Id::None, ast, Expr, ExprKind),
[
Box, Array, ConstBlock, Call, MethodCall, Tup, Binary, Unary, Lit, Cast, Type, Let,
If, While, ForLoop, Loop, Match, Closure, Block, Async, Await, TryBlock, Assign,
fn visit_ty(&mut self, t: &'v ast::Ty) {
record_variants!(
- (self, t, t.kind, Ty, TyKind),
+ (self, t, t.kind, Id::None, ast, Ty, TyKind),
[
Slice,
Array,
fn visit_where_predicate(&mut self, p: &'v ast::WherePredicate) {
record_variants!(
- (self, p, p, WherePredicate, WherePredicate),
+ (self, p, p, Id::None, ast, WherePredicate, WherePredicate),
[BoundPredicate, RegionPredicate, EqPredicate]
);
ast_visit::walk_where_predicate(self, p)
fn visit_assoc_item(&mut self, i: &'v ast::AssocItem, ctxt: ast_visit::AssocCtxt) {
record_variants!(
- (self, i, i.kind, AssocItem, AssocItemKind),
+ (self, i, i.kind, Id::None, ast, AssocItem, AssocItemKind),
[Const, Fn, TyAlias, MacCall]
);
ast_visit::walk_assoc_item(self, i, ctxt);
}
fn visit_param_bound(&mut self, b: &'v ast::GenericBound, _ctxt: BoundKind) {
- record_variants!((self, b, b, GenericBound, GenericBound), [Trait, Outlives]);
+ record_variants!(
+ (self, b, b, Id::None, ast, GenericBound, GenericBound),
+ [Trait, Outlives]
+ );
ast_visit::walk_param_bound(self, b)
}
// common, so we don't implement `visit_use_tree` and tolerate the missed
// coverage in the latter case.
+ // `PathSegment` has one inline use (in `ast::ExprKind::MethodCall`) and
+ // one non-inline use (in `ast::Path::segments`). The latter case is more
+ // common than the former case, so we implement this visitor and tolerate
+ // the double counting in the former case.
fn visit_path_segment(&mut self, path_span: Span, path_segment: &'v ast::PathSegment) {
self.record("PathSegment", Id::None, path_segment);
ast_visit::walk_path_segment(self, path_span, path_segment)
// common, so we implement `visit_generic_args` and tolerate the double
// counting in the former case.
fn visit_generic_args(&mut self, sp: Span, g: &'v ast::GenericArgs) {
- record_variants!((self, g, g, GenericArgs, GenericArgs), [AngleBracketed, Parenthesized]);
+ record_variants!(
+ (self, g, g, Id::None, ast, GenericArgs, GenericArgs),
+ [AngleBracketed, Parenthesized]
+ );
ast_visit::walk_generic_args(self, sp, g)
}
fn visit_attribute(&mut self, attr: &'v ast::Attribute) {
- record_variants!((self, attr, attr.kind, Attribute, AttrKind), [Normal, DocComment]);
+ record_variants!(
+ (self, attr, attr.kind, Id::None, ast, Attribute, AttrKind),
+ [Normal, DocComment]
+ );
ast_visit::walk_attribute(self, attr)
}
self.propagate_through_expr(&f, succ)
}
- hir::ExprKind::MethodCall(.., ref args, _) => {
+ hir::ExprKind::MethodCall(.., receiver, ref args, _) => {
let succ = self.check_is_ty_uninhabited(expr, succ);
- self.propagate_through_exprs(args, succ)
+ let succ = self.propagate_through_exprs(args, succ);
+ self.propagate_through_expr(receiver, succ)
}
hir::ExprKind::Tup(ref exprs) => self.propagate_through_exprs(exprs, succ),
// added, such as `core::intrinsics::transmute`
let parents = path.segments.iter().rev().skip(1);
for path_segment in parents {
- if let Some(def_id) = path_segment.res.as_ref().and_then(Res::opt_def_id) {
+ if let Some(def_id) = path_segment.res.opt_def_id() {
// use `None` for id to prevent deprecation check
self.tcx.check_stability_allow_unstable(
def_id,
// If only some candidates are accessible, take just them
if !candidates.iter().all(|v: &ImportSuggestion| !v.accessible) {
- candidates = candidates.into_iter().filter(|x| x.accessible).collect();
+ candidates.retain(|x| x.accessible)
}
candidates
&mut self,
ex: &'tcx hir::Expr<'tcx>,
seg: &'tcx hir::PathSegment<'tcx>,
+ receiver: &'tcx hir::Expr<'tcx>,
args: &'tcx [hir::Expr<'tcx>],
) {
debug!("process_method_call {:?} {:?}", ex, ex.span);
}
// walk receiver and args
+ self.visit_expr(receiver);
walk_list!(self, visit_expr, args);
}
_,
)
| Res::SelfTy { .. } => {
- self.dump_path_segment_ref(id, &hir::PathSegment::from_ident(ident));
+ self.dump_path_segment_ref(
+ id,
+ &hir::PathSegment::new(ident, hir::HirId::INVALID, Res::Err),
+ );
}
def => {
error!("unexpected definition kind when processing collected idents: {:?}", def)
self.process_macro_use(trait_item.span);
match trait_item.kind {
hir::TraitItemKind::Const(ref ty, body) => {
- let body = body.map(|b| &self.tcx.hir().body(b).value);
+ let body = body.map(|b| self.tcx.hir().body(b).value);
let attrs = self.tcx.hir().attrs(trait_item.hir_id());
self.process_assoc_const(
trait_item.def_id,
let res = self.save_ctxt.get_path_res(hir_expr.hir_id);
self.process_struct_lit(ex, path, fields, adt.variant_of_res(res), *rest)
}
- hir::ExprKind::MethodCall(ref seg, args, _) => self.process_method_call(ex, seg, args),
+ hir::ExprKind::MethodCall(ref seg, receiver, args, _) => {
+ self.process_method_call(ex, seg, receiver, args)
+ }
hir::ExprKind::Field(ref sub_ex, _) => {
self.visit_expr(&sub_ex);
Node::TraitRef(tr) => tr.path.res,
Node::Item(&hir::Item { kind: hir::ItemKind::Use(path, _), .. }) => path.res,
- Node::PathSegment(seg) => match seg.res {
- Some(res) if res != Res::Err => res,
- _ => {
+ Node::PathSegment(seg) => {
+ if seg.res != Res::Err {
+ seg.res
+ } else {
let parent_node = self.tcx.hir().get_parent_node(hir_id);
self.get_path_res(parent_node)
}
- },
+ }
Node::Expr(&hir::Expr { kind: hir::ExprKind::Struct(ref qpath, ..), .. }) => {
self.typeck_results().qpath_res(qpath, hir_id)
}
pub fn get_path_segment_data(&self, path_seg: &hir::PathSegment<'_>) -> Option<Ref> {
- self.get_path_segment_data_with_id(path_seg, path_seg.hir_id?)
+ self.get_path_segment_data_with_id(path_seg, path_seg.hir_id)
}
pub fn get_path_segment_data_with_id(
const_deallocate,
const_eval_limit,
const_eval_select,
- const_eval_select_ct,
const_evaluatable_checked,
const_extern_fn,
const_fn,
&& let [
..,
trait_path_segment @ hir::PathSegment {
- res: Some(rustc_hir::def::Res::Def(rustc_hir::def::DefKind::Trait, trait_id)),
+ res: rustc_hir::def::Res::Def(rustc_hir::def::DefKind::Trait, trait_id),
..
},
hir::PathSegment {
ident: assoc_item_name,
- res: Some(rustc_hir::def::Res::Def(_, item_id)),
+ res: rustc_hir::def::Res::Def(_, item_id),
..
}
] = path.segments
multispan.push_span_label(span_late, note);
tcx.struct_span_lint_hir(
LATE_BOUND_LIFETIME_ARGUMENTS,
- args.args[0].id(),
+ args.args[0].hir_id(),
multispan,
|lint| {
lint.build(msg).emit();
if has_default {
tcx.check_optional_stability(
param.def_id,
- Some(arg.id()),
+ Some(arg.hir_id()),
arg.span(),
None,
AllowUnstable::No,
let ident = Ident::new(assoc_item.name, binding.item_name.span);
let item_segment = hir::PathSegment {
ident,
- hir_id: Some(binding.hir_id),
- res: None,
+ hir_id: binding.hir_id,
+ res: Res::Err,
args: Some(binding.gen_args),
infer_args: false,
};
[.., hir::PathSegment {
ident,
args,
- res: Some(Res::Def(DefKind::Enum, _)),
+ res: Res::Def(DefKind::Enum, _),
..
}, _] => (
// We need to include the `::` in `Type::Variant::<Args>`
let types_and_spans: Vec<_> = segments
.clone()
.flat_map(|segment| {
- segment.res.and_then(|res| {
- if segment.args().args.is_empty() {
- None
- } else {
- Some((
- match res {
- Res::PrimTy(ty) => format!("{} `{}`", res.descr(), ty.name()),
+ if segment.args().args.is_empty() {
+ None
+ } else {
+ Some((
+ match segment.res {
+ Res::PrimTy(ty) => format!("{} `{}`", segment.res.descr(), ty.name()),
Res::Def(_, def_id)
if let Some(name) = self.tcx().opt_item_name(def_id) => {
- format!("{} `{name}`", res.descr())
+ format!("{} `{name}`", segment.res.descr())
}
Res::Err => "this type".to_string(),
- _ => res.descr().to_string(),
+ _ => segment.res.descr().to_string(),
},
segment.ident.span,
))
- }
- })
+ }
})
.collect();
let this_type = match &types_and_spans[..] {
fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) {
match arg.kind {
hir::TyKind::Path(hir::QPath::Resolved(None, path)) => match &path.segments {
- [
- PathSegment {
- res: Some(Res::SelfTy { trait_: _, alias_to: impl_ref }),
- ..
- },
- ] => {
+ [PathSegment { res: Res::SelfTy { trait_: _, alias_to: impl_ref }, .. }] => {
let impl_ty_name =
impl_ref.map(|(def_id, _)| self.tcx.def_path_str(def_id));
self.selftys.push((path.span, impl_ty_name));
self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty);
self.suggest_missing_parentheses(err, expr);
self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected);
+ self.suggest_copied_or_cloned(err, expr, expr_ty, expected);
self.note_type_is_not_clone(err, expected, expr_ty, expr);
self.note_need_for_fn_pointer(err, expected, expr_ty);
self.note_internal_mutation_in_method(err, expr, expected, expr_ty);
let closure_params_len = closure_fn_decl.inputs.len();
let (
Some(Node::Expr(hir::Expr {
- kind: hir::ExprKind::MethodCall(method_path, method_expr, _),
+ kind: hir::ExprKind::MethodCall(method_path, receiver, ..),
..
})),
1,
return None;
};
- let self_ty = self.typeck_results.borrow().expr_ty(&method_expr[0]);
+ let self_ty = self.typeck_results.borrow().expr_ty(receiver);
let name = method_path.ident.name;
let is_as_ref_able = match self_ty.peel_refs().kind() {
ty::Adt(def, _) => {
};
if self.can_coerce(ref_ty, expected) {
let mut sugg_sp = sp;
- if let hir::ExprKind::MethodCall(ref segment, ref args, _) = expr.kind {
+ if let hir::ExprKind::MethodCall(ref segment, receiver, args, _) = expr.kind {
let clone_trait =
self.tcx.require_lang_item(LangItem::Clone, Some(segment.ident.span));
- if let ([arg], Some(true), sym::clone) = (
- &args[..],
- self.typeck_results.borrow().type_dependent_def_id(expr.hir_id).map(
+ if args.is_empty()
+ && self.typeck_results.borrow().type_dependent_def_id(expr.hir_id).map(
|did| {
let ai = self.tcx.associated_item(did);
ai.trait_container(self.tcx) == Some(clone_trait)
},
- ),
- segment.ident.name,
- ) {
+ ) == Some(true)
+ && segment.ident.name == sym::clone
+ {
// If this expression had a clone call when suggesting borrowing
// we want to suggest removing it because it'd now be unnecessary.
- sugg_sp = arg.span;
+ sugg_sp = receiver.span;
}
}
if let Ok(src) = sm.span_to_snippet(sugg_sp) {
}
ExprKind::Block(body, _) => self.check_block_with_expected(&body, expected),
ExprKind::Call(callee, args) => self.check_call(expr, &callee, args, expected),
- ExprKind::MethodCall(segment, args, _) => {
- self.check_method_call(expr, segment, args, expected)
+ ExprKind::MethodCall(segment, receiver, args, _) => {
+ self.check_method_call(expr, segment, receiver, args, expected)
}
ExprKind::Cast(e, t) => self.check_expr_cast(e, t, expr),
ExprKind::Type(e, t) => {
&self,
expr: &'tcx hir::Expr<'tcx>,
segment: &hir::PathSegment<'_>,
+ rcvr: &'tcx hir::Expr<'tcx>,
args: &'tcx [hir::Expr<'tcx>],
expected: Expectation<'tcx>,
) -> Ty<'tcx> {
- let rcvr = &args[0];
let rcvr_t = self.check_expr(&rcvr);
// no need to check for bot/err -- callee does that
- let rcvr_t = self.structurally_resolved_type(args[0].span, rcvr_t);
+ let rcvr_t = self.structurally_resolved_type(rcvr.span, rcvr_t);
let span = segment.ident.span;
let method = match self.lookup_method(rcvr_t, segment, span, expr, rcvr, args) {
span,
rcvr_t,
segment.ident,
- SelfSource::MethodCall(&args[0]),
+ SelfSource::MethodCall(rcvr),
error,
- Some(args),
+ Some((rcvr, args)),
) {
err.emit();
}
};
// Call the generic checker.
- self.check_method_argument_types(
- span,
- expr,
- method,
- &args[1..],
- DontTupleArguments,
- expected,
- )
+ self.check_method_argument_types(span, expr, method, &args, DontTupleArguments, expected)
}
fn check_expr_cast(
if found != self.tcx.types.unit {
return;
}
- if let ExprKind::MethodCall(path_segment, [rcvr, ..], _) = expr.kind {
+ if let ExprKind::MethodCall(path_segment, rcvr, ..) = expr.kind {
if self
.typeck_results
.borrow()
use rustc_middle::ty::{self, DefIdTree, IsSuggestable, Ty, TypeSuperVisitable, TypeVisitor};
use rustc_session::Session;
use rustc_span::symbol::Ident;
-use rustc_span::{self, Span};
+use rustc_span::{self, sym, Span};
use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext};
use std::iter;
let minimum_input_count = expected_input_tys.len();
let provided_arg_count = provided_args.len();
+ let is_const_eval_select = matches!(fn_def_id, Some(def_id) if
+ self.tcx.def_kind(def_id) == hir::def::DefKind::Fn
+ && self.tcx.is_intrinsic(def_id)
+ && self.tcx.item_name(def_id) == sym::const_eval_select);
+
// We introduce a helper function to demand that a given argument satisfy a given input
// This is more complicated than just checking type equality, as arguments could be coerced
// This version writes those types back so further type checking uses the narrowed types
return Compatibility::Incompatible(coerce_error);
}
+ // Check that second and third argument of `const_eval_select` must be `FnDef`, and additionally that
+ // the second argument must be `const fn`. The first argument must be a tuple, but this is already expressed
+ // in the function signature (`F: FnOnce<ARG>`), so I did not bother to add another check here.
+ //
+ // This check is here because there is currently no way to express a trait bound for `FnDef` types only.
+ if is_const_eval_select && (1..=2).contains(&idx) {
+ if let ty::FnDef(def_id, _) = checked_ty.kind() {
+ if idx == 1 && !self.tcx.is_const_fn_raw(*def_id) {
+ self.tcx
+ .sess
+ .struct_span_err(provided_arg.span, "this argument must be a `const fn`")
+ .help("consult the documentation on `const_eval_select` for more information")
+ .emit();
+ }
+ } else {
+ self.tcx
+ .sess
+ .struct_span_err(provided_arg.span, "this argument must be a function item")
+ .note(format!("expected a function item, found {checked_ty}"))
+ .help(
+ "consult the documentation on `const_eval_select` for more information",
+ )
+ .emit();
+ }
+ }
+
// 3. Check if the formal type is a supertype of the checked one
// and register any such obligations for future type checks
let supertype_error = self
}
}
hir::ExprKind::Call(hir::Expr { span, .. }, _) => (call_span, *span, None, false),
- hir::ExprKind::MethodCall(path_segment, _, span) => {
+ hir::ExprKind::MethodCall(path_segment, _, _, span) => {
let ident_span = path_segment.ident.span;
let ident_span = if let Some(args) = path_segment.args {
ident_span.with_hi(args.span_ext.hi())
.collect();
let callee_expr = match &call_expr.peel_blocks().kind {
hir::ExprKind::Call(callee, _) => Some(*callee),
- hir::ExprKind::MethodCall(_, callee, _) => {
+ hir::ExprKind::MethodCall(_, receiver, ..) => {
if let Some((DefKind::AssocFn, def_id)) =
self.typeck_results.borrow().type_dependent_def(call_expr.hir_id)
&& let Some(assoc) = tcx.opt_associated_item(def_id)
&& assoc.fn_has_self_parameter
{
- Some(&callee[0])
+ Some(*receiver)
} else {
None
}
param,
*call_hir_id,
callee.span,
+ None,
args,
)
{
return true;
}
}
- hir::ExprKind::MethodCall(segment, args, ..) => {
+ hir::ExprKind::MethodCall(segment, receiver, args, ..) => {
for param in [param_to_point_at, fallback_param_to_point_at, self_param_to_point_at]
.into_iter()
.flatten()
param,
hir_id,
segment.ident.span,
+ Some(receiver),
args,
) {
return true;
param_to_point_at: ty::GenericArg<'tcx>,
call_hir_id: hir::HirId,
callee_span: Span,
- args: &[hir::Expr<'tcx>],
+ receiver: Option<&'tcx hir::Expr<'tcx>>,
+ args: &'tcx [hir::Expr<'tcx>],
) -> bool {
let sig = self.tcx.fn_sig(def_id).skip_binder();
let args_referencing_param: Vec<_> = sig
.enumerate()
.filter(|(_, ty)| find_param_in_ty(**ty, param_to_point_at))
.collect();
-
// If there's one field that references the given generic, great!
- if let [(idx, _)] = args_referencing_param.as_slice() && let Some(arg) = args.get(*idx) {
+ if let [(idx, _)] = args_referencing_param.as_slice()
+ && let Some(arg) = receiver
+ .map_or(args.get(*idx), |rcvr| if *idx == 0 { Some(rcvr) } else { args.get(*idx - 1) }) {
error.obligation.cause.span = arg.span.find_ancestor_in_same_ctxt(error.obligation.cause.span).unwrap_or(arg.span);
error.obligation.cause.map_code(|parent_code| {
ObligationCauseCode::FunctionArgumentObligation {
use rustc_middle::ty::{self, Binder, IsSuggestable, Subst, ToPredicate, Ty};
use rustc_span::symbol::sym;
use rustc_span::Span;
+use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
+ pub(crate) fn suggest_copied_or_cloned(
+ &self,
+ diag: &mut Diagnostic,
+ expr: &hir::Expr<'_>,
+ expr_ty: Ty<'tcx>,
+ expected_ty: Ty<'tcx>,
+ ) {
+ let ty::Adt(adt_def, substs) = expr_ty.kind() else { return; };
+ let ty::Adt(expected_adt_def, expected_substs) = expected_ty.kind() else { return; };
+ if adt_def != expected_adt_def {
+ return;
+ }
+
+ let mut suggest_copied_or_cloned = || {
+ let expr_inner_ty = substs.type_at(0);
+ let expected_inner_ty = expected_substs.type_at(0);
+ if let ty::Ref(_, ty, hir::Mutability::Not) = expr_inner_ty.kind()
+ && self.can_eq(self.param_env, *ty, expected_inner_ty).is_ok()
+ {
+ let def_path = self.tcx.def_path_str(adt_def.did());
+ if self.type_is_copy_modulo_regions(self.param_env, *ty, expr.span) {
+ diag.span_suggestion_verbose(
+ expr.span.shrink_to_hi(),
+ format!(
+ "use `{def_path}::copied` to copy the value inside the `{def_path}`"
+ ),
+ ".copied()",
+ Applicability::MachineApplicable,
+ );
+ } else if let Some(clone_did) = self.tcx.lang_items().clone_trait()
+ && rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions(
+ self,
+ self.param_env,
+ *ty,
+ clone_did,
+ expr.span
+ )
+ {
+ diag.span_suggestion_verbose(
+ expr.span.shrink_to_hi(),
+ format!(
+ "use `{def_path}::cloned` to clone the value inside the `{def_path}`"
+ ),
+ ".cloned()",
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+ };
+
+ if let Some(result_did) = self.tcx.get_diagnostic_item(sym::Result)
+ && adt_def.did() == result_did
+ // Check that the error types are equal
+ && self.can_eq(self.param_env, substs.type_at(1), expected_substs.type_at(1)).is_ok()
+ {
+ suggest_copied_or_cloned();
+ } else if let Some(option_did) = self.tcx.get_diagnostic_item(sym::Option)
+ && adt_def.did() == option_did
+ {
+ suggest_copied_or_cloned();
+ }
+ }
+
/// Suggest wrapping the block in square brackets instead of curly braces
/// in case the block was mistaken array syntax, e.g. `{ 1 }` -> `[ 1 ]`.
pub(crate) fn suggest_block_to_brackets(
found_ty: Ty<'tcx>,
expr: &hir::Expr<'_>,
) {
- let hir::ExprKind::MethodCall(segment, &[ref callee_expr], _) = expr.kind else { return; };
+ let hir::ExprKind::MethodCall(segment, callee_expr, &[], _) = expr.kind else { return; };
let Some(clone_trait_did) = self.tcx.lang_items().clone_trait() else { return; };
let ty::Ref(_, pointee_ty, _) = found_ty.kind() else { return };
let results = self.typeck_results.borrow();
self.handle_uninhabited_return(expr);
}
- ExprKind::MethodCall(_, exprs, _) => {
+ ExprKind::MethodCall(_, receiver, exprs, _) => {
+ self.visit_expr(receiver);
for expr in exprs {
self.visit_expr(expr);
}
if precise {
let args = args
.iter()
- .skip(1)
.map(|arg| {
let span = arg.span.find_ancestor_inside(sp).unwrap_or_default();
format!(
item_name: Ident,
source: SelfSource<'tcx>,
error: MethodError<'tcx>,
- args: Option<&'tcx [hir::Expr<'tcx>]>,
+ args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>,
) -> Option<DiagnosticBuilder<'_, ErrorGuaranteed>> {
// Avoid suggestions when we don't know what's going on.
if rcvr_ty.references_error() {
span,
rcvr_ty,
item_name,
- args.map(|args| args.len()),
+ args.map(|(_, args)| args.len() + 1),
source,
out_of_scope_traits,
&unsatisfied_predicates,
fn print_disambiguation_help<'tcx>(
item_name: Ident,
- args: Option<&'tcx [hir::Expr<'tcx>]>,
+ args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>,
err: &mut Diagnostic,
trait_name: String,
rcvr_ty: Ty<'_>,
fn_has_self_parameter: bool,
) {
let mut applicability = Applicability::MachineApplicable;
- let (span, sugg) = if let (ty::AssocKind::Fn, Some(args)) = (kind, args) {
+ let (span, sugg) = if let (ty::AssocKind::Fn, Some((receiver, args))) = (kind, args) {
let args = format!(
"({}{})",
if rcvr_ty.is_region_ptr() {
} else {
""
},
- args.iter()
+ std::iter::once(receiver)
+ .chain(args.iter())
.map(|arg| source_map.span_to_snippet(arg.span).unwrap_or_else(|_| {
applicability = Applicability::HasPlaceholders;
"_".to_owned()
)
.is_ok()
{
- // Suppress this error, since we already emitted
- // a deref suggestion in check_overloaded_binop
- err.downgrade_to_delayed_bug();
+ // If LHS += RHS is an error, but *LHS += RHS is successful, then we will have
+ // emitted a better suggestion during error handling in check_overloaded_binop.
+ if self
+ .lookup_op_method(
+ lhs_ty,
+ Some(rhs_ty),
+ Some(rhs),
+ Op::Binary(op, IsAssign::Yes),
+ expected,
+ )
+ .is_err()
+ {
+ err.downgrade_to_delayed_bug();
+ } else {
+ // Otherwise, it's valid to suggest dereferencing the LHS here.
+ err.span_suggestion_verbose(
+ lhs.span.shrink_to_lo(),
+ "consider dereferencing the left-hand side of this operation",
+ "*",
+ Applicability::MaybeIncorrect,
+ );
+ }
}
}
});
tcx: TyCtxt<'_>,
closure_id: hir::HirId,
) -> bool {
+ if tcx.sess.rust_2021() {
+ return false;
+ }
+
let (level, _) =
tcx.lint_level_at_node(lint::builtin::RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES, closure_id);
fn could_be_self(trait_def_id: LocalDefId, ty: &hir::Ty<'_>) -> bool {
match ty.kind {
hir::TyKind::TraitObject([trait_ref], ..) => match trait_ref.trait_ref.path.segments {
- [s] => s.res.and_then(|r| r.opt_def_id()) == Some(trait_def_id.to_def_id()),
+ [s] => s.res.opt_def_id() == Some(trait_def_id.to_def_id()),
_ => false,
},
_ => false,
use rustc_errors::{Applicability, StashKey};
use rustc_hir as hir;
-use rustc_hir::def::Res;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit;
use rustc_hir::intravisit::Visitor;
args.args
.iter()
.filter(|arg| arg.is_ty_or_const())
- .position(|arg| arg.id() == hir_id)
+ .position(|arg| arg.hir_id() == hir_id)
})
.unwrap_or_else(|| {
bug!("no arg matching AnonConst in segment");
args.args
.iter()
.filter(|arg| arg.is_ty_or_const())
- .position(|arg| arg.id() == hir_id)
+ .position(|arg| arg.hir_id() == hir_id)
})
.unwrap_or_else(|| {
bug!("no arg matching AnonConst in segment");
args.args
.iter()
.filter(|arg| arg.is_ty_or_const())
- .position(|arg| arg.id() == hir_id)
+ .position(|arg| arg.hir_id() == hir_id)
.map(|index| (index, seg)).or_else(|| args.bindings
.iter()
.filter_map(TypeBinding::opt_const)
return None;
};
- // Try to use the segment resolution if it is valid, otherwise we
- // default to the path resolution.
- let res = segment.res.filter(|&r| r != Res::Err).unwrap_or(path.res);
- let generics = match tcx.res_generics_def_id(res) {
+ let generics = match tcx.res_generics_def_id(segment.res) {
Some(def_id) => tcx.generics_of(def_id),
None => {
tcx.sess.delay_span_bug(
tcx.def_span(def_id),
- &format!("unexpected anon const res {:?} in path: {:?}", res, path),
+ &format!("unexpected anon const res {:?} in path: {:?}", segment.res, path),
);
return None;
}
.iter()
.filter_map(|seg| seg.args)
.flat_map(|args| args.args)
- .any(|arg| arg.id() == arg_id)
+ .any(|arg| arg.hir_id() == arg_id)
};
let mut arg_path = None;
pat.walk(|pat| match pat.kind {
self.consume_exprs(args);
}
- hir::ExprKind::MethodCall(.., args, _) => {
+ hir::ExprKind::MethodCall(.., receiver, args, _) => {
// callee.m(args)
+ self.consume_expr(receiver);
self.consume_exprs(args);
}
// Creates lifetime name suggestions from the lifetime parameter names
fn get_lifetime_args_suggestions_from_param_names(
&self,
- path_hir_id: Option<hir::HirId>,
+ path_hir_id: hir::HirId,
num_params_to_take: usize,
) -> String {
debug!(?path_hir_id);
- if let Some(path_hir_id) = path_hir_id {
- let mut ret = Vec::new();
- for (id, node) in self.tcx.hir().parent_iter(path_hir_id) {
- debug!(?id);
- let params = if let Some(generics) = node.generics() {
- generics.params
- } else if let hir::Node::Ty(ty) = node
- && let hir::TyKind::BareFn(bare_fn) = ty.kind
- {
- bare_fn.generic_params
- } else {
- &[]
- };
- ret.extend(params.iter().filter_map(|p| {
- let hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Explicit }
- = p.kind
- else { return None };
- let hir::ParamName::Plain(name) = p.name else { return None };
- Some(name.to_string())
- }));
- // Suggest `'static` when in const/static item-like.
- if let hir::Node::Item(hir::Item {
- kind: hir::ItemKind::Static { .. } | hir::ItemKind::Const { .. },
- ..
- })
- | hir::Node::TraitItem(hir::TraitItem {
- kind: hir::TraitItemKind::Const { .. },
- ..
- })
- | hir::Node::ImplItem(hir::ImplItem {
- kind: hir::ImplItemKind::Const { .. },
- ..
- })
- | hir::Node::ForeignItem(hir::ForeignItem {
- kind: hir::ForeignItemKind::Static { .. },
- ..
- })
- | hir::Node::AnonConst(..) = node
- {
- ret.extend(
- std::iter::repeat("'static".to_owned())
- .take(num_params_to_take.saturating_sub(ret.len())),
- );
- }
- if ret.len() >= num_params_to_take {
- return ret[..num_params_to_take].join(", ");
- }
- // We cannot refer to lifetimes defined in an outer function.
- if let hir::Node::Item(_) = node {
- break;
- }
+ let mut ret = Vec::new();
+ for (id, node) in self.tcx.hir().parent_iter(path_hir_id) {
+ debug!(?id);
+ let params = if let Some(generics) = node.generics() {
+ generics.params
+ } else if let hir::Node::Ty(ty) = node
+ && let hir::TyKind::BareFn(bare_fn) = ty.kind
+ {
+ bare_fn.generic_params
+ } else {
+ &[]
+ };
+ ret.extend(params.iter().filter_map(|p| {
+ let hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Explicit }
+ = p.kind
+ else { return None };
+ let hir::ParamName::Plain(name) = p.name else { return None };
+ Some(name.to_string())
+ }));
+ // Suggest `'static` when in const/static item-like.
+ if let hir::Node::Item(hir::Item {
+ kind: hir::ItemKind::Static { .. } | hir::ItemKind::Const { .. },
+ ..
+ })
+ | hir::Node::TraitItem(hir::TraitItem {
+ kind: hir::TraitItemKind::Const { .. },
+ ..
+ })
+ | hir::Node::ImplItem(hir::ImplItem {
+ kind: hir::ImplItemKind::Const { .. },
+ ..
+ })
+ | hir::Node::ForeignItem(hir::ForeignItem {
+ kind: hir::ForeignItemKind::Static { .. },
+ ..
+ })
+ | hir::Node::AnonConst(..) = node
+ {
+ ret.extend(
+ std::iter::repeat("'static".to_owned())
+ .take(num_params_to_take.saturating_sub(ret.len())),
+ );
+ }
+ if ret.len() >= num_params_to_take {
+ return ret[..num_params_to_take].join(", ");
+ }
+ // We cannot refer to lifetimes defined in an outer function.
+ if let hir::Node::Item(_) = node {
+ break;
}
}
num = num_trait_generics_except_self,
);
- if let Some(hir_id) = self.path_segment.hir_id
- && let Some(parent_node) = self.tcx.hir().find_parent_node(hir_id)
+ if let Some(parent_node) = self.tcx.hir().find_parent_node(self.path_segment.hir_id)
&& let Some(parent_node) = self.tcx.hir().find(parent_node)
&& let hir::Node::Expr(expr) = parent_node {
match expr.kind {
num_assoc_fn_excess_args: usize,
num_trait_generics_except_self: usize,
) {
- if let hir::ExprKind::MethodCall(_, args, _) = expr.kind {
- assert_eq!(args.len(), 1);
+ if let hir::ExprKind::MethodCall(_, receiver, args, ..) = expr.kind {
+ assert_eq!(args.len(), 0);
if num_assoc_fn_excess_args == num_trait_generics_except_self {
if let Some(gen_args) = self.gen_args.span_ext()
&& let Ok(gen_args) = self.tcx.sess.source_map().span_to_snippet(gen_args)
- && let Ok(args) = self.tcx.sess.source_map().span_to_snippet(args[0].span) {
- let sugg = format!("{}::{}::{}({})", self.tcx.item_name(trait_), gen_args, self.tcx.item_name(self.def_id), args);
+ && let Ok(receiver) = self.tcx.sess.source_map().span_to_snippet(receiver.span) {
+ let sugg = format!("{}::{}::{}({})", self.tcx.item_name(trait_), gen_args, self.tcx.item_name(self.def_id), receiver);
err.span_suggestion(expr.span, msg, sugg, Applicability::MaybeIncorrect);
}
}
/// A iterator for deduping the key of a sorted iterator.
/// When encountering the duplicated key, only the last key-value pair is yielded.
///
-/// Used by [`BTreeMap::bulk_build_from_sorted_iter`].
+/// Used by [`BTreeMap::bulk_build_from_sorted_iter`][1].
+///
+/// [1]: crate::collections::BTreeMap::bulk_build_from_sorted_iter
pub struct DedupSortedIter<K, V, I>
where
I: Iterator<Item = (K, V)>,
#![feature(ptr_metadata)]
#![feature(ptr_sub_ptr)]
#![feature(receiver_trait)]
+#![feature(saturating_int_impl)]
#![feature(set_ptr_value)]
#![feature(slice_from_ptr_range)]
#![feature(slice_group_by)]
+use core::num::{Saturating, Wrapping};
+
use crate::boxed::Box;
#[rustc_specialization_trait]
NonZeroUsize,
NonZeroIsize,
);
+
+unsafe impl<T: IsZero> IsZero for Wrapping<T> {
+ #[inline]
+ fn is_zero(&self) -> bool {
+ self.0.is_zero()
+ }
+}
+
+unsafe impl<T: IsZero> IsZero for Saturating<T> {
+ #[inline]
+ fn is_zero(&self) -> bool {
+ self.0.is_zero()
+ }
+}
/// assert!(!'ä¸'.is_lowercase());
/// assert!(!' '.is_lowercase());
/// ```
+ ///
+ /// In a const context:
+ ///
+ /// ```
+ /// #![feature(const_unicode_case_lookup)]
+ /// const CAPITAL_DELTA_IS_LOWERCASE: bool = 'Δ'.is_lowercase();
+ /// assert!(!CAPITAL_DELTA_IS_LOWERCASE);
+ /// ```
#[must_use]
#[stable(feature = "rust1", since = "1.0.0")]
+ #[rustc_const_unstable(feature = "const_unicode_case_lookup", issue = "101400")]
#[inline]
- pub fn is_lowercase(self) -> bool {
+ pub const fn is_lowercase(self) -> bool {
match self {
'a'..='z' => true,
c => c > '\x7f' && unicode::Lowercase(c),
/// assert!(!'ä¸'.is_uppercase());
/// assert!(!' '.is_uppercase());
/// ```
+ ///
+ /// In a const context:
+ ///
+ /// ```
+ /// #![feature(const_unicode_case_lookup)]
+ /// const CAPITAL_DELTA_IS_UPPERCASE: bool = 'Δ'.is_uppercase();
+ /// assert!(CAPITAL_DELTA_IS_UPPERCASE);
+ /// ```
#[must_use]
#[stable(feature = "rust1", since = "1.0.0")]
+ #[rustc_const_unstable(feature = "const_unicode_case_lookup", issue = "101400")]
#[inline]
- pub fn is_uppercase(self) -> bool {
+ pub const fn is_uppercase(self) -> bool {
match self {
'A'..='Z' => true,
c => c > '\x7f' && unicode::Uppercase(c),
)]
#![allow(missing_docs)]
-use crate::marker::{Destruct, DiscriminantKind};
+#[cfg(bootstrap)]
+use crate::marker::Destruct;
+use crate::marker::DiscriminantKind;
use crate::mem;
// These imports are used for simplifying intra-doc links
/// `ptr` must point to a vtable.
/// The intrinsic will return the alignment stored in that vtable.
pub fn vtable_align(ptr: *const ()) -> usize;
+
+ /// Selects which function to call depending on the context.
+ ///
+ /// If this function is evaluated at compile-time, then a call to this
+ /// intrinsic will be replaced with a call to `called_in_const`. It gets
+ /// replaced with a call to `called_at_rt` otherwise.
+ ///
+ /// # Type Requirements
+ ///
+ /// The two functions must be both function items. They cannot be function
+ /// pointers or closures. The first function must be a `const fn`.
+ ///
+ /// `arg` will be the tupled arguments that will be passed to either one of
+ /// the two functions, therefore, both functions must accept the same type of
+ /// arguments. Both functions must return RET.
+ ///
+ /// # Safety
+ ///
+ /// The two functions must behave observably equivalent. Safe code in other
+ /// crates may assume that calling a `const fn` at compile-time and at run-time
+ /// produces the same result. A function that produces a different result when
+ /// evaluated at run-time, or has any other observable side-effects, is
+ /// *unsound*.
+ ///
+ /// Here is an example of how this could cause a problem:
+ /// ```no_run
+ /// #![feature(const_eval_select)]
+ /// #![feature(core_intrinsics)]
+ /// use std::hint::unreachable_unchecked;
+ /// use std::intrinsics::const_eval_select;
+ ///
+ /// // Crate A
+ /// pub const fn inconsistent() -> i32 {
+ /// fn runtime() -> i32 { 1 }
+ /// const fn compiletime() -> i32 { 2 }
+ ///
+ /// unsafe {
+ // // âš This code violates the required equivalence of `compiletime`
+ /// // and `runtime`.
+ /// const_eval_select((), compiletime, runtime)
+ /// }
+ /// }
+ ///
+ /// // Crate B
+ /// const X: i32 = inconsistent();
+ /// let x = inconsistent();
+ /// if x != X { unsafe { unreachable_unchecked(); }}
+ /// ```
+ ///
+ /// This code causes Undefined Behavior when being run, since the
+ /// `unreachable_unchecked` is actually being reached. The bug is in *crate A*,
+ /// which violates the principle that a `const fn` must behave the same at
+ /// compile-time and at run-time. The unsafe code in crate B is fine.
+ #[cfg(not(bootstrap))]
+ #[rustc_const_unstable(feature = "const_eval_select", issue = "none")]
+ pub fn const_eval_select<ARG, F, G, RET>(arg: ARG, called_in_const: F, called_at_rt: G) -> RET
+ where
+ G: FnOnce<ARG, Output = RET>,
+ F: FnOnce<ARG, Output = RET>;
}
// Some functions are defined here because they accidentally got made
/// Check that the preconditions of an unsafe function are followed, if debug_assertions are on,
/// and only at runtime.
///
+/// This macro should be called as `assert_unsafe_precondition!([Generics](name: Type) => Expression)`
+/// where the names specified will be moved into the macro as captured variables, and defines an item
+/// to call `const_eval_select` on. The tokens inside the square brackets are used to denote generics
+/// for the function declaractions and can be omitted if there is no generics.
+///
/// # Safety
///
/// Invoking this macro is only sound if the following code is already UB when the passed
/// the occasional mistake, and this check should help them figure things out.
#[allow_internal_unstable(const_eval_select)] // permit this to be called in stably-const fn
macro_rules! assert_unsafe_precondition {
- ($e:expr) => {
+ ($([$($tt:tt)*])?($($i:ident:$ty:ty),*$(,)?) => $e:expr) => {
if cfg!(debug_assertions) {
- // Use a closure so that we can capture arbitrary expressions from the invocation
- let runtime = || {
+ // allow non_snake_case to allow capturing const generics
+ #[allow(non_snake_case)]
+ #[inline(always)]
+ fn runtime$(<$($tt)*>)?($($i:$ty),*) {
if !$e {
// abort instead of panicking to reduce impact on code size
::core::intrinsics::abort();
}
- };
- const fn comptime() {}
+ }
+ #[allow(non_snake_case)]
+ const fn comptime$(<$($tt)*>)?($(_:$ty),*) {}
- ::core::intrinsics::const_eval_select((), comptime, runtime);
+ ::core::intrinsics::const_eval_select(($($i,)*), comptime, runtime);
}
};
}
// SAFETY: the safety contract for `copy_nonoverlapping` must be
// upheld by the caller.
unsafe {
- assert_unsafe_precondition!(
+ assert_unsafe_precondition!([T](src: *const T, dst: *mut T, count: usize) =>
is_aligned_and_not_null(src)
&& is_aligned_and_not_null(dst)
&& is_nonoverlapping(src, dst, count)
// SAFETY: the safety contract for `copy` must be upheld by the caller.
unsafe {
- assert_unsafe_precondition!(is_aligned_and_not_null(src) && is_aligned_and_not_null(dst));
+ assert_unsafe_precondition!([T](src: *const T, dst: *mut T) =>
+ is_aligned_and_not_null(src) && is_aligned_and_not_null(dst));
copy(src, dst, count)
}
}
// SAFETY: the safety contract for `write_bytes` must be upheld by the caller.
unsafe {
- assert_unsafe_precondition!(is_aligned_and_not_null(dst));
+ assert_unsafe_precondition!([T](dst: *mut T) => is_aligned_and_not_null(dst));
write_bytes(dst, val, count)
}
}
-/// Selects which function to call depending on the context.
-///
-/// If this function is evaluated at compile-time, then a call to this
-/// intrinsic will be replaced with a call to `called_in_const`. It gets
-/// replaced with a call to `called_at_rt` otherwise.
-///
-/// # Type Requirements
-///
-/// The two functions must be both function items. They cannot be function
-/// pointers or closures.
-///
-/// `arg` will be the arguments that will be passed to either one of the
-/// two functions, therefore, both functions must accept the same type of
-/// arguments. Both functions must return RET.
-///
-/// # Safety
-///
-/// The two functions must behave observably equivalent. Safe code in other
-/// crates may assume that calling a `const fn` at compile-time and at run-time
-/// produces the same result. A function that produces a different result when
-/// evaluated at run-time, or has any other observable side-effects, is
-/// *unsound*.
-///
-/// Here is an example of how this could cause a problem:
-/// ```no_run
-/// #![feature(const_eval_select)]
-/// #![feature(core_intrinsics)]
-/// use std::hint::unreachable_unchecked;
-/// use std::intrinsics::const_eval_select;
-///
-/// // Crate A
-/// pub const fn inconsistent() -> i32 {
-/// fn runtime() -> i32 { 1 }
-/// const fn compiletime() -> i32 { 2 }
-///
-/// unsafe {
-// // âš This code violates the required equivalence of `compiletime`
-/// // and `runtime`.
-/// const_eval_select((), compiletime, runtime)
-/// }
-/// }
-///
-/// // Crate B
-/// const X: i32 = inconsistent();
-/// let x = inconsistent();
-/// if x != X { unsafe { unreachable_unchecked(); }}
-/// ```
-///
-/// This code causes Undefined Behavior when being run, since the
-/// `unreachable_unchecked` is actually being reached. The bug is in *crate A*,
-/// which violates the principle that a `const fn` must behave the same at
-/// compile-time and at run-time. The unsafe code in crate B is fine.
+#[cfg(bootstrap)]
#[unstable(
feature = "const_eval_select",
issue = "none",
called_at_rt.call_once(arg)
}
+#[cfg(bootstrap)]
#[unstable(
feature = "const_eval_select",
issue = "none",
#![feature(const_type_id)]
#![feature(const_type_name)]
#![feature(const_default_impls)]
+#![feature(const_unicode_case_lookup)]
#![feature(const_unsafecell_get_mut)]
#![feature(core_panic)]
#![feature(duration_consts_float)]
#[inline]
pub(crate) const unsafe fn new_unchecked(align: usize) -> Self {
// SAFETY: Precondition passed to the caller.
- unsafe { assert_unsafe_precondition!(align.is_power_of_two()) };
+ unsafe { assert_unsafe_precondition!((align: usize) => align.is_power_of_two()) };
// SAFETY: By precondition, this must be a power of two, and
// our variants encompass all possible powers of two.
}
}
}
- // SAFETY: `u32` is a plain old datatype so we can always... uh...
- // ...look, just pretend you forgot what you just read.
- // Stability concerns.
- let rt_f32_to_u32 = |rt| unsafe { mem::transmute::<f32, u32>(rt) };
+
+ #[inline(always)] // See https://github.com/rust-lang/compiler-builtins/issues/491
+ fn rt_f32_to_u32(x: f32) -> u32 {
+ // SAFETY: `u32` is a plain old datatype so we can always... uh...
+ // ...look, just pretend you forgot what you just read.
+ // Stability concerns.
+ unsafe { mem::transmute(x) }
+ }
// SAFETY: We use internal implementations that either always work or fail at compile time.
unsafe { intrinsics::const_eval_select((self,), ct_f32_to_u32, rt_f32_to_u32) }
}
}
}
}
- // SAFETY: `u32` is a plain old datatype so we can always... uh...
- // ...look, just pretend you forgot what you just read.
- // Stability concerns.
- let rt_u32_to_f32 = |rt| unsafe { mem::transmute::<u32, f32>(rt) };
+
+ #[inline(always)] // See https://github.com/rust-lang/compiler-builtins/issues/491
+ fn rt_u32_to_f32(x: u32) -> f32 {
+ // SAFETY: `u32` is a plain old datatype so we can always... uh...
+ // ...look, just pretend you forgot what you just read.
+ // Stability concerns.
+ unsafe { mem::transmute(x) }
+ }
// SAFETY: We use internal implementations that either always work or fail at compile time.
unsafe { intrinsics::const_eval_select((v,), ct_u32_to_f32, rt_u32_to_f32) }
}
}
}
}
- // SAFETY: `u64` is a plain old datatype so we can always... uh...
- // ...look, just pretend you forgot what you just read.
- // Stability concerns.
- let rt_f64_to_u64 = |rt| unsafe { mem::transmute::<f64, u64>(rt) };
+
+ #[inline(always)] // See https://github.com/rust-lang/compiler-builtins/issues/491
+ fn rt_f64_to_u64(rt: f64) -> u64 {
+ // SAFETY: `u64` is a plain old datatype so we can always... uh...
+ // ...look, just pretend you forgot what you just read.
+ // Stability concerns.
+ unsafe { mem::transmute::<f64, u64>(rt) }
+ }
// SAFETY: We use internal implementations that either always work or fail at compile time.
unsafe { intrinsics::const_eval_select((self,), ct_f64_to_u64, rt_f64_to_u64) }
}
}
}
}
- // SAFETY: `u64` is a plain old datatype so we can always... uh...
- // ...look, just pretend you forgot what you just read.
- // Stability concerns.
- let rt_u64_to_f64 = |rt| unsafe { mem::transmute::<u64, f64>(rt) };
+
+ #[inline(always)] // See https://github.com/rust-lang/compiler-builtins/issues/491
+ fn rt_u64_to_f64(rt: u64) -> f64 {
+ // SAFETY: `u64` is a plain old datatype so we can always... uh...
+ // ...look, just pretend you forgot what you just read.
+ // Stability concerns.
+ unsafe { mem::transmute::<u64, f64>(rt) }
+ }
// SAFETY: We use internal implementations that either always work or fail at compile time.
unsafe { intrinsics::const_eval_select((v,), ct_u64_to_f64, rt_u64_to_f64) }
}
pub const unsafe fn new_unchecked(n: $Int) -> Self {
// SAFETY: this is guaranteed to be safe by the caller.
unsafe {
- core::intrinsics::assert_unsafe_precondition!(n != 0);
+ core::intrinsics::assert_unsafe_precondition!((n: $Int) => n != 0);
Self(n)
}
}
where
T: Sized,
{
+ let this = self;
// SAFETY: The comparison has no side-effects, and the intrinsic
// does this check internally in the CTFE implementation.
- unsafe { assert_unsafe_precondition!(self >= origin) };
+ unsafe {
+ assert_unsafe_precondition!([T](this: *const T, origin: *const T) => this >= origin)
+ };
let pointee_size = mem::size_of::<T>();
assert!(0 < pointee_size && pointee_size <= isize::MAX as usize);
// SAFETY: the caller must guarantee that `x` and `y` are
// valid for writes and properly aligned.
unsafe {
- assert_unsafe_precondition!(
+ assert_unsafe_precondition!([T](x: *mut T, y: *mut T, count: usize) =>
is_aligned_and_not_null(x)
&& is_aligned_and_not_null(y)
&& is_nonoverlapping(x, y, count)
// and cannot overlap `src` since `dst` must point to a distinct
// allocated object.
unsafe {
- assert_unsafe_precondition!(is_aligned_and_not_null(dst));
+ assert_unsafe_precondition!([T](dst: *mut T) => is_aligned_and_not_null(dst));
mem::swap(&mut *dst, &mut src); // cannot overlap
}
src
pub unsafe fn read_volatile<T>(src: *const T) -> T {
// SAFETY: the caller must uphold the safety contract for `volatile_load`.
unsafe {
- assert_unsafe_precondition!(is_aligned_and_not_null(src));
+ assert_unsafe_precondition!([T](src: *const T) => is_aligned_and_not_null(src));
intrinsics::volatile_load(src)
}
}
pub unsafe fn write_volatile<T>(dst: *mut T, src: T) {
// SAFETY: the caller must uphold the safety contract for `volatile_store`.
unsafe {
- assert_unsafe_precondition!(is_aligned_and_not_null(dst));
+ assert_unsafe_precondition!([T](dst: *mut T) => is_aligned_and_not_null(dst));
intrinsics::volatile_store(dst, src);
}
}
}
// FIXME const-hack
+#[track_caller]
fn slice_start_index_len_fail_rt(index: usize, len: usize) -> ! {
panic!("range start index {index} out of range for slice of length {len}");
}
+#[track_caller]
const fn slice_start_index_len_fail_ct(_: usize, _: usize) -> ! {
panic!("slice start index is out of range for slice");
}
}
// FIXME const-hack
+#[track_caller]
fn slice_end_index_len_fail_rt(index: usize, len: usize) -> ! {
panic!("range end index {index} out of range for slice of length {len}");
}
+#[track_caller]
const fn slice_end_index_len_fail_ct(_: usize, _: usize) -> ! {
panic!("slice end index is out of range for slice");
}
}
// FIXME const-hack
+#[track_caller]
fn slice_index_order_fail_rt(index: usize, end: usize) -> ! {
panic!("slice index starts at {index} but ends at {end}");
}
+#[track_caller]
const fn slice_index_order_fail_ct(_: usize, _: usize) -> ! {
panic!("slice index start is larger than end");
}
#[inline]
unsafe fn get_unchecked(self, slice: *const [T]) -> *const T {
+ let this = self;
// SAFETY: the caller guarantees that `slice` is not dangling, so it
// cannot be longer than `isize::MAX`. They also guarantee that
// `self` is in bounds of `slice` so `self` cannot overflow an `isize`,
// so the call to `add` is safe.
unsafe {
- assert_unsafe_precondition!(self < slice.len());
+ assert_unsafe_precondition!([T](this: usize, slice: *const [T]) => this < slice.len());
slice.as_ptr().add(self)
}
}
#[inline]
unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut T {
+ let this = self;
// SAFETY: see comments for `get_unchecked` above.
unsafe {
- assert_unsafe_precondition!(self < slice.len());
+ assert_unsafe_precondition!([T](this: usize, slice: *mut [T]) => this < slice.len());
slice.as_mut_ptr().add(self)
}
}
#[inline]
unsafe fn get_unchecked(self, slice: *const [T]) -> *const [T] {
+ let this = ops::Range { start: self.start, end: self.end };
// SAFETY: the caller guarantees that `slice` is not dangling, so it
// cannot be longer than `isize::MAX`. They also guarantee that
// `self` is in bounds of `slice` so `self` cannot overflow an `isize`,
// so the call to `add` is safe.
unsafe {
- assert_unsafe_precondition!(self.end >= self.start && self.end <= slice.len());
+ assert_unsafe_precondition!([T](this: ops::Range<usize>, slice: *const [T]) =>
+ this.end >= this.start && this.end <= slice.len());
ptr::slice_from_raw_parts(slice.as_ptr().add(self.start), self.end - self.start)
}
}
#[inline]
unsafe fn get_unchecked_mut(self, slice: *mut [T]) -> *mut [T] {
+ let this = ops::Range { start: self.start, end: self.end };
// SAFETY: see comments for `get_unchecked` above.
unsafe {
- assert_unsafe_precondition!(self.end >= self.start && self.end <= slice.len());
+ assert_unsafe_precondition!([T](this: ops::Range<usize>, slice: *mut [T]) =>
+ this.end >= this.start && this.end <= slice.len());
ptr::slice_from_raw_parts_mut(slice.as_mut_ptr().add(self.start), self.end - self.start)
}
}
#[unstable(feature = "slice_swap_unchecked", issue = "88539")]
#[rustc_const_unstable(feature = "const_swap", issue = "83163")]
pub const unsafe fn swap_unchecked(&mut self, a: usize, b: usize) {
- let ptr = self.as_mut_ptr();
+ let this = self;
+ let ptr = this.as_mut_ptr();
// SAFETY: caller has to guarantee that `a < self.len()` and `b < self.len()`
unsafe {
- assert_unsafe_precondition!(a < self.len() && b < self.len());
+ assert_unsafe_precondition!([T](a: usize, b: usize, this: &mut [T]) => a < this.len() && b < this.len());
ptr::swap(ptr.add(a), ptr.add(b));
}
}
#[inline]
#[must_use]
pub unsafe fn as_chunks_unchecked<const N: usize>(&self) -> &[[T; N]] {
+ let this = self;
// SAFETY: Caller must guarantee that `N` is nonzero and exactly divides the slice length
let new_len = unsafe {
- assert_unsafe_precondition!(N != 0 && self.len() % N == 0);
+ assert_unsafe_precondition!([T](this: &[T], N: usize) => N != 0 && this.len() % N == 0);
exact_div(self.len(), N)
};
// SAFETY: We cast a slice of `new_len * N` elements into
#[inline]
#[must_use]
pub unsafe fn as_chunks_unchecked_mut<const N: usize>(&mut self) -> &mut [[T; N]] {
+ let this = &*self;
// SAFETY: Caller must guarantee that `N` is nonzero and exactly divides the slice length
let new_len = unsafe {
- assert_unsafe_precondition!(N != 0 && self.len() % N == 0);
- exact_div(self.len(), N)
+ assert_unsafe_precondition!([T](this: &[T], N: usize) => N != 0 && this.len() % N == 0);
+ exact_div(this.len(), N)
};
// SAFETY: We cast a slice of `new_len * N` elements into
// a slice of `new_len` many `N` elements chunks.
// `[ptr; mid]` and `[mid; len]` are not overlapping, so returning a mutable reference
// is fine.
unsafe {
- assert_unsafe_precondition!(mid <= len);
+ assert_unsafe_precondition!((mid: usize, len: usize) => mid <= len);
(from_raw_parts_mut(ptr, mid), from_raw_parts_mut(ptr.add(mid), len - mid))
}
}
pub const unsafe fn from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T] {
// SAFETY: the caller must uphold the safety contract for `from_raw_parts`.
unsafe {
- assert_unsafe_precondition!(
+ assert_unsafe_precondition!([T](data: *const T, len: usize) =>
is_aligned_and_not_null(data)
&& crate::mem::size_of::<T>().saturating_mul(len) <= isize::MAX as usize
);
pub const unsafe fn from_raw_parts_mut<'a, T>(data: *mut T, len: usize) -> &'a mut [T] {
// SAFETY: the caller must uphold the safety contract for `from_raw_parts_mut`.
unsafe {
- assert_unsafe_precondition!(
+ assert_unsafe_precondition!([T](data: *mut T, len: usize) =>
is_aligned_and_not_null(data)
&& crate::mem::size_of::<T>().saturating_mul(len) <= isize::MAX as usize
);
}
}
+#[track_caller]
const fn slice_error_fail_ct(_: &str, _: usize, _: usize) -> ! {
panic!("failed to slice string");
}
+#[track_caller]
fn slice_error_fail_rt(s: &str, begin: usize, end: usize) -> ! {
const MAX_DISPLAY_LENGTH: usize = 256;
let trunc_len = s.floor_char_boundary(MAX_DISPLAY_LENGTH);
///! This file is generated by src/tools/unicode-table-generator; do not edit manually!
+#[rustc_const_unstable(feature = "const_unicode_case_lookup", issue = "101400")]
#[inline(always)]
-fn bitset_search<
+const fn bitset_search<
const N: usize,
const CHUNK_SIZE: usize,
const N1: usize,
let bucket_idx = (needle / 64) as usize;
let chunk_map_idx = bucket_idx / CHUNK_SIZE;
let chunk_piece = bucket_idx % CHUNK_SIZE;
- let chunk_idx = if let Some(&v) = chunk_idx_map.get(chunk_map_idx) {
- v
+ // FIXME: const-hack: Revert to `slice::get` after `const_slice_index`
+ // feature stabilizes.
+ let chunk_idx = if chunk_map_idx < chunk_idx_map.len() {
+ chunk_idx_map[chunk_map_idx]
} else {
return false;
};
let idx = bitset_chunk_idx[chunk_idx as usize][chunk_piece] as usize;
- let word = if let Some(word) = bitset_canonical.get(idx) {
- *word
+ // FIXME: const-hack: Revert to `slice::get` after `const_slice_index`
+ // feature stabilizes.
+ let word = if idx < bitset_canonical.len() {
+ bitset_canonical[idx]
} else {
let (real_idx, mapping) = bitset_canonicalized[idx - bitset_canonical.len()];
let mut word = bitset_canonical[real_idx as usize];
#[rustfmt::skip]
pub mod lowercase {
- static BITSET_CHUNKS_MAP: [u8; 123] = [
+ const BITSET_CHUNKS_MAP: &'static [u8; 123] = &[
14, 17, 0, 0, 9, 0, 0, 12, 13, 10, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 4, 1, 0, 15, 0, 8, 0, 0, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0,
3, 0, 0, 7,
];
- static BITSET_INDEX_CHUNKS: [[u8; 16]; 19] = [
+ const BITSET_INDEX_CHUNKS: &'static [[u8; 16]; 19] = &[
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 59, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 14, 55, 0],
[16, 49, 2, 20, 66, 9, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[63, 39, 54, 12, 73, 61, 18, 1, 6, 62, 71, 19, 68, 69, 3, 44],
];
- static BITSET_CANONICAL: [u64; 55] = [
+ const BITSET_CANONICAL: &'static [u64; 55] = &[
0b0000000000000000000000000000000000000000000000000000000000000000,
0b1111111111111111110000000000000000000000000011111111111111111111,
0b1010101010101010101010101010101010101010101010101010100000000010,
0b1110011111111111111111111111111111111111111111110000000000000000,
0b1110101111000000000000000000000000001111111111111111111111111100,
];
- static BITSET_MAPPING: [(u8, u8); 20] = [
+ const BITSET_MAPPING: &'static [(u8, u8); 20] = &[
(0, 64), (1, 188), (1, 183), (1, 176), (1, 109), (1, 124), (1, 126), (1, 66), (1, 70),
(1, 77), (2, 146), (2, 144), (2, 83), (3, 12), (3, 6), (4, 156), (4, 78), (5, 187),
(6, 132), (7, 93),
];
- pub fn lookup(c: char) -> bool {
+ #[rustc_const_unstable(feature = "const_unicode_case_lookup", issue = "101400")]
+ pub const fn lookup(c: char) -> bool {
super::bitset_search(
c as u32,
&BITSET_CHUNKS_MAP,
#[rustfmt::skip]
pub mod uppercase {
- static BITSET_CHUNKS_MAP: [u8; 125] = [
+ const BITSET_CHUNKS_MAP: &'static [u8; 125] = &[
12, 15, 6, 6, 0, 6, 6, 2, 4, 11, 6, 16, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 8, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 6, 6, 5, 6, 14, 6, 10, 6, 6, 1, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
6, 7, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 13, 6, 6,
6, 6, 9, 6, 3,
];
- static BITSET_INDEX_CHUNKS: [[u8; 16]; 17] = [
+ const BITSET_INDEX_CHUNKS: &'static [[u8; 16]; 17] = &[
[43, 43, 5, 34, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 5, 1],
[43, 43, 5, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43],
[43, 43, 39, 43, 43, 43, 43, 43, 17, 17, 62, 17, 42, 29, 24, 23],
[57, 19, 2, 18, 10, 47, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43],
[57, 37, 17, 27, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43],
];
- static BITSET_CANONICAL: [u64; 43] = [
+ const BITSET_CANONICAL: &'static [u64; 43] = &[
0b0000011111111111111111111111111000000000000000000000000000000000,
0b0000000000111111111111111111111111111111111111111111111111111111,
0b0101010101010101010101010101010101010101010101010101010000000001,
0b1111011111111111000000000000000000000000000000000000000000000000,
0b1111111100000000111111110000000000111111000000001111111100000000,
];
- static BITSET_MAPPING: [(u8, u8); 25] = [
+ const BITSET_MAPPING: &'static [(u8, u8); 25] = &[
(0, 187), (0, 177), (0, 171), (0, 167), (0, 164), (0, 32), (0, 47), (0, 51), (0, 121),
(0, 117), (0, 109), (1, 150), (1, 148), (1, 142), (1, 134), (1, 131), (1, 64), (2, 164),
(2, 146), (2, 20), (3, 146), (3, 140), (3, 134), (4, 178), (4, 171),
];
- pub fn lookup(c: char) -> bool {
+ #[rustc_const_unstable(feature = "const_unicode_case_lookup", issue = "101400")]
+ pub const fn lookup(c: char) -> bool {
super::bitset_search(
c as u32,
&BITSET_CHUNKS_MAP,
#![cfg_attr(feature = "as_crate", no_std)] // We are std!
-#![cfg_attr(
- feature = "as_crate",
- feature(platform_intrinsics),
- feature(portable_simd)
-)]
+#![cfg_attr(feature = "as_crate", feature(platform_intrinsics), feature(portable_simd))]
#[cfg(not(feature = "as_crate"))]
use core::simd;
#[cfg(feature = "as_crate")]
use super::*;
+use std::cell::Cell;
use std::marker::PhantomData;
// FIXME(eddyb) generate the definition of `HandleStore` in `server.rs`.
) -> Buffer;
}
+thread_local! {
+ /// While running a proc-macro with the same-thread executor, this flag will
+ /// be set, forcing nested proc-macro invocations (e.g. due to
+ /// `TokenStream::expand_expr`) to be run using a cross-thread executor.
+ ///
+ /// This is required as the thread-local state in the proc_macro client does
+ /// not handle being re-entered, and will invalidate all `Symbol`s when
+ /// entering a nested macro.
+ static ALREADY_RUNNING_SAME_THREAD: Cell<bool> = Cell::new(false);
+}
+
+/// Keep `ALREADY_RUNNING_SAME_THREAD` (see also its documentation)
+/// set to `true`, preventing same-thread reentrance.
+struct RunningSameThreadGuard(());
+
+impl RunningSameThreadGuard {
+ fn new() -> Self {
+ let already_running = ALREADY_RUNNING_SAME_THREAD.replace(true);
+ assert!(
+ !already_running,
+ "same-thread nesting (\"reentrance\") of proc macro executions is not supported"
+ );
+ RunningSameThreadGuard(())
+ }
+}
+
+impl Drop for RunningSameThreadGuard {
+ fn drop(&mut self) {
+ ALREADY_RUNNING_SAME_THREAD.set(false);
+ }
+}
+
pub struct MaybeCrossThread<P> {
cross_thread: bool,
marker: PhantomData<P>,
run_client: extern "C" fn(BridgeConfig<'_>) -> Buffer,
force_show_panics: bool,
) -> Buffer {
- if self.cross_thread {
+ if self.cross_thread || ALREADY_RUNNING_SAME_THREAD.get() {
<CrossThread<P>>::new().run_bridge_and_client(
dispatcher,
input,
run_client: extern "C" fn(BridgeConfig<'_>) -> Buffer,
force_show_panics: bool,
) -> Buffer {
+ let _guard = RunningSameThreadGuard::new();
+
let mut dispatch = |buf| dispatcher.dispatch(buf);
run_client(BridgeConfig {
/// The default memory allocator provided by the operating system.
///
/// This is based on `malloc` on Unix platforms and `HeapAlloc` on Windows,
-/// plus related functions.
+/// plus related functions. However, it is not valid to mix use of the backing
+/// system allocator with `System`, as this implementation may include extra
+/// work, such as to serve alignment requests greater than the alignment
+/// provided directly by the backing system allocator.
///
/// This type implements the `GlobalAlloc` trait and Rust programs by default
/// work as if they had this definition:
use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};
use crate::time::SystemTime;
-/// A reference to an open file on the filesystem.
+/// An object providing access to an open file on the filesystem.
///
/// An instance of a `File` can be read and/or written depending on what options
/// it was opened with. Files also implement [`Seek`] to alter the logical cursor
use crate::cell::{Cell, RefCell};
use crate::fmt;
use crate::io::{self, BufReader, IoSlice, IoSliceMut, LineWriter, Lines};
-use crate::pin::Pin;
use crate::sync::atomic::{AtomicBool, Ordering};
use crate::sync::{Arc, Mutex, MutexGuard, OnceLock};
use crate::sys::stdio;
// FIXME: this should be LineWriter or BufWriter depending on the state of
// stdout (tty or not). Note that if this is not line buffered it
// should also flush-on-panic or some form of flush-on-abort.
- inner: Pin<&'static ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>>,
+ inner: &'static ReentrantMutex<RefCell<LineWriter<StdoutRaw>>>,
}
/// A locked reference to the [`Stdout`] handle.
#[stable(feature = "rust1", since = "1.0.0")]
pub fn stdout() -> Stdout {
Stdout {
- inner: Pin::static_ref(&STDOUT).get_or_init_pin(
- || unsafe { ReentrantMutex::new(RefCell::new(LineWriter::new(stdout_raw()))) },
- |mutex| unsafe { mutex.init() },
- ),
+ inner: STDOUT
+ .get_or_init(|| ReentrantMutex::new(RefCell::new(LineWriter::new(stdout_raw())))),
}
}
+// Flush the data and disable buffering during shutdown
+// by replacing the line writer by one with zero
+// buffering capacity.
pub fn cleanup() {
- if let Some(instance) = STDOUT.get() {
- // Flush the data and disable buffering during shutdown
- // by replacing the line writer by one with zero
- // buffering capacity.
+ let mut initialized = false;
+ let stdout = STDOUT.get_or_init(|| {
+ initialized = true;
+ ReentrantMutex::new(RefCell::new(LineWriter::with_capacity(0, stdout_raw())))
+ });
+
+ if !initialized {
+ // The buffer was previously initialized, overwrite it here.
// We use try_lock() instead of lock(), because someone
// might have leaked a StdoutLock, which would
// otherwise cause a deadlock here.
- if let Some(lock) = Pin::static_ref(instance).try_lock() {
+ if let Some(lock) = stdout.try_lock() {
*lock.borrow_mut() = LineWriter::with_capacity(0, stdout_raw());
}
}
/// standard library or via raw Windows API calls, will fail.
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Stderr {
- inner: Pin<&'static ReentrantMutex<RefCell<StderrRaw>>>,
+ inner: &'static ReentrantMutex<RefCell<StderrRaw>>,
}
/// A locked reference to the [`Stderr`] handle.
#[stable(feature = "rust1", since = "1.0.0")]
pub fn stderr() -> Stderr {
// Note that unlike `stdout()` we don't use `at_exit` here to register a
- // destructor. Stderr is not buffered , so there's no need to run a
+ // destructor. Stderr is not buffered, so there's no need to run a
// destructor for flushing the buffer
- static INSTANCE: OnceLock<ReentrantMutex<RefCell<StderrRaw>>> = OnceLock::new();
+ static INSTANCE: ReentrantMutex<RefCell<StderrRaw>> =
+ ReentrantMutex::new(RefCell::new(stderr_raw()));
- Stderr {
- inner: Pin::static_ref(&INSTANCE).get_or_init_pin(
- || unsafe { ReentrantMutex::new(RefCell::new(stderr_raw())) },
- |mutex| unsafe { mutex.init() },
- ),
- }
+ Stderr { inner: &INSTANCE }
}
impl Stderr {
use crate::marker::PhantomData;
use crate::mem::MaybeUninit;
use crate::panic::{RefUnwindSafe, UnwindSafe};
-use crate::pin::Pin;
use crate::sync::Once;
/// A synchronization primitive which can be written to only once.
Ok(unsafe { self.get_unchecked() })
}
- /// Internal-only API that gets the contents of the cell, initializing it
- /// in two steps with `f` and `g` if the cell was empty.
- ///
- /// `f` is called to construct the value, which is then moved into the cell
- /// and given as a (pinned) mutable reference to `g` to finish
- /// initialization.
- ///
- /// This allows `g` to inspect an manipulate the value after it has been
- /// moved into its final place in the cell, but before the cell is
- /// considered initialized.
- ///
- /// # Panics
- ///
- /// If `f` or `g` panics, the panic is propagated to the caller, and the
- /// cell remains uninitialized.
- ///
- /// With the current implementation, if `g` panics, the value from `f` will
- /// not be dropped. This should probably be fixed if this is ever used for
- /// a type where this matters.
- ///
- /// It is an error to reentrantly initialize the cell from `f`. The exact
- /// outcome is unspecified. Current implementation deadlocks, but this may
- /// be changed to a panic in the future.
- pub(crate) fn get_or_init_pin<F, G>(self: Pin<&Self>, f: F, g: G) -> Pin<&T>
- where
- F: FnOnce() -> T,
- G: FnOnce(Pin<&mut T>),
- {
- if let Some(value) = self.get_ref().get() {
- // SAFETY: The inner value was already initialized, and will not be
- // moved anymore.
- return unsafe { Pin::new_unchecked(value) };
- }
-
- let slot = &self.value;
-
- // Ignore poisoning from other threads
- // If another thread panics, then we'll be able to run our closure
- self.once.call_once_force(|_| {
- let value = f();
- // SAFETY: We use the Once (self.once) to guarantee unique access
- // to the UnsafeCell (slot).
- let value: &mut T = unsafe { (&mut *slot.get()).write(value) };
- // SAFETY: The value has been written to its final place in
- // self.value. We do not to move it anymore, which we promise here
- // with a Pin<&mut T>.
- g(unsafe { Pin::new_unchecked(value) });
- });
-
- // SAFETY: The inner value has been initialized, and will not be moved
- // anymore.
- unsafe { Pin::new_unchecked(self.get_ref().get_unchecked()) }
- }
-
/// Consumes the `OnceLock`, returning the wrapped value. Returns
/// `None` if the cell was empty.
///
Mutex { inner: Spinlock::new(MutexInner::new()) }
}
- #[inline]
- pub unsafe fn init(&mut self) {}
-
#[inline]
pub unsafe fn lock(&self) {
loop {
Mutex { mtx: SpinIdOnceCell::new() }
}
- pub unsafe fn init(&mut self) {
- // Initialize `self.mtx` eagerly
- let id = new_mtx().unwrap_or_else(|e| fail(e, &"acre_mtx"));
- unsafe { self.mtx.set_unchecked((id, ())) };
- }
-
/// Get the inner mutex's ID, which is lazily created.
fn raw(&self) -> abi::ID {
match self.mtx.get_or_try_init(|| new_mtx().map(|id| (id, ()))) {
Mutex { inner: SpinMutex::new(WaitVariable::new(false)) }
}
- #[inline]
- pub unsafe fn init(&mut self) {}
-
#[inline]
pub unsafe fn lock(&self) {
let mut guard = self.inner.lock();
Mutex { futex: AtomicU32::new(UNLOCKED) }
}
- #[inline]
- pub unsafe fn init(&mut self) {}
-
#[inline]
pub unsafe fn try_lock(&self) -> bool {
let thread_self = zx_thread_self();
Self { futex: AtomicU32::new(0) }
}
- #[inline]
- pub unsafe fn init(&mut self) {}
-
#[inline]
pub unsafe fn try_lock(&self) -> bool {
self.futex.compare_exchange(0, 1, Acquire, Relaxed).is_ok()
Mutex { inner: UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER) }
}
#[inline]
- pub unsafe fn init(&mut self) {
+ unsafe fn init(&mut self) {
// Issue #33770
//
// A pthread mutex initialized with PTHREAD_MUTEX_INITIALIZER will have
Some(b"") => Cgroup::V2,
Some(controllers)
if from_utf8(controllers)
- .is_ok_and(|c| c.split(",").any(|c| c == "cpu")) =>
+ .is_ok_and(|c| c.split(',').any(|c| c == "cpu")) =>
{
Cgroup::V1
}
Mutex { locked: Cell::new(false) }
}
- #[inline]
- pub unsafe fn init(&mut self) {}
-
#[inline]
pub unsafe fn lock(&self) {
assert_eq!(self.locked.replace(true), false, "cannot recursively acquire mutex");
pub const fn new() -> Mutex {
Mutex { srwlock: UnsafeCell::new(c::SRWLOCK_INIT) }
}
- #[inline]
- pub unsafe fn init(&mut self) {}
#[inline]
pub unsafe fn lock(&self) {
#[cfg(all(test, not(target_os = "emscripten")))]
mod tests;
+use super::mutex as sys;
use crate::cell::UnsafeCell;
-use crate::marker::PhantomPinned;
use crate::ops::Deref;
use crate::panic::{RefUnwindSafe, UnwindSafe};
-use crate::pin::Pin;
use crate::sync::atomic::{AtomicUsize, Ordering::Relaxed};
-use crate::sys::locks as sys;
/// A re-entrant mutual exclusion
///
/// synchronization is left to the mutex, making relaxed memory ordering for
/// the `owner` field fine in all cases.
pub struct ReentrantMutex<T> {
- mutex: sys::Mutex,
+ mutex: sys::MovableMutex,
owner: AtomicUsize,
lock_count: UnsafeCell<u32>,
data: T,
- _pinned: PhantomPinned,
}
unsafe impl<T: Send> Send for ReentrantMutex<T> {}
/// guarded data.
#[must_use = "if unused the ReentrantMutex will immediately unlock"]
pub struct ReentrantMutexGuard<'a, T: 'a> {
- lock: Pin<&'a ReentrantMutex<T>>,
+ lock: &'a ReentrantMutex<T>,
}
impl<T> !Send for ReentrantMutexGuard<'_, T> {}
impl<T> ReentrantMutex<T> {
/// Creates a new reentrant mutex in an unlocked state.
- ///
- /// # Unsafety
- ///
- /// This function is unsafe because it is required that `init` is called
- /// once this mutex is in its final resting place, and only then are the
- /// lock/unlock methods safe.
- pub const unsafe fn new(t: T) -> ReentrantMutex<T> {
+ pub const fn new(t: T) -> ReentrantMutex<T> {
ReentrantMutex {
- mutex: sys::Mutex::new(),
+ mutex: sys::MovableMutex::new(),
owner: AtomicUsize::new(0),
lock_count: UnsafeCell::new(0),
data: t,
- _pinned: PhantomPinned,
}
}
- /// Initializes this mutex so it's ready for use.
- ///
- /// # Unsafety
- ///
- /// Unsafe to call more than once, and must be called after this will no
- /// longer move in memory.
- pub unsafe fn init(self: Pin<&mut Self>) {
- self.get_unchecked_mut().mutex.init()
- }
-
/// Acquires a mutex, blocking the current thread until it is able to do so.
///
/// This function will block the caller until it is available to acquire the mutex.
/// If another user of this mutex panicked while holding the mutex, then
/// this call will return failure if the mutex would otherwise be
/// acquired.
- pub fn lock(self: Pin<&Self>) -> ReentrantMutexGuard<'_, T> {
+ pub fn lock(&self) -> ReentrantMutexGuard<'_, T> {
let this_thread = current_thread_unique_ptr();
- // Safety: We only touch lock_count when we own the lock,
- // and since self is pinned we can safely call the lock() on the mutex.
+ // Safety: We only touch lock_count when we own the lock.
unsafe {
if self.owner.load(Relaxed) == this_thread {
self.increment_lock_count();
} else {
- self.mutex.lock();
+ self.mutex.raw_lock();
self.owner.store(this_thread, Relaxed);
debug_assert_eq!(*self.lock_count.get(), 0);
*self.lock_count.get() = 1;
/// If another user of this mutex panicked while holding the mutex, then
/// this call will return failure if the mutex would otherwise be
/// acquired.
- pub fn try_lock(self: Pin<&Self>) -> Option<ReentrantMutexGuard<'_, T>> {
+ pub fn try_lock(&self) -> Option<ReentrantMutexGuard<'_, T>> {
let this_thread = current_thread_unique_ptr();
- // Safety: We only touch lock_count when we own the lock,
- // and since self is pinned we can safely call the try_lock on the mutex.
+ // Safety: We only touch lock_count when we own the lock.
unsafe {
if self.owner.load(Relaxed) == this_thread {
self.increment_lock_count();
impl<T> Drop for ReentrantMutexGuard<'_, T> {
#[inline]
fn drop(&mut self) {
- // Safety: We own the lock, and the lock is pinned.
+ // Safety: We own the lock.
unsafe {
*self.lock.lock_count.get() -= 1;
if *self.lock.lock_count.get() == 0 {
self.lock.owner.store(0, Relaxed);
- self.lock.mutex.unlock();
+ self.lock.mutex.raw_unlock();
}
}
}
-use crate::boxed::Box;
use crate::cell::RefCell;
-use crate::pin::Pin;
use crate::sync::Arc;
use crate::sys_common::remutex::{ReentrantMutex, ReentrantMutexGuard};
use crate::thread;
#[test]
fn smoke() {
- let m = unsafe {
- let mut m = Box::pin(ReentrantMutex::new(()));
- m.as_mut().init();
- m
- };
- let m = m.as_ref();
+ let m = ReentrantMutex::new(());
{
let a = m.lock();
{
#[test]
fn is_mutex() {
- let m = unsafe {
- // FIXME: Simplify this if Arc gets an Arc::get_pin_mut.
- let mut m = Arc::new(ReentrantMutex::new(RefCell::new(0)));
- Pin::new_unchecked(Arc::get_mut_unchecked(&mut m)).init();
- Pin::new_unchecked(m)
- };
+ let m = Arc::new(ReentrantMutex::new(RefCell::new(0)));
let m2 = m.clone();
- let lock = m.as_ref().lock();
+ let lock = m.lock();
let child = thread::spawn(move || {
- let lock = m2.as_ref().lock();
+ let lock = m2.lock();
assert_eq!(*lock.borrow(), 4950);
});
for i in 0..100 {
- let lock = m.as_ref().lock();
+ let lock = m.lock();
*lock.borrow_mut() += i;
}
drop(lock);
#[test]
fn trylock_works() {
- let m = unsafe {
- // FIXME: Simplify this if Arc gets an Arc::get_pin_mut.
- let mut m = Arc::new(ReentrantMutex::new(()));
- Pin::new_unchecked(Arc::get_mut_unchecked(&mut m)).init();
- Pin::new_unchecked(m)
- };
+ let m = Arc::new(ReentrantMutex::new(()));
let m2 = m.clone();
- let _lock = m.as_ref().try_lock();
- let _lock2 = m.as_ref().try_lock();
+ let _lock = m.try_lock();
+ let _lock2 = m.try_lock();
thread::spawn(move || {
- let lock = m2.as_ref().try_lock();
+ let lock = m2.try_lock();
assert!(lock.is_none());
})
.join()
.unwrap();
- let _lock3 = m.as_ref().try_lock();
+ let _lock3 = m.try_lock();
}
pub struct Answer<'a>(pub ReentrantMutexGuard<'a, RefCell<u32>>);
python3 ../x.py test --stage 0 src/tools/compiletest && \
python3 ../x.py test --stage 2 src/tools/tidy && \
python3 ../x.py test --stage 0 core alloc std test proc_macro && \
- python3 ../x.py doc --stage 0 library/test && \
+ # Build both public and internal documentation.
+ RUSTDOCFLAGS="--document-private-items" python3 ../x.py doc --stage 0 library/test && \
/scripts/validate-toolstate.sh && \
/scripts/validate-error-codes.sh && \
reuse lint && \
pub(crate) fn clean_variant_def<'tcx>(variant: &ty::VariantDef, cx: &mut DocContext<'tcx>) -> Item {
let kind = match variant.ctor_kind {
- CtorKind::Const => Variant::CLike,
+ CtorKind::Const => Variant::CLike(match variant.discr {
+ ty::VariantDiscr::Explicit(def_id) => Some(Discriminant { expr: None, value: def_id }),
+ ty::VariantDiscr::Relative(_) => None,
+ }),
CtorKind::Fn => Variant::Tuple(
variant.fields.iter().map(|field| clean_middle_field(field, cx)).collect(),
),
fn clean_variant_data<'tcx>(
variant: &hir::VariantData<'tcx>,
+ disr_expr: &Option<hir::AnonConst>,
cx: &mut DocContext<'tcx>,
) -> Variant {
match variant {
hir::VariantData::Tuple(..) => {
Variant::Tuple(variant.fields().iter().map(|x| clean_field(x, cx)).collect())
}
- hir::VariantData::Unit(..) => Variant::CLike,
+ hir::VariantData::Unit(..) => Variant::CLike(disr_expr.map(|disr| Discriminant {
+ expr: Some(disr.body),
+ value: cx.tcx.hir().local_def_id(disr.hir_id).to_def_id(),
+ })),
}
}
}
fn clean_variant<'tcx>(variant: &hir::Variant<'tcx>, cx: &mut DocContext<'tcx>) -> Item {
- let kind = VariantItem(clean_variant_data(&variant.data, cx));
+ let kind = VariantItem(clean_variant_data(&variant.data, &variant.disr_expr, cx));
let what_rustc_thinks =
Item::from_hir_id_and_parts(variant.id, Some(variant.ident.name), kind, cx);
// don't show `pub` for variants, which are always public
#[derive(Clone, Debug)]
pub(crate) enum Variant {
- CLike,
+ CLike(Option<Discriminant>),
Tuple(Vec<Item>),
Struct(VariantStruct),
}
pub(crate) fn has_stripped_entries(&self) -> Option<bool> {
match *self {
Self::Struct(ref struct_) => Some(struct_.has_stripped_entries()),
- Self::CLike | Self::Tuple(_) => None,
+ Self::CLike(..) | Self::Tuple(_) => None,
}
}
}
+#[derive(Clone, Debug)]
+pub(crate) struct Discriminant {
+ // In the case of cross crate re-exports, we don't have the nessesary information
+ // to reconstruct the expression of the discriminant, only the value.
+ pub(super) expr: Option<BodyId>,
+ pub(super) value: DefId,
+}
+
+impl Discriminant {
+ /// Will be `None` in the case of cross-crate reexports, and may be
+ /// simplified
+ pub(crate) fn expr(&self, tcx: TyCtxt<'_>) -> Option<String> {
+ self.expr.map(|body| print_const_expr(tcx, body))
+ }
+ /// Will always be a machine readable number, without underscores or suffixes.
+ pub(crate) fn value(&self, tcx: TyCtxt<'_>) -> String {
+ print_evaluated_const(tcx, self.value, false).unwrap()
+ }
+}
+
/// Small wrapper around [`rustc_span::Span`] that adds helper methods
/// and enforces calling [`rustc_span::Span::source_callsite()`].
#[derive(Copy, Clone, Debug)]
match *self {
ConstantKind::TyConst { .. } | ConstantKind::Anonymous { .. } => None,
ConstantKind::Extern { def_id } | ConstantKind::Local { def_id, .. } => {
- print_evaluated_const(tcx, def_id)
+ print_evaluated_const(tcx, def_id, true)
}
}
}
}
}
-pub(crate) fn print_evaluated_const(tcx: TyCtxt<'_>, def_id: DefId) -> Option<String> {
+pub(crate) fn print_evaluated_const(
+ tcx: TyCtxt<'_>,
+ def_id: DefId,
+ underscores_and_type: bool,
+) -> Option<String> {
tcx.const_eval_poly(def_id).ok().and_then(|val| {
let ty = tcx.type_of(def_id);
match (val, ty.kind()) {
(ConstValue::Scalar(_), &ty::Adt(_, _)) => None,
(ConstValue::Scalar(_), _) => {
let const_ = mir::ConstantKind::from_value(val, ty);
- Some(print_const_with_custom_print_scalar(tcx, const_))
+ Some(print_const_with_custom_print_scalar(tcx, const_, underscores_and_type))
}
_ => None,
}
.collect()
}
-fn print_const_with_custom_print_scalar(tcx: TyCtxt<'_>, ct: mir::ConstantKind<'_>) -> String {
+fn print_const_with_custom_print_scalar(
+ tcx: TyCtxt<'_>,
+ ct: mir::ConstantKind<'_>,
+ underscores_and_type: bool,
+) -> String {
// Use a slightly different format for integer types which always shows the actual value.
// For all other types, fallback to the original `pretty_print_const`.
match (ct, ct.ty().kind()) {
(mir::ConstantKind::Val(ConstValue::Scalar(int), _), ty::Uint(ui)) => {
- format!("{}{}", format_integer_with_underscore_sep(&int.to_string()), ui.name_str())
+ if underscores_and_type {
+ format!("{}{}", format_integer_with_underscore_sep(&int.to_string()), ui.name_str())
+ } else {
+ int.to_string()
+ }
}
(mir::ConstantKind::Val(ConstValue::Scalar(int), _), ty::Int(i)) => {
let ty = tcx.lift(ct.ty()).unwrap();
let size = tcx.layout_of(ty::ParamEnv::empty().and(ty)).unwrap().size;
let data = int.assert_bits(size);
let sign_extended_data = size.sign_extend(data) as i128;
- format!(
- "{}{}",
- format_integer_with_underscore_sep(&sign_extended_data.to_string()),
- i.name_str()
- )
+ if underscores_and_type {
+ format!(
+ "{}{}",
+ format_integer_with_underscore_sep(&sign_extended_data.to_string()),
+ i.name_str()
+ )
+ } else {
+ sign_extended_data.to_string()
+ }
}
_ => ct.to_string(),
}
let fields = fields.into_iter().filter_map(|x| self.fold_item(x)).collect();
VariantItem(Variant::Tuple(fields))
}
- Variant::CLike => VariantItem(Variant::CLike),
+ Variant::CLike(disr) => VariantItem(Variant::CLike(disr)),
},
ExternCrateItem { src: _ }
| ImportItem(_)
let Some(v) = shared.cache.impls.get(&def_id) else { return (Vec::new(), Vec::new(), Vec::new()) };
// Since there is no "direct implementation" on the reference primitive type, we filter out
// every implementation which isn't a trait implementation.
- let traits: Vec<_> = v.iter().filter(|i| i.inner_impl().trait_.is_some()).collect();
+ let traits = v.iter().filter(|i| i.inner_impl().trait_.is_some());
let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
- traits.into_iter().partition(|t| t.inner_impl().kind.is_auto());
+ traits.partition(|t| t.inner_impl().kind.is_auto());
let (blanket_impl, concrete): (Vec<&Impl>, _) =
concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket());
let name = v.name.unwrap();
match *v.kind {
clean::VariantItem(ref var) => match var {
- clean::Variant::CLike => write!(w, "{}", name),
+ // FIXME(#101337): Show discriminant
+ clean::Variant::CLike(..) => write!(w, "{}", name),
clean::Variant::Tuple(ref s) => {
write!(w, "{}(", name);
print_tuple_struct_fields(w, cx, s);
fn visit_expr(&mut self, expr: &'tcx rustc_hir::Expr<'tcx>) {
if let ExprKind::MethodCall(segment, ..) = expr.kind {
- if let Some(hir_id) = segment.hir_id {
- let hir = self.tcx.hir();
- let body_id = hir.enclosing_body_owner(hir_id);
- // FIXME: this is showing error messages for parts of the code that are not
- // compiled (because of cfg)!
- //
- // See discussion in https://github.com/rust-lang/rust/issues/69426#issuecomment-1019412352
- let typeck_results = self.tcx.typeck_body(
- hir.maybe_body_owned_by(body_id).expect("a body which isn't a body"),
+ let hir = self.tcx.hir();
+ let body_id = hir.enclosing_body_owner(segment.hir_id);
+ // FIXME: this is showing error messages for parts of the code that are not
+ // compiled (because of cfg)!
+ //
+ // See discussion in https://github.com/rust-lang/rust/issues/69426#issuecomment-1019412352
+ let typeck_results = self
+ .tcx
+ .typeck_body(hir.maybe_body_owned_by(body_id).expect("a body which isn't a body"));
+ if let Some(def_id) = typeck_results.type_dependent_def_id(expr.hir_id) {
+ self.matches.insert(
+ segment.ident.span,
+ match hir.span_if_local(def_id) {
+ Some(span) => LinkFromSrc::Local(clean::Span::new(span)),
+ None => LinkFromSrc::External(def_id),
+ },
);
- if let Some(def_id) = typeck_results.type_dependent_def_id(expr.hir_id) {
- self.matches.insert(
- segment.ident.span,
- match hir.span_if_local(def_id) {
- Some(span) => LinkFromSrc::Local(clean::Span::new(span)),
- None => LinkFromSrc::External(def_id),
- },
- );
- }
}
} else if self.handle_macro(expr.span) {
// We don't want to go deeper into the macro.
if line.starts_with(&prefix) {
continue;
}
- if line.ends_with(",") {
+ if line.ends_with(',') {
ret.push(line[..line.len() - 1].to_string());
} else {
// No comma (it's the case for the last added crate line)
background-color: var(--sidebar-background-color);
}
- .source nav:not(.sidebar).sub {
- margin-left: 32px;
- }
-
.content {
margin-left: 0px;
}
fn from_tcx(variant: clean::Variant, tcx: TyCtxt<'_>) -> Self {
use clean::Variant::*;
match variant {
- CLike => Variant::Plain,
+ CLike(disr) => Variant::Plain(disr.map(|disr| disr.into_tcx(tcx))),
Tuple(fields) => Variant::Tuple(
fields
.into_iter()
}
}
+impl FromWithTcx<clean::Discriminant> for Discriminant {
+ fn from_tcx(disr: clean::Discriminant, tcx: TyCtxt<'_>) -> Self {
+ Discriminant {
+ // expr is only none if going throught the inlineing path, which gets
+ // `rustc_middle` types, not `rustc_hir`, but because JSON never inlines
+ // the expr is always some.
+ expr: disr.expr(tcx).unwrap(),
+ value: disr.value(tcx),
+ }
+ }
+}
+
impl FromWithTcx<clean::Import> for Import {
fn from_tcx(import: clean::Import, tcx: TyCtxt<'_>) -> Self {
use clean::ImportKind::*;
return;
}
}
- hir::ExprKind::MethodCall(path, _, call_span) => {
+ hir::ExprKind::MethodCall(path, _, _, call_span) => {
let types = tcx.typeck(ex.hir_id.owner);
let Some(def_id) = types.type_dependent_def_id(ex.hir_id) else {
trace!("type_dependent_def_id({}) = None", ex.hir_id);
VariantItem(i) => match i {
Variant::Struct(j) => j.fields.iter().for_each(|x| self.visit_item(x)),
Variant::Tuple(fields) => fields.iter().for_each(|x| self.visit_item(x)),
- Variant::CLike => {}
+ Variant::CLike(_) => {}
},
ExternCrateItem { src: _ }
| ImportItem(_)
use serde::{Deserialize, Serialize};
/// rustdoc format-version.
-pub const FORMAT_VERSION: u32 = 18;
+pub const FORMAT_VERSION: u32 = 19;
/// A `Crate` is the root of the emitted JSON blob. It contains all type/documentation information
/// about the language items in the local crate, as well as info about external items to allow
#[serde(rename_all = "snake_case")]
#[serde(tag = "variant_kind", content = "variant_inner")]
pub enum Variant {
- Plain,
+ Plain(Option<Discriminant>),
Tuple(Vec<Type>),
Struct(Vec<Id>),
}
+#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
+pub struct Discriminant {
+ /// The expression that produced the discriminant.
+ ///
+ /// Unlike `value`, this preserves the original formatting (eg suffixes,
+ /// hexadecimal, and underscores), making it unsuitable to be machine
+ /// interpreted.
+ ///
+ /// In some cases, when the value is to complex, this may be `"{ _ }"`.
+ /// When this occurs is unstable, and may change without notice.
+ pub expr: String,
+ /// The numerical value of the discriminant. Stored as a string due to
+ /// JSON's poor support for large integers, and the fact that it would need
+ /// to store from [`i128::MIN`] to [`u128::MAX`].
+ pub value: String,
+}
+
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum StructType {
+++ /dev/null
-// Ensures that the search input border color changes on focus.
-goto: file://|DOC_PATH|/test_docs/index.html
-local-storage: {"rustdoc-use-system-theme": "false", "rustdoc-theme": "dark"}
-reload:
-
-assert-css: (".search-input", {"border-color": "rgb(224, 224, 224)"})
-click: ".search-input"
-assert-css: (".search-input", {"border-color": "rgb(0, 141, 253)"})
-
-local-storage: {"rustdoc-theme": "light"}
-reload:
-
-assert-css: (".search-input", {"border-color": "rgb(224, 224, 224)"})
-click: ".search-input"
-assert-css: (".search-input", {"border-color": "rgb(102, 175, 233)"})
-
-local-storage: {"rustdoc-theme": "ayu"}
-reload:
-
-assert-css: (".search-input", {"border-color": "rgb(92, 103, 115)"})
-click: ".search-input"
-assert-css: (".search-input", {"border-color": "rgb(92, 103, 115)"})
--- /dev/null
+#[repr(i8)]
+pub enum Ordering {
+ // @is "$.index[*][?(@.name=='Less')].inner.variant_inner.expr" '"-1"'
+ // @is "$.index[*][?(@.name=='Less')].inner.variant_inner.value" '"-1"'
+ Less = -1,
+ // @is "$.index[*][?(@.name=='Equal')].inner.variant_inner.expr" '"0"'
+ // @is "$.index[*][?(@.name=='Equal')].inner.variant_inner.value" '"0"'
+ Equal = 0,
+ // @is "$.index[*][?(@.name=='Greater')].inner.variant_inner.expr" '"1"'
+ // @is "$.index[*][?(@.name=='Greater')].inner.variant_inner.value" '"1"'
+ Greater = 1,
+}
--- /dev/null
+pub enum Foo {
+ // @is "$.index[*][?(@.name=='Addition')].inner.variant_inner.value" '"0"'
+ // @is "$.index[*][?(@.name=='Addition')].inner.variant_inner.expr" '"{ _ }"'
+ Addition = 0 + 0,
+ // @is "$.index[*][?(@.name=='Bin')].inner.variant_inner.value" '"1"'
+ // @is "$.index[*][?(@.name=='Bin')].inner.variant_inner.expr" '"0b1"'
+ Bin = 0b1,
+ // @is "$.index[*][?(@.name=='Oct')].inner.variant_inner.value" '"2"'
+ // @is "$.index[*][?(@.name=='Oct')].inner.variant_inner.expr" '"0o2"'
+ Oct = 0o2,
+ // @is "$.index[*][?(@.name=='PubConst')].inner.variant_inner.value" '"3"'
+ // @is "$.index[*][?(@.name=='PubConst')].inner.variant_inner.expr" '"THREE"'
+ PubConst = THREE,
+ // @is "$.index[*][?(@.name=='Hex')].inner.variant_inner.value" '"4"'
+ // @is "$.index[*][?(@.name=='Hex')].inner.variant_inner.expr" '"0x4"'
+ Hex = 0x4,
+ // @is "$.index[*][?(@.name=='Cast')].inner.variant_inner.value" '"5"'
+ // @is "$.index[*][?(@.name=='Cast')].inner.variant_inner.expr" '"{ _ }"'
+ Cast = 5 as isize,
+ // @is "$.index[*][?(@.name=='PubCall')].inner.variant_inner.value" '"6"'
+ // @is "$.index[*][?(@.name=='PubCall')].inner.variant_inner.expr" '"{ _ }"'
+ PubCall = six(),
+ // @is "$.index[*][?(@.name=='PrivCall')].inner.variant_inner.value" '"7"'
+ // @is "$.index[*][?(@.name=='PrivCall')].inner.variant_inner.expr" '"{ _ }"'
+ PrivCall = seven(),
+ // @is "$.index[*][?(@.name=='PrivConst')].inner.variant_inner.value" '"8"'
+ // @is "$.index[*][?(@.name=='PrivConst')].inner.variant_inner.expr" '"EIGHT"'
+ PrivConst = EIGHT,
+}
+
+pub const THREE: isize = 3;
+const EIGHT: isize = 8;
+
+pub const fn six() -> isize {
+ 6
+}
+const fn seven() -> isize {
+ 7
+}
--- /dev/null
+// ignore-tidy-linelength
+#![feature(repr128)]
+#![allow(incomplete_features)]
+
+#[repr(u64)]
+pub enum U64 {
+ // @is "$.index[*][?(@.name=='U64Min')].inner.variant_inner.value" '"0"'
+ // @is "$.index[*][?(@.name=='U64Min')].inner.variant_inner.expr" '"u64::MIN"'
+ U64Min = u64::MIN,
+ // @is "$.index[*][?(@.name=='U64Max')].inner.variant_inner.value" '"18446744073709551615"'
+ // @is "$.index[*][?(@.name=='U64Max')].inner.variant_inner.expr" '"u64::MAX"'
+ U64Max = u64::MAX,
+}
+
+#[repr(i64)]
+pub enum I64 {
+ // @is "$.index[*][?(@.name=='I64Min')].inner.variant_inner.value" '"-9223372036854775808"'
+ // @is "$.index[*][?(@.name=='I64Min')].inner.variant_inner.expr" '"i64::MIN"'
+ I64Min = i64::MIN,
+ // @is "$.index[*][?(@.name=='I64Max')].inner.variant_inner.value" '"9223372036854775807"'
+ // @is "$.index[*][?(@.name=='I64Max')].inner.variant_inner.expr" '"i64::MAX"'
+ I64Max = i64::MAX,
+}
+
+#[repr(u128)]
+pub enum U128 {
+ // @is "$.index[*][?(@.name=='U128Min')].inner.variant_inner.value" '"0"'
+ // @is "$.index[*][?(@.name=='U128Min')].inner.variant_inner.expr" '"u128::MIN"'
+ U128Min = u128::MIN,
+ // @is "$.index[*][?(@.name=='U128Max')].inner.variant_inner.value" '"340282366920938463463374607431768211455"'
+ // @is "$.index[*][?(@.name=='U128Max')].inner.variant_inner.expr" '"u128::MAX"'
+ U128Max = u128::MAX,
+}
+
+#[repr(i128)]
+pub enum I128 {
+ // @is "$.index[*][?(@.name=='I128Min')].inner.variant_inner.value" '"-170141183460469231731687303715884105728"'
+ // @is "$.index[*][?(@.name=='I128Min')].inner.variant_inner.expr" '"i128::MIN"'
+ I128Min = i128::MIN,
+ // @is "$.index[*][?(@.name=='I128Max')].inner.variant_inner.value" '"170141183460469231731687303715884105727"'
+ // @is "$.index[*][?(@.name=='I128Max')].inner.variant_inner.expr" '"i128::MAX"'
+ I128Max = i128::MAX,
+}
--- /dev/null
+#[repr(u32)]
+pub enum Foo {
+ // @is "$.index[*][?(@.name=='Basic')].inner.variant_inner.value" '"0"'
+ // @is "$.index[*][?(@.name=='Basic')].inner.variant_inner.expr" '"0"'
+ Basic = 0,
+ // @is "$.index[*][?(@.name=='Suffix')].inner.variant_inner.value" '"10"'
+ // @is "$.index[*][?(@.name=='Suffix')].inner.variant_inner.expr" '"10u32"'
+ Suffix = 10u32,
+ // @is "$.index[*][?(@.name=='Underscore')].inner.variant_inner.value" '"100"'
+ // @is "$.index[*][?(@.name=='Underscore')].inner.variant_inner.expr" '"1_0_0"'
+ Underscore = 1_0_0,
+ // @is "$.index[*][?(@.name=='SuffixUnderscore')].inner.variant_inner.value" '"1000"'
+ // @is "$.index[*][?(@.name=='SuffixUnderscore')].inner.variant_inner.expr" '"1_0_0_0u32"'
+ SuffixUnderscore = 1_0_0_0u32,
+}
--- /dev/null
+pub enum Foo {
+ // @is "$.index[*][?(@.name=='Has')].inner.variant_inner" '{"expr":"0", "value":"0"}'
+ Has = 0,
+ // @is "$.index[*][?(@.name=='Doesnt')].inner.variant_inner" null
+ Doesnt,
+ // @is "$.index[*][?(@.name=='AlsoDoesnt')].inner.variant_inner" null
+ AlsoDoesnt,
+ // @is "$.index[*][?(@.name=='AlsoHas')].inner.variant_inner" '{"expr":"44", "value":"44"}'
+ AlsoHas = 44,
+}
--- /dev/null
+struct State;
+
+fn once(_: impl FnOnce()) {}
+
+fn fill_memory_blocks_mt(state: &mut State) {
+ loop {
+ once(move || {
+ //~^ ERROR use of moved value: `state`
+ fill_segment(state);
+ });
+ }
+}
+
+fn fill_segment(_: &mut State) {}
+
+fn main() {}
--- /dev/null
+error[E0382]: use of moved value: `state`
+ --> $DIR/issue-101119.rs:7:14
+ |
+LL | fn fill_memory_blocks_mt(state: &mut State) {
+ | ----- move occurs because `state` has type `&mut State`, which does not implement the `Copy` trait
+LL | loop {
+LL | once(move || {
+ | ^^^^^^^ value moved into closure here, in previous iteration of loop
+LL |
+LL | fill_segment(state);
+ | ----- use occurs due to use in closure
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0382`.
LL | unsafe { intrinsics::const_eval_select((self,), ct_f32_to_u32, rt_f32_to_u32) }
| -------------------------------------------------------------------- inside `core::f32::<impl f32>::to_bits` at $SRC_DIR/core/src/num/f32.rs:LL:COL
|
- ::: $SRC_DIR/core/src/ops/function.rs:LL:COL
- |
-LL | extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
- | ------------------------------------------------------------------ inside `<fn(f32) -> u32 {core::f32::<impl f32>::to_bits::ct_f32_to_u32} as FnOnce<(f32,)>>::call_once - shim(fn(f32) -> u32 {core::f32::<impl f32>::to_bits::ct_f32_to_u32})` at $SRC_DIR/core/src/ops/function.rs:LL:COL
- |
- ::: $SRC_DIR/core/src/intrinsics.rs:LL:COL
- |
-LL | called_in_const.call_once(arg)
- | ------------------------------ inside `const_eval_select::<(f32,), fn(f32) -> u32 {core::f32::<impl f32>::to_bits::ct_f32_to_u32}, [closure@core::f32::<impl f32>::to_bits::{closure#0}], u32>` at $SRC_DIR/core/src/intrinsics.rs:LL:COL
- |
::: $DIR/const-float-bits-reject-conv.rs:27:30
|
LL | const MASKED_NAN1: u32 = f32::NAN.to_bits() ^ 0x002A_AAAA;
LL | unsafe { intrinsics::const_eval_select((self,), ct_f32_to_u32, rt_f32_to_u32) }
| -------------------------------------------------------------------- inside `core::f32::<impl f32>::to_bits` at $SRC_DIR/core/src/num/f32.rs:LL:COL
|
- ::: $SRC_DIR/core/src/ops/function.rs:LL:COL
- |
-LL | extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
- | ------------------------------------------------------------------ inside `<fn(f32) -> u32 {core::f32::<impl f32>::to_bits::ct_f32_to_u32} as FnOnce<(f32,)>>::call_once - shim(fn(f32) -> u32 {core::f32::<impl f32>::to_bits::ct_f32_to_u32})` at $SRC_DIR/core/src/ops/function.rs:LL:COL
- |
- ::: $SRC_DIR/core/src/intrinsics.rs:LL:COL
- |
-LL | called_in_const.call_once(arg)
- | ------------------------------ inside `const_eval_select::<(f32,), fn(f32) -> u32 {core::f32::<impl f32>::to_bits::ct_f32_to_u32}, [closure@core::f32::<impl f32>::to_bits::{closure#0}], u32>` at $SRC_DIR/core/src/intrinsics.rs:LL:COL
- |
::: $DIR/const-float-bits-reject-conv.rs:28:30
|
LL | const MASKED_NAN2: u32 = f32::NAN.to_bits() ^ 0x0055_5555;
LL | unsafe { intrinsics::const_eval_select((self,), ct_f64_to_u64, rt_f64_to_u64) }
| -------------------------------------------------------------------- inside `core::f64::<impl f64>::to_bits` at $SRC_DIR/core/src/num/f64.rs:LL:COL
|
- ::: $SRC_DIR/core/src/ops/function.rs:LL:COL
- |
-LL | extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
- | ------------------------------------------------------------------ inside `<fn(f64) -> u64 {core::f64::<impl f64>::to_bits::ct_f64_to_u64} as FnOnce<(f64,)>>::call_once - shim(fn(f64) -> u64 {core::f64::<impl f64>::to_bits::ct_f64_to_u64})` at $SRC_DIR/core/src/ops/function.rs:LL:COL
- |
- ::: $SRC_DIR/core/src/intrinsics.rs:LL:COL
- |
-LL | called_in_const.call_once(arg)
- | ------------------------------ inside `const_eval_select::<(f64,), fn(f64) -> u64 {core::f64::<impl f64>::to_bits::ct_f64_to_u64}, [closure@core::f64::<impl f64>::to_bits::{closure#0}], u64>` at $SRC_DIR/core/src/intrinsics.rs:LL:COL
- |
::: $DIR/const-float-bits-reject-conv.rs:54:30
|
LL | const MASKED_NAN1: u64 = f64::NAN.to_bits() ^ 0x000A_AAAA_AAAA_AAAA;
LL | unsafe { intrinsics::const_eval_select((self,), ct_f64_to_u64, rt_f64_to_u64) }
| -------------------------------------------------------------------- inside `core::f64::<impl f64>::to_bits` at $SRC_DIR/core/src/num/f64.rs:LL:COL
|
- ::: $SRC_DIR/core/src/ops/function.rs:LL:COL
- |
-LL | extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
- | ------------------------------------------------------------------ inside `<fn(f64) -> u64 {core::f64::<impl f64>::to_bits::ct_f64_to_u64} as FnOnce<(f64,)>>::call_once - shim(fn(f64) -> u64 {core::f64::<impl f64>::to_bits::ct_f64_to_u64})` at $SRC_DIR/core/src/ops/function.rs:LL:COL
- |
- ::: $SRC_DIR/core/src/intrinsics.rs:LL:COL
- |
-LL | called_in_const.call_once(arg)
- | ------------------------------ inside `const_eval_select::<(f64,), fn(f64) -> u64 {core::f64::<impl f64>::to_bits::ct_f64_to_u64}, [closure@core::f64::<impl f64>::to_bits::{closure#0}], u64>` at $SRC_DIR/core/src/intrinsics.rs:LL:COL
- |
::: $DIR/const-float-bits-reject-conv.rs:55:30
|
LL | const MASKED_NAN2: u64 = f64::NAN.to_bits() ^ 0x0005_5555_5555_5555;
--- /dev/null
+// See issue #100696.
+// run-fail
+// check-run-results
+fn main() {
+ &""[1..];
+}
--- /dev/null
+thread 'main' panicked at 'byte index 1 is out of bounds of ``', $DIR/const-eval-select-backtrace-std.rs:5:6
+note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
--- /dev/null
+#![feature(core_intrinsics)]
+// See issue #100696.
+// run-fail
+// check-run-results
+
+#[track_caller]
+fn uhoh() {
+ panic!("Aaah!")
+}
+
+const fn c() {}
+
+fn main() {
+ // safety: this is unsound and just used to test
+ unsafe {
+ std::intrinsics::const_eval_select((), c, uhoh);
+ }
+}
--- /dev/null
+thread 'main' panicked at 'Aaah!', $DIR/const-eval-select-backtrace.rs:16:9
+note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
const fn not_fn_items() {
const_eval_select((), || {}, || {});
- //~^ ERROR the trait bound
+ //~^ ERROR this argument must be a function item
+ //~| ERROR this argument must be a function item
const_eval_select((), 42, 0xDEADBEEF);
- //~^ ERROR the trait bound
+ //~^ ERROR expected a `FnOnce<()>` closure
//~| ERROR expected a `FnOnce<()>` closure
+ //~| ERROR this argument must be a function item
+ //~| ERROR this argument must be a function item
}
const fn foo(n: i32) -> i32 {
//~^ ERROR type mismatch
}
+const fn non_const_fn() {
+ const_eval_select((1,), bar, bar);
+ //~^ ERROR this argument must be a `const fn`
+}
+
fn main() {}
-error[E0277]: the trait bound `[closure@$DIR/const-eval-select-bad.rs:7:27: 7:29]: FnOnce<()>` is not satisfied
+error: this argument must be a function item
--> $DIR/const-eval-select-bad.rs:7:27
|
LL | const_eval_select((), || {}, || {});
- | ----------------- ^^^^^ expected an `FnOnce<()>` closure, found `[closure@$DIR/const-eval-select-bad.rs:7:27: 7:29]`
- | |
- | required by a bound introduced by this call
+ | ^^^^^
|
- = help: the trait `~const FnOnce<()>` is not implemented for closure `[closure@$DIR/const-eval-select-bad.rs:7:27: 7:29]`
-note: the trait `FnOnce<()>` is implemented for `[closure@$DIR/const-eval-select-bad.rs:7:27: 7:29]`, but that implementation is not `const`
- --> $DIR/const-eval-select-bad.rs:7:27
+ = note: expected a function item, found [closure@$DIR/const-eval-select-bad.rs:7:27: 7:29]
+ = help: consult the documentation on `const_eval_select` for more information
+
+error: this argument must be a function item
+ --> $DIR/const-eval-select-bad.rs:7:34
|
LL | const_eval_select((), || {}, || {});
- | ^^^^^
- = note: wrap the `[closure@$DIR/const-eval-select-bad.rs:7:27: 7:29]` in a closure with no arguments: `|| { /* code */ }`
-note: required by a bound in `const_eval_select`
- --> $SRC_DIR/core/src/intrinsics.rs:LL:COL
+ | ^^^^^
|
-LL | F: ~const FnOnce<ARG, Output = RET>,
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `const_eval_select`
+ = note: expected a function item, found [closure@$DIR/const-eval-select-bad.rs:7:34: 7:36]
+ = help: consult the documentation on `const_eval_select` for more information
-error[E0277]: the trait bound `{integer}: FnOnce<()>` is not satisfied
- --> $DIR/const-eval-select-bad.rs:9:27
+error: this argument must be a function item
+ --> $DIR/const-eval-select-bad.rs:10:27
+ |
+LL | const_eval_select((), 42, 0xDEADBEEF);
+ | ^^
+ |
+ = note: expected a function item, found {integer}
+ = help: consult the documentation on `const_eval_select` for more information
+
+error[E0277]: expected a `FnOnce<()>` closure, found `{integer}`
+ --> $DIR/const-eval-select-bad.rs:10:27
|
LL | const_eval_select((), 42, 0xDEADBEEF);
| ----------------- ^^ expected an `FnOnce<()>` closure, found `{integer}`
| |
| required by a bound introduced by this call
|
- = help: the trait `~const FnOnce<()>` is not implemented for `{integer}`
+ = help: the trait `FnOnce<()>` is not implemented for `{integer}`
= note: wrap the `{integer}` in a closure with no arguments: `|| { /* code */ }`
note: required by a bound in `const_eval_select`
--> $SRC_DIR/core/src/intrinsics.rs:LL:COL
|
-LL | F: ~const FnOnce<ARG, Output = RET>,
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `const_eval_select`
+LL | F: FnOnce<ARG, Output = RET>;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `const_eval_select`
+
+error: this argument must be a function item
+ --> $DIR/const-eval-select-bad.rs:10:31
+ |
+LL | const_eval_select((), 42, 0xDEADBEEF);
+ | ^^^^^^^^^^
+ |
+ = note: expected a function item, found {integer}
+ = help: consult the documentation on `const_eval_select` for more information
error[E0277]: expected a `FnOnce<()>` closure, found `{integer}`
- --> $DIR/const-eval-select-bad.rs:9:31
+ --> $DIR/const-eval-select-bad.rs:10:31
|
LL | const_eval_select((), 42, 0xDEADBEEF);
| ----------------- ^^^^^^^^^^ expected an `FnOnce<()>` closure, found `{integer}`
note: required by a bound in `const_eval_select`
--> $SRC_DIR/core/src/intrinsics.rs:LL:COL
|
-LL | G: FnOnce<ARG, Output = RET> + ~const Destruct,
- | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `const_eval_select`
+LL | G: FnOnce<ARG, Output = RET>,
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `const_eval_select`
error[E0271]: expected `fn(i32) -> bool {bar}` to be a fn item that returns `i32`, but it returns `bool`
- --> $DIR/const-eval-select-bad.rs:29:34
+ --> $DIR/const-eval-select-bad.rs:32:34
|
LL | const_eval_select((1,), foo, bar);
| ----------------- ^^^ expected `i32`, found `bool`
note: required by a bound in `const_eval_select`
--> $SRC_DIR/core/src/intrinsics.rs:LL:COL
|
-LL | G: FnOnce<ARG, Output = RET> + ~const Destruct,
- | ^^^^^^^^^^^^ required by this bound in `const_eval_select`
+LL | G: FnOnce<ARG, Output = RET>,
+ | ^^^^^^^^^^^^ required by this bound in `const_eval_select`
error[E0631]: type mismatch in function arguments
- --> $DIR/const-eval-select-bad.rs:34:32
+ --> $DIR/const-eval-select-bad.rs:37:32
|
LL | const fn foo(n: i32) -> i32 {
| --------------------------- found signature defined here
note: required by a bound in `const_eval_select`
--> $SRC_DIR/core/src/intrinsics.rs:LL:COL
|
-LL | F: ~const FnOnce<ARG, Output = RET>,
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `const_eval_select`
+LL | F: FnOnce<ARG, Output = RET>;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `const_eval_select`
+
+error: this argument must be a `const fn`
+ --> $DIR/const-eval-select-bad.rs:42:29
+ |
+LL | const_eval_select((1,), bar, bar);
+ | ^^^
+ |
+ = help: consult the documentation on `const_eval_select` for more information
-error: aborting due to 5 previous errors
+error: aborting due to 9 previous errors
Some errors have detailed explanations: E0271, E0277, E0631.
For more information about an error, try `rustc --explain E0271`.
--- /dev/null
+// run-pass
+// edition:2021
+// check-run-results
+//
+// Drop order tests for let else
+//
+// Mostly this ensures two things:
+// 1. That let and let else temporary drop order is the same.
+// This is a specific design request: https://github.com/rust-lang/rust/pull/93628#issuecomment-1047140316
+// 2. That the else block truly only runs after the
+// temporaries have dropped.
+//
+// We also print some nice tables for an overview by humans.
+// Changes in those tables are considered breakages, but the
+// important properties 1 and 2 are also enforced by the code.
+// This is important as it's easy to update the stdout file
+// with a --bless and miss the impact of that change.
+
+#![feature(let_else)]
+#![allow(irrefutable_let_patterns)]
+
+use std::cell::RefCell;
+use std::rc::Rc;
+
+#[derive(Clone)]
+struct DropAccountant(Rc<RefCell<Vec<Vec<String>>>>);
+
+impl DropAccountant {
+ fn new() -> Self {
+ Self(Default::default())
+ }
+ fn build_droppy(&self, v: u32) -> Droppy<u32> {
+ Droppy(self.clone(), v)
+ }
+ fn build_droppy_enum_none(&self, _v: u32) -> ((), DroppyEnum<u32>) {
+ ((), DroppyEnum::None(self.clone()))
+ }
+ fn new_list(&self, s: impl ToString) {
+ self.0.borrow_mut().push(vec![s.to_string()]);
+ }
+ fn push(&self, s: impl ToString) {
+ let s = s.to_string();
+ let mut accounts = self.0.borrow_mut();
+ accounts.last_mut().unwrap().push(s);
+ }
+ fn print_table(&self) {
+ println!();
+
+ let accounts = self.0.borrow();
+ let before_last = &accounts[accounts.len() - 2];
+ let last = &accounts[accounts.len() - 1];
+ let before_last = get_comma_list(before_last);
+ let last = get_comma_list(last);
+ const LINES: &[&str] = &[
+ "vanilla",
+ "&",
+ "&mut",
+ "move",
+ "fn(this)",
+ "tuple",
+ "array",
+ "ref &",
+ "ref mut &mut",
+ ];
+ let max_len = LINES.iter().map(|v| v.len()).max().unwrap();
+ let max_len_before = before_last.iter().map(|v| v.len()).max().unwrap();
+ let max_len_last = last.iter().map(|v| v.len()).max().unwrap();
+
+ println!(
+ "| {: <max_len$} | {: <max_len_before$} | {: <max_len_last$} |",
+ "construct", before_last[0], last[0]
+ );
+ println!("| {:-<max_len$} | {:-<max_len_before$} | {:-<max_len_last$} |", "", "", "");
+
+ for ((l, l_before), l_last) in
+ LINES.iter().zip(before_last[1..].iter()).zip(last[1..].iter())
+ {
+ println!(
+ "| {: <max_len$} | {: <max_len_before$} | {: <max_len_last$} |",
+ l, l_before, l_last,
+ );
+ }
+ }
+ #[track_caller]
+ fn assert_all_equal_to(&self, st: &str) {
+ let accounts = self.0.borrow();
+ let last = &accounts[accounts.len() - 1];
+ let last = get_comma_list(last);
+ for line in last[1..].iter() {
+ assert_eq!(line.trim(), st.trim());
+ }
+ }
+ #[track_caller]
+ fn assert_equality_last_two_lists(&self) {
+ let accounts = self.0.borrow();
+ let last = &accounts[accounts.len() - 1];
+ let before_last = &accounts[accounts.len() - 2];
+ for (l, b) in last[1..].iter().zip(before_last[1..].iter()) {
+ if !(l == b || l == "n/a" || b == "n/a") {
+ panic!("not equal: '{last:?}' != '{before_last:?}'");
+ }
+ }
+ }
+}
+
+fn get_comma_list(sl: &[String]) -> Vec<String> {
+ std::iter::once(sl[0].clone())
+ .chain(sl[1..].chunks(2).map(|c| c.join(",")))
+ .collect::<Vec<String>>()
+}
+
+struct Droppy<T>(DropAccountant, T);
+
+impl<T> Drop for Droppy<T> {
+ fn drop(&mut self) {
+ self.0.push("drop");
+ }
+}
+
+#[allow(dead_code)]
+enum DroppyEnum<T> {
+ Some(DropAccountant, T),
+ None(DropAccountant),
+}
+
+impl<T> Drop for DroppyEnum<T> {
+ fn drop(&mut self) {
+ match self {
+ DroppyEnum::Some(acc, _inner) => acc,
+ DroppyEnum::None(acc) => acc,
+ }
+ .push("drop");
+ }
+}
+
+macro_rules! nestings_with {
+ ($construct:ident, $binding:pat, $exp:expr) => {
+ // vanilla:
+ $construct!($binding, $exp.1);
+
+ // &:
+ $construct!(&$binding, &$exp.1);
+
+ // &mut:
+ $construct!(&mut $binding, &mut ($exp.1));
+
+ {
+ // move:
+ let w = $exp;
+ $construct!(
+ $binding,
+ {
+ let w = w;
+ w
+ }
+ .1
+ );
+ }
+
+ // fn(this):
+ $construct!($binding, std::convert::identity($exp).1);
+ };
+}
+
+macro_rules! nestings {
+ ($construct:ident, $binding:pat, $exp:expr) => {
+ nestings_with!($construct, $binding, $exp);
+
+ // tuple:
+ $construct!(($binding, 77), ($exp.1, 77));
+
+ // array:
+ $construct!([$binding], [$exp.1]);
+ };
+}
+
+macro_rules! let_else {
+ ($acc:expr, $v:expr, $binding:pat, $build:ident) => {
+ let acc = $acc;
+ let v = $v;
+
+ macro_rules! let_else_construct {
+ ($arg:pat, $exp:expr) => {
+ loop {
+ let $arg = $exp else {
+ acc.push("else");
+ break;
+ };
+ acc.push("body");
+ break;
+ }
+ };
+ }
+ nestings!(let_else_construct, $binding, acc.$build(v));
+ // ref &:
+ let_else_construct!($binding, &acc.$build(v).1);
+
+ // ref mut &mut:
+ let_else_construct!($binding, &mut acc.$build(v).1);
+ };
+}
+
+macro_rules! let_ {
+ ($acc:expr, $binding:tt) => {
+ let acc = $acc;
+
+ macro_rules! let_construct {
+ ($arg:pat, $exp:expr) => {{
+ let $arg = $exp;
+ acc.push("body");
+ }};
+ }
+ let v = 0;
+ {
+ nestings_with!(let_construct, $binding, acc.build_droppy(v));
+ }
+ acc.push("n/a");
+ acc.push("n/a");
+ acc.push("n/a");
+ acc.push("n/a");
+
+ // ref &:
+ let_construct!($binding, &acc.build_droppy(v).1);
+
+ // ref mut &mut:
+ let_construct!($binding, &mut acc.build_droppy(v).1);
+ };
+}
+
+fn main() {
+ let acc = DropAccountant::new();
+
+ println!(" --- matching cases ---");
+
+ // Ensure that let and let else have the same behaviour
+ acc.new_list("let _");
+ let_!(&acc, _);
+ acc.new_list("let else _");
+ let_else!(&acc, 0, _, build_droppy);
+ acc.assert_equality_last_two_lists();
+ acc.print_table();
+
+ // Ensure that let and let else have the same behaviour
+ acc.new_list("let _v");
+ let_!(&acc, _v);
+ acc.new_list("let else _v");
+ let_else!(&acc, 0, _v, build_droppy);
+ acc.assert_equality_last_two_lists();
+ acc.print_table();
+
+ println!();
+
+ println!(" --- mismatching cases ---");
+
+ acc.new_list("let else _ mismatch");
+ let_else!(&acc, 1, DroppyEnum::Some(_, _), build_droppy_enum_none);
+ acc.new_list("let else _v mismatch");
+ let_else!(&acc, 1, DroppyEnum::Some(_, _v), build_droppy_enum_none);
+ acc.print_table();
+ // This ensures that we always drop before visiting the else case
+ acc.assert_all_equal_to("drop,else");
+
+ acc.new_list("let else 0 mismatch");
+ let_else!(&acc, 1, 0, build_droppy);
+ acc.new_list("let else 0 mismatch");
+ let_else!(&acc, 1, 0, build_droppy);
+ acc.print_table();
+ // This ensures that we always drop before visiting the else case
+ acc.assert_all_equal_to("drop,else");
+}
--- /dev/null
+ --- matching cases ---
+
+| construct | let _ | let else _ |
+| ------------ | --------- | ---------- |
+| vanilla | drop,body | drop,body |
+| & | body,drop | body,drop |
+| &mut | body,drop | body,drop |
+| move | drop,body | drop,body |
+| fn(this) | drop,body | drop,body |
+| tuple | n/a,n/a | drop,body |
+| array | n/a,n/a | drop,body |
+| ref & | body,drop | body,drop |
+| ref mut &mut | body,drop | body,drop |
+
+| construct | let _v | let else _v |
+| ------------ | --------- | ----------- |
+| vanilla | drop,body | drop,body |
+| & | body,drop | body,drop |
+| &mut | body,drop | body,drop |
+| move | drop,body | drop,body |
+| fn(this) | drop,body | drop,body |
+| tuple | n/a,n/a | drop,body |
+| array | n/a,n/a | drop,body |
+| ref & | body,drop | body,drop |
+| ref mut &mut | body,drop | body,drop |
+
+ --- mismatching cases ---
+
+| construct | let else _ mismatch | let else _v mismatch |
+| ------------ | ------------------- | -------------------- |
+| vanilla | drop,else | drop,else |
+| & | drop,else | drop,else |
+| &mut | drop,else | drop,else |
+| move | drop,else | drop,else |
+| fn(this) | drop,else | drop,else |
+| tuple | drop,else | drop,else |
+| array | drop,else | drop,else |
+| ref & | drop,else | drop,else |
+| ref mut &mut | drop,else | drop,else |
+
+| construct | let else 0 mismatch | let else 0 mismatch |
+| ------------ | ------------------- | ------------------- |
+| vanilla | drop,else | drop,else |
+| & | drop,else | drop,else |
+| &mut | drop,else | drop,else |
+| move | drop,else | drop,else |
+| fn(this) | drop,else | drop,else |
+| tuple | drop,else | drop,else |
+| array | drop,else | drop,else |
+| ref & | drop,else | drop,else |
+| ref mut &mut | drop,else | drop,else |
--- /dev/null
+// check-pass
+// edition:2021
+#![deny(rust_2021_compatibility)]
+
+pub struct Warns {
+ // `Arc` has significant drop
+ _significant_drop: std::sync::Arc<()>,
+ field: String,
+}
+
+pub fn test(w: Warns) {
+ _ = || drop(w.field);
+}
+
+fn main() {}
pub fn expand_expr_is(input: TokenStream) -> TokenStream {
let mut iter = input.into_iter();
let mut expected_tts = Vec::new();
- loop {
+ let comma = loop {
match iter.next() {
- Some(TokenTree::Punct(ref p)) if p.as_char() == ',' => break,
+ Some(TokenTree::Punct(p)) if p.as_char() == ',' => break p,
Some(tt) => expected_tts.push(tt),
None => panic!("expected comma"),
}
- }
+ };
+
+ // Make sure that `Ident` and `Literal` objects from this proc-macro's
+ // environment are not invalidated when `expand_expr` recursively invokes
+ // another macro by taking a local copy, and checking it after the fact.
+ let pre_expand_span = comma.span();
+ let pre_expand_ident = Ident::new("ident", comma.span());
+ let pre_expand_literal = Literal::string("literal");
+ let pre_expand_call_site = Span::call_site();
let expected = expected_tts.into_iter().collect::<TokenStream>();
let expanded = iter.collect::<TokenStream>().expand_expr().expect("expand_expr failed");
// Also compare the raw tts to make sure they line up.
assert_ts_eq(&expected, &expanded);
+ assert!(comma.span().eq(&pre_expand_span), "pre-expansion span is still equal");
+ assert_eq!(pre_expand_ident.to_string(), "ident", "pre-expansion identifier is still valid");
+ assert_eq!(
+ pre_expand_literal.to_string(),
+ "\"literal\"",
+ "pre-expansion literal is still valid"
+ );
+ assert!(Span::call_site().eq(&pre_expand_call_site), "pre-expansion call-site is still equal");
+
TokenStream::new()
}
-
-PRE EXPANSION AST STATS
-
-Name Accumulated Size Count Item Size
-----------------------------------------------------------------
-ExprField 48 ( 0.6%) 1 48
-Crate 56 ( 0.7%) 1 56
-Attribute 64 ( 0.8%) 2 32
-- Normal 32 ( 0.4%) 1
-- DocComment 32 ( 0.4%) 1
-GenericArgs 64 ( 0.8%) 1 64
-- AngleBracketed 64 ( 0.8%) 1
-Local 72 ( 0.9%) 1 72
-WherePredicate 72 ( 0.9%) 1 72
-- BoundPredicate 72 ( 0.9%) 1
-Arm 96 ( 1.1%) 2 48
-ForeignItem 96 ( 1.1%) 1 96
-- Fn 96 ( 1.1%) 1
-FieldDef 160 ( 1.9%) 2 80
-Stmt 160 ( 1.9%) 5 32
-- Local 32 ( 0.4%) 1
-- MacCall 32 ( 0.4%) 1
-- Expr 96 ( 1.1%) 3
-Param 160 ( 1.9%) 4 40
-FnDecl 200 ( 2.4%) 5 40
-Variant 240 ( 2.8%) 2 120
-Block 288 ( 3.4%) 6 48
-GenericBound 352 ( 4.2%) 4 88
-- Trait 352 ( 4.2%) 4
-AssocItem 416 ( 4.9%) 4 104
-- TyAlias 208 ( 2.5%) 2
-- Fn 208 ( 2.5%) 2
-GenericParam 520 ( 6.1%) 5 104
-PathSegment 720 ( 8.5%) 30 24
-Expr 832 ( 9.8%) 8 104
-- Path 104 ( 1.2%) 1
-- Match 104 ( 1.2%) 1
-- Struct 104 ( 1.2%) 1
-- Lit 208 ( 2.5%) 2
-- Block 312 ( 3.7%) 3
-Pat 840 ( 9.9%) 7 120
-- Struct 120 ( 1.4%) 1
-- Wild 120 ( 1.4%) 1
-- Ident 600 ( 7.1%) 5
-Ty 1_344 (15.9%) 14 96
-- Rptr 96 ( 1.1%) 1
-- Ptr 96 ( 1.1%) 1
-- ImplicitSelf 192 ( 2.3%) 2
-- Path 960 (11.4%) 10
-Item 1_656 (19.6%) 9 184
-- Trait 184 ( 2.2%) 1
-- Enum 184 ( 2.2%) 1
-- ForeignMod 184 ( 2.2%) 1
-- Impl 184 ( 2.2%) 1
-- Fn 368 ( 4.4%) 2
-- Use 552 ( 6.5%) 3
-----------------------------------------------------------------
-Total 8_456
-
-
-POST EXPANSION AST STATS
-
-Name Accumulated Size Count Item Size
-----------------------------------------------------------------
-ExprField 48 ( 0.5%) 1 48
-Crate 56 ( 0.6%) 1 56
-GenericArgs 64 ( 0.7%) 1 64
-- AngleBracketed 64 ( 0.7%) 1
-Local 72 ( 0.8%) 1 72
-WherePredicate 72 ( 0.8%) 1 72
-- BoundPredicate 72 ( 0.8%) 1
-Arm 96 ( 1.0%) 2 48
-ForeignItem 96 ( 1.0%) 1 96
-- Fn 96 ( 1.0%) 1
-InlineAsm 120 ( 1.3%) 1 120
-Attribute 128 ( 1.4%) 4 32
-- DocComment 32 ( 0.3%) 1
-- Normal 96 ( 1.0%) 3
-FieldDef 160 ( 1.7%) 2 80
-Stmt 160 ( 1.7%) 5 32
-- Local 32 ( 0.3%) 1
-- Semi 32 ( 0.3%) 1
-- Expr 96 ( 1.0%) 3
-Param 160 ( 1.7%) 4 40
-FnDecl 200 ( 2.2%) 5 40
-Variant 240 ( 2.6%) 2 120
-Block 288 ( 3.1%) 6 48
-GenericBound 352 ( 3.8%) 4 88
-- Trait 352 ( 3.8%) 4
-AssocItem 416 ( 4.5%) 4 104
-- TyAlias 208 ( 2.3%) 2
-- Fn 208 ( 2.3%) 2
-GenericParam 520 ( 5.7%) 5 104
-PathSegment 792 ( 8.6%) 33 24
-Pat 840 ( 9.1%) 7 120
-- Struct 120 ( 1.3%) 1
-- Wild 120 ( 1.3%) 1
-- Ident 600 ( 6.5%) 5
-Expr 936 (10.2%) 9 104
-- Path 104 ( 1.1%) 1
-- Match 104 ( 1.1%) 1
-- Struct 104 ( 1.1%) 1
-- InlineAsm 104 ( 1.1%) 1
-- Lit 208 ( 2.3%) 2
-- Block 312 ( 3.4%) 3
-Ty 1_344 (14.6%) 14 96
-- Rptr 96 ( 1.0%) 1
-- Ptr 96 ( 1.0%) 1
-- ImplicitSelf 192 ( 2.1%) 2
-- Path 960 (10.5%) 10
-Item 2_024 (22.0%) 11 184
-- Trait 184 ( 2.0%) 1
-- Enum 184 ( 2.0%) 1
-- ExternCrate 184 ( 2.0%) 1
-- ForeignMod 184 ( 2.0%) 1
-- Impl 184 ( 2.0%) 1
-- Fn 368 ( 4.0%) 2
-- Use 736 ( 8.0%) 4
-----------------------------------------------------------------
-Total 9_184
-
-
-HIR STATS
-
-Name Accumulated Size Count Item Size
-----------------------------------------------------------------
-Param 64 ( 0.7%) 2 32
-Local 64 ( 0.7%) 1 64
-ForeignItem 72 ( 0.8%) 1 72
-FieldDef 96 ( 1.0%) 2 48
-Arm 96 ( 1.0%) 2 48
-Stmt 96 ( 1.0%) 3 32
-FnDecl 120 ( 1.3%) 3 40
-Attribute 128 ( 1.4%) 4 32
-Lifetime 128 ( 1.4%) 4 32
-Variant 160 ( 1.7%) 2 80
-ImplItem 176 ( 1.9%) 2 88
-GenericBound 192 ( 2.1%) 4 48
-TraitItem 192 ( 2.1%) 2 96
-WherePredicate 216 ( 2.3%) 3 72
-Block 288 ( 3.1%) 6 48
-QPath 408 ( 4.4%) 17 24
-Pat 440 ( 4.8%) 5 88
-Expr 672 ( 7.3%) 12 56
-Item 960 (10.4%) 12 80
-Ty 1_152 (12.4%) 16 72
-Path 1_296 (14.0%) 27 48
-PathSegment 2_240 (24.2%) 40 56
-----------------------------------------------------------------
-Total 9_256
-
+ast-stats-1 PRE EXPANSION AST STATS
+ast-stats-1 Name Accumulated Size Count Item Size
+ast-stats-1 ----------------------------------------------------------------
+ast-stats-1 ExprField 48 ( 0.6%) 1 48
+ast-stats-1 Crate 56 ( 0.7%) 1 56
+ast-stats-1 Attribute 64 ( 0.8%) 2 32
+ast-stats-1 - Normal 32 ( 0.4%) 1
+ast-stats-1 - DocComment 32 ( 0.4%) 1
+ast-stats-1 GenericArgs 64 ( 0.8%) 1 64
+ast-stats-1 - AngleBracketed 64 ( 0.8%) 1
+ast-stats-1 Local 72 ( 0.9%) 1 72
+ast-stats-1 WherePredicate 72 ( 0.9%) 1 72
+ast-stats-1 - BoundPredicate 72 ( 0.9%) 1
+ast-stats-1 Arm 96 ( 1.1%) 2 48
+ast-stats-1 ForeignItem 96 ( 1.1%) 1 96
+ast-stats-1 - Fn 96 ( 1.1%) 1
+ast-stats-1 FieldDef 160 ( 1.9%) 2 80
+ast-stats-1 Stmt 160 ( 1.9%) 5 32
+ast-stats-1 - Local 32 ( 0.4%) 1
+ast-stats-1 - MacCall 32 ( 0.4%) 1
+ast-stats-1 - Expr 96 ( 1.1%) 3
+ast-stats-1 Param 160 ( 1.9%) 4 40
+ast-stats-1 FnDecl 200 ( 2.4%) 5 40
+ast-stats-1 Variant 240 ( 2.8%) 2 120
+ast-stats-1 Block 288 ( 3.4%) 6 48
+ast-stats-1 GenericBound 352 ( 4.2%) 4 88
+ast-stats-1 - Trait 352 ( 4.2%) 4
+ast-stats-1 AssocItem 416 ( 4.9%) 4 104
+ast-stats-1 - TyAlias 208 ( 2.5%) 2
+ast-stats-1 - Fn 208 ( 2.5%) 2
+ast-stats-1 GenericParam 520 ( 6.1%) 5 104
+ast-stats-1 PathSegment 720 ( 8.5%) 30 24
+ast-stats-1 Expr 832 ( 9.8%) 8 104
+ast-stats-1 - Path 104 ( 1.2%) 1
+ast-stats-1 - Match 104 ( 1.2%) 1
+ast-stats-1 - Struct 104 ( 1.2%) 1
+ast-stats-1 - Lit 208 ( 2.5%) 2
+ast-stats-1 - Block 312 ( 3.7%) 3
+ast-stats-1 Pat 840 ( 9.9%) 7 120
+ast-stats-1 - Struct 120 ( 1.4%) 1
+ast-stats-1 - Wild 120 ( 1.4%) 1
+ast-stats-1 - Ident 600 ( 7.1%) 5
+ast-stats-1 Ty 1_344 (15.9%) 14 96
+ast-stats-1 - Rptr 96 ( 1.1%) 1
+ast-stats-1 - Ptr 96 ( 1.1%) 1
+ast-stats-1 - ImplicitSelf 192 ( 2.3%) 2
+ast-stats-1 - Path 960 (11.4%) 10
+ast-stats-1 Item 1_656 (19.6%) 9 184
+ast-stats-1 - Trait 184 ( 2.2%) 1
+ast-stats-1 - Enum 184 ( 2.2%) 1
+ast-stats-1 - ForeignMod 184 ( 2.2%) 1
+ast-stats-1 - Impl 184 ( 2.2%) 1
+ast-stats-1 - Fn 368 ( 4.4%) 2
+ast-stats-1 - Use 552 ( 6.5%) 3
+ast-stats-1 ----------------------------------------------------------------
+ast-stats-1 Total 8_456
+ast-stats-1
+ast-stats-2 POST EXPANSION AST STATS
+ast-stats-2 Name Accumulated Size Count Item Size
+ast-stats-2 ----------------------------------------------------------------
+ast-stats-2 ExprField 48 ( 0.5%) 1 48
+ast-stats-2 Crate 56 ( 0.6%) 1 56
+ast-stats-2 GenericArgs 64 ( 0.7%) 1 64
+ast-stats-2 - AngleBracketed 64 ( 0.7%) 1
+ast-stats-2 Local 72 ( 0.8%) 1 72
+ast-stats-2 WherePredicate 72 ( 0.8%) 1 72
+ast-stats-2 - BoundPredicate 72 ( 0.8%) 1
+ast-stats-2 Arm 96 ( 1.0%) 2 48
+ast-stats-2 ForeignItem 96 ( 1.0%) 1 96
+ast-stats-2 - Fn 96 ( 1.0%) 1
+ast-stats-2 InlineAsm 120 ( 1.3%) 1 120
+ast-stats-2 Attribute 128 ( 1.4%) 4 32
+ast-stats-2 - DocComment 32 ( 0.3%) 1
+ast-stats-2 - Normal 96 ( 1.0%) 3
+ast-stats-2 FieldDef 160 ( 1.7%) 2 80
+ast-stats-2 Stmt 160 ( 1.7%) 5 32
+ast-stats-2 - Local 32 ( 0.3%) 1
+ast-stats-2 - Semi 32 ( 0.3%) 1
+ast-stats-2 - Expr 96 ( 1.0%) 3
+ast-stats-2 Param 160 ( 1.7%) 4 40
+ast-stats-2 FnDecl 200 ( 2.2%) 5 40
+ast-stats-2 Variant 240 ( 2.6%) 2 120
+ast-stats-2 Block 288 ( 3.1%) 6 48
+ast-stats-2 GenericBound 352 ( 3.8%) 4 88
+ast-stats-2 - Trait 352 ( 3.8%) 4
+ast-stats-2 AssocItem 416 ( 4.5%) 4 104
+ast-stats-2 - TyAlias 208 ( 2.3%) 2
+ast-stats-2 - Fn 208 ( 2.3%) 2
+ast-stats-2 GenericParam 520 ( 5.7%) 5 104
+ast-stats-2 PathSegment 792 ( 8.6%) 33 24
+ast-stats-2 Pat 840 ( 9.1%) 7 120
+ast-stats-2 - Struct 120 ( 1.3%) 1
+ast-stats-2 - Wild 120 ( 1.3%) 1
+ast-stats-2 - Ident 600 ( 6.5%) 5
+ast-stats-2 Expr 936 (10.2%) 9 104
+ast-stats-2 - Path 104 ( 1.1%) 1
+ast-stats-2 - Match 104 ( 1.1%) 1
+ast-stats-2 - Struct 104 ( 1.1%) 1
+ast-stats-2 - InlineAsm 104 ( 1.1%) 1
+ast-stats-2 - Lit 208 ( 2.3%) 2
+ast-stats-2 - Block 312 ( 3.4%) 3
+ast-stats-2 Ty 1_344 (14.6%) 14 96
+ast-stats-2 - Rptr 96 ( 1.0%) 1
+ast-stats-2 - Ptr 96 ( 1.0%) 1
+ast-stats-2 - ImplicitSelf 192 ( 2.1%) 2
+ast-stats-2 - Path 960 (10.5%) 10
+ast-stats-2 Item 2_024 (22.0%) 11 184
+ast-stats-2 - Trait 184 ( 2.0%) 1
+ast-stats-2 - Enum 184 ( 2.0%) 1
+ast-stats-2 - ExternCrate 184 ( 2.0%) 1
+ast-stats-2 - ForeignMod 184 ( 2.0%) 1
+ast-stats-2 - Impl 184 ( 2.0%) 1
+ast-stats-2 - Fn 368 ( 4.0%) 2
+ast-stats-2 - Use 736 ( 8.0%) 4
+ast-stats-2 ----------------------------------------------------------------
+ast-stats-2 Total 9_184
+ast-stats-2
+hir-stats HIR STATS
+hir-stats Name Accumulated Size Count Item Size
+hir-stats ----------------------------------------------------------------
+hir-stats ForeignItemRef 24 ( 0.2%) 1 24
+hir-stats Mod 32 ( 0.3%) 1 32
+hir-stats ExprField 40 ( 0.4%) 1 40
+hir-stats TraitItemRef 56 ( 0.5%) 2 28
+hir-stats Param 64 ( 0.6%) 2 32
+hir-stats Local 64 ( 0.6%) 1 64
+hir-stats InlineAsm 72 ( 0.7%) 1 72
+hir-stats ImplItemRef 72 ( 0.7%) 2 36
+hir-stats FieldDef 96 ( 0.9%) 2 48
+hir-stats Arm 96 ( 0.9%) 2 48
+hir-stats Body 96 ( 0.9%) 3 32
+hir-stats Stmt 96 ( 0.9%) 3 32
+hir-stats - Local 32 ( 0.3%) 1
+hir-stats - Semi 32 ( 0.3%) 1
+hir-stats - Expr 32 ( 0.3%) 1
+hir-stats FnDecl 120 ( 1.2%) 3 40
+hir-stats Attribute 128 ( 1.3%) 4 32
+hir-stats GenericArgs 144 ( 1.4%) 3 48
+hir-stats Variant 160 ( 1.6%) 2 80
+hir-stats GenericArg 160 ( 1.6%) 4 40
+hir-stats - Type 40 ( 0.4%) 1
+hir-stats - Lifetime 120 ( 1.2%) 3
+hir-stats GenericBound 192 ( 1.9%) 4 48
+hir-stats - Trait 192 ( 1.9%) 4
+hir-stats WherePredicate 216 ( 2.1%) 3 72
+hir-stats - BoundPredicate 216 ( 2.1%) 3
+hir-stats Block 288 ( 2.8%) 6 48
+hir-stats GenericParam 400 ( 3.9%) 5 80
+hir-stats Pat 440 ( 4.3%) 5 88
+hir-stats - Wild 88 ( 0.9%) 1
+hir-stats - Struct 88 ( 0.9%) 1
+hir-stats - Binding 264 ( 2.6%) 3
+hir-stats Generics 560 ( 5.5%) 10 56
+hir-stats Expr 768 ( 7.5%) 12 64
+hir-stats - Path 64 ( 0.6%) 1
+hir-stats - Struct 64 ( 0.6%) 1
+hir-stats - Match 64 ( 0.6%) 1
+hir-stats - InlineAsm 64 ( 0.6%) 1
+hir-stats - Lit 128 ( 1.3%) 2
+hir-stats - Block 384 ( 3.8%) 6
+hir-stats Item 960 ( 9.4%) 12 80
+hir-stats - Trait 80 ( 0.8%) 1
+hir-stats - Enum 80 ( 0.8%) 1
+hir-stats - ExternCrate 80 ( 0.8%) 1
+hir-stats - ForeignMod 80 ( 0.8%) 1
+hir-stats - Impl 80 ( 0.8%) 1
+hir-stats - Fn 160 ( 1.6%) 2
+hir-stats - Use 400 ( 3.9%) 5
+hir-stats Ty 1_080 (10.6%) 15 72
+hir-stats - Ptr 72 ( 0.7%) 1
+hir-stats - Rptr 72 ( 0.7%) 1
+hir-stats - Path 936 ( 9.2%) 13
+hir-stats Path 1_536 (15.1%) 32 48
+hir-stats PathSegment 2_240 (22.0%) 40 56
+hir-stats ----------------------------------------------------------------
+hir-stats Total 10_200
+hir-stats
--- /dev/null
+// run-rustfix
+
+fn expect<T>(_: T) {}
+
+fn main() {
+ let x = Some(&());
+ expect::<Option<()>>(x.copied());
+ //~^ ERROR mismatched types
+ //~| HELP use `Option::copied` to copy the value inside the `Option`
+ let x = Ok(&());
+ expect::<Result<(), ()>>(x.copied());
+ //~^ ERROR mismatched types
+ //~| HELP use `Result::copied` to copy the value inside the `Result`
+ let s = String::new();
+ let x = Some(&s);
+ expect::<Option<String>>(x.cloned());
+ //~^ ERROR mismatched types
+ //~| HELP use `Option::cloned` to clone the value inside the `Option`
+ let x = Ok(&s);
+ expect::<Result<String, ()>>(x.cloned());
+ //~^ ERROR mismatched types
+ //~| HELP use `Result::cloned` to clone the value inside the `Result`
+}
--- /dev/null
+// run-rustfix
+
+fn expect<T>(_: T) {}
+
+fn main() {
+ let x = Some(&());
+ expect::<Option<()>>(x);
+ //~^ ERROR mismatched types
+ //~| HELP use `Option::copied` to copy the value inside the `Option`
+ let x = Ok(&());
+ expect::<Result<(), ()>>(x);
+ //~^ ERROR mismatched types
+ //~| HELP use `Result::copied` to copy the value inside the `Result`
+ let s = String::new();
+ let x = Some(&s);
+ expect::<Option<String>>(x);
+ //~^ ERROR mismatched types
+ //~| HELP use `Option::cloned` to clone the value inside the `Option`
+ let x = Ok(&s);
+ expect::<Result<String, ()>>(x);
+ //~^ ERROR mismatched types
+ //~| HELP use `Result::cloned` to clone the value inside the `Result`
+}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/copied-and-cloned.rs:7:26
+ |
+LL | expect::<Option<()>>(x);
+ | -------------------- ^ expected `()`, found `&()`
+ | |
+ | arguments to this function are incorrect
+ |
+ = note: expected enum `Option<()>`
+ found enum `Option<&()>`
+note: function defined here
+ --> $DIR/copied-and-cloned.rs:3:4
+ |
+LL | fn expect<T>(_: T) {}
+ | ^^^^^^ ----
+help: use `Option::copied` to copy the value inside the `Option`
+ |
+LL | expect::<Option<()>>(x.copied());
+ | +++++++++
+
+error[E0308]: mismatched types
+ --> $DIR/copied-and-cloned.rs:11:30
+ |
+LL | expect::<Result<(), ()>>(x);
+ | ------------------------ ^ expected `()`, found `&()`
+ | |
+ | arguments to this function are incorrect
+ |
+ = note: expected enum `Result<(), ()>`
+ found enum `Result<&(), _>`
+note: function defined here
+ --> $DIR/copied-and-cloned.rs:3:4
+ |
+LL | fn expect<T>(_: T) {}
+ | ^^^^^^ ----
+help: use `Result::copied` to copy the value inside the `Result`
+ |
+LL | expect::<Result<(), ()>>(x.copied());
+ | +++++++++
+
+error[E0308]: mismatched types
+ --> $DIR/copied-and-cloned.rs:16:30
+ |
+LL | expect::<Option<String>>(x);
+ | ------------------------ ^ expected struct `String`, found `&String`
+ | |
+ | arguments to this function are incorrect
+ |
+ = note: expected enum `Option<String>`
+ found enum `Option<&String>`
+note: function defined here
+ --> $DIR/copied-and-cloned.rs:3:4
+ |
+LL | fn expect<T>(_: T) {}
+ | ^^^^^^ ----
+help: use `Option::cloned` to clone the value inside the `Option`
+ |
+LL | expect::<Option<String>>(x.cloned());
+ | +++++++++
+
+error[E0308]: mismatched types
+ --> $DIR/copied-and-cloned.rs:20:34
+ |
+LL | expect::<Result<String, ()>>(x);
+ | ---------------------------- ^ expected struct `String`, found `&String`
+ | |
+ | arguments to this function are incorrect
+ |
+ = note: expected enum `Result<String, ()>`
+ found enum `Result<&String, _>`
+note: function defined here
+ --> $DIR/copied-and-cloned.rs:3:4
+ |
+LL | fn expect<T>(_: T) {}
+ | ^^^^^^ ----
+help: use `Result::cloned` to clone the value inside the `Result`
+ |
+LL | expect::<Result<String, ()>>(x.cloned());
+ | +++++++++
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
--- /dev/null
+// issue #101376
+
+use std::ops::AddAssign;
+struct Foo;
+
+impl AddAssign<()> for Foo {
+ fn add_assign(&mut self, _: ()) {}
+}
+
+impl AddAssign<()> for &mut Foo {
+ fn add_assign(&mut self, _: ()) {}
+}
+
+fn main() {
+ (&mut Foo) += ();
+ //~^ ERROR invalid left-hand side of assignment
+ //~| NOTE cannot assign to this expression
+ //~| HELP consider dereferencing the left-hand side of this operation
+}
--- /dev/null
+error[E0067]: invalid left-hand side of assignment
+ --> $DIR/assign-non-lval-needs-deref.rs:15:16
+ |
+LL | (&mut Foo) += ();
+ | ---------- ^^
+ | |
+ | cannot assign to this expression
+ |
+help: consider dereferencing the left-hand side of this operation
+ |
+LL | *(&mut Foo) += ();
+ | +
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0067`.
impl<'tcx> LateLintPass<'tcx> for MyStructLint {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
// Check our expr is calling a method
- if let hir::ExprKind::MethodCall(path, _, [_self_arg, ..]) = &expr.kind
+ if let hir::ExprKind::MethodCall(path, _, _self_arg, ..) = &expr.kind
// Check the name of this method is `some_method`
&& path.ident.name == sym!(some_method)
// Optionally, check the type of the self argument.
&& matches!(cx.tcx.get_diagnostic_name(macro_call.def_id), Some(sym::assert_macro))
&& let Some((condition, panic_expn)) = find_assert_args(cx, e, macro_call.expn)
&& matches!(panic_expn, PanicExpn::Empty)
- && let ExprKind::MethodCall(method_segment, [recv], _) = condition.kind
+ && let ExprKind::MethodCall(method_segment, recv, [], _) = condition.kind
&& let result_type_with_refs = cx.typeck_results().expr_ty(recv)
&& let result_type = result_type_with_refs.peel_refs()
&& is_type_diagnostic_item(cx, result_type, sym::Result)
// do not lint if the closure is called using an iterator (see #1141)
if_chain! {
if let Some(parent) = get_parent_expr(self.cx, expr);
- if let ExprKind::MethodCall(_, [self_arg, ..], _) = &parent.kind;
+ if let ExprKind::MethodCall(_, self_arg, ..) = &parent.kind;
let caller = self.cx.typeck_results().expr_ty(self_arg);
if let Some(iter_id) = self.cx.tcx.get_diagnostic_item(sym::Iterator);
if implements_trait(self.cx, caller, iter_id, &[]);
);
}
} else {
- let span = block.expr.as_ref().map_or_else(|| block.stmts[0].span, |e| e.span);
+ let span =
+ block.expr.as_ref().map_or_else(|| block.stmts[0].span, |e| e.span);
if span.from_expansion() || expr.span.from_expansion() {
return;
}
))
})
},
- ExprKind::MethodCall(path, args, _) if args.len() == 1 => {
- let type_of_receiver = cx.typeck_results().expr_ty(&args[0]);
+ ExprKind::MethodCall(path, receiver, [], _) => {
+ let type_of_receiver = cx.typeck_results().expr_ty(receiver);
if !is_type_diagnostic_item(cx, type_of_receiver, sym::Option)
&& !is_type_diagnostic_item(cx, type_of_receiver, sym::Result)
{
let path: &str = path.ident.name.as_str();
a == path
})
- .and_then(|(_, neg_method)| Some(format!("{}.{}()", snippet_opt(cx, args[0].span)?, neg_method)))
+ .and_then(|(_, neg_method)| Some(format!("{}.{}()", snippet_opt(cx, receiver.span)?, neg_method)))
},
_ => None,
}
if meets_msrv(msrv, msrvs::UNSIGNED_ABS)
&& let ty::Int(from) = cast_from.kind()
&& let ty::Uint(to) = cast_to.kind()
- && let ExprKind::MethodCall(method_path, args, _) = cast_expr.kind
+ && let ExprKind::MethodCall(method_path, receiver, ..) = cast_expr.kind
&& method_path.ident.name.as_str() == "abs"
{
let span = if from.bit_width() == to.bit_width() {
span,
&format!("casting the result of `{cast_from}::abs()` to {cast_to}"),
"replace with",
- format!("{}.unsigned_abs()", Sugg::hir(cx, &args[0], "..").maybe_par()),
+ format!("{}.unsigned_abs()", Sugg::hir(cx, receiver, "..").maybe_par()),
Applicability::MachineApplicable,
);
}
.saturating_sub(constant_int(cx, right).map_or(0, |s| u64::try_from(s).expect("shift too high"))),
_ => nbits,
},
- ExprKind::MethodCall(method, [left, right], _) => {
+ ExprKind::MethodCall(method, left, [right], _) => {
if signed {
return nbits;
}
};
apply_reductions(cx, nbits, left, signed).min(max_bits.unwrap_or(u64::max_value()))
},
- ExprKind::MethodCall(method, [_, lo, hi], _) => {
+ ExprKind::MethodCall(method, _, [lo, hi], _) => {
if method.ident.as_str() == "clamp" {
//FIXME: make this a diagnostic item
if let (Some(lo_bits), Some(hi_bits)) = (get_constant_bits(cx, lo), get_constant_bits(cx, hi)) {
}
nbits
},
- ExprKind::MethodCall(method, [_value], _) => {
+ ExprKind::MethodCall(method, _value, [], _) => {
if method.ident.name.as_str() == "signum" {
0 // do not lint if cast comes from a `signum` function
} else {
cx.typeck_results().expr_ty(expr),
);
lint_cast_ptr_alignment(cx, expr, cast_from, cast_to);
- } else if let ExprKind::MethodCall(method_path, [self_arg, ..], _) = &expr.kind {
+ } else if let ExprKind::MethodCall(method_path, self_arg, ..) = &expr.kind {
if method_path.ident.name == sym!(cast)
&& let Some(generic_args) = method_path.args
&& let [GenericArg::Type(cast_to)] = generic_args.args
return false;
};
match parent.kind {
- ExprKind::MethodCall(name, [self_arg, ..], _) if self_arg.hir_id == e.hir_id => {
+ ExprKind::MethodCall(name, self_arg, ..) if self_arg.hir_id == e.hir_id => {
if matches!(name.ident.as_str(), "read_unaligned" | "write_unaligned")
&& let Some(def_id) = cx.typeck_results().type_dependent_def_id(parent.hir_id)
&& let Some(def_id) = cx.tcx.impl_of_method(def_id)
}
// Don't lint for the result of methods that always return non-negative values.
- if let ExprKind::MethodCall(path, _, _) = cast_op.kind {
+ if let ExprKind::MethodCall(path, ..) = cast_op.kind {
let mut method_name = path.ident.name.as_str();
let allowed_methods = ["abs", "checked_abs", "rem_euclid", "checked_rem_euclid"];
if_chain! {
if method_name == "unwrap";
if let Some(arglist) = method_chain_args(cast_op, &["unwrap"]);
- if let ExprKind::MethodCall(inner_path, _, _) = &arglist[0][0].kind;
+ if let ExprKind::MethodCall(inner_path, ..) = &arglist[0].0.kind;
then {
method_name = inner_path.ident.name.as_str();
}
impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> {
fn new(cx: &'a LateContext<'tcx>) -> Self {
- Self {
- ty_bounds: vec![TyBound::Nothing],
- cx,
- }
+ Self { ty_bounds: vec![TyBound::Nothing], cx }
}
/// Check whether a passed literal has potential to cause fallback or not.
}
return;
}
- },
+ }
- ExprKind::MethodCall(_, args, _) => {
+ ExprKind::MethodCall(_, receiver, args, _) => {
if let Some(def_id) = self.cx.typeck_results().type_dependent_def_id(expr.hir_id) {
let fn_sig = self.cx.tcx.fn_sig(def_id).skip_binder();
- for (expr, bound) in iter::zip(*args, fn_sig.inputs()) {
+ for (expr, bound) in
+ iter::zip(std::iter::once(*receiver).chain(args.iter()), fn_sig.inputs())
+ {
self.ty_bounds.push(TyBound::Ty(*bound));
self.visit_expr(expr);
self.ty_bounds.pop();
}
return;
}
- },
+ }
ExprKind::Struct(_, fields, base) => {
let ty = self.cx.typeck_results().expr_ty(expr);
return;
}
}
- },
+ }
ExprKind::Lit(lit) => {
let ty = self.cx.typeck_results().expr_ty(expr);
self.check_lit(lit, ty, expr.hir_id);
return;
- },
+ }
- _ => {},
+ _ => {}
}
walk_expr(self, expr);
} else {
self.ty_bounds.push(TyBound::Nothing);
}
- },
+ }
_ => self.ty_bounds.push(TyBound::Nothing),
}
expr: &'tcx Expr<'_>,
) -> Option<(RefOp, &'tcx Expr<'tcx>)> {
let (def_id, arg) = match expr.kind {
- ExprKind::MethodCall(_, [arg], _) => (typeck.type_dependent_def_id(expr.hir_id)?, arg),
+ ExprKind::MethodCall(_, arg, [], _) => (typeck.type_dependent_def_id(expr.hir_id)?, arg),
ExprKind::Call(
Expr {
kind: ExprKind::Path(path),
},
})
}),
- ExprKind::MethodCall(_, args, _) => {
+ ExprKind::MethodCall(_, receiver, args, _) => {
let id = cx.typeck_results().type_dependent_def_id(parent.hir_id).unwrap();
- args.iter().position(|arg| arg.hir_id == child_id).map(|i| {
- if i == 0 {
- // Check for calls to trait methods where the trait is implemented on a reference.
- // Two cases need to be handled:
- // * `self` methods on `&T` will never have auto-borrow
- // * `&self` methods on `&T` can have auto-borrow, but `&self` methods on `T` will take
- // priority.
- if e.hir_id != child_id {
- Position::ReborrowStable(precedence)
- } else if let Some(trait_id) = cx.tcx.trait_of_item(id)
- && let arg_ty = cx.tcx.erase_regions(cx.typeck_results().expr_ty_adjusted(e))
- && let ty::Ref(_, sub_ty, _) = *arg_ty.kind()
- && let subs = match cx
- .typeck_results()
- .node_substs_opt(parent.hir_id)
- .and_then(|subs| subs.get(1..))
- {
- Some(subs) => cx.tcx.mk_substs(subs.iter().copied()),
- None => cx.tcx.mk_substs(std::iter::empty::<ty::subst::GenericArg<'_>>()),
- } && let impl_ty = if cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref() {
- // Trait methods taking `&self`
- sub_ty
- } else {
- // Trait methods taking `self`
- arg_ty
- } && impl_ty.is_ref()
- && cx.tcx.infer_ctxt().enter(|infcx|
- infcx
- .type_implements_trait(trait_id, impl_ty, subs, cx.param_env)
- .must_apply_modulo_regions()
- )
+ if receiver.hir_id == child_id {
+ // Check for calls to trait methods where the trait is implemented on a reference.
+ // Two cases need to be handled:
+ // * `self` methods on `&T` will never have auto-borrow
+ // * `&self` methods on `&T` can have auto-borrow, but `&self` methods on `T` will take
+ // priority.
+ if e.hir_id != child_id {
+ return Some(Position::ReborrowStable(precedence))
+ } else if let Some(trait_id) = cx.tcx.trait_of_item(id)
+ && let arg_ty = cx.tcx.erase_regions(cx.typeck_results().expr_ty_adjusted(e))
+ && let ty::Ref(_, sub_ty, _) = *arg_ty.kind()
+ && let subs = match cx
+ .typeck_results()
+ .node_substs_opt(parent.hir_id)
+ .and_then(|subs| subs.get(1..))
{
- Position::MethodReceiverRefImpl
+ Some(subs) => cx.tcx.mk_substs(subs.iter().copied()),
+ None => cx.tcx.mk_substs(std::iter::empty::<ty::subst::GenericArg<'_>>()),
+ } && let impl_ty = if cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref() {
+ // Trait methods taking `&self`
+ sub_ty
} else {
- Position::MethodReceiver
- }
+ // Trait methods taking `self`
+ arg_ty
+ } && impl_ty.is_ref()
+ && cx.tcx.infer_ctxt().enter(|infcx|
+ infcx
+ .type_implements_trait(trait_id, impl_ty, subs, cx.param_env)
+ .must_apply_modulo_regions()
+ )
+ {
+ return Some(Position::MethodReceiverRefImpl)
} else {
- let ty = cx.tcx.fn_sig(id).skip_binder().inputs()[i];
+ return Some(Position::MethodReceiver)
+ }
+ }
+ args.iter()
+ .position(|arg| arg.hir_id == child_id)
+ .map(|i| {
+ let ty = cx.tcx.fn_sig(id).skip_binder().inputs()[i + 1];
if let ty::Param(param_ty) = ty.kind() {
- needless_borrow_impl_arg_position(cx, parent, i, *param_ty, e, precedence, msrv)
+ needless_borrow_impl_arg_position(cx, parent, i + 1, *param_ty, e, precedence, msrv)
} else {
ty_auto_deref_stability(
cx,
- cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(id).input(i)),
+ cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(id).input(i + 1)),
precedence,
)
.position_for_arg()
}
- }
- })
+ })
},
ExprKind::Field(child, name) if child.hir_id == e.hir_id => Some(Position::FieldAccess(name.name)),
ExprKind::Unary(UnOp::Deref, child) if child.hir_id == e.hir_id => Some(Position::Deref),
// check for `unwrap`
if let Some(arglists) = method_chain_args(expr, &["unwrap"]) {
- let receiver_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs();
+ let receiver_ty = self.typeck_results.expr_ty(&arglists[0].0).peel_refs();
if is_type_diagnostic_item(self.cx, receiver_ty, sym::Option)
|| is_type_diagnostic_item(self.cx, receiver_ty, sym::Result)
{
match expr.kind {
ExprKind::MethodCall(
_,
+ map,
[
- map,
Expr {
kind: ExprKind::AddrOf(_, _, key),
span: key_span,
value: &'tcx Expr<'tcx>,
}
fn try_parse_insert<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<InsertExpr<'tcx>> {
- if let ExprKind::MethodCall(_, [map, key, value], _) = expr.kind {
+ if let ExprKind::MethodCall(_, map, [key, value], _) = expr.kind {
let id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?;
if match_def_path(cx, id, &paths::BTREEMAP_INSERT) || match_def_path(cx, id, &paths::HASHMAP_INSERT) {
Some(InsertExpr { map, key, value })
if !is_adjusted(cx, &body.value);
if let ExprKind::Call(callee, args) = body.value.kind;
if let ExprKind::Path(_) = callee.kind;
- if check_inputs(cx, body.params, args);
+ if check_inputs(cx, body.params, None, args);
let callee_ty = cx.typeck_results().expr_ty_adjusted(callee);
let call_ty = cx.typeck_results().type_dependent_def_id(body.value.hir_id)
.map_or(callee_ty, |id| cx.tcx.type_of(id));
if_chain!(
if !is_adjusted(cx, &body.value);
- if let ExprKind::MethodCall(path, args, _) = body.value.kind;
- if check_inputs(cx, body.params, args);
+ if let ExprKind::MethodCall(path, receiver, args, _) = body.value.kind;
+ if check_inputs(cx, body.params, Some(receiver), args);
let method_def_id = cx.typeck_results().type_dependent_def_id(body.value.hir_id).unwrap();
let substs = cx.typeck_results().node_substs(body.value.hir_id);
let call_ty = cx.tcx.bound_type_of(method_def_id).subst(cx.tcx, substs);
}
}
-fn check_inputs(cx: &LateContext<'_>, params: &[Param<'_>], call_args: &[Expr<'_>]) -> bool {
- if params.len() != call_args.len() {
+fn check_inputs(
+ cx: &LateContext<'_>,
+ params: &[Param<'_>],
+ receiver: Option<&Expr<'_>>,
+ call_args: &[Expr<'_>],
+) -> bool {
+ if receiver.map_or(params.len() != call_args.len(), |_| params.len() != call_args.len() + 1) {
return false;
}
let binding_modes = cx.typeck_results().pat_binding_modes();
- std::iter::zip(params, call_args).all(|(param, arg)| {
+ let check_inputs = |param: &Param<'_>, arg| {
match param.pat.kind {
PatKind::Binding(_, id, ..) if path_to_local_id(arg, id) => {},
_ => return false,
},
_ => false,
}
- })
+ };
+ std::iter::zip(params, receiver.into_iter().chain(call_args.iter()))
+ .all(|(param, arg)| check_inputs(param, arg))
}
fn check_sig<'tcx>(cx: &LateContext<'tcx>, closure_ty: Ty<'tcx>, call_ty: Ty<'tcx>) -> bool {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if_chain! {
// match call to unwrap
- if let ExprKind::MethodCall(unwrap_fun, [write_call], _) = expr.kind;
+ if let ExprKind::MethodCall(unwrap_fun, write_call, [], _) = expr.kind;
if unwrap_fun.ident.name == sym::unwrap;
// match call to write_fmt
- if let ExprKind::MethodCall(write_fun, [write_recv, write_arg], _) = look_in_block(cx, &write_call.kind);
+ if let ExprKind::MethodCall(write_fun, write_recv, [write_arg], _) = look_in_block(cx, &write_call.kind);
if write_fun.ident.name == sym!(write_fmt);
// match calls to std::io::stdout() / std::io::stderr ()
if let Some(dest_name) = if match_function_call(cx, write_recv, &paths::STDOUT).is_some() {
// check for `unwrap`
if let Some(arglists) = method_chain_args(expr, &["unwrap"]) {
- let receiver_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs();
+ let receiver_ty = self.typeck_results.expr_ty(&arglists[0].0).peel_refs();
if is_type_diagnostic_item(self.lcx, receiver_ty, sym::Option)
|| is_type_diagnostic_item(self.lcx, receiver_ty, sym::Result)
{
suggestion.maybe_par()
}
-fn check_log_base(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
- if let Some(method) = get_specialized_log_method(cx, &args[1]) {
+fn check_log_base(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) {
+ if let Some(method) = get_specialized_log_method(cx, &args[0]) {
span_lint_and_sugg(
cx,
SUBOPTIMAL_FLOPS,
expr.span,
"logarithm for bases 2, 10 and e can be computed more accurately",
"consider using",
- format!("{}.{}()", Sugg::hir(cx, &args[0], "..").maybe_par(), method),
+ format!("{}.{}()", Sugg::hir(cx, receiver, "..").maybe_par(), method),
Applicability::MachineApplicable,
);
}
// TODO: Lint expressions of the form `(x + y).ln()` where y > 1 and
// suggest usage of `(x + (y - 1)).ln_1p()` instead
-fn check_ln1p(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
+fn check_ln1p(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>) {
if let ExprKind::Binary(
Spanned {
node: BinOpKind::Add, ..
},
lhs,
rhs,
- ) = &args[0].kind
+ ) = receiver.kind
{
let recv = match (
constant(cx, cx.typeck_results(), lhs),
}
}
-fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
+fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) {
// Check receiver
- if let Some((value, _)) = constant(cx, cx.typeck_results(), &args[0]) {
+ if let Some((value, _)) = constant(cx, cx.typeck_results(), receiver) {
let method = if F32(f32_consts::E) == value || F64(f64_consts::E) == value {
"exp"
} else if F32(2.0) == value || F64(2.0) == value {
expr.span,
"exponent for bases 2 and e can be computed more accurately",
"consider using",
- format!("{}.{}()", prepare_receiver_sugg(cx, &args[1]), method),
+ format!("{}.{}()", prepare_receiver_sugg(cx, &args[0]), method),
Applicability::MachineApplicable,
);
}
// Check argument
- if let Some((value, _)) = constant(cx, cx.typeck_results(), &args[1]) {
+ if let Some((value, _)) = constant(cx, cx.typeck_results(), &args[0]) {
let (lint, help, suggestion) = if F32(1.0 / 2.0) == value || F64(1.0 / 2.0) == value {
(
SUBOPTIMAL_FLOPS,
"square-root of a number can be computed more efficiently and accurately",
- format!("{}.sqrt()", Sugg::hir(cx, &args[0], "..").maybe_par()),
+ format!("{}.sqrt()", Sugg::hir(cx, receiver, "..").maybe_par()),
)
} else if F32(1.0 / 3.0) == value || F64(1.0 / 3.0) == value {
(
IMPRECISE_FLOPS,
"cube-root of a number can be computed more accurately",
- format!("{}.cbrt()", Sugg::hir(cx, &args[0], "..").maybe_par()),
+ format!("{}.cbrt()", Sugg::hir(cx, receiver, "..").maybe_par()),
)
} else if let Some(exponent) = get_integer_from_float_constant(&value) {
(
"exponentiation with integer powers can be computed more efficiently",
format!(
"{}.powi({})",
- Sugg::hir(cx, &args[0], "..").maybe_par(),
+ Sugg::hir(cx, receiver, "..").maybe_par(),
numeric_literal::format(&exponent.to_string(), None, false)
),
)
}
}
-fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
- if let Some((value, _)) = constant(cx, cx.typeck_results(), &args[1]) {
+fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) {
+ if let Some((value, _)) = constant(cx, cx.typeck_results(), &args[0]) {
if value == Int(2) {
if let Some(parent) = get_parent_expr(cx, expr) {
if let Some(grandparent) = get_parent_expr(cx, parent) {
- if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, args, _) = grandparent.kind {
- if method_name.as_str() == "sqrt" && detect_hypot(cx, args).is_some() {
+ if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, receiver, ..) = grandparent.kind
+ {
+ if method_name.as_str() == "sqrt" && detect_hypot(cx, receiver).is_some() {
return;
}
}
"consider using",
format!(
"{}.mul_add({}, {})",
- Sugg::hir(cx, &args[0], "..").maybe_par(),
- Sugg::hir(cx, &args[0], ".."),
+ Sugg::hir(cx, receiver, "..").maybe_par(),
+ Sugg::hir(cx, receiver, ".."),
Sugg::hir(cx, other_addend, ".."),
),
Applicability::MachineApplicable,
}
}
-fn detect_hypot(cx: &LateContext<'_>, args: &[Expr<'_>]) -> Option<String> {
+fn detect_hypot(cx: &LateContext<'_>, receiver: &Expr<'_>) -> Option<String> {
if let ExprKind::Binary(
Spanned {
node: BinOpKind::Add, ..
},
add_lhs,
add_rhs,
- ) = args[0].kind
+ ) = receiver.kind
{
// check if expression of the form x * x + y * y
if_chain! {
if_chain! {
if let ExprKind::MethodCall(
PathSegment { ident: lmethod_name, .. },
- [largs_0, largs_1, ..],
+ largs_0, [largs_1, ..],
_
) = &add_lhs.kind;
if let ExprKind::MethodCall(
PathSegment { ident: rmethod_name, .. },
- [rargs_0, rargs_1, ..],
+ rargs_0, [rargs_1, ..],
_
) = &add_rhs.kind;
if lmethod_name.as_str() == "powi" && rmethod_name.as_str() == "powi";
None
}
-fn check_hypot(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
- if let Some(message) = detect_hypot(cx, args) {
+fn check_hypot(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>) {
+ if let Some(message) = detect_hypot(cx, receiver) {
span_lint_and_sugg(
cx,
IMPRECISE_FLOPS,
if cx.typeck_results().expr_ty(lhs).is_floating_point();
if let Some((value, _)) = constant(cx, cx.typeck_results(), rhs);
if F32(1.0) == value || F64(1.0) == value;
- if let ExprKind::MethodCall(path, [self_arg, ..], _) = &lhs.kind;
+ if let ExprKind::MethodCall(path, self_arg, ..) = &lhs.kind;
if cx.typeck_results().expr_ty(self_arg).is_floating_point();
if path.ident.name.as_str() == "exp";
then {
) = &expr.kind
{
if let Some(parent) = get_parent_expr(cx, expr) {
- if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, args, _) = parent.kind {
- if method_name.as_str() == "sqrt" && detect_hypot(cx, args).is_some() {
+ if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, receiver, ..) = parent.kind {
+ if method_name.as_str() == "sqrt" && detect_hypot(cx, receiver).is_some() {
return;
}
}
fn are_same_base_logs(cx: &LateContext<'_>, expr_a: &Expr<'_>, expr_b: &Expr<'_>) -> bool {
if_chain! {
- if let ExprKind::MethodCall(PathSegment { ident: method_name_a, .. }, args_a, _) = expr_a.kind;
- if let ExprKind::MethodCall(PathSegment { ident: method_name_b, .. }, args_b, _) = expr_b.kind;
+ if let ExprKind::MethodCall(PathSegment { ident: method_name_a, .. }, _, args_a, _) = expr_a.kind;
+ if let ExprKind::MethodCall(PathSegment { ident: method_name_b, .. }, _, args_b, _) = expr_b.kind;
then {
return method_name_a.as_str() == method_name_b.as_str() &&
args_a.len() == args_b.len() &&
(
["ln", "log2", "log10"].contains(&method_name_a.as_str()) ||
- method_name_a.as_str() == "log" && args_a.len() == 2 && eq_expr_value(cx, &args_a[1], &args_b[1])
+ method_name_a.as_str() == "log" && args_a.len() == 1 && eq_expr_value(cx, &args_a[0], &args_b[0])
);
}
}
rhs,
) = &expr.kind;
if are_same_base_logs(cx, lhs, rhs);
- if let ExprKind::MethodCall(_, [largs_self, ..], _) = &lhs.kind;
- if let ExprKind::MethodCall(_, [rargs_self, ..], _) = &rhs.kind;
+ if let ExprKind::MethodCall(_, largs_self, ..) = &lhs.kind;
+ if let ExprKind::MethodCall(_, rargs_self, ..) = &rhs.kind;
then {
span_lint_and_sugg(
cx,
return;
}
- if let ExprKind::MethodCall(path, args, _) = &expr.kind {
- let recv_ty = cx.typeck_results().expr_ty(&args[0]);
+ if let ExprKind::MethodCall(path, receiver, args, _) = &expr.kind {
+ let recv_ty = cx.typeck_results().expr_ty(receiver);
if recv_ty.is_floating_point() {
match path.ident.name.as_str() {
- "ln" => check_ln1p(cx, expr, args),
- "log" => check_log_base(cx, expr, args),
- "powf" => check_powf(cx, expr, args),
- "powi" => check_powi(cx, expr, args),
- "sqrt" => check_hypot(cx, expr, args),
+ "ln" => check_ln1p(cx, expr, receiver),
+ "log" => check_log_base(cx, expr, receiver, args),
+ "powf" => check_powf(cx, expr, receiver, args),
+ "powi" => check_powi(cx, expr, receiver, args),
+ "sqrt" => check_hypot(cx, expr, receiver),
_ => {},
}
}
}
}
-fn check_format_in_format_args(cx: &LateContext<'_>, call_site: Span, name: Symbol, arg: &Expr<'_>) {
+fn check_format_in_format_args(
+ cx: &LateContext<'_>,
+ call_site: Span,
+ name: Symbol,
+ arg: &Expr<'_>,
+) {
let expn_data = arg.span.ctxt().outer_expn_data();
if expn_data.call_site.from_expansion() {
return;
fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Expr<'_>) {
if_chain! {
if !value.span.from_expansion();
- if let ExprKind::MethodCall(_, [receiver], _) = value.kind;
+ if let ExprKind::MethodCall(_, receiver, [], _) = value.kind;
if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(value.hir_id);
if is_diag_trait_item(cx, method_def_id, sym::ToString);
let receiver_ty = cx.typeck_results().expr_ty(receiver);
// Returns true if `hir_id` is referred to by multiple format params
fn is_aliased(args: &FormatArgsExpn<'_>, hir_id: HirId) -> bool {
- args.params()
- .filter(|param| param.value.hir_id == hir_id)
- .at_most_one()
- .is_err()
+ args.params().filter(|param| param.value.hir_id == hir_id).at_most_one().is_err()
}
fn count_needed_derefs<'tcx, I>(mut ty: Ty<'tcx>, mut iter: I) -> (usize, Ty<'tcx>)
let mut n_total = 0;
let mut n_needed = 0;
loop {
- if let Some(Adjustment {
- kind: Adjust::Deref(overloaded_deref),
- target,
- }) = iter.next()
- {
+ if let Some(Adjustment { kind: Adjust::Deref(overloaded_deref), target }) = iter.next() {
n_total += 1;
if overloaded_deref.is_some() {
n_needed = n_total;
fn check_to_string_in_display(cx: &LateContext<'_>, expr: &Expr<'_>) {
if_chain! {
// Get the hir_id of the object we are calling the method on
- if let ExprKind::MethodCall(path, [ref self_arg, ..], _) = expr.kind;
+ if let ExprKind::MethodCall(path, self_arg, ..) = expr.kind;
// Is the method to_string() ?
if path.ident.name == sym::to_string;
// Is the method a part of the ToString trait? (i.e. not to_string() implemented
impl<'tcx> LateLintPass<'tcx> for FormatPushString {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
let arg = match expr.kind {
- ExprKind::MethodCall(_, [_, arg], _) => {
+ ExprKind::MethodCall(_, _, [arg], _) => {
if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) &&
match_def_path(cx, fn_def_id, &paths::PUSH_STR) {
arg
return;
}
match expr.kind {
- Call(_, args) | MethodCall(_, args, _) => {
+ Call(_, args) => {
let mut tys = DefIdSet::default();
for arg in args {
if self.cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id())
tys.clear();
}
},
+ MethodCall(_, receiver, args, _) => {
+ let mut tys = DefIdSet::default();
+ for arg in std::iter::once(receiver).chain(args.iter()) {
+ if self.cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id())
+ && is_mutable_ty(
+ self.cx,
+ self.cx.tcx.typeck(arg.hir_id.owner).expr_ty(arg),
+ arg.span,
+ &mut tys,
+ )
+ && is_mutated_static(arg)
+ {
+ self.mutates_static = true;
+ return;
+ }
+ tys.clear();
+ }
+ },
Assign(target, ..) | AssignOp(_, target, _) | AddrOf(_, hir::Mutability::Mut, target) => {
self.mutates_static |= is_mutated_static(target);
},
}
}
},
- hir::ExprKind::MethodCall(_, args, _) => {
+ hir::ExprKind::MethodCall(_, receiver, args, _) => {
let def_id = self.typeck_results.type_dependent_def_id(expr.hir_id).unwrap();
let base_type = self.cx.tcx.type_of(def_id);
if type_is_unsafe_function(self.cx, base_type) {
+ self.check_arg(receiver);
for arg in args {
self.check_arg(arg);
}
fn is_mutex_lock_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
if_chain! {
- if let ExprKind::MethodCall(path, [self_arg, ..], _) = &expr.kind;
+ if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind;
if path.ident.as_str() == "lock";
let ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
if is_type_diagnostic_item(cx, ty, sym::Mutex);
MaybeInfinite => (MAYBE_INFINITE_ITER, "possible infinite iteration detected"),
Finite => {
return;
- },
+ }
};
span_lint(cx, lint, expr.span, msg);
}
/// is an upper bound, e.g., some methods can return a possibly
/// infinite iterator at worst, e.g., `take_while`.
const HEURISTICS: [(&str, usize, Heuristic, Finiteness); 19] = [
- ("zip", 2, All, Infinite),
- ("chain", 2, Any, Infinite),
- ("cycle", 1, Always, Infinite),
- ("map", 2, First, Infinite),
- ("by_ref", 1, First, Infinite),
- ("cloned", 1, First, Infinite),
- ("rev", 1, First, Infinite),
- ("inspect", 1, First, Infinite),
- ("enumerate", 1, First, Infinite),
- ("peekable", 2, First, Infinite),
- ("fuse", 1, First, Infinite),
- ("skip", 2, First, Infinite),
- ("skip_while", 1, First, Infinite),
- ("filter", 2, First, Infinite),
- ("filter_map", 2, First, Infinite),
- ("flat_map", 2, First, Infinite),
- ("unzip", 1, First, Infinite),
- ("take_while", 2, First, MaybeInfinite),
- ("scan", 3, First, MaybeInfinite),
+ ("zip", 1, All, Infinite),
+ ("chain", 1, Any, Infinite),
+ ("cycle", 0, Always, Infinite),
+ ("map", 1, First, Infinite),
+ ("by_ref", 0, First, Infinite),
+ ("cloned", 0, First, Infinite),
+ ("rev", 0, First, Infinite),
+ ("inspect", 0, First, Infinite),
+ ("enumerate", 0, First, Infinite),
+ ("peekable", 1, First, Infinite),
+ ("fuse", 0, First, Infinite),
+ ("skip", 1, First, Infinite),
+ ("skip_while", 0, First, Infinite),
+ ("filter", 1, First, Infinite),
+ ("filter_map", 1, First, Infinite),
+ ("flat_map", 1, First, Infinite),
+ ("unzip", 0, First, Infinite),
+ ("take_while", 1, First, MaybeInfinite),
+ ("scan", 2, First, MaybeInfinite),
];
fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness {
match expr.kind {
- ExprKind::MethodCall(method, args, _) => {
+ ExprKind::MethodCall(method, receiver, args, _) => {
for &(name, len, heuristic, cap) in &HEURISTICS {
if method.ident.name.as_str() == name && args.len() == len {
return (match heuristic {
Always => Infinite,
- First => is_infinite(cx, &args[0]),
- Any => is_infinite(cx, &args[0]).or(is_infinite(cx, &args[1])),
- All => is_infinite(cx, &args[0]).and(is_infinite(cx, &args[1])),
+ First => is_infinite(cx, receiver),
+ Any => is_infinite(cx, receiver).or(is_infinite(cx, &args[0])),
+ All => is_infinite(cx, receiver).and(is_infinite(cx, &args[0])),
})
.and(cap);
}
}
- if method.ident.name == sym!(flat_map) && args.len() == 2 {
- if let ExprKind::Closure(&Closure { body, .. }) = args[1].kind {
+ if method.ident.name == sym!(flat_map) && args.len() == 1 {
+ if let ExprKind::Closure(&Closure { body, .. }) = args[0].kind {
let body = cx.tcx.hir().body(body);
return is_infinite(cx, &body.value);
}
/// the names and argument lengths of methods that *may* exhaust their
/// iterators
const POSSIBLY_COMPLETING_METHODS: [(&str, usize); 6] = [
- ("find", 2),
- ("rfind", 2),
- ("position", 2),
- ("rposition", 2),
- ("any", 2),
- ("all", 2),
+ ("find", 1),
+ ("rfind", 1),
+ ("position", 1),
+ ("rposition", 1),
+ ("any", 1),
+ ("all", 1),
];
/// the names and argument lengths of methods that *always* exhaust
/// their iterators
const COMPLETING_METHODS: [(&str, usize); 12] = [
- ("count", 1),
- ("fold", 3),
- ("for_each", 2),
- ("partition", 2),
- ("max", 1),
- ("max_by", 2),
- ("max_by_key", 2),
- ("min", 1),
- ("min_by", 2),
- ("min_by_key", 2),
- ("sum", 1),
- ("product", 1),
+ ("count", 0),
+ ("fold", 2),
+ ("for_each", 1),
+ ("partition", 1),
+ ("max", 0),
+ ("max_by", 1),
+ ("max_by_key", 1),
+ ("min", 0),
+ ("min_by", 1),
+ ("min_by_key", 1),
+ ("sum", 0),
+ ("product", 0),
];
/// the paths of types that are known to be infinitely allocating
fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness {
match expr.kind {
- ExprKind::MethodCall(method, args, _) => {
+ ExprKind::MethodCall(method, receiver, args, _) => {
for &(name, len) in &COMPLETING_METHODS {
if method.ident.name.as_str() == name && args.len() == len {
- return is_infinite(cx, &args[0]);
+ return is_infinite(cx, receiver);
}
}
for &(name, len) in &POSSIBLY_COMPLETING_METHODS {
if method.ident.name.as_str() == name && args.len() == len {
- return MaybeInfinite.and(is_infinite(cx, &args[0]));
+ return MaybeInfinite.and(is_infinite(cx, receiver));
}
}
- if method.ident.name == sym!(last) && args.len() == 1 {
- let not_double_ended = cx
- .tcx
- .get_diagnostic_item(sym::DoubleEndedIterator)
- .map_or(false, |id| {
- !implements_trait(cx, cx.typeck_results().expr_ty(&args[0]), id, &[])
+ if method.ident.name == sym!(last) && args.is_empty() {
+ let not_double_ended =
+ cx.tcx.get_diagnostic_item(sym::DoubleEndedIterator).map_or(false, |id| {
+ !implements_trait(cx, cx.typeck_results().expr_ty(receiver), id, &[])
});
if not_double_ended {
- return is_infinite(cx, &args[0]);
+ return is_infinite(cx, receiver);
}
} else if method.ident.name == sym!(collect) {
let ty = cx.typeck_results().expr_ty(expr);
.iter()
.any(|diag_item| is_type_diagnostic_item(cx, ty, *diag_item))
{
- return is_infinite(cx, &args[0]);
+ return is_infinite(cx, receiver);
}
}
},
}
fn check_cmp(cx: &LateContext<'_>, span: Span, method: &Expr<'_>, lit: &Expr<'_>, op: &str, compare_to: u32) {
- if let (&ExprKind::MethodCall(method_path, args, _), &ExprKind::Lit(ref lit)) = (&method.kind, &lit.kind) {
+ if let (&ExprKind::MethodCall(method_path, receiver, args, _), &ExprKind::Lit(ref lit)) = (&method.kind, &lit.kind) {
// check if we are in an is_empty() method
if let Some(name) = get_item_name(cx, method) {
if name.as_str() == "is_empty" {
}
}
- check_len(cx, span, method_path.ident.name, args, &lit.node, op, compare_to);
+ check_len(cx, span, method_path.ident.name, receiver, args, &lit.node, op, compare_to);
} else {
check_empty_expr(cx, span, method, lit, op);
}
cx: &LateContext<'_>,
span: Span,
method_name: Symbol,
+ receiver: &Expr<'_>,
args: &[Expr<'_>],
lit: &LitKind,
op: &str,
return;
}
- if method_name == sym::len && args.len() == 1 && has_is_empty(cx, &args[0]) {
+ if method_name == sym::len && args.is_empty() && has_is_empty(cx, receiver) {
let mut applicability = Applicability::MachineApplicable;
span_lint_and_sugg(
cx,
format!(
"{}{}.is_empty()",
op,
- snippet_with_applicability(cx, args[0].span, "_", &mut applicability)
+ snippet_with_applicability(cx, receiver.span, "_", &mut applicability)
),
applicability,
);
let print_limit = |end: &Expr<'_>, end_str: &str, base: &Expr<'_>, sugg: MinifyingSugg<'static>| {
if_chain! {
- if let ExprKind::MethodCall(method, [recv], _) = end.kind;
+ if let ExprKind::MethodCall(method, recv, [], _) = end.kind;
if method.ident.name == sym::len;
if path_to_local(recv) == path_to_local(base);
then {
fn fetch_cloned_expr<'tcx>(expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> {
if_chain! {
- if let ExprKind::MethodCall(method, [arg], _) = expr.kind;
+ if let ExprKind::MethodCall(method, arg, [], _) = expr.kind;
if method.ident.name == sym::clone;
then { arg } else { expr }
}
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, body: &'tcx Expr<'_>) {
if_chain! {
if let ExprKind::Block(Block { stmts: [], expr: None, ..}, _) = body.kind;
- if let ExprKind::MethodCall(method, [callee, ..], _) = unpack_cond(cond).kind;
+ if let ExprKind::MethodCall(method, callee, ..) = unpack_cond(cond).kind;
if [sym::load, sym::compare_exchange, sym::compare_exchange_weak].contains(&method.ident.name);
if let ty::Adt(def, _substs) = cx.typeck_results().expr_ty(callee).kind();
if cx.tcx.is_diagnostic_item(sym::AtomicBool, def.did());
fn check_for_loop_arg(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>) {
let mut next_loop_linted = false; // whether or not ITER_NEXT_LOOP lint was used
- if let ExprKind::MethodCall(method, [self_arg], _) = arg.kind {
+ if let ExprKind::MethodCall(method, self_arg, [], _) = arg.kind {
let method_name = method.ident.as_str();
// check for looping over x.iter() or x.iter_mut(), could use &x or &mut x
match method_name {
}
fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) {
if_chain! {
- if let ExprKind::MethodCall(method, args, _) = expr.kind;
- if let ExprKind::MethodCall(chain_method, _, _) = args[0].kind;
- if chain_method.ident.name == sym!(collect) && is_trait_method(cx, &args[0], sym::Iterator);
+ if let ExprKind::MethodCall(method, receiver, args, _) = expr.kind;
+ if let ExprKind::MethodCall(chain_method, ..) = receiver.kind;
+ if chain_method.ident.name == sym!(collect) && is_trait_method(cx, receiver, sym::Iterator);
then {
- let ty = cx.typeck_results().expr_ty(&args[0]);
+ let ty = cx.typeck_results().expr_ty(receiver);
let mut applicability = Applicability::MaybeIncorrect;
let is_empty_sugg = "next().is_none()".to_string();
let method_name = method.ident.name.as_str();
"len" => "count()".to_string(),
"is_empty" => is_empty_sugg,
"contains" => {
- let contains_arg = snippet_with_applicability(cx, args[1].span, "??", &mut applicability);
+ let contains_arg = snippet_with_applicability(cx, args[0].span, "??", &mut applicability);
let (arg, pred) = contains_arg
.strip_prefix('&')
.map_or(("&x", &*contains_arg), |s| ("x", s));
if let StmtKind::Local(local) = stmt.kind;
if let PatKind::Binding(_, id, ..) = local.pat.kind;
if let Some(init_expr) = local.init;
- if let ExprKind::MethodCall(method_name, &[ref iter_source], ..) = init_expr.kind;
+ if let ExprKind::MethodCall(method_name, iter_source, [], ..) = init_expr.kind;
if method_name.ident.name == sym!(collect) && is_trait_method(cx, init_expr, sym::Iterator);
let ty = cx.typeck_results().expr_ty(init_expr);
if is_type_diagnostic_item(cx, ty, sym::Vec) ||
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
// Check function calls on our collection
- if let ExprKind::MethodCall(method_name, [recv, args @ ..], _) = &expr.kind {
+ if let ExprKind::MethodCall(method_name, recv, [args @ ..], _) = &expr.kind {
if method_name.ident.name == sym!(collect) && is_trait_method(self.cx, expr, sym::Iterator) {
self.current_mutably_captured_ids = get_captured_ids(self.cx, self.cx.typeck_results().expr_ty(recv));
self.visit_expr(recv);
fn is_len_call(expr: &Expr<'_>, var: Symbol) -> bool {
if_chain! {
- if let ExprKind::MethodCall(method, [recv], _) = expr.kind;
+ if let ExprKind::MethodCall(method, recv, [], _) = expr.kind;
if method.ident.name == sym::len;
if let ExprKind::Path(QPath::Resolved(_, path)) = recv.kind;
if path.segments.len() == 1;
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
if_chain! {
// a range index op
- if let ExprKind::MethodCall(meth, [args_0, args_1, ..], _) = &expr.kind;
+ if let ExprKind::MethodCall(meth, args_0, [args_1, ..], _) = &expr.kind;
if (meth.ident.name == sym::index && match_trait_method(self.cx, expr, &paths::INDEX))
|| (meth.ident.name == sym::index_mut && match_trait_method(self.cx, expr, &paths::INDEX_MUT));
if !self.check(args_1, args_0, expr);
self.visit_expr(expr);
}
},
- ExprKind::MethodCall(_, args, _) => {
+ ExprKind::MethodCall(_, receiver, args, _) => {
let def_id = self.cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap();
- for (ty, expr) in iter::zip(self.cx.tcx.fn_sig(def_id).inputs().skip_binder(), args) {
+ for (ty, expr) in iter::zip(
+ self.cx.tcx.fn_sig(def_id).inputs().skip_binder(),
+ std::iter::once(receiver).chain(args.iter()),
+ ) {
self.prefer_mutable = false;
if let ty::Ref(_, _, mutbl) = *ty.kind() {
if mutbl == Mutability::Mut {
| ExprKind::Repeat(e, _)
| ExprKind::DropTemps(e) => never_loop_expr(e, main_loop_id),
ExprKind::Let(let_expr) => never_loop_expr(let_expr.init, main_loop_id),
- ExprKind::Array(es) | ExprKind::MethodCall(_, es, _) | ExprKind::Tup(es) => {
- never_loop_expr_all(&mut es.iter(), main_loop_id)
+ ExprKind::Array(es) | ExprKind::Tup(es) => never_loop_expr_all(&mut es.iter(), main_loop_id),
+ ExprKind::MethodCall(_, receiver, es, _) => {
+ never_loop_expr_all(&mut std::iter::once(receiver).chain(es.iter()), main_loop_id)
},
ExprKind::Struct(_, fields, base) => {
let fields = never_loop_expr_all(&mut fields.iter().map(|f| f.expr), main_loop_id);
InlineAsmOperand::In { expr, .. } | InlineAsmOperand::InOut { expr, .. } => {
never_loop_expr(expr, main_loop_id)
},
- InlineAsmOperand::Out { expr, .. } => never_loop_expr_all(&mut expr.iter(), main_loop_id),
+ InlineAsmOperand::Out { expr, .. } => never_loop_expr_all(&mut expr.iter().copied(), main_loop_id),
InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => {
- never_loop_expr_all(&mut once(in_expr).chain(out_expr.iter()), main_loop_id)
+ never_loop_expr_all(&mut once(*in_expr).chain(out_expr.iter().copied()), main_loop_id)
},
InlineAsmOperand::Const { .. }
| InlineAsmOperand::SymFn { .. }
if_chain! {
// Extract method being called
if let StmtKind::Semi(semi_stmt) = &stmt.kind;
- if let ExprKind::MethodCall(path, args, _) = &semi_stmt.kind;
+ if let ExprKind::MethodCall(path, self_expr, args, _) = &semi_stmt.kind;
// Figure out the parameters for the method call
- if let Some(self_expr) = args.get(0);
- if let Some(pushed_item) = args.get(1);
+ if let Some(pushed_item) = args.get(0);
// Check that the method being called is push() on a Vec
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr), sym::Vec);
if path.ident.name.as_str() == "push";
) => (arg, "&mut "),
ExprKind::MethodCall(
method,
- [
- Expr {
- kind: ExprKind::Array([arg]),
- ..
- },
- ],
+ Expr {
+ kind: ExprKind::Array([arg]),
+ ..
+ },
+ [],
_,
) if method.ident.name == rustc_span::sym::iter => (arg, "&"),
ExprKind::MethodCall(
method,
- [
- Expr {
- kind: ExprKind::Array([arg]),
- ..
- },
- ],
+ Expr {
+ kind: ExprKind::Array([arg]),
+ ..
+ },
+ [],
_,
) if method.ident.name.as_str() == "iter_mut" => (arg, "&mut "),
ExprKind::MethodCall(
method,
- [
- Expr {
- kind: ExprKind::Array([arg]),
- ..
- },
- ],
+ Expr {
+ kind: ExprKind::Array([arg]),
+ ..
+ },
+ [],
_,
) if method.ident.name == rustc_span::sym::into_iter => (arg, ""),
// Only check for arrays edition 2021 or later, as this case will trigger a compiler error otherwise.
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &'tcx Block<'_>) {
let (init, has_trailing_exprs) = match (loop_block.stmts, loop_block.expr) {
([stmt, stmts @ ..], expr) => {
- if let StmtKind::Local(&Local { init: Some(e), els: None, .. }) | StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind {
+ if let StmtKind::Local(&Local {
+ init: Some(e),
+ els: None,
+ ..
+ })
+ | StmtKind::Semi(e)
+ | StmtKind::Expr(e) = stmt.kind
+ {
(e, !stmts.is_empty() || expr.is_some())
} else {
return;
if let Res::Def(_, pat_did) = pat_path.res;
if match_def_path(cx, pat_did, &paths::OPTION_SOME);
// check for call to `Iterator::next`
- if let ExprKind::MethodCall(method_name, [iter_expr], _) = let_expr.kind;
+ if let ExprKind::MethodCall(method_name, iter_expr, [], _) = let_expr.kind;
if method_name.ident.name == sym::next;
if is_trait_method(cx, let_expr, sym::Iterator);
if let Some(iter_expr_struct) = try_parse_iter_expr(cx, iter_expr);
if let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id();
if cx.tcx.is_diagnostic_item(sym::mem_size_of, def_id);
then {
- cx.typeck_results().node_substs(count_func.hir_id).types().next().map(|resolved_ty| (real_ty, resolved_ty))
+ cx.typeck_results().node_substs(count_func.hir_id).types().next().map(|resolved_ty| (*real_ty, resolved_ty))
} else {
None
}
fn is_ty_conversion(expr: &Expr<'_>) -> bool {
if let ExprKind::Cast(..) = expr.kind {
true
- } else if let ExprKind::MethodCall(path, [_], _) = expr.kind
+ } else if let ExprKind::MethodCall(path, _, [], _) = expr.kind
&& path.ident.name == rustc_span::sym::try_into
{
// This is only called for `usize` which implements `TryInto`. Therefore,
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
if let Some(parent_expr) = get_parent_expr(cx, expr)
&& let Assign(left_expr, collect_expr, _) = &parent_expr.kind
- && let hir::ExprKind::MethodCall(seg, _, _) = &collect_expr.kind
+ && let hir::ExprKind::MethodCall(seg, ..) = &collect_expr.kind
&& seg.args.is_none()
- && let hir::ExprKind::MethodCall(_, [target_expr], _) = &collect_expr.kind
+ && let hir::ExprKind::MethodCall(_, target_expr, [], _) = &collect_expr.kind
&& let Some(collect_def_id) = cx.typeck_results().type_dependent_def_id(collect_expr.hir_id)
&& match_def_path(cx, collect_def_id, &paths::CORE_ITER_COLLECT) {
check_into_iter(cx, parent_expr, left_expr, target_expr, self.msrv);
target_expr: &hir::Expr<'_>,
msrv: Option<RustcVersion>,
) {
- if let hir::ExprKind::MethodCall(_, [into_iter_expr, _], _) = &target_expr.kind
+ if let hir::ExprKind::MethodCall(_, into_iter_expr, [_], _) = &target_expr.kind
&& let Some(filter_def_id) = cx.typeck_results().type_dependent_def_id(target_expr.hir_id)
&& match_def_path(cx, filter_def_id, &paths::CORE_ITER_FILTER)
- && let hir::ExprKind::MethodCall(_, [struct_expr], _) = &into_iter_expr.kind
+ && let hir::ExprKind::MethodCall(_, struct_expr, [], _) = &into_iter_expr.kind
&& let Some(into_iter_def_id) = cx.typeck_results().type_dependent_def_id(into_iter_expr.hir_id)
&& match_def_path(cx, into_iter_def_id, &paths::CORE_ITER_INTO_ITER)
&& match_acceptable_type(cx, left_expr, msrv)
target_expr: &hir::Expr<'_>,
msrv: Option<RustcVersion>,
) {
- if let hir::ExprKind::MethodCall(_, [filter_expr], _) = &target_expr.kind
+ if let hir::ExprKind::MethodCall(_, filter_expr, [], _) = &target_expr.kind
&& let Some(copied_def_id) = cx.typeck_results().type_dependent_def_id(target_expr.hir_id)
&& (match_def_path(cx, copied_def_id, &paths::CORE_ITER_COPIED)
|| match_def_path(cx, copied_def_id, &paths::CORE_ITER_CLONED))
- && let hir::ExprKind::MethodCall(_, [iter_expr, _], _) = &filter_expr.kind
+ && let hir::ExprKind::MethodCall(_, iter_expr, [_], _) = &filter_expr.kind
&& let Some(filter_def_id) = cx.typeck_results().type_dependent_def_id(filter_expr.hir_id)
&& match_def_path(cx, filter_def_id, &paths::CORE_ITER_FILTER)
- && let hir::ExprKind::MethodCall(_, [struct_expr], _) = &iter_expr.kind
+ && let hir::ExprKind::MethodCall(_, struct_expr, [], _) = &iter_expr.kind
&& let Some(iter_expr_def_id) = cx.typeck_results().type_dependent_def_id(iter_expr.hir_id)
&& match_acceptable_def_path(cx, iter_expr_def_id)
&& match_acceptable_type(cx, left_expr, msrv)
msrv: Option<RustcVersion>,
) {
if meets_msrv(msrv, msrvs::STRING_RETAIN)
- && let hir::ExprKind::MethodCall(_, [filter_expr], _) = &target_expr.kind
+ && let hir::ExprKind::MethodCall(_, filter_expr, [], _) = &target_expr.kind
&& let Some(to_owned_def_id) = cx.typeck_results().type_dependent_def_id(target_expr.hir_id)
&& match_def_path(cx, to_owned_def_id, &paths::TO_OWNED_METHOD)
- && let hir::ExprKind::MethodCall(_, [chars_expr, _], _) = &filter_expr.kind
+ && let hir::ExprKind::MethodCall(_, chars_expr, [_], _) = &filter_expr.kind
&& let Some(filter_def_id) = cx.typeck_results().type_dependent_def_id(filter_expr.hir_id)
&& match_def_path(cx, filter_def_id, &paths::CORE_ITER_FILTER)
- && let hir::ExprKind::MethodCall(_, [str_expr], _) = &chars_expr.kind
+ && let hir::ExprKind::MethodCall(_, str_expr, [], _) = &chars_expr.kind
&& let Some(chars_expr_def_id) = cx.typeck_results().type_dependent_def_id(chars_expr.hir_id)
&& match_def_path(cx, chars_expr_def_id, &paths::STR_CHARS)
&& let ty = cx.typeck_results().expr_ty(str_expr).peel_refs()
}
fn suggest(cx: &LateContext<'_>, parent_expr: &hir::Expr<'_>, left_expr: &hir::Expr<'_>, filter_expr: &hir::Expr<'_>) {
- if let hir::ExprKind::MethodCall(_, [_, closure], _) = filter_expr.kind
+ if let hir::ExprKind::MethodCall(_, _, [closure], _) = filter_expr.kind
&& let hir::ExprKind::Closure(&hir::Closure { body, ..}) = closure.kind
&& let filter_body = cx.tcx.hir().body(body)
&& let [filter_params] = filter_body.params
ExprKind::Call(func, args) => {
parse_call(cx, expr.span, func, args);
},
- ExprKind::MethodCall(path_segment, args, _) => {
- parse_method_call(cx, expr.span, path_segment, args);
+ ExprKind::MethodCall(path_segment, receiver, ..) => {
+ parse_method_call(cx, expr.span, path_segment, receiver);
},
_ => (),
}
}
/// Tries to parse an expression as a method call, emitting the warning if necessary.
-fn parse_method_call(cx: &LateContext<'_>, span: Span, path_segment: &PathSegment<'_>, args: &[Expr<'_>]) {
- if args.is_empty() {
- // When parsing TryFrom::try_from(...).expect(...), we will have more than 1 arg.
- return;
- }
-
+fn parse_method_call(cx: &LateContext<'_>, span: Span, path_segment: &PathSegment<'_>, receiver: &Expr<'_>) {
let ident = path_segment.ident.as_str();
- let method_arg_kind = &args[0].kind;
+ let method_arg_kind = &receiver.kind;
if ["to_string", "to_owned", "into"].contains(&ident) && is_expr_kind_empty_str(method_arg_kind) {
warn_then_suggest(cx, span);
} else if let ExprKind::Call(func, args) = method_arg_kind {
if_chain! {
if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr);
- if let ExprKind::MethodCall(_, [target_arg, pattern], _) = cond.kind;
+ if let ExprKind::MethodCall(_, target_arg, [pattern], _) = cond.kind;
if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(cond.hir_id);
if let ExprKind::Path(target_path) = &target_arg.kind;
then {
// Returns `Some(arg)` if `expr` matches `arg.len()` and `None` otherwise.
fn len_arg<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
if_chain! {
- if let ExprKind::MethodCall(_, [arg], _) = expr.kind;
+ if let ExprKind::MethodCall(_, arg, [], _) = expr.kind;
if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
if match_def_path(cx, method_def_id, &paths::STR_LEN);
then {
)
}
-fn lint_map_unit_fn(cx: &LateContext<'_>, stmt: &hir::Stmt<'_>, expr: &hir::Expr<'_>, map_args: &[hir::Expr<'_>]) {
- let var_arg = &map_args[0];
+fn lint_map_unit_fn(
+ cx: &LateContext<'_>,
+ stmt: &hir::Stmt<'_>,
+ expr: &hir::Expr<'_>,
+ map_args: (&hir::Expr<'_>, &[hir::Expr<'_>]),
+) {
+ let var_arg = &map_args.0;
let (map_type, variant, lint) = if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(var_arg), sym::Option) {
("Option", "Some", OPTION_MAP_UNIT_FN)
} else {
return;
};
- let fn_arg = &map_args[1];
+ let fn_arg = &map_args.1[0];
if is_unit_function(cx, fn_arg) {
let mut applicability = Applicability::MachineApplicable;
};
if_chain! {
- if let ExprKind::MethodCall(ok_path, [ref result_types_0, ..], _) = let_expr.kind; //check is expr.ok() has type Result<T,E>.ok(, _)
+ if let ExprKind::MethodCall(ok_path, result_types_0, ..) = let_expr.kind; //check is expr.ok() has type Result<T,E>.ok(, _)
if let PatKind::TupleStruct(QPath::Resolved(_, x), y, _) = let_pat.kind; //get operation
if method_chain_args(let_expr, &["ok"]).is_some(); //test to see if using ok() method use std::marker::Sized;
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(result_types_0), sym::Result);
impl<'a, 'tcx> Visitor<'tcx> for MatchExprVisitor<'a, 'tcx> {
fn visit_expr(&mut self, ex: &'tcx Expr<'_>) {
match ex.kind {
- ExprKind::MethodCall(segment, [receiver], _) if self.case_altered(segment.ident.as_str(), receiver) => {},
+ ExprKind::MethodCall(segment, receiver, [], _) if self.case_altered(segment.ident.as_str(), receiver) => {},
_ => walk_expr(self, ex),
}
}
// check that `while_let_on_iterator` lint does not trigger
if_chain! {
if keyword == "while";
- if let ExprKind::MethodCall(method_path, _, _) = let_expr.kind;
+ if let ExprKind::MethodCall(method_path, ..) = let_expr.kind;
if method_path.ident.name == sym::next;
if is_trait_method(cx, let_expr, sym::Iterator);
then {
self.is_chain_end = false;
match ex.kind {
- ExprKind::MethodCall(_, [ref expr, ..], _) => {
+ ExprKind::MethodCall(_, expr, ..) => {
self.visit_expr(expr);
}
ExprKind::Binary(_, left, right) => {
ExprKind::Index(..) |
ExprKind::Ret(..) |
ExprKind::Repeat(..) |
- ExprKind::Yield(..) |
- ExprKind::MethodCall(..) => walk_expr(self, ex),
+ ExprKind::Yield(..) => walk_expr(self, ex),
ExprKind::AddrOf(_, _, _) |
ExprKind::Block(_, _) |
ExprKind::Break(_, _) |
if ty::Uint(UintTy::U8) == *cx.typeck_results().expr_ty(needle).peel_refs().kind();
if !is_local_used(cx, needle, arg_id);
then {
- let haystack = if let ExprKind::MethodCall(path, args, _) =
+ let haystack = if let ExprKind::MethodCall(path, receiver, [], _) =
filter_recv.kind {
let p = path.ident.name;
- if (p == sym::iter || p == sym!(iter_mut)) && args.len() == 1 {
- &args[0]
+ if p == sym::iter || p == sym!(iter_mut) {
+ receiver
} else {
filter_recv
}
if Some(id) == cx.tcx.lang_items().option_some_variant();
then {
let mut applicability = Applicability::MachineApplicable;
- let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0][0]).peel_refs();
+ let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0].0).peel_refs();
if *self_ty.kind() != ty::Str {
return false;
"like this",
format!("{}{}.{}({})",
if info.eq { "" } else { "!" },
- snippet_with_applicability(cx, args[0][0].span, "..", &mut applicability),
+ snippet_with_applicability(cx, args[0].0.span, "..", &mut applicability),
suggest,
snippet_with_applicability(cx, arg_char.span, "..", &mut applicability)),
applicability,
"like this",
format!("{}{}.{}('{}')",
if info.eq { "" } else { "!" },
- snippet_with_applicability(cx, args[0][0].span, "..", &mut applicability),
+ snippet_with_applicability(cx, args[0].0.span, "..", &mut applicability),
suggest,
c.escape_default()),
applicability,
/// Checks for the `CLONE_ON_COPY` lint.
#[allow(clippy::too_many_lines)]
-pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol, args: &[Expr<'_>]) {
- let arg = match args {
- [arg] if method_name == sym::clone => arg,
- _ => return,
- };
+pub(super) fn check(
+ cx: &LateContext<'_>,
+ expr: &Expr<'_>,
+ method_name: Symbol,
+ receiver: &Expr<'_>,
+ args: &[Expr<'_>],
+) {
+ let arg = if method_name == sym::clone && args.is_empty() { receiver } else { return };
if cx
.typeck_results()
.type_dependent_def_id(expr.hir_id)
// &*x is a nop, &x.clone() is not
ExprKind::AddrOf(..) => return,
// (*x).func() is useless, x.clone().func() can work in case func borrows self
- ExprKind::MethodCall(_, [self_arg, ..], _)
+ ExprKind::MethodCall(_, self_arg, ..)
if expr.hir_id == self_arg.hir_id && ty != cx.typeck_results().expr_ty_adjusted(expr) =>
{
return;
hir_callee.kind,
ExprKind::Path(QPath::LangItem(rustc_hir::LangItem::TryTraitBranch, _, _))
),
- ExprKind::MethodCall(_, [self_arg, ..], _) if expr.hir_id == self_arg.hir_id => true,
+ ExprKind::MethodCall(_, self_arg, ..) if expr.hir_id == self_arg.hir_id => true,
ExprKind::Match(_, _, MatchSource::TryDesugar | MatchSource::AwaitDesugar)
| ExprKind::Field(..)
| ExprKind::Index(..) => true,
use super::CLONE_ON_REF_PTR;
-pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_name: Symbol, args: &[hir::Expr<'_>]) {
- if !(args.len() == 1 && method_name == sym::clone) {
+pub(super) fn check(
+ cx: &LateContext<'_>,
+ expr: &hir::Expr<'_>,
+ method_name: Symbol,
+ receiver: &hir::Expr<'_>,
+ args: &[hir::Expr<'_>],
+) {
+ if !(args.is_empty() && method_name == sym::clone) {
return;
}
- let arg = &args[0];
- let obj_ty = cx.typeck_results().expr_ty(arg).peel_refs();
+ let obj_ty = cx.typeck_results().expr_ty(receiver).peel_refs();
if let ty::Adt(_, subst) = obj_ty.kind() {
let caller_type = if is_type_diagnostic_item(cx, obj_ty, sym::Rc) {
return;
};
- let snippet = snippet_with_macro_callsite(cx, arg.span, "..");
+ let snippet = snippet_with_macro_callsite(cx, receiver.span, "..");
span_lint_and_sugg(
cx,
// If the parent node's `to` argument is the same as the `to` argument
// of the last replace call in the current chain, don't lint as it was already linted
if let Some(parent) = get_parent_expr(cx, expr)
- && let Some(("replace", [_, current_from, current_to], _)) = method_call(parent)
+ && let Some(("replace", _, [current_from, current_to], _)) = method_call(parent)
&& eq_expr_value(cx, to, current_to)
&& from_kind == cx.typeck_results().expr_ty(current_from).peel_refs().kind()
{
let mut from_args = VecDeque::new();
let _: Option<()> = for_each_expr(expr, |e| {
- if let Some(("replace", [_, from, to], _)) = method_call(e) {
+ if let Some(("replace", _, [from, to], _)) = method_call(e) {
if eq_expr_value(cx, to_arg, to) && cx.typeck_results().expr_ty(from).peel_refs().is_char() {
methods.push_front(e);
from_args.push_front(from);
.collect();
let app = Applicability::MachineApplicable;
let earliest_replace_call = replace_methods.methods.front().unwrap();
- if let Some((_, [..], span_lo)) = method_call(earliest_replace_call) {
+ if let Some((_, _, [..], span_lo)) = method_call(earliest_replace_call) {
span_lint_and_sugg(
cx,
COLLAPSIBLE_STR_REPLACE,
expr: &hir::Expr<'_>,
method_span: Span,
name: &str,
+ receiver: &'tcx hir::Expr<'tcx>,
args: &'tcx [hir::Expr<'tcx>],
) {
// Strip `&`, `as_ref()` and `as_str()` off `arg` until we're left with either a `String` or
loop {
arg_root = match &arg_root.kind {
hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, expr) => expr,
- hir::ExprKind::MethodCall(method_name, call_args, _) => {
- if call_args.len() == 1
- && (method_name.ident.name == sym::as_str || method_name.ident.name == sym::as_ref)
- && {
- let arg_type = cx.typeck_results().expr_ty(&call_args[0]);
- let base_type = arg_type.peel_refs();
- *base_type.kind() == ty::Str || is_type_diagnostic_item(cx, base_type, sym::String)
- }
- {
- &call_args[0]
+ hir::ExprKind::MethodCall(method_name, receiver, [], ..) => {
+ if (method_name.ident.name == sym::as_str || method_name.ident.name == sym::as_ref) && {
+ let arg_type = cx.typeck_results().expr_ty(receiver);
+ let base_type = arg_type.peel_refs();
+ *base_type.kind() == ty::Str || is_type_diagnostic_item(cx, base_type, sym::String)
+ } {
+ receiver
} else {
break;
}
}
}
- if args.len() != 2 || name != "expect" || !is_call(&args[1].kind) {
+ if args.len() != 1 || name != "expect" || !is_call(&args[0].kind) {
return;
}
- let receiver_type = cx.typeck_results().expr_ty_adjusted(&args[0]);
+ let receiver_type = cx.typeck_results().expr_ty_adjusted(receiver);
let closure_args = if is_type_diagnostic_item(cx, receiver_type, sym::Option) {
"||"
} else if is_type_diagnostic_item(cx, receiver_type, sym::Result) {
return;
};
- let arg_root = get_arg_root(cx, &args[1]);
+ let arg_root = get_arg_root(cx, &args[0]);
let span_replace_word = method_span.with_hi(expr.span.hi());
if_chain! {
if is_type_diagnostic_item(cx, ty, sym::Vec);
//check source object
- if let ExprKind::MethodCall(src_method, [drain_vec, drain_arg], _) = &arg.kind;
+ if let ExprKind::MethodCall(src_method, drain_vec, [drain_arg], _) = &arg.kind;
if src_method.ident.as_str() == "drain";
let src_ty = cx.typeck_results().expr_ty(drain_vec);
//check if actual src type is mutable for code suggestion
let closure_expr = peel_blocks(&body.value);
let arg_id = body.params[0].pat.hir_id;
match closure_expr.kind {
- hir::ExprKind::MethodCall(hir::PathSegment { ident, .. }, args, _) => {
+ hir::ExprKind::MethodCall(hir::PathSegment { ident, .. }, receiver, ..) => {
if_chain! {
if ident.name == method_name;
- if let hir::ExprKind::Path(path) = &args[0].kind;
- if let Res::Local(ref local) = cx.qpath_res(path, args[0].hir_id);
+ if let hir::ExprKind::Path(path) = &receiver.kind;
+ if let Res::Local(ref local) = cx.qpath_res(path, receiver.hir_id);
then {
return arg_id == *local
}
};
// closure ends with is_some() or is_ok()
if let PatKind::Binding(_, filter_param_id, _, None) = filter_pat.kind;
- if let ExprKind::MethodCall(path, [filter_arg], _) = filter_body.value.kind;
+ if let ExprKind::MethodCall(path, filter_arg, [], _) = filter_body.value.kind;
if let Some(opt_ty) = cx.typeck_results().expr_ty(filter_arg).peel_refs().ty_adt_def();
if let Some(is_result) = if cx.tcx.is_diagnostic_item(sym::Option, opt_ty.did()) {
Some(false)
if let [map_param] = map_body.params;
if let PatKind::Binding(_, map_param_id, map_param_ident, None) = map_param.pat.kind;
// closure ends with expect() or unwrap()
- if let ExprKind::MethodCall(seg, [map_arg, ..], _) = map_body.value.kind;
+ if let ExprKind::MethodCall(seg, map_arg, ..) = map_body.value.kind;
if matches!(seg.ident.name, sym::expect | sym::unwrap | sym::unwrap_or);
// .filter(..).map(|y| f(y).copied().unwrap())
// ~~~~
let map_arg_peeled = match map_arg.kind {
- ExprKind::MethodCall(method, [original_arg], _) if acceptable_methods(method) => {
+ ExprKind::MethodCall(method, original_arg, [], _) if acceptable_methods(method) => {
original_arg
},
_ => map_arg,
) = arg.kind
// LHS of subtraction is "x.len()"
- && let ExprKind::MethodCall(lhs_path, [lhs_recv], _) = &lhs.kind
+ && let ExprKind::MethodCall(lhs_path, lhs_recv, [], _) = &lhs.kind
&& lhs_path.ident.name == sym::len
// RHS of subtraction is 1
use super::INEFFICIENT_TO_STRING;
/// Checks for the `INEFFICIENT_TO_STRING` lint
-pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, method_name: Symbol, args: &[hir::Expr<'_>]) {
+pub fn check<'tcx>(
+ cx: &LateContext<'tcx>,
+ expr: &hir::Expr<'_>,
+ method_name: Symbol,
+ receiver: &hir::Expr<'_>,
+ args: &[hir::Expr<'_>],
+) {
if_chain! {
- if args.len() == 1 && method_name == sym::to_string;
+ if args.is_empty() && method_name == sym::to_string;
if let Some(to_string_meth_did) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
if match_def_path(cx, to_string_meth_did, &paths::TO_STRING_METHOD);
if let Some(substs) = cx.typeck_results().node_substs_opt(expr.hir_id);
- let arg_ty = cx.typeck_results().expr_ty_adjusted(&args[0]);
+ let arg_ty = cx.typeck_results().expr_ty_adjusted(receiver);
let self_ty = substs.type_at(0);
let (deref_self_ty, deref_count) = walk_ptrs_ty_depth(self_ty);
if deref_count >= 1;
self_ty, deref_self_ty
));
let mut applicability = Applicability::MachineApplicable;
- let arg_snippet = snippet_with_applicability(cx, args[0].span, "..", &mut applicability);
+ let arg_snippet = snippet_with_applicability(cx, receiver.span, "..", &mut applicability);
diag.span_suggestion(
expr.span,
"try dereferencing the receiver",
expr: &hir::Expr<'_>,
method_span: Span,
method_name: Symbol,
- args: &[hir::Expr<'_>],
+ receiver: &hir::Expr<'_>,
) {
- let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0]);
+ let self_ty = cx.typeck_results().expr_ty_adjusted(receiver);
if_chain! {
if let ty::Ref(..) = self_ty.kind();
if method_name == sym::into_iter;
&& range.end.map_or(true, |e| {
if range.limits == RangeLimits::HalfOpen
&& let ExprKind::Path(QPath::Resolved(None, container_path)) = container.kind
- && let ExprKind::MethodCall(name, [self_arg], _) = e.kind
+ && let ExprKind::MethodCall(name, self_arg, [], _) = e.kind
&& name.ident.name == sym::len
&& let ExprKind::Path(QPath::Resolved(None, path)) = self_arg.kind
{
}
}
},
- hir::ExprKind::MethodCall(method, [obj], _) => if_chain! {
+ hir::ExprKind::MethodCall(method, obj, [], _) => if_chain! {
if ident_eq(name, obj) && method.ident.name == sym::clone;
if let Some(fn_id) = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id);
if let Some(trait_id) = cx.tcx.trait_of_item(fn_id);
]);
/// Extracts a method call name, args, and `Span` of the method name.
-fn method_call<'tcx>(recv: &'tcx hir::Expr<'tcx>) -> Option<(&'tcx str, &'tcx [hir::Expr<'tcx>], Span)> {
- if let ExprKind::MethodCall(path, args, _) = recv.kind {
- if !args.iter().any(|e| e.span.from_expansion()) {
+fn method_call<'tcx>(
+ recv: &'tcx hir::Expr<'tcx>,
+) -> Option<(&'tcx str, &'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>], Span)> {
+ if let ExprKind::MethodCall(path, receiver, args, _) = recv.kind {
+ if !args.iter().any(|e| e.span.from_expansion()) && !receiver.span.from_expansion() {
let name = path.ident.name.as_str();
- return Some((name, args, path.ident.span));
+ return Some((name, receiver, args, path.ident.span));
}
}
None
hir::ExprKind::Call(func, args) => {
from_iter_instead_of_collect::check(cx, expr, args, func);
},
- hir::ExprKind::MethodCall(method_call, args, _) => {
+ hir::ExprKind::MethodCall(method_call, receiver, args, _) => {
let method_span = method_call.ident.span;
- or_fun_call::check(cx, expr, method_span, method_call.ident.as_str(), args);
- expect_fun_call::check(cx, expr, method_span, method_call.ident.as_str(), args);
- clone_on_copy::check(cx, expr, method_call.ident.name, args);
- clone_on_ref_ptr::check(cx, expr, method_call.ident.name, args);
- inefficient_to_string::check(cx, expr, method_call.ident.name, args);
- single_char_add_str::check(cx, expr, args);
- into_iter_on_ref::check(cx, expr, method_span, method_call.ident.name, args);
- single_char_pattern::check(cx, expr, method_call.ident.name, args);
- unnecessary_to_owned::check(cx, expr, method_call.ident.name, args, self.msrv);
+ or_fun_call::check(cx, expr, method_span, method_call.ident.as_str(), receiver, args);
+ expect_fun_call::check(cx, expr, method_span, method_call.ident.as_str(), receiver, args);
+ clone_on_copy::check(cx, expr, method_call.ident.name, receiver, args);
+ clone_on_ref_ptr::check(cx, expr, method_call.ident.name, receiver, args);
+ inefficient_to_string::check(cx, expr, method_call.ident.name, receiver, args);
+ single_char_add_str::check(cx, expr, receiver, args);
+ into_iter_on_ref::check(cx, expr, method_span, method_call.ident.name, receiver);
+ single_char_pattern::check(cx, expr, method_call.ident.name, receiver, args);
+ unnecessary_to_owned::check(cx, expr, method_call.ident.name, receiver, args, self.msrv);
},
hir::ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => {
let mut info = BinaryExprInfo {
impl Methods {
#[allow(clippy::too_many_lines)]
fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- if let Some((name, [recv, args @ ..], span)) = method_call(expr) {
+ if let Some((name, recv, args, span)) = method_call(expr) {
match (name, args) {
("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => {
zst_offset::check(cx, expr, recv);
("assume_init", []) => uninit_assumed_init::check(cx, expr, recv),
("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, self.msrv),
("collect", []) => match method_call(recv) {
- Some((name @ ("cloned" | "copied"), [recv2], _)) => {
+ Some((name @ ("cloned" | "copied"), recv2, [], _)) => {
iter_cloned_collect::check(cx, name, expr, recv2);
},
- Some(("map", [m_recv, m_arg], _)) => {
+ Some(("map", m_recv, [m_arg], _)) => {
map_collect_result_unit::check(cx, expr, m_recv, m_arg, recv);
},
- Some(("take", [take_self_arg, take_arg], _)) => {
+ Some(("take", take_self_arg, [take_arg], _)) => {
if meets_msrv(self.msrv, msrvs::STR_REPEAT) {
manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg);
}
_ => {},
},
("count", []) if is_trait_method(cx, expr, sym::Iterator) => match method_call(recv) {
- Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, true, false),
- Some((name2 @ ("into_iter" | "iter" | "iter_mut"), [recv2], _)) => {
+ Some(("cloned", recv2, [], _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, true, false),
+ Some((name2 @ ("into_iter" | "iter" | "iter_mut"), recv2, [], _)) => {
iter_count::check(cx, expr, recv2, name2);
},
- Some(("map", [_, arg], _)) => suspicious_map::check(cx, expr, recv, arg),
- Some(("filter", [recv2, arg], _)) => bytecount::check(cx, expr, recv2, arg),
- Some(("bytes", [recv2], _)) => bytes_count_to_len::check(cx, expr, recv, recv2),
+ Some(("map", _, [arg], _)) => suspicious_map::check(cx, expr, recv, arg),
+ Some(("filter", recv2, [arg], _)) => bytecount::check(cx, expr, recv2, arg),
+ Some(("bytes", recv2, [], _)) => bytes_count_to_len::check(cx, expr, recv, recv2),
_ => {},
},
("drain", [arg]) => {
iter_with_drain::check(cx, expr, recv, span, arg);
},
("ends_with", [arg]) => {
- if let ExprKind::MethodCall(_, _, span) = expr.kind {
+ if let ExprKind::MethodCall(.., span) = expr.kind {
case_sensitive_file_extension_comparisons::check(cx, expr, span, recv, arg);
}
},
("expect", [_]) => match method_call(recv) {
- Some(("ok", [recv], _)) => ok_expect::check(cx, expr, recv),
- Some(("err", [recv], err_span)) => err_expect::check(cx, expr, recv, self.msrv, span, err_span),
+ Some(("ok", recv, [], _)) => ok_expect::check(cx, expr, recv),
+ Some(("err", recv, [], err_span)) => err_expect::check(cx, expr, recv, self.msrv, span, err_span),
_ => expect_used::check(cx, expr, recv, false, self.allow_expect_in_tests),
},
("expect_err", [_]) => expect_used::check(cx, expr, recv, true, self.allow_expect_in_tests),
flat_map_option::check(cx, expr, arg, span);
},
("flatten", []) => match method_call(recv) {
- Some(("map", [recv, map_arg], map_span)) => map_flatten::check(cx, expr, recv, map_arg, map_span),
- Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, true),
+ Some(("map", recv, [map_arg], map_span)) => map_flatten::check(cx, expr, recv, map_arg, map_span),
+ Some(("cloned", recv2, [], _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, true),
_ => {},
},
("fold", [init, acc]) => unnecessary_fold::check(cx, expr, init, acc, span),
("for_each", [_]) => {
- if let Some(("inspect", [_, _], span2)) = method_call(recv) {
+ if let Some(("inspect", _, [_], span2)) = method_call(recv) {
inspect_for_each::check(cx, expr, span2);
}
},
iter_on_single_or_empty_collections::check(cx, expr, name, recv);
},
("join", [join_arg]) => {
- if let Some(("collect", _, span)) = method_call(recv) {
+ if let Some(("collect", _, _, span)) = method_call(recv) {
unnecessary_join::check(cx, expr, recv, join_arg, span);
}
},
("last", []) | ("skip", [_]) => {
- if let Some((name2, [recv2, args2 @ ..], _span2)) = method_call(recv) {
+ if let Some((name2, recv2, args2, _span2)) = method_call(recv) {
if let ("cloned", []) = (name2, args2) {
iter_overeager_cloned::check(cx, expr, recv, recv2, false, false);
}
} else {
map_err_ignore::check(cx, expr, m_arg);
}
- if let Some((name, [recv2, args @ ..], span2)) = method_call(recv) {
+ if let Some((name, recv2, args, span2)) = method_call(recv) {
match (name, args) {
("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, self.msrv),
("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, self.msrv),
manual_ok_or::check(cx, expr, recv, def, map);
},
("next", []) => {
- if let Some((name2, [recv2, args2 @ ..], _)) = method_call(recv) {
+ if let Some((name2, recv2, args2, _)) = method_call(recv) {
match (name2, args2) {
("cloned", []) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, false),
("filter", [arg]) => filter_next::check(cx, expr, recv2, arg),
}
},
("nth", [n_arg]) => match method_call(recv) {
- Some(("bytes", [recv2], _)) => bytes_nth::check(cx, expr, recv2, n_arg),
- Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, false),
- Some(("iter", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false),
- Some(("iter_mut", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true),
+ Some(("bytes", recv2, [], _)) => bytes_nth::check(cx, expr, recv2, n_arg),
+ Some(("cloned", recv2, [], _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, false),
+ Some(("iter", recv2, [], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false),
+ Some(("iter_mut", recv2, [], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true),
_ => iter_nth_zero::check(cx, expr, recv, n_arg),
},
("ok_or_else", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"),
},
("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg),
("take", [_arg]) => {
- if let Some((name2, [recv2, args2 @ ..], _span2)) = method_call(recv) {
+ if let Some((name2, recv2, args2, _span2)) = method_call(recv) {
if let ("cloned", []) = (name2, args2) {
iter_overeager_cloned::check(cx, expr, recv, recv2, false, false);
}
},
("unwrap", []) => {
match method_call(recv) {
- Some(("get", [recv, get_arg], _)) => {
+ Some(("get", recv, [get_arg], _)) => {
get_unwrap::check(cx, expr, recv, get_arg, false);
},
- Some(("get_mut", [recv, get_arg], _)) => {
+ Some(("get_mut", recv, [get_arg], _)) => {
get_unwrap::check(cx, expr, recv, get_arg, true);
},
- Some(("or", [recv, or_arg], or_span)) => {
+ Some(("or", recv, [or_arg], or_span)) => {
or_then_unwrap::check(cx, expr, recv, or_arg, or_span);
},
_ => {},
},
("unwrap_err", []) => unwrap_used::check(cx, expr, recv, true, self.allow_unwrap_in_tests),
("unwrap_or", [u_arg]) => match method_call(recv) {
- Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), [lhs, rhs], _)) => {
+ Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), lhs, [rhs], _)) => {
manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, &arith["checked_".len()..]);
},
- Some(("map", [m_recv, m_arg], span)) => {
+ Some(("map", m_recv, [m_arg], span)) => {
option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span);
},
- Some(("then_some", [t_recv, t_arg], _)) => {
+ Some(("then_some", t_recv, [t_arg], _)) => {
obfuscated_if_else::check(cx, expr, t_recv, t_arg, u_arg);
},
_ => {},
},
("unwrap_or_else", [u_arg]) => match method_call(recv) {
- Some(("map", [recv, map_arg], _))
+ Some(("map", recv, [map_arg], _))
if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, self.msrv) => {},
_ => {
unwrap_or_else_default::check(cx, expr, recv, u_arg);
},
},
("zip", [arg]) => {
- if let ExprKind::MethodCall(name, [iter_recv], _) = recv.kind
+ if let ExprKind::MethodCall(name, iter_recv, [], _) = recv.kind
&& name.ident.name == sym::iter
{
range_zip_with_len::check(cx, expr, iter_recv, arg);
}
fn check_is_some_is_none(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, is_some: bool) {
- if let Some((name @ ("find" | "position" | "rposition"), [f_recv, arg], span)) = method_call(recv) {
+ if let Some((name @ ("find" | "position" | "rposition"), f_recv, [arg], span)) = method_call(recv) {
search_is_some::check(cx, expr, name, is_some, f_recv, arg, recv, span);
}
}
}
fn get_open_options(cx: &LateContext<'_>, argument: &Expr<'_>, options: &mut Vec<(OpenOption, Argument)>) {
- if let ExprKind::MethodCall(path, arguments, _) = argument.kind {
- let obj_ty = cx.typeck_results().expr_ty(&arguments[0]).peel_refs();
+ if let ExprKind::MethodCall(path, receiver, arguments, _) = argument.kind {
+ let obj_ty = cx.typeck_results().expr_ty(receiver).peel_refs();
// Only proceed if this is a call on some object of type std::fs::OpenOptions
- if match_type(cx, obj_ty, &paths::OPEN_OPTIONS) && arguments.len() >= 2 {
- let argument_option = match arguments[1].kind {
+ if match_type(cx, obj_ty, &paths::OPEN_OPTIONS) && arguments.len() >= 1 {
+ let argument_option = match arguments[0].kind {
ExprKind::Lit(ref span) => {
if let Spanned {
node: LitKind::Bool(lit),
_ => (),
}
- get_open_options(cx, &arguments[0], options);
+ get_open_options(cx, receiver, options);
}
}
}
let closure_expr = peel_blocks(&closure_body.value);
match &closure_expr.kind {
- hir::ExprKind::MethodCall(_, args, _) => {
+ hir::ExprKind::MethodCall(_, receiver, [], _) => {
if_chain! {
- if args.len() == 1;
- if path_to_local_id(&args[0], closure_body.params[0].pat.hir_id);
+ if path_to_local_id(receiver, closure_body.params[0].pat.hir_id);
let adj = cx
.typeck_results()
- .expr_adjustments(&args[0])
+ .expr_adjustments(receiver)
.iter()
.map(|x| &x.kind)
.collect::<Box<[_]>>();
expr: &hir::Expr<'_>,
method_span: Span,
name: &str,
+ receiver: &'tcx hir::Expr<'_>,
args: &'tcx [hir::Expr<'_>],
) {
/// Checks for `unwrap_or(T::new())` or `unwrap_or(T::default())`.
}
}
- if let [self_arg, arg] = args {
+ if let [arg] = args {
let inner_arg = if let hir::ExprKind::Block(
hir::Block {
stmts: [],
let or_has_args = !or_args.is_empty();
if !check_unwrap_or_default(cx, name, fun, arg, or_has_args, expr.span, method_span) {
let fun_span = if or_has_args { None } else { Some(fun.span) };
- check_general_case(cx, name, method_span, self_arg, arg, expr.span, fun_span);
+ check_general_case(cx, name, method_span, receiver, arg, expr.span, fun_span);
}
},
hir::ExprKind::Index(..) | hir::ExprKind::MethodCall(..) => {
- check_general_case(cx, name, method_span, self_arg, arg, expr.span, None);
+ check_general_case(cx, name, method_span, receiver, arg, expr.span, None);
},
_ => (),
}
if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::Range::hir(zip_arg);
if is_integer_const(cx, start, 0);
// `.len()` call
- if let ExprKind::MethodCall(len_path, [len_recv], _) = end.kind;
+ if let ExprKind::MethodCall(len_path, len_recv, [], _) = end.kind;
if len_path.ident.name == sym::len;
// `.iter()` and `.len()` called on same `Path`
if let ExprKind::Path(QPath::Resolved(_, iter_path)) = recv.kind;
use rustc_hir as hir;
use rustc_lint::LateContext;
-pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
+pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) {
if match_def_path(cx, fn_def_id, &paths::PUSH_STR) {
- single_char_push_string::check(cx, expr, args);
+ single_char_push_string::check(cx, expr, receiver, args);
} else if match_def_path(cx, fn_def_id, &paths::INSERT_STR) {
- single_char_insert_string::check(cx, expr, args);
+ single_char_insert_string::check(cx, expr, receiver, args);
}
}
}
use super::SINGLE_CHAR_ADD_STR;
/// lint for length-1 `str`s as argument for `insert_str`
-pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
+pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
let mut applicability = Applicability::MachineApplicable;
- if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[2], &mut applicability) {
+ if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[1], &mut applicability) {
let base_string_snippet =
- snippet_with_applicability(cx, args[0].span.source_callsite(), "_", &mut applicability);
- let pos_arg = snippet_with_applicability(cx, args[1].span, "..", &mut applicability);
+ snippet_with_applicability(cx, receiver.span.source_callsite(), "_", &mut applicability);
+ let pos_arg = snippet_with_applicability(cx, args[0].span, "..", &mut applicability);
let sugg = format!("{}.insert({}, {})", base_string_snippet, pos_arg, extension_string);
span_lint_and_sugg(
cx,
use super::SINGLE_CHAR_PATTERN;
const PATTERN_METHODS: [(&str, usize); 24] = [
- ("contains", 1),
- ("starts_with", 1),
- ("ends_with", 1),
- ("find", 1),
- ("rfind", 1),
- ("split", 1),
- ("split_inclusive", 1),
- ("rsplit", 1),
- ("split_terminator", 1),
- ("rsplit_terminator", 1),
- ("splitn", 2),
- ("rsplitn", 2),
- ("split_once", 1),
- ("rsplit_once", 1),
- ("matches", 1),
- ("rmatches", 1),
- ("match_indices", 1),
- ("rmatch_indices", 1),
- ("strip_prefix", 1),
- ("strip_suffix", 1),
- ("trim_start_matches", 1),
- ("trim_end_matches", 1),
- ("replace", 1),
- ("replacen", 1),
+ ("contains", 0),
+ ("starts_with", 0),
+ ("ends_with", 0),
+ ("find", 0),
+ ("rfind", 0),
+ ("split", 0),
+ ("split_inclusive", 0),
+ ("rsplit", 0),
+ ("split_terminator", 0),
+ ("rsplit_terminator", 0),
+ ("splitn", 1),
+ ("rsplitn", 1),
+ ("split_once", 0),
+ ("rsplit_once", 0),
+ ("matches", 0),
+ ("rmatches", 0),
+ ("match_indices", 0),
+ ("rmatch_indices", 0),
+ ("strip_prefix", 0),
+ ("strip_suffix", 0),
+ ("trim_start_matches", 0),
+ ("trim_end_matches", 0),
+ ("replace", 0),
+ ("replacen", 0),
];
/// lint for length-1 `str`s for methods in `PATTERN_METHODS`
-pub(super) fn check(cx: &LateContext<'_>, _expr: &hir::Expr<'_>, method_name: Symbol, args: &[hir::Expr<'_>]) {
+pub(super) fn check(
+ cx: &LateContext<'_>,
+ _expr: &hir::Expr<'_>,
+ method_name: Symbol,
+ receiver: &hir::Expr<'_>,
+ args: &[hir::Expr<'_>],
+) {
for &(method, pos) in &PATTERN_METHODS {
if_chain! {
- if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(&args[0]).kind();
+ if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(receiver).kind();
if *ty.kind() == ty::Str;
if method_name.as_str() == method && args.len() > pos;
let arg = &args[pos];
use super::SINGLE_CHAR_ADD_STR;
/// lint for length-1 `str`s as argument for `push_str`
-pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
+pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
let mut applicability = Applicability::MachineApplicable;
- if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[1], &mut applicability) {
+ if let Some(extension_string) = get_hint_if_single_char_arg(cx, &args[0], &mut applicability) {
let base_string_snippet =
- snippet_with_applicability(cx, args[0].span.source_callsite(), "..", &mut applicability);
+ snippet_with_applicability(cx, receiver.span.source_callsite(), "..", &mut applicability);
let sugg = format!("{}.push({})", base_string_snippet, extension_string);
span_lint_and_sugg(
cx,
) -> Option<IterUsage> {
let (kind, span) = match iter.next() {
Some((_, Node::Expr(e))) if e.span.ctxt() == ctxt => {
- let (name, args) = if let ExprKind::MethodCall(name, [_, args @ ..], _) = e.kind {
+ let (name, args) = if let ExprKind::MethodCall(name, _, [args @ ..], _) = e.kind {
(name, args)
} else {
return None;
} else {
if_chain! {
if let Some((_, Node::Expr(next_expr))) = iter.next();
- if let ExprKind::MethodCall(next_name, [_], _) = next_expr.kind;
+ if let ExprKind::MethodCall(next_name, _, [], _) = next_expr.kind;
if next_name.ident.name == sym::next;
if next_expr.span.ctxt() == ctxt;
if let Some(next_id) = cx.typeck_results().type_dependent_def_id(next_expr.hir_id);
}
},
_ if e.span.ctxt() != ctxt => (None, span),
- ExprKind::MethodCall(name, [_], _)
+ ExprKind::MethodCall(name, _, [], _)
if name.ident.name == sym::unwrap
&& cx
.typeck_results()
return;
}
if let Some(arglists) = method_chain_args(arg, &["chars"]) {
- let target = &arglists[0][0];
+ let target = &arglists[0].0;
let self_ty = cx.typeck_results().expr_ty(target).peel_refs();
let ref_str = if *self_ty.kind() == ty::Str {
""
if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
then {
let snippet = if_chain! {
- if let ExprKind::MethodCall(maybe_iter_method_name, [collection], _) = receiver.kind;
+ if let ExprKind::MethodCall(maybe_iter_method_name, collection, [], _) = receiver.kind;
if maybe_iter_method_name.ident.name == sym::iter;
if let Some(iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
// This is a duplicate of what's happening in clippy_lints::methods::method_call,
// which isn't ideal, We want to get the method call span,
// but prefer to avoid changing the signature of the function itself.
- if let hir::ExprKind::MethodCall(_, _, span) = expr.kind {
+ if let hir::ExprKind::MethodCall(.., span) = expr.kind {
span_lint_and_then(cx, UNNECESSARY_LAZY_EVALUATIONS, expr.span, msg, |diag| {
diag.span_suggestion(
span,
// The two exprs are method calls.
// Check to see that the function is the same and the arguments are mirrored
// This is enough because the receiver of the method is listed in the arguments
- (ExprKind::MethodCall(left_segment, left_args, _), ExprKind::MethodCall(right_segment, right_args, _)) => {
+ (
+ ExprKind::MethodCall(left_segment, left_receiver, left_args, _),
+ ExprKind::MethodCall(right_segment, right_receiver, right_args, _),
+ ) => {
left_segment.ident == right_segment.ident
&& iter::zip(*left_args, *right_args).all(|(left, right)| mirrored_exprs(left, a_ident, right, b_ident))
+ && mirrored_exprs(left_receiver, a_ident, right_receiver, b_ident)
},
// Two tuples with mirrored contents
(ExprKind::Tup(left_exprs), ExprKind::Tup(right_exprs)) => {
Param { pat: Pat { kind: PatKind::Binding(_, _, left_ident, _), .. }, ..},
Param { pat: Pat { kind: PatKind::Binding(_, _, right_ident, _), .. }, .. }
] = &closure_body.params;
- if let ExprKind::MethodCall(method_path, [left_expr, right_expr], _) = closure_body.value.kind;
+ if let ExprKind::MethodCall(method_path, left_expr, [right_expr], _) = closure_body.value.kind;
if method_path.ident.name == sym::cmp;
if is_trait_method(cx, &closure_body.value, sym::Ord);
then {
cx: &LateContext<'tcx>,
expr: &'tcx Expr<'tcx>,
method_name: Symbol,
- args: &'tcx [Expr<'tcx>],
+ receiver: &'tcx Expr<'_>,
+ args: &'tcx [Expr<'_>],
msrv: Option<RustcVersion>,
) {
if_chain! {
if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
- if let [receiver] = args;
+ if args.is_empty();
then {
if is_cloned_or_copied(cx, method_name, method_def_id) {
unnecessary_iter_cloned::check(cx, expr, method_name, receiver);
) -> bool {
if_chain! {
if let Some((maybe_call, maybe_arg)) = skip_addr_of_ancestors(cx, expr);
- if let Some((callee_def_id, call_substs, call_args)) = get_callee_substs_and_args(cx, maybe_call);
+ if let Some((callee_def_id, call_substs, call_receiver, call_args)) = get_callee_substs_and_args(cx, maybe_call);
let fn_sig = cx.tcx.fn_sig(callee_def_id).skip_binder();
- if let Some(i) = call_args.iter().position(|arg| arg.hir_id == maybe_arg.hir_id);
+ let index = if let Some(call_receiver) = call_receiver {
+ std::iter::once(call_receiver).chain(call_args.iter()).position(|arg| arg.hir_id == maybe_arg.hir_id)
+ } else {
+ call_args.iter().position(|arg| arg.hir_id == maybe_arg.hir_id)
+ };
+ if let Some(i) = index;
if let Some(input) = fn_sig.inputs().get(i);
let (input, n_refs) = peel_mid_ty_refs(*input);
if let (trait_predicates, projection_predicates) = get_input_traits_and_projections(cx, callee_def_id, input);
fn get_callee_substs_and_args<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx Expr<'tcx>,
-) -> Option<(DefId, SubstsRef<'tcx>, &'tcx [Expr<'tcx>])> {
+) -> Option<(DefId, SubstsRef<'tcx>, Option<&'tcx Expr<'tcx>>, &'tcx [Expr<'tcx>])> {
if_chain! {
if let ExprKind::Call(callee, args) = expr.kind;
let callee_ty = cx.typeck_results().expr_ty(callee);
if let ty::FnDef(callee_def_id, _) = callee_ty.kind();
then {
let substs = cx.typeck_results().node_substs(callee.hir_id);
- return Some((*callee_def_id, substs, args));
+ return Some((*callee_def_id, substs, None, args));
}
}
if_chain! {
- if let ExprKind::MethodCall(_, args, _) = expr.kind;
+ if let ExprKind::MethodCall(_, receiver, args, _) = expr.kind;
if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
then {
let substs = cx.typeck_results().node_substs(expr.hir_id);
- return Some((method_def_id, substs, args));
+ return Some((method_def_id, substs, Some(receiver), args));
}
}
None
}
}
- if let hir::ExprKind::MethodCall(path, [self_arg, ..], _) = &expr.kind {
+ if let hir::ExprKind::MethodCall(path, self_arg, ..) = &expr.kind {
if path.ident.name == sym::iter && may_slice(cx, cx.typeck_results().expr_ty(self_arg)) {
Some(self_arg)
} else {
self.addr_of_exprs.push(parent);
return;
},
- ExprKind::MethodCall(_, args, _) => {
+ ExprKind::MethodCall(.., args, _) => {
if_chain! {
- if args.iter().skip(1).all(|arg| !self.is_binding(arg));
+ if args.iter().all(|arg| !self.is_binding(arg));
if let Some(method_def_id) = self.cx.typeck_results().type_dependent_def_id(parent.hir_id);
let method_ty = self.cx.tcx.type_of(method_def_id);
let self_ty = method_ty.fn_sig(self.cx.tcx).input(0).skip_binder();
.qpath_res(qpath, path.hir_id)
.opt_def_id()
.and_then(|def_id| match cx.tcx.get_diagnostic_name(def_id) {
- Some(sym::cmp_min) => fetch_const(cx, args, MinMax::Min),
- Some(sym::cmp_max) => fetch_const(cx, args, MinMax::Max),
+ Some(sym::cmp_min) => fetch_const(cx, None, args, MinMax::Min),
+ Some(sym::cmp_max) => fetch_const(cx, None, args, MinMax::Max),
_ => None,
})
} else {
None
}
},
- ExprKind::MethodCall(path, args, _) => {
+ ExprKind::MethodCall(path, receiver, args @ [_], _) => {
if_chain! {
- if let [obj, _] = args;
- if cx.typeck_results().expr_ty(obj).is_floating_point() || match_trait_method(cx, expr, &paths::ORD);
+ if cx.typeck_results().expr_ty(receiver).is_floating_point() || match_trait_method(cx, expr, &paths::ORD);
then {
if path.ident.name == sym!(max) {
- fetch_const(cx, args, MinMax::Max)
+ fetch_const(cx, Some(receiver), args, MinMax::Max)
} else if path.ident.name == sym!(min) {
- fetch_const(cx, args, MinMax::Min)
+ fetch_const(cx, Some(receiver), args, MinMax::Min)
} else {
None
}
}
}
-fn fetch_const<'a>(cx: &LateContext<'_>, args: &'a [Expr<'a>], m: MinMax) -> Option<(MinMax, Constant, &'a Expr<'a>)> {
- if args.len() != 2 {
+fn fetch_const<'a>(
+ cx: &LateContext<'_>,
+ receiver: Option<&'a Expr<'a>>,
+ args: &'a [Expr<'a>],
+ m: MinMax,
+) -> Option<(MinMax, Constant, &'a Expr<'a>)> {
+ let mut args = receiver.into_iter().chain(args.into_iter());
+ let arg0 = args.next()?;
+ let arg1 = args.next()?;
+ if args.next().is_some() {
return None;
}
- constant_simple(cx, cx.typeck_results(), &args[0]).map_or_else(
- || constant_simple(cx, cx.typeck_results(), &args[1]).map(|c| (m, c, &args[0])),
+ constant_simple(cx, cx.typeck_results(), arg0).map_or_else(
+ || constant_simple(cx, cx.typeck_results(), arg1).map(|c| (m, c, arg0)),
|c| {
- if constant_simple(cx, cx.typeck_results(), &args[1]).is_none() {
+ if constant_simple(cx, cx.typeck_results(), arg1).is_none() {
// otherwise ignore
- Some((m, c, &args[1]))
+ Some((m, c, arg1))
} else {
None
}
if let ExprKind::Path(ref path) = fn_expr.kind {
check_arguments(
cx,
- arguments,
+ arguments.iter().collect(),
cx.typeck_results().expr_ty(fn_expr),
&rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false)),
"function",
);
}
},
- ExprKind::MethodCall(path, arguments, _) => {
+ ExprKind::MethodCall(path, receiver, arguments, _) => {
let def_id = cx.typeck_results().type_dependent_def_id(e.hir_id).unwrap();
let substs = cx.typeck_results().node_substs(e.hir_id);
let method_type = cx.tcx.bound_type_of(def_id).subst(cx.tcx, substs);
- check_arguments(cx, arguments, method_type, path.ident.as_str(), "method");
+ check_arguments(
+ cx,
+ std::iter::once(receiver).chain(arguments.iter()).collect(),
+ method_type,
+ path.ident.as_str(),
+ "method",
+ );
},
_ => (),
}
fn check_arguments<'tcx>(
cx: &LateContext<'tcx>,
- arguments: &[Expr<'_>],
+ arguments: Vec<&Expr<'_>>,
type_definition: Ty<'tcx>,
name: &str,
fn_kind: &str,
if_chain! {
// Check the method name is `for_each`.
- if let ExprKind::MethodCall(method_name, [for_each_recv, for_each_arg], _) = expr.kind;
+ if let ExprKind::MethodCall(method_name, for_each_recv, [for_each_arg], _) = expr.kind;
if method_name.ident.name == Symbol::intern("for_each");
// Check `for_each` is an associated function of `Iterator`.
if is_trait_method(cx, expr, sym::Iterator);
// Checks the receiver of `for_each` is also a method call.
- if let ExprKind::MethodCall(_, [iter_recv], _) = for_each_recv.kind;
+ if let ExprKind::MethodCall(_, iter_recv, [], _) = for_each_recv.kind;
// Skip the lint if the call chain is too long. e.g. `v.field.iter().for_each()` or
// `v.foo().iter().for_each()` must be skipped.
if matches!(
impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
match &expr.kind {
- ExprKind::MethodCall(path, [func, param], _) => {
+ ExprKind::MethodCall(path, func, [param], _) => {
let obj_ty = cx.typeck_results().expr_ty(func).peel_refs();
if_chain! {
}
return;
},
- ExprKind::MethodCall(_, args, _)
+ ExprKind::MethodCall(_, receiver, args, _)
if typeck.type_dependent_def_id(parent.hir_id).map_or(false, |id| {
id == param.fn_id
&& has_matching_substs(param.fn_kind, typeck.node_substs(parent.hir_id))
}) =>
{
- if let Some(idx) = args.iter().position(|arg| arg.hir_id == child_id) {
+ if let Some(idx) = std::iter::once(receiver).chain(args.iter()).position(|arg| arg.hir_id == child_id) {
param.uses.push(Usage::new(span, idx));
}
return;
fn check_op(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) {
let typeck = cx.typeck_results();
let (arg, arg_span) = match expr.kind {
- ExprKind::MethodCall(.., [arg], _)
+ ExprKind::MethodCall(_, arg, [], _)
if typeck
.type_dependent_def_id(expr.hir_id)
.and_then(|id| cx.tcx.trait_of_item(id))
right: &'tcx Expr<'_>,
) {
if op == BinOpKind::Div
- && let ExprKind::MethodCall(method_path, [self_arg], _) = left.kind
+ && let ExprKind::MethodCall(method_path, self_arg, [], _) = left.kind
&& is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_arg).peel_refs(), sym::Duration)
&& let Some((Constant::Int(divisor), _)) = constant(cx, cx.typeck_results(), right)
{
}
if_chain! {
- if let ExprKind::MethodCall(method_name, [ref self_arg, ..], _) = expr.kind;
+ if let ExprKind::MethodCall(method_name, self_arg, ..) = expr.kind;
if sym!(signum) == method_name.ident.name;
// Check that the receiver of the signum() is a float (expressions[0] is the receiver of
// the method call)
if let ItemKind::Impl(item) = &item.kind;
if let Some(of_trait) = &item.of_trait;
if let Some(seg) = of_trait.path.segments.last();
- if let Some(Res::Def(_, trait_id)) = seg.res;
+ if let Res::Def(_, trait_id) = seg.res;
if trait_id == bin_op;
if let Some(generic_args) = seg.args;
if let Some(GenericArg::Type(other_ty)) = generic_args.args.last();
set_skip_flag();
}
},
- ExprKind::MethodCall(name, expr_args @ [self_arg, ..], _) => {
- let i = expr_args.iter().position(|arg| arg.hir_id == child_id).unwrap_or(0);
+ ExprKind::MethodCall(name, self_arg, expr_args, _) => {
+ let i = std::iter::once(self_arg)
+ .chain(expr_args.iter())
+ .position(|arg| arg.hir_id == child_id)
+ .unwrap_or(0);
if i == 0 {
// Check if the method can be renamed.
let name = name.ident.as_str();
cx: &LateContext<'tcx>,
expr: &'tcx Expr<'_>,
) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Method)> {
- if let ExprKind::MethodCall(path_segment, [arg_0, arg_1, ..], _) = &expr.kind {
+ if let ExprKind::MethodCall(path_segment, arg_0, [arg_1, ..], _) = &expr.kind {
if is_expr_ty_raw_ptr(cx, arg_0) {
if path_segment.ident.name == sym::offset {
return Some((arg_0, arg_1, Method::Offset));
if_chain! {
if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr);
if !is_else_clause(cx.tcx, expr);
- if let ExprKind::MethodCall(segment, [caller, ..], _) = &cond.kind;
+ if let ExprKind::MethodCall(segment, caller, ..) = &cond.kind;
let caller_ty = cx.typeck_results().expr_ty(caller);
let if_block = IfBlockType::IfIs(caller, caller_ty, segment.ident.name, then, r#else);
if is_early_return(sym::Option, cx, &if_block) || is_early_return(sym::Result, cx, &if_block);
// finds use of `_.read(&mut v)`
let mut read_found = false;
let mut visitor = expr_visitor_no_bodies(|expr| {
- if let ExprKind::MethodCall(path, [_self, arg], _) = expr.kind
+ if let ExprKind::MethodCall(path, _self, [arg], _) = expr.kind
&& let PathSegment { ident: read_or_read_exact, .. } = *path
&& matches!(read_or_read_exact.as_str(), "read" | "read_exact")
&& let ExprKind::AddrOf(_, hir::Mutability::Mut, inner) = arg.kind
if mut_ty.mutbl == Mutability::Not;
if let TyKind::Path(ref qpath) = &mut_ty.ty.kind;
let last = last_path_segment(qpath);
- if let Some(res) = last.res;
- if let Some(def_id) = res.opt_def_id();
+ if let Some(def_id) = last.res.opt_def_id();
if cx.tcx.is_diagnostic_item(sym::Option, def_id);
if let Some(params) = last_path_segment(qpath).args ;
};
if_chain! {
// Find calls to copy_{from,to}{,_nonoverlapping} and write_bytes methods
- if let ExprKind::MethodCall(method_path, [ptr_self, .., count], _) = expr.kind;
+ if let ExprKind::MethodCall(method_path, ptr_self, [.., count], _) = expr.kind;
let method_ident = method_path.ident.as_str();
if METHODS.iter().any(|m| *m == method_ident);
fn search_slow_extend_filling(&mut self, expr: &'tcx Expr<'_>) {
if_chain! {
if self.initialization_found;
- if let ExprKind::MethodCall(path, [self_arg, extend_arg], _) = expr.kind;
+ if let ExprKind::MethodCall(path, self_arg, [extend_arg], _) = expr.kind;
if path_to_local_id(self_arg, self.vec_alloc.local_id);
if path.ident.name == sym!(extend);
if self.is_repeat_take(extend_arg);
/// Checks if the given expression is resizing a vector with 0
fn search_slow_resize_filling(&mut self, expr: &'tcx Expr<'_>) {
if self.initialization_found
- && let ExprKind::MethodCall(path, [self_arg, len_arg, fill_arg], _) = expr.kind
+ && let ExprKind::MethodCall(path, self_arg, [len_arg, fill_arg], _) = expr.kind
&& path_to_local_id(self_arg, self.vec_alloc.local_id)
&& path.ident.name == sym!(resize)
// Check that is filled with 0
// Check that len expression is equals to `with_capacity` expression
if SpanlessEq::new(self.cx).eq_expr(len_arg, self.vec_alloc.len_expr) {
self.slow_expression = Some(InitializationType::Resize(expr));
- } else if let ExprKind::MethodCall(path, _, _) = len_arg.kind && path.ident.as_str() == "capacity" {
+ } else if let ExprKind::MethodCall(path, ..) = len_arg.kind && path.ident.as_str() == "capacity" {
self.slow_expression = Some(InitializationType::Resize(expr));
}
}
/// Returns `true` if give expression is `repeat(0).take(...)`
fn is_repeat_take(&self, expr: &Expr<'_>) -> bool {
if_chain! {
- if let ExprKind::MethodCall(take_path, [recv, len_arg, ..], _) = expr.kind;
+ if let ExprKind::MethodCall(take_path, recv, [len_arg, ..], _) = expr.kind;
if take_path.ident.name == sym!(take);
// Check that take is applied to `repeat(0)`
if self.is_repeat_zero(recv);
// Check that len expression is equals to `with_capacity` expression
if SpanlessEq::new(self.cx).eq_expr(len_arg, self.vec_alloc.len_expr) {
return true;
- } else if let ExprKind::MethodCall(path, _, _) = len_arg.kind && path.ident.as_str() == "capacity" {
+ } else if let ExprKind::MethodCall(path, ..) = len_arg.kind && path.ident.as_str() == "capacity" {
return true;
}
}
let (method_names, expressions, _) = method_calls(left, 1);
if method_names.len() == 1;
if expressions.len() == 1;
- if expressions[0].len() == 1;
+ if expressions[0].1.is_empty();
if method_names[0] == sym!(as_bytes);
// Check for slicer
then {
let mut applicability = Applicability::MachineApplicable;
- let string_expression = &expressions[0][0];
+ let string_expression = &expressions[0].0;
let snippet_app = snippet_with_applicability(
cx,
}
if_chain! {
- if let ExprKind::MethodCall(path, args, _) = &e.kind;
+ if let ExprKind::MethodCall(path, receiver, ..) = &e.kind;
if path.ident.name == sym!(as_bytes);
- if let ExprKind::Lit(lit) = &args[0].kind;
+ if let ExprKind::Lit(lit) = &receiver.kind;
if let LitKind::Str(lit_content, _) = &lit.node;
then {
- let callsite = snippet(cx, args[0].span.source_callsite(), r#""foo""#);
+ let callsite = snippet(cx, receiver.span.source_callsite(), r#""foo""#);
let mut applicability = Applicability::MachineApplicable;
if callsite.starts_with("include_str!") {
span_lint_and_sugg(
e.span,
"calling `as_bytes()` on `include_str!(..)`",
"consider using `include_bytes!(..)` instead",
- snippet_with_applicability(cx, args[0].span, r#""foo""#, &mut applicability).replacen(
+ snippet_with_applicability(cx, receiver.span, r#""foo""#, &mut applicability).replacen(
"include_str",
"include_bytes",
1,
);
} else if lit_content.as_str().is_ascii()
&& lit_content.as_str().len() <= MAX_LENGTH_BYTE_STRING_LIT
- && !args[0].span.from_expansion()
+ && !receiver.span.from_expansion()
{
span_lint_and_sugg(
cx,
"consider using a byte string literal instead",
format!(
"b{}",
- snippet_with_applicability(cx, args[0].span, r#""foo""#, &mut applicability)
+ snippet_with_applicability(cx, receiver.span, r#""foo""#, &mut applicability)
),
applicability,
);
}
if_chain! {
- if let ExprKind::MethodCall(path, [recv], _) = &e.kind;
+ if let ExprKind::MethodCall(path, recv, [], _) = &e.kind;
if path.ident.name == sym!(into_bytes);
- if let ExprKind::MethodCall(path, [recv], _) = &recv.kind;
+ if let ExprKind::MethodCall(path, recv, [], _) = &recv.kind;
if matches!(path.ident.name.as_str(), "to_owned" | "to_string");
if let ExprKind::Lit(lit) = &recv.kind;
if let LitKind::Str(lit_content, _) = &lit.node;
impl<'tcx> LateLintPass<'tcx> for StrToString {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
if_chain! {
- if let ExprKind::MethodCall(path, [self_arg, ..], _) = &expr.kind;
+ if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind;
if path.ident.name == sym::to_string;
let ty = cx.typeck_results().expr_ty(self_arg);
if let ty::Ref(_, ty, ..) = ty.kind();
impl<'tcx> LateLintPass<'tcx> for StringToString {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
if_chain! {
- if let ExprKind::MethodCall(path, [self_arg, ..], _) = &expr.kind;
+ if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind;
if path.ident.name == sym::to_string;
let ty = cx.typeck_results().expr_ty(self_arg);
if is_type_diagnostic_item(cx, ty, sym::String);
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
let tyckres = cx.typeck_results();
if_chain! {
- if let ExprKind::MethodCall(path, [split_recv], split_ws_span) = expr.kind;
+ if let ExprKind::MethodCall(path, split_recv, [], split_ws_span) = expr.kind;
if path.ident.name == sym!(split_whitespace);
if let Some(split_ws_def_id) = tyckres.type_dependent_def_id(expr.hir_id);
if cx.tcx.is_diagnostic_item(sym::str_split_whitespace, split_ws_def_id);
- if let ExprKind::MethodCall(path, [_trim_recv], trim_span) = split_recv.kind;
+ if let ExprKind::MethodCall(path, _trim_recv, [], trim_span) = split_recv.kind;
if let trim_fn_name @ ("trim" | "trim_start" | "trim_end") = path.ident.name.as_str();
if let Some(trim_def_id) = tyckres.type_dependent_def_id(split_recv.hir_id);
if is_one_of_trim_diagnostic_items(cx, trim_def_id);
if let ExprKind::Path(path) = &func.kind;
if let Some(did) = cx.qpath_res(path, func.hir_id).opt_def_id();
if match_libc_symbol(cx, did, "strlen");
- if let ExprKind::MethodCall(path, [self_arg], _) = recv.kind;
+ if let ExprKind::MethodCall(path, self_arg, [], _) = recv.kind;
if !recv.span.from_expansion();
if path.ident.name == sym::as_ptr;
then {
impl<'tcx> LateLintPass<'tcx> for ToDigitIsSome {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
if_chain! {
- if let hir::ExprKind::MethodCall(is_some_path, is_some_args, _) = &expr.kind;
+ if let hir::ExprKind::MethodCall(is_some_path, to_digit_expr, [], _) = &expr.kind;
if is_some_path.ident.name.as_str() == "is_some";
- if let [to_digit_expr] = &**is_some_args;
then {
let match_result = match &to_digit_expr.kind {
- hir::ExprKind::MethodCall(to_digits_path, to_digit_args, _) => {
+ hir::ExprKind::MethodCall(to_digits_path, char_arg, [radix_arg], _) => {
if_chain! {
- if let [char_arg, radix_arg] = &**to_digit_args;
if to_digits_path.ident.name.as_str() == "to_digit";
let char_arg_ty = cx.typeck_results().expr_ty_adjusted(char_arg);
if *char_arg_ty.kind() == ty::Char;
then {
- Some((true, char_arg, radix_arg))
+ Some((true, *char_arg, radix_arg))
} else {
None
}
}
hir::ExprKind::Call(to_digits_call, to_digit_args) => {
if_chain! {
- if let [char_arg, radix_arg] = &**to_digit_args;
+ if let [char_arg, radix_arg] = *to_digit_args;
if let hir::ExprKind::Path(to_digits_path) = &to_digits_call.kind;
if let to_digits_call_res = cx.qpath_res(to_digits_path, to_digits_call.hir_id);
if let Some(to_digits_def_id) = to_digits_call_res.opt_def_id();
if !bound_predicate.span.from_expansion();
if let TyKind::Path(QPath::Resolved(_, Path { segments, .. })) = bound_predicate.bounded_ty.kind;
if let Some(PathSegment {
- res: Some(Res::SelfTy{ trait_: Some(def_id), alias_to: _ }), ..
+ res: Res::SelfTy{ trait_: Some(def_id), alias_to: _ }, ..
}) = segments.first();
if let Some(
Node::Item(
});
}
},
- ExprKind::MethodCall(path, [self_expr, _], _) if is_reserve(cx, path, self_expr) => {
+ ExprKind::MethodCall(path, self_expr, [_], _) if is_reserve(cx, path, self_expr) => {
return Some(TargetVec {
location: VecLocation::Expr(self_expr),
init_kind: None,
}
});
match expr.kind {
- ExprKind::MethodCall(path, [self_expr, _], _) => {
+ ExprKind::MethodCall(path, self_expr, [_], _) => {
let self_type = cx.typeck_results().expr_ty(self_expr).peel_refs();
if is_type_diagnostic_item(cx, self_type, sym::Vec) && path.ident.name.as_str() == "set_len" {
Some((self_expr, expr.span))
impl<'tcx> LateLintPass<'tcx> for UnitReturnExpectingOrd {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
- if let ExprKind::MethodCall(_, args, _) = expr.kind {
+ if let ExprKind::MethodCall(_, receiver, args, _) = expr.kind {
let arg_indices = get_args_to_check(cx, expr);
+ let args = std::iter::once(receiver).chain(args.iter()).collect::<Vec<_>>();
for (i, trait_name) in arg_indices {
if i < args.len() {
match check_arg(cx, &args[i]) {
locals_to_check: &mut Vec<HirId>,
seen_locals: &mut HirIdSet,
) -> bool {
- let (id, args) = match e.kind {
+ let (id, receiver, args) = match e.kind {
ExprKind::Call(
Expr {
kind: ExprKind::Path(ref path),
},
args,
) => match cx.qpath_res(path, *hir_id) {
- Res::Def(DefKind::AssocFn | DefKind::Fn, id) => (id, args),
+ Res::Def(DefKind::AssocFn | DefKind::Fn, id) => (id, None, args),
_ => return false,
},
- ExprKind::MethodCall(_, args, _) => match cx.typeck_results().type_dependent_def_id(e.hir_id) {
- Some(id) => (id, args),
+ ExprKind::MethodCall(_, receiver, args, _) => match cx.typeck_results().type_dependent_def_id(e.hir_id) {
+ Some(id) => (id, Some(receiver), args),
None => return false,
},
ExprKind::Path(QPath::Resolved(None, path)) => {
};
let sig = cx.tcx.fn_sig(id).skip_binder();
if let ty::Param(output_ty) = *sig.output().kind() {
+ let args: Vec<&Expr<'_>> = if let Some(receiver) = receiver {
+ std::iter::once(receiver).chain(args.iter()).collect()
+ } else {
+ args.iter().collect()
+ };
sig.inputs().iter().zip(args).all(|(&ty, arg)| {
!ty.is_param(output_ty.index) || each_value_source_needs_inference(cx, arg, locals_to_check, seen_locals)
})
}
}
- match expr.kind {
- ExprKind::Call(_, args) | ExprKind::MethodCall(_, args, _) => {
- let args_to_recover = args
- .iter()
- .filter(|arg| {
- if cx.typeck_results().expr_ty(arg).is_unit() && !utils::is_unit_literal(arg) {
- !matches!(
- &arg.kind,
- ExprKind::Match(.., MatchSource::TryDesugar) | ExprKind::Path(..)
- )
- } else {
- false
- }
- })
- .collect::<Vec<_>>();
- if !args_to_recover.is_empty() && !is_from_proc_macro(cx, expr) {
- lint_unit_args(cx, expr, &args_to_recover);
+ let args: Vec<_> = match expr.kind {
+ ExprKind::Call(_, args) => args.iter().collect(),
+ ExprKind::MethodCall(_, receiver, args, _) => std::iter::once(receiver).chain(args.iter()).collect(),
+ _ => return,
+ };
+
+ let args_to_recover = args
+ .into_iter()
+ .filter(|arg| {
+ if cx.typeck_results().expr_ty(arg).is_unit() && !utils::is_unit_literal(arg) {
+ !matches!(
+ &arg.kind,
+ ExprKind::Match(.., MatchSource::TryDesugar) | ExprKind::Path(..)
+ )
+ } else {
+ false
}
- },
- _ => (),
+ })
+ .collect::<Vec<_>>();
+ if !args_to_recover.is_empty() && !is_from_proc_macro(cx, expr) {
+ lint_unit_args(cx, expr, &args_to_recover.as_slice());
}
}
check_map_error(cx, res, expr);
}
},
- hir::ExprKind::MethodCall(path, [ref arg_0, ..], _) => match path.ident.as_str() {
+ hir::ExprKind::MethodCall(path, arg_0, ..) => match path.ident.as_str() {
"expect" | "unwrap" | "unwrap_or" | "unwrap_or_else" => {
check_map_error(cx, arg_0, expr);
},
fn check_map_error(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>) {
let mut call = call;
- while let hir::ExprKind::MethodCall(path, args, _) = call.kind {
+ while let hir::ExprKind::MethodCall(path, receiver, ..) = call.kind {
if matches!(path.ident.as_str(), "or" | "or_else" | "ok") {
- call = &args[0];
+ call = receiver;
} else {
break;
}
}
fn check_method_call(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>, is_await: bool) {
- if let hir::ExprKind::MethodCall(path, _, _) = call.kind {
+ if let hir::ExprKind::MethodCall(path, ..) = call.kind {
let symbol = path.ident.as_str();
let read_trait = if is_await {
match_trait_method(cx, call, &paths::FUTURES_IO_ASYNCREADEXT)
ident: method_name_ident,
..
},
- [self_arg, remaining_args @ ..],
+ self_arg,
+ remaining_args,
_,
) => {
let method_name = method_name_ident.name.as_str();
return collect_unwrap_info(cx, if_expr, expr, branch, !invert, false);
} else {
if_chain! {
- if let ExprKind::MethodCall(method_name, args, _) = &expr.kind;
- if let Some(local_id) = path_to_local(&args[0]);
- let ty = cx.typeck_results().expr_ty(&args[0]);
+ if let ExprKind::MethodCall(method_name, receiver, args, _) = &expr.kind;
+ if let Some(local_id) = path_to_local(receiver);
+ let ty = cx.typeck_results().expr_ty(receiver);
let name = method_name.ident.as_str();
if is_relevant_option_call(cx, ty, name) || is_relevant_result_call(cx, ty, name);
then {
- assert!(args.len() == 1);
+ assert!(args.len() == 0);
let unwrappable = match name {
"is_some" | "is_ok" => true,
"is_err" | "is_none" => false,
} else {
// find `unwrap[_err]()` calls:
if_chain! {
- if let ExprKind::MethodCall(method_name, [self_arg, ..], _) = expr.kind;
+ if let ExprKind::MethodCall(method_name, self_arg, ..) = expr.kind;
if let Some(id) = path_to_local(self_arg);
if [sym::unwrap, sym::expect, sym!(unwrap_err)].contains(&method_name.ident.name);
let call_to_unwrap = [sym::unwrap, sym::expect].contains(&method_name.ident.name);
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
// check for `expect`
if let Some(arglists) = method_chain_args(expr, &["expect"]) {
- let receiver_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs();
+ let receiver_ty = self.typeck_results.expr_ty(&arglists[0].0).peel_refs();
if is_type_diagnostic_item(self.lcx, receiver_ty, sym::Option)
|| is_type_diagnostic_item(self.lcx, receiver_ty, sym::Result)
{
// check for `unwrap`
if let Some(arglists) = method_chain_args(expr, &["unwrap"]) {
- let receiver_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs();
+ let receiver_ty = self.typeck_results.expr_ty(&arglists[0].0).peel_refs();
if is_type_diagnostic_item(self.lcx, receiver_ty, sym::Option)
|| is_type_diagnostic_item(self.lcx, receiver_ty, sym::Result)
{
}
},
- ExprKind::MethodCall(name, .., [recv, ..], _) => {
+ ExprKind::MethodCall(name, recv, ..) => {
if is_trait_method(cx, e, sym::Into) && name.ident.as_str() == "into" {
let a = cx.typeck_results().expr_ty(e);
let b = cx.typeck_results().expr_ty(recv);
self.expr(func);
self.slice(args, |e| self.expr(e));
},
- ExprKind::MethodCall(method_name, args, _) => {
- bind!(self, method_name, args);
- kind!("MethodCall({method_name}, {args}, _)");
+ ExprKind::MethodCall(method_name, receiver, args, _) => {
+ bind!(self, method_name, receiver, args);
+ kind!("MethodCall({method_name}, {receiver}, {args}, _)");
self.ident(field!(method_name.ident));
+ self.expr(receiver);
self.slice(args, |e| self.expr(e));
},
ExprKind::Tup(elements) => {
}
fn body(&self, body_id: &Binding<hir::BodyId>) {
- let expr = &self.cx.tcx.hir().body(body_id.value).value;
+ let expr = self.cx.tcx.hir().body(body_id.value).value;
bind!(self, expr);
out!("let {expr} = &cx.tcx.hir().body({body_id}).value;");
self.expr(expr);
if let ["expn_data", "outer_expn"] = method_names.as_slice();
let args = arg_lists[1];
if args.len() == 1;
- let self_arg = &args[0];
+ let self_arg = &args.0;
let self_ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
if match_type(cx, self_ty, &paths::SYNTAX_CONTEXT);
then {
|| get_parent_expr(cx, last_place)
.map_or(false, |e| matches!(e.kind, ExprKind::AddrOf(_, Mutability::Mut, _)));
},
- ExprKind::MethodCall(_, [recv, ..], _)
+ ExprKind::MethodCall(_, recv, ..)
if recv.hir_id == e.hir_id
&& adjusted_mut == Mutability::Mut
&& !adjusted_ty.peel_refs().is_slice() =>
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
if let Some(searcher) = self.searcher.take() {
if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind
- && let ExprKind::MethodCall(name, [self_arg, _], _) = expr.kind
+ && let ExprKind::MethodCall(name, self_arg, [_], _) = expr.kind
&& path_to_local_id(self_arg, searcher.local_id)
&& name.ident.as_str() == "push"
{
ExprKind::Unary(UnOp::Neg, e) => (Pat::Str("-"), expr_search_pat(tcx, e).1),
ExprKind::Lit(ref lit) => lit_search_pat(&lit.node),
ExprKind::Array(_) | ExprKind::Repeat(..) => (Pat::Str("["), Pat::Str("]")),
- ExprKind::Call(e, []) | ExprKind::MethodCall(_, [e], _) => (expr_search_pat(tcx, e).0, Pat::Str("(")),
+ ExprKind::Call(e, []) | ExprKind::MethodCall(_, e, [], _) => (expr_search_pat(tcx, e).0, Pat::Str("(")),
ExprKind::Call(first, [.., last])
- | ExprKind::MethodCall(_, [first, .., last], _)
+ | ExprKind::MethodCall(_, first, [.., last], _)
| ExprKind::Binary(_, first, last)
| ExprKind::Tup([first, .., last])
| ExprKind::Assign(first, last, _)
});
}
-pub fn span_lint_hir(
- cx: &LateContext<'_>,
- lint: &'static Lint,
- hir_id: HirId,
- sp: Span,
- msg: &str,
-) {
+pub fn span_lint_hir(cx: &LateContext<'_>, lint: &'static Lint, hir_id: HirId, sp: Span, msg: &str) {
cx.tcx.struct_span_lint_hir(lint, hir_id, sp, |diag| {
let mut diag = diag.build(msg);
docs_link(&mut diag, lint);
}
/// Determine the eagerness of the given function call.
-fn fn_eagerness<'tcx>(
- cx: &LateContext<'tcx>,
- fn_id: DefId,
- name: Symbol,
- args: &'tcx [Expr<'_>],
-) -> EagernessSuggestion {
+fn fn_eagerness<'tcx>(cx: &LateContext<'tcx>, fn_id: DefId, name: Symbol, have_one_arg: bool) -> EagernessSuggestion {
use EagernessSuggestion::{Eager, Lazy, NoChange};
let name = name.as_str();
None => return Lazy,
};
- if (name.starts_with("as_") || name == "len" || name == "is_empty") && args.len() == 1 {
+ if (name.starts_with("as_") || name == "len" || name == "is_empty") && have_one_arg {
if matches!(
cx.tcx.crate_name(fn_id.krate),
sym::std | sym::core | sym::alloc | sym::proc_macro
},
Res::Def(_, id) => match path {
QPath::Resolved(_, p) => {
- self.eagerness |= fn_eagerness(self.cx, id, p.segments.last().unwrap().ident.name, args);
+ self.eagerness |=
+ fn_eagerness(self.cx, id, p.segments.last().unwrap().ident.name, !args.is_empty());
},
QPath::TypeRelative(_, name) => {
- self.eagerness |= fn_eagerness(self.cx, id, name.ident.name, args);
+ self.eagerness |= fn_eagerness(self.cx, id, name.ident.name, !args.is_empty());
},
QPath::LangItem(..) => self.eagerness = Lazy,
},
self.eagerness |= NoChange;
return;
},
- ExprKind::MethodCall(name, args, _) => {
+ ExprKind::MethodCall(name, ..) => {
self.eagerness |= self
.cx
.typeck_results()
.type_dependent_def_id(e.hir_id)
- .map_or(Lazy, |id| fn_eagerness(self.cx, id, name.ident.name, args));
+ .map_or(Lazy, |id| fn_eagerness(self.cx, id, name.ident.name, true));
},
ExprKind::Index(_, e) => {
let ty = self.cx.typeck_results().expr_ty_adjusted(e);
&& self.eq_expr(l.body, r.body)
})
},
- (&ExprKind::MethodCall(l_path, l_args, _), &ExprKind::MethodCall(r_path, r_args, _)) => {
- self.inner.allow_side_effects && self.eq_path_segment(l_path, r_path) && self.eq_exprs(l_args, r_args)
+ (
+ &ExprKind::MethodCall(l_path, l_receiver, l_args, _),
+ &ExprKind::MethodCall(r_path, r_receiver, r_args, _),
+ ) => {
+ self.inner.allow_side_effects
+ && self.eq_path_segment(l_path, r_path)
+ && self.eq_expr(l_receiver, r_receiver)
+ && self.eq_exprs(l_args, r_args)
},
(&ExprKind::Repeat(le, ll), &ExprKind::Repeat(re, rl)) => {
self.eq_expr(le, re) && self.eq_array_length(ll, rl)
s.hash(&mut self.s);
},
- ExprKind::MethodCall(path, args, ref _fn_span) => {
+ ExprKind::MethodCall(path, receiver, args, ref _fn_span) => {
self.hash_name(path.ident.name);
+ self.hash_expr(receiver);
self.hash_exprs(args);
},
ExprKind::ConstBlock(ref l_id) => {
.map_or(&[][..], |a| a.args)
.iter()
.filter_map(|a| match a {
- hir::GenericArg::Type(ty) => Some(ty),
+ hir::GenericArg::Type(ty) => Some(*ty),
_ => None,
})
}
pub fn method_calls<'tcx>(
expr: &'tcx Expr<'tcx>,
max_depth: usize,
-) -> (Vec<Symbol>, Vec<&'tcx [Expr<'tcx>]>, Vec<Span>) {
+) -> (Vec<Symbol>, Vec<(&'tcx Expr<'tcx>, &'tcx [Expr<'tcx>])>, Vec<Span>) {
let mut method_names = Vec::with_capacity(max_depth);
let mut arg_lists = Vec::with_capacity(max_depth);
let mut spans = Vec::with_capacity(max_depth);
let mut current = expr;
for _ in 0..max_depth {
- if let ExprKind::MethodCall(path, args, _) = ¤t.kind {
- if args.iter().any(|e| e.span.from_expansion()) {
+ if let ExprKind::MethodCall(path, receiver, args, _) = ¤t.kind {
+ if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
break;
}
method_names.push(path.ident.name);
- arg_lists.push(&**args);
+ arg_lists.push((*receiver, &**args));
spans.push(path.ident.span);
- current = &args[0];
+ current = receiver;
} else {
break;
}
/// `method_chain_args(expr, &["bar", "baz"])` will return a `Vec`
/// containing the `Expr`s for
/// `.bar()` and `.baz()`
-pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[&str]) -> Option<Vec<&'a [Expr<'a>]>> {
+pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[&str]) -> Option<Vec<(&'a Expr<'a>, &'a [Expr<'a>])>> {
let mut current = expr;
let mut matched = Vec::with_capacity(methods.len());
for method_name in methods.iter().rev() {
// method chains are stored last -> first
- if let ExprKind::MethodCall(path, args, _) = current.kind {
+ if let ExprKind::MethodCall(path, receiver, args, _) = current.kind {
if path.ident.name.as_str() == *method_name {
- if args.iter().any(|e| e.span.from_expansion()) {
+ if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
return None;
}
- matched.push(args); // build up `matched` backwards
- current = &args[0]; // go to parent expression
+ matched.push((receiver, args)); // build up `matched` backwards
+ current = receiver; // go to parent expression
} else {
return None;
}
ty_is_fn_once_param(cx.tcx, ty.skip_binder(), predicates).then_some(())
})
},
- ExprKind::MethodCall(_, args, _) => {
- let i = args.iter().position(|arg| arg.hir_id == id)?;
+ ExprKind::MethodCall(_, receiver, args, _) => {
+ let i = std::iter::once(receiver)
+ .chain(args.iter())
+ .position(|arg| arg.hir_id == id)?;
let id = cx.typeck_results().type_dependent_def_id(e.hir_id)?;
let ty = cx.tcx.fn_sig(id).skip_binder().inputs()[i];
ty_is_fn_once_param(cx.tcx, ty, cx.tcx.param_env(id).caller_bounds()).then_some(())
}
};
- let mut expr = &func.value;
+ let mut expr = func.value;
loop {
match expr.kind {
#[rustfmt::skip]
if abort {
return false;
}
- if let ExprKind::MethodCall(seg, [recv], _) = expr.kind {
+ if let ExprKind::MethodCall(seg, recv, [], _) = expr.kind {
if path_to_local_id(recv, id) {
if seg.ident.name.as_str() == "capacity" {
abort = true;
| AssocOp::LessEqual
| AssocOp::NotEqual
| AssocOp::Greater
- | AssocOp::GreaterEqual => format!(
- "{} {} {}",
- lhs,
- op.to_ast_binop().expect("Those are AST ops").to_string(),
- rhs
- ),
+ | AssocOp::GreaterEqual => {
+ format!(
+ "{} {} {}",
+ lhs,
+ op.to_ast_binop().expect("Those are AST ops").to_string(),
+ rhs
+ )
+ },
AssocOp::Assign => format!("{} = {}", lhs, rhs),
AssocOp::AssignOp(op) => {
format!("{} {}= {}", lhs, token_kind_to_string(&token::BinOp(op)), rhs)
/// indicates whether the function from `parent_expr` takes its args by double reference
fn func_takes_arg_by_double_ref(&self, parent_expr: &'tcx hir::Expr<'_>, cmt_hir_id: HirId) -> bool {
let ty = match parent_expr.kind {
- ExprKind::MethodCall(_, call_args, _) => {
+ ExprKind::MethodCall(_, receiver, call_args, _) => {
if let Some(sig) = self
.cx
.typeck_results()
.type_dependent_def_id(parent_expr.hir_id)
.map(|did| self.cx.tcx.fn_sig(did).skip_binder())
{
- call_args
- .iter()
+ std::iter::once(receiver)
+ .chain(call_args.iter())
.position(|arg| arg.hir_id == cmt_hir_id)
.map(|i| sig.inputs()[i])
} else {
match &parent_expr.kind {
// given expression is the self argument and will be handled completely by the compiler
// i.e.: `|x| x.is_something()`
- ExprKind::MethodCall(_, [self_expr, ..], _) if self_expr.hir_id == cmt.hir_id => {
+ ExprKind::MethodCall(_, self_expr, ..) if self_expr.hir_id == cmt.hir_id => {
let _ = write!(self.suggestion_start, "{}{}", start_snip, ident_str_with_proj);
self.next_pos = span.hi();
return;
},
// item is used in a call
// i.e.: `Call`: `|x| please(x)` or `MethodCall`: `|x| [1, 2, 3].contains(x)`
- ExprKind::Call(_, [call_args @ ..]) | ExprKind::MethodCall(_, [_, call_args @ ..], _) => {
+ ExprKind::Call(_, [call_args @ ..]) | ExprKind::MethodCall(_, _, [call_args @ ..], _) => {
let expr = self.cx.tcx.hir().expect_expr(cmt.hir_id);
let arg_ty_kind = self.cx.typeck_results().expr_ty(expr).kind();
helper(typeck, true, arg, f)?;
}
},
- ExprKind::MethodCall(_, args, _) | ExprKind::Tup(args) | ExprKind::Array(args) => {
+ ExprKind::MethodCall(_, receiver, args, _) => {
+ helper(typeck, true, receiver, f)?;
+ for arg in args {
+ helper(typeck, true, arg, f)?;
+ }
+ },
+ ExprKind::Tup(args) | ExprKind::Array(args) => {
for arg in args {
helper(typeck, true, arg, f)?;
}
}
}
if_chain! {
- if let ExprKind::MethodCall(method_name, args, _) = expr.kind;
+ if let ExprKind::MethodCall(method_name, receiver, args, _) = expr.kind;
if method_name.ident.as_str() == "test";
- if args.len() == 1;
- if let ExprKind::Path(ref qpath) = args[0].kind;
+ if let ExprKind::Path(ref qpath) = receiver.kind;
if match_qpath(qpath, &["test_method_call"]);
+ if args.is_empty();
then {
// report your lint here
}
+#[rustc_const_unstable(feature = "const_unicode_case_lookup", issue = "101400")]
#[inline(always)]
-fn bitset_search<
+const fn bitset_search<
const N: usize,
const CHUNK_SIZE: usize,
const N1: usize,
let bucket_idx = (needle / 64) as usize;
let chunk_map_idx = bucket_idx / CHUNK_SIZE;
let chunk_piece = bucket_idx % CHUNK_SIZE;
- let chunk_idx = if let Some(&v) = chunk_idx_map.get(chunk_map_idx) {
- v
+ // FIXME: const-hack: Revert to `slice::get` after `const_slice_index`
+ // feature stabilizes.
+ let chunk_idx = if chunk_map_idx < chunk_idx_map.len() {
+ chunk_idx_map[chunk_map_idx]
} else {
return false;
};
let idx = bitset_chunk_idx[chunk_idx as usize][chunk_piece] as usize;
- let word = if let Some(word) = bitset_canonical.get(idx) {
- *word
+ // FIXME: const-hack: Revert to `slice::get` after `const_slice_index`
+ // feature stabilizes.
+ let word = if idx < bitset_canonical.len() {
+ bitset_canonical[idx]
} else {
let (real_idx, mapping) = bitset_canonicalized[idx - bitset_canonical.len()];
let mut word = bitset_canonical[real_idx as usize];
writeln!(
&mut self.file,
- "static BITSET_CANONICAL: [u64; {}] = [{}];",
+ "const BITSET_CANONICAL: &'static [u64; {}] = &[{}];",
canonicalized.canonical_words.len(),
fmt_list(canonicalized.canonical_words.iter().map(|v| Bits(*v))),
)
self.bytes_used += 8 * canonicalized.canonical_words.len();
writeln!(
&mut self.file,
- "static BITSET_MAPPING: [(u8, u8); {}] = [{}];",
+ "const BITSET_MAPPING: &'static [(u8, u8); {}] = &[{}];",
canonicalized.canonicalized_words.len(),
fmt_list(&canonicalized.canonicalized_words),
)
self.blank_line();
- writeln!(&mut self.file, "pub fn lookup(c: char) -> bool {{").unwrap();
+ writeln!(
+ &mut self.file,
+ r#"#[rustc_const_unstable(feature = "const_unicode_case_lookup", issue = "101400")]"#
+ )
+ .unwrap();
+ writeln!(&mut self.file, "pub const fn lookup(c: char) -> bool {{").unwrap();
writeln!(&mut self.file, " super::bitset_search(",).unwrap();
writeln!(&mut self.file, " c as u32,").unwrap();
writeln!(&mut self.file, " &BITSET_CHUNKS_MAP,").unwrap();
writeln!(
&mut self.file,
- "static BITSET_CHUNKS_MAP: [u8; {}] = [{}];",
+ "const BITSET_CHUNKS_MAP: &'static [u8; {}] = &[{}];",
chunk_indices.len(),
fmt_list(&chunk_indices),
)
self.bytes_used += chunk_indices.len();
writeln!(
&mut self.file,
- "static BITSET_INDEX_CHUNKS: [[u8; {}]; {}] = [{}];",
+ "const BITSET_INDEX_CHUNKS: &'static [[u8; {}]; {}] = &[{}];",
chunk_length,
chunks.len(),
fmt_list(chunks.iter()),
* Changing observable runtime behavior of library APIs
"""
-[mentions."library/proc_macro/src/bridge"]
-cc = ["@rust-lang/wg-rls-2"]
-
[mentions."src/librustdoc/clean/types.rs"]
cc = ["@camelid"]