[[package]]
name = "clippy"
-version = "0.1.58"
+version = "0.1.59"
dependencies = [
"cargo_metadata 0.14.0",
"clippy_lints",
"filetime",
"if_chain",
"itertools 0.10.1",
+ "parking_lot",
"quote",
"regex",
"rustc-workspace-hack",
version = "0.0.1"
dependencies = [
"bytecount",
+ "cargo_metadata 0.14.0",
"clap",
"indoc",
"itertools 0.10.1",
[[package]]
name = "clippy_lints"
-version = "0.1.58"
+version = "0.1.59"
dependencies = [
"cargo_metadata 0.14.0",
"clippy_utils",
"if_chain",
"itertools 0.10.1",
- "pulldown-cmark 0.8.0",
+ "pulldown-cmark",
"quine-mc_cluskey",
"regex-syntax",
"rustc-semver",
[[package]]
name = "clippy_utils"
-version = "0.1.58"
+version = "0.1.59"
dependencies = [
"if_chain",
"rustc-semver",
[[package]]
name = "instant"
-version = "0.1.6"
+version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b141fdc7836c525d4d594027d318c84161ca17aaf8113ab1f81ab93ae897485"
+checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
+dependencies = [
+ "cfg-if 1.0.0",
+]
[[package]]
name = "itertools"
[[package]]
name = "lock_api"
-version = "0.4.1"
+version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "28247cc5a5be2f05fbcd76dd0cf2c7d3b5400cb978a28042abcd4fa0b3f8261c"
+checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109"
dependencies = [
"scopeguard",
]
[[package]]
name = "mdbook"
-version = "0.4.12"
+version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0651782b4cc514c3f98c0acf9b5af1101a735bbe1ac6852bb1a90cb91bdf0ed4"
+checksum = "f6e77253c46a90eb7e96b2807201dab941a4db5ea05eca5aaaf7027395f352b3"
dependencies = [
"ammonia",
"anyhow",
"lazy_static",
"log",
"memchr",
- "open",
- "pulldown-cmark 0.7.2",
+ "opener",
+ "pulldown-cmark",
"regex",
"serde",
"serde_derive",
"shlex",
"tempfile",
"toml",
+ "topological-sort",
]
[[package]]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
-[[package]]
-name = "open"
-version = "1.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7c283bf0114efea9e42f1a60edea9859e8c47528eae09d01df4b29c1e489cc48"
-dependencies = [
- "winapi",
-]
-
[[package]]
name = "opener"
version = "0.5.0"
[[package]]
name = "parking_lot"
-version = "0.11.1"
+version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb"
+checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
dependencies = [
"instant",
"lock_api",
[[package]]
name = "parking_lot_core"
-version = "0.8.3"
+version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018"
+checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216"
dependencies = [
"cfg-if 1.0.0",
"instant",
"cc",
]
-[[package]]
-name = "pulldown-cmark"
-version = "0.7.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca36dea94d187597e104a5c8e4b07576a8a45aa5db48a65e12940d3eb7461f55"
-dependencies = [
- "bitflags",
- "getopts",
- "memchr",
- "unicase",
-]
-
[[package]]
name = "pulldown-cmark"
version = "0.8.0"
checksum = "ffade02495f22453cd593159ea2f59827aae7f53fa8323f756799b670881dcf8"
dependencies = [
"bitflags",
+ "getopts",
"memchr",
"unicase",
]
"expect-test",
"itertools 0.9.0",
"minifier",
- "pulldown-cmark 0.8.0",
+ "pulldown-cmark",
"rayon",
"regex",
"rustdoc-json-types",
"serde",
]
+[[package]]
+name = "topological-sort"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa7c7f42dea4b1b99439786f5633aeb9c14c1b53f75e282803c2ec2ad545873c"
+
[[package]]
name = "tower-service"
version = "0.3.1"
self.word(w);
self.nbsp()
}
+
+ // Synthesizes a comment that was not textually present in the original
+ // source file.
+ pub fn synth_comment(&mut self, text: impl Into<Cow<'static, str>>) {
+ self.word("/*");
+ self.space();
+ self.word(text);
+ self.space();
+ self.word("*/")
+ }
}
self.strsep(",", false, b, elts, op)
}
- fn maybe_print_comment(&mut self, pos: BytePos) {
+ fn maybe_print_comment(&mut self, pos: BytePos) -> bool {
+ let mut has_comment = false;
while let Some(ref cmnt) = self.next_comment() {
if cmnt.pos < pos {
+ has_comment = true;
self.print_comment(cmnt);
} else {
break;
}
}
+ has_comment
}
fn print_comment(&mut self, cmnt: &Comment) {
self.comments().as_mut().and_then(|c| c.next())
}
+ fn maybe_print_trailing_comment(&mut self, span: rustc_span::Span, next_pos: Option<BytePos>) {
+ if let Some(cmnts) = self.comments() {
+ if let Some(cmnt) = cmnts.trailing_comment(span, next_pos) {
+ self.print_comment(&cmnt);
+ }
+ }
+ }
+
+ fn print_remaining_comments(&mut self) {
+ // If there aren't any remaining comments, then we need to manually
+ // make sure there is a line break at the end.
+ if self.next_comment().is_none() {
+ self.hardbreak();
+ }
+ while let Some(ref cmnt) = self.next_comment() {
+ self.print_comment(cmnt)
+ }
+ }
+
fn print_literal(&mut self, lit: &ast::Lit) {
self.maybe_print_comment(lit.span.lo());
self.word(lit.token.to_string())
self.print_tts(tts, convert_dollar_crate);
self.end();
match delim {
- DelimToken::Brace => self.bclose(span),
+ DelimToken::Brace => {
+ let empty = tts.is_empty();
+ self.bclose(span, empty);
+ }
_ => {
let token_str = self.token_kind_to_string(&token::CloseDelim(delim));
self.word(token_str)
self.end(); // Close the head-box.
}
- fn bclose_maybe_open(&mut self, span: rustc_span::Span, close_box: bool) {
- self.maybe_print_comment(span.hi());
- self.break_offset_if_not_bol(1, -(INDENT_UNIT as isize));
+ fn bclose_maybe_open(&mut self, span: rustc_span::Span, empty: bool, close_box: bool) {
+ let has_comment = self.maybe_print_comment(span.hi());
+ if !empty || has_comment {
+ self.break_offset_if_not_bol(1, -(INDENT_UNIT as isize));
+ }
self.word("}");
if close_box {
self.end(); // Close the outer-box.
}
}
- fn bclose(&mut self, span: rustc_span::Span) {
- self.bclose_maybe_open(span, true)
+ fn bclose(&mut self, span: rustc_span::Span, empty: bool) {
+ let close_box = true;
+ self.bclose_maybe_open(span, empty, close_box)
}
fn break_offset_if_not_bol(&mut self, n: usize, off: isize) {
State { s: pp::mk_printer(), comments: None, ann: &NoAnn }
}
- // Synthesizes a comment that was not textually present in the original source
- // file.
- pub fn synth_comment(&mut self, text: String) {
- self.s.word("/*");
- self.s.space();
- self.s.word(text);
- self.s.space();
- self.s.word("*/")
- }
-
crate fn commasep_cmnt<T, F, G>(&mut self, b: Breaks, elts: &[T], mut op: F, mut get_span: G)
where
F: FnMut(&mut State<'_>, &T),
for item in items {
self.print_item(item);
}
- self.bclose(item.span);
+ let empty = item.attrs.is_empty() && items.is_empty();
+ self.bclose(item.span, empty);
}
ModKind::Unloaded => {
self.s.word(";");
}
self.bopen();
self.print_foreign_mod(nmod, &item.attrs);
- self.bclose(item.span);
+ let empty = item.attrs.is_empty() && nmod.items.is_empty();
+ self.bclose(item.span, empty);
}
ast::ItemKind::GlobalAsm(ref asm) => {
self.head(visibility_qualified(&item.vis, "global_asm!"));
for impl_item in items {
self.print_assoc_item(impl_item);
}
- self.bclose(item.span);
+ let empty = item.attrs.is_empty() && items.is_empty();
+ self.bclose(item.span, empty);
}
ast::ItemKind::Trait(box ast::Trait {
is_auto,
for trait_item in items {
self.print_assoc_item(trait_item);
}
- self.bclose(item.span);
+ let empty = item.attrs.is_empty() && items.is_empty();
+ self.bclose(item.span, empty);
}
ast::ItemKind::TraitAlias(ref generics, ref bounds) => {
self.head("");
self.end();
self.maybe_print_trailing_comment(v.span, None);
}
- self.bclose(span)
+ let empty = variants.is_empty();
+ self.bclose(span, empty)
}
crate fn print_visibility(&mut self, vis: &ast::Visibility) {
crate fn print_record_struct_body(&mut self, fields: &[ast::FieldDef], span: rustc_span::Span) {
self.nbsp();
self.bopen();
- self.hardbreak_if_not_bol();
- for field in fields {
+ let empty = fields.is_empty();
+ if !empty {
self.hardbreak_if_not_bol();
- self.maybe_print_comment(field.span.lo());
- self.print_outer_attributes(&field.attrs);
- self.print_visibility(&field.vis);
- self.print_ident(field.ident.unwrap());
- self.word_nbsp(":");
- self.print_type(&field.ty);
- self.s.word(",");
+
+ for field in fields {
+ self.hardbreak_if_not_bol();
+ self.maybe_print_comment(field.span.lo());
+ self.print_outer_attributes(&field.attrs);
+ self.print_visibility(&field.vis);
+ self.print_ident(field.ident.unwrap());
+ self.word_nbsp(":");
+ self.print_type(&field.ty);
+ self.s.word(",");
+ }
}
- self.bclose(span)
+ self.bclose(span, empty);
}
crate fn print_struct(
}
}
- self.bclose_maybe_open(blk.span, close_box);
+ let empty = attrs.is_empty() && blk.stmts.is_empty();
+ self.bclose_maybe_open(blk.span, empty, close_box);
self.ann.post(self, AnnNode::Block(blk))
}
for arm in arms {
self.print_arm(arm);
}
- self.bclose(expr.span);
+ let empty = attrs.is_empty() && arms.is_empty();
+ self.bclose(expr.span, empty);
}
ast::ExprKind::Closure(
capture_clause,
ast::ExprKind::Async(capture_clause, _, ref blk) => {
self.word_nbsp("async");
self.print_capture_clause(capture_clause);
- self.s.space();
// cbox/ibox in analogy to the `ExprKind::Block` arm above
self.cbox(INDENT_UNIT);
self.ibox(0);
self.end();
}
- crate fn maybe_print_trailing_comment(
- &mut self,
- span: rustc_span::Span,
- next_pos: Option<BytePos>,
- ) {
- if let Some(cmnts) = self.comments() {
- if let Some(cmnt) = cmnts.trailing_comment(span, next_pos) {
- self.print_comment(&cmnt);
- }
- }
- }
-
- crate fn print_remaining_comments(&mut self) {
- // If there aren't any remaining comments, then we need to manually
- // make sure there is a line break at the end.
- if self.next_comment().is_none() {
- self.s.hardbreak();
- }
- while let Some(ref cmnt) = self.next_comment() {
- self.print_comment(cmnt);
- }
- }
-
crate fn print_fn_header_info(&mut self, header: ast::FnHeader) {
self.print_constness(header.constness);
self.print_asyncness(header.asyncness);
| InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low4) => unimplemented!(),
InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg)
| InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg) => unimplemented!(),
+ InlineAsmRegClass::Avr(_) => unimplemented!(),
InlineAsmRegClass::Bpf(_) => unimplemented!(),
InlineAsmRegClass::Hexagon(HexagonInlineAsmRegClass::reg) => unimplemented!(),
InlineAsmRegClass::Mips(MipsInlineAsmRegClass::reg) => unimplemented!(),
| InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low4) => {
unimplemented!()
}
+ InlineAsmRegClass::Avr(_) => unimplemented!(),
InlineAsmRegClass::Bpf(_) => unimplemented!(),
InlineAsmRegClass::Hexagon(HexagonInlineAsmRegClass::reg) => cx.type_i32(),
InlineAsmRegClass::Mips(MipsInlineAsmRegClass::reg) => cx.type_i32(),
| InlineAsmRegClass::Arm(ArmInlineAsmRegClass::qreg_low4) => {
unimplemented!()
}
+ InlineAsmRegClass::Avr(_) => unimplemented!(),
InlineAsmRegClass::Bpf(_) => unimplemented!(),
InlineAsmRegClass::Hexagon(_) => unimplemented!(),
InlineAsmRegClass::Mips(_) => unimplemented!(),
"~{vxrm}".to_string(),
]);
}
+ InlineAsmArch::Avr => {
+ constraints.push("~{sreg}".to_string());
+ }
InlineAsmArch::Nvptx64 => {}
InlineAsmArch::PowerPC | InlineAsmArch::PowerPC64 => {}
InlineAsmArch::Hexagon => {}
InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => "r",
InlineAsmRegClass::Bpf(BpfInlineAsmRegClass::reg) => "r",
InlineAsmRegClass::Bpf(BpfInlineAsmRegClass::wreg) => "w",
+ InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg) => "r",
+ InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_upper) => "d",
+ InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_pair) => "r",
+ InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_iw) => "w",
+ InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_ptr) => "e",
InlineAsmRegClass::S390x(S390xInlineAsmRegClass::reg) => "r",
InlineAsmRegClass::S390x(S390xInlineAsmRegClass::freg) => "f",
InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => {
}
InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => None,
InlineAsmRegClass::Bpf(_) => None,
+ InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_pair)
+ | InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_iw)
+ | InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_ptr) => match modifier {
+ Some('h') => Some('B'),
+ Some('l') => Some('A'),
+ _ => None,
+ },
+ InlineAsmRegClass::Avr(_) => None,
InlineAsmRegClass::S390x(_) => None,
InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => {
bug!("LLVM backend does not support SPIR-V")
InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => cx.type_i32(),
InlineAsmRegClass::Bpf(BpfInlineAsmRegClass::reg) => cx.type_i64(),
InlineAsmRegClass::Bpf(BpfInlineAsmRegClass::wreg) => cx.type_i32(),
+ InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg) => cx.type_i8(),
+ InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_upper) => cx.type_i8(),
+ InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_pair) => cx.type_i16(),
+ InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_iw) => cx.type_i16(),
+ InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_ptr) => cx.type_i16(),
InlineAsmRegClass::S390x(S390xInlineAsmRegClass::reg) => cx.type_i32(),
InlineAsmRegClass::S390x(S390xInlineAsmRegClass::freg) => cx.type_f64(),
InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => {
use rustc_index::vec::IndexVec;
use rustc_macros::HashStable;
use rustc_middle::mir;
+use rustc_middle::mir::interpret::{InterpError, InvalidProgramInfo};
use rustc_middle::ty::layout::{self, LayoutError, LayoutOf, LayoutOfHelpers, TyAndLayout};
use rustc_middle::ty::{
self, query::TyCtxtAt, subst::SubstsRef, ParamEnv, Ty, TyCtxt, TypeFoldable,
use rustc_mir_dataflow::storage::AlwaysLiveLocals;
use rustc_query_system::ich::StableHashingContext;
use rustc_session::Limit;
-use rustc_span::{Pos, Span};
+use rustc_span::{Pos, Span, DUMMY_SP};
use rustc_target::abi::{Align, HasDataLayout, Size, TargetDataLayout};
use super::{
pub(super) fn subst_from_current_frame_and_normalize_erasing_regions<T: TypeFoldable<'tcx>>(
&self,
value: T,
- ) -> T {
+ ) -> Result<T, InterpError<'tcx>> {
self.subst_from_frame_and_normalize_erasing_regions(self.frame(), value)
}
&self,
frame: &Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>,
value: T,
- ) -> T {
- frame.instance.subst_mir_and_normalize_erasing_regions(*self.tcx, self.param_env, value)
+ ) -> Result<T, InterpError<'tcx>> {
+ frame
+ .instance
+ .try_subst_mir_and_normalize_erasing_regions(*self.tcx, self.param_env, value)
+ .or_else(|e| {
+ self.tcx.sess.delay_span_bug(
+ DUMMY_SP,
+ format!("failed to normalize {}", e.get_type_for_failure()).as_str(),
+ );
+
+ Err(InterpError::InvalidProgram(InvalidProgramInfo::TooGeneric))
+ })
}
/// The `substs` are assumed to already be in our interpreter "universe" (param_env).
let layout = from_known_layout(self.tcx, self.param_env, layout, || {
let local_ty = frame.body.local_decls[local].ty;
let local_ty =
- self.subst_from_frame_and_normalize_erasing_regions(frame, local_ty);
+ self.subst_from_frame_and_normalize_erasing_regions(frame, local_ty)?;
self.layout_of(local_ty)
})?;
if let Some(state) = frame.locals.get(local) {
for const_ in &body.required_consts {
let span = const_.span;
let const_ =
- self.subst_from_current_frame_and_normalize_erasing_regions(const_.literal);
+ self.subst_from_current_frame_and_normalize_erasing_regions(const_.literal)?;
self.mir_const_to_op(&const_, None).map_err(|err| {
// If there was an error, set the span of the current frame to this constant.
// Avoiding doing this when evaluation succeeds.
self.param_env,
self.layout_of(self.subst_from_current_frame_and_normalize_erasing_regions(
place.ty(&self.frame().body.local_decls, *self.tcx).ty
- ))?,
+ )?)?,
op.layout,
));
Ok(op)
Constant(ref constant) => {
let val =
- self.subst_from_current_frame_and_normalize_erasing_regions(constant.literal);
+ self.subst_from_current_frame_and_normalize_erasing_regions(constant.literal)?;
// This can still fail:
// * During ConstProp, with `TooGeneric` or since the `requried_consts` were not all
// checked yet.
self.param_env,
self.layout_of(self.subst_from_current_frame_and_normalize_erasing_regions(
place.ty(&self.frame().body.local_decls, *self.tcx).ty
- ))?,
+ )?)?,
place_ty.layout,
));
Ok(place_ty)
}
NullaryOp(null_op, ty) => {
- let ty = self.subst_from_current_frame_and_normalize_erasing_regions(ty);
+ let ty = self.subst_from_current_frame_and_normalize_erasing_regions(ty)?;
let layout = self.layout_of(ty)?;
if layout.is_unsized() {
// FIXME: This should be a span_bug (#80742)
Cast(cast_kind, ref operand, cast_ty) => {
let src = self.eval_operand(operand, None)?;
- let cast_ty = self.subst_from_current_frame_and_normalize_erasing_regions(cast_ty);
+ let cast_ty =
+ self.subst_from_current_frame_and_normalize_erasing_regions(cast_ty)?;
self.cast(&src, cast_kind, cast_ty, &dest)?;
}
if let Some(trait_id) = tcx.trait_of_item(callee) {
trace!("attempting to call a trait method");
if !self.tcx.features().const_trait_impl {
- self.check_op(ops::FnCallNonConst);
+ self.check_op(ops::FnCallNonConst(Some((callee, substs))));
return;
}
}
if !nonconst_call_permission {
- self.check_op(ops::FnCallNonConst);
+ self.check_op(ops::FnCallNonConst(None));
return;
}
}
}
if !nonconst_call_permission {
- self.check_op(ops::FnCallNonConst);
+ self.check_op(ops::FnCallNonConst(None));
return;
}
}
//! Concrete error types for all operations which may be invalid in a certain const context.
-use rustc_errors::{struct_span_err, DiagnosticBuilder};
+use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
-use rustc_middle::mir;
+use rustc_middle::ty::subst::{GenericArgKind, SubstsRef};
+use rustc_middle::{mir, ty::AssocKind};
use rustc_session::parse::feature_err;
use rustc_span::symbol::sym;
-use rustc_span::{Span, Symbol};
+use rustc_span::{symbol::Ident, Span, Symbol};
+use rustc_span::{BytePos, Pos};
use super::ConstCx;
/// A function call where the callee is not marked as `const`.
#[derive(Debug)]
-pub struct FnCallNonConst;
-impl NonConstOp for FnCallNonConst {
+pub struct FnCallNonConst<'tcx>(pub Option<(DefId, SubstsRef<'tcx>)>);
+impl<'a> NonConstOp for FnCallNonConst<'a> {
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
- struct_span_err!(
+ let mut err = struct_span_err!(
ccx.tcx.sess,
span,
E0015,
"calls in {}s are limited to constant functions, \
tuple structs and tuple variants",
ccx.const_kind(),
- )
+ );
+
+ if let FnCallNonConst(Some((callee, substs))) = *self {
+ if let Some(trait_def_id) = ccx.tcx.lang_items().eq_trait() {
+ if let Some(eq_item) = ccx.tcx.associated_items(trait_def_id).find_by_name_and_kind(
+ ccx.tcx,
+ Ident::with_dummy_span(sym::eq),
+ AssocKind::Fn,
+ trait_def_id,
+ ) {
+ if callee == eq_item.def_id && substs.len() == 2 {
+ match (substs[0].unpack(), substs[1].unpack()) {
+ (GenericArgKind::Type(self_ty), GenericArgKind::Type(rhs_ty))
+ if self_ty == rhs_ty
+ && self_ty.is_ref()
+ && self_ty.peel_refs().is_primitive() =>
+ {
+ let mut num_refs = 0;
+ let mut tmp_ty = self_ty;
+ while let rustc_middle::ty::Ref(_, inner_ty, _) = tmp_ty.kind() {
+ num_refs += 1;
+ tmp_ty = inner_ty;
+ }
+ let deref = "*".repeat(num_refs);
+
+ if let Ok(call_str) =
+ ccx.tcx.sess.source_map().span_to_snippet(span)
+ {
+ if let Some(eq_idx) = call_str.find("==") {
+ if let Some(rhs_idx) = call_str[(eq_idx + 2)..]
+ .find(|c: char| !c.is_whitespace())
+ {
+ let rhs_pos = span.lo()
+ + BytePos::from_usize(eq_idx + 2 + rhs_idx);
+ let rhs_span = span.with_lo(rhs_pos).with_hi(rhs_pos);
+ err.multipart_suggestion(
+ "consider dereferencing here",
+ vec![
+ (span.shrink_to_lo(), deref.clone()),
+ (rhs_span, deref),
+ ],
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+ }
+ }
+ _ => {}
+ }
+ }
+ }
+ }
+ }
+
+ err
}
}
/// function finds the range of elements that match the key. `data`
/// must have been sorted as if by a call to `sort_by_key` for this to
/// work.
-pub fn binary_search_slice<E, K>(data: &'d [E], key_fn: impl Fn(&E) -> K, key: &K) -> &'d [E]
+pub fn binary_search_slice<'d, E, K>(data: &'d [E], key_fn: impl Fn(&E) -> K, key: &K) -> &'d [E]
where
K: Ord,
{
//! Finding the dominators in a control-flow graph.
//!
-//! Algorithm based on Keith D. Cooper, Timothy J. Harvey, and Ken Kennedy,
-//! "A Simple, Fast Dominance Algorithm",
-//! Rice Computer Science TS-06-33870,
-//! <https://www.cs.rice.edu/~keith/EMBED/dom.pdf>.
+//! Algorithm based on Loukas Georgiadis,
+//! "Linear-Time Algorithms for Dominators and Related Problems",
+//! <ftp://ftp.cs.princeton.edu/techreports/2005/737.pdf>
+//!
+//! Additionally useful is the original Lengauer-Tarjan paper on this subject,
+//! "A Fast Algorithm for Finding Dominators in a Flowgraph"
+//! Thomas Lengauer and Robert Endre Tarjan.
+//! <https://www.cs.princeton.edu/courses/archive/spr03/cs423/download/dominators.pdf>
-use super::iterate::reverse_post_order;
use super::ControlFlowGraph;
use rustc_index::vec::{Idx, IndexVec};
use std::cmp::Ordering;
#[cfg(test)]
mod tests;
-pub fn dominators<G: ControlFlowGraph>(graph: G) -> Dominators<G::Node> {
- let start_node = graph.start_node();
- let rpo = reverse_post_order(&graph, start_node);
- dominators_given_rpo(graph, &rpo)
+struct PreOrderFrame<Iter> {
+ pre_order_idx: PreorderIndex,
+ iter: Iter,
}
-fn dominators_given_rpo<G: ControlFlowGraph>(graph: G, rpo: &[G::Node]) -> Dominators<G::Node> {
- let start_node = graph.start_node();
- assert_eq!(rpo[0], start_node);
+rustc_index::newtype_index! {
+ struct PreorderIndex { .. }
+}
+pub fn dominators<G: ControlFlowGraph>(graph: G) -> Dominators<G::Node> {
// compute the post order index (rank) for each node
let mut post_order_rank = IndexVec::from_elem_n(0, graph.num_nodes());
- for (index, node) in rpo.iter().rev().cloned().enumerate() {
- post_order_rank[node] = index;
- }
- let mut immediate_dominators = IndexVec::from_elem_n(None, graph.num_nodes());
- immediate_dominators[start_node] = Some(start_node);
-
- let mut changed = true;
- while changed {
- changed = false;
-
- for &node in &rpo[1..] {
- let mut new_idom = None;
- for pred in graph.predecessors(node) {
- if immediate_dominators[pred].is_some() {
- // (*) dominators for `pred` have been calculated
- new_idom = Some(if let Some(new_idom) = new_idom {
- intersect(&post_order_rank, &immediate_dominators, new_idom, pred)
- } else {
- pred
- });
- }
- }
+ // We allocate capacity for the full set of nodes, because most of the time
+ // most of the nodes *are* reachable.
+ let mut parent: IndexVec<PreorderIndex, PreorderIndex> =
+ IndexVec::with_capacity(graph.num_nodes());
+
+ let mut stack = vec![PreOrderFrame {
+ pre_order_idx: PreorderIndex::new(0),
+ iter: graph.successors(graph.start_node()),
+ }];
+ let mut pre_order_to_real: IndexVec<PreorderIndex, G::Node> =
+ IndexVec::with_capacity(graph.num_nodes());
+ let mut real_to_pre_order: IndexVec<G::Node, Option<PreorderIndex>> =
+ IndexVec::from_elem_n(None, graph.num_nodes());
+ pre_order_to_real.push(graph.start_node());
+ parent.push(PreorderIndex::new(0)); // the parent of the root node is the root for now.
+ real_to_pre_order[graph.start_node()] = Some(PreorderIndex::new(0));
+ let mut post_order_idx = 0;
+
+ // Traverse the graph, collecting a number of things:
+ //
+ // * Preorder mapping (to it, and back to the actual ordering)
+ // * Postorder mapping (used exclusively for rank_partial_cmp on the final product)
+ // * Parents for each vertex in the preorder tree
+ //
+ // These are all done here rather than through one of the 'standard'
+ // graph traversals to help make this fast.
+ 'recurse: while let Some(frame) = stack.last_mut() {
+ while let Some(successor) = frame.iter.next() {
+ if real_to_pre_order[successor].is_none() {
+ let pre_order_idx = pre_order_to_real.push(successor);
+ real_to_pre_order[successor] = Some(pre_order_idx);
+ parent.push(frame.pre_order_idx);
+ stack.push(PreOrderFrame { pre_order_idx, iter: graph.successors(successor) });
- if new_idom != immediate_dominators[node] {
- immediate_dominators[node] = new_idom;
- changed = true;
+ continue 'recurse;
}
}
+ post_order_rank[pre_order_to_real[frame.pre_order_idx]] = post_order_idx;
+ post_order_idx += 1;
+
+ stack.pop();
}
- Dominators { post_order_rank, immediate_dominators }
-}
+ let reachable_vertices = pre_order_to_real.len();
+
+ let mut idom = IndexVec::from_elem_n(PreorderIndex::new(0), reachable_vertices);
+ let mut semi = IndexVec::from_fn_n(std::convert::identity, reachable_vertices);
+ let mut label = semi.clone();
+ let mut bucket = IndexVec::from_elem_n(vec![], reachable_vertices);
+ let mut lastlinked = None;
+
+ // We loop over vertices in reverse preorder. This implements the pseudocode
+ // of the simple Lengauer-Tarjan algorithm. A few key facts are noted here
+ // which are helpful for understanding the code (full proofs and such are
+ // found in various papers, including one cited at the top of this file).
+ //
+ // For each vertex w (which is not the root),
+ // * semi[w] is a proper ancestor of the vertex w (i.e., semi[w] != w)
+ // * idom[w] is an ancestor of semi[w] (i.e., idom[w] may equal semi[w])
+ //
+ // An immediate dominator of w (idom[w]) is a vertex v where v dominates w
+ // and every other dominator of w dominates v. (Every vertex except the root has
+ // a unique immediate dominator.)
+ //
+ // A semidominator for a given vertex w (semi[w]) is the vertex v with minimum
+ // preorder number such that there exists a path from v to w in which all elements (other than w) have
+ // preorder numbers greater than w (i.e., this path is not the tree path to
+ // w).
+ for w in (PreorderIndex::new(1)..PreorderIndex::new(reachable_vertices)).rev() {
+ // Optimization: process buckets just once, at the start of the
+ // iteration. Do not explicitly empty the bucket (even though it will
+ // not be used again), to save some instructions.
+ //
+ // The bucket here contains the vertices whose semidominator is the
+ // vertex w, which we are guaranteed to have found: all vertices who can
+ // be semidominated by w must have a preorder number exceeding w, so
+ // they have been placed in the bucket.
+ //
+ // We compute a partial set of immediate dominators here.
+ let z = parent[w];
+ for &v in bucket[z].iter() {
+ // This uses the result of Lemma 5 from section 2 from the original
+ // 1979 paper, to compute either the immediate or relative dominator
+ // for a given vertex v.
+ //
+ // eval returns a vertex y, for which semi[y] is minimum among
+ // vertices semi[v] +> y *> v. Note that semi[v] = z as we're in the
+ // z bucket.
+ //
+ // Given such a vertex y, semi[y] <= semi[v] and idom[y] = idom[v].
+ // If semi[y] = semi[v], though, idom[v] = semi[v].
+ //
+ // Using this, we can either set idom[v] to be:
+ // * semi[v] (i.e. z), if semi[y] is z
+ // * idom[y], otherwise
+ //
+ // We don't directly set to idom[y] though as it's not necessarily
+ // known yet. The second preorder traversal will cleanup by updating
+ // the idom for any that were missed in this pass.
+ let y = eval(&mut parent, lastlinked, &semi, &mut label, v);
+ idom[v] = if semi[y] < z { y } else { z };
+ }
+
+ // This loop computes the semi[w] for w.
+ semi[w] = w;
+ for v in graph.predecessors(pre_order_to_real[w]) {
+ let v = real_to_pre_order[v].unwrap();
+
+ // eval returns a vertex x from which semi[x] is minimum among
+ // vertices semi[v] +> x *> v.
+ //
+ // From Lemma 4 from section 2, we know that the semidominator of a
+ // vertex w is the minimum (by preorder number) vertex of the
+ // following:
+ //
+ // * direct predecessors of w with preorder number less than w
+ // * semidominators of u such that u > w and there exists (v, w)
+ // such that u *> v
+ //
+ // This loop therefore identifies such a minima. Note that any
+ // semidominator path to w must have all but the first vertex go
+ // through vertices numbered greater than w, so the reverse preorder
+ // traversal we are using guarantees that all of the information we
+ // might need is available at this point.
+ //
+ // The eval call will give us semi[x], which is either:
+ //
+ // * v itself, if v has not yet been processed
+ // * A possible 'best' semidominator for w.
+ let x = eval(&mut parent, lastlinked, &semi, &mut label, v);
+ semi[w] = std::cmp::min(semi[w], semi[x]);
+ }
+ // semi[w] is now semidominator(w) and won't change any more.
-fn intersect<Node: Idx>(
- post_order_rank: &IndexVec<Node, usize>,
- immediate_dominators: &IndexVec<Node, Option<Node>>,
- mut node1: Node,
- mut node2: Node,
-) -> Node {
- while node1 != node2 {
- while post_order_rank[node1] < post_order_rank[node2] {
- node1 = immediate_dominators[node1].unwrap();
+ // Optimization: Do not insert into buckets if parent[w] = semi[w], as
+ // we then immediately know the idom.
+ //
+ // If we don't yet know the idom directly, then push this vertex into
+ // our semidominator's bucket, where it will get processed at a later
+ // stage to compute its immediate dominator.
+ if parent[w] != semi[w] {
+ bucket[semi[w]].push(w);
+ } else {
+ idom[w] = parent[w];
}
- while post_order_rank[node2] < post_order_rank[node1] {
- node2 = immediate_dominators[node2].unwrap();
+ // Optimization: We share the parent array between processed and not
+ // processed elements; lastlinked represents the divider.
+ lastlinked = Some(w);
+ }
+
+ // Finalize the idoms for any that were not fully settable during initial
+ // traversal.
+ //
+ // If idom[w] != semi[w] then we know that we've stored vertex y from above
+ // into idom[w]. It is known to be our 'relative dominator', which means
+ // that it's one of w's ancestors and has the same immediate dominator as w,
+ // so use that idom.
+ for w in PreorderIndex::new(1)..PreorderIndex::new(reachable_vertices) {
+ if idom[w] != semi[w] {
+ idom[w] = idom[idom[w]];
}
}
- node1
+ let mut immediate_dominators = IndexVec::from_elem_n(None, graph.num_nodes());
+ for (idx, node) in pre_order_to_real.iter_enumerated() {
+ immediate_dominators[*node] = Some(pre_order_to_real[idom[idx]]);
+ }
+
+ Dominators { post_order_rank, immediate_dominators }
+}
+
+/// Evaluate the link-eval virtual forest, providing the currently minimum semi
+/// value for the passed `node` (which may be itself).
+///
+/// This maintains that for every vertex v, `label[v]` is such that:
+///
+/// ```text
+/// semi[eval(v)] = min { semi[label[u]] | root_in_forest(v) +> u *> v }
+/// ```
+///
+/// where `+>` is a proper ancestor and `*>` is just an ancestor.
+#[inline]
+fn eval(
+ ancestor: &mut IndexVec<PreorderIndex, PreorderIndex>,
+ lastlinked: Option<PreorderIndex>,
+ semi: &IndexVec<PreorderIndex, PreorderIndex>,
+ label: &mut IndexVec<PreorderIndex, PreorderIndex>,
+ node: PreorderIndex,
+) -> PreorderIndex {
+ if is_processed(node, lastlinked) {
+ compress(ancestor, lastlinked, semi, label, node);
+ label[node]
+ } else {
+ node
+ }
+}
+
+#[inline]
+fn is_processed(v: PreorderIndex, lastlinked: Option<PreorderIndex>) -> bool {
+ if let Some(ll) = lastlinked { v >= ll } else { false }
+}
+
+#[inline]
+fn compress(
+ ancestor: &mut IndexVec<PreorderIndex, PreorderIndex>,
+ lastlinked: Option<PreorderIndex>,
+ semi: &IndexVec<PreorderIndex, PreorderIndex>,
+ label: &mut IndexVec<PreorderIndex, PreorderIndex>,
+ v: PreorderIndex,
+) {
+ assert!(is_processed(v, lastlinked));
+ let u = ancestor[v];
+ if is_processed(u, lastlinked) {
+ compress(ancestor, lastlinked, semi, label, u);
+ if semi[label[u]] < semi[label[v]] {
+ label[v] = label[u];
+ }
+ ancestor[v] = ancestor[u];
+ }
}
#[derive(Clone, Debug)]
assert_eq!(immediate_dominators[5], Some(6));
assert_eq!(immediate_dominators[6], Some(6));
}
+
+#[test]
+fn paper_slt() {
+ // example from the paper:
+ let graph = TestGraph::new(
+ 1,
+ &[(1, 2), (1, 3), (2, 3), (2, 7), (3, 4), (3, 6), (4, 5), (5, 4), (6, 7), (7, 8), (8, 5)],
+ );
+
+ dominators(&graph);
+}
visited: BitSet<G::Node>,
}
-impl<G> DepthFirstSearch<'graph, G>
+impl<'graph, G> DepthFirstSearch<'graph, G>
where
G: ?Sized + DirectedGraph + WithNumNodes + WithSuccessors,
{
settled: BitSet<G::Node>,
}
-impl<G> TriColorDepthFirstSearch<'graph, G>
+impl<'graph, G> TriColorDepthFirstSearch<'graph, G>
where
G: ?Sized + DirectedGraph + WithNumNodes + WithSuccessors,
{
}
}
-impl<G> TriColorDepthFirstSearch<'graph, G>
+impl<G> TriColorDepthFirstSearch<'_, G>
where
G: ?Sized + DirectedGraph + WithNumNodes + WithSuccessors + WithStartNode,
{
}
}
-impl<N: Idx, S: Idx> GraphSuccessors<'graph> for Sccs<N, S> {
+impl<'graph, N: Idx, S: Idx> GraphSuccessors<'graph> for Sccs<N, S> {
type Item = S;
type Iter = std::iter::Cloned<std::slice::Iter<'graph, S>>;
}
}
-impl<N: Idx> GraphSuccessors<'graph> for VecGraph<N> {
+impl<'graph, N: Idx> GraphSuccessors<'graph> for VecGraph<N> {
type Item = N;
type Iter = std::iter::Cloned<std::slice::Iter<'graph, N>>;
#![feature(core_intrinsics)]
#![feature(extend_one)]
#![feature(hash_raw_entry)]
-#![feature(in_band_lifetimes)]
#![feature(maybe_uninit_uninit_array)]
#![feature(min_specialization)]
#![feature(never_type)]
#![feature(type_alias_impl_trait)]
#![feature(new_uninit)]
-#![feature(nll)]
#![feature(once_cell)]
#![feature(test)]
#![feature(thread_id_value)]
/// If there are multiple items that are equivalent to `key`, they will be yielded in
/// insertion order.
#[inline]
- pub fn get_by_key(&'a self, key: K) -> impl 'a + Iterator<Item = &'a V> {
+ pub fn get_by_key(&self, key: K) -> impl Iterator<Item = &V> + '_ {
self.get_by_key_enumerated(key).map(|(_, v)| v)
}
/// If there are multiple items that are equivalent to `key`, they will be yielded in
/// insertion order.
#[inline]
- pub fn get_by_key_enumerated(&'a self, key: K) -> impl '_ + Iterator<Item = (I, &V)> {
+ pub fn get_by_key_enumerated(&self, key: K) -> impl Iterator<Item = (I, &V)> + '_ {
let lower_bound = self.idx_sorted_by_item_key.partition_point(|&i| self.items[i].0 < key);
self.idx_sorted_by_item_key[lower_bound..].iter().map_while(move |&i| {
let (k, v) = &self.items[i];
/// adapts Item of array reference iterator to Item of hashmap reference iterator.
#[inline(always)]
-fn adapt_array_ref_it<K, V>(pair: &'a (K, V)) -> (&'a K, &'a V) {
+fn adapt_array_ref_it<K, V>(pair: &(K, V)) -> (&K, &V) {
let (a, b) = pair;
(a, b)
}
/// adapts Item of array mut reference iterator to Item of hashmap mut reference iterator.
#[inline(always)]
-fn adapt_array_mut_it<K, V>(pair: &'a mut (K, V)) -> (&'a K, &'a mut V) {
+fn adapt_array_mut_it<K, V>(pair: &mut (K, V)) -> (&K, &mut V) {
let (a, b) = pair;
(a, b)
}
/// An iterator visiting all elements in arbitrary order.
/// The iterator element type is `&'a T`.
#[inline]
- pub fn iter(&'a self) -> impl Iterator<Item = &'a T> {
+ pub fn iter(&self) -> impl Iterator<Item = &T> {
self.into_iter()
}
pub fn iter<Ls>(
first: Option<Ls::LinkIndex>,
- links: &'a Ls,
-) -> impl Iterator<Item = Ls::LinkIndex> + 'a
+ links: &Ls,
+) -> impl Iterator<Item = Ls::LinkIndex> + '_
where
Ls: Links,
{
);
trees.push((bracket_group, Spacing::Alone));
let tokens = Some(LazyTokenStream::new(AttrAnnotatedTokenStream::new(trees)));
- self.process_cfg_attr(attr::mk_attr_from_item(item, tokens, attr.style, span))
+ let attr = attr::mk_attr_from_item(item, tokens, attr.style, span);
+ if attr.has_name(sym::crate_type) {
+ self.sess.parse_sess.buffer_lint(
+ rustc_lint_defs::builtin::DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME,
+ attr.span,
+ ast::CRATE_NODE_ID,
+ "`crate_type` within an `#![cfg_attr] attribute is deprecated`",
+ );
+ }
+ if attr.has_name(sym::crate_name) {
+ self.sess.parse_sess.buffer_lint(
+ rustc_lint_defs::builtin::DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME,
+ attr.span,
+ ast::CRATE_NODE_ID,
+ "`crate_name` within an `#![cfg_attr] attribute is deprecated`",
+ );
+ }
+ self.process_cfg_attr(attr)
})
.collect()
}
use rustc_hir::{GenericBound, PatKind, RangeEnd, TraitBoundModifier};
use rustc_span::source_map::{SourceMap, Spanned};
use rustc_span::symbol::{kw, Ident, IdentPrinter, Symbol};
-use rustc_span::{self, BytePos, FileName};
+use rustc_span::{self, FileName};
use rustc_target::spec::abi::Abi;
use std::borrow::Cow;
}
impl<'a> State<'a> {
- pub fn cbox(&mut self, u: usize) {
- self.s.cbox(u);
- }
-
- pub fn nbsp(&mut self) {
- self.s.word(" ")
- }
-
- pub fn word_nbsp<S: Into<Cow<'static, str>>>(&mut self, w: S) {
- self.s.word(w);
- self.nbsp()
- }
-
- pub fn head<S: Into<Cow<'static, str>>>(&mut self, w: S) {
- let w = w.into();
- // outer-box is consistent
- self.cbox(INDENT_UNIT);
- // head-box is inconsistent
- self.ibox(w.len() + 1);
- // keyword that starts the head
- if !w.is_empty() {
- self.word_nbsp(w);
- }
- }
-
- pub fn bopen(&mut self) {
- self.s.word("{");
- self.end(); // close the head-box
- }
-
pub fn bclose_maybe_open(&mut self, span: rustc_span::Span, close_box: bool) {
self.maybe_print_comment(span.hi());
self.break_offset_if_not_bol(1, -(INDENT_UNIT as isize));
self.bclose_maybe_open(span, true)
}
- pub fn space_if_not_bol(&mut self) {
- if !self.s.is_beginning_of_line() {
- self.s.space();
- }
- }
-
- pub fn break_offset_if_not_bol(&mut self, n: usize, off: isize) {
- if !self.s.is_beginning_of_line() {
- self.s.break_offset(n, off)
- } else if off != 0 && self.s.last_token().is_hardbreak_tok() {
- // We do something pretty sketchy here: tuck the nonzero
- // offset-adjustment we were going to deposit along with the
- // break into the previous hardbreak.
- self.s.replace_last_token(pp::Printer::hardbreak_tok_offset(off));
- }
- }
-
- // Synthesizes a comment that was not textually present in the original source
- // file.
- pub fn synth_comment(&mut self, text: String) {
- self.s.word("/*");
- self.s.space();
- self.s.word(text);
- self.s.space();
- self.s.word("*/")
- }
-
pub fn commasep_cmnt<T, F, G>(&mut self, b: Breaks, elts: &[T], mut op: F, mut get_span: G)
where
F: FnMut(&mut State<'_>, &T),
match decl.output {
hir::FnRetTy::Return(ref ty) => {
self.print_type(&ty);
- self.maybe_print_comment(ty.span.lo())
+ self.maybe_print_comment(ty.span.lo());
}
hir::FnRetTy::DefaultReturn(..) => unreachable!(),
}
self.end();
if let hir::FnRetTy::Return(ref output) = decl.output {
- self.maybe_print_comment(output.span.lo())
+ self.maybe_print_comment(output.span.lo());
}
}
self.end();
}
- pub fn maybe_print_trailing_comment(
- &mut self,
- span: rustc_span::Span,
- next_pos: Option<BytePos>,
- ) {
- if let Some(cmnts) = self.comments() {
- if let Some(cmnt) = cmnts.trailing_comment(span, next_pos) {
- self.print_comment(&cmnt);
- }
- }
- }
-
- pub fn print_remaining_comments(&mut self) {
- // If there aren't any remaining comments, then we need to manually
- // make sure there is a line break at the end.
- if self.next_comment().is_none() {
- self.s.hardbreak();
- }
- while let Some(ref cmnt) = self.next_comment() {
- self.print_comment(cmnt)
- }
- }
-
pub fn print_fn_header_info(&mut self, header: hir::FnHeader, vis: &hir::Visibility<'_>) {
self.s.word(visibility_qualified(vis, ""));
use crate::infer::type_variable::TypeVariableOriginKind;
-use crate::infer::InferCtxt;
-use crate::rustc_middle::ty::TypeFoldable;
+use crate::infer::{InferCtxt, Symbol};
use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder};
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Namespace};
use rustc_middle::infer::unify_key::ConstVariableOriginKind;
use rustc_middle::ty::print::Print;
use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
-use rustc_middle::ty::{self, DefIdTree, InferConst, Ty, TyCtxt};
+use rustc_middle::ty::{self, Const, DefIdTree, InferConst, Ty, TyCtxt, TypeFoldable, TypeFolder};
use rustc_span::symbol::kw;
use rustc_span::Span;
use std::borrow::Cow;
Const { is_parameter: bool },
}
+impl UnderspecifiedArgKind {
+ fn descr(&self) -> &'static str {
+ match self {
+ Self::Type { .. } => "type",
+ Self::Const { .. } => "const",
+ }
+ }
+}
+
impl InferenceDiagnosticsData {
/// Generate a label for a generic argument which can't be inferred. When not
/// much is known about the argument, `use_diag` may be used to describe the
}
}
+ let param_type = arg_data.kind.descr();
let suffix = match local_visitor.found_node_ty {
Some(ty) if ty.is_closure() => {
let substs =
}
Some(ty) if is_named_and_not_impl_trait(ty) && arg_data.name == "_" => {
let ty = ty_to_string(ty);
- format!("the explicit type `{}`, with the type parameters specified", ty)
+ format!("the explicit type `{}`, with the {} parameters specified", ty, param_type)
}
Some(ty) if is_named_and_not_impl_trait(ty) && ty.to_string() != arg_data.name => {
+ let ty = ResolvedTypeParamEraser::new(self.tcx).fold_ty(ty);
+ let ty = ErrTypeParamEraser(self.tcx).fold_ty(ty);
let ty = ty_to_string(ty);
format!(
- "the explicit type `{}`, where the type parameter `{}` is specified",
- ty, arg_data.name,
+ "the explicit type `{}`, where the {} parameter `{}` is specified",
+ ty, param_type, arg_data.name,
)
}
_ => "a type".to_string(),
err
}
}
+
+/// Turn *resolved* type params into `[type error]` to signal we don't want to display them. After
+/// performing that replacement, we'll turn all remaining infer type params to use their name from
+/// their definition, and replace all the `[type error]`s back to being infer so they display in
+/// the output as `_`. If we didn't go through `[type error]`, we would either show all type params
+/// by their name *or* `_`, neither of which is desireable: we want to show all types that we could
+/// infer as `_` to reduce verbosity and avoid telling the user about unnecessary type annotations.
+struct ResolvedTypeParamEraser<'tcx> {
+ tcx: TyCtxt<'tcx>,
+ level: usize,
+}
+
+impl<'tcx> ResolvedTypeParamEraser<'tcx> {
+ fn new(tcx: TyCtxt<'tcx>) -> Self {
+ ResolvedTypeParamEraser { tcx, level: 0 }
+ }
+
+ /// Replace not yet inferred const params with their def name.
+ fn replace_infers(&self, c: &'tcx Const<'tcx>, index: u32, name: Symbol) -> &'tcx Const<'tcx> {
+ match c.val {
+ ty::ConstKind::Infer(..) => self.tcx().mk_const_param(index, name, c.ty),
+ _ => c,
+ }
+ }
+}
+
+impl<'tcx> TypeFolder<'tcx> for ResolvedTypeParamEraser<'tcx> {
+ fn tcx<'a>(&'a self) -> TyCtxt<'tcx> {
+ self.tcx
+ }
+ fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
+ self.level += 1;
+ let t = match t.kind() {
+ // We'll hide this type only if all its type params are hidden as well.
+ ty::Adt(def, substs) => {
+ let generics = self.tcx().generics_of(def.did);
+ // Account for params with default values, like `Vec`, where we
+ // want to show `Vec<T>`, not `Vec<T, _>`. If we replaced that
+ // subst, then we'd get the incorrect output, so we passthrough.
+ let substs: Vec<_> = substs
+ .iter()
+ .zip(generics.params.iter())
+ .map(|(subst, param)| match &(subst.unpack(), ¶m.kind) {
+ (_, ty::GenericParamDefKind::Type { has_default: true, .. }) => subst,
+ (crate::infer::GenericArgKind::Const(c), _) => {
+ self.replace_infers(c, param.index, param.name).into()
+ }
+ _ => subst.super_fold_with(self),
+ })
+ .collect();
+ let should_keep = |subst: &GenericArg<'_>| match subst.unpack() {
+ ty::subst::GenericArgKind::Type(t) => match t.kind() {
+ ty::Error(_) => false,
+ _ => true,
+ },
+ // Account for `const` params here, otherwise `doesnt_infer.rs`
+ // shows `_` instead of `Foo<{ _: u32 }>`
+ ty::subst::GenericArgKind::Const(_) => true,
+ _ => false,
+ };
+ if self.level == 1 || substs.iter().any(should_keep) {
+ let substs = self.tcx().intern_substs(&substs[..]);
+ self.tcx().mk_ty(ty::Adt(def, substs))
+ } else {
+ self.tcx().ty_error()
+ }
+ }
+ ty::Ref(_, ty, _) => {
+ let ty = self.fold_ty(ty);
+ match ty.kind() {
+ // Avoid `&_`, these can be safely presented as `_`.
+ ty::Error(_) => self.tcx().ty_error(),
+ _ => t.super_fold_with(self),
+ }
+ }
+ // We could account for `()` if we wanted to replace it, but it's assured to be short.
+ ty::Tuple(_)
+ | ty::Slice(_)
+ | ty::RawPtr(_)
+ | ty::FnDef(..)
+ | ty::FnPtr(_)
+ | ty::Opaque(..)
+ | ty::Projection(_)
+ | ty::Never => t.super_fold_with(self),
+ ty::Array(ty, c) => self
+ .tcx()
+ .mk_ty(ty::Array(self.fold_ty(ty), self.replace_infers(c, 0, Symbol::intern("N")))),
+ // We don't want to hide type params that haven't been resolved yet.
+ // This would be the type that will be written out with the type param
+ // name in the output.
+ ty::Infer(_) => t,
+ // We don't want to hide the outermost type, only its type params.
+ _ if self.level == 1 => t.super_fold_with(self),
+ // Hide this type
+ _ => self.tcx().ty_error(),
+ };
+ self.level -= 1;
+ t
+ }
+}
+
+/// Replace `[type error]` with `ty::Infer(ty::Var)` to display `_`.
+struct ErrTypeParamEraser<'tcx>(TyCtxt<'tcx>);
+impl<'tcx> TypeFolder<'tcx> for ErrTypeParamEraser<'tcx> {
+ fn tcx<'a>(&'a self) -> TyCtxt<'tcx> {
+ self.0
+ }
+ fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
+ match t.kind() {
+ ty::Error(_) => self.tcx().mk_ty_var(ty::TyVid::from_u32(0)),
+ _ => t.super_fold_with(self),
+ }
+ }
+}
if let Some(def_id) = def_id.as_local() {
let opaque_hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
let parent_def_id = self.infcx.defining_use_anchor;
- let def_scope_default = || {
- let opaque_parent_hir_id = tcx.hir().get_parent_item(opaque_hir_id);
- parent_def_id == tcx.hir().local_def_id(opaque_parent_hir_id)
- };
let (in_definition_scope, origin) = match tcx.hir().expect_item(def_id).kind
{
// Anonymous `impl Trait`
}) => {
(may_define_opaque_type(tcx, parent_def_id, opaque_hir_id), origin)
}
- _ => (def_scope_default(), hir::OpaqueTyOrigin::TyAlias),
+ ref itemkind => {
+ span_bug!(
+ self.value_span,
+ "weird opaque type: {:#?}, {:#?}",
+ ty.kind(),
+ itemkind
+ )
+ }
};
if in_definition_scope {
let opaque_type_key =
untracked!(dump_mir_dir, String::from("abc"));
untracked!(dump_mir_exclude_pass_number, true);
untracked!(dump_mir_graphviz, true);
- untracked!(emit_future_incompat_report, true);
untracked!(emit_stack_sizes, true);
untracked!(future_incompat_test, true);
untracked!(hir_stats, true);
"detects large moves or copies",
}
+declare_lint! {
+ /// The `deprecated_cfg_attr_crate_type_name` lint detects uses of the
+ /// `#![cfg_attr(..., crate_type = "...")]` and
+ /// `#![cfg_attr(..., crate_name = "...")]` attributes to conditionally
+ /// specify the crate type and name in the source code.
+ ///
+ /// ### Example
+ ///
+ /// ```rust
+ /// #![cfg_attr(debug_assertions, crate_type = "lib")]
+ /// ```
+ ///
+ /// {{produces}}
+ ///
+ ///
+ /// ### Explanation
+ ///
+ /// The `#![crate_type]` and `#![crate_name]` attributes require a hack in
+ /// the compiler to be able to change the used crate type and crate name
+ /// after macros have been expanded. Neither attribute works in combination
+ /// with Cargo as it explicitly passes `--crate-type` and `--crate-name` on
+ /// the commandline. These values must match the value used in the source
+ /// code to prevent an error.
+ ///
+ /// To fix the warning use `--crate-type` on the commandline when running
+ /// rustc instead of `#![cfg_attr(..., crate_type = "...")]` and
+ /// `--crate-name` instead of `#![cfg_attr(..., crate_name = "...")]`.
+ pub DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME,
+ Warn,
+ "detects usage of `#![cfg_attr(..., crate_type/crate_name = \"...\")]`",
+ @future_incompatible = FutureIncompatibleInfo {
+ reference: "issue #91632 <https://github.com/rust-lang/rust/issues/91632>",
+ };
+}
+
declare_lint_pass! {
/// Does nothing as a lint pass, but registers some `Lint`s
/// that are used by other parts of the compiler.
NON_EXHAUSTIVE_OMITTED_PATTERNS,
TEXT_DIRECTION_CODEPOINT_IN_COMMENT,
DEREF_INTO_DYN_SUPERTRAIT,
+ DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME,
]
}
"stdc++"
};
- // RISC-V requires libatomic for sub-word atomic operations
- if target.starts_with("riscv") {
+ // RISC-V GCC erroneously requires libatomic for sub-word
+ // atomic operations. FreeBSD uses Clang as its system
+ // compiler and provides no libatomic in its base system so
+ // does not want this.
+ if !target.contains("freebsd") && target.starts_with("riscv") {
println!("cargo:rustc-link-lib=atomic");
}
}
/// Changes a statement to a nop and returns the original statement.
+ #[must_use = "If you don't need the statement, use `make_nop` instead"]
pub fn replace_nop(&mut self) -> Self {
Statement {
source_info: self.source_info,
pub fn all_targets_mut(&mut self) -> &mut [BasicBlock] {
&mut self.targets
}
+
+ /// Finds the `BasicBlock` to which this `SwitchInt` will branch given the
+ /// specific value. This cannot fail, as it'll return the `otherwise`
+ /// branch if there's not a specific match for the value.
+ pub fn target_for_value(&self, value: u128) -> BasicBlock {
+ self.iter().find_map(|(v, t)| (v == value).then_some(t)).unwrap_or_else(|| self.otherwise())
+ }
}
pub struct SwitchTargetsIter<'a> {
use rustc_hir::def_id::{CrateNum, DefId};
use rustc_hir::lang_items::LangItem;
use rustc_macros::HashStable;
+use rustc_middle::ty::normalize_erasing_regions::NormalizationError;
use std::fmt;
}
}
+ #[inline(always)]
+ pub fn try_subst_mir_and_normalize_erasing_regions<T>(
+ &self,
+ tcx: TyCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ v: T,
+ ) -> Result<T, NormalizationError<'tcx>>
+ where
+ T: TypeFoldable<'tcx> + Clone,
+ {
+ if let Some(substs) = self.substs_for_mir_body() {
+ tcx.try_subst_and_normalize_erasing_regions(substs, param_env, v)
+ } else {
+ tcx.try_normalize_erasing_regions(param_env, v)
+ }
+ }
+
/// Returns a new `Instance` where generic parameters in `instance.substs` are replaced by
/// identity parameters if they are determined to be unused in `instance.def`.
pub fn polymorphize(self, tcx: TyCtxt<'tcx>) -> Self {
/// Monomorphizes a type from the AST by first applying the
/// in-scope substitutions and then normalizing any associated
/// types.
+ /// Panics if normalization fails. In case normalization might fail
+ /// use `try_subst_and_normalize_erasing_regions` instead.
pub fn subst_and_normalize_erasing_regions<T>(
self,
param_substs: SubstsRef<'tcx>,
let substituted = value.subst(self, param_substs);
self.normalize_erasing_regions(param_env, substituted)
}
+
+ /// Monomorphizes a type from the AST by first applying the
+ /// in-scope substitutions and then trying to normalize any associated
+ /// types. Contrary to `subst_and_normalize_erasing_regions` this does
+ /// not assume that normalization succeeds.
+ pub fn try_subst_and_normalize_erasing_regions<T>(
+ self,
+ param_substs: SubstsRef<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ value: T,
+ ) -> Result<T, NormalizationError<'tcx>>
+ where
+ T: TypeFoldable<'tcx>,
+ {
+ debug!(
+ "subst_and_normalize_erasing_regions(\
+ param_substs={:?}, \
+ value={:?}, \
+ param_env={:?})",
+ param_substs, value, param_env,
+ );
+ let substituted = value.subst(self, param_substs);
+ self.try_normalize_erasing_regions(param_env, substituted)
+ }
}
struct NormalizeAfterErasingRegionsFolder<'tcx> {
}
/// Determine whether this type may be a reference (or box), and thus needs retagging.
-fn may_be_reference(ty: Ty<'tcx>) -> bool {
+fn may_be_reference(ty: Ty<'_>) -> bool {
match ty.kind() {
// Primitive types that are not references
ty::Bool
target_local: Option<Local>,
}
-impl<'a, 'tcx> ConstMutationChecker<'a, 'tcx> {
+impl<'tcx> ConstMutationChecker<'_, 'tcx> {
fn is_const_item(&self, local: Local) -> Option<DefId> {
if let Some(box LocalInfo::ConstRef { def_id }) = self.body.local_decls[local].local_info {
Some(def_id)
}
}
-impl<'a, 'tcx> Visitor<'tcx> for ConstMutationChecker<'a, 'tcx> {
+impl<'tcx> Visitor<'tcx> for ConstMutationChecker<'_, 'tcx> {
fn visit_statement(&mut self, stmt: &Statement<'tcx>, loc: Location) {
if let StatementKind::Assign(box (lhs, _)) = &stmt.kind {
// Check for assignment to fields of a constant
}
}
-impl<'a, 'tcx> Visitor<'tcx> for PackedRefChecker<'a, 'tcx> {
+impl<'tcx> Visitor<'tcx> for PackedRefChecker<'_, 'tcx> {
fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
// Make sure we know where in the MIR we are.
self.source_info = terminator.source_info;
}
}
-impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
+impl<'tcx> Visitor<'tcx> for UnsafetyChecker<'_, 'tcx> {
fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
self.source_info = terminator.source_info;
match terminator.kind {
}
}
-impl<'a, 'tcx> UnsafetyChecker<'a, 'tcx> {
+impl<'tcx> UnsafetyChecker<'_, 'tcx> {
fn require_unsafe(&mut self, kind: UnsafetyViolationKind, details: UnsafetyViolationDetails) {
// Violations can turn out to be `UnsafeFn` during analysis, but they should not start out as such.
assert_ne!(kind, UnsafetyViolationKind::UnsafeFn);
unsafe_blocks: &'a mut Vec<(hir::HirId, bool)>,
}
-impl<'a, 'tcx> intravisit::Visitor<'tcx> for UnusedUnsafeVisitor<'a> {
+impl<'tcx> intravisit::Visitor<'tcx> for UnusedUnsafeVisitor<'_> {
type Map = intravisit::ErasedMap<'tcx>;
fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
eligable_locals
}
-impl<'tcx> Visitor<'tcx> for LocalUseVisitor {
+impl Visitor<'_> for LocalUseVisitor {
fn visit_local(&mut self, local: &Local, context: PlaceContext, location: Location) {
if context.is_mutating_use() {
self.local_mutating_uses[*local] = self.local_mutating_uses[*local].saturating_add(1);
}
}
-impl<'a, 'tcx> Visitor<'tcx> for ConstGotoOptimizationFinder<'a, 'tcx> {
+impl<'tcx> Visitor<'tcx> for ConstGotoOptimizationFinder<'_, 'tcx> {
fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
let _: Option<_> = try {
let target = terminator.kind.as_goto()?;
// Now find which value in the Switch matches the const value.
let const_value =
_const.literal.try_eval_bits(self.tcx, self.param_env, switch_ty)?;
- let found_value_idx_option = targets
- .iter()
- .enumerate()
- .find(|(_, (value, _))| const_value == *value)
- .map(|(idx, _)| idx);
-
- let target_to_use_in_goto =
- if let Some(found_value_idx) = found_value_idx_option {
- targets.iter().nth(found_value_idx).unwrap().1
- } else {
- // If we did not find the const value in values, it must be the otherwise case
- targets.otherwise()
- };
-
+ let target_to_use_in_goto = targets.target_for_value(const_value);
self.optimizations.push(OptimizationToApply {
bb_with_goto: location.block,
target_to_use_in_goto,
can_const_prop: IndexVec<Local, ConstPropMode>,
}
-impl<'mir, 'tcx> ConstPropMachine<'mir, 'tcx> {
+impl ConstPropMachine<'_, '_> {
fn new(
only_propagate_inside_block_locals: BitSet<Local>,
can_const_prop: IndexVec<Local, ConstPropMode>,
}
#[inline(always)]
- fn stack(
+ fn stack<'a>(
ecx: &'a InterpCx<'mir, 'tcx, Self>,
) -> &'a [Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>] {
&ecx.machine.stack
}
#[inline(always)]
- fn stack_mut(
+ fn stack_mut<'a>(
ecx: &'a mut InterpCx<'mir, 'tcx, Self>,
) -> &'a mut Vec<Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>> {
&mut ecx.machine.stack
source_info: Option<SourceInfo>,
}
-impl<'mir, 'tcx> LayoutOfHelpers<'tcx> for ConstPropagator<'mir, 'tcx> {
+impl<'tcx> LayoutOfHelpers<'tcx> for ConstPropagator<'_, 'tcx> {
type LayoutOfResult = Result<TyAndLayout<'tcx>, LayoutError<'tcx>>;
#[inline]
}
}
-impl<'mir, 'tcx> HasDataLayout for ConstPropagator<'mir, 'tcx> {
+impl HasDataLayout for ConstPropagator<'_, '_> {
#[inline]
fn data_layout(&self) -> &TargetDataLayout {
&self.tcx.data_layout
}
}
-impl<'mir, 'tcx> ty::layout::HasTyCtxt<'tcx> for ConstPropagator<'mir, 'tcx> {
+impl<'tcx> ty::layout::HasTyCtxt<'tcx> for ConstPropagator<'_, 'tcx> {
#[inline]
fn tcx(&self) -> TyCtxt<'tcx> {
self.tcx
}
}
-impl<'mir, 'tcx> ty::layout::HasParamEnv<'tcx> for ConstPropagator<'mir, 'tcx> {
+impl<'tcx> ty::layout::HasParamEnv<'tcx> for ConstPropagator<'_, 'tcx> {
#[inline]
fn param_env(&self) -> ty::ParamEnv<'tcx> {
self.param_env
impl CanConstProp {
/// Returns true if `local` can be propagated
- fn check(
+ fn check<'tcx>(
tcx: TyCtxt<'tcx>,
param_env: ParamEnv<'tcx>,
body: &Body<'tcx>,
}
}
-impl<'tcx> Visitor<'tcx> for CanConstProp {
+impl Visitor<'_> for CanConstProp {
fn visit_local(&mut self, &local: &Local, context: PlaceContext, _: Location) {
use rustc_middle::mir::visit::PlaceContext::*;
match context {
}
}
-impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
+impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> {
fn tcx(&self) -> TyCtxt<'tcx> {
self.tcx
}
}
/// Generates the MIR pass `CoverageSpan`-specific spanview dump file.
-pub(super) fn dump_coverage_spanview(
+pub(super) fn dump_coverage_spanview<'tcx>(
tcx: TyCtxt<'tcx>,
mir_body: &mir::Body<'tcx>,
basic_coverage_blocks: &CoverageGraph,
}
/// Converts the computed `BasicCoverageBlockData`s into `SpanViewable`s.
-fn span_viewables(
+fn span_viewables<'tcx>(
tcx: TyCtxt<'tcx>,
mir_body: &mir::Body<'tcx>,
basic_coverage_blocks: &CoverageGraph,
}
/// Generates the MIR pass coverage-specific graphviz dump file.
-pub(super) fn dump_coverage_graphviz(
+pub(super) fn dump_coverage_graphviz<'tcx>(
tcx: TyCtxt<'tcx>,
mir_body: &mir::Body<'tcx>,
pass_name: &str,
.expect("Unexpected error writing BasicCoverageBlock graphviz DOT file");
}
-fn bcb_to_string_sections(
+fn bcb_to_string_sections<'tcx>(
tcx: TyCtxt<'tcx>,
mir_body: &mir::Body<'tcx>,
debug_counters: &DebugCounters,
/// Returns a simple string representation of a `TerminatorKind` variant, independent of any
/// values it might hold.
-pub(super) fn term_type(kind: &TerminatorKind<'tcx>) -> &'static str {
+pub(super) fn term_type(kind: &TerminatorKind<'_>) -> &'static str {
match kind {
TerminatorKind::Goto { .. } => "Goto",
TerminatorKind::SwitchInt { .. } => "SwitchInt",
}
impl CoverageGraph {
- pub fn from_mir(mir_body: &mir::Body<'tcx>) -> Self {
+ pub fn from_mir(mir_body: &mir::Body<'_>) -> Self {
let (bcbs, bb_to_bcb) = Self::compute_basic_coverage_blocks(mir_body);
// Pre-transform MIR `BasicBlock` successors and predecessors into the BasicCoverageBlock
}
fn compute_basic_coverage_blocks(
- mir_body: &mir::Body<'tcx>,
+ mir_body: &mir::Body<'_>,
) -> (
IndexVec<BasicCoverageBlock, BasicCoverageBlockData>,
IndexVec<BasicBlock, Option<BasicCoverageBlock>>,
}
}
-impl graph::GraphPredecessors<'graph> for CoverageGraph {
+impl<'graph> graph::GraphPredecessors<'graph> for CoverageGraph {
type Item = BasicCoverageBlock;
type Iter = std::iter::Copied<std::slice::Iter<'graph, BasicCoverageBlock>>;
}
}
fn inject_edge_counter_basic_block(
- mir_body: &mut mir::Body<'tcx>,
+ mir_body: &mut mir::Body<'_>,
from_bb: BasicBlock,
to_bb: BasicBlock,
) -> BasicBlock {
}
fn inject_statement(
- mir_body: &mut mir::Body<'tcx>,
+ mir_body: &mut mir::Body<'_>,
counter_kind: CoverageKind,
bb: BasicBlock,
some_code_region: Option<CodeRegion>,
}
// Non-code expressions are injected into the coverage map, without generating executable code.
-fn inject_intermediate_expression(mir_body: &mut mir::Body<'tcx>, expression: CoverageKind) {
+fn inject_intermediate_expression(mir_body: &mut mir::Body<'_>, expression: CoverageKind) {
debug_assert!(matches!(expression, CoverageKind::Expression { .. }));
debug!(" injecting non-code expression {:?}", expression);
let inject_in_bb = mir::START_BLOCK;
coverage_visitor.info
}
-fn covered_file_name<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Option<Symbol> {
+fn covered_file_name(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Symbol> {
if tcx.is_mir_available(def_id) {
let body = mir_body(tcx, def_id);
for bb_data in body.basic_blocks().iter() {
}
impl CoverageStatement {
- pub fn format(&self, tcx: TyCtxt<'tcx>, mir_body: &'a mir::Body<'tcx>) -> String {
+ pub fn format<'tcx>(&self, tcx: TyCtxt<'tcx>, mir_body: &mir::Body<'tcx>) -> String {
match *self {
Self::Statement(bb, span, stmt_index) => {
let stmt = &mir_body[bb].statements[stmt_index];
}
pub fn for_statement(
- statement: &Statement<'tcx>,
+ statement: &Statement<'_>,
span: Span,
expn_span: Span,
bcb: BasicCoverageBlock,
self.bcb == other.bcb
}
- pub fn format(&self, tcx: TyCtxt<'tcx>, mir_body: &'a mir::Body<'tcx>) -> String {
+ pub fn format<'tcx>(&self, tcx: TyCtxt<'tcx>, mir_body: &mir::Body<'tcx>) -> String {
format!(
"{}\n {}",
source_range_no_file(tcx, &self.span),
)
}
- pub fn format_coverage_statements(
+ pub fn format_coverage_statements<'tcx>(
&self,
tcx: TyCtxt<'tcx>,
- mir_body: &'a mir::Body<'tcx>,
+ mir_body: &mir::Body<'tcx>,
) -> String {
let mut sorted_coverage_statements = self.coverage_statements.clone();
sorted_coverage_statements.sort_unstable_by_key(|covstmt| match *covstmt {
/// If the MIR `Statement` has a span contributive to computing coverage spans,
/// return it; otherwise return `None`.
-pub(super) fn filtered_statement_span(statement: &'a Statement<'tcx>) -> Option<Span> {
+pub(super) fn filtered_statement_span(statement: &Statement<'_>) -> Option<Span> {
match statement.kind {
// These statements have spans that are often outside the scope of the executed source code
// for their parent `BasicBlock`.
/// If the MIR `Terminator` has a span contributive to computing coverage spans,
/// return it; otherwise return `None`.
-pub(super) fn filtered_terminator_span(terminator: &'a Terminator<'tcx>) -> Option<Span> {
+pub(super) fn filtered_terminator_span(terminator: &Terminator<'_>) -> Option<Span> {
match terminator.kind {
// These terminators have spans that don't positively contribute to computing a reasonable
// span of actually executed source code. (For example, SwitchInt terminators extracted from
}
}
-fn debug_basic_blocks(mir_body: &Body<'tcx>) -> String {
+fn debug_basic_blocks<'tcx>(mir_body: &Body<'tcx>) -> String {
format!(
"{:?}",
mir_body
}
/// Create a mock `Body` with a simple flow.
-fn goto_switchint() -> Body<'a> {
+fn goto_switchint<'a>() -> Body<'a> {
let mut blocks = MockBlocks::new();
let start = blocks.call(None);
let goto = blocks.goto(Some(start));
}
/// Create a mock `Body` with a loop.
-fn switchint_then_loop_else_return() -> Body<'a> {
+fn switchint_then_loop_else_return<'a>() -> Body<'a> {
let mut blocks = MockBlocks::new();
let start = blocks.call(None);
let switchint = blocks.switchint(Some(start));
}
/// Create a mock `Body` with nested loops.
-fn switchint_loop_then_inner_loop_else_break() -> Body<'a> {
+fn switchint_loop_then_inner_loop_else_break<'a>() -> Body<'a> {
let mut blocks = MockBlocks::new();
let start = blocks.call(None);
let switchint = blocks.switchint(Some(start));
}
}
-fn find_duplicates<'a, 'tcx>(body: &'a Body<'tcx>) -> FxHashMap<BasicBlock, BasicBlock> {
+fn find_duplicates(body: &Body<'_>) -> FxHashMap<BasicBlock, BasicBlock> {
let mut duplicates = FxHashMap::default();
let bbs_to_go_through =
basic_block_data: &'a BasicBlockData<'tcx>,
}
-impl<'tcx, 'a> Hash for BasicBlockHashable<'tcx, 'a> {
+impl Hash for BasicBlockHashable<'_, '_> {
fn hash<H: Hasher>(&self, state: &mut H) {
hash_statements(state, self.basic_block_data.statements.iter());
// Note that since we only hash the kind, we lose span information if we deduplicate the blocks
}
}
-impl<'tcx, 'a> Eq for BasicBlockHashable<'tcx, 'a> {}
+impl Eq for BasicBlockHashable<'_, '_> {}
-impl<'tcx, 'a> PartialEq for BasicBlockHashable<'tcx, 'a> {
+impl PartialEq for BasicBlockHashable<'_, '_> {
fn eq(&self, other: &Self) -> bool {
self.basic_block_data.statements.len() == other.basic_block_data.statements.len()
&& &self.basic_block_data.terminator().kind == &other.basic_block_data.terminator().kind
}
}
-fn statement_hash<'tcx, H: Hasher>(hasher: &mut H, stmt: &StatementKind<'tcx>) {
+fn statement_hash<H: Hasher>(hasher: &mut H, stmt: &StatementKind<'_>) {
match stmt {
StatementKind::Assign(box (place, rvalue)) => {
place.hash(hasher);
};
}
-fn rvalue_hash<H: Hasher>(hasher: &mut H, rvalue: &Rvalue<'tcx>) {
+fn rvalue_hash<H: Hasher>(hasher: &mut H, rvalue: &Rvalue<'_>) {
match rvalue {
Rvalue::Use(op) => operand_hash(hasher, op),
x => x.hash(hasher),
};
}
-fn operand_hash<H: Hasher>(hasher: &mut H, operand: &Operand<'tcx>) {
+fn operand_hash<H: Hasher>(hasher: &mut H, operand: &Operand<'_>) {
match operand {
Operand::Constant(box Constant { user_ty: _, literal, span: _ }) => literal.hash(hasher),
x => x.hash(hasher),
res
}
-fn rvalue_eq(lhs: &Rvalue<'tcx>, rhs: &Rvalue<'tcx>) -> bool {
+fn rvalue_eq<'tcx>(lhs: &Rvalue<'tcx>, rhs: &Rvalue<'tcx>) -> bool {
let res = match (lhs, rhs) {
(Rvalue::Use(op1), Rvalue::Use(op2)) => operand_eq(op1, op2),
(x, y) => x == y,
res
}
-fn operand_eq(lhs: &Operand<'tcx>, rhs: &Operand<'tcx>) -> bool {
+fn operand_eq<'tcx>(lhs: &Operand<'tcx>, rhs: &Operand<'tcx>) -> bool {
let res = match (lhs, rhs) {
(
Operand::Constant(box Constant { user_ty: _, literal, span: _ }),
kill: BitSet<Local>,
}
-impl Replacements<'tcx> {
+impl<'tcx> Replacements<'tcx> {
fn new(locals: usize) -> Self {
Self { map: IndexVec::from_elem_n(None, locals), kill: BitSet::new_empty(locals) }
}
}
impl<'tcx> MutVisitor<'tcx> for Replacer<'tcx> {
- fn tcx<'a>(&'a self) -> TyCtxt<'tcx> {
+ fn tcx(&self) -> TyCtxt<'tcx> {
self.tcx
}
unified_locals: InPlaceUnificationTable<UnifyLocal>,
}
-impl Conflicts<'a> {
+impl<'a> Conflicts<'a> {
fn build<'tcx>(
tcx: TyCtxt<'tcx>,
body: &'_ Body<'tcx>,
/// comment) and also throw out assignments that involve a local that has its address taken or is
/// otherwise ineligible (eg. locals used as array indices are ignored because we cannot propagate
/// arbitrary places into array indices).
-fn find_candidates<'a, 'tcx>(
- tcx: TyCtxt<'tcx>,
- body: &'a Body<'tcx>,
-) -> Vec<CandidateAssignment<'tcx>> {
+fn find_candidates<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) -> Vec<CandidateAssignment<'tcx>> {
let mut visitor = FindAssignments {
tcx,
body,
locals_used_as_array_index: BitSet<Local>,
}
-impl<'a, 'tcx> Visitor<'tcx> for FindAssignments<'a, 'tcx> {
+impl<'tcx> Visitor<'tcx> for FindAssignments<'_, 'tcx> {
fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
if let StatementKind::Assign(box (
dest,
}
}
-fn is_switch<'tcx>(terminator: &Terminator<'tcx>) -> bool {
+fn is_switch(terminator: &Terminator<'_>) -> bool {
matches!(terminator.kind, TerminatorKind::SwitchInt { .. })
}
second_switch_info: SwitchDiscriminantInfo<'tcx>,
}
-impl<'a, 'tcx> Helper<'a, 'tcx> {
+impl<'tcx> Helper<'_, 'tcx> {
pub fn go(
&self,
bb: &BasicBlockData<'tcx>,
ctxt: &'a mut ElaborateDropsCtxt<'b, 'tcx>,
}
-impl<'a, 'b, 'tcx> fmt::Debug for Elaborator<'a, 'b, 'tcx> {
+impl fmt::Debug for Elaborator<'_, '_, '_> {
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
Ok(())
}
}
-impl<'a, 'b, 'tcx> DropElaborator<'a, 'tcx> for Elaborator<'a, 'b, 'tcx> {
+impl<'a, 'tcx> DropElaborator<'a, 'tcx> for Elaborator<'a, '_, 'tcx> {
type Path = MovePathIndex;
fn patch(&mut self) -> &mut MirPatch<'tcx> {
body: &'a Body<'tcx>,
}
-impl<'a, 'tcx> Visitor<'tcx> for FunctionItemRefChecker<'a, 'tcx> {
+impl<'tcx> Visitor<'tcx> for FunctionItemRefChecker<'_, 'tcx> {
/// Emits a lint for function reference arguments bound by `fmt::Pointer` or passed to
/// `transmute`. This only handles arguments in calls outside macro expansions to avoid double
/// counting function references formatted as pointers by macros.
}
}
-impl<'a, 'tcx> FunctionItemRefChecker<'a, 'tcx> {
+impl<'tcx> FunctionItemRefChecker<'_, 'tcx> {
/// Emits a lint for function reference arguments bound by `fmt::Pointer` in calls to the
/// function defined by `def_id` with the substitutions `substs_ref`.
fn check_bound_args(
new_ret_local: Local,
}
-impl TransformVisitor<'tcx> {
+impl<'tcx> TransformVisitor<'tcx> {
// Make a GeneratorState variant assignment. `core::ops::GeneratorState` only has single
// element tuple variants, so we can just write to the downcasted first field and then set the
// discriminant to the appropriate variant.
}
}
-impl MutVisitor<'tcx> for TransformVisitor<'tcx> {
+impl<'tcx> MutVisitor<'tcx> for TransformVisitor<'tcx> {
fn tcx(&self) -> TyCtxt<'tcx> {
self.tcx
}
storage_liveness: IndexVec<BasicBlock, Option<BitSet<Local>>>,
}
-fn locals_live_across_suspend_points(
+fn locals_live_across_suspend_points<'tcx>(
tcx: TyCtxt<'tcx>,
body: &Body<'tcx>,
always_live_locals: &storage::AlwaysLiveLocals,
/// time. Generates a bitset for every local of all the other locals that may be
/// StorageLive simultaneously with that local. This is used in the layout
/// computation; see `GeneratorLayout` for more.
-fn compute_storage_conflicts(
+fn compute_storage_conflicts<'mir, 'tcx>(
body: &'mir Body<'tcx>,
saved_locals: &GeneratorSavedLocals,
always_live_locals: storage::AlwaysLiveLocals,
local_conflicts: BitMatrix<Local, Local>,
}
-impl rustc_mir_dataflow::ResultsVisitor<'mir, 'tcx> for StorageConflictVisitor<'mir, 'tcx, '_> {
+impl<'mir, 'tcx> rustc_mir_dataflow::ResultsVisitor<'mir, 'tcx>
+ for StorageConflictVisitor<'mir, 'tcx, '_>
+{
type FlowState = BitSet<Local>;
fn visit_statement_before_primary_effect(
}
}
-impl<'body, 'tcx, 's> StorageConflictVisitor<'body, 'tcx, 's> {
+impl StorageConflictVisitor<'_, '_, '_> {
fn apply_state(&mut self, flow_state: &BitSet<Local>, loc: Location) {
// Ignore unreachable blocks.
if self.body.basic_blocks()[loc.block].terminator().kind == TerminatorKind::Unreachable {
self.saved_locals.get(place.local)
}
- fn check_assigned_place(&mut self, place: Place<'tcx>, f: impl FnOnce(&mut Self)) {
+ fn check_assigned_place(&mut self, place: Place<'_>, f: impl FnOnce(&mut Self)) {
if let Some(assigned_local) = self.saved_local_for_direct_place(place) {
assert!(self.assigned_local.is_none(), "`check_assigned_place` must not recurse");
}
}
-impl Visitor<'tcx> for EnsureGeneratorFieldAssignmentsNeverAlias<'_> {
+impl<'tcx> Visitor<'tcx> for EnsureGeneratorFieldAssignmentsNeverAlias<'_> {
fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) {
let lhs = match self.assigned_local {
Some(l) => l,
}
}
-fn inline(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> bool {
+fn inline<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> bool {
let def_id = body.source.def_id();
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
changed: bool,
}
-impl Inliner<'tcx> {
+impl<'tcx> Inliner<'tcx> {
fn process_blocks(&mut self, caller_body: &mut Body<'tcx>, blocks: Range<BasicBlock>) {
for bb in blocks {
let bb_data = &caller_body[bb];
always_live_locals: BitSet<Local>,
}
-impl<'a, 'tcx> Integrator<'a, 'tcx> {
+impl Integrator<'_, '_> {
fn map_local(&self, local: Local) -> Local {
let new = if local == RETURN_PLACE {
self.destination.local
}
}
-impl<'a, 'tcx> MutVisitor<'tcx> for Integrator<'a, 'tcx> {
+impl<'tcx> MutVisitor<'tcx> for Integrator<'_, 'tcx> {
fn tcx(&self) -> TyCtxt<'tcx> {
self.tcx
}
// FIXME: check whether it is cheaper to precompute the entire call graph instead of invoking
// this query riddiculously often.
#[instrument(level = "debug", skip(tcx, root, target))]
-crate fn mir_callgraph_reachable(
+crate fn mir_callgraph_reachable<'tcx>(
tcx: TyCtxt<'tcx>,
(root, target): (ty::Instance<'tcx>, LocalDefId),
) -> bool {
level = "debug",
skip(tcx, param_env, target, stack, seen, recursion_limiter, caller, recursion_limit)
)]
- fn process(
+ fn process<'tcx>(
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
caller: ty::Instance<'tcx>,
local_decls: &'a LocalDecls<'tcx>,
}
-impl<'tcx, 'a> InstCombineContext<'tcx, 'a> {
+impl<'tcx> InstCombineContext<'tcx, '_> {
fn should_combine(&self, source_info: &SourceInfo, rvalue: &Rvalue<'tcx>) -> bool {
self.tcx.consider_optimizing(|| {
format!("InstCombine - Rvalue: {:?} SourceInfo: {:?}", rvalue, source_info)
#![feature(box_patterns)]
#![feature(box_syntax)]
#![feature(crate_visibility_modifier)]
-#![feature(in_band_lifetimes)]
#![feature(iter_zip)]
#![feature(let_else)]
#![feature(map_try_insert)]
tcx: TyCtxt<'tcx>,
set: &'a mut FxHashSet<LocalDefId>,
}
- impl<'a, 'tcx> Visitor<'tcx> for GatherCtors<'a, 'tcx> {
+ impl<'tcx> Visitor<'tcx> for GatherCtors<'_, 'tcx> {
fn visit_variant_data(
&mut self,
v: &'tcx hir::VariantData<'tcx>,
}
/// Compute the main MIR body and the list of MIR bodies of the promoteds.
-fn mir_promoted(
+fn mir_promoted<'tcx>(
tcx: TyCtxt<'tcx>,
def: ty::WithOptConstParam<LocalDefId>,
) -> (&'tcx Steal<Body<'tcx>>, &'tcx Steal<IndexVec<Promoted, Body<'tcx>>>) {
// FIXME(#70073): This pass is responsible for both optimization as well as some lints.
&const_prop::ConstProp,
//
- // FIXME: The old pass manager ran this only at mir-opt-level >= 1, but
- // const-prop runs unconditionally. Should this run unconditionally as well?
+ // Const-prop runs unconditionally, but doesn't mutate the MIR at mir-opt-level=0.
&o1(simplify_branches::SimplifyConstCondition::new("after-const-prop")),
&early_otherwise_branch::EarlyOtherwiseBranch,
&simplify_comparison_integral::SimplifyComparisonIntegral,
}
}
-fn resolve_rust_intrinsic(
+fn resolve_rust_intrinsic<'tcx>(
tcx: TyCtxt<'tcx>,
func_ty: Ty<'tcx>,
) -> Option<(Symbol, SubstsRef<'tcx>)> {
None
}
-fn validate_simd_shuffle(tcx: TyCtxt<'tcx>, args: &[Operand<'tcx>], span: Span) {
+fn validate_simd_shuffle<'tcx>(tcx: TyCtxt<'tcx>, args: &[Operand<'tcx>], span: Span) {
match &args[2] {
Operand::Constant(_) => {} // all good
_ => {
statement_idx: usize,
}
-impl<'a, 'tcx> Patcher<'a, 'tcx> {
+impl<'tcx> Patcher<'_, 'tcx> {
fn patch_expand_statement(
&mut self,
statement: &mut Statement<'tcx>,
}
/// Replaces all uses of `self.to_rename` with `_0`.
-impl MutVisitor<'tcx> for RenameToReturnPlace<'tcx> {
+impl<'tcx> MutVisitor<'tcx> for RenameToReturnPlace<'tcx> {
fn tcx(&self) -> TyCtxt<'tcx> {
self.tcx
}
}
}
-impl Visitor<'tcx> for IsReturnPlaceRead {
+impl<'tcx> Visitor<'tcx> for IsReturnPlaceRead {
fn visit_local(&mut self, &l: &Local, ctxt: PlaceContext, _: Location) {
if l == mir::RETURN_PLACE && ctxt.is_use() && !ctxt.is_place_assignment() {
self.0 = true;
#[derive(Debug, Clone)]
pub struct Lint<T>(pub T);
-impl<T> MirPass<'tcx> for Lint<T>
+impl<'tcx, T> MirPass<'tcx> for Lint<T>
where
T: MirLint<'tcx>,
{
pub struct WithMinOptLevel<T>(pub u32, pub T);
-impl<T> MirPass<'tcx> for WithMinOptLevel<T>
+impl<'tcx, T> MirPass<'tcx> for WithMinOptLevel<T>
where
T: MirPass<'tcx>,
{
}
}
-pub fn run_passes(tcx: TyCtxt<'tcx>, body: &'mir mut Body<'tcx>, passes: &[&dyn MirPass<'tcx>]) {
+pub fn run_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, passes: &[&dyn MirPass<'tcx>]) {
let start_phase = body.phase;
let mut cnt = 0;
}
}
-pub fn validate_body(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, when: String) {
+pub fn validate_body<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, when: String) {
validate::Validator { when, mir_phase: body.phase }.run_pass(tcx, body);
}
-pub fn dump_mir(
+pub fn dump_mir<'tcx>(
tcx: TyCtxt<'tcx>,
body: &Body<'tcx>,
phase: MirPhase,
}
}
-fn is_needs_drop_and_init(
+fn is_needs_drop_and_init<'tcx>(
tcx: TyCtxt<'tcx>,
param_env: ParamEnv<'tcx>,
maybe_inits: &BitSet<MovePathIndex>,
}
}
-fn variant_needs_drop(
+fn variant_needs_drop<'tcx>(
tcx: TyCtxt<'tcx>,
param_env: ParamEnv<'tcx>,
substs: SubstsRef<'tcx>,
}
}
-impl<'a, 'tcx> Visitor<'tcx> for RequiredConstsVisitor<'a, 'tcx> {
+impl<'tcx> Visitor<'tcx> for RequiredConstsVisitor<'_, 'tcx> {
fn visit_constant(&mut self, constant: &Constant<'tcx>, _: Location) {
if let Some(ct) = constant.literal.const_for_ty() {
if let ConstKind::Unevaluated(_) = ct.val {
}
/// Returns the amount of blocks that were duplicated
-pub fn separate_const_switch<'tcx>(body: &mut Body<'tcx>) -> usize {
+pub fn separate_const_switch(body: &mut Body<'_>) -> usize {
let mut new_blocks: SmallVec<[(BasicBlock, BasicBlock); 6]> = SmallVec::new();
let predecessors = body.predecessors();
'block_iter: for (block_id, block) in body.basic_blocks().iter_enumerated() {
build_call_shim(tcx, instance, Some(Adjustment::RefMut), CallKind::Direct(call_mut))
}
- ty::InstanceDef::DropGlue(def_id, ty) => build_drop_shim(tcx, def_id, ty),
+
+ ty::InstanceDef::DropGlue(def_id, ty) => {
+ // FIXME(#91576): Drop shims for generators aren't subject to the MIR passes at the end
+ // of this function. Is this intentional?
+ if let Some(ty::Generator(gen_def_id, substs, _)) = ty.map(ty::TyS::kind) {
+ let body = tcx.optimized_mir(*gen_def_id).generator_drop().unwrap();
+ let body = body.clone().subst(tcx, substs);
+ debug!("make_shim({:?}) = {:?}", instance, body);
+ return body;
+ }
+
+ build_drop_shim(tcx, def_id, ty)
+ }
ty::InstanceDef::CloneShim(def_id, ty) => build_clone_shim(tcx, def_id, ty),
ty::InstanceDef::Virtual(..) => {
bug!("InstanceDef::Virtual ({:?}) is for direct calls only", instance)
};
debug!("make_shim({:?}) = untransformed {:?}", instance, result);
- // In some of the above cases, we seem to be invoking the passes for non-shim MIR bodies.
- // If that happens, there's no need to run them again.
- //
- // FIXME: Is this intentional?
- if result.phase >= MirPhase::Const {
- return result;
- }
-
pm::run_passes(
tcx,
&mut result,
fn build_drop_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, ty: Option<Ty<'tcx>>) -> Body<'tcx> {
debug!("build_drop_shim(def_id={:?}, ty={:?})", def_id, ty);
- // Check if this is a generator, if so, return the drop glue for it
- if let Some(&ty::Generator(gen_def_id, substs, _)) = ty.map(|ty| ty.kind()) {
- let body = tcx.optimized_mir(gen_def_id).generator_drop().unwrap();
- return body.clone().subst(tcx, substs);
- }
+ assert!(!matches!(ty, Some(ty) if ty.is_generator()));
let substs = if let Some(ty) = ty {
tcx.intern_substs(&[ty.into()])
pub param_env: ty::ParamEnv<'tcx>,
}
-impl<'a, 'tcx> fmt::Debug for DropShimElaborator<'a, 'tcx> {
+impl fmt::Debug for DropShimElaborator<'_, '_> {
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
Ok(())
}
sig: ty::FnSig<'tcx>,
}
-impl CloneShimBuilder<'tcx> {
+impl<'tcx> CloneShimBuilder<'tcx> {
fn new(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'tcx>) -> Self {
// we must subst the self_ty because it's
// otherwise going to be TySelf and we can't index
}
}
-pub fn simplify_cfg(tcx: TyCtxt<'tcx>, body: &mut Body<'_>) {
+pub fn simplify_cfg<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
CfgSimplifier::new(body).simplify();
remove_dead_blocks(tcx, body);
}
}
-pub fn remove_dead_blocks(tcx: TyCtxt<'tcx>, body: &mut Body<'_>) {
+pub fn remove_dead_blocks<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let reachable = traversal::reachable_as_bitset(body);
let num_blocks = body.basic_blocks().len();
if num_blocks == reachable.count() {
}
/// Updates the use counts to reflect the removal of given statement.
- fn statement_removed(&mut self, statement: &Statement<'tcx>) {
+ fn statement_removed(&mut self, statement: &Statement<'_>) {
self.increment = false;
// The location of the statement is irrelevant.
}
/// Visits a left-hand side of an assignment.
- fn visit_lhs(&mut self, place: &Place<'tcx>, location: Location) {
+ fn visit_lhs(&mut self, place: &Place<'_>, location: Location) {
if place.is_indirect() {
// A use, not a definition.
self.visit_place(place, PlaceContext::MutatingUse(MutatingUseContext::Store), location);
}
}
-impl Visitor<'_> for UsedLocals {
+impl<'tcx> Visitor<'tcx> for UsedLocals {
fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
match statement.kind {
StatementKind::LlvmInlineAsm(..)
}
/// Removes unused definitions. Updates the used locals to reflect the changes made.
-fn remove_unused_definitions<'a, 'tcx>(used_locals: &'a mut UsedLocals, body: &mut Body<'tcx>) {
+fn remove_unused_definitions(used_locals: &mut UsedLocals, body: &mut Body<'_>) {
// The use counts are updated as we remove the statements. A local might become unused
// during the retain operation, leading to a temporary inconsistency (storage statements or
// definitions referencing the local might remain). For correctness it is crucial that this
} => {
let constant = c.literal.try_eval_bits(tcx, param_env, switch_ty);
if let Some(constant) = constant {
- let otherwise = targets.otherwise();
- let mut ret = TerminatorKind::Goto { target: otherwise };
- for (v, t) in targets.iter() {
- if v == constant {
- ret = TerminatorKind::Goto { target: t };
- break;
- }
- }
- ret
+ let target = targets.target_for_value(constant);
+ TerminatorKind::Goto { target }
} else {
continue;
}
body: &'a Body<'tcx>,
}
-impl<'a, 'tcx> OptimizationFinder<'a, 'tcx> {
+impl<'tcx> OptimizationFinder<'_, 'tcx> {
fn find_optimizations(&self) -> Vec<OptimizationInfo<'tcx>> {
self.body
.basic_blocks()
type StmtIter<'a, 'tcx> = Peekable<Enumerate<Iter<'a, Statement<'tcx>>>>;
- fn is_storage_stmt<'tcx>(stmt: &Statement<'tcx>) -> bool {
+ fn is_storage_stmt(stmt: &Statement<'_>) -> bool {
matches!(stmt.kind, StatementKind::StorageLive(_) | StatementKind::StorageDead(_))
}
/// Eats consecutive `StorageLive` and `StorageDead` Statements.
/// The iterator `stmt_iter` is not advanced if none were found.
- fn try_eat_storage_stmts<'a, 'tcx>(
- stmt_iter: &mut StmtIter<'a, 'tcx>,
+ fn try_eat_storage_stmts(
+ stmt_iter: &mut StmtIter<'_, '_>,
storage_live_stmts: &mut Vec<(usize, Local)>,
storage_dead_stmts: &mut Vec<(usize, Local)>,
) {
})
}
- fn is_tmp_storage_stmt<'tcx>(stmt: &Statement<'tcx>) -> bool {
+ fn is_tmp_storage_stmt(stmt: &Statement<'_>) -> bool {
use rustc_middle::mir::StatementKind::Assign;
if let Assign(box (place, Rvalue::Use(Operand::Copy(p) | Operand::Move(p)))) = &stmt.kind {
place.as_local().is_some() && p.as_local().is_some()
/// Eats consecutive `Assign` Statements.
// The iterator `stmt_iter` is not advanced if none were found.
- fn try_eat_assign_tmp_stmts<'a, 'tcx>(
- stmt_iter: &mut StmtIter<'a, 'tcx>,
+ fn try_eat_assign_tmp_stmts(
+ stmt_iter: &mut StmtIter<'_, '_>,
tmp_assigns: &mut Vec<(Local, Local)>,
nop_stmts: &mut Vec<usize>,
) {
})
}
- fn find_storage_live_dead_stmts_for_local<'tcx>(
+ fn find_storage_live_dead_stmts_for_local(
local: Local,
- stmts: &[Statement<'tcx>],
+ stmts: &[Statement<'_>],
) -> Option<(usize, usize)> {
trace!("looking for {:?}", local);
let mut storage_live_stmt = None;
}
impl LocalUseCounter {
- fn get_local_uses<'tcx>(body: &Body<'tcx>) -> IndexVec<Local, usize> {
+ fn get_local_uses(body: &Body<'_>) -> IndexVec<Local, usize> {
let mut counter = LocalUseCounter { local_uses: IndexVec::from_elem(0, &body.local_decls) };
counter.visit_body(body);
counter.local_uses
}
}
-impl<'tcx> Visitor<'tcx> for LocalUseCounter {
+impl Visitor<'_> for LocalUseCounter {
fn visit_local(&mut self, local: &Local, context: PlaceContext, _location: Location) {
if context.is_storage_marker()
|| context == PlaceContext::NonUse(NonUseContext::VarDebugInfo)
/// ```rust
/// discriminant(_LOCAL_TO_SET) = VAR_IDX;
/// ```
-fn match_set_discr<'tcx>(stmt: &Statement<'tcx>) -> Option<(Local, VariantIdx)> {
+fn match_set_discr(stmt: &Statement<'_>) -> Option<(Local, VariantIdx)> {
match &stmt.kind {
StatementKind::SetDiscriminant { place, variant_index } => {
Some((place.as_local()?, *variant_index))
tcx: TyCtxt<'tcx>,
}
-impl<'a, 'tcx> SimplifyBranchSameOptimizationFinder<'a, 'tcx> {
+impl<'tcx> SimplifyBranchSameOptimizationFinder<'_, 'tcx> {
fn find(&self) -> Vec<SimplifyBranchSameOptimization> {
self.body
.basic_blocks()
}
}
-fn remove_successors<F>(
+fn remove_successors<'tcx, F>(
terminator_kind: &TerminatorKind<'tcx>,
predicate: F,
) -> Option<TerminatorKind<'tcx>>
-use super::{AttrWrapper, Capturing, ForceCollect, Parser, PathStyle};
+use super::{AttrWrapper, Capturing, FnParseMode, ForceCollect, Parser, PathStyle};
use rustc_ast as ast;
use rustc_ast::attr;
use rustc_ast::token::{self, Nonterminal};
AttrWrapper::empty(),
true,
false,
- |_| true,
+ FnParseMode { req_name: |_| true, req_body: true },
ForceCollect::No,
) {
Ok(Some(item)) => {
}
pub fn maybe_consume_incorrect_semicolon(&mut self, items: &[P<Item>]) -> bool {
- if self.eat(&token::Semi) {
+ if self.token.kind == TokenKind::Semi {
+ self.bump();
let mut err = self.struct_span_err(self.prev_token.span, "expected item, found `;`");
err.span_suggestion_short(
self.prev_token.span,
impl<'a> Parser<'a> {
pub fn parse_item(&mut self, force_collect: ForceCollect) -> PResult<'a, Option<P<Item>>> {
- self.parse_item_(|_| true, force_collect).map(|i| i.map(P))
+ let fn_parse_mode = FnParseMode { req_name: |_| true, req_body: true };
+ self.parse_item_(fn_parse_mode, force_collect).map(|i| i.map(P))
}
fn parse_item_(
&mut self,
- req_name: ReqName,
+ fn_parse_mode: FnParseMode,
force_collect: ForceCollect,
) -> PResult<'a, Option<Item>> {
let attrs = self.parse_outer_attributes()?;
- self.parse_item_common(attrs, true, false, req_name, force_collect)
+ self.parse_item_common(attrs, true, false, fn_parse_mode, force_collect)
}
pub(super) fn parse_item_common(
attrs: AttrWrapper,
mac_allowed: bool,
attrs_allowed: bool,
- req_name: ReqName,
+ fn_parse_mode: FnParseMode,
force_collect: ForceCollect,
) -> PResult<'a, Option<Item>> {
// Don't use `maybe_whole` so that we have precise control
let mut unclosed_delims = vec![];
let item =
self.collect_tokens_trailing_token(attrs, force_collect, |this: &mut Self, attrs| {
- let item = this.parse_item_common_(attrs, mac_allowed, attrs_allowed, req_name);
+ let item =
+ this.parse_item_common_(attrs, mac_allowed, attrs_allowed, fn_parse_mode);
unclosed_delims.append(&mut this.unclosed_delims);
Ok((item?, TrailingToken::None))
})?;
mut attrs: Vec<Attribute>,
mac_allowed: bool,
attrs_allowed: bool,
- req_name: ReqName,
+ fn_parse_mode: FnParseMode,
) -> PResult<'a, Option<Item>> {
let lo = self.token.span;
let vis = self.parse_visibility(FollowedByType::No)?;
let mut def = self.parse_defaultness();
- let kind = self.parse_item_kind(&mut attrs, mac_allowed, lo, &vis, &mut def, req_name)?;
+ let kind =
+ self.parse_item_kind(&mut attrs, mac_allowed, lo, &vis, &mut def, fn_parse_mode)?;
if let Some((ident, kind)) = kind {
self.error_on_unconsumed_default(def, &kind);
let span = lo.to(self.prev_token.span);
lo: Span,
vis: &Visibility,
def: &mut Defaultness,
- req_name: ReqName,
+ fn_parse_mode: FnParseMode,
) -> PResult<'a, Option<ItemInfo>> {
let def_final = def == &Defaultness::Final;
let mut def = || mem::replace(def, Defaultness::Final);
(Ident::empty(), ItemKind::Use(tree))
} else if self.check_fn_front_matter(def_final) {
// FUNCTION ITEM
- let (ident, sig, generics, body) = self.parse_fn(attrs, req_name, lo)?;
+ let (ident, sig, generics, body) = self.parse_fn(attrs, fn_parse_mode, lo)?;
(ident, ItemKind::Fn(Box::new(Fn { defaultness: def(), sig, generics, body })))
} else if self.eat_keyword(kw::Extern) {
if self.eat_keyword(kw::Crate) {
&mut self,
force_collect: ForceCollect,
) -> PResult<'a, Option<Option<P<AssocItem>>>> {
- self.parse_assoc_item(|_| true, force_collect)
+ let fn_parse_mode = FnParseMode { req_name: |_| true, req_body: true };
+ self.parse_assoc_item(fn_parse_mode, force_collect)
}
pub fn parse_trait_item(
&mut self,
force_collect: ForceCollect,
) -> PResult<'a, Option<Option<P<AssocItem>>>> {
- self.parse_assoc_item(|edition| edition >= Edition::Edition2018, force_collect)
+ let fn_parse_mode =
+ FnParseMode { req_name: |edition| edition >= Edition::Edition2018, req_body: false };
+ self.parse_assoc_item(fn_parse_mode, force_collect)
}
/// Parses associated items.
fn parse_assoc_item(
&mut self,
- req_name: ReqName,
+ fn_parse_mode: FnParseMode,
force_collect: ForceCollect,
) -> PResult<'a, Option<Option<P<AssocItem>>>> {
- Ok(self.parse_item_(req_name, force_collect)?.map(
+ Ok(self.parse_item_(fn_parse_mode, force_collect)?.map(
|Item { attrs, id, span, vis, ident, kind, tokens }| {
let kind = match AssocItemKind::try_from(kind) {
Ok(kind) => kind,
&mut self,
force_collect: ForceCollect,
) -> PResult<'a, Option<Option<P<ForeignItem>>>> {
- Ok(self.parse_item_(|_| true, force_collect)?.map(
+ let fn_parse_mode = FnParseMode { req_name: |_| true, req_body: false };
+ Ok(self.parse_item_(fn_parse_mode, force_collect)?.map(
|Item { attrs, id, span, vis, ident, kind, tokens }| {
let kind = match ForeignItemKind::try_from(kind) {
Ok(kind) => kind,
if !is_raw && ident.is_reserved() {
let err = if self.check_fn_front_matter(false) {
// We use `parse_fn` to get a span for the function
- if let Err(mut db) = self.parse_fn(&mut Vec::new(), |_| true, lo) {
+ let fn_parse_mode = FnParseMode { req_name: |_| true, req_body: true };
+ if let Err(mut db) = self.parse_fn(&mut Vec::new(), fn_parse_mode, lo) {
db.delay_as_bug();
}
let mut err = self.struct_span_err(
/// The parsing configuration used to parse a parameter list (see `parse_fn_params`).
///
/// The function decides if, per-parameter `p`, `p` must have a pattern or just a type.
+///
+/// This function pointer accepts an edition, because in edition 2015, trait declarations
+/// were allowed to omit parameter names. In 2018, they became required.
type ReqName = fn(Edition) -> bool;
+/// Parsing configuration for functions.
+///
+/// The syntax of function items is slightly different within trait definitions,
+/// impl blocks, and modules. It is still parsed using the same code, just with
+/// different flags set, so that even when the input is wrong and produces a parse
+/// error, it still gets into the AST and the rest of the parser and
+/// type checker can run.
+#[derive(Clone, Copy)]
+pub(crate) struct FnParseMode {
+ /// A function pointer that decides if, per-parameter `p`, `p` must have a
+ /// pattern or just a type. This field affects parsing of the parameters list.
+ ///
+ /// ```text
+ /// fn foo(alef: A) -> X { X::new() }
+ /// -----^^ affects parsing this part of the function signature
+ /// |
+ /// if req_name returns false, then this name is optional
+ ///
+ /// fn bar(A) -> X;
+ /// ^
+ /// |
+ /// if req_name returns true, this is an error
+ /// ```
+ ///
+ /// Calling this function pointer should only return false if:
+ ///
+ /// * The item is being parsed inside of a trait definition.
+ /// Within an impl block or a module, it should always evaluate
+ /// to true.
+ /// * The span is from Edition 2015. In particular, you can get a
+ /// 2015 span inside a 2021 crate using macros.
+ pub req_name: ReqName,
+ /// If this flag is set to `true`, then plain, semicolon-terminated function
+ /// prototypes are not allowed here.
+ ///
+ /// ```text
+ /// fn foo(alef: A) -> X { X::new() }
+ /// ^^^^^^^^^^^^
+ /// |
+ /// this is always allowed
+ ///
+ /// fn bar(alef: A, bet: B) -> X;
+ /// ^
+ /// |
+ /// if req_body is set to true, this is an error
+ /// ```
+ ///
+ /// This field should only be set to false if the item is inside of a trait
+ /// definition or extern block. Within an impl block or a module, it should
+ /// always be set to true.
+ pub req_body: bool,
+}
+
/// Parsing of functions and methods.
impl<'a> Parser<'a> {
/// Parse a function starting from the front matter (`const ...`) to the body `{ ... }` or `;`.
fn parse_fn(
&mut self,
attrs: &mut Vec<Attribute>,
- req_name: ReqName,
+ fn_parse_mode: FnParseMode,
sig_lo: Span,
) -> PResult<'a, (Ident, FnSig, Generics, Option<P<Block>>)> {
let header = self.parse_fn_front_matter()?; // `const ... fn`
let ident = self.parse_ident()?; // `foo`
let mut generics = self.parse_generics()?; // `<'a, T, ...>`
- let decl = self.parse_fn_decl(req_name, AllowPlus::Yes, RecoverReturnSign::Yes)?; // `(p: u8, ...)`
+ let decl =
+ self.parse_fn_decl(fn_parse_mode.req_name, AllowPlus::Yes, RecoverReturnSign::Yes)?; // `(p: u8, ...)`
generics.where_clause = self.parse_where_clause()?; // `where T: Ord`
let mut sig_hi = self.prev_token.span;
- let body = self.parse_fn_body(attrs, &ident, &mut sig_hi)?; // `;` or `{ ... }`.
+ let body = self.parse_fn_body(attrs, &ident, &mut sig_hi, fn_parse_mode.req_body)?; // `;` or `{ ... }`.
let fn_sig_span = sig_lo.to(sig_hi);
Ok((ident, FnSig { header, decl, span: fn_sig_span }, generics, body))
}
attrs: &mut Vec<Attribute>,
ident: &Ident,
sig_hi: &mut Span,
+ req_body: bool,
) -> PResult<'a, Option<P<Block>>> {
- let (inner_attrs, body) = if self.eat(&token::Semi) {
+ let has_semi = if req_body {
+ self.token.kind == TokenKind::Semi
+ } else {
+ // Only include `;` in list of expected tokens if body is not required
+ self.check(&TokenKind::Semi)
+ };
+ let (inner_attrs, body) = if has_semi {
// Include the trailing semicolon in the span of the signature
+ self.expect_semi()?;
*sig_hi = self.prev_token.span;
(Vec::new(), None)
} else if self.check(&token::OpenDelim(token::Brace)) || self.token.is_whole_block() {
.emit();
(Vec::new(), Some(self.mk_block_err(span)))
} else {
- if let Err(mut err) =
- self.expected_one_of_not_found(&[], &[token::Semi, token::OpenDelim(token::Brace)])
- {
+ let expected = if req_body {
+ &[token::OpenDelim(token::Brace)][..]
+ } else {
+ &[token::Semi, token::OpenDelim(token::Brace)]
+ };
+ if let Err(mut err) = self.expected_one_of_not_found(&[], &expected) {
if self.token.kind == token::CloseDelim(token::Brace) {
// The enclosing `mod`, `trait` or `impl` is being closed, so keep the `fn` in
// the AST for typechecking.
pub use attr_wrapper::AttrWrapper;
pub use diagnostics::AttemptLocalParseRecovery;
use diagnostics::Error;
+pub(crate) use item::FnParseMode;
pub use pat::{RecoverColon, RecoverComma};
pub use path::PathStyle;
use super::pat::RecoverComma;
use super::path::PathStyle;
use super::TrailingToken;
-use super::{AttrWrapper, BlockMode, ForceCollect, Parser, Restrictions, SemiColonMode};
+use super::{
+ AttrWrapper, BlockMode, FnParseMode, ForceCollect, Parser, Restrictions, SemiColonMode,
+};
use crate::maybe_whole;
use rustc_ast as ast;
} else {
self.parse_stmt_path_start(lo, attrs)
}?
- } else if let Some(item) =
- self.parse_item_common(attrs.clone(), false, true, |_| true, force_collect)?
- {
+ } else if let Some(item) = self.parse_item_common(
+ attrs.clone(),
+ false,
+ true,
+ FnParseMode { req_name: |_| true, req_body: true },
+ force_collect,
+ )? {
// FIXME: Bad copy of attrs
self.mk_stmt(lo.to(item.span), StmtKind::Item(P(item)))
} else if self.eat(&token::Semi) {
}};
}
+/// A byte that [cannot occur in UTF8 sequences][utf8]. Used to mark the end of a string.
+/// This way we can skip validation and still be relatively sure that deserialization
+/// did not desynchronize.
+///
+/// [utf8]: https://en.wikipedia.org/w/index.php?title=UTF-8&oldid=1058865525#Codepage_layout
+const STR_SENTINEL: u8 = 0xC1;
+
impl serialize::Encoder for Encoder {
type Error = !;
#[inline]
fn emit_str(&mut self, v: &str) -> EncodeResult {
self.emit_usize(v.len())?;
- self.emit_raw_bytes(v.as_bytes())
+ self.emit_raw_bytes(v.as_bytes())?;
+ self.emit_u8(STR_SENTINEL)
}
#[inline]
#[inline]
fn emit_str(&mut self, v: &str) -> FileEncodeResult {
self.emit_usize(v.len())?;
- self.emit_raw_bytes(v.as_bytes())
+ self.emit_raw_bytes(v.as_bytes())?;
+ self.emit_u8(STR_SENTINEL)
}
#[inline]
#[inline]
fn read_str(&mut self) -> Result<Cow<'_, str>, Self::Error> {
let len = self.read_usize()?;
- let s = std::str::from_utf8(&self.data[self.position..self.position + len]).unwrap();
- self.position += len;
+ let sentinel = self.data[self.position + len];
+ assert!(sentinel == STR_SENTINEL);
+ let s = unsafe {
+ std::str::from_utf8_unchecked(&self.data[self.position..self.position + len])
+ };
+ self.position += len + 1;
Ok(Cow::Borrowed(s))
}
edition: DEFAULT_EDITION,
json_artifact_notifications: false,
json_unused_externs: false,
+ json_future_incompat: false,
pretty: None,
working_dir: RealFileName::LocalPath(std::env::current_dir().unwrap()),
}
pub json_rendered: HumanReadableErrorType,
pub json_artifact_notifications: bool,
pub json_unused_externs: bool,
+ pub json_future_incompat: bool,
}
/// Parse the `--json` flag.
let mut json_color = ColorConfig::Never;
let mut json_artifact_notifications = false;
let mut json_unused_externs = false;
+ let mut json_future_incompat = false;
for option in matches.opt_strs("json") {
// For now conservatively forbid `--color` with `--json` since `--json`
// won't actually be emitting any colors and anything colorized is
"diagnostic-rendered-ansi" => json_color = ColorConfig::Always,
"artifacts" => json_artifact_notifications = true,
"unused-externs" => json_unused_externs = true,
+ "future-incompat" => json_future_incompat = true,
s => early_error(
ErrorOutputType::default(),
&format!("unknown `--json` option `{}`", s),
json_rendered: json_rendered(json_color),
json_artifact_notifications,
json_unused_externs,
+ json_future_incompat,
}
}
let edition = parse_crate_edition(matches);
- let JsonConfig { json_rendered, json_artifact_notifications, json_unused_externs } =
- parse_json(matches);
+ let JsonConfig {
+ json_rendered,
+ json_artifact_notifications,
+ json_unused_externs,
+ json_future_incompat,
+ } = parse_json(matches);
let error_format = parse_error_format(matches, color, json_rendered);
edition,
json_artifact_notifications,
json_unused_externs,
+ json_future_incompat,
pretty,
working_dir,
}
/// `true` if we're emitting a JSON blob containing the unused externs
json_unused_externs: bool [UNTRACKED],
+ /// `true` if we're emitting a JSON job containg a future-incompat report for lints
+ json_future_incompat: bool [TRACKED],
+
pretty: Option<PpMode> [UNTRACKED],
/// The (potentially remapped) working directory
computed `block` spans (one span encompassing a block's terminator and \
all statements). If `-Z instrument-coverage` is also enabled, create \
an additional `.html` file showing the computed coverage spans."),
- emit_future_incompat_report: bool = (false, parse_bool, [UNTRACKED],
- "emits a future-incompatibility report for lints (RFC 2834)"),
emit_stack_sizes: bool = (false, parse_bool, [UNTRACKED],
"emit a section containing stack size metadata (default: no)"),
fewer_names: Option<bool> = (None, parse_opt_bool, [TRACKED],
}
fn emit_future_breakage(&self) {
- if !self.opts.debugging_opts.emit_future_incompat_report {
+ if !self.opts.json_future_incompat {
return;
}
core_panic_macro,
cosf32,
cosf64,
+ count,
cr,
crate_id,
crate_in_paths,
reg64,
reg_abcd,
reg_byte,
+ reg_iw,
reg_nonzero,
+ reg_pair,
+ reg_ptr,
reg_thumb,
+ reg_upper,
register_attr,
register_tool,
relaxed_adts,
--- /dev/null
+use super::{InlineAsmArch, InlineAsmType};
+use rustc_macros::HashStable_Generic;
+use std::fmt;
+
+def_reg_class! {
+ Avr AvrInlineAsmRegClass {
+ reg,
+ reg_upper,
+ reg_pair,
+ reg_iw,
+ reg_ptr,
+ }
+}
+
+impl AvrInlineAsmRegClass {
+ pub fn valid_modifiers(self, _arch: InlineAsmArch) -> &'static [char] {
+ match self {
+ Self::reg_pair | Self::reg_iw | Self::reg_ptr => &['h', 'l'],
+ _ => &[],
+ }
+ }
+
+ pub fn suggest_class(self, _arch: InlineAsmArch, _ty: InlineAsmType) -> Option<Self> {
+ None
+ }
+
+ pub fn suggest_modifier(
+ self,
+ _arch: InlineAsmArch,
+ _ty: InlineAsmType,
+ ) -> Option<(char, &'static str)> {
+ None
+ }
+
+ pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> {
+ None
+ }
+
+ pub fn supported_types(
+ self,
+ _arch: InlineAsmArch,
+ ) -> &'static [(InlineAsmType, Option<&'static str>)] {
+ match self {
+ Self::reg => types! { _: I8; },
+ Self::reg_upper => types! { _: I8; },
+ Self::reg_pair => types! { _: I16; },
+ Self::reg_iw => types! { _: I16; },
+ Self::reg_ptr => types! { _: I16; },
+ }
+ }
+}
+
+def_regs! {
+ Avr AvrInlineAsmReg AvrInlineAsmRegClass {
+ r2: reg = ["r2"],
+ r3: reg = ["r3"],
+ r4: reg = ["r4"],
+ r5: reg = ["r5"],
+ r6: reg = ["r6"],
+ r7: reg = ["r7"],
+ r8: reg = ["r8"],
+ r9: reg = ["r9"],
+ r10: reg = ["r10"],
+ r11: reg = ["r11"],
+ r12: reg = ["r12"],
+ r13: reg = ["r13"],
+ r14: reg = ["r14"],
+ r15: reg = ["r15"],
+ r16: reg, reg_upper = ["r16"],
+ r17: reg, reg_upper = ["r17"],
+ r18: reg, reg_upper = ["r18"],
+ r19: reg, reg_upper = ["r19"],
+ r20: reg, reg_upper = ["r20"],
+ r21: reg, reg_upper = ["r21"],
+ r22: reg, reg_upper = ["r22"],
+ r23: reg, reg_upper = ["r23"],
+ r24: reg, reg_upper = ["r24"],
+ r25: reg, reg_upper = ["r25"],
+ r26: reg, reg_upper = ["r26", "XL"],
+ r27: reg, reg_upper = ["r27", "XH"],
+ r30: reg, reg_upper = ["r30", "ZL"],
+ r31: reg, reg_upper = ["r31", "ZH"],
+
+ r3r2: reg_pair = ["r3r2"],
+ r5r4: reg_pair = ["r5r4"],
+ r7r6: reg_pair = ["r7r6"],
+ r9r8: reg_pair = ["r9r8"],
+ r11r10: reg_pair = ["r11r10"],
+ r13r12: reg_pair = ["r13r12"],
+ r15r14: reg_pair = ["r15r14"],
+ r17r16: reg_pair = ["r17r16"],
+ r19r18: reg_pair = ["r19r18"],
+ r21r20: reg_pair = ["r21r20"],
+ r23r22: reg_pair = ["r23r22"],
+
+ r25r24: reg_iw, reg_pair = ["r25r24"],
+
+ X: reg_ptr, reg_iw, reg_pair = ["r27r26", "X"],
+ Z: reg_ptr, reg_iw, reg_pair = ["r31r30", "Z"],
+
+ #error = ["Y", "YL", "YH"] =>
+ "the frame pointer cannot be used as an operand for inline asm",
+ #error = ["SP", "SPL", "SPH"] =>
+ "the stack pointer cannot be used as an operand for inline asm",
+ #error = ["r0", "r1", "r1r0"] =>
+ "r0 and r1 are not available due to an issue in LLVM",
+ }
+}
+
+macro_rules! emit_pairs {
+ (
+ $self:ident $modifier:ident,
+ $($pair:ident $name:literal $hi:literal $lo:literal,)*
+ ) => {
+ match ($self, $modifier) {
+ $(
+ (AvrInlineAsmReg::$pair, Some('h')) => $hi,
+ (AvrInlineAsmReg::$pair, Some('l')) => $lo,
+ (AvrInlineAsmReg::$pair, _) => $name,
+ )*
+ _ => $self.name(),
+ }
+ };
+}
+
+impl AvrInlineAsmReg {
+ pub fn emit(
+ self,
+ out: &mut dyn fmt::Write,
+ _arch: InlineAsmArch,
+ modifier: Option<char>,
+ ) -> fmt::Result {
+ let name = emit_pairs! {
+ self modifier,
+ Z "Z" "ZH" "ZL",
+ X "X" "XH" "XL",
+ r25r24 "r25:r24" "r25" "r24",
+ r23r22 "r23:r22" "r23" "r22",
+ r21r20 "r21:r20" "r21" "r20",
+ r19r18 "r19:r18" "r19" "r18",
+ r17r16 "r17:r16" "r17" "r16",
+ r15r14 "r15:r14" "r15" "r14",
+ r13r12 "r13:r12" "r13" "r12",
+ r11r10 "r11:r10" "r11" "r10",
+ r9r8 "r9:r8" "r9" "r8",
+ r7r6 "r7:r6" "r7" "r6",
+ r5r4 "r5:r4" "r5" "r4",
+ r3r2 "r3:r2" "r3" "r2",
+ };
+ out.write_str(name)
+ }
+
+ pub fn overlapping_regs(self, mut cb: impl FnMut(AvrInlineAsmReg)) {
+ cb(self);
+
+ macro_rules! reg_conflicts {
+ (
+ $(
+ $pair:ident : $hi:ident $lo:ident,
+ )*
+ ) => {
+ match self {
+ $(
+ Self::$pair => {
+ cb(Self::$hi);
+ cb(Self::$lo);
+ }
+ Self::$hi => {
+ cb(Self::$pair);
+ }
+ Self::$lo => {
+ cb(Self::$pair);
+ }
+ )*
+ }
+ };
+ }
+
+ reg_conflicts! {
+ Z : r31 r30,
+ X : r27 r26,
+ r25r24 : r25 r24,
+ r23r22 : r23 r22,
+ r21r20 : r21 r20,
+ r19r18 : r19 r18,
+ r17r16 : r17 r16,
+ r15r14 : r15 r14,
+ r13r12 : r13 r12,
+ r11r10 : r11 r10,
+ r9r8 : r9 r8,
+ r7r6 : r7 r6,
+ r5r4 : r5 r4,
+ r3r2 : r3 r2,
+ }
+ }
+}
mod aarch64;
mod arm;
+mod avr;
mod bpf;
mod hexagon;
mod mips;
pub use aarch64::{AArch64InlineAsmReg, AArch64InlineAsmRegClass};
pub use arm::{ArmInlineAsmReg, ArmInlineAsmRegClass};
+pub use avr::{AvrInlineAsmReg, AvrInlineAsmRegClass};
pub use bpf::{BpfInlineAsmReg, BpfInlineAsmRegClass};
pub use hexagon::{HexagonInlineAsmReg, HexagonInlineAsmRegClass};
pub use mips::{MipsInlineAsmReg, MipsInlineAsmRegClass};
Wasm32,
Wasm64,
Bpf,
+ Avr,
}
impl FromStr for InlineAsmArch {
"wasm32" => Ok(Self::Wasm32),
"wasm64" => Ok(Self::Wasm64),
"bpf" => Ok(Self::Bpf),
+ "avr" => Ok(Self::Avr),
_ => Err(()),
}
}
SpirV(SpirVInlineAsmReg),
Wasm(WasmInlineAsmReg),
Bpf(BpfInlineAsmReg),
+ Avr(AvrInlineAsmReg),
// Placeholder for invalid register constraints for the current target
Err,
}
Self::Mips(r) => r.name(),
Self::S390x(r) => r.name(),
Self::Bpf(r) => r.name(),
+ Self::Avr(r) => r.name(),
Self::Err => "<reg>",
}
}
Self::Mips(r) => InlineAsmRegClass::Mips(r.reg_class()),
Self::S390x(r) => InlineAsmRegClass::S390x(r.reg_class()),
Self::Bpf(r) => InlineAsmRegClass::Bpf(r.reg_class()),
+ Self::Avr(r) => InlineAsmRegClass::Avr(r.reg_class()),
Self::Err => InlineAsmRegClass::Err,
}
}
InlineAsmArch::Bpf => {
Self::Bpf(BpfInlineAsmReg::parse(arch, has_feature, target, &name)?)
}
+ InlineAsmArch::Avr => {
+ Self::Avr(AvrInlineAsmReg::parse(arch, has_feature, target, &name)?)
+ }
})
}
Self::Mips(r) => r.emit(out, arch, modifier),
Self::S390x(r) => r.emit(out, arch, modifier),
Self::Bpf(r) => r.emit(out, arch, modifier),
+ Self::Avr(r) => r.emit(out, arch, modifier),
Self::Err => unreachable!("Use of InlineAsmReg::Err"),
}
}
Self::Mips(_) => cb(self),
Self::S390x(_) => cb(self),
Self::Bpf(r) => r.overlapping_regs(|r| cb(Self::Bpf(r))),
+ Self::Avr(r) => r.overlapping_regs(|r| cb(Self::Avr(r))),
Self::Err => unreachable!("Use of InlineAsmReg::Err"),
}
}
SpirV(SpirVInlineAsmRegClass),
Wasm(WasmInlineAsmRegClass),
Bpf(BpfInlineAsmRegClass),
+ Avr(AvrInlineAsmRegClass),
// Placeholder for invalid register constraints for the current target
Err,
}
Self::SpirV(r) => r.name(),
Self::Wasm(r) => r.name(),
Self::Bpf(r) => r.name(),
+ Self::Avr(r) => r.name(),
Self::Err => rustc_span::symbol::sym::reg,
}
}
Self::SpirV(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::SpirV),
Self::Wasm(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Wasm),
Self::Bpf(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Bpf),
+ Self::Avr(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Avr),
Self::Err => unreachable!("Use of InlineAsmRegClass::Err"),
}
}
Self::SpirV(r) => r.suggest_modifier(arch, ty),
Self::Wasm(r) => r.suggest_modifier(arch, ty),
Self::Bpf(r) => r.suggest_modifier(arch, ty),
+ Self::Avr(r) => r.suggest_modifier(arch, ty),
Self::Err => unreachable!("Use of InlineAsmRegClass::Err"),
}
}
Self::SpirV(r) => r.default_modifier(arch),
Self::Wasm(r) => r.default_modifier(arch),
Self::Bpf(r) => r.default_modifier(arch),
+ Self::Avr(r) => r.default_modifier(arch),
Self::Err => unreachable!("Use of InlineAsmRegClass::Err"),
}
}
Self::SpirV(r) => r.supported_types(arch),
Self::Wasm(r) => r.supported_types(arch),
Self::Bpf(r) => r.supported_types(arch),
+ Self::Avr(r) => r.supported_types(arch),
Self::Err => unreachable!("Use of InlineAsmRegClass::Err"),
}
}
Self::Wasm(WasmInlineAsmRegClass::parse(arch, name)?)
}
InlineAsmArch::Bpf => Self::Bpf(BpfInlineAsmRegClass::parse(arch, name)?),
+ InlineAsmArch::Avr => Self::Avr(AvrInlineAsmRegClass::parse(arch, name)?),
})
}
Self::SpirV(r) => r.valid_modifiers(arch),
Self::Wasm(r) => r.valid_modifiers(arch),
Self::Bpf(r) => r.valid_modifiers(arch),
+ Self::Avr(r) => r.valid_modifiers(arch),
Self::Err => unreachable!("Use of InlineAsmRegClass::Err"),
}
}
bpf::fill_reg_map(arch, has_feature, target, &mut map);
map
}
+ InlineAsmArch::Avr => {
+ let mut map = avr::regclass_map();
+ avr::fill_reg_map(arch, has_feature, target, &mut map);
+ map
+ }
}
}
use crate::spec::{Target, TargetOptions};
pub fn target() -> Target {
- let mut base = super::linux_base::opts();
+ let mut base = super::linux_gnu_base::opts();
base.max_atomic_width = Some(32);
Target {
("powerpc-unknown-freebsd", powerpc_unknown_freebsd),
("powerpc64-unknown-freebsd", powerpc64_unknown_freebsd),
("powerpc64le-unknown-freebsd", powerpc64le_unknown_freebsd),
+ ("riscv64gc-unknown-freebsd", riscv64gc_unknown_freebsd),
("x86_64-unknown-freebsd", x86_64_unknown_freebsd),
("x86_64-unknown-dragonfly", x86_64_unknown_dragonfly),
--- /dev/null
+use crate::spec::{CodeModel, Target, TargetOptions};
+
+pub fn target() -> Target {
+ Target {
+ llvm_target: "riscv64-unknown-freebsd".to_string(),
+ pointer_width: 64,
+ data_layout: "e-m:e-p:64:64-i64:64-i128:128-n64-S128".to_string(),
+ arch: "riscv64".to_string(),
+ options: TargetOptions {
+ code_model: Some(CodeModel::Medium),
+ cpu: "generic-rv64".to_string(),
+ features: "+m,+a,+f,+d,+c".to_string(),
+ llvm_abiname: "lp64d".to_string(),
+ max_atomic_width: Some(64),
+ ..super::freebsd_base::opts()
+ },
+ }
+}
let arg = self.recurse_build(source)?;
self.nodes.push(Node::Cast(abstract_const::CastKind::As, arg, node.ty))
}
-
+ ExprKind::Borrow{ arg, ..} => {
+ let arg_node = &self.body.exprs[*arg];
+
+ // Skip reborrows for now until we allow Deref/Borrow/AddressOf
+ // expressions.
+ // FIXME(generic_const_exprs): Verify/explain why this is sound
+ if let ExprKind::Deref {arg} = arg_node.kind {
+ self.recurse_build(arg)?
+ } else {
+ self.maybe_supported_error(
+ node.span,
+ "borrowing is not supported in generic constants",
+ )?
+ }
+ }
// FIXME(generic_const_exprs): We may want to support these.
- ExprKind::AddressOf { .. }
- | ExprKind::Borrow { .. }
- | ExprKind::Deref { .. } => self.maybe_supported_error(
+ ExprKind::AddressOf { .. } | ExprKind::Deref {..}=> self.maybe_supported_error(
node.span,
- "dereferencing is not supported in generic constants",
+ "dereferencing or taking the address is not supported in generic constants",
)?,
ExprKind::Repeat { .. } | ExprKind::Array { .. } => self.maybe_supported_error(
node.span,
}
has_emitted
}
- ty::Tuple(ref tys) => {
+ ty::Tuple(_) => {
let mut has_emitted = false;
- let spans = if let Some(hir::ExprKind::Tup(comps)) = data.expr.map(|e| &e.kind) {
- debug_assert_eq!(comps.len(), tys.len());
- comps.iter().map(|e| e.span).collect()
- } else {
- vec![]
+ let comps = match data.expr.map(|e| &e.kind) {
+ Some(hir::ExprKind::Tup(comps)) => {
+ debug_assert_eq!(comps.len(), ty.tuple_fields().count());
+ Some(comps)
+ }
+ _ => None,
};
- for (i, ty) in tys.iter().map(|k| k.expect_ty()).enumerate() {
+ for (i, ty) in ty.tuple_fields().enumerate() {
let descr_post = &format!(" in tuple element {}", i);
- let span = *spans.get(i).unwrap_or(&data.source_span);
+ let span = comps.and_then(|c| c.get(i)).map(|e| e.span).unwrap_or(data.source_span);
if check_must_not_suspend_ty(
fcx,
ty,
hir_id,
- SuspendCheckData { descr_post, source_span: span, ..data },
+ SuspendCheckData {
+ descr_post,
+ expr: comps.and_then(|comps| comps.get(i)),
+ source_span: span,
+ ..data
+ },
) {
has_emitted = true;
}
}
}
+ fn is_slice_ty(&self, ty: Ty<'tcx>, span: Span) -> bool {
+ self.autoderef(span, ty).any(|(ty, _)| matches!(ty.kind(), ty::Slice(..) | ty::Array(..)))
+ }
+
pub fn report_method_error(
&self,
mut span: Span,
let mut restrict_type_params = false;
let mut unsatisfied_bounds = false;
- if !unsatisfied_predicates.is_empty() {
+ if item_name.name == sym::count && self.is_slice_ty(actual, span) {
+ let msg = "consider using `len` instead";
+ if let SelfSource::MethodCall(_expr) = source {
+ err.span_suggestion_short(
+ span,
+ msg,
+ String::from("len"),
+ Applicability::MachineApplicable,
+ );
+ } else {
+ err.span_label(span, msg);
+ }
+ if let Some(iterator_trait) = self.tcx.get_diagnostic_item(sym::Iterator) {
+ let iterator_trait = self.tcx.def_path_str(iterator_trait);
+ err.note(&format!("`count` is defined on `{iterator_trait}`, which `{actual}` does not implement"));
+ }
+ } else if !unsatisfied_predicates.is_empty() {
let def_span = |def_id| {
self.tcx.sess.source_map().guess_head_span(self.tcx.def_span(def_id))
};
}
}
- let mut fallback_span = true;
- let msg = "remove this method call";
if item_name.name == sym::as_str && actual.peel_refs().is_str() {
+ let msg = "remove this method call";
+ let mut fallback_span = true;
if let SelfSource::MethodCall(expr) = source {
let call_expr =
self.tcx.hir().expect_expr(self.tcx.hir().get_parent_node(expr.hir_id));
) -> bool /* did we suggest to call a function because of missing parentheses? */ {
err.span_label(span, ty.to_string());
if let FnDef(def_id, _) = *ty.kind() {
- let source_map = self.tcx.sess.source_map();
if !self.tcx.has_typeck_results(def_id) {
return false;
}
.lookup_op_method(fn_sig.output(), &[other_ty], Op::Binary(op, is_assign))
.is_ok()
{
- if let Ok(snippet) = source_map.span_to_snippet(span) {
- let (variable_snippet, applicability) = if !fn_sig.inputs().is_empty() {
- (format!("{}( /* arguments */ )", snippet), Applicability::HasPlaceholders)
- } else {
- (format!("{}()", snippet), Applicability::MaybeIncorrect)
- };
+ let (variable_snippet, applicability) = if !fn_sig.inputs().is_empty() {
+ ("( /* arguments */ )".to_string(), Applicability::HasPlaceholders)
+ } else {
+ ("()".to_string(), Applicability::MaybeIncorrect)
+ };
- err.span_suggestion(
- span,
- "you might have forgotten to call this function",
- variable_snippet,
- applicability,
- );
- }
+ err.span_suggestion_verbose(
+ span.shrink_to_hi(),
+ "you might have forgotten to call this function",
+ variable_snippet,
+ applicability,
+ );
return true;
}
}
use rustc_middle::ty::util::Discr;
use rustc_middle::ty::util::IntTypeExt;
use rustc_middle::ty::{self, AdtKind, Const, DefIdTree, Ty, TyCtxt};
-use rustc_middle::ty::{ReprOptions, ToPredicate, WithConstness};
+use rustc_middle::ty::{ReprOptions, ToPredicate, TypeFoldable, WithConstness};
use rustc_session::lint;
use rustc_session::parse::feature_err;
use rustc_span::symbol::{kw, sym, Ident, Symbol};
visitor.visit_ty(ty);
let mut diag = bad_placeholder_type(tcx, visitor.0, "return type");
let ret_ty = fn_sig.skip_binder().output();
- if ret_ty != tcx.ty_error() {
+ if !ret_ty.references_error() {
if !ret_ty.is_closure() {
let ret_ty_str = match ret_ty.kind() {
// Suggest a function pointer return type instead of a unique function definition
// We've encountered an `AnonConst` in some path, so we need to
// figure out which generic parameter it corresponds to and return
// the relevant type.
- let (arg_index, segment) = path
+ let filtered = path
.segments
.iter()
.filter_map(|seg| seg.args.map(|args| (args.args, seg)))
.filter(|arg| arg.is_const())
.position(|arg| arg.id() == hir_id)
.map(|index| (index, seg))
- })
- .unwrap_or_else(|| {
- bug!("no arg matching AnonConst in path");
});
+ let (arg_index, segment) = match filtered {
+ None => {
+ tcx.sess.delay_span_bug(
+ tcx.def_span(def_id),
+ "no arg matching AnonConst in path",
+ );
+ return None;
+ }
+ Some(inner) => inner,
+ };
// Try to use the segment resolution if it is valid, otherwise we
// default to the path resolution.
///
/// Note that the allocator may give the collection more space than it
/// requests. Therefore, capacity can not be relied upon to be precisely
- /// minimal. Prefer [`reserve`] if future insertions are expected.
+ /// minimal. Prefer [`try_reserve`] if future insertions are expected.
///
- /// [`reserve`]: VecDeque::reserve
+ /// [`try_reserve`]: VecDeque::try_reserve
///
/// # Errors
///
///
/// Note that the allocator may give the collection more space than it
/// requests. Therefore, capacity can not be relied upon to be precisely
- /// minimal. Prefer [`reserve`] if future insertions are expected.
+ /// minimal. Prefer [`try_reserve`] if future insertions are expected.
///
- /// [`reserve`]: String::reserve
+ /// [`try_reserve`]: String::try_reserve
///
/// # Errors
///
///
/// Note that the allocator may give the collection more space than it
/// requests. Therefore, capacity can not be relied upon to be precisely
- /// minimal. Prefer [`reserve`] if future insertions are expected.
+ /// minimal. Prefer [`try_reserve`] if future insertions are expected.
///
- /// [`reserve`]: Vec::reserve
+ /// [`try_reserve`]: Vec::try_reserve
///
/// # Errors
///
//! Defines the `IntoIter` owned iterator for arrays.
use crate::{
- fmt,
+ cmp, fmt,
iter::{self, ExactSizeIterator, FusedIterator, TrustedLen},
mem::{self, MaybeUninit},
ops::Range,
IntoIterator::into_iter(array)
}
+ /// Creates an iterator over the elements in a partially-initialized buffer.
+ ///
+ /// If you have a fully-initialized array, then use [`IntoIterator`].
+ /// But this is useful for returning partial results from unsafe code.
+ ///
+ /// # Safety
+ ///
+ /// - The `buffer[initialized]` elements must all be initialized.
+ /// - The range must be canonical, with `initialized.start <= initialized.end`.
+ /// - The range must in in-bounds for the buffer, with `initialized.end <= N`.
+ /// (Like how indexing `[0][100..100]` fails despite the range being empty.)
+ ///
+ /// It's sound to have more elements initialized than mentioned, though that
+ /// will most likely result in them being leaked.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(array_into_iter_constructors)]
+ ///
+ /// #![feature(maybe_uninit_array_assume_init)]
+ /// #![feature(maybe_uninit_uninit_array)]
+ /// use std::array::IntoIter;
+ /// use std::mem::MaybeUninit;
+ ///
+ /// # // Hi! Thanks for reading the code. This is restricted to `Copy` because
+ /// # // otherwise it could leak. A fully-general version this would need a drop
+ /// # // guard to handle panics from the iterator, but this works for an example.
+ /// fn next_chunk<T: Copy, const N: usize>(
+ /// it: &mut impl Iterator<Item = T>,
+ /// ) -> Result<[T; N], IntoIter<T, N>> {
+ /// let mut buffer = MaybeUninit::uninit_array();
+ /// let mut i = 0;
+ /// while i < N {
+ /// match it.next() {
+ /// Some(x) => {
+ /// buffer[i].write(x);
+ /// i += 1;
+ /// }
+ /// None => {
+ /// // SAFETY: We've initialized the first `i` items
+ /// unsafe {
+ /// return Err(IntoIter::new_unchecked(buffer, 0..i));
+ /// }
+ /// }
+ /// }
+ /// }
+ ///
+ /// // SAFETY: We've initialized all N items
+ /// unsafe { Ok(MaybeUninit::array_assume_init(buffer)) }
+ /// }
+ ///
+ /// let r: [_; 4] = next_chunk(&mut (10..16)).unwrap();
+ /// assert_eq!(r, [10, 11, 12, 13]);
+ /// let r: IntoIter<_, 40> = next_chunk(&mut (10..16)).unwrap_err();
+ /// assert_eq!(r.collect::<Vec<_>>(), vec![10, 11, 12, 13, 14, 15]);
+ /// ```
+ #[unstable(feature = "array_into_iter_constructors", issue = "91583")]
+ #[rustc_const_unstable(feature = "const_array_into_iter_constructors", issue = "91583")]
+ pub const unsafe fn new_unchecked(
+ buffer: [MaybeUninit<T>; N],
+ initialized: Range<usize>,
+ ) -> Self {
+ Self { data: buffer, alive: initialized }
+ }
+
+ /// Creates an iterator over `T` which returns no elements.
+ ///
+ /// If you just need an empty iterator, then use
+ /// [`iter::empty()`](crate::iter::empty) instead.
+ /// And if you need an empty array, use `[]`.
+ ///
+ /// But this is useful when you need an `array::IntoIter<T, N>` *specifically*.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(array_into_iter_constructors)]
+ /// use std::array::IntoIter;
+ ///
+ /// let empty = IntoIter::<i32, 3>::empty();
+ /// assert_eq!(empty.len(), 0);
+ /// assert_eq!(empty.as_slice(), &[]);
+ ///
+ /// let empty = IntoIter::<std::convert::Infallible, 200>::empty();
+ /// assert_eq!(empty.len(), 0);
+ /// ```
+ ///
+ /// `[1, 2].into_iter()` and `[].into_iter()` have different types
+ /// ```should_fail,edition2021
+ /// #![feature(array_into_iter_constructors)]
+ /// use std::array::IntoIter;
+ ///
+ /// pub fn get_bytes(b: bool) -> IntoIter<i8, 4> {
+ /// if b {
+ /// [1, 2, 3, 4].into_iter()
+ /// } else {
+ /// [].into_iter() // error[E0308]: mismatched types
+ /// }
+ /// }
+ /// ```
+ ///
+ /// But using this method you can get an empty iterator of appropriate size:
+ /// ```edition2021
+ /// #![feature(array_into_iter_constructors)]
+ /// use std::array::IntoIter;
+ ///
+ /// pub fn get_bytes(b: bool) -> IntoIter<i8, 4> {
+ /// if b {
+ /// [1, 2, 3, 4].into_iter()
+ /// } else {
+ /// IntoIter::empty()
+ /// }
+ /// }
+ ///
+ /// assert_eq!(get_bytes(true).collect::<Vec<_>>(), vec![1, 2, 3, 4]);
+ /// assert_eq!(get_bytes(false).collect::<Vec<_>>(), vec![]);
+ /// ```
+ #[unstable(feature = "array_into_iter_constructors", issue = "91583")]
+ #[rustc_const_unstable(feature = "const_array_into_iter_constructors", issue = "91583")]
+ pub const fn empty() -> Self {
+ let buffer = MaybeUninit::uninit_array();
+ let initialized = 0..0;
+
+ // SAFETY: We're telling it that none of the elements are initialized,
+ // which is trivially true. And ∀N: usize, 0 <= N.
+ unsafe { Self::new_unchecked(buffer, initialized) }
+ }
+
/// Returns an immutable slice of all elements that have not been yielded
/// yet.
#[stable(feature = "array_value_iter", since = "1.51.0")]
fn last(mut self) -> Option<Self::Item> {
self.next_back()
}
+
+ fn advance_by(&mut self, n: usize) -> Result<(), usize> {
+ let len = self.len();
+
+ // The number of elements to drop. Always in-bounds by construction.
+ let delta = cmp::min(n, len);
+
+ let range_to_drop = self.alive.start..(self.alive.start + delta);
+
+ // Moving the start marks them as conceptually "dropped", so if anything
+ // goes bad then our drop impl won't double-free them.
+ self.alive.start += delta;
+
+ // SAFETY: These elements are currently initialized, so it's fine to drop them.
+ unsafe {
+ let slice = self.data.get_unchecked_mut(range_to_drop);
+ ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(slice));
+ }
+
+ if n > len { Err(len) } else { Ok(()) }
+ }
}
#[stable(feature = "array_value_iter_impls", since = "1.40.0")]
unsafe { self.data.get_unchecked(idx).assume_init_read() }
})
}
+
+ fn advance_back_by(&mut self, n: usize) -> Result<(), usize> {
+ let len = self.len();
+
+ // The number of elements to drop. Always in-bounds by construction.
+ let delta = cmp::min(n, len);
+
+ let range_to_drop = (self.alive.end - delta)..self.alive.end;
+
+ // Moving the end marks them as conceptually "dropped", so if anything
+ // goes bad then our drop impl won't double-free them.
+ self.alive.end -= delta;
+
+ // SAFETY: These elements are currently initialized, so it's fine to drop them.
+ unsafe {
+ let slice = self.data.get_unchecked_mut(range_to_drop);
+ ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(slice));
+ }
+
+ if n > len { Err(len) } else { Ok(()) }
+ }
}
#[stable(feature = "array_value_iter_impls", since = "1.40.0")]
#![feature(const_align_of_val)]
#![feature(const_alloc_layout)]
#![feature(const_arguments_as_str)]
+#![feature(const_array_into_iter_constructors)]
#![feature(const_bigint_helper_methods)]
#![feature(const_caller_location)]
#![feature(const_cell_into_inner)]
#![feature(const_type_name)]
#![feature(const_default_impls)]
#![feature(duration_consts_float)]
+#![feature(maybe_uninit_uninit_array)]
#![feature(ptr_metadata)]
#![feature(slice_ptr_get)]
#![feature(str_internals)]
v.split_array_mut::<7>();
}
+
+#[test]
+fn array_intoiter_advance_by() {
+ use std::cell::Cell;
+ struct DropCounter<'a>(usize, &'a Cell<usize>);
+ impl Drop for DropCounter<'_> {
+ fn drop(&mut self) {
+ let x = self.1.get();
+ self.1.set(x + 1);
+ }
+ }
+
+ let counter = Cell::new(0);
+ let a: [_; 100] = std::array::from_fn(|i| DropCounter(i, &counter));
+ let mut it = IntoIterator::into_iter(a);
+
+ let r = it.advance_by(1);
+ assert_eq!(r, Ok(()));
+ assert_eq!(it.len(), 99);
+ assert_eq!(counter.get(), 1);
+
+ let r = it.advance_by(0);
+ assert_eq!(r, Ok(()));
+ assert_eq!(it.len(), 99);
+ assert_eq!(counter.get(), 1);
+
+ let r = it.advance_by(11);
+ assert_eq!(r, Ok(()));
+ assert_eq!(it.len(), 88);
+ assert_eq!(counter.get(), 12);
+
+ let x = it.next();
+ assert_eq!(x.as_ref().map(|x| x.0), Some(12));
+ assert_eq!(it.len(), 87);
+ assert_eq!(counter.get(), 12);
+ drop(x);
+ assert_eq!(counter.get(), 13);
+
+ let r = it.advance_by(123456);
+ assert_eq!(r, Err(87));
+ assert_eq!(it.len(), 0);
+ assert_eq!(counter.get(), 100);
+
+ let r = it.advance_by(0);
+ assert_eq!(r, Ok(()));
+ assert_eq!(it.len(), 0);
+ assert_eq!(counter.get(), 100);
+
+ let r = it.advance_by(10);
+ assert_eq!(r, Err(0));
+ assert_eq!(it.len(), 0);
+ assert_eq!(counter.get(), 100);
+}
+
+#[test]
+fn array_intoiter_advance_back_by() {
+ use std::cell::Cell;
+ struct DropCounter<'a>(usize, &'a Cell<usize>);
+ impl Drop for DropCounter<'_> {
+ fn drop(&mut self) {
+ let x = self.1.get();
+ self.1.set(x + 1);
+ }
+ }
+
+ let counter = Cell::new(0);
+ let a: [_; 100] = std::array::from_fn(|i| DropCounter(i, &counter));
+ let mut it = IntoIterator::into_iter(a);
+
+ let r = it.advance_back_by(1);
+ assert_eq!(r, Ok(()));
+ assert_eq!(it.len(), 99);
+ assert_eq!(counter.get(), 1);
+
+ let r = it.advance_back_by(0);
+ assert_eq!(r, Ok(()));
+ assert_eq!(it.len(), 99);
+ assert_eq!(counter.get(), 1);
+
+ let r = it.advance_back_by(11);
+ assert_eq!(r, Ok(()));
+ assert_eq!(it.len(), 88);
+ assert_eq!(counter.get(), 12);
+
+ let x = it.next_back();
+ assert_eq!(x.as_ref().map(|x| x.0), Some(87));
+ assert_eq!(it.len(), 87);
+ assert_eq!(counter.get(), 12);
+ drop(x);
+ assert_eq!(counter.get(), 13);
+
+ let r = it.advance_back_by(123456);
+ assert_eq!(r, Err(87));
+ assert_eq!(it.len(), 0);
+ assert_eq!(counter.get(), 100);
+
+ let r = it.advance_back_by(0);
+ assert_eq!(r, Ok(()));
+ assert_eq!(it.len(), 0);
+ assert_eq!(counter.get(), 100);
+
+ let r = it.advance_back_by(10);
+ assert_eq!(r, Err(0));
+ assert_eq!(it.len(), 0);
+ assert_eq!(counter.get(), 100);
+}
let x = f32x4::from_array([1.0, 1.0, 1.0, 1.0]);
let y = -x;
- let h = x * 0.5;
+ let h = x * f32x4::splat(0.5);
let r = y.abs();
assert_eq!(x, r);
## CI
-We currently have 2 CI matrices through Travis CI and GitHub Actions that will automatically build and test your change in order to verify that `std::simd`'s portable API is, in fact, portable. If your change builds locally, but does not build on either, this is likely due to a platform-specific concern that your code has not addressed. Please consult the build logs and address the error, or ask for help if you need it.
+We currently use GitHub Actions which will automatically build and test your change in order to verify that `std::simd`'s portable API is, in fact, portable. If your change builds locally, but does not build in CI, this is likely due to a platform-specific concern that your code has not addressed. Please consult the build logs and address the error, or ask for help if you need it.
## Beyond stdsimd
# The Rust standard library's portable SIMD API
-[![Build Status](https://travis-ci.com/rust-lang/portable-simd.svg?branch=master)](https://travis-ci.com/rust-lang/portable-simd)
+![Build Status](https://github.com/rust-lang/portable-simd/actions/workflows/ci.yml/badge.svg?branch=master)
Code repository for the [Portable SIMD Project Group](https://github.com/rust-lang/project-portable-simd).
Please refer to [CONTRIBUTING.md](./CONTRIBUTING.md) for our contributing guidelines.
let sun = &mut sun[0];
for body in rest {
let m_ratio = body.mass / SOLAR_MASS;
- sun.v -= body.v * m_ratio;
+ sun.v -= body.v * Simd::splat(m_ratio);
}
}
let mut i = 0;
for j in 0..N_BODIES {
for k in j + 1..N_BODIES {
- let f = r[i] * mag[i];
- bodies[j].v -= f * bodies[k].mass;
- bodies[k].v += f * bodies[j].mass;
+ let f = r[i] * Simd::splat(mag[i]);
+ bodies[j].v -= f * Simd::splat(bodies[k].mass);
+ bodies[k].v += f * Simd::splat(bodies[j].mass);
i += 1
}
}
for body in bodies {
- body.x += dt * body.v
+ body.x += Simd::splat(dt) * body.v
}
}
{
/// Test if each lane is equal to the corresponding lane in `other`.
#[inline]
+ #[must_use = "method returns a new mask and does not mutate the original value"]
pub fn lanes_eq(self, other: Self) -> Mask<T::Mask, LANES> {
unsafe { Mask::from_int_unchecked(intrinsics::simd_eq(self, other)) }
}
/// Test if each lane is not equal to the corresponding lane in `other`.
#[inline]
+ #[must_use = "method returns a new mask and does not mutate the original value"]
pub fn lanes_ne(self, other: Self) -> Mask<T::Mask, LANES> {
unsafe { Mask::from_int_unchecked(intrinsics::simd_ne(self, other)) }
}
{
/// Test if each lane is less than the corresponding lane in `other`.
#[inline]
+ #[must_use = "method returns a new mask and does not mutate the original value"]
pub fn lanes_lt(self, other: Self) -> Mask<T::Mask, LANES> {
unsafe { Mask::from_int_unchecked(intrinsics::simd_lt(self, other)) }
}
/// Test if each lane is greater than the corresponding lane in `other`.
#[inline]
+ #[must_use = "method returns a new mask and does not mutate the original value"]
pub fn lanes_gt(self, other: Self) -> Mask<T::Mask, LANES> {
unsafe { Mask::from_int_unchecked(intrinsics::simd_gt(self, other)) }
}
/// Test if each lane is less than or equal to the corresponding lane in `other`.
#[inline]
+ #[must_use = "method returns a new mask and does not mutate the original value"]
pub fn lanes_le(self, other: Self) -> Mask<T::Mask, LANES> {
unsafe { Mask::from_int_unchecked(intrinsics::simd_le(self, other)) }
}
/// Test if each lane is greater than or equal to the corresponding lane in `other`.
#[inline]
+ #[must_use = "method returns a new mask and does not mutate the original value"]
pub fn lanes_ge(self, other: Self) -> Mask<T::Mask, LANES> {
unsafe { Mask::from_int_unchecked(intrinsics::simd_ge(self, other)) }
}
pub trait SupportedLaneCount: Sealed {
#[doc(hidden)]
type BitMask: Copy + Default + AsRef<[u8]> + AsMut<[u8]>;
-
- #[doc(hidden)]
- type IntBitMask;
}
impl<const LANES: usize> Sealed for LaneCount<LANES> {}
impl SupportedLaneCount for LaneCount<1> {
type BitMask = [u8; 1];
- type IntBitMask = u8;
}
impl SupportedLaneCount for LaneCount<2> {
type BitMask = [u8; 1];
- type IntBitMask = u8;
}
impl SupportedLaneCount for LaneCount<4> {
type BitMask = [u8; 1];
- type IntBitMask = u8;
}
impl SupportedLaneCount for LaneCount<8> {
type BitMask = [u8; 1];
- type IntBitMask = u8;
}
impl SupportedLaneCount for LaneCount<16> {
type BitMask = [u8; 2];
- type IntBitMask = u16;
}
impl SupportedLaneCount for LaneCount<32> {
type BitMask = [u8; 4];
- type IntBitMask = u32;
+}
+impl SupportedLaneCount for LaneCount<64> {
+ type BitMask = [u8; 8];
}
/// # Safety
/// All lanes must be either 0 or -1.
#[inline]
+ #[must_use = "method returns a new mask and does not mutate the original value"]
pub unsafe fn from_int_unchecked(value: Simd<T, LANES>) -> Self {
unsafe { Self(mask_impl::Mask::from_int_unchecked(value)) }
}
/// # Panics
/// Panics if any lane is not 0 or -1.
#[inline]
+ #[must_use = "method returns a new mask and does not mutate the original value"]
pub fn from_int(value: Simd<T, LANES>) -> Self {
assert!(T::valid(value), "all values must be either 0 or -1",);
unsafe { Self::from_int_unchecked(value) }
/// Converts the mask to a vector of integers, where 0 represents `false` and -1
/// represents `true`.
#[inline]
+ #[must_use = "method returns a new vector and does not mutate the original value"]
pub fn to_int(self) -> Simd<T, LANES> {
self.0.to_int()
}
/// # Safety
/// `lane` must be less than `LANES`.
#[inline]
+ #[must_use = "method returns a new bool and does not mutate the original value"]
pub unsafe fn test_unchecked(&self, lane: usize) -> bool {
unsafe { self.0.test_unchecked(lane) }
}
/// # Panics
/// Panics if `lane` is greater than or equal to the number of lanes in the vector.
#[inline]
+ #[must_use = "method returns a new bool and does not mutate the original value"]
pub fn test(&self, lane: usize) -> bool {
assert!(lane < LANES, "lane index out of range");
unsafe { self.test_unchecked(lane) }
/// Convert this mask to a bitmask, with one bit set per lane.
#[cfg(feature = "generic_const_exprs")]
+ #[inline]
+ #[must_use = "method returns a new array and does not mutate the original value"]
pub fn to_bitmask(self) -> [u8; LaneCount::<LANES>::BITMASK_LEN] {
self.0.to_bitmask()
}
/// Convert a bitmask to a mask.
#[cfg(feature = "generic_const_exprs")]
+ #[inline]
+ #[must_use = "method returns a new mask and does not mutate the original value"]
pub fn from_bitmask(bitmask: [u8; LaneCount::<LANES>::BITMASK_LEN]) -> Self {
Self(mask_impl::Mask::from_bitmask(bitmask))
}
/// Returns true if any lane is set, or false otherwise.
#[inline]
+ #[must_use = "method returns a new bool and does not mutate the original value"]
pub fn any(self) -> bool {
self.0.any()
}
/// Returns true if all lanes are set, or false otherwise.
#[inline]
+ #[must_use = "method returns a new bool and does not mutate the original value"]
pub fn all(self) -> bool {
self.0.all()
}
LaneCount<LANES>: SupportedLaneCount,
{
#[inline]
+ #[must_use = "method returns a defaulted mask with all lanes set to false (0)"]
fn default() -> Self {
Self::splat(false)
}
LaneCount<LANES>: SupportedLaneCount,
{
#[inline]
+ #[must_use = "method returns a new bool and does not mutate the original value"]
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
LaneCount<LANES>: SupportedLaneCount,
{
#[inline]
+ #[must_use = "method returns a new Ordering and does not mutate the original value"]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.0.partial_cmp(&other.0)
}
{
type Output = Self;
#[inline]
+ #[must_use = "method returns a new mask and does not mutate the original value"]
fn bitand(self, rhs: Self) -> Self {
Self(self.0 & rhs.0)
}
{
type Output = Self;
#[inline]
+ #[must_use = "method returns a new mask and does not mutate the original value"]
fn bitand(self, rhs: bool) -> Self {
self & Self::splat(rhs)
}
{
type Output = Mask<T, LANES>;
#[inline]
+ #[must_use = "method returns a new mask and does not mutate the original value"]
fn bitand(self, rhs: Mask<T, LANES>) -> Mask<T, LANES> {
Mask::splat(self) & rhs
}
{
type Output = Self;
#[inline]
+ #[must_use = "method returns a new mask and does not mutate the original value"]
fn bitor(self, rhs: Self) -> Self {
Self(self.0 | rhs.0)
}
{
type Output = Self;
#[inline]
+ #[must_use = "method returns a new mask and does not mutate the original value"]
fn bitor(self, rhs: bool) -> Self {
self | Self::splat(rhs)
}
{
type Output = Mask<T, LANES>;
#[inline]
+ #[must_use = "method returns a new mask and does not mutate the original value"]
fn bitor(self, rhs: Mask<T, LANES>) -> Mask<T, LANES> {
Mask::splat(self) | rhs
}
{
type Output = Self;
#[inline]
+ #[must_use = "method returns a new mask and does not mutate the original value"]
fn bitxor(self, rhs: Self) -> Self::Output {
Self(self.0 ^ rhs.0)
}
{
type Output = Self;
#[inline]
+ #[must_use = "method returns a new mask and does not mutate the original value"]
fn bitxor(self, rhs: bool) -> Self::Output {
self ^ Self::splat(rhs)
}
{
type Output = Mask<T, LANES>;
#[inline]
+ #[must_use = "method returns a new mask and does not mutate the original value"]
fn bitxor(self, rhs: Mask<T, LANES>) -> Self::Output {
Mask::splat(self) ^ rhs
}
{
type Output = Mask<T, LANES>;
#[inline]
+ #[must_use = "method returns a new mask and does not mutate the original value"]
fn not(self) -> Self::Output {
Self(!self.0)
}
+#![allow(unused_imports)]
use super::MaskElement;
use crate::simd::intrinsics;
use crate::simd::{LaneCount, Simd, SupportedLaneCount};
LaneCount<LANES>: SupportedLaneCount,
{
#[inline]
+ #[must_use = "method returns a new mask and does not mutate the original value"]
pub fn splat(value: bool) -> Self {
let mut mask = <LaneCount<LANES> as SupportedLaneCount>::BitMask::default();
if value {
}
#[inline]
+ #[must_use = "method returns a new bool and does not mutate the original value"]
pub unsafe fn test_unchecked(&self, lane: usize) -> bool {
(self.0.as_ref()[lane / 8] >> (lane % 8)) & 0x1 > 0
}
}
#[inline]
+ #[must_use = "method returns a new vector and does not mutate the original value"]
pub fn to_int(self) -> Simd<T, LANES> {
unsafe {
- let mask: <LaneCount<LANES> as SupportedLaneCount>::IntBitMask =
- core::mem::transmute_copy(&self);
- intrinsics::simd_select_bitmask(mask, Simd::splat(T::TRUE), Simd::splat(T::FALSE))
+ crate::intrinsics::simd_select_bitmask(
+ self.0,
+ Simd::splat(T::TRUE),
+ Simd::splat(T::FALSE),
+ )
}
}
#[inline]
+ #[must_use = "method returns a new mask and does not mutate the original value"]
pub unsafe fn from_int_unchecked(value: Simd<T, LANES>) -> Self {
- // TODO remove the transmute when rustc is more flexible
- assert_eq!(
- core::mem::size_of::<<LaneCount::<LANES> as SupportedLaneCount>::BitMask>(),
- core::mem::size_of::<<LaneCount::<LANES> as SupportedLaneCount>::IntBitMask>(),
- );
- unsafe {
- let mask: <LaneCount<LANES> as SupportedLaneCount>::IntBitMask =
- intrinsics::simd_bitmask(value);
- Self(core::mem::transmute_copy(&mask), PhantomData)
- }
+ unsafe { Self(crate::intrinsics::simd_bitmask(value), PhantomData) }
}
#[cfg(feature = "generic_const_exprs")]
#[inline]
+ #[must_use = "method returns a new array and does not mutate the original value"]
pub fn to_bitmask(self) -> [u8; LaneCount::<LANES>::BITMASK_LEN] {
// Safety: these are the same type and we are laundering the generic
unsafe { core::mem::transmute_copy(&self.0) }
#[cfg(feature = "generic_const_exprs")]
#[inline]
+ #[must_use = "method returns a new mask and does not mutate the original value"]
pub fn from_bitmask(bitmask: [u8; LaneCount::<LANES>::BITMASK_LEN]) -> Self {
// Safety: these are the same type and we are laundering the generic
Self(unsafe { core::mem::transmute_copy(&bitmask) }, PhantomData)
}
#[inline]
+ #[must_use = "method returns a new mask and does not mutate the original value"]
pub fn convert<U>(self) -> Mask<U, LANES>
where
U: MaskElement,
}
#[inline]
+ #[must_use = "method returns a new bool and does not mutate the original value"]
pub fn any(self) -> bool {
self != Self::splat(false)
}
#[inline]
+ #[must_use = "method returns a new bool and does not mutate the original value"]
pub fn all(self) -> bool {
self == Self::splat(true)
}
{
type Output = Self;
#[inline]
+ #[must_use = "method returns a new mask and does not mutate the original value"]
fn bitand(mut self, rhs: Self) -> Self {
for (l, r) in self.0.as_mut().iter_mut().zip(rhs.0.as_ref().iter()) {
*l &= r;
{
type Output = Self;
#[inline]
+ #[must_use = "method returns a new mask and does not mutate the original value"]
fn bitor(mut self, rhs: Self) -> Self {
for (l, r) in self.0.as_mut().iter_mut().zip(rhs.0.as_ref().iter()) {
*l |= r;
{
type Output = Self;
#[inline]
+ #[must_use = "method returns a new mask and does not mutate the original value"]
fn bitxor(mut self, rhs: Self) -> Self::Output {
for (l, r) in self.0.as_mut().iter_mut().zip(rhs.0.as_ref().iter()) {
*l ^= r;
{
type Output = Self;
#[inline]
+ #[must_use = "method returns a new mask and does not mutate the original value"]
fn not(mut self) -> Self::Output {
for x in self.0.as_mut() {
*x = !*x;
LaneCount<LANES>: SupportedLaneCount,
{
#[inline]
+ #[must_use = "method returns a new mask and does not mutate the original value"]
fn clone(&self) -> Self {
*self
}
T: MaskElement,
LaneCount<LANES>: SupportedLaneCount,
{
+ #[inline]
+ #[must_use = "method returns a new mask and does not mutate the original value"]
pub fn splat(value: bool) -> Self {
Self(Simd::splat(if value { T::TRUE } else { T::FALSE }))
}
#[inline]
+ #[must_use = "method returns a new bool and does not mutate the original value"]
pub unsafe fn test_unchecked(&self, lane: usize) -> bool {
T::eq(self.0[lane], T::TRUE)
}
}
#[inline]
+ #[must_use = "method returns a new vector and does not mutate the original value"]
pub fn to_int(self) -> Simd<T, LANES> {
self.0
}
#[inline]
+ #[must_use = "method returns a new mask and does not mutate the original value"]
pub unsafe fn from_int_unchecked(value: Simd<T, LANES>) -> Self {
Self(value)
}
#[inline]
+ #[must_use = "method returns a new mask and does not mutate the original value"]
pub fn convert<U>(self) -> Mask<U, LANES>
where
U: MaskElement,
#[cfg(feature = "generic_const_exprs")]
#[inline]
+ #[must_use = "method returns a new array and does not mutate the original value"]
pub fn to_bitmask(self) -> [u8; LaneCount::<LANES>::BITMASK_LEN] {
unsafe {
- // TODO remove the transmute when rustc can use arrays of u8 as bitmasks
- assert_eq!(
- core::mem::size_of::<<LaneCount::<LANES> as SupportedLaneCount>::IntBitMask>(),
- LaneCount::<LANES>::BITMASK_LEN,
- );
- let bitmask: <LaneCount<LANES> as SupportedLaneCount>::IntBitMask =
- intrinsics::simd_bitmask(self.0);
let mut bitmask: [u8; LaneCount::<LANES>::BITMASK_LEN] =
- core::mem::transmute_copy(&bitmask);
+ crate::intrinsics::simd_bitmask(self.0);
// There is a bug where LLVM appears to implement this operation with the wrong
// bit order.
#[cfg(feature = "generic_const_exprs")]
#[inline]
+ #[must_use = "method returns a new mask and does not mutate the original value"]
pub fn from_bitmask(mut bitmask: [u8; LaneCount::<LANES>::BITMASK_LEN]) -> Self {
unsafe {
// There is a bug where LLVM appears to implement this operation with the wrong
}
}
- // TODO remove the transmute when rustc can use arrays of u8 as bitmasks
- assert_eq!(
- core::mem::size_of::<<LaneCount::<LANES> as SupportedLaneCount>::IntBitMask>(),
- LaneCount::<LANES>::BITMASK_LEN,
- );
- let bitmask: <LaneCount<LANES> as SupportedLaneCount>::IntBitMask =
- core::mem::transmute_copy(&bitmask);
-
- Self::from_int_unchecked(intrinsics::simd_select_bitmask(
+ Self::from_int_unchecked(crate::intrinsics::simd_select_bitmask(
bitmask,
Self::splat(true).to_int(),
Self::splat(false).to_int(),
}
#[inline]
+ #[must_use = "method returns a new bool and does not mutate the original value"]
pub fn any(self) -> bool {
unsafe { intrinsics::simd_reduce_any(self.to_int()) }
}
#[inline]
+ #[must_use = "method returns a new vector and does not mutate the original value"]
pub fn all(self) -> bool {
unsafe { intrinsics::simd_reduce_all(self.to_int()) }
}
{
type Output = Self;
#[inline]
+ #[must_use = "method returns a new mask and does not mutate the original value"]
fn bitand(self, rhs: Self) -> Self {
unsafe { Self(intrinsics::simd_and(self.0, rhs.0)) }
}
{
type Output = Self;
#[inline]
+ #[must_use = "method returns a new mask and does not mutate the original value"]
fn bitor(self, rhs: Self) -> Self {
unsafe { Self(intrinsics::simd_or(self.0, rhs.0)) }
}
{
type Output = Self;
#[inline]
+ #[must_use = "method returns a new mask and does not mutate the original value"]
fn bitxor(self, rhs: Self) -> Self {
unsafe { Self(intrinsics::simd_xor(self.0, rhs.0)) }
}
{
type Output = Self;
#[inline]
+ #[must_use = "method returns a new mask and does not mutate the original value"]
fn not(self) -> Self::Output {
Self::splat(true) ^ self
}
/// let max = Simd::splat(MAX);
/// let unsat = x + max;
/// let sat = x.saturating_add(max);
- /// assert_eq!(x - 1, unsat);
+ /// assert_eq!(unsat, Simd::from_array([1, 0, MAX, MAX - 1]));
/// assert_eq!(sat, max);
/// ```
#[inline]
/// let max = Simd::splat(MAX);
/// let unsat = x - max;
/// let sat = x.saturating_sub(max);
- /// assert_eq!(unsat, x + 1);
+ /// assert_eq!(unsat, Simd::from_array([3, 2, 1, 0]));
/// assert_eq!(sat, Simd::splat(0));
#[inline]
pub fn saturating_sub(self, second: Self) -> Self {
#[inline]
pub fn abs(self) -> Self {
const SHR: $ty = <$ty>::BITS as $ty - 1;
- let m = self >> SHR;
+ let m = self >> Simd::splat(SHR);
(self^m) - m
}
pub fn saturating_abs(self) -> Self {
// arith shift for -1 or 0 mask based on sign bit, giving 2s complement
const SHR: $ty = <$ty>::BITS as $ty - 1;
- let m = self >> SHR;
+ let m = self >> Simd::splat(SHR);
(self^m).saturating_sub(m)
}
use crate::simd::intrinsics;
use crate::simd::{LaneCount, Simd, SimdElement, SupportedLaneCount};
+use core::ops::{Add, Mul};
+use core::ops::{BitAnd, BitOr, BitXor};
+use core::ops::{Div, Rem, Sub};
+use core::ops::{Shl, Shr};
+
+mod assign;
+mod deref;
+mod unary;
impl<I, T, const LANES: usize> core::ops::Index<I> for Simd<T, LANES>
where
$(#[$attrs])*
fn $fn($self_tok, $rhs_arg: $rhs_arg_ty) -> Self::Output $body
}
-
- impl<const $lanes: usize> core::ops::$trait<&'_ $rhs> for $type
- where
- LaneCount<$lanes2>: SupportedLaneCount,
- {
- type Output = <$type as core::ops::$trait<$rhs>>::Output;
-
- $(#[$attrs])*
- fn $fn($self_tok, $rhs_arg: &$rhs) -> Self::Output {
- core::ops::$trait::$fn($self_tok, *$rhs_arg)
- }
- }
-
- impl<const $lanes: usize> core::ops::$trait<$rhs> for &'_ $type
- where
- LaneCount<$lanes2>: SupportedLaneCount,
- {
- type Output = <$type as core::ops::$trait<$rhs>>::Output;
-
- $(#[$attrs])*
- fn $fn($self_tok, $rhs_arg: $rhs) -> Self::Output {
- core::ops::$trait::$fn(*$self_tok, $rhs_arg)
- }
- }
-
- impl<const $lanes: usize> core::ops::$trait<&'_ $rhs> for &'_ $type
- where
- LaneCount<$lanes2>: SupportedLaneCount,
- {
- type Output = <$type as core::ops::$trait<$rhs>>::Output;
-
- $(#[$attrs])*
- fn $fn($self_tok, $rhs_arg: &$rhs) -> Self::Output {
- core::ops::$trait::$fn(*$self_tok, *$rhs_arg)
- }
- }
};
-
- // binary assignment op
- {
- impl<const $lanes:ident: usize> core::ops::$trait:ident<$rhs:ty> for $type:ty
- where
- LaneCount<$lanes2:ident>: SupportedLaneCount,
- {
- $(#[$attrs:meta])*
- fn $fn:ident(&mut $self_tok:ident, $rhs_arg:ident: $rhs_arg_ty:ty) $body:tt
- }
- } => {
- impl<const $lanes: usize> core::ops::$trait<$rhs> for $type
- where
- LaneCount<$lanes2>: SupportedLaneCount,
- {
- $(#[$attrs])*
- fn $fn(&mut $self_tok, $rhs_arg: $rhs_arg_ty) $body
- }
-
- impl<const $lanes: usize> core::ops::$trait<&'_ $rhs> for $type
- where
- LaneCount<$lanes2>: SupportedLaneCount,
- {
- $(#[$attrs])*
- fn $fn(&mut $self_tok, $rhs_arg: &$rhs_arg_ty) {
- core::ops::$trait::$fn($self_tok, *$rhs_arg)
- }
- }
- };
-
- // unary op
- {
- impl<const $lanes:ident: usize> core::ops::$trait:ident for $type:ty
- where
- LaneCount<$lanes2:ident>: SupportedLaneCount,
- {
- type Output = $output:ty;
- fn $fn:ident($self_tok:ident) -> Self::Output $body:tt
- }
- } => {
- impl<const $lanes: usize> core::ops::$trait for $type
- where
- LaneCount<$lanes2>: SupportedLaneCount,
- {
- type Output = $output;
- fn $fn($self_tok) -> Self::Output $body
- }
-
- impl<const $lanes: usize> core::ops::$trait for &'_ $type
- where
- LaneCount<$lanes2>: SupportedLaneCount,
- {
- type Output = <$type as core::ops::$trait>::Output;
- fn $fn($self_tok) -> Self::Output {
- core::ops::$trait::$fn(*$self_tok)
- }
- }
- }
}
/// Automatically implements operators over vectors and scalars for a particular vector.
macro_rules! impl_op {
{ impl Add for $scalar:ty } => {
- impl_op! { @binary $scalar, Add::add, AddAssign::add_assign, simd_add }
+ impl_op! { @binary $scalar, Add::add, simd_add }
};
{ impl Sub for $scalar:ty } => {
- impl_op! { @binary $scalar, Sub::sub, SubAssign::sub_assign, simd_sub }
+ impl_op! { @binary $scalar, Sub::sub, simd_sub }
};
{ impl Mul for $scalar:ty } => {
- impl_op! { @binary $scalar, Mul::mul, MulAssign::mul_assign, simd_mul }
+ impl_op! { @binary $scalar, Mul::mul, simd_mul }
};
{ impl Div for $scalar:ty } => {
- impl_op! { @binary $scalar, Div::div, DivAssign::div_assign, simd_div }
+ impl_op! { @binary $scalar, Div::div, simd_div }
};
{ impl Rem for $scalar:ty } => {
- impl_op! { @binary $scalar, Rem::rem, RemAssign::rem_assign, simd_rem }
+ impl_op! { @binary $scalar, Rem::rem, simd_rem }
};
{ impl Shl for $scalar:ty } => {
- impl_op! { @binary $scalar, Shl::shl, ShlAssign::shl_assign, simd_shl }
+ impl_op! { @binary $scalar, Shl::shl, simd_shl }
};
{ impl Shr for $scalar:ty } => {
- impl_op! { @binary $scalar, Shr::shr, ShrAssign::shr_assign, simd_shr }
+ impl_op! { @binary $scalar, Shr::shr, simd_shr }
};
{ impl BitAnd for $scalar:ty } => {
- impl_op! { @binary $scalar, BitAnd::bitand, BitAndAssign::bitand_assign, simd_and }
+ impl_op! { @binary $scalar, BitAnd::bitand, simd_and }
};
{ impl BitOr for $scalar:ty } => {
- impl_op! { @binary $scalar, BitOr::bitor, BitOrAssign::bitor_assign, simd_or }
+ impl_op! { @binary $scalar, BitOr::bitor, simd_or }
};
{ impl BitXor for $scalar:ty } => {
- impl_op! { @binary $scalar, BitXor::bitxor, BitXorAssign::bitxor_assign, simd_xor }
- };
-
- { impl Not for $scalar:ty } => {
- impl_ref_ops! {
- impl<const LANES: usize> core::ops::Not for Simd<$scalar, LANES>
- where
- LaneCount<LANES>: SupportedLaneCount,
- {
- type Output = Self;
- fn not(self) -> Self::Output {
- self ^ Self::splat(!<$scalar>::default())
- }
- }
- }
- };
-
- { impl Neg for $scalar:ty } => {
- impl_ref_ops! {
- impl<const LANES: usize> core::ops::Neg for Simd<$scalar, LANES>
- where
- LaneCount<LANES>: SupportedLaneCount,
- {
- type Output = Self;
- fn neg(self) -> Self::Output {
- unsafe { intrinsics::simd_neg(self) }
- }
- }
- }
+ impl_op! { @binary $scalar, BitXor::bitxor, simd_xor }
};
// generic binary op with assignment when output is `Self`
- { @binary $scalar:ty, $trait:ident :: $trait_fn:ident, $assign_trait:ident :: $assign_trait_fn:ident, $intrinsic:ident } => {
+ { @binary $scalar:ty, $trait:ident :: $trait_fn:ident, $intrinsic:ident } => {
impl_ref_ops! {
impl<const LANES: usize> core::ops::$trait<Self> for Simd<$scalar, LANES>
where
}
}
}
-
- impl_ref_ops! {
- impl<const LANES: usize> core::ops::$trait<$scalar> for Simd<$scalar, LANES>
- where
- LaneCount<LANES>: SupportedLaneCount,
- {
- type Output = Self;
-
- #[inline]
- fn $trait_fn(self, rhs: $scalar) -> Self::Output {
- core::ops::$trait::$trait_fn(self, Self::splat(rhs))
- }
- }
- }
-
- impl_ref_ops! {
- impl<const LANES: usize> core::ops::$trait<Simd<$scalar, LANES>> for $scalar
- where
- LaneCount<LANES>: SupportedLaneCount,
- {
- type Output = Simd<$scalar, LANES>;
-
- #[inline]
- fn $trait_fn(self, rhs: Simd<$scalar, LANES>) -> Self::Output {
- core::ops::$trait::$trait_fn(Simd::splat(self), rhs)
- }
- }
- }
-
- impl_ref_ops! {
- impl<const LANES: usize> core::ops::$assign_trait<Self> for Simd<$scalar, LANES>
- where
- LaneCount<LANES>: SupportedLaneCount,
- {
- #[inline]
- fn $assign_trait_fn(&mut self, rhs: Self) {
- unsafe {
- *self = intrinsics::$intrinsic(*self, rhs);
- }
- }
- }
- }
-
- impl_ref_ops! {
- impl<const LANES: usize> core::ops::$assign_trait<$scalar> for Simd<$scalar, LANES>
- where
- LaneCount<LANES>: SupportedLaneCount,
- {
- #[inline]
- fn $assign_trait_fn(&mut self, rhs: $scalar) {
- core::ops::$assign_trait::$assign_trait_fn(self, Self::splat(rhs));
- }
- }
- }
};
}
impl_op! { impl Mul for $scalar }
impl_op! { impl Div for $scalar }
impl_op! { impl Rem for $scalar }
- impl_op! { impl Neg for $scalar }
)*
};
}
impl_op! { impl BitAnd for $scalar }
impl_op! { impl BitOr for $scalar }
impl_op! { impl BitXor for $scalar }
- impl_op! { impl Not for $scalar }
// Integers panic on divide by 0
impl_ref_ops! {
}
}
- impl_ref_ops! {
- impl<const LANES: usize> core::ops::Div<$scalar> for Simd<$scalar, LANES>
- where
- LaneCount<LANES>: SupportedLaneCount,
- {
- type Output = Self;
-
- #[inline]
- fn div(self, rhs: $scalar) -> Self::Output {
- if rhs == 0 {
- panic!("attempt to divide by zero");
- }
- if <$scalar>::MIN != 0 &&
- self.as_array().iter().any(|x| *x == <$scalar>::MIN) &&
- rhs == -1 as _ {
- panic!("attempt to divide with overflow");
- }
- let rhs = Self::splat(rhs);
- unsafe { intrinsics::simd_div(self, rhs) }
- }
- }
- }
-
- impl_ref_ops! {
- impl<const LANES: usize> core::ops::Div<Simd<$scalar, LANES>> for $scalar
- where
- LaneCount<LANES>: SupportedLaneCount,
- {
- type Output = Simd<$scalar, LANES>;
-
- #[inline]
- fn div(self, rhs: Simd<$scalar, LANES>) -> Self::Output {
- Simd::splat(self) / rhs
- }
- }
- }
-
- impl_ref_ops! {
- impl<const LANES: usize> core::ops::DivAssign<Self> for Simd<$scalar, LANES>
- where
- LaneCount<LANES>: SupportedLaneCount,
- {
- #[inline]
- fn div_assign(&mut self, rhs: Self) {
- *self = *self / rhs;
- }
- }
- }
-
- impl_ref_ops! {
- impl<const LANES: usize> core::ops::DivAssign<$scalar> for Simd<$scalar, LANES>
- where
- LaneCount<LANES>: SupportedLaneCount,
- {
- #[inline]
- fn div_assign(&mut self, rhs: $scalar) {
- *self = *self / rhs;
- }
- }
- }
-
// remainder panics on zero divisor
impl_ref_ops! {
impl<const LANES: usize> core::ops::Rem<Self> for Simd<$scalar, LANES>
}
}
- impl_ref_ops! {
- impl<const LANES: usize> core::ops::Rem<$scalar> for Simd<$scalar, LANES>
- where
- LaneCount<LANES>: SupportedLaneCount,
- {
- type Output = Self;
-
- #[inline]
- fn rem(self, rhs: $scalar) -> Self::Output {
- if rhs == 0 {
- panic!("attempt to calculate the remainder with a divisor of zero");
- }
- if <$scalar>::MIN != 0 &&
- self.as_array().iter().any(|x| *x == <$scalar>::MIN) &&
- rhs == -1 as _ {
- panic!("attempt to calculate the remainder with overflow");
- }
- let rhs = Self::splat(rhs);
- unsafe { intrinsics::simd_rem(self, rhs) }
- }
- }
- }
-
- impl_ref_ops! {
- impl<const LANES: usize> core::ops::Rem<Simd<$scalar, LANES>> for $scalar
- where
- LaneCount<LANES>: SupportedLaneCount,
- {
- type Output = Simd<$scalar, LANES>;
-
- #[inline]
- fn rem(self, rhs: Simd<$scalar, LANES>) -> Self::Output {
- Simd::splat(self) % rhs
- }
- }
- }
-
- impl_ref_ops! {
- impl<const LANES: usize> core::ops::RemAssign<Self> for Simd<$scalar, LANES>
- where
- LaneCount<LANES>: SupportedLaneCount,
- {
- #[inline]
- fn rem_assign(&mut self, rhs: Self) {
- *self = *self % rhs;
- }
- }
- }
-
- impl_ref_ops! {
- impl<const LANES: usize> core::ops::RemAssign<$scalar> for Simd<$scalar, LANES>
- where
- LaneCount<LANES>: SupportedLaneCount,
- {
- #[inline]
- fn rem_assign(&mut self, rhs: $scalar) {
- *self = *self % rhs;
- }
- }
- }
-
// shifts panic on overflow
impl_ref_ops! {
impl<const LANES: usize> core::ops::Shl<Self> for Simd<$scalar, LANES>
}
}
- impl_ref_ops! {
- impl<const LANES: usize> core::ops::Shl<$scalar> for Simd<$scalar, LANES>
- where
- LaneCount<LANES>: SupportedLaneCount,
- {
- type Output = Self;
-
- #[inline]
- fn shl(self, rhs: $scalar) -> Self::Output {
- if invalid_shift_rhs(rhs) {
- panic!("attempt to shift left with overflow");
- }
- let rhs = Self::splat(rhs);
- unsafe { intrinsics::simd_shl(self, rhs) }
- }
- }
- }
-
-
- impl_ref_ops! {
- impl<const LANES: usize> core::ops::ShlAssign<Self> for Simd<$scalar, LANES>
- where
- LaneCount<LANES>: SupportedLaneCount,
- {
- #[inline]
- fn shl_assign(&mut self, rhs: Self) {
- *self = *self << rhs;
- }
- }
- }
-
- impl_ref_ops! {
- impl<const LANES: usize> core::ops::ShlAssign<$scalar> for Simd<$scalar, LANES>
- where
- LaneCount<LANES>: SupportedLaneCount,
- {
- #[inline]
- fn shl_assign(&mut self, rhs: $scalar) {
- *self = *self << rhs;
- }
- }
- }
-
impl_ref_ops! {
impl<const LANES: usize> core::ops::Shr<Self> for Simd<$scalar, LANES>
where
}
}
}
-
- impl_ref_ops! {
- impl<const LANES: usize> core::ops::Shr<$scalar> for Simd<$scalar, LANES>
- where
- LaneCount<LANES>: SupportedLaneCount,
- {
- type Output = Self;
-
- #[inline]
- fn shr(self, rhs: $scalar) -> Self::Output {
- if invalid_shift_rhs(rhs) {
- panic!("attempt to shift with overflow");
- }
- let rhs = Self::splat(rhs);
- unsafe { intrinsics::simd_shr(self, rhs) }
- }
- }
- }
-
-
- impl_ref_ops! {
- impl<const LANES: usize> core::ops::ShrAssign<Self> for Simd<$scalar, LANES>
- where
- LaneCount<LANES>: SupportedLaneCount,
- {
- #[inline]
- fn shr_assign(&mut self, rhs: Self) {
- *self = *self >> rhs;
- }
- }
- }
-
- impl_ref_ops! {
- impl<const LANES: usize> core::ops::ShrAssign<$scalar> for Simd<$scalar, LANES>
- where
- LaneCount<LANES>: SupportedLaneCount,
- {
- #[inline]
- fn shr_assign(&mut self, rhs: $scalar) {
- *self = *self >> rhs;
- }
- }
- }
)*
};
}
macro_rules! impl_signed_int_ops {
{ $($scalar:ty),* } => {
impl_unsigned_int_ops! { $($scalar),* }
- $( // scalar
- impl_op! { impl Neg for $scalar }
- )*
};
}
--- /dev/null
+//! Assignment operators
+use super::*;
+use core::ops::{AddAssign, MulAssign}; // commutative binary op-assignment
+use core::ops::{BitAndAssign, BitOrAssign, BitXorAssign}; // commutative bit binary op-assignment
+use core::ops::{DivAssign, RemAssign, SubAssign}; // non-commutative binary op-assignment
+use core::ops::{ShlAssign, ShrAssign}; // non-commutative bit binary op-assignment
+
+// Arithmetic
+
+macro_rules! assign_ops {
+ ($(impl<T, U, const LANES: usize> $assignTrait:ident<U> for Simd<T, LANES>
+ where
+ Self: $trait:ident,
+ {
+ fn $assign_call:ident(rhs: U) {
+ $call:ident
+ }
+ })*) => {
+ $(impl<T, U, const LANES: usize> $assignTrait<U> for Simd<T, LANES>
+ where
+ Self: $trait<U, Output = Self>,
+ T: SimdElement,
+ LaneCount<LANES>: SupportedLaneCount,
+ {
+ #[inline]
+ fn $assign_call(&mut self, rhs: U) {
+ *self = self.$call(rhs);
+ }
+ })*
+ }
+}
+
+assign_ops! {
+ // Arithmetic
+ impl<T, U, const LANES: usize> AddAssign<U> for Simd<T, LANES>
+ where
+ Self: Add,
+ {
+ fn add_assign(rhs: U) {
+ add
+ }
+ }
+
+ impl<T, U, const LANES: usize> MulAssign<U> for Simd<T, LANES>
+ where
+ Self: Mul,
+ {
+ fn mul_assign(rhs: U) {
+ mul
+ }
+ }
+
+ impl<T, U, const LANES: usize> SubAssign<U> for Simd<T, LANES>
+ where
+ Self: Sub,
+ {
+ fn sub_assign(rhs: U) {
+ sub
+ }
+ }
+
+ impl<T, U, const LANES: usize> DivAssign<U> for Simd<T, LANES>
+ where
+ Self: Div,
+ {
+ fn div_assign(rhs: U) {
+ div
+ }
+ }
+ impl<T, U, const LANES: usize> RemAssign<U> for Simd<T, LANES>
+ where
+ Self: Rem,
+ {
+ fn rem_assign(rhs: U) {
+ rem
+ }
+ }
+
+ // Bitops
+ impl<T, U, const LANES: usize> BitAndAssign<U> for Simd<T, LANES>
+ where
+ Self: BitAnd,
+ {
+ fn bitand_assign(rhs: U) {
+ bitand
+ }
+ }
+
+ impl<T, U, const LANES: usize> BitOrAssign<U> for Simd<T, LANES>
+ where
+ Self: BitOr,
+ {
+ fn bitor_assign(rhs: U) {
+ bitor
+ }
+ }
+
+ impl<T, U, const LANES: usize> BitXorAssign<U> for Simd<T, LANES>
+ where
+ Self: BitXor,
+ {
+ fn bitxor_assign(rhs: U) {
+ bitxor
+ }
+ }
+
+ impl<T, U, const LANES: usize> ShlAssign<U> for Simd<T, LANES>
+ where
+ Self: Shl,
+ {
+ fn shl_assign(rhs: U) {
+ shl
+ }
+ }
+
+ impl<T, U, const LANES: usize> ShrAssign<U> for Simd<T, LANES>
+ where
+ Self: Shr,
+ {
+ fn shr_assign(rhs: U) {
+ shr
+ }
+ }
+}
--- /dev/null
+//! This module hacks in "implicit deref" for Simd's operators.
+//! Ideally, Rust would take care of this itself,
+//! and method calls usually handle the LHS implicitly.
+//! But this is not the case with arithmetic ops.
+use super::*;
+
+macro_rules! deref_lhs {
+ (impl<T, const LANES: usize> $trait:ident for $simd:ty {
+ fn $call:ident
+ }) => {
+ impl<T, const LANES: usize> $trait<$simd> for &$simd
+ where
+ T: SimdElement,
+ $simd: $trait<$simd, Output = $simd>,
+ LaneCount<LANES>: SupportedLaneCount,
+ {
+ type Output = Simd<T, LANES>;
+
+ #[inline]
+ #[must_use = "operator returns a new vector without mutating the inputs"]
+ fn $call(self, rhs: $simd) -> Self::Output {
+ (*self).$call(rhs)
+ }
+ }
+ };
+}
+
+macro_rules! deref_rhs {
+ (impl<T, const LANES: usize> $trait:ident for $simd:ty {
+ fn $call:ident
+ }) => {
+ impl<T, const LANES: usize> $trait<&$simd> for $simd
+ where
+ T: SimdElement,
+ $simd: $trait<$simd, Output = $simd>,
+ LaneCount<LANES>: SupportedLaneCount,
+ {
+ type Output = Simd<T, LANES>;
+
+ #[inline]
+ #[must_use = "operator returns a new vector without mutating the inputs"]
+ fn $call(self, rhs: &$simd) -> Self::Output {
+ self.$call(*rhs)
+ }
+ }
+ };
+}
+
+macro_rules! deref_ops {
+ ($(impl<T, const LANES: usize> $trait:ident for $simd:ty {
+ fn $call:ident
+ })*) => {
+ $(
+ deref_rhs! {
+ impl<T, const LANES: usize> $trait for $simd {
+ fn $call
+ }
+ }
+ deref_lhs! {
+ impl<T, const LANES: usize> $trait for $simd {
+ fn $call
+ }
+ }
+ impl<'lhs, 'rhs, T, const LANES: usize> $trait<&'rhs $simd> for &'lhs $simd
+ where
+ T: SimdElement,
+ $simd: $trait<$simd, Output = $simd>,
+ LaneCount<LANES>: SupportedLaneCount,
+ {
+ type Output = $simd;
+
+ #[inline]
+ #[must_use = "operator returns a new vector without mutating the inputs"]
+ fn $call(self, rhs: &$simd) -> Self::Output {
+ (*self).$call(*rhs)
+ }
+ }
+ )*
+ }
+}
+
+deref_ops! {
+ // Arithmetic
+ impl<T, const LANES: usize> Add for Simd<T, LANES> {
+ fn add
+ }
+
+ impl<T, const LANES: usize> Mul for Simd<T, LANES> {
+ fn mul
+ }
+
+ impl<T, const LANES: usize> Sub for Simd<T, LANES> {
+ fn sub
+ }
+
+ impl<T, const LANES: usize> Div for Simd<T, LANES> {
+ fn div
+ }
+
+ impl<T, const LANES: usize> Rem for Simd<T, LANES> {
+ fn rem
+ }
+
+ // Bitops
+ impl<T, const LANES: usize> BitAnd for Simd<T, LANES> {
+ fn bitand
+ }
+
+ impl<T, const LANES: usize> BitOr for Simd<T, LANES> {
+ fn bitor
+ }
+
+ impl<T, const LANES: usize> BitXor for Simd<T, LANES> {
+ fn bitxor
+ }
+
+ impl<T, const LANES: usize> Shl for Simd<T, LANES> {
+ fn shl
+ }
+
+ impl<T, const LANES: usize> Shr for Simd<T, LANES> {
+ fn shr
+ }
+}
--- /dev/null
+use crate::simd::intrinsics;
+use crate::simd::{LaneCount, Simd, SimdElement, SupportedLaneCount};
+use core::ops::{Neg, Not}; // unary ops
+
+macro_rules! neg {
+ ($(impl<const LANES: usize> Neg for Simd<$scalar:ty, LANES>)*) => {
+ $(impl<const LANES: usize> Neg for Simd<$scalar, LANES>
+ where
+ $scalar: SimdElement,
+ LaneCount<LANES>: SupportedLaneCount,
+ {
+ type Output = Self;
+
+ #[inline]
+ #[must_use = "operator returns a new vector without mutating the input"]
+ fn neg(self) -> Self::Output {
+ unsafe { intrinsics::simd_neg(self) }
+ }
+ })*
+ }
+}
+
+neg! {
+ impl<const LANES: usize> Neg for Simd<f32, LANES>
+
+ impl<const LANES: usize> Neg for Simd<f64, LANES>
+
+ impl<const LANES: usize> Neg for Simd<i8, LANES>
+
+ impl<const LANES: usize> Neg for Simd<i16, LANES>
+
+ impl<const LANES: usize> Neg for Simd<i32, LANES>
+
+ impl<const LANES: usize> Neg for Simd<i64, LANES>
+
+ impl<const LANES: usize> Neg for Simd<isize, LANES>
+}
+
+macro_rules! not {
+ ($(impl<const LANES: usize> Not for Simd<$scalar:ty, LANES>)*) => {
+ $(impl<const LANES: usize> Not for Simd<$scalar, LANES>
+ where
+ $scalar: SimdElement,
+ LaneCount<LANES>: SupportedLaneCount,
+ {
+ type Output = Self;
+
+ #[inline]
+ #[must_use = "operator returns a new vector without mutating the input"]
+ fn not(self) -> Self::Output {
+ self ^ (Simd::splat(!(0 as $scalar)))
+ }
+ })*
+ }
+}
+
+not! {
+ impl<const LANES: usize> Not for Simd<i8, LANES>
+
+ impl<const LANES: usize> Not for Simd<i16, LANES>
+
+ impl<const LANES: usize> Not for Simd<i32, LANES>
+
+ impl<const LANES: usize> Not for Simd<i64, LANES>
+
+ impl<const LANES: usize> Not for Simd<isize, LANES>
+
+ impl<const LANES: usize> Not for Simd<u8, LANES>
+
+ impl<const LANES: usize> Not for Simd<u16, LANES>
+
+ impl<const LANES: usize> Not for Simd<u32, LANES>
+
+ impl<const LANES: usize> Not for Simd<u64, LANES>
+
+ impl<const LANES: usize> Not for Simd<usize, LANES>
+}
simd_reduce_add_ordered, simd_reduce_and, simd_reduce_max, simd_reduce_min,
simd_reduce_mul_ordered, simd_reduce_or, simd_reduce_xor,
};
-use crate::simd::{LaneCount, Simd, SupportedLaneCount};
+use crate::simd::{LaneCount, Simd, SimdElement, SupportedLaneCount};
+use core::ops::{BitAnd, BitOr, BitXor};
macro_rules! impl_integer_reductions {
{ $scalar:ty } => {
unsafe { simd_reduce_mul_ordered(self, 1) }
}
- /// Horizontal bitwise "and". Returns the cumulative bitwise "and" across the lanes of
- /// the vector.
- #[inline]
- pub fn horizontal_and(self) -> $scalar {
- unsafe { simd_reduce_and(self) }
- }
-
- /// Horizontal bitwise "or". Returns the cumulative bitwise "or" across the lanes of
- /// the vector.
- #[inline]
- pub fn horizontal_or(self) -> $scalar {
- unsafe { simd_reduce_or(self) }
- }
-
- /// Horizontal bitwise "xor". Returns the cumulative bitwise "xor" across the lanes of
- /// the vector.
- #[inline]
- pub fn horizontal_xor(self) -> $scalar {
- unsafe { simd_reduce_xor(self) }
- }
-
/// Horizontal maximum. Returns the maximum lane in the vector.
#[inline]
pub fn horizontal_max(self) -> $scalar {
impl_float_reductions! { f32 }
impl_float_reductions! { f64 }
+
+impl<T, const LANES: usize> Simd<T, LANES>
+where
+ Self: BitAnd<Self, Output = Self>,
+ T: SimdElement + BitAnd<T, Output = T>,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+ /// Horizontal bitwise "and". Returns the cumulative bitwise "and" across the lanes of
+ /// the vector.
+ #[inline]
+ pub fn horizontal_and(self) -> T {
+ unsafe { simd_reduce_and(self) }
+ }
+}
+
+impl<T, const LANES: usize> Simd<T, LANES>
+where
+ Self: BitOr<Self, Output = Self>,
+ T: SimdElement + BitOr<T, Output = T>,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+ /// Horizontal bitwise "or". Returns the cumulative bitwise "or" across the lanes of
+ /// the vector.
+ #[inline]
+ pub fn horizontal_or(self) -> T {
+ unsafe { simd_reduce_or(self) }
+ }
+}
+
+impl<T, const LANES: usize> Simd<T, LANES>
+where
+ Self: BitXor<Self, Output = Self>,
+ T: SimdElement + BitXor<T, Output = T>,
+ LaneCount<LANES>: SupportedLaneCount,
+{
+ /// Horizontal bitwise "xor". Returns the cumulative bitwise "xor" across the lanes of
+ /// the vector.
+ #[inline]
+ pub fn horizontal_xor(self) -> T {
+ unsafe { simd_reduce_xor(self) }
+ }
+}
LaneCount<LANES>: SupportedLaneCount,
{
#[inline]
+ #[must_use = "method returns a new vector and does not mutate the original inputs"]
fn select(mask: Mask<T::Mask, LANES>, true_values: Self, false_values: Self) -> Self {
unsafe { intrinsics::simd_select(mask.to_int(), true_values, false_values) }
}
LaneCount<LANES>: SupportedLaneCount,
{
#[inline]
+ #[must_use = "method returns a new vector and does not mutate the original inputs"]
fn select(mask: Self, true_values: Self, false_values: Self) -> Self {
mask & true_values | !mask & false_values
}
/// assert_eq!(c.to_array(), [true, false, true, false]);
/// ```
#[inline]
+ #[must_use = "method returns a new vector and does not mutate the original inputs"]
pub fn select<S: Select<Self>>(self, true_values: S, false_values: S) -> S {
S::select(self, true_values, false_values)
}
/// Create a new vector from the lanes of `vector`.
///
/// Lane `i` of the output is `vector[Self::INDEX[i]]`.
+ #[inline]
+ #[must_use = "method returns a new vector and does not mutate the original inputs"]
fn swizzle<T>(vector: Simd<T, INPUT_LANES>) -> Simd<T, OUTPUT_LANES>
where
T: SimdElement,
///
/// Lane `i` is `first[j]` when `Self::INDEX[i]` is `First(j)`, or `second[j]` when it is
/// `Second(j)`.
+ #[inline]
+ #[must_use = "method returns a new vector and does not mutate the original inputs"]
fn swizzle2<T>(
first: Simd<T, INPUT_LANES>,
second: Simd<T, INPUT_LANES>,
{
/// Reverse the order of the lanes in the vector.
#[inline]
+ #[must_use = "method returns a new vector and does not mutate the original inputs"]
pub fn reverse(self) -> Self {
const fn reverse_index<const LANES: usize>() -> [usize; LANES] {
let mut index = [0; LANES];
/// while the last `LANES - OFFSET` elements move to the front. After calling `rotate_lanes_left`,
/// the element previously in lane `OFFSET` will become the first element in the slice.
#[inline]
+ #[must_use = "method returns a new vector and does not mutate the original inputs"]
pub fn rotate_lanes_left<const OFFSET: usize>(self) -> Self {
const fn rotate_index<const OFFSET: usize, const LANES: usize>() -> [usize; LANES] {
let offset = OFFSET % LANES;
/// the end while the last `OFFSET` elements move to the front. After calling `rotate_lanes_right`,
/// the element previously at index `LANES - OFFSET` will become the first element in the slice.
#[inline]
+ #[must_use = "method returns a new vector and does not mutate the original inputs"]
pub fn rotate_lanes_right<const OFFSET: usize>(self) -> Self {
const fn rotate_index<const OFFSET: usize, const LANES: usize>() -> [usize; LANES] {
let offset = LANES - OFFSET % LANES;
/// assert_eq!(y.to_array(), [2, 6, 3, 7]);
/// ```
#[inline]
+ #[must_use = "method returns a new vector and does not mutate the original inputs"]
pub fn interleave(self, other: Self) -> (Self, Self) {
const fn lo<const LANES: usize>() -> [Which; LANES] {
let mut idx = [Which::First(0); LANES];
/// assert_eq!(y.to_array(), [4, 5, 6, 7]);
/// ```
#[inline]
+ #[must_use = "method returns a new vector and does not mutate the original inputs"]
pub fn deinterleave(self, other: Self) -> (Self, Self) {
const fn even<const LANES: usize>() -> [Which; LANES] {
let mut idx = [Which::First(0); LANES];
/// Raw transmutation to an unsigned integer vector type with the
/// same size and number of lanes.
#[inline]
+ #[must_use = "method returns a new vector and does not mutate the original value"]
pub fn to_bits(self) -> Simd<$bits_ty, LANES> {
assert_eq!(core::mem::size_of::<Self>(), core::mem::size_of::<Simd<$bits_ty, LANES>>());
unsafe { core::mem::transmute_copy(&self) }
/// Raw transmutation from an unsigned integer vector type with the
/// same size and number of lanes.
#[inline]
+ #[must_use = "method returns a new vector and does not mutate the original value"]
pub fn from_bits(bits: Simd<$bits_ty, LANES>) -> Self {
assert_eq!(core::mem::size_of::<Self>(), core::mem::size_of::<Simd<$bits_ty, LANES>>());
unsafe { core::mem::transmute_copy(&bits) }
/// Produces a vector where every lane has the absolute value of the
/// equivalently-indexed lane in `self`.
#[inline]
+ #[must_use = "method returns a new vector and does not mutate the original value"]
pub fn abs(self) -> Self {
unsafe { intrinsics::simd_fabs(self) }
}
/// hardware in mind.
#[cfg(feature = "std")]
#[inline]
+ #[must_use = "method returns a new vector and does not mutate the original value"]
pub fn mul_add(self, a: Self, b: Self) -> Self {
unsafe { intrinsics::simd_fma(self, a, b) }
}
/// Produces a vector where every lane has the square root value
/// of the equivalently-indexed lane in `self`
#[inline]
+ #[must_use = "method returns a new vector and does not mutate the original value"]
#[cfg(feature = "std")]
pub fn sqrt(self) -> Self {
unsafe { intrinsics::simd_fsqrt(self) }
/// Takes the reciprocal (inverse) of each lane, `1/x`.
#[inline]
+ #[must_use = "method returns a new vector and does not mutate the original value"]
pub fn recip(self) -> Self {
Self::splat(1.0) / self
}
/// Converts each lane from radians to degrees.
#[inline]
+ #[must_use = "method returns a new vector and does not mutate the original value"]
pub fn to_degrees(self) -> Self {
// to_degrees uses a special constant for better precision, so extract that constant
self * Self::splat(<$type>::to_degrees(1.))
/// Converts each lane from degrees to radians.
#[inline]
+ #[must_use = "method returns a new vector and does not mutate the original value"]
pub fn to_radians(self) -> Self {
self * Self::splat(<$type>::to_radians(1.))
}
/// Returns true for each lane if it has a positive sign, including
/// `+0.0`, `NaN`s with positive sign bit and positive infinity.
#[inline]
+ #[must_use = "method returns a new mask and does not mutate the original value"]
pub fn is_sign_positive(self) -> Mask<$mask_ty, LANES> {
!self.is_sign_negative()
}
/// Returns true for each lane if it has a negative sign, including
/// `-0.0`, `NaN`s with negative sign bit and negative infinity.
#[inline]
+ #[must_use = "method returns a new mask and does not mutate the original value"]
pub fn is_sign_negative(self) -> Mask<$mask_ty, LANES> {
let sign_bits = self.to_bits() & Simd::splat((!0 >> 1) + 1);
sign_bits.lanes_gt(Simd::splat(0))
/// Returns true for each lane if its value is `NaN`.
#[inline]
+ #[must_use = "method returns a new mask and does not mutate the original value"]
pub fn is_nan(self) -> Mask<$mask_ty, LANES> {
self.lanes_ne(self)
}
/// Returns true for each lane if its value is positive infinity or negative infinity.
#[inline]
+ #[must_use = "method returns a new mask and does not mutate the original value"]
pub fn is_infinite(self) -> Mask<$mask_ty, LANES> {
self.abs().lanes_eq(Self::splat(<$type>::INFINITY))
}
/// Returns true for each lane if its value is neither infinite nor `NaN`.
#[inline]
+ #[must_use = "method returns a new mask and does not mutate the original value"]
pub fn is_finite(self) -> Mask<$mask_ty, LANES> {
self.abs().lanes_lt(Self::splat(<$type>::INFINITY))
}
/// Returns true for each lane if its value is subnormal.
#[inline]
+ #[must_use = "method returns a new mask and does not mutate the original value"]
pub fn is_subnormal(self) -> Mask<$mask_ty, LANES> {
self.abs().lanes_ne(Self::splat(0.0)) & (self.to_bits() & Self::splat(<$type>::INFINITY).to_bits()).lanes_eq(Simd::splat(0))
}
/// Returns true for each lane if its value is neither neither zero, infinite,
/// subnormal, or `NaN`.
#[inline]
+ #[must_use = "method returns a new mask and does not mutate the original value"]
pub fn is_normal(self) -> Mask<$mask_ty, LANES> {
!(self.abs().lanes_eq(Self::splat(0.0)) | self.is_nan() | self.is_subnormal() | self.is_infinite())
}
/// * `-1.0` if the number is negative, `-0.0`, or `NEG_INFINITY`
/// * `NAN` if the number is `NAN`
#[inline]
+ #[must_use = "method returns a new vector and does not mutate the original value"]
pub fn signum(self) -> Self {
self.is_nan().select(Self::splat(<$type>::NAN), Self::splat(1.0).copysign(self))
}
///
/// If any lane is a `NAN`, then a `NAN` with the sign of `sign` is returned.
#[inline]
+ #[must_use = "method returns a new vector and does not mutate the original value"]
pub fn copysign(self, sign: Self) -> Self {
let sign_bit = sign.to_bits() & Self::splat(-0.).to_bits();
let magnitude = self.to_bits() & !Self::splat(-0.).to_bits();
///
/// If one of the values is `NAN`, then the other value is returned.
#[inline]
+ #[must_use = "method returns a new vector and does not mutate the original value"]
pub fn min(self, other: Self) -> Self {
// TODO consider using an intrinsic
self.is_nan().select(
///
/// If one of the values is `NAN`, then the other value is returned.
#[inline]
+ #[must_use = "method returns a new vector and does not mutate the original value"]
pub fn max(self, other: Self) -> Self {
// TODO consider using an intrinsic
self.is_nan().select(
/// greater than `max`, and the corresponding lane in `min` if the lane is less
/// than `min`. Otherwise returns the lane in `self`.
#[inline]
+ #[must_use = "method returns a new vector and does not mutate the original value"]
pub fn clamp(self, min: Self, max: Self) -> Self {
assert!(
min.lanes_le(max).all(),
pub fn wrapping_add(self, addend: Simd<usize, LANES>) -> Self {
unsafe {
let x: Simd<usize, LANES> = mem::transmute_copy(&self);
- mem::transmute_copy(&{ x + (addend * mem::size_of::<T>()) })
+ mem::transmute_copy(&{ x + (addend * Simd::splat(mem::size_of::<T>())) })
}
}
}
pub fn wrapping_add(self, addend: Simd<usize, LANES>) -> Self {
unsafe {
let x: Simd<usize, LANES> = mem::transmute_copy(&self);
- mem::transmute_copy(&{ x + (addend * mem::size_of::<T>()) })
+ mem::transmute_copy(&{ x + (addend * Simd::splat(mem::size_of::<T>())) })
}
}
}
from_transmute! { unsafe u8x16 => __m128i }
from_transmute! { unsafe u8x32 => __m256i }
-//from_transmute! { unsafe u8x64 => __m512i }
+from_transmute! { unsafe u8x64 => __m512i }
from_transmute! { unsafe i8x16 => __m128i }
from_transmute! { unsafe i8x32 => __m256i }
-//from_transmute! { unsafe i8x64 => __m512i }
+from_transmute! { unsafe i8x64 => __m512i }
from_transmute! { unsafe u16x8 => __m128i }
from_transmute! { unsafe u16x16 => __m256i }
--- /dev/null
+// Test that we handle all our "auto-deref" cases correctly.
+#![feature(portable_simd)]
+use core_simd::f32x4;
+
+#[cfg(target_arch = "wasm32")]
+use wasm_bindgen_test::*;
+
+#[cfg(target_arch = "wasm32")]
+wasm_bindgen_test_configure!(run_in_browser);
+
+#[test]
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
+fn deref() {
+ let x = f32x4::splat(1.0);
+ let y = f32x4::splat(2.0);
+ let a = &x;
+ let b = &y;
+ assert_eq!(f32x4::splat(3.0), x + y);
+ assert_eq!(f32x4::splat(3.0), x + b);
+ assert_eq!(f32x4::splat(3.0), a + y);
+ assert_eq!(f32x4::splat(3.0), a + b);
+}
);
}
- fn scalar_rhs<const LANES: usize>() {
- test_helpers::test_binary_scalar_rhs_elementwise(
- &<Simd<$scalar, LANES> as core::ops::$trait<$scalar>>::$fn,
- &$scalar_fn,
- &|_, _| true,
- );
- }
-
- fn scalar_lhs<const LANES: usize>() {
- test_helpers::test_binary_scalar_lhs_elementwise(
- &<$scalar as core::ops::$trait<Simd<$scalar, LANES>>>::$fn,
- &$scalar_fn,
- &|_, _| true,
- );
- }
-
fn assign<const LANES: usize>() {
test_helpers::test_binary_elementwise(
&|mut a, b| { <Simd<$scalar, LANES> as core::ops::$trait_assign>::$fn_assign(&mut a, b); a },
&|_, _| true,
);
}
-
- fn assign_scalar_rhs<const LANES: usize>() {
- test_helpers::test_binary_scalar_rhs_elementwise(
- &|mut a, b| { <Simd<$scalar, LANES> as core::ops::$trait_assign<$scalar>>::$fn_assign(&mut a, b); a },
- &$scalar_fn,
- &|_, _| true,
- );
- }
}
}
};
);
}
- fn scalar_rhs<const LANES: usize>() {
- test_helpers::test_binary_scalar_rhs_elementwise(
- &<Simd<$scalar, LANES> as core::ops::$trait<$scalar>>::$fn,
- &$scalar_fn,
- &|x, y| x.iter().all(|x| $check_fn(*x, y)),
- );
- }
-
- fn scalar_lhs<const LANES: usize>() {
- test_helpers::test_binary_scalar_lhs_elementwise(
- &<$scalar as core::ops::$trait<Simd<$scalar, LANES>>>::$fn,
- &$scalar_fn,
- &|x, y| y.iter().all(|y| $check_fn(x, *y)),
- );
- }
-
fn assign<const LANES: usize>() {
test_helpers::test_binary_elementwise(
&|mut a, b| { <Simd<$scalar, LANES> as core::ops::$trait_assign>::$fn_assign(&mut a, b); a },
&|x, y| x.iter().zip(y.iter()).all(|(x, y)| $check_fn(*x, *y)),
)
}
-
- fn assign_scalar_rhs<const LANES: usize>() {
- test_helpers::test_binary_scalar_rhs_elementwise(
- &|mut a, b| { <Simd<$scalar, LANES> as core::ops::$trait_assign<$scalar>>::$fn_assign(&mut a, b); a },
- &$scalar_fn,
- &|x, y| x.iter().all(|x| $check_fn(*x, y)),
- )
- }
}
}
};
fn lanes_32() {
implementation::<32>();
}
+
+ #[test]
+ #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
+ fn lanes_64() {
+ implementation::<64>();
+ }
}
)*
}
fn lanes_32() {
implementation::<32>();
}
+
+ #[test]
+ #[should_panic]
+ fn lanes_64() {
+ implementation::<64>();
+ }
}
)*
}
//! [`OsStr`] and Rust strings work similarly to those for [`CString`]
//! and [`CStr`].
//!
-//! * [`OsString`] represents an owned string in whatever
-//! representation the operating system prefers. In the Rust standard
-//! library, various APIs that transfer strings to/from the operating
+//! * [`OsString`] losslessly represents an owned platform string. However, this
+//! representation is not necessarily in a form native to the platform.
+//! In the Rust standard library, various APIs that transfer strings to/from the operating
//! system use [`OsString`] instead of plain strings. For example,
//! [`env::var_os()`] is used to query environment variables; it
//! returns an <code>[Option]<[OsString]></code>. If the environment variable
//! your code can detect errors in case the environment variable did
//! not in fact contain valid Unicode data.
//!
-//! * [`OsStr`] represents a borrowed reference to a string in a
-//! format that can be passed to the operating system. It can be
-//! converted into a UTF-8 Rust string slice in a similar way to
+//! * [`OsStr`] losslessly represents a borrowed reference to a platform string.
+//! However, this representation is not necessarily in a form native to the platform.
+//! It can be converted into a UTF-8 Rust string slice in a similar way to
//! [`OsString`].
//!
//! # Conversions
//!
//! ## On Windows
//!
+//! An [`OsStr`] can be losslessly converted to a native Windows string. And
+//! a native Windows string can be losslessly converted to an [`OsString`].
+//!
//! On Windows, [`OsStr`] implements the
//! <code>std::os::windows::ffi::[OsStrExt][windows.OsStrExt]</code> trait,
//! which provides an [`encode_wide`] method. This provides an
-//! iterator that can be [`collect`]ed into a vector of [`u16`].
+//! iterator that can be [`collect`]ed into a vector of [`u16`]. After a nul
+//! characters is appended, this is the same as a native Windows string.
//!
//! Additionally, on Windows [`OsString`] implements the
//! <code>std::os::windows:ffi::[OsStringExt][windows.OsStringExt]</code>
-//! trait, which provides a [`from_wide`] method. The result of this
-//! method is an [`OsString`] which can be round-tripped to a Windows
-//! string losslessly.
+//! trait, which provides a [`from_wide`] method to convert a native Windows
+//! string (without the terminating nul character) to an [`OsString`].
//!
//! [Unicode scalar value]: https://www.unicode.org/glossary/#unicode_scalar_value
//! [Unicode code point]: https://www.unicode.org/glossary/#code_point
target_arch = "aarch64",
target_arch = "arm",
target_arch = "powerpc",
- target_arch = "powerpc64"
+ target_arch = "powerpc64",
+ target_arch = "riscv64"
)
),
all(
target_arch = "aarch64",
target_arch = "arm",
target_arch = "powerpc",
- target_arch = "powerpc64"
+ target_arch = "powerpc64",
+ target_arch = "riscv64"
)
),
all(
err = "unknown OS type: {}".format(ostype)
sys.exit(err)
- if cputype == 'powerpc' and ostype == 'unknown-freebsd':
+ if cputype in ['powerpc', 'riscv'] and ostype == 'unknown-freebsd':
cputype = subprocess.check_output(
['uname', '-p']).strip().decode(default_encoding)
cputype_mapper = {
Change this file to make users of the `download-ci-llvm` configuration download
a new version of LLVM from CI, even if the LLVM submodule hasn’t changed.
-Last change is for: https://github.com/rust-lang/rust/pull/88069
+Last change is for: https://github.com/rust-lang/rust/pull/91229
}
}
- if target.starts_with("riscv") {
- // In RISC-V, using C++ atomics require linking to `libatomic` but the LLVM build
- // system check cannot detect this. Therefore it is set manually here.
+ if !target.contains("freebsd") && target.starts_with("riscv") {
+ // RISC-V GCC erroneously requires linking against
+ // `libatomic` when using 1-byte and 2-byte C++
+ // atomics but the LLVM build system check cannot
+ // detect this. Therefore it is set manually here.
+ // FreeBSD uses Clang as its system compiler and
+ // provides no libatomic in its base system so does
+ // not want this.
if !builder.config.llvm_tools_enabled {
cfg.define("CMAKE_EXE_LINKER_FLAGS", "-latomic");
} else {
+FROM ubuntu:20.04
+RUN apt-get update && \
+ apt-get install -y --no-install-recommends \
+ curl \
+ ca-certificates
+WORKDIR /tmp
+RUN curl -f https://curl.se/ca/cacert.pem -o cacert.pem
+
FROM ubuntu:16.04
+# The ca-certificates in ubuntu-16 is too old, so update the certificates
+# with something more recent.
+COPY --from=0 /tmp/cacert.pem /tmp/cacert.pem
+ENV CURL_CA_BUNDLE /tmp/cacert.pem
+
COPY scripts/cross-apt-packages.sh /scripts/
RUN sh /scripts/cross-apt-packages.sh
+FROM ubuntu:20.04
+RUN apt-get update && \
+ apt-get install -y --no-install-recommends \
+ curl \
+ ca-certificates
+WORKDIR /tmp
+RUN curl -f https://curl.se/ca/cacert.pem -o cacert.pem
+
FROM ubuntu:16.04
+# The ca-certificates in ubuntu-16 is too old, so update the certificates
+# with something more recent.
+COPY --from=0 /tmp/cacert.pem /tmp/cacert.pem
+ENV CURL_CA_BUNDLE /tmp/cacert.pem
+
COPY scripts/cross-apt-packages.sh /scripts/
RUN sh /scripts/cross-apt-packages.sh
+FROM ubuntu:20.04
+RUN apt-get update && \
+ apt-get install -y --no-install-recommends \
+ curl \
+ ca-certificates
+WORKDIR /tmp
+RUN curl -f https://curl.se/ca/cacert.pem -o cacert.pem
+
FROM ubuntu:16.04
+# The ca-certificates in ubuntu-16 is too old, so update the certificates
+# with something more recent.
+COPY --from=0 /tmp/cacert.pem /tmp/cacert.pem
+ENV CURL_CA_BUNDLE /tmp/cacert.pem
+
COPY scripts/cross-apt-packages.sh /scripts/
RUN sh /scripts/cross-apt-packages.sh
+FROM ubuntu:20.04
+RUN apt-get update && \
+ apt-get install -y --no-install-recommends \
+ curl \
+ ca-certificates
+WORKDIR /tmp
+RUN curl -f https://curl.se/ca/cacert.pem -o cacert.pem
+
FROM ubuntu:16.04
+# The ca-certificates in ubuntu-16 is too old, so update the certificates
+# with something more recent.
+COPY --from=0 /tmp/cacert.pem /tmp/cacert.pem
+ENV CURL_CA_BUNDLE /tmp/cacert.pem
+
COPY scripts/cross-apt-packages.sh /scripts/
RUN sh /scripts/cross-apt-packages.sh
-Subproject commit a5e0c5b2c5f9054be3b961aea2c7edfeea591de8
+Subproject commit 5f9358faeb1f46e19b8a23a21e79fd7fe150491e
-Subproject commit 8e0ec8c77d8b28b86159fdee9d33a758225ecf9c
+Subproject commit beea0a3cdc3885375342fd010f9ad658e6a5e09a
-Subproject commit c6b4bf831e9a40aec34f53067d20634839a6778b
+Subproject commit 49681ea4a9fa81173dbe9ffed74b4d4a35eae9e3
-Subproject commit c0f222da23568477155991d391c9ce918e381351
+Subproject commit 954f3d441ad880737a13e241108f791a4d2a38cd
-Subproject commit 43f82530210b83cf888282b207ed13d5893da9b2
+Subproject commit 1ca6a7bd1d73edc4a3e6c7d6a40f5d4b66c1e517
-Subproject commit a2fc9635029c04e692474965a6606f8e286d539a
+Subproject commit a374e7d8bb6b79de45b92295d06b4ac0ef35bc09
`riscv32gc-unknown-linux-gnu` | | | RISC-V Linux (kernel 5.4, glibc 2.33)
`riscv32gc-unknown-linux-musl` | | | RISC-V Linux (kernel 5.4, musl + RISCV32 support patches)
`riscv32imc-esp-espidf` | ✓ | | RISC-V ESP-IDF
+`riscv64gc-unknown-freebsd` | | | RISC-V FreeBSD
`riscv64gc-unknown-linux-musl` | | | RISC-V Linux (kernel 4.20, musl 1.2.0)
`s390x-unknown-linux-musl` | | | S390x Linux (kernel 2.6.32, MUSL)
`sparc-unknown-linux-gnu` | ✓ | | 32-bit SPARC Linux
# fn foo() {}
```
-`edition2018` tells `rustdoc` that the code sample should be compiled using
-the 2018 edition of Rust. Similarly, you can specify `edition2015` to compile
-the code with the 2015 edition.
+`edition2015`, `edition2018` and `edition2021` tell `rustdoc`
+that the code sample should be compiled using the respective edition of Rust.
```rust
/// Only runs on the 2018 edition.
- wasm32
- BPF
- SPIR-V
+- AVR
## Basic usage
- wasm32
- BPF
- SPIR-V
+- AVR
Support for more targets may be added in the future. The compiler will emit an error if `asm!` is used on an unsupported target.
| wasm32 | `local` | None\* | `r` |
| BPF | `reg` | `r[0-10]` | `r` |
| BPF | `wreg` | `w[0-10]` | `w` |
+| AVR | `reg` | `r[2-25]`, `XH`, `XL`, `ZH`, `ZL` | `r` |
+| AVR | `reg_upper` | `r[16-25]`, `XH`, `XL`, `ZH`, `ZL` | `d` |
+| AVR | `reg_pair` | `r3r2` .. `r25r24`, `X`, `Z` | `r` |
+| AVR | `reg_iw` | `r25r24`, `X`, `Z` | `w` |
+| AVR | `reg_ptr` | `X`, `Z` | `e` |
> **Note**: On x86 we treat `reg_byte` differently from `reg` because the compiler can allocate `al` and `ah` separately whereas `reg` reserves the whole register.
>
| wasm32 | `local` | None | `i8` `i16` `i32` `i64` `f32` `f64` |
| BPF | `reg` | None | `i8` `i16` `i32` `i64` |
| BPF | `wreg` | `alu32` | `i8` `i16` `i32` |
+| AVR | `reg`, `reg_upper` | None | `i8` |
+| AVR | `reg_pair`, `reg_iw`, `reg_ptr` | None | `i16` |
> **Note**: For the purposes of the above table pointers, function pointers and `isize`/`usize` are treated as the equivalent integer type (`i16`/`i32`/`i64` depending on the target).
| Hexagon | `r30` | `fr` |
| Hexagon | `r31` | `lr` |
| BPF | `r[0-10]` | `w[0-10]` |
+| AVR | `XH` | `r27` |
+| AVR | `XL` | `r26` |
+| AVR | `ZH` | `r31` |
+| AVR | `ZL` | `r30` |
Some registers cannot be used for input or output operands:
| Architecture | Unsupported register | Reason |
| ------------ | -------------------- | ------ |
| All | `sp` | The stack pointer must be restored to its original value at the end of an asm code block. |
-| All | `bp` (x86), `x29` (AArch64), `x8` (RISC-V), `fr` (Hexagon), `$fp` (MIPS) | The frame pointer cannot be used as an input or output. |
+| All | `bp` (x86), `x29` (AArch64), `x8` (RISC-V), `fr` (Hexagon), `$fp` (MIPS), `Y` (AVR) | The frame pointer cannot be used as an input or output. |
| ARM | `r7` or `r11` | On ARM the frame pointer can be either `r7` or `r11` depending on the target. The frame pointer cannot be used as an input or output. |
| All | `si` (x86-32), `bx` (x86-64), `r6` (ARM), `x19` (AArch64), `r19` (Hexagon), `x9` (RISC-V) | This is used internally by LLVM as a "base pointer" for functions with complex stack frames. |
| x86 | `k0` | This is a constant zero register which can't be modified. |
| RISC-V | `x0` | This is a constant zero register which can't be modified. |
| RISC-V | `gp`, `tp` | These registers are reserved and cannot be used as inputs or outputs. |
| Hexagon | `lr` | This is the link register which cannot be used as an input or output. |
+| AVR | `r0`, `r1`, `r1r0` | Due to an issue in LLVM, the `r0` and `r1` registers cannot be used as inputs or outputs. If modified, they must be restored to their original values before the end of the block. |
In some cases LLVM will allocate a "reserved register" for `reg` operands even though this register cannot be explicitly specified. Assembly code making use of reserved registers should be careful since `reg` operands may alias with those registers. Reserved registers are the frame pointer and base pointer
- The frame pointer and LLVM base pointer on all architectures.
- `r9` on ARM.
- `x18` on AArch64.
+- `r0` and `r1` on AVR.
## Template modifiers
- RISC-V
- Floating-point exception flags in `fcsr` (`fflags`).
- Vector extension state (`vtype`, `vl`, `vcsr`).
+ - AVR
+ - The status register `SREG`.
- On x86, the direction flag (DF in `EFLAGS`) is clear on entry to an asm block and must be clear on exit.
- Behavior is undefined if the direction flag is set on exiting an asm block.
- The requirement of restoring the stack pointer and non-output registers to their original value only applies when exiting an `asm!` block.
map.insert("theme-choices".to_owned(), 1);
map.insert("settings-menu".to_owned(), 1);
map.insert("help-button".to_owned(), 1);
- map.insert("main".to_owned(), 1);
+ map.insert("main-content".to_owned(), 1);
map.insert("search".to_owned(), 1);
map.insert("crate-search".to_owned(), 1);
map.insert("render-detail".to_owned(), 1);
"examples",
"method.into_iter",
"foo",
- "main",
+ "main-content",
"search",
"methods",
"examples",
"examples-2",
"method.into_iter-1",
"foo-1",
- "main-1",
+ "main-content-1",
"search-1",
"methods",
"examples-3",
);
t(
&mut map,
- "# Main",
- "<h2 id=\"main-1\" class=\"section-header\"><a href=\"#main-1\">Main</a></h2>",
+ "# Search",
+ "<h2 id=\"search-1\" class=\"section-header\"><a href=\"#search-1\">Search</a></h2>",
);
t(
&mut map,
rules.
*/
-#main .attributes {
+#main-content .attributes {
/* Since there is no toggle (the "[-]") when JS is disabled, no need for this margin either. */
margin-left: 0 !important;
}
font: 16px/1.4 "Source Serif 4", NanumBarunGothic, serif;
margin: 0;
position: relative;
- padding: 10px 15px 20px 15px;
-webkit-font-feature-settings: "kern", "liga";
-moz-font-feature-settings: "kern", "liga";
div.impl-items > div:not(.docblock):not(.item-info),
.content ul.crate a.crate, a.srclink,
/* This selector is for the items listed in the "all items" page. */
-#main > ul.docblock > li > a {
+#main-content > ul.docblock > li > a {
font-family: "Fira Sans", Arial, NanumBarunGothic, sans-serif;
}
/* end tweaks for normalize.css 8 */
+.rustdoc {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: nowrap;
+}
+
+main {
+ position: relative;
+ flex-grow: 1;
+ padding: 10px 15px 40px 45px;
+ min-width: 0;
+}
+
+.source main {
+ padding: 15px;
+}
+
+.width-limiter {
+ max-width: 960px;
+ margin-right: auto;
+}
+
+.source .width-limiter {
+ max-width: unset;
+}
+
details:not(.rustdoc-toggle) summary {
margin-bottom: .6em;
}
}
.source .content {
- margin-top: 50px;
max-width: none;
overflow: visible;
margin-left: 0px;
}
nav.sub {
+ position: relative;
font-size: 16px;
text-transform: uppercase;
}
+.sub-container {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: nowrap;
+}
+
+.sub-logo-container {
+ display: none;
+ margin-right: 20px;
+}
+
+.source .sub-logo-container {
+ display: block;
+}
+
+.source .sub-logo-container > img {
+ height: 60px;
+ width: 60px;
+ object-fit: contain;
+}
+
.sidebar {
width: 200px;
- position: fixed;
- left: 0;
- top: 0;
- bottom: 0;
overflow-y: scroll;
+ position: sticky;
+ min-width: 200px;
+ height: 100vh;
+ top: 0;
+ left: 0;
}
.rustdoc.source .sidebar {
+ width: 50px;
+ min-width: 0px;
+ max-width: 300px;
+ flex-grow: 0;
+ flex-shrink: 0;
+ flex-basis: auto;
+ border-right: 1px solid;
+ overflow-x: hidden;
+ /* The sidebar is by default hidden */
+ overflow-y: hidden;
+}
+
+.source .sidebar > *:not(:first-child) {
+ transition: opacity 0.5s, visibility 0.2s;
+ opacity: 0;
+ visibility: hidden;
+}
+
+.source .sidebar.expanded {
overflow-y: auto;
+ width: 300px;
+}
+
+.source .sidebar.expanded > * {
+ opacity: 1;
+ visibility: visible;
}
/* Improve the scrollbar display on firefox */
margin-right: -10px;
}
-.content, nav {
- max-width: 960px;
-}
-
/* Everything else */
.hidden {
}
.logo-container {
- height: 100px;
- width: 100px;
- position: relative;
- margin: 20px auto;
- display: block;
+ display: flex;
margin-top: 10px;
+ margin-bottom: 10px;
+ justify-content: center;
}
.logo-container > img {
- max-width: 100px;
- max-height: 100px;
- height: 100%;
- position: absolute;
- left: 50%;
- top: 50%;
- transform: translate(-50%, -50%);
- display: block;
+ height: 100px;
+ width: 100px;
}
.sidebar .location {
display: none;
}
-.content {
- padding: 15px 0;
-}
-
.source .content pre.rust {
white-space: pre;
overflow: auto;
}
#search {
- margin-left: 230px;
position: relative;
}
display: inline-block;
}
-#main {
+#main-content {
position: relative;
}
-#main > .since {
+#main-content > .since {
top: inherit;
font-family: "Fira Sans", Arial, sans-serif;
}
flex-basis: 100%;
}
-#main > .item-info {
+#main-content > .item-info {
margin-top: 0;
}
nav:not(.sidebar) {
+ flex-grow: 1;
border-bottom: 1px solid;
padding-bottom: 10px;
- margin-bottom: 10px;
+ margin-bottom: 25px;
+}
+.source nav:not(.sidebar).sub {
+ margin-left: 32px;
}
nav.main {
padding: 20px 0;
nav.sum { text-align: right; }
nav.sub form { display: inline; }
-nav.sub, .content {
- margin-left: 230px;
-}
-
a {
text-decoration: none;
background: transparent;
.search-container {
position: relative;
+ max-width: 960px;
}
.search-container > div {
display: inline-flex;
}
#sidebar-toggle {
- position: fixed;
- top: 30px;
- left: 300px;
- z-index: 10;
- padding: 3px;
- border-top-right-radius: 3px;
- border-bottom-right-radius: 3px;
+ position: sticky;
+ top: 0;
+ left: 0;
cursor: pointer;
font-weight: bold;
- transition: left .5s;
font-size: 1.2em;
- border: 1px solid;
- border-left: 0;
+ border-bottom: 1px solid;
+ display: flex;
+ height: 40px;
+ justify-content: center;
+ align-items: center;
+ z-index: 10;
}
#source-sidebar {
- position: fixed;
- top: 0;
- bottom: 0;
- left: 0;
width: 300px;
z-index: 1;
overflow: auto;
- transition: left .5s;
- border-right: 1px solid;
}
#source-sidebar > .title {
font-size: 1.5em;
.theme-picker {
position: absolute;
- left: 211px;
- top: 19px;
+ left: -34px;
+ top: 9px;
}
.theme-picker button {
left: -5px;
}
-#main > ul {
+#main-content > ul {
padding-left: 10px;
}
-#main > ul > li {
+#main-content > ul > li {
list-style: none;
}
.docblock > .information:first-child > .tooltip {
margin-top: 16px;
}
+
+ /* When we expand the sidebar on the source code page, we hide the logo on the left of the
+ search bar to have more space. */
+ .sidebar.expanded + main .width-limiter .sub-logo-container.rust-logo {
+ display: none;
+ }
+
+ /* It doesn't render well on mobile because of the layout, so better only have the transition
+ on desktop. */
+ .rustdoc.source .sidebar {
+ transition: width .5s;
+ }
}
@media (max-width: 700px) {
padding-top: 0px;
}
- .rustdoc > .sidebar {
+ main {
+ padding-left: 15px;
+ padding-top: 0px;
+ }
+
+ .rustdoc {
+ flex-direction: column;
+ }
+
+ .rustdoc:not(.source) > .sidebar {
+ width: 100%;
height: 45px;
min-height: 40px;
+ max-height: 45px;
margin: 0;
- margin-left: -15px;
padding: 0 15px;
position: static;
z-index: 11;
+ overflow-y: hidden;
+ }
+
+ .rustdoc.source > .sidebar {
+ position: fixed;
+ top: 0;
+ left: 0;
+ margin: 0;
+ z-index: 11;
+ width: 0;
+ }
+
+ .sidebar.mobile {
+ position: sticky !important;
+ top: 0;
+ left: 0;
+ width: 100%;
+ margin-left: 0;
+ background-color: rgba(0,0,0,0);
}
.sidebar > .location {
padding: 0;
}
- .sidebar .logo-container {
+ .rustdoc:not(.source) .sidebar .logo-container {
width: 35px;
height: 35px;
margin-top: 5px;
cursor: pointer;
width: 45px;
left: 0;
+ top: 0;
text-align: center;
display: block;
border-bottom: 1px solid;
nav.sub {
width: calc(100% - 32px);
- float: right;
+ margin-left: 32px;
+ margin-bottom: 10px;
+ }
+
+ .source nav:not(.sidebar).sub {
+ margin-left: 32px;
}
.content {
margin-left: 0px;
}
- #main, #search {
- margin-top: 45px;
- padding: 0;
+ .source .content {
+ margin-top: 10px;
}
#search {
margin-left: 0;
+ padding: 0;
}
.anchor {
}
.theme-picker {
- left: 10px;
- top: 54px;
z-index: 1;
}
height: 50px;
}
- .sidebar.mobile {
- position: fixed;
- width: 100%;
- margin-left: 0;
- background-color: rgba(0,0,0,0);
- height: 100%;
- }
- /*
- This allows to prevent the version text to overflow the sidebar title on mobile mode when the
- sidebar is displayed (after clicking on the "hamburger" button).
- */
- .sidebar.mobile > div.version {
- overflow: hidden;
- max-height: 33px;
- }
- .sidebar {
- width: calc(100% + 30px);
- }
-
.show-it, .sidebar-elems:focus-within {
z-index: 2;
left: 0;
border-bottom: 1px solid;
}
- #main > details.rustdoc-toggle > summary::before,
- #main > div > details.rustdoc-toggle > summary::before {
+ #main-content > details.rustdoc-toggle > summary::before,
+ #main-content > div > details.rustdoc-toggle > summary::before {
left: -11px;
}
margin: 10px;
}
- #sidebar-toggle {
+ .sidebar.expanded #sidebar-toggle {
+ font-size: 1.5rem;
+ }
+
+ .sidebar:not(.expanded) #sidebar-toggle {
+ position: fixed;
+ left: 1px;
top: 100px;
width: 30px;
font-size: 1.5rem;
text-align: center;
padding: 0;
+ z-index: 10;
+ border-top-right-radius: 3px;
+ border-bottom-right-radius: 3px;
+ cursor: pointer;
+ font-weight: bold;
+ border: 1px solid;
+ border-left: 0;
}
#source-sidebar {
z-index: 11;
}
- #main > .line-numbers {
+ #main-content > .line-numbers {
margin-top: 0;
}
height: 73px;
}
- /* This is to prevent the search bar from being underneath the <section>
- * element following it.
- */
- #main, #search {
- margin-top: 100px;
- }
-
- #main > table:not(.table-display) td {
+ #main-content > table:not(.table-display) td {
word-break: break-word;
width: 50%;
}
.docblock code {
overflow-wrap: anywhere;
}
+
+ .sub-container {
+ flex-direction: column;
+ }
+
+ .sub-logo-container {
+ align-self: center;
+ }
+
+ .source .sub-logo-container > img {
+ height: 35px;
+ width: 35px;
+ }
+
+ .sidebar:not(.expanded) #sidebar-toggle {
+ top: 10px;
+ }
}
background-color: #14191f;
}
-.logo-container.rust-logo > img {
+.rust-logo > img {
filter: drop-shadow(1px 0 0px #fff)
drop-shadow(0 1px 0 #fff)
drop-shadow(-1px 0 0 #fff)
}
.source .sidebar {
- background-color: #0f1419;
+ background-color: #14191f;
}
.sidebar .location {
background-color: #505050;
}
-.logo-container.rust-logo > img {
+.rust-logo > img {
filter: drop-shadow(1px 0 0px #fff)
drop-shadow(0 1px 0 #fff)
drop-shadow(-1px 0 0 #fff)
}
.source .sidebar {
- background-color: #353535;
+ background-color: #565656;
}
.sidebar .location {
scrollbar-color: rgba(36, 37, 39, 0.6) #d9d9d9;
}
-.logo-container.rust-logo > img {
+.rust-logo > img {
/* No need for a border in here! */
}
}
.source .sidebar {
- background-color: #fff;
+ background-color: #f1f1f1;
}
.sidebar .location {
var THEME_PICKER_ELEMENT_ID = "theme-picker";
var THEMES_ELEMENT_ID = "theme-choices";
+var MAIN_ID = "main-content";
function getThemesElement() {
return document.getElementById(THEMES_ELEMENT_ID);
}
var toggleAllDocsId = "toggle-all-docs";
- var main = document.getElementById("main");
+ var main = document.getElementById(MAIN_ID);
var savedHash = "";
function handleHashes(ev) {
} else {
addClass(innerToggle, "will-expand");
onEachLazy(document.getElementsByClassName("rustdoc-toggle"), function(e) {
- if (e.parentNode.id !== "main" ||
+ if (e.parentNode.id !== MAIN_ID ||
(!hasClass(e, "implementors-toggle") &&
!hasClass(e, "type-contents-toggle")))
{
container.appendChild(rustdoc_version);
popup.appendChild(container);
- insertAfter(popup, searchState.outputElement());
+ insertAfter(popup, document.querySelector("main"));
// So that it's only built once and then it'll do nothing when called!
buildHelperPopup = function() {};
};
}
function toggleSidebar() {
- var sidebar = document.getElementById("source-sidebar");
- var child = this.children[0].children[0];
+ var sidebar = document.querySelector("nav.sidebar");
+ var child = this.children[0];
if (child.innerText === ">") {
- sidebar.style.left = "";
- this.style.left = "";
+ sidebar.classList.add("expanded");
child.innerText = "<";
updateLocalStorage("rustdoc-source-sidebar-show", "true");
} else {
- sidebar.style.left = "-300px";
- this.style.left = "0";
+ sidebar.classList.remove("expanded");
child.innerText = ">";
updateLocalStorage("rustdoc-source-sidebar-show", "false");
}
sidebarToggle.id = "sidebar-toggle";
sidebarToggle.onclick = toggleSidebar;
- var inner1 = document.createElement("div");
- inner1.style.position = "relative";
+ var inner = document.createElement("div");
- var inner2 = document.createElement("div");
- inner2.style.paddingTop = "3px";
if (getCurrentValue("rustdoc-source-sidebar-show") === "true") {
- inner2.innerText = "<";
+ inner.innerText = "<";
} else {
- inner2.innerText = ">";
- sidebarToggle.style.left = "0";
+ inner.innerText = ">";
}
- inner1.appendChild(inner2);
- sidebarToggle.appendChild(inner1);
+ sidebarToggle.appendChild(inner);
return sidebarToggle;
}
if (!window.rootPath.endsWith("/")) {
window.rootPath += "/";
}
- var main = document.getElementById("main");
+ var container = document.querySelector("nav.sidebar");
var sidebarToggle = createSidebarToggle();
- main.insertBefore(sidebarToggle, main.firstChild);
+ container.insertBefore(sidebarToggle, container.firstChild);
var sidebar = document.createElement("div");
sidebar.id = "source-sidebar";
if (getCurrentValue("rustdoc-source-sidebar-show") !== "true") {
- sidebar.style.left = "-300px";
+ container.classList.remove("expanded");
+ } else {
+ container.classList.add("expanded");
}
var currentFile = getCurrentFilePath();
currentFile, hasFoundFile);
});
- main.insertBefore(sidebar, main.firstChild);
+ container.insertBefore(sidebar, document.querySelector(".sidebar-logo").nextSibling);
// Focus on the current file in the source files sidebar.
var selected_elem = sidebar.getElementsByClassName("selected")[0];
if (typeof selected_elem !== "undefined") {
{{- layout.external_html.before_content | safe -}}
<nav class="sidebar"> {#- -#}
<div class="sidebar-menu" role="button">☰</div> {#- -#}
- <a href='{{page.root_path | safe}}{{krate_with_trailing_slash | safe}}index.html'> {#- -#}
+ <a class="sidebar-logo" href='{{page.root_path | safe}}{{krate_with_trailing_slash | safe}}index.html'> {#- -#}
<div class='logo-container rust-logo'> {#- -#}
- <img src='
- {%- if layout.logo -%}
- {{layout.logo}}
- {%- else -%}
- {{static_root_path | safe}}rust-logo{{page.resource_suffix}}.png
- {%- endif -%}
- ' alt='logo'> {#- -#}
+ <img src='
+ {%- if layout.logo -%}
+ {{layout.logo}}
+ {%- else -%}
+ {{static_root_path | safe}}rust-logo{{page.resource_suffix}}.png
+ {%- endif -%}
+ ' alt='logo'> {#- -#}
</div> {#- -#}
</a> {#- -#}
{{- sidebar | safe -}}
</nav> {#- -#}
- <div class="theme-picker"> {#- -#}
- <button id="theme-picker" aria-label="Pick another theme!" aria-haspopup="menu" title="themes"> {#- -#}
- <img width="18" height="18" alt="Pick another theme!" {# -#}
- src="{{static_root_path | safe}}brush{{page.resource_suffix}}.svg"> {#- -#}
- </button> {#- -#}
- <div id="theme-choices" role="menu"></div> {#- -#}
- </div> {#- -#}
- <nav class="sub"> {#- -#}
- <form class="search-form"> {#- -#}
- <div class="search-container"> {#- -#}
- <div>{%- if layout.generate_search_filter -%}
- <select id="crate-search"> {#- -#}
- <option value="All crates">All crates</option> {#- -#}
- </select> {#- -#}
- {%- endif -%}
- <input {# -#}
- class="search-input" {# -#}
- name="search" {# -#}
- autocomplete="off" {# -#}
- spellcheck="false" {# -#}
- placeholder="Click or press ‘S’ to search, ‘?’ for more options…" {# -#}
- type="search"> {#- -#}
- </div> {#- -#}
- <button type="button" id="help-button" title="help">?</button> {#- -#}
- <a id="settings-menu" href="{{page.root_path | safe}}settings.html" title="settings"> {#- -#}
- <img width="18" height="18" alt="Change settings" {# -#}
- src="{{static_root_path | safe}}wheel{{page.resource_suffix}}.svg"> {#- -#}
+ <main> {#- -#}
+ <div class="width-limiter"> {#- -#}
+ <div class="sub-container"> {#- -#}
+ <a class="sub-logo-container rust-logo" href='{{page.root_path | safe}}{{krate_with_trailing_slash | safe}}index.html'> {#- -#}
+ <img src='
+ {%- if layout.logo -%}
+ {{layout.logo}}
+ {%- else -%}
+ {{static_root_path | safe}}rust-logo{{page.resource_suffix}}.png
+ {%- endif -%}
+ ' alt='logo'> {#- -#}
</a> {#- -#}
+ <nav class="sub"> {#- -#}
+ <div class="theme-picker"> {#- -#}
+ <button id="theme-picker" aria-label="Pick another theme!" aria-haspopup="menu" title="themes"> {#- -#}
+ <img width="18" height="18" alt="Pick another theme!" {# -#}
+ src="{{static_root_path | safe}}brush{{page.resource_suffix}}.svg"> {#- -#}
+ </button> {#- -#}
+ <div id="theme-choices" role="menu"></div> {#- -#}
+ </div> {#- -#}
+ <form class="search-form"> {#- -#}
+ <div class="search-container"> {#- -#}
+ <div>{%- if layout.generate_search_filter -%}
+ <select id="crate-search"> {#- -#}
+ <option value="All crates">All crates</option> {#- -#}
+ </select> {#- -#}
+ {%- endif -%}
+ <input {# -#}
+ class="search-input" {# -#}
+ name="search" {# -#}
+ autocomplete="off" {# -#}
+ spellcheck="false" {# -#}
+ placeholder="Click or press ‘S’ to search, ‘?’ for more options…" {# -#}
+ type="search"> {#- -#}
+ </div> {#- -#}
+ <button type="button" id="help-button" title="help">?</button> {#- -#}
+ <a id="settings-menu" href="{{page.root_path | safe}}settings.html" title="settings"> {#- -#}
+ <img width="18" height="18" alt="Change settings" {# -#}
+ src="{{static_root_path | safe}}wheel{{page.resource_suffix}}.svg"> {#- -#}
+ </a> {#- -#}
+ </div> {#- -#}
+ </form> {#- -#}
+ </nav> {#- -#}
</div> {#- -#}
- </form> {#- -#}
- </nav> {#- -#}
- <section id="main" class="content">{{- content | safe -}}</section> {#- -#}
- <section id="search" class="content hidden"></section> {#- -#}
+ <section id="main-content" class="content">{{- content | safe -}}</section> {#- -#}
+ <section id="search" class="content hidden"></section> {#- -#}
+ </div> {#- -#}
+ </main> {#- -#}
{{- layout.external_html.after_content | safe -}}
<div id="rustdoc-vars" {# -#}
data-root-path="{{page.root_path | safe}}" {# -#}
--- /dev/null
+// min-llvm-version: 13.0
+// assembly-output: emit-asm
+// compile-flags: --target avr-unknown-gnu-atmega328
+// needs-llvm-components: avr
+
+#![feature(no_core, lang_items, rustc_attrs, asm_experimental_arch)]
+#![crate_type = "rlib"]
+#![no_core]
+#![allow(non_camel_case_types)]
+
+#[rustc_builtin_macro]
+macro_rules! asm {
+ () => {};
+}
+#[rustc_builtin_macro]
+macro_rules! concat {
+ () => {};
+}
+
+#[lang = "sized"]
+trait Sized {}
+#[lang = "copy"]
+trait Copy {}
+
+type ptr = *const u64;
+
+impl Copy for i8 {}
+impl Copy for i16 {}
+impl Copy for i32 {}
+impl Copy for i64 {}
+impl Copy for ptr {}
+
+macro_rules! check {
+ ($func:ident $hi:literal $lo:literal $reg:tt) => {
+ #[no_mangle]
+ unsafe fn $func() -> i16 {
+ let y;
+ asm!(concat!("mov {0:", $hi, "}, {0:", $lo, "}"), out($reg) y);
+ y
+ }
+ };
+}
+
+// CHECK-LABEL: reg_pair_modifiers:
+// CHECK: ;APP
+// CHECK: mov r{{[1-9]?[13579]}}, r{{[1-9]?[24680]}}
+// CHECK: ;NO_APP
+check!(reg_pair_modifiers "h" "l" reg_pair);
+
+// CHECK-LABEL: reg_iw_modifiers:
+// CHECK: ;APP
+// CHECK: mov r{{[1-9]?[13579]}}, r{{[1-9]?[24680]}}
+// CHECK: ;NO_APP
+check!(reg_iw_modifiers "h" "l" reg_iw);
+
+// CHECK-LABEL: reg_ptr_modifiers:
+// CHECK: ;APP
+// CHECK: mov r{{[1-9]?[13579]}}, r{{[1-9]?[24680]}}
+// CHECK: ;NO_APP
+check!(reg_ptr_modifiers "h" "l" reg_ptr);
--- /dev/null
+// min-llvm-version: 13.0
+// assembly-output: emit-asm
+// compile-flags: --target avr-unknown-gnu-atmega328
+// needs-llvm-components: avr
+
+#![feature(no_core, lang_items, rustc_attrs, asm_sym, asm_experimental_arch)]
+#![crate_type = "rlib"]
+#![no_core]
+#![allow(non_camel_case_types)]
+
+#[rustc_builtin_macro]
+macro_rules! asm {
+ () => {};
+}
+#[rustc_builtin_macro]
+macro_rules! concat {
+ () => {};
+}
+
+#[lang = "sized"]
+trait Sized {}
+#[lang = "copy"]
+trait Copy {}
+
+type ptr = *const u64;
+
+impl Copy for i8 {}
+impl Copy for i16 {}
+impl Copy for i32 {}
+impl Copy for i64 {}
+impl Copy for ptr {}
+
+macro_rules! check {
+ ($func:ident $ty:ident $class:ident) => {
+ #[no_mangle]
+ pub unsafe fn $func(x: $ty) -> $ty {
+ let y;
+ asm!("mov {}, {}", lateout($class) y, in($class) x);
+ y
+ }
+ };
+}
+
+macro_rules! checkw {
+ ($func:ident $ty:ident $class:ident) => {
+ #[no_mangle]
+ pub unsafe fn $func(x: $ty) -> $ty {
+ let y;
+ asm!("movw {}, {}", lateout($class) y, in($class) x);
+ y
+ }
+ };
+}
+
+macro_rules! check_reg {
+ ($func:ident $ty:ident $reg:tt) => {
+ #[no_mangle]
+ pub unsafe fn $func(x: $ty) -> $ty {
+ let y;
+ asm!(concat!("mov ", $reg, ", ", $reg), lateout($reg) y, in($reg) x);
+ y
+ }
+ };
+}
+
+macro_rules! check_regw {
+ ($func:ident $ty:ident $reg:tt $reg_lit:tt) => {
+ #[no_mangle]
+ pub unsafe fn $func(x: $ty) -> $ty {
+ let y;
+ asm!(concat!("movw ", $reg_lit, ", ", $reg_lit), lateout($reg) y, in($reg) x);
+ y
+ }
+ };
+}
+
+extern "C" {
+ fn extern_func();
+ static extern_static: i8;
+}
+
+// CHECK-LABEL: sym_fn
+// CHECK: ;APP
+// CHECK: call extern_func
+// CHECK: ;NO_APP
+#[no_mangle]
+pub unsafe fn sym_fn() {
+ asm!("call {}", sym extern_func);
+}
+
+// CHECK-LABEL: sym_static
+// CHECK: ;APP
+// CHECK: lds r{{[0-9]+}}, extern_static
+// CHECK: ;NO_APP
+#[no_mangle]
+pub unsafe fn sym_static() -> i8 {
+ let y;
+ asm!("lds {}, {}", lateout(reg) y, sym extern_static);
+ y
+}
+
+// CHECK-LABEL: ld_z:
+// CHECK: ;APP
+// CHECK: ld r{{[0-9]+}}, Z
+// CHECK: ;NO_APP
+#[no_mangle]
+pub unsafe fn ld_z(x: i16) -> i8 {
+ let y;
+ asm!("ld {}, Z", out(reg) y, in("Z") x);
+ y
+}
+
+// CHECK-LABEL: ldd_z:
+// CHECK: ;APP
+// CHECK: ldd r{{[0-9]+}}, Z+4
+// CHECK: ;NO_APP
+#[no_mangle]
+pub unsafe fn ldd_z(x: i16) -> i8 {
+ let y;
+ asm!("ldd {}, Z+4", out(reg) y, in("Z") x);
+ y
+}
+
+// CHECK-LABEL: ld_predecrement:
+// CHECK: ;APP
+// CHECK: ld r{{[0-9]+}}, -Z
+// CHECK: ;NO_APP
+#[no_mangle]
+pub unsafe fn ld_predecrement(x: i16) -> i8 {
+ let y;
+ asm!("ld {}, -Z", out(reg) y, in("Z") x);
+ y
+}
+
+// CHECK-LABEL: ld_postincrement:
+// CHECK: ;APP
+// CHECK: ld r{{[0-9]+}}, Z+
+// CHECK: ;NO_APP
+#[no_mangle]
+pub unsafe fn ld_postincrement(x: i16) -> i8 {
+ let y;
+ asm!("ld {}, Z+", out(reg) y, in("Z") x);
+ y
+}
+
+// CHECK-LABEL: muls_clobber:
+// CHECK: ;APP
+// CHECK: muls r{{[0-9]+}}, r{{[0-9]+}}
+// CHECK: movw r{{[0-9]+}}, r0
+// CHECK: ;NO_APP
+#[no_mangle]
+pub unsafe fn muls_clobber(x: i8, y: i8) -> i16 {
+ let z;
+ asm!(
+ "muls {}, {}",
+ "movw {}, r1:r0",
+ out(reg_iw) z,
+ in(reg) x,
+ in(reg) y,
+ );
+ z
+}
+
+// CHECK-LABEL: reg_i8:
+// CHECK: ;APP
+// CHECK: mov r{{[0-9]+}}, r{{[0-9]+}}
+// CHECK: ;NO_APP
+check!(reg_i8 i8 reg);
+
+// CHECK-LABEL: reg_upper_i8:
+// CHECK: ;APP
+// CHECK: mov r{{[1-3][0-9]}}, r{{[1-3][0-9]}}
+// CHECK: ;NO_APP
+check!(reg_upper_i8 i8 reg_upper);
+
+// CHECK-LABEL: reg_pair_i16:
+// CHECK: ;APP
+// CHECK: movw r{{[0-9]+}}, r{{[0-9]+}}
+// CHECK: ;NO_APP
+checkw!(reg_pair_i16 i16 reg_pair);
+
+// CHECK-LABEL: reg_iw_i16:
+// CHECK: ;APP
+// CHECK: movw r{{[0-9]+}}, r{{[0-9]+}}
+// CHECK: ;NO_APP
+checkw!(reg_iw_i16 i16 reg_iw);
+
+// CHECK-LABEL: reg_ptr_i16:
+// CHECK: ;APP
+// CHECK: movw r{{[0-9]+}}, r{{[0-9]+}}
+// CHECK: ;NO_APP
+checkw!(reg_ptr_i16 i16 reg_ptr);
+
+// CHECK-LABEL: r2_i8:
+// CHECK: ;APP
+// CHECK: mov r2, r2
+// CHECK: ;NO_APP
+check_reg!(r2_i8 i8 "r2");
+
+// CHECK-LABEL: xl_i8:
+// CHECK: ;APP
+// CHECK: mov r26, r26
+// CHECK: ;NO_APP
+check_reg!(xl_i8 i8 "XL");
+
+// CHECK-LABEL: xh_i8:
+// CHECK: ;APP
+// CHECK: mov r27, r27
+// CHECK: ;NO_APP
+check_reg!(xh_i8 i8 "XH");
+
+// CHECK-LABEL: x_i16:
+// CHECK: ;APP
+// CHECK: movw r26, r26
+// CHECK: ;NO_APP
+check_regw!(x_i16 i16 "X" "X");
+
+// CHECK-LABEL: r25r24_i16:
+// CHECK: ;APP
+// CHECK: movw r24, r24
+// CHECK: ;NO_APP
+check_regw!(r25r24_i16 i16 "r25r24" "r24");
--- /dev/null
+// revisions:cfail1 cfail2
+//[cfail1] compile-flags: --crate-type=lib --edition=2021 -Zassert-incr-state=not-loaded
+//[cfail2] compile-flags: --crate-type=lib --edition=2021 -Zassert-incr-state=loaded
+// build-pass
+
+use core::any::Any;
+use core::marker::PhantomData;
+
+struct DerefWrap<T>(T);
+
+impl<T> core::ops::Deref for DerefWrap<T> {
+ type Target = T;
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+struct Storage<T, D> {
+ phantom: PhantomData<(T, D)>,
+}
+
+type ReadStorage<T> = Storage<T, DerefWrap<MaskedStorage<T>>>;
+
+pub trait Component {
+ type Storage;
+}
+
+struct VecStorage;
+
+struct Pos;
+
+impl Component for Pos {
+ type Storage = VecStorage;
+}
+
+struct GenericComp<T> {
+ _t: T,
+}
+
+impl<T: 'static> Component for GenericComp<T> {
+ type Storage = VecStorage;
+}
+struct ReadData {
+ pos_interpdata: ReadStorage<GenericComp<Pos>>,
+}
+
+trait System {
+ type SystemData;
+
+ fn run(data: Self::SystemData, any: Box<dyn Any>);
+}
+
+struct Sys;
+
+impl System for Sys {
+ type SystemData = (ReadData, ReadStorage<Pos>);
+
+ fn run((data, pos): Self::SystemData, any: Box<dyn Any>) {
+ <ReadStorage<GenericComp<Pos>> as SystemData>::setup(any);
+
+ ParJoin::par_join((&pos, &data.pos_interpdata));
+ }
+}
+
+trait ParJoin {
+ fn par_join(self)
+ where
+ Self: Sized,
+ {
+ }
+}
+
+impl<'a, T, D> ParJoin for &'a Storage<T, D>
+where
+ T: Component,
+ D: core::ops::Deref<Target = MaskedStorage<T>>,
+ T::Storage: Sync,
+{
+}
+
+impl<A, B> ParJoin for (A, B)
+where
+ A: ParJoin,
+ B: ParJoin,
+{
+}
+
+pub trait SystemData {
+ fn setup(any: Box<dyn Any>);
+}
+
+impl<T: 'static> SystemData for ReadStorage<T>
+where
+ T: Component,
+{
+ fn setup(any: Box<dyn Any>) {
+ let storage: &MaskedStorage<T> = any.downcast_ref().unwrap();
+
+ <dyn Any as CastFrom<MaskedStorage<T>>>::cast(&storage);
+ }
+}
+
+pub struct MaskedStorage<T: Component> {
+ _inner: T::Storage,
+}
+
+pub unsafe trait CastFrom<T> {
+ fn cast(t: &T) -> &Self;
+}
+
+unsafe impl<T> CastFrom<T> for dyn Any
+where
+ T: Any + 'static,
+{
+ fn cast(t: &T) -> &Self {
+ t
+ }
+}
// pp-exact
-fn main() { }
+fn main() {}
#[cfg(FALSE)]
fn syntax() {
let _ = #[attr] foo!(#! [attr]);
let _ = #[attr] foo![];
let _ = #[attr] foo![#! [attr]];
- let _ = #[attr] foo! { };
+ let _ = #[attr] foo! {};
let _ = #[attr] foo! { #! [attr] };
let _ = #[attr] Foo{bar: baz,};
let _ = #[attr] Foo{..foo};
foo!();
#[attr]
- foo! { }
+ foo! {}
#[attr]
foo![];
{
#[attr]
- foo! { }
+ foo! {}
}
}
--- /dev/null
+// pp-exact
+// pretty-compare-only
+// edition:2021
+
+async fn f() {
+ let first = async { 1 };
+ let second = async move { 2 };
+ join(first, second).await
+}
Qwerty,
}
-fn main() { }
+fn main() {}
// pp-exact
-auto trait MyTrait { }
+auto trait MyTrait {}
-unsafe auto trait UnsafeMyTrait { }
+unsafe auto trait UnsafeMyTrait {}
-pub fn main() { }
+pub fn main() {}
// compile-flags: --crate-type=lib
// pp-exact
-fn f() {
-} /*
- The next line should not be indented.
+fn f() {} /*
+ The next line should not be indented.
- That one. It shouldn't have been indented.
- */
+ That one. It shouldn't have been indented.
+ */
// pp-exact
-fn call_it(f: Box<FnMut(String) -> String>) { }
+fn call_it(f: Box<FnMut(String) -> String>) {}
-fn call_this<F>(f: F) where F: Fn(&str) + Send { }
+fn call_this<F>(f: F) where F: Fn(&str) + Send {}
-fn call_that<F>(f: F) where F: for<'a> Fn(&'a isize, &'a isize) -> isize { }
+fn call_that<F>(f: F) where F: for<'a> Fn(&'a isize, &'a isize) -> isize {}
-fn call_extern(f: fn() -> isize) { }
+fn call_extern(f: fn() -> isize) {}
-fn call_abid_extern(f: extern "C" fn() -> isize) { }
+fn call_abid_extern(f: extern "C" fn() -> isize) {}
-pub fn main() { }
+pub fn main() {}
fn id<F>(f: F) -> isize where F: Fn() -> isize { f() }
fn wsucc(_n: isize) -> isize { id(|| { 1 }) - 0 }
-fn main() { }
+fn main() {}
// Check that the visibility is printed on an enum variant.
-fn main() { }
+fn main() {}
#[cfg(FALSE)]
enum Foo { pub V, }
// pp-exact
-fn main() { }
+fn main() {}
// pp-exact:example2.pp
-fn main() { }
+fn main() {}
// pp-exact:example2.pp
-fn main() { }
+fn main() {}
// pp-exact:expanded-and-path-remap-80832.pp
// compile-flags: --remap-path-prefix {{src-base}}=the/src
-fn main() { }
+fn main() {}
// pp-exact
-// Check that `fn f() -> () { }` does not print as `fn f() { }`.
+// Check that `fn f() -> () {}` does not print as `fn f() {}`.
-fn f() -> () { }
+fn f() -> () {}
-fn main() { }
+fn main() {}
// pp-exact
-fn from_foreign_fn(_x: fn()) { }
-fn from_stack_closure<F>(_x: F) where F: Fn() { }
-fn main() { }
+fn from_foreign_fn(_x: fn()) {}
+fn from_stack_closure<F>(_x: F) where F: Fn() {}
+fn main() {}
ap.arg::<usize>()
}
-fn main() { }
+fn main() {}
fn simple_attr() {
#[attr]
- if true { }
+ if true {}
#[allow_warnings]
- if true { }
+ if true {}
}
#[cfg(FALSE)]
fn if_else_chain() {
#[first_attr]
- if true { } else if false { } else { }
+ if true {} else if false {} else {}
}
#[cfg(FALSE)]
fn if_let() {
#[attr]
- if let Some(_) = Some(true) { }
+ if let Some(_) = Some(true) {}
}
#[cfg(FALSE)]
fn let_attr_if() {
- let _ = #[attr] if let _ = 0 { };
- let _ = #[attr] if true { };
+ let _ = #[attr] if let _ = 0 {};
+ let _ = #[attr] if true {};
- let _ = #[attr] if let _ = 0 { } else { };
- let _ = #[attr] if true { } else { };
+ let _ = #[attr] if let _ = 0 {} else {};
+ let _ = #[attr] if true {} else {};
}
-fn main() { }
+fn main() {}
#[path = "issue-12590-b.rs"]
mod issue_12590_b;
-fn main() { }
+fn main() {}
#[path = "issue-12590-b.rs"]
mod issue_12590_b {
- fn b() { }
- fn main() { }
+ fn b() {}
+ fn main() {}
}
-fn main() { }
+fn main() {}
#[path = "issue-12590-b.rs"]
mod issue_12590_b;
-fn main() { }
+fn main() {}
fn main() {
match true {
true if true => (),
- false if false => unsafe { },
- true => { }
+ false if false => unsafe {},
+ true => {}
false => (),
}
}
// pretty-compare-only
// pp-exact
-fn main() { b! { } c }
+fn main() { b! {} c }
// pp-exact
-fn main() { }
+fn main() {}
struct C {
field: u8,
// pp-exact
-fn f1<'a, 'b, 'c>(_x: &'a u32, _y: &'b u32, _z: &'c u32) where 'c: 'a + 'b { }
+fn f1<'a, 'b, 'c>(_x: &'a u32, _y: &'b u32, _z: &'c u32) where 'c: 'a + 'b {}
-fn main() { }
+fn main() {}
pub(crate) macro mac { ($arg : expr) => { $arg + $arg } }
-fn main() { }
+fn main() {}
// pp-exact
-macro_rules! brace { () => { } ; }
+macro_rules! brace { () => {} ; }
-macro_rules! bracket[() => { } ;];
+macro_rules! bracket[() => {} ;];
-macro_rules! paren(() => { } ;);
+macro_rules! paren(() => {} ;);
macro_rules! matcher_brackets {
- (paren) => { } ; (bracket) => { } ; (brace) => { } ;
+ (paren) => {} ; (bracket) => {} ; (brace) => {} ;
}
macro_rules! all_fragments {
($b : block, $e : expr, $i : ident, $it : item, $l : lifetime, $lit :
literal, $m : meta, $p : pat, $pth : path, $s : stmt, $tt : tt, $ty : ty,
- $vis : vis) => { } ;
+ $vis : vis) => {} ;
}
-fn main() { }
+fn main() {}
// pp-exact
-fn main() { }
+fn main() {}
#[cfg(FALSE)]
extern "C" {
trait Tr {
- fn dummy(&self) { }
+ fn dummy(&self) {}
}
-impl Tr for isize { }
+impl Tr for isize {}
fn foo<'a>(x: Box<Tr + Sync + 'a>) -> Box<Tr + Sync + 'a> { x }
}
trait Tu {
- fn dummy() { }
+ fn dummy() {}
}
fn foo<T: m::Tr>() { <T as m::Tr>::Ts::dummy(); }
-fn main() { }
+fn main() {}
#![feature(rustc_attrs)]
#![feature(stmt_expr_attributes)]
-fn main() { }
+fn main() {}
fn _0() {
fn _3() {
#[rustc_dummy]
- match () { _ => { } }
+ match () { _ => {} }
}
fn _4() {
stmt_mac!();
#[rustc_dummy]
- stmt_mac! { };
+ stmt_mac! {};
#[rustc_dummy]
stmt_mac![];
#[rustc_dummy]
- stmt_mac! { }
+ stmt_mac! {}
let _ = ();
}
fn _10() {
let _ = #[rustc_dummy] expr_mac!();
let _ = #[rustc_dummy] expr_mac![];
- let _ = #[rustc_dummy] expr_mac! { };
+ let _ = #[rustc_dummy] expr_mac! {};
}
fn _11() {
|| #[rustc_dummy] return;
let _ = #[rustc_dummy] expr_mac!();
let _ = #[rustc_dummy] expr_mac![];
- let _ = #[rustc_dummy] expr_mac! { };
+ let _ = #[rustc_dummy] expr_mac! {};
let _ = #[rustc_dummy] Foo{data: (),};
let _ = #[rustc_dummy] Foo{..s};
let _ = #[rustc_dummy] Foo{data: (), ..s};
}
}
-fn foo() { }
-fn foo3(_: i32, _: (), _: ()) { }
-fn qux(_: i32) { }
+fn foo() {}
+fn foo3(_: i32, _: (), _: ()) {}
+fn qux(_: i32) {}
baz,
}
-fn main() { }
+fn main() {}
#![allow(bar)]
}
-fn main() { }
+fn main() {}
struct Test;
-impl !Send for Test { }
+impl !Send for Test {}
-pub fn main() { }
+pub fn main() {}
}
unsafe impl UnsafeTrait for isize {
- fn foo(&self) { }
+ fn foo(&self) {}
}
pub unsafe trait PubUnsafeTrait {
fn foo(&self);
}
-fn main() { }
+fn main() {}
fn f<'a, 'b, T>(t: T) -> isize where T: 'a, 'a: 'b, T: Eq { 0 }
-fn main() { }
+fn main() {}
// This test ensures that the docblock elements have the appropriate left margin.
goto: file://|DOC_PATH|/test_docs/fn.foo.html
// The top docblock elements shouldn't have left margin...
-assert-css: ("#main .docblock.item-decl", {"margin-left": "0px"})
+assert-css: ("#main-content .docblock.item-decl", {"margin-left": "0px"})
// ... but all the others should!
-assert-css: ("#main .docblock:not(.item-decl)", {"margin-left": "24px"})
+assert-css: ("#main-content .docblock:not(.item-decl)", {"margin-left": "24px"})
write: (".search-input", "test")
wait-for: "#search > h1" // The search element is empty before the first search
assert-attribute: ("#search", {"class": "content"})
-assert-attribute: ("#main", {"class": "content hidden"})
+assert-attribute: ("#main-content", {"class": "content hidden"})
press-key: "Escape"
assert-attribute: ("#search", {"class": "content hidden"})
-assert-attribute: ("#main", {"class": "content"})
+assert-attribute: ("#main-content", {"class": "content"})
// Check that focusing the search input brings back the search results
focus: ".search-input"
assert-attribute: ("#search", {"class": "content"})
-assert-attribute: ("#main", {"class": "content hidden"})
+assert-attribute: ("#main-content", {"class": "content hidden"})
// Now let's check that when the help popup is displayed and we press Escape, it doesn't
// hide the search results too.
press-key: "Escape"
assert-attribute: ("#help", {"class": "hidden"})
assert-attribute: ("#search", {"class": "content"})
-assert-attribute: ("#main", {"class": "content hidden"})
+assert-attribute: ("#main-content", {"class": "content hidden"})
// Check that Escape hides the search results when a search result is focused.
focus: ".search-input"
press-key: "Escape"
assert-attribute: ("#help", {"class": "hidden"})
assert-attribute: ("#search", {"class": "content hidden"})
-assert-attribute: ("#main", {"class": "content"})
+assert-attribute: ("#main-content", {"class": "content"})
// This test ensures that the impl blocks are open by default.
goto: file://|DOC_PATH|/test_docs/struct.Foo.html
-assert-attribute: ("#main > details.implementors-toggle", {"open": ""})
+assert-attribute: ("#main-content > details.implementors-toggle", {"open": ""})
wait-for: "#titles"
assert-text-false: (".fqn .in-band", "Struct test_docs::Foo")
// Ensure that the search results are displayed, not the "normal" content.
-assert-css: ("#main", {"display": "none"})
+assert-css: ("#main-content", {"display": "none"})
// Now we can check that the feature is working as expected!
goto: file://|DOC_PATH|/test_docs/index.html?search=struct%3AFoo&go_to_first=true
goto: file://|DOC_PATH|/src/test_docs/lib.rs.html
// Check that we can click on the line number.
-click: (40, 224) // This is the position of the span for line 4.
+click: ".line-numbers > span:nth-child(4)" // This is the span for line 4.
// Unfortunately, "#4" isn't a valid query selector, so we have to go around that limitation
// by instead getting the nth span.
assert-attribute: (".line-numbers > span:nth-child(4)", {"class": "line-highlighted"})
goto: file://|DOC_PATH|/test_docs/index.html
-assert-attribute: ("#main > details.top-doc", {"open": ""})
+assert-attribute: ("#main-content > details.top-doc", {"open": ""})
click: "#toggle-all-docs"
wait-for: 1000
// This is now collapsed so there shouldn't be the "open" attribute on details.
-assert-attribute-false: ("#main > details.top-doc", {"open": ""})
+assert-attribute-false: ("#main-content > details.top-doc", {"open": ""})
click: "#toggle-all-docs"
wait-for: 1000
// Not collapsed anymore so the "open" attribute should be back.
-assert-attribute: ("#main > details.top-doc", {"open": ""})
+assert-attribute: ("#main-content > details.top-doc", {"open": ""})
goto: file://|DOC_PATH|/lib2/too_long/type.ReallyLongTypeNameLongLongLong.html
assert-property: ("body", {"scrollWidth": "1100"})
// We now check that the section width hasn't grown because of it.
-assert-property: ("#main", {"scrollWidth": "840"})
+assert-property: ("#main-content", {"scrollWidth": "840"})
// And now checking that it has scrollable content.
assert-property: (".item-decl pre", {"scrollWidth": "1103"})
goto: file://|DOC_PATH|/lib2/too_long/constant.ReallyLongTypeNameLongLongLongConstBecauseWhyNotAConstRightGigaGigaSupraLong.html
assert-property: ("body", {"scrollWidth": "1100"})
// We now check that the section width hasn't grown because of it.
-assert-property: ("#main", {"scrollWidth": "840"})
+assert-property: ("#main-content", {"scrollWidth": "840"})
// And now checking that it has scrollable content.
assert-property: (".item-decl pre", {"scrollWidth": "950"})
#![feature(target_feature, cfg_target_feature)]
// @has doc_cfg/struct.Portable.html
-// @!has - '//*[@id="main"]/*[@class="item-info"]/*[@class="stab portability"]' ''
+// @!has - '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' ''
// @has - '//*[@id="method.unix_and_arm_only_function"]' 'fn unix_and_arm_only_function()'
// @has - '//*[@class="stab portability"]' 'This is supported on Unix and ARM only.'
// @has - '//*[@id="method.wasi_and_wasm32_only_function"]' 'fn wasi_and_wasm32_only_function()'
pub struct Portable;
// @has doc_cfg/unix_only/index.html \
-// '//*[@id="main"]/*[@class="item-info"]/*[@class="stab portability"]' \
+// '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \
// 'This is supported on Unix only.'
// @matches - '//*[@class="item-left module-item"]//*[@class="stab portability"]' '\AARM\Z'
// @count - '//*[@class="stab portability"]' 2
#[doc(cfg(unix))]
pub mod unix_only {
// @has doc_cfg/unix_only/fn.unix_only_function.html \
- // '//*[@id="main"]/*[@class="item-info"]/*[@class="stab portability"]' \
+ // '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \
// 'This is supported on Unix only.'
// @count - '//*[@class="stab portability"]' 1
pub fn unix_only_function() {
}
// @has doc_cfg/unix_only/trait.ArmOnly.html \
- // '//*[@id="main"]/*[@class="item-info"]/*[@class="stab portability"]' \
+ // '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \
// 'This is supported on Unix and ARM only.'
// @count - '//*[@class="stab portability"]' 1
#[doc(cfg(target_arch = "arm"))]
}
// @has doc_cfg/wasi_only/index.html \
-// '//*[@id="main"]/*[@class="item-info"]/*[@class="stab portability"]' \
+// '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \
// 'This is supported on WASI only.'
// @matches - '//*[@class="item-left module-item"]//*[@class="stab portability"]' '\AWebAssembly\Z'
// @count - '//*[@class="stab portability"]' 2
#[doc(cfg(target_os = "wasi"))]
pub mod wasi_only {
// @has doc_cfg/wasi_only/fn.wasi_only_function.html \
- // '//*[@id="main"]/*[@class="item-info"]/*[@class="stab portability"]' \
+ // '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \
// 'This is supported on WASI only.'
// @count - '//*[@class="stab portability"]' 1
pub fn wasi_only_function() {
}
// @has doc_cfg/wasi_only/trait.Wasm32Only.html \
- // '//*[@id="main"]/*[@class="item-info"]/*[@class="stab portability"]' \
+ // '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \
// 'This is supported on WASI and WebAssembly only.'
// @count - '//*[@class="stab portability"]' 1
#[doc(cfg(target_arch = "wasm32"))]
// @matches - '//*[@class="item-left module-item"]//*[@class="stab portability"]' '\Aavx\Z'
// @has doc_cfg/fn.uses_target_feature.html
-// @has - '//*[@id="main"]/*[@class="item-info"]/*[@class="stab portability"]' \
+// @has - '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \
// 'This is supported with target feature avx only.'
#[target_feature(enable = "avx")]
pub unsafe fn uses_target_feature() {
}
// @has doc_cfg/fn.uses_cfg_target_feature.html
-// @has - '//*[@id="main"]/*[@class="item-info"]/*[@class="stab portability"]' \
+// @has - '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \
// 'This is supported with target feature avx only.'
#[doc(cfg(target_feature = "avx"))]
pub fn uses_cfg_target_feature() {
// multiple attributes should be allowed
// @has doc_cfg/fn.multiple_attrs.html \
-// '//*[@id="main"]/*[@class="item-info"]/*[@class="stab portability"]' \
+// '//*[@id="main-content"]/*[@class="item-info"]/*[@class="stab portability"]' \
// 'This is supported on x and y and z only.'
#[doc(cfg(x))]
#[doc(cfg(y), cfg(z))]
// @has - '//h3[@class="code-header in-band"]' 'impl Foo'
// @has - '//h3[@class="code-header in-band"]' 'impl Bar for Foo'
// @count - '//*[@id="trait-implementations-list"]//*[@class="impl has-srclink"]' 1
-// @count - '//*[@id="main"]/details/summary/*[@class="impl has-srclink"]' 1
+// @count - '//*[@id="main-content"]/details/summary/*[@class="impl has-srclink"]' 1
// @has issue_33054/impls/bar/trait.Bar.html
// @has - '//h3[@class="code-header in-band"]' 'impl Bar for Foo'
// @count - '//*[@class="struct"]' 1
impl Foo for Bar {}
impl Foo2 for Bar {}
-// @has foo/fn.foo.html '//section[@id="main"]//pre' "x: &'x impl Foo"
-// @has foo/fn.foo.html '//section[@id="main"]//pre' "-> &'x impl Foo"
+// @has foo/fn.foo.html '//section[@id="main-content"]//pre' "x: &'x impl Foo"
+// @has foo/fn.foo.html '//section[@id="main-content"]//pre' "-> &'x impl Foo"
pub fn foo<'x>(x: &'x impl Foo) -> &'x impl Foo {
x
}
-// @has foo/fn.foo2.html '//section[@id="main"]//pre' "x: &'x impl Foo"
-// @has foo/fn.foo2.html '//section[@id="main"]//pre' '-> impl Foo2'
+// @has foo/fn.foo2.html '//section[@id="main-content"]//pre' "x: &'x impl Foo"
+// @has foo/fn.foo2.html '//section[@id="main-content"]//pre' '-> impl Foo2'
pub fn foo2<'x>(_x: &'x impl Foo) -> impl Foo2 {
Bar
}
-// @has foo/fn.foo_foo.html '//section[@id="main"]//pre' '-> impl Foo + Foo2'
+// @has foo/fn.foo_foo.html '//section[@id="main-content"]//pre' '-> impl Foo + Foo2'
pub fn foo_foo() -> impl Foo + Foo2 {
Bar
}
-// @has foo/fn.foo_foo_foo.html '//section[@id="main"]//pre' "x: &'x impl Foo + Foo2"
+// @has foo/fn.foo_foo_foo.html '//section[@id="main-content"]//pre' "x: &'x impl Foo + Foo2"
pub fn foo_foo_foo<'x>(_x: &'x (impl Foo + Foo2)) {
}
#![crate_type = "rlib"]
// @has prim_methods_external_core/index.html
-// @has - '//*[@id="main"]//a[@href="../my_core/primitive.char.html"]' 'char'
-// @has - '//*[@id="main"]//a[@href="../my_core/primitive.char.html#method.len_utf8"]' 'char::len_utf8'
+// @has - '//*[@id="main-content"]//a[@href="../my_core/primitive.char.html"]' 'char'
+// @has - '//*[@id="main-content"]//a[@href="../my_core/primitive.char.html#method.len_utf8"]' 'char::len_utf8'
//! A [`char`] and its [`char::len_utf8`].
#![no_core]
#![crate_type = "rlib"]
-
// @has prim_methods_local/index.html
-// @has - '//*[@id="main"]//a[@href="primitive.char.html"]' 'char'
-// @has - '//*[@id="main"]//a[@href="primitive.char.html#method.len_utf8"]' 'char::len_utf8'
+// @has - '//*[@id="main-content"]//a[@href="primitive.char.html"]' 'char'
+// @has - '//*[@id="main-content"]//a[@href="primitive.char.html#method.len_utf8"]' 'char::len_utf8'
//! A [prim@`char`] and its [`char::len_utf8`].
#![deny(rustdoc::broken_intra_doc_links)]
-
// @has prim_methods/index.html
-// @has - '//*[@id="main"]//a[@href="{{channel}}/std/primitive.char.html"]' 'char'
-// @has - '//*[@id="main"]//a[@href="{{channel}}/std/primitive.char.html#method.len_utf8"]' 'char::len_utf8'
+// @has - '//*[@id="main-content"]//a[@href="{{channel}}/std/primitive.char.html"]' 'char'
+// @has - '//*[@id="main-content"]//a[@href="{{channel}}/std/primitive.char.html#method.len_utf8"]' 'char::len_utf8'
//! A [`char`] and its [`char::len_utf8`].
#![deny(rustdoc::broken_intra_doc_links)]
#![crate_name = "foo"]
-
// @has foo/index.html
-// @has - '//*[@id="main"]//a[@href="{{channel}}/std/primitive.bool.html"]' 'true'
-// @has - '//*[@id="main"]//a[@href="{{channel}}/std/primitive.bool.html"]' 'false'
+// @has - '//*[@id="main-content"]//a[@href="{{channel}}/std/primitive.bool.html"]' 'true'
+// @has - '//*[@id="main-content"]//a[@href="{{channel}}/std/primitive.bool.html"]' 'false'
//! A `bool` is either [`true`] or [`false`].
// @has issue_55364/subone/index.html
// These foo/bar links in the module's documentation should refer inside `subone`
-// @has - '//section[@id="main"]/details[@open=""]/div[@class="docblock"]//a[@href="fn.foo.html"]' 'foo'
-// @has - '//section[@id="main"]/details[@open=""]/div[@class="docblock"]//a[@href="fn.bar.html"]' 'bar'
+// @has - '//section[@id="main-content"]/details[@open=""]/div[@class="docblock"]//a[@href="fn.foo.html"]' 'foo'
+// @has - '//section[@id="main-content"]/details[@open=""]/div[@class="docblock"]//a[@href="fn.bar.html"]' 'bar'
pub mod subone {
//! See either [foo] or [bar].
// This should refer to subone's `bar`
// @has issue_55364/subone/fn.foo.html
- // @has - '//section[@id="main"]/details/div[@class="docblock"]//a[@href="fn.bar.html"]' 'bar'
+ // @has - '//section[@id="main-content"]/details/div[@class="docblock"]//a[@href="fn.bar.html"]' 'bar'
/// See [bar]
pub fn foo() {}
// This should refer to subone's `foo`
// @has issue_55364/subone/fn.bar.html
- // @has - '//section[@id="main"]/details/div[@class="docblock"]//a[@href="fn.foo.html"]' 'foo'
+ // @has - '//section[@id="main-content"]/details/div[@class="docblock"]//a[@href="fn.foo.html"]' 'foo'
/// See [foo]
pub fn bar() {}
}
// @has issue_55364/subtwo/index.html
// These foo/bar links in the module's documentation should not reference inside `subtwo`
-// @!has - '//section[@id="main"]/div[@class="docblock"]//a[@href="fn.foo.html"]' 'foo'
-// @!has - '//section[@id="main"]/div[@class="docblock"]//a[@href="fn.bar.html"]' 'bar'
+// @!has - '//section[@id="main-content"]/div[@class="docblock"]//a[@href="fn.foo.html"]' 'foo'
+// @!has - '//section[@id="main-content"]/div[@class="docblock"]//a[@href="fn.bar.html"]' 'bar'
// Instead it should be referencing the top level functions
-// @has - '//section[@id="main"]/details/div[@class="docblock"]//a[@href="../fn.foo.html"]' 'foo'
-// @has - '//section[@id="main"]/details/div[@class="docblock"]//a[@href="../fn.bar.html"]' 'bar'
+// @has - '//section[@id="main-content"]/details/div[@class="docblock"]//a[@href="../fn.foo.html"]' 'foo'
+// @has - '//section[@id="main-content"]/details/div[@class="docblock"]//a[@href="../fn.bar.html"]' 'bar'
// Though there should be such links later
-// @has - '//section[@id="main"]/div[@class="item-table"]//div[@class="item-left module-item"]/a[@class="fn"][@href="fn.foo.html"]' 'foo'
-// @has - '//section[@id="main"]/div[@class="item-table"]//div[@class="item-left module-item"]/a[@class="fn"][@href="fn.bar.html"]' 'bar'
+// @has - '//section[@id="main-content"]/div[@class="item-table"]//div[@class="item-left module-item"]/a[@class="fn"][@href="fn.foo.html"]' 'foo'
+// @has - '//section[@id="main-content"]/div[@class="item-table"]//div[@class="item-left module-item"]/a[@class="fn"][@href="fn.bar.html"]' 'bar'
/// See either [foo] or [bar].
pub mod subtwo {
// Despite the module's docs referring to the top level foo/bar,
// this should refer to subtwo's `bar`
// @has issue_55364/subtwo/fn.foo.html
- // @has - '//section[@id="main"]/details/div[@class="docblock"]//a[@href="fn.bar.html"]' 'bar'
+ // @has - '//section[@id="main-content"]/details/div[@class="docblock"]//a[@href="fn.bar.html"]' 'bar'
/// See [bar]
pub fn foo() {}
// Despite the module's docs referring to the top level foo/bar,
// this should refer to subtwo's `foo`
// @has issue_55364/subtwo/fn.bar.html
- // @has - '//section[@id="main"]/details/div[@class="docblock"]//a[@href="fn.foo.html"]' 'foo'
+ // @has - '//section[@id="main-content"]/details/div[@class="docblock"]//a[@href="fn.foo.html"]' 'foo'
/// See [foo]
pub fn bar() {}
}
// @has issue_55364/subthree/index.html
// This module should also refer to the top level foo/bar
-// @has - '//section[@id="main"]/details/div[@class="docblock"]//a[@href="../fn.foo.html"]' 'foo'
-// @has - '//section[@id="main"]/details/div[@class="docblock"]//a[@href="../fn.bar.html"]' 'bar'
+// @has - '//section[@id="main-content"]/details/div[@class="docblock"]//a[@href="../fn.foo.html"]' 'foo'
+// @has - '//section[@id="main-content"]/details/div[@class="docblock"]//a[@href="../fn.bar.html"]' 'bar'
pub mod subthree {
//! See either [foo][super::foo] or [bar][super::bar]
}
// Next we go *deeper* - In order to ensure it's not just "this or parent"
// we test `crate::` and a `super::super::...` chain
// @has issue_55364/subfour/subfive/subsix/subseven/subeight/index.html
-// @has - '//section[@id="main"]/div[@class="item-table"]//div[@class="item-right docblock-short"]//a[@href="../../../../../subone/fn.foo.html"]' 'other foo'
-// @has - '//section[@id="main"]/div[@class="item-table"]//div[@class="item-right docblock-short"]//a[@href="../../../../../subtwo/fn.bar.html"]' 'other bar'
+// @has - '//section[@id="main-content"]/div[@class="item-table"]//div[@class="item-right docblock-short"]//a[@href="../../../../../subone/fn.foo.html"]' 'other foo'
+// @has - '//section[@id="main-content"]/div[@class="item-table"]//div[@class="item-right docblock-short"]//a[@href="../../../../../subtwo/fn.bar.html"]' 'other bar'
pub mod subfour {
pub mod subfive {
pub mod subsix {
// @has foo/index.html '//div[@class="sidebar-elems"]//li/a/@href' '#keywords'
// @has foo/keyword.match.html '//a[@class="keyword"]' 'match'
// @has foo/keyword.match.html '//span[@class="in-band"]' 'Keyword match'
-// @has foo/keyword.match.html '//section[@id="main"]//div[@class="docblock"]//p' 'this is a test!'
+// @has foo/keyword.match.html '//section[@id="main-content"]//div[@class="docblock"]//p' 'this is a test!'
// @has foo/index.html '//a/@href' '../foo/index.html'
// @!has foo/foo/index.html
// @!has-dir foo/foo
/// this is a test!
mod foo{}
-// @has foo/keyword.foo.html '//section[@id="main"]//div[@class="docblock"]//p' 'hello'
+// @has foo/keyword.foo.html '//section[@id="main-content"]//div[@class="docblock"]//p' 'hello'
#[doc(keyword = "foo")]
/// hello
mod bar {}
pub struct Struc;
// @has foo/struct.Struc.html
-// @has - '//*[@id="main"]/h2[@id="implementations"]' "Implementations"
+// @has - '//*[@id="main-content"]/h2[@id="implementations"]' "Implementations"
impl Struc {
pub const S: u64 = 0;
}
// @has foo/index.html '//a[@class="traitalias"]' 'Foo'
// @has foo/traitalias.CopyAlias.html
-// @has - '//section[@id="main"]/div[@class="docblock item-decl"]/pre' 'trait CopyAlias = Copy;'
+// @has - '//section[@id="main-content"]/div[@class="docblock item-decl"]/pre' 'trait CopyAlias = Copy;'
pub trait CopyAlias = Copy;
// @has foo/traitalias.Alias2.html
-// @has - '//section[@id="main"]/div[@class="docblock item-decl"]/pre' 'trait Alias2 = Copy + Debug;'
+// @has - '//section[@id="main-content"]/div[@class="docblock item-decl"]/pre' 'trait Alias2 = Copy + Debug;'
pub trait Alias2 = Copy + Debug;
// @has foo/traitalias.Foo.html
-// @has - '//section[@id="main"]/div[@class="docblock item-decl"]/pre' 'trait Foo<T> = Into<T> + Debug;'
+// @has - '//section[@id="main-content"]/div[@class="docblock item-decl"]/pre' 'trait Foo<T> = Into<T> + Debug;'
pub trait Foo<T> = Into<T> + Debug;
// @has foo/fn.bar.html '//a[@href="traitalias.Alias2.html"]' 'Alias2'
pub fn bar<T>() where T: Alias2 {}
// @has - '//h2[@id="fields"]' 'Tuple Fields'
// @has - '//h3[@class="sidebar-title"]/a[@href="#fields"]' 'Tuple Fields'
// @has - '//*[@id="structfield.0"]' '0: u32'
-// @has - '//*[@id="main"]/div[@class="docblock"]' 'hello'
+// @has - '//*[@id="main-content"]/div[@class="docblock"]' 'hello'
// @!has - '//*[@id="structfield.1"]'
// @has - '//*[@id="structfield.2"]' '2: char'
// @has - '//*[@id="structfield.3"]' '3: i8'
-// @has - '//*[@id="main"]/div[@class="docblock"]' 'not hello'
+// @has - '//*[@id="main-content"]/div[@class="docblock"]' 'not hello'
pub struct Foo(
/// hello
pub u32,
// edition:2018
// pp-exact
-fn main() { let _a = (async { }); }
+fn main() { let _a = (async { }); }
//~^ WARNING unnecessary parentheses around assigned value
warning: unnecessary parentheses around assigned value
--> $DIR/issue-54752-async-block.rs:6:22
|
-LL | fn main() { let _a = (async { }); }
- | ^ ^
+LL | fn main() { let _a = (async { }); }
+ | ^ ^
|
= note: `#[warn(unused_parens)]` on by default
help: remove these parentheses
|
-LL - fn main() { let _a = (async { }); }
-LL + fn main() { let _a = async { }; }
+LL - fn main() { let _a = (async { }); }
+LL + fn main() { let _a = async { }; }
|
warning: 1 warning emitted
-async fn f(mut x : u8) { }
-async fn g((mut x, y, mut z) : (u8, u8, u8)) { }
-async fn g(mut x : u8, (a, mut b, c) : (u8, u8, u8), y : u8) { }
+async fn f(mut x : u8) {}
+async fn g((mut x, y, mut z) : (u8, u8, u8)) {}
+async fn g(mut x : u8, (a, mut b, c) : (u8, u8, u8), y : u8) {}
| ^^^^^^^^^^^^^^^^^
error: unexpected token: `{
- impl std::ops::Neg for i8 { }
+ impl std::ops::Neg for i8 {}
}`
--> $DIR/issue-90873.rs:7:6
|
+++ /dev/null
-// no-prefer-dynamic
-// compile-flags: --cfg foo
-
-#![cfg_attr(foo, crate_type="lib")]
-
-pub fn foo() {}
+++ /dev/null
-// run-pass
-// aux-build:crate-attributes-using-cfg_attr.rs
-
-extern crate crate_attributes_using_cfg_attr;
-
-pub fn main() {}
--- /dev/null
+// check-fail
+// compile-flags:--cfg foo
+
+#![deny(warnings)]
+#![cfg_attr(foo, crate_type="bin")]
+//~^ERROR `crate_type` within
+//~| WARN this was previously accepted
+#![cfg_attr(foo, crate_name="bar")]
+//~^ERROR `crate_name` within
+//~| WARN this was previously accepted
+
+fn main() {}
--- /dev/null
+error: `crate_type` within an `#![cfg_attr] attribute is deprecated`
+ --> $DIR/future-compat-crate-attributes-using-cfg_attr.rs:5:18
+ |
+LL | #![cfg_attr(foo, crate_type="bin")]
+ | ^^^^^^^^^^^^^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/future-compat-crate-attributes-using-cfg_attr.rs:4:9
+ |
+LL | #![deny(warnings)]
+ | ^^^^^^^^
+ = note: `#[deny(deprecated_cfg_attr_crate_type_name)]` implied by `#[deny(warnings)]`
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #91632 <https://github.com/rust-lang/rust/issues/91632>
+
+error: `crate_name` within an `#![cfg_attr] attribute is deprecated`
+ --> $DIR/future-compat-crate-attributes-using-cfg_attr.rs:8:18
+ |
+LL | #![cfg_attr(foo, crate_name="bar")]
+ | ^^^^^^^^^^^^^^^^
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #91632 <https://github.com/rust-lang/rust/issues/91632>
+
+error: aborting due to 2 previous errors
+
LL | let foo = Foo::foo();
| --- ^^^^^^^^ cannot infer the value of const parameter `N`
| |
- | consider giving `foo` the explicit type `Foo<{_: u32}>`, where the type parameter `N` is specified
+ | consider giving `foo` the explicit type `Foo<N>`, where the const parameter `N` is specified
error: aborting due to previous error
#[macro_use]
extern crate std;
-trait Foo<const KIND : bool = true> { }
+trait Foo<const KIND : bool = true> {}
-fn foo<const SIZE : usize = 5>() { }
+fn foo<const SIZE : usize = 5>() {}
struct Range<const FROM : usize = 0, const LEN : usize = 0, const TO : usize =
FROM>;
LL | fn test<const N: usize>() -> [u8; N + (|| 42)()] {}
| ^^^^-------^^
| |
- | dereferencing is not supported in generic constants
+ | borrowing is not supported in generic constants
|
= help: consider moving this anonymous constant into a `const` function
= note: this operation may be supported in the future
--- /dev/null
+#![feature(generic_const_exprs)]
+#![feature(specialization)]
+#![allow(incomplete_features)]
+
+//--------------------------------------------------
+
+trait Depth {
+ const C: usize;
+}
+
+trait Type {
+ type AT: Depth;
+}
+
+//--------------------------------------------------
+
+enum Predicate<const B: bool> {}
+
+trait Satisfied {}
+
+impl Satisfied for Predicate<true> {}
+
+//--------------------------------------------------
+
+trait Spec1 {}
+
+impl<T: Type> Spec1 for T where Predicate<{T::AT::C > 0}>: Satisfied {}
+
+trait Spec2 {}
+
+//impl<T: Type > Spec2 for T where Predicate<{T::AT::C > 1}>: Satisfied {}
+impl<T: Type > Spec2 for T where Predicate<true>: Satisfied {}
+
+//--------------------------------------------------
+
+trait Foo {
+ fn Bar();
+}
+
+impl<T: Spec1> Foo for T {
+ default fn Bar() {}
+}
+
+impl<T: Spec2> Foo for T {
+//~^ ERROR conflicting implementations of trait
+ fn Bar() {}
+}
+
+fn main() {}
--- /dev/null
+error[E0119]: conflicting implementations of trait `Foo`
+ --> $DIR/issue-72845.rs:44:1
+ |
+LL | impl<T: Spec1> Foo for T {
+ | ------------------------ first implementation here
+...
+LL | impl<T: Spec2> Foo for T {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0119`.
--- /dev/null
+#![feature(generic_const_exprs, adt_const_params)]
+#![allow(incomplete_features)]
+
+struct FieldElement<const N: &'static str> {
+ n: [u64; num_limbs(N)],
+ //~^ ERROR unconstrained generic constant
+}
+const fn num_limbs(_: &str) -> usize {
+ 0
+}
+
+fn main() {}
--- /dev/null
+error: unconstrained generic constant
+ --> $DIR/issue-90455.rs:5:8
+ |
+LL | n: [u64; num_limbs(N)],
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+ = help: try adding a `where` bound using this expression: `where [(); num_limbs(N)]:`
+
+error: aborting due to previous error
+
LL | let x: &'static i32 = &X;
| ^ referenced constant has errors
query stack during panic:
-#0 [normalize_mir_const_after_erasing_regions] normalizing `main::promoted[1]`
+#0 [try_normalize_mir_const_after_erasing_regions] normalizing `main::promoted[1]`
#1 [optimized_mir] optimizing MIR for `main`
#2 [collect_and_partition_mono_items] collect_and_partition_mono_items
end of query stack
--- /dev/null
+// Regression test for issue #90870.
+
+// run-rustfix
+
+#![allow(dead_code)]
+
+const fn f(a: &u8, b: &u8) -> bool {
+ *a == *b
+ //~^ ERROR: calls in constant functions are limited to constant functions, tuple structs and tuple variants [E0015]
+ //~| HELP: consider dereferencing here
+}
+
+const fn g(a: &&&&i64, b: &&&&i64) -> bool {
+ ****a == ****b
+ //~^ ERROR: calls in constant functions are limited to constant functions, tuple structs and tuple variants [E0015]
+ //~| HELP: consider dereferencing here
+}
+
+const fn h(mut a: &[u8], mut b: &[u8]) -> bool {
+ while let ([l, at @ ..], [r, bt @ ..]) = (a, b) {
+ if *l == *r {
+ //~^ ERROR: calls in constant functions are limited to constant functions, tuple structs and tuple variants [E0015]
+ //~| HELP: consider dereferencing here
+ a = at;
+ b = bt;
+ } else {
+ return false;
+ }
+ }
+
+ a.is_empty() && b.is_empty()
+}
+
+fn main() {}
--- /dev/null
+// Regression test for issue #90870.
+
+// run-rustfix
+
+#![allow(dead_code)]
+
+const fn f(a: &u8, b: &u8) -> bool {
+ a == b
+ //~^ ERROR: calls in constant functions are limited to constant functions, tuple structs and tuple variants [E0015]
+ //~| HELP: consider dereferencing here
+}
+
+const fn g(a: &&&&i64, b: &&&&i64) -> bool {
+ a == b
+ //~^ ERROR: calls in constant functions are limited to constant functions, tuple structs and tuple variants [E0015]
+ //~| HELP: consider dereferencing here
+}
+
+const fn h(mut a: &[u8], mut b: &[u8]) -> bool {
+ while let ([l, at @ ..], [r, bt @ ..]) = (a, b) {
+ if l == r {
+ //~^ ERROR: calls in constant functions are limited to constant functions, tuple structs and tuple variants [E0015]
+ //~| HELP: consider dereferencing here
+ a = at;
+ b = bt;
+ } else {
+ return false;
+ }
+ }
+
+ a.is_empty() && b.is_empty()
+}
+
+fn main() {}
--- /dev/null
+error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
+ --> $DIR/issue-90870.rs:8:5
+ |
+LL | a == b
+ | ^^^^^^
+ |
+help: consider dereferencing here
+ |
+LL | *a == *b
+ | + +
+
+error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
+ --> $DIR/issue-90870.rs:14:5
+ |
+LL | a == b
+ | ^^^^^^
+ |
+help: consider dereferencing here
+ |
+LL | ****a == ****b
+ | ++++ ++++
+
+error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
+ --> $DIR/issue-90870.rs:21:12
+ |
+LL | if l == r {
+ | ^^^^^^
+ |
+help: consider dereferencing here
+ |
+LL | if *l == *r {
+ | + +
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0015`.
help: you might have forgotten to call this function
|
LL | let x = f() == g;
- | ~~~
+ | ++
help: you might have forgotten to call this function
|
LL | let x = f == g();
- | ~~~
+ | ++
error[E0308]: mismatched types
--> $DIR/fn-compare-mismatch.rs:4:18
fn foo() => impl Fn() => bool {
//~^ ERROR return types are denoted using `->`
- //~| ERROR expected one of `+`, `->`, `::`, `;`, `where`, or `{`, found `=>`
+ //~| ERROR expected one of `+`, `->`, `::`, `where`, or `{`, found `=>`
unimplemented!()
}
LL | fn foo() => impl Fn() => bool {
| ^^ help: use `->` instead
-error: expected one of `+`, `->`, `::`, `;`, `where`, or `{`, found `=>`
+error: expected one of `+`, `->`, `::`, `where`, or `{`, found `=>`
--> $DIR/fn-recover-return-sign2.rs:4:23
|
LL | fn foo() => impl Fn() => bool {
- | ^^ expected one of `+`, `->`, `::`, `;`, `where`, or `{`
+ | ^^ expected one of `+`, `->`, `::`, `where`, or `{`
error: aborting due to 2 previous errors
y /* 0#1 */ + x /* 0#0 */
}
-fn y /* 0#0 */() { }
+fn y /* 0#0 */() {}
/*
Expansions:
--- /dev/null
+fn main() {
+ let foo = foo(1, ""); //~ ERROR E0283
+}
+fn baz() {
+ let bar = bar(1, ""); //~ ERROR E0283
+}
+
+struct Bar<T, K, N: Default> {
+ t: T,
+ k: K,
+ n: N,
+}
+
+fn bar<T, K, Z: Default>(t: T, k: K) -> Bar<T, K, Z> {
+ Bar { t, k, n: Default::default() }
+}
+
+struct Foo<T, K, N: Default, M: Default> {
+ t: T,
+ k: K,
+ n: N,
+ m: M,
+}
+
+fn foo<T, K, W: Default, Z: Default>(t: T, k: K) -> Foo<T, K, W, Z> {
+ Foo { t, k, n: Default::default(), m: Default::default() }
+}
--- /dev/null
+error[E0283]: type annotations needed for `Foo<i32, &str, W, Z>`
+ --> $DIR/erase-type-params-in-label.rs:2:15
+ |
+LL | let foo = foo(1, "");
+ | --- ^^^ cannot infer type for type parameter `W` declared on the function `foo`
+ | |
+ | consider giving `foo` the explicit type `Foo<_, _, W, Z>`, where the type parameter `W` is specified
+ |
+ = note: cannot satisfy `_: Default`
+note: required by a bound in `foo`
+ --> $DIR/erase-type-params-in-label.rs:25:17
+ |
+LL | fn foo<T, K, W: Default, Z: Default>(t: T, k: K) -> Foo<T, K, W, Z> {
+ | ^^^^^^^ required by this bound in `foo`
+help: consider specifying the type arguments in the function call
+ |
+LL | let foo = foo::<T, K, W, Z>(1, "");
+ | ++++++++++++++
+
+error[E0283]: type annotations needed for `Bar<i32, &str, Z>`
+ --> $DIR/erase-type-params-in-label.rs:5:15
+ |
+LL | let bar = bar(1, "");
+ | --- ^^^ cannot infer type for type parameter `Z` declared on the function `bar`
+ | |
+ | consider giving `bar` the explicit type `Bar<_, _, Z>`, where the type parameter `Z` is specified
+ |
+ = note: cannot satisfy `_: Default`
+note: required by a bound in `bar`
+ --> $DIR/erase-type-params-in-label.rs:14:17
+ |
+LL | fn bar<T, K, Z: Default>(t: T, k: K) -> Bar<T, K, Z> {
+ | ^^^^^^^ required by this bound in `bar`
+help: consider specifying the type arguments in the function call
+ |
+LL | let bar = bar::<T, K, Z>(1, "");
+ | +++++++++++
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0283`.
LL | let _ = foo("foo"); //<- Do not suggest `foo::<N>("foo");`!
| - ^^^ cannot infer the value of const parameter `N` declared on the function `foo`
| |
- | consider giving this pattern the explicit type `[usize; _]`, where the type parameter `N` is specified
+ | consider giving this pattern the explicit type `[_; N]`, where the const parameter `N` is specified
error: aborting due to previous error
| --- ^ -- {integer}
| |
| fn() -> i32 {foo}
- | help: you might have forgotten to call this function: `foo()`
+ |
+help: you might have forgotten to call this function
+ |
+LL | foo() > 12;
+ | ++
error[E0308]: mismatched types
--> $DIR/issue-59488.rs:14:11
| --- ^ -- {integer}
| |
| fn(i64) -> i64 {bar}
- | help: you might have forgotten to call this function: `bar( /* arguments */ )`
+ |
+help: you might have forgotten to call this function
+ |
+LL | bar( /* arguments */ ) > 13;
+ | +++++++++++++++++++
error[E0308]: mismatched types
--> $DIR/issue-59488.rs:18:11
help: you might have forgotten to call this function
|
LL | foo() > foo;
- | ~~~~~
+ | ++
help: you might have forgotten to call this function
|
LL | foo > foo();
- | ~~~~~
+ | ++
error[E0369]: binary operation `>` cannot be applied to type `fn() -> i32 {foo}`
--> $DIR/issue-59488.rs:25:9
| |
| fn() -> i32 {a}
| {integer}
- | help: you might have forgotten to call this function: `*left_val()`
|
= note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: you might have forgotten to call this function
+ |
+LL | if !(*left_val() == *right_val) {
+ | ++
error[E0308]: mismatched types
--> $DIR/issue-70724-add_type_neq_err_label-unwrap.rs:6:5
-// compile-flags: -Zfuture-incompat-test -Zemit-future-incompat-report
+// compile-flags: -Zfuture-incompat-test
// check-pass
// The `-Zfuture-incompat-test flag causes any normal warning to be included
macro_rules! foo { () => { break 'x ; } }
-pub fn main() { loop { } }
+pub fn main() { loop {} }
struct S;
}
- n!(a $nt_item b); //~ ERROR no rules expected the token `enum E { }`
+ n!(a $nt_item b); //~ ERROR no rules expected the token `enum E {}`
}
simple_nonterminal!(a, 'a, (x, y, z)); // OK
-error: no rules expected the token `enum E { }`
+error: no rules expected the token `enum E {}`
--> $DIR/nonterminal-matching.rs:19:10
|
LL | macro n(a $nt_item b) {
// to happen in #24780. For example, following should be an error:
// expected one of ..., `>`, ... found `>`.
-fn foo() -> Vec<usize>> { //~ ERROR expected one of `!`, `+`, `::`, `;`, `where`, or `{`, found `>`
+fn foo() -> Vec<usize>> { //~ ERROR expected one of `!`, `+`, `::`, `where`, or `{`, found `>`
Vec::new()
}
-error: expected one of `!`, `+`, `::`, `;`, `where`, or `{`, found `>`
+error: expected one of `!`, `+`, `::`, `where`, or `{`, found `>`
--> $DIR/issue-24780.rs:5:23
|
LL | fn foo() -> Vec<usize>> {
- | ^ expected one of `!`, `+`, `::`, `;`, `where`, or `{`
+ | ^ expected one of `!`, `+`, `::`, `where`, or `{`
error: aborting due to previous error
//~^ ERROR cannot find type `A` in this scope
fn b(self>
//~^ ERROR expected one of `)`, `,`, or `:`, found `>`
- //~| ERROR expected one of `->`, `;`, `where`, or `{`, found `>`
+ //~| ERROR expected one of `->`, `where`, or `{`, found `>`
}
fn main() {}
| |
| unclosed delimiter
-error: expected one of `->`, `;`, `where`, or `{`, found `>`
+error: expected one of `->`, `where`, or `{`, found `>`
--> $DIR/issue-58856-1.rs:3:14
|
LL | impl A {
| - while parsing this item list starting here
LL |
LL | fn b(self>
- | ^ expected one of `->`, `;`, `where`, or `{`
+ | ^ expected one of `->`, `where`, or `{`
...
LL | }
| - the item list ends here
| expected one of `(`, `)`, `+`, `,`, `::`, or `<`
| help: missing `,`
-error: expected one of `->`, `;`, `where`, or `{`, found `<eof>`
+error: expected one of `->`, `where`, or `{`, found `<eof>`
--> $DIR/issue-84148-1.rs:1:15
|
LL | fn f(t:for<>t?)
- | ^ expected one of `->`, `;`, `where`, or `{`
+ | ^ expected one of `->`, `where`, or `{`
error: aborting due to 3 previous errors
| expected one of `(`, `)`, `+`, `,`, `::`, or `<`
| help: missing `,`
-error: expected one of `->`, `;`, `where`, or `{`, found `<eof>`
+error: expected one of `->`, `where`, or `{`, found `<eof>`
--> $DIR/issue-84148-2.rs:4:16
|
LL | fn f(t:for<>t?
- | ^ expected one of `->`, `;`, `where`, or `{`
+ | ^ expected one of `->`, `where`, or `{`
error: aborting due to 4 previous errors
impl Foo {
pub fn bar()
- //~^ ERROR: expected `;`, found `}`
- //~| ERROR: associated function in `impl` without body
+ //~^ ERROR: associated function in `impl` without body
}
+//~^ERROR expected one of `->`, `where`, or `{`, found `}`
fn main() {}
-error: expected `;`, found `}`
- --> $DIR/issue-87635.rs:4:17
+error: expected one of `->`, `where`, or `{`, found `}`
+ --> $DIR/issue-87635.rs:6:1
|
LL | pub fn bar()
- | ^ help: add `;` here
-...
+ | --- - expected one of `->`, `where`, or `{`
+ | |
+ | while parsing this `fn`
+LL |
LL | }
- | - unexpected token
+ | ^ unexpected token
error: associated function in `impl` without body
--> $DIR/issue-87635.rs:4:5
LL | fn main((ؼ
| ^ expected one of `:` or `|`
-error: expected one of `->`, `;`, `where`, or `{`, found `<eof>`
+error: expected one of `->`, `where`, or `{`, found `<eof>`
--> $DIR/missing_right_paren.rs:3:11
|
LL | fn main((ؼ
- | ^ expected one of `->`, `;`, `where`, or `{`
+ | ^ expected one of `->`, `where`, or `{`
error: aborting due to 4 previous errors
span: $DIR/allowed-attr-stmt-expr.rs:49:20: 49:21 (#0),
},
]
-PRINT-ATTR INPUT (DISPLAY): #[rustc_dummy] struct Foo { }
+PRINT-ATTR INPUT (DISPLAY): #[rustc_dummy] struct Foo {}
PRINT-ATTR INPUT (DEBUG): TokenStream [
Punct {
ch: '#',
span: $DIR/allowed-attr-stmt-expr.rs:61:28: 61:29 (#0),
},
]
-PRINT-ATTR INPUT (DISPLAY): second_make_stmt! (#[allow(dead_code)] struct Bar { }) ;
+PRINT-ATTR INPUT (DISPLAY): second_make_stmt! (#[allow(dead_code)] struct Bar {}) ;
PRINT-ATTR INPUT (DEBUG): TokenStream [
Ident {
ident: "second_make_stmt",
span: $DIR/allowed-attr-stmt-expr.rs:64:57: 64:58 (#0),
},
]
-PRINT-ATTR INPUT (DISPLAY): #[rustc_dummy] #[allow(dead_code)] struct Bar { }
+PRINT-ATTR INPUT (DISPLAY): #[rustc_dummy] #[allow(dead_code)] struct Bar {}
PRINT-ATTR INPUT (DEBUG): TokenStream [
Punct {
ch: '#',
span: $DIR/allowed-attr-stmt-expr.rs:64:54: 64:56 (#0),
},
]
-PRINT-ATTR INPUT (DISPLAY): #[rustc_dummy] struct Other { }
+PRINT-ATTR INPUT (DISPLAY): #[rustc_dummy] struct Other {}
PRINT-ATTR INPUT (DEBUG): TokenStream [
Punct {
ch: '#',
-PRINT-ATTR INPUT (DISPLAY): fn foo < T : MyTrait < MyStruct < { true } >> > () { }
+PRINT-ATTR INPUT (DISPLAY): fn foo < T : MyTrait < MyStruct < { true } >> > () {}
PRINT-ATTR INPUT (DEBUG): TokenStream [
Ident {
ident: "fn",
-PRINT-ATTR INPUT (DISPLAY): #[rustc_dummy] struct Foo { }
+PRINT-ATTR INPUT (DISPLAY): #[rustc_dummy] struct Foo {}
PRINT-ATTR INPUT (DEBUG): TokenStream [
Punct {
ch: '#',
span: $DIR/attr-stmt-expr.rs:53:28: 53:29 (#0),
},
]
-PRINT-ATTR INPUT (DISPLAY): second_make_stmt! (#[allow(dead_code)] struct Bar { }) ;
+PRINT-ATTR INPUT (DISPLAY): second_make_stmt! (#[allow(dead_code)] struct Bar {}) ;
PRINT-ATTR INPUT (DEBUG): TokenStream [
Ident {
ident: "second_make_stmt",
span: $DIR/attr-stmt-expr.rs:56:57: 56:58 (#0),
},
]
-PRINT-ATTR INPUT (DISPLAY): #[rustc_dummy] #[allow(dead_code)] struct Bar { }
+PRINT-ATTR INPUT (DISPLAY): #[rustc_dummy] #[allow(dead_code)] struct Bar {}
PRINT-ATTR INPUT (DEBUG): TokenStream [
Punct {
ch: '#',
span: $DIR/attr-stmt-expr.rs:56:54: 56:56 (#0),
},
]
-PRINT-ATTR INPUT (DISPLAY): #[rustc_dummy] struct Other { }
+PRINT-ATTR INPUT (DISPLAY): #[rustc_dummy] struct Other {}
PRINT-ATTR INPUT (DEBUG): TokenStream [
Punct {
ch: '#',
span: $DIR/attribute-after-derive.rs:16:24: 19:2 (#0),
},
]
-PRINT-DERIVE INPUT (DISPLAY): struct AttributeDerive { }
+PRINT-DERIVE INPUT (DISPLAY): struct AttributeDerive {}
PRINT-DERIVE INPUT (DEBUG): TokenStream [
Ident {
ident: "struct",
span: $DIR/attribute-after-derive.rs:16:24: 19:2 (#0),
},
]
-PRINT-DERIVE INPUT (DISPLAY): #[print_attr] struct DeriveAttribute { }
+PRINT-DERIVE INPUT (DISPLAY): #[print_attr] struct DeriveAttribute {}
PRINT-DERIVE INPUT (DEBUG): TokenStream [
Punct {
ch: '#',
let input = input.to_string();
- assert_eq!(input, "fn foo() { }");
+ assert_eq!(input, "fn foo() {}");
r#"
fn foo() -> &'static str { "Hello, world!" }
#[proc_macro_attribute]
pub fn foo(attr: TokenStream, item: TokenStream) -> TokenStream {
drop(attr);
- assert_eq!(item.to_string(), "fn foo() { }");
+ assert_eq!(item.to_string(), "fn foo() {}");
"fn foo(&self);".parse().unwrap()
}
{
#! [rustc_dummy(cursed_inner)] #! [allow(unused)] struct Inner
{ field : [u8 ; { #! [rustc_dummy(another_cursed_inner)] 1 }] } 0
- }] > { #! [rustc_dummy(evaluated_attr)] fn bar() { } }
+ }] > { #! [rustc_dummy(evaluated_attr)] fn bar() {} }
PRINT-ATTR INPUT (DEBUG): TokenStream [
Ident {
ident: "impl",
-Derive First: #[derive(Second)] #[derive(Third, Fourth)] #[derive(Fifth)] pub struct Foo { }
-Derive Second: #[derive(Third, Fourth)] #[derive(Fifth)] pub struct Foo { }
-Derive Third: #[derive(Fifth)] pub struct Foo { }
-Derive Fourth: #[derive(Fifth)] pub struct Foo { }
-Derive Fifth: pub struct Foo { }
+Derive First: #[derive(Second)] #[derive(Third, Fourth)] #[derive(Fifth)] pub struct Foo {}
+Derive Second: #[derive(Third, Fourth)] #[derive(Fifth)] pub struct Foo {}
+Derive Third: #[derive(Fifth)] pub struct Foo {}
+Derive Fourth: #[derive(Fifth)] pub struct Foo {}
+Derive Fifth: pub struct Foo {}
span: #8 bytes(430..483),
},
]
-PRINT-DERIVE INPUT (DISPLAY): enum E { V = { let _ = { { } } ; 0 }, }
+PRINT-DERIVE INPUT (DISPLAY): enum E { V = { let _ = { {} } ; 0 }, }
PRINT-DERIVE INPUT (DEBUG): TokenStream [
Ident {
ident: "enum",
span: $DIR/inner-attrs.rs:20:30: 20:36 (#0),
},
]
-PRINT-ATTR INPUT (DISPLAY): fn foo() { }
+PRINT-ATTR INPUT (DISPLAY): fn foo() {}
PRINT-ATTR INPUT (DEBUG): TokenStream [
Ident {
ident: "fn",
span: $DIR/inner-attrs.rs:27:30: 27:40 (#0),
},
]
-PRINT-ATTR INPUT (DISPLAY): mod inline_mod { }
+PRINT-ATTR INPUT (DISPLAY): mod inline_mod {}
PRINT-ATTR INPUT (DEBUG): TokenStream [
Ident {
ident: "mod",
span: $DIR/inner-attrs.rs:82:42: 82:47 (#0),
},
]
-PRINT-ATTR INPUT (DISPLAY): fn weird_extern() { }
+PRINT-ATTR INPUT (DISPLAY): fn weird_extern() {}
PRINT-ATTR INPUT (DEBUG): TokenStream [
Ident {
ident: "fn",
span: #4 bytes(432..433),
},
]
-PRINT-DERIVE INPUT (DISPLAY): struct A { }
+PRINT-DERIVE INPUT (DISPLAY): struct A {}
PRINT-DERIVE INPUT (DEBUG): TokenStream [
Ident {
ident: "struct",
-PRINT-ATTR INPUT (DISPLAY): fn main() { & | _ : u8 | { } ; mul_2! (1 + 1) ; }
+PRINT-ATTR INPUT (DISPLAY): fn main() { & | _ : u8 | {} ; mul_2! (1 + 1) ; }
PRINT-ATTR INPUT (DEBUG): TokenStream [
Ident {
ident: "fn",
#[cfg(FALSE)] struct Bar ; #[cfg(not(FALSE))] struct Inner ;
#[cfg(FALSE)] let a = 25 ; match true
{
- #[cfg(FALSE)] true => { },
- #[cfg_attr(not(FALSE), allow(warnings))] false => { }, _ => { }
+ #[cfg(FALSE)] true => {},
+ #[cfg_attr(not(FALSE), allow(warnings))] false => {}, _ => {}
} ; #[print_helper(should_be_removed)] fn removed_fn()
{ #! [cfg(FALSE)] } #[print_helper(c)] #[cfg(not(FALSE))] fn
kept_fn() { #! [cfg(not(FALSE))] let my_val = true ; } enum TupleEnum
[u8 ;
{
#[cfg(not(FALSE))] struct Inner ; match true
- { #[allow(warnings)] false => { }, _ => { } } ; #[print_helper(c)]
+ { #[allow(warnings)] false => {}, _ => {} } ; #[print_helper(c)]
#[cfg(not(FALSE))] fn kept_fn()
{ #! [cfg(not(FALSE))] let my_val = true ; } enum TupleEnum
{ Foo(#[cfg(not(FALSE))] i32, u8) } struct
-PRINT-ATTR INPUT (DISPLAY): #[doc = r" A doc comment"] struct Foo { }
+PRINT-ATTR INPUT (DISPLAY): #[doc = r" A doc comment"] struct Foo {}
PRINT-ATTR INPUT (DEBUG): TokenStream [
Punct {
ch: '#',
span: $DIR/issue-81007-item-attrs.rs:22:16: 22:18 (#0),
},
]
-PRINT-ATTR INPUT (DISPLAY): #[rustc_dummy] #[doc = r" Another comment comment"] struct Bar { }
+PRINT-ATTR INPUT (DISPLAY): #[rustc_dummy] #[doc = r" Another comment comment"] struct Bar {}
PRINT-ATTR INPUT (DEBUG): TokenStream [
Punct {
ch: '#',
span: $DIR/auxiliary/nested-macro-rules.rs:9:30: 9:35 (#6),
},
]
-PRINT-ATTR INPUT (DISPLAY): struct FirstAttrStruct { }
+PRINT-ATTR INPUT (DISPLAY): struct FirstAttrStruct {}
PRINT-ATTR INPUT (DEBUG): TokenStream [
Ident {
ident: "struct",
span: $DIR/auxiliary/nested-macro-rules.rs:9:30: 9:35 (#15),
},
]
-PRINT-ATTR INPUT (DISPLAY): struct SecondAttrStruct { }
+PRINT-ATTR INPUT (DISPLAY): struct SecondAttrStruct {}
PRINT-ATTR INPUT (DEBUG): TokenStream [
Ident {
ident: "struct",
struct S /* 0#5 */;
// OK, not a duplicate definition of `S`
-fn main /* 0#0 */() { }
+fn main /* 0#0 */() {}
/*
Expansions:
-PRINT-ATTR INPUT (DISPLAY): fn foo < T > () where T : Copy + { }
+PRINT-ATTR INPUT (DISPLAY): fn foo < T > () where T : Copy + {}
PRINT-ATTR INPUT (DEBUG): TokenStream [
Ident {
ident: "fn",
span: $DIR/weird-braces.rs:20:30: 20:42 (#0),
},
]
-PRINT-ATTR INPUT (DISPLAY): impl Bar < { 1 > 0 } > for Foo < { true } > { }
+PRINT-ATTR INPUT (DISPLAY): impl Bar < { 1 > 0 } > for Foo < { true } > {}
PRINT-ATTR INPUT (DEBUG): TokenStream [
Ident {
ident: "impl",
// [pretty]compile-flags: -Zunpretty=everybody_loops
// [pretty]check-pass
#[repr("C")]
-struct A {
-}
+struct A {}
-fn main() { loop { } }
+fn main() { loop {} }
// build-pass (FIXME(62277): could be check-pass?)
// compile-flags: -Z unpretty=expanded
-fn main() { if let 0 = 1 { } }
+fn main() { if let 0 = 1 {} }
}
checker!(attr_extern, r#"extern "C" { fn ffi(#[a1] arg1 : i32, #[a2] ...) ; }"#);
-checker!(attr_extern_cvar, r#"unsafe extern "C" fn cvar(arg1 : i32, #[a1] mut args : ...) { }"#);
+checker!(attr_extern_cvar, r#"unsafe extern "C" fn cvar(arg1 : i32, #[a1] mut args : ...) {}"#);
checker!(attr_alias, "type Alias = fn(#[a1] u8, #[a2] ...) ;");
checker!(attr_free, "fn free(#[a1] arg1 : u8) { let lam = | #[a2] W(x), #[a3] y | () ; }");
-checker!(attr_inherent_1, "fn inherent1(#[a1] self, #[a2] arg1 : u8) { }");
-checker!(attr_inherent_2, "fn inherent2(#[a1] & self, #[a2] arg1 : u8) { }");
-checker!(attr_inherent_3, "fn inherent3 < 'a > (#[a1] & 'a mut self, #[a2] arg1 : u8) { }");
-checker!(attr_inherent_4, "fn inherent4 < 'a > (#[a1] self : Box < Self >, #[a2] arg1 : u8) { }");
-checker!(attr_inherent_issue_64682, "fn inherent5(#[a1] #[a2] arg1 : u8, #[a3] arg2 : u8) { }");
+checker!(attr_inherent_1, "fn inherent1(#[a1] self, #[a2] arg1 : u8) {}");
+checker!(attr_inherent_2, "fn inherent2(#[a1] & self, #[a2] arg1 : u8) {}");
+checker!(attr_inherent_3, "fn inherent3 < 'a > (#[a1] & 'a mut self, #[a2] arg1 : u8) {}");
+checker!(attr_inherent_4, "fn inherent4 < 'a > (#[a1] self : Box < Self >, #[a2] arg1 : u8) {}");
+checker!(attr_inherent_issue_64682, "fn inherent5(#[a1] #[a2] arg1 : u8, #[a3] arg2 : u8) {}");
checker!(attr_trait_1, "fn trait1(#[a1] self, #[a2] arg1 : u8) ;");
checker!(attr_trait_2, "fn trait2(#[a1] & self, #[a2] arg1 : u8) ;");
checker!(attr_trait_3, "fn trait3 < 'a > (#[a1] & 'a mut self, #[a2] arg1 : u8) ;");
checker!(rename_params, r#"impl Foo
{
fn hello(#[angery(true)] a : i32, #[a2] b : i32, #[what = "how"] c : u32)
- { } fn
+ {} fn
hello2(#[a1] #[a2] a : i32, #[what = "how"] b : i32, #[angery(true)] c :
- u32) { } fn
+ u32) {} fn
hello_self(#[a1] #[a2] & self, #[a1] #[a2] a : i32, #[what = "how"] b :
- i32, #[angery(true)] c : u32) { }
+ i32, #[angery(true)] c : u32) {}
}"#);
--- /dev/null
+fn main() {
+ let slice = [1,2,3,4];
+ let vec = vec![1,2,3,4];
+
+ slice.count(); //~ERROR: E0599
+ vec.count(); //~ERROR: E0599
+ vec.as_slice().count(); //~ERROR: E0599
+}
--- /dev/null
+error[E0599]: no method named `count` found for array `[{integer}; 4]` in the current scope
+ --> $DIR/count2len.rs:5:11
+ |
+LL | slice.count();
+ | ^^^^^
+ | |
+ | method cannot be called on `[{integer}; 4]` due to unsatisfied trait bounds
+ | help: consider using `len` instead
+ |
+ = note: `count` is defined on `Iterator`, which `[{integer}; 4]` does not implement
+
+error[E0599]: no method named `count` found for struct `Vec<{integer}>` in the current scope
+ --> $DIR/count2len.rs:6:9
+ |
+LL | vec.count();
+ | ^^^^^
+ | |
+ | method cannot be called on `Vec<{integer}>` due to unsatisfied trait bounds
+ | help: consider using `len` instead
+ |
+ = note: `count` is defined on `Iterator`, which `Vec<{integer}>` does not implement
+
+error[E0599]: no method named `count` found for reference `&[{integer}]` in the current scope
+ --> $DIR/count2len.rs:7:20
+ |
+LL | vec.as_slice().count();
+ | ^^^^^
+ | |
+ | method cannot be called on `&[{integer}]` due to unsatisfied trait bounds
+ | help: consider using `len` instead
+ |
+ = note: `count` is defined on `Iterator`, which `&[{integer}]` does not implement
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0599`.
--- /dev/null
+// run-rustfix
+
+#[allow(dead_code)]
+
+extern "C" {
+ fn foo(); //~ERROR expected `;`
+}
+
+fn main() {}
--- /dev/null
+// run-rustfix
+
+#[allow(dead_code)]
+
+extern "C" {
+ fn foo() //~ERROR expected `;`
+}
+
+fn main() {}
--- /dev/null
+error: expected `;`, found `}`
+ --> $DIR/suggest-semicolon-for-fn-in-extern-block.rs:6:11
+ |
+LL | fn foo()
+ | ^ help: add `;` here
+LL | }
+ | - unexpected token
+
+error: aborting due to previous error
+
--- /dev/null
+// compile-flags: --edition=2021
+
+#![feature(rustc_attrs)]
+
+use core::any::Any;
+use core::marker::PhantomData;
+
+fn main() {
+ test::<MaskedStorage<GenericComp<Pos>>>(make());
+ //~^ ERROR evaluate(Binder(TraitPredicate(<MaskedStorage<GenericComp<Pos>> as std::marker::Sized>, polarity:Positive), [])) = Ok(EvaluatedToOk)
+ //~| ERROR evaluate(Binder(TraitPredicate(<MaskedStorage<GenericComp<Pos>> as std::marker::Sized>, polarity:Positive), [])) = Ok(EvaluatedToOk)
+
+ test::<MaskedStorage<GenericComp2<Pos>>>(make());
+ //~^ ERROR evaluate(Binder(TraitPredicate(<MaskedStorage<GenericComp2<Pos>> as std::marker::Sized>, polarity:Positive), [])) = Ok(EvaluatedToOkModuloRegions)
+ //~| ERROR evaluate(Binder(TraitPredicate(<MaskedStorage<GenericComp2<Pos>> as std::marker::Sized>, polarity:Positive), [])) = Ok(EvaluatedToOkModuloRegions)
+}
+
+#[rustc_evaluate_where_clauses]
+fn test<T: Sized>(_: T) {}
+
+fn make<T>() -> T {
+ todo!()
+}
+
+struct DerefWrap<T>(T);
+
+impl<T> core::ops::Deref for DerefWrap<T> {
+ type Target = T;
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+struct Storage<T, D> {
+ phantom: PhantomData<(T, D)>,
+}
+
+type ReadStorage<T> = Storage<T, DerefWrap<MaskedStorage<T>>>;
+
+pub trait Component {
+ type Storage;
+}
+
+struct VecStorage;
+
+struct Pos;
+
+impl Component for Pos {
+ type Storage = VecStorage;
+}
+
+struct GenericComp<T> {
+ _t: T,
+}
+
+impl<T: 'static> Component for GenericComp<T> {
+ type Storage = VecStorage;
+}
+
+struct GenericComp2<T> {
+ _t: T,
+}
+
+impl<T: 'static> Component for GenericComp2<T> where for<'a> &'a bool: 'a {
+ type Storage = VecStorage;
+}
+
+struct ReadData {
+ pos_interpdata: ReadStorage<GenericComp<Pos>>,
+}
+
+trait System {
+ type SystemData;
+
+ fn run(data: Self::SystemData, any: Box<dyn Any>);
+}
+
+struct Sys;
+
+impl System for Sys {
+ type SystemData = (ReadData, ReadStorage<Pos>);
+
+ fn run((data, pos): Self::SystemData, any: Box<dyn Any>) {
+ <ReadStorage<GenericComp<Pos>> as SystemData>::setup(any);
+
+ ParJoin::par_join((&pos, &data.pos_interpdata));
+ }
+}
+
+trait ParJoin {
+ fn par_join(self)
+ where
+ Self: Sized,
+ {
+ }
+}
+
+impl<'a, T, D> ParJoin for &'a Storage<T, D>
+where
+ T: Component,
+ D: core::ops::Deref<Target = MaskedStorage<T>>,
+ T::Storage: Sync,
+{
+}
+
+impl<A, B> ParJoin for (A, B)
+where
+ A: ParJoin,
+ B: ParJoin,
+{
+}
+
+pub trait SystemData {
+ fn setup(any: Box<dyn Any>);
+}
+
+impl<T: 'static> SystemData for ReadStorage<T>
+where
+ T: Component,
+{
+ fn setup(any: Box<dyn Any>) {
+ let storage: &MaskedStorage<T> = any.downcast_ref().unwrap();
+
+ <dyn Any as CastFrom<MaskedStorage<T>>>::cast(&storage);
+ }
+}
+
+pub struct MaskedStorage<T: Component> {
+ _inner: T::Storage,
+}
+
+pub unsafe trait CastFrom<T> {
+ fn cast(t: &T) -> &Self;
+}
+
+unsafe impl<T> CastFrom<T> for dyn Any
+where
+ T: Any + 'static,
+{
+ fn cast(t: &T) -> &Self {
+ t
+ }
+}
--- /dev/null
+error: evaluate(Binder(TraitPredicate(<MaskedStorage<GenericComp<Pos>> as std::marker::Sized>, polarity:Positive), [])) = Ok(EvaluatedToOk)
+ --> $DIR/issue-85360-eval-obligation-ice.rs:9:5
+ |
+LL | test::<MaskedStorage<GenericComp<Pos>>>(make());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn test<T: Sized>(_: T) {}
+ | - predicate
+
+error: evaluate(Binder(TraitPredicate(<MaskedStorage<GenericComp<Pos>> as std::marker::Sized>, polarity:Positive), [])) = Ok(EvaluatedToOk)
+ --> $DIR/issue-85360-eval-obligation-ice.rs:9:5
+ |
+LL | test::<MaskedStorage<GenericComp<Pos>>>(make());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn test<T: Sized>(_: T) {}
+ | ----- predicate
+
+error: evaluate(Binder(TraitPredicate(<MaskedStorage<GenericComp2<Pos>> as std::marker::Sized>, polarity:Positive), [])) = Ok(EvaluatedToOkModuloRegions)
+ --> $DIR/issue-85360-eval-obligation-ice.rs:13:5
+ |
+LL | test::<MaskedStorage<GenericComp2<Pos>>>(make());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn test<T: Sized>(_: T) {}
+ | - predicate
+
+error: evaluate(Binder(TraitPredicate(<MaskedStorage<GenericComp2<Pos>> as std::marker::Sized>, polarity:Positive), [])) = Ok(EvaluatedToOkModuloRegions)
+ --> $DIR/issue-85360-eval-obligation-ice.rs:13:5
+ |
+LL | test::<MaskedStorage<GenericComp2<Pos>>>(make());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | fn test<T: Sized>(_: T) {}
+ | ----- predicate
+
+error: aborting due to 4 previous errors
+
--- /dev/null
+fn main() {
+ 0: u8<e<5>=e>
+ //~^ ERROR: cannot find type `e` in this scope [E0412]
+ //~| ERROR: associated type bindings are not allowed here [E0229]
+ //~| ERROR: mismatched types [E0308]
+}
--- /dev/null
+error[E0412]: cannot find type `e` in this scope
+ --> $DIR/issue-91267.rs:2:16
+ |
+LL | 0: u8<e<5>=e>
+ | ^
+ | |
+ | not found in this scope
+ | help: maybe you meant to write an assignment here: `let e`
+
+error[E0229]: associated type bindings are not allowed here
+ --> $DIR/issue-91267.rs:2:11
+ |
+LL | 0: u8<e<5>=e>
+ | ^^^^^^ associated type not allowed here
+
+error[E0308]: mismatched types
+ --> $DIR/issue-91267.rs:2:5
+ |
+LL | fn main() {
+ | - expected `()` because of default return type
+LL | 0: u8<e<5>=e>
+ | ^^^^^^^^^^^^^ expected `()`, found `u8`
+
+error: aborting due to 3 previous errors
+
+Some errors have detailed explanations: E0229, E0308, E0412.
+For more information about an error, try `rustc --explain E0229`.
--- /dev/null
+// Regression test for the ICE described in issue #91334.
+
+// error-pattern: this file contains an unclosed delimiter
+// error-pattern: expected one of
+// error-pattern: mismatched closing delimiter
+// error-pattern: mismatched types
+
+#![feature(generators)]
+
+fn f(){||yield(((){),
--- /dev/null
+error: this file contains an unclosed delimiter
+ --> $DIR/issue-91334.rs:10:23
+ |
+LL | fn f(){||yield(((){),
+ | - - ^
+ | | |
+ | | unclosed delimiter
+ | unclosed delimiter
+
+error: this file contains an unclosed delimiter
+ --> $DIR/issue-91334.rs:10:23
+ |
+LL | fn f(){||yield(((){),
+ | - - ^
+ | | |
+ | | unclosed delimiter
+ | unclosed delimiter
+
+error: expected one of `)`, `,`, `.`, `?`, or an operator, found `{`
+ --> $DIR/issue-91334.rs:10:19
+ |
+LL | fn f(){||yield(((){),
+ | ^
+ | |
+ | expected one of `)`, `,`, `.`, `?`, or an operator
+ | help: missing `,`
+
+error: mismatched closing delimiter: `)`
+ --> $DIR/issue-91334.rs:10:19
+ |
+LL | fn f(){||yield(((){),
+ | - ^^ mismatched closing delimiter
+ | | |
+ | | unclosed delimiter
+ | closing delimiter possibly meant for this
+
+error[E0308]: mismatched types
+ --> $DIR/issue-91334.rs:10:8
+ |
+LL | fn f(){||yield(((){),
+ | -^^^^^^^^^^^^^^^ expected `()`, found generator
+ | |
+ | help: try adding a return type: `-> [generator@$DIR/issue-91334.rs:10:8: 10:23]`
+ |
+ = note: expected unit type `()`
+ found generator `[generator@$DIR/issue-91334.rs:10:8: 10:23]`
+
+error: aborting due to 5 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
--- /dev/null
+// Regression test for #91450.
+// This test ensures that the compiler does not suggest `Foo<[type error]>` in diagnostic messages.
+
+fn foo() -> Option<_> {} //~ ERROR: [E0308]
+//~^ ERROR: the type placeholder `_` is not allowed
+
+fn main() {}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/issue-91450-inner-ty-error.rs:4:13
+ |
+LL | fn foo() -> Option<_> {}
+ | --- ^^^^^^^^^ expected enum `Option`, found `()`
+ | |
+ | implicitly returns `()` as its body has no tail or `return` expression
+ |
+ = note: expected enum `Option<_>`
+ found unit type `()`
+
+error[E0121]: the type placeholder `_` is not allowed within types on item signatures for return types
+ --> $DIR/issue-91450-inner-ty-error.rs:4:20
+ |
+LL | fn foo() -> Option<_> {}
+ | ^ not allowed in type signatures
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0121, E0308.
+For more information about an error, try `rustc --explain E0121`.
+++ /dev/null
----
-name: Blank Issue
-about: Create a blank issue.
----
-
-
-<!--
-Additional labels can be added to this issue by including the following command
-(without the space after the @ symbol):
-
-@ rustbot label +<label>
-
-Common labels for this issue type are:
-* C-an-interesting-project
-* C-enhancement
-* C-question
-* C-tracking-issue
--->
--- /dev/null
+name: Blank Issue
+description: Create a blank issue.
+body:
+ - type: markdown
+ attributes:
+ value: Thank you for filing an issue!
+ - type: textarea
+ id: problem
+ attributes:
+ label: Description
+ description: >
+ Please provide a discription of the issue, along with any information
+ you feel relevant to replicate it.
+ validations:
+ required: true
+ - type: textarea
+ id: version
+ attributes:
+ label: Version
+ description: "Rust version (`rustc -Vv`)"
+ placeholder: |
+ rustc 1.46.0-nightly (f455e46ea 2020-06-20)
+ binary: rustc
+ commit-hash: f455e46eae1a227d735091091144601b467e1565
+ commit-date: 2020-06-20
+ host: x86_64-unknown-linux-gnu
+ release: 1.46.0-nightly
+ LLVM version: 10.0
+ render: text
+ - type: textarea
+ id: labels
+ attributes:
+ label: Additional Labels
+ description: >
+ Additional labels can be added to this issue by including the following
+ command
+ placeholder: |
+ @rustbot label +<label>
+
+ Common labels for this issue type are:
+ * C-an-interesting-project
+ * C-enhancement
+ * C-question
+ * C-tracking-issue
+++ /dev/null
----
-name: Bug Report
-about: Create a bug report for Clippy
-labels: C-bug
----
-<!--
-Thank you for filing a bug report! 🐛 Please provide a short summary of the bug,
-along with any information you feel relevant to replicating the bug.
--->
-
-I tried this code:
-
-```rust
-<code>
-```
-
-I expected to see this happen: *explanation*
-
-Instead, this happened: *explanation*
-
-### Meta
-
-**Rust version (`rustc -Vv`):**
-
-```
-rustc 1.46.0-nightly (f455e46ea 2020-06-20)
-binary: rustc
-commit-hash: f455e46eae1a227d735091091144601b467e1565
-commit-date: 2020-06-20
-host: x86_64-unknown-linux-gnu
-release: 1.46.0-nightly
-LLVM version: 10.0
-```
-
-<!--
-Additional labels can be added to this issue by including the following command
-(without the space after the @ symbol):
-
-@ rustbot label +<label>
-
-Common labels for this issue type are:
-* `I-suggestion-causes-error`
--->
--- /dev/null
+name: Bug Report
+description: Create a bug report for Clippy
+labels: ["C-bug"]
+body:
+ - type: markdown
+ attributes:
+ value: Thank you for filing a bug report! 🐛
+ - type: textarea
+ id: problem
+ attributes:
+ label: Summary
+ description: >
+ Please provide a short summary of the bug, along with any information
+ you feel relevant to replicate the bug.
+ validations:
+ required: true
+ - type: textarea
+ id: reproducer
+ attributes:
+ label: Reproducer
+ description: Please provide the code and steps to repoduce the bug
+ value: |
+ I tried this code:
+
+ ```rust
+ <code>
+ ```
+
+ I expected to see this happen:
+
+ Instead, this happened:
+ - type: textarea
+ id: version
+ attributes:
+ label: Version
+ description: "Rust version (`rustc -Vv`)"
+ placeholder: |
+ rustc 1.46.0-nightly (f455e46ea 2020-06-20)
+ binary: rustc
+ commit-hash: f455e46eae1a227d735091091144601b467e1565
+ commit-date: 2020-06-20
+ host: x86_64-unknown-linux-gnu
+ release: 1.46.0-nightly
+ LLVM version: 10.0
+ render: text
+ - type: textarea
+ id: labels
+ attributes:
+ label: Additional Labels
+ description: >
+ Additional labels can be added to this issue by including the following
+ command
+ placeholder: |
+ @rustbot label +<label>
+
+ Common labels for this issue type are:
+ * `I-suggestion-causes-error`
+++ /dev/null
----
-name: Bug Report (False Negative)
-about: Create a bug report about missing warnings from a lint
-labels: C-bug, I-false-negative
----
-<!--
-Thank you for filing a bug report! 🐛 Please provide a short summary of the bug,
-along with any information you feel relevant to replicating the bug.
--->
-Lint name:
-
-
-I tried this code:
-
-```rust
-<code>
-```
-
-I expected to see this happen: *explanation*
-
-Instead, this happened: *explanation*
-
-### Meta
-
-**Rust version (`rustc -Vv`):**
-
-```
-rustc 1.46.0-nightly (f455e46ea 2020-06-20)
-binary: rustc
-commit-hash: f455e46eae1a227d735091091144601b467e1565
-commit-date: 2020-06-20
-host: x86_64-unknown-linux-gnu
-release: 1.46.0-nightly
-LLVM version: 10.0
-```
--- /dev/null
+name: Bug Report (False Negative)
+description: Create a bug report about missing warnings from a lint
+labels: ["C-bug", "I-false-negative"]
+body:
+ - type: markdown
+ attributes:
+ value: Thank you for filing a bug report! 🐛
+ - type: textarea
+ id: problem
+ attributes:
+ label: Summary
+ description: >
+ Please provide a short summary of the bug, along with any information
+ you feel relevant to replicate the bug.
+ validations:
+ required: true
+ - type: input
+ id: lint-name
+ attributes:
+ label: Lint Name
+ description: Please provide the lint name.
+ - type: textarea
+ id: reproducer
+ attributes:
+ label: Reproducer
+ description: Please provide the code and steps to repoduce the bug
+ value: |
+ I tried this code:
+
+ ```rust
+ <code>
+ ```
+
+ I expected to see this happen:
+
+ Instead, this happened:
+ - type: textarea
+ id: version
+ attributes:
+ label: Version
+ description: "Rust version (`rustc -Vv`)"
+ placeholder: |
+ rustc 1.46.0-nightly (f455e46ea 2020-06-20)
+ binary: rustc
+ commit-hash: f455e46eae1a227d735091091144601b467e1565
+ commit-date: 2020-06-20
+ host: x86_64-unknown-linux-gnu
+ release: 1.46.0-nightly
+ LLVM version: 10.0
+ render: text
+++ /dev/null
----
-name: Bug Report (False Positive)
-about: Create a bug report about a wrongly emitted lint warning
-labels: C-bug, I-false-positive
----
-<!--
-Thank you for filing a bug report! 🐛 Please provide a short summary of the bug,
-along with any information you feel relevant to replicating the bug.
--->
-Lint name:
-
-
-I tried this code:
-
-```rust
-<code>
-```
-
-I expected to see this happen: *explanation*
-
-Instead, this happened: *explanation*
-
-### Meta
-
-**Rust version (`rustc -Vv`):**
-```
-rustc 1.46.0-nightly (f455e46ea 2020-06-20)
-binary: rustc
-commit-hash: f455e46eae1a227d735091091144601b467e1565
-commit-date: 2020-06-20
-host: x86_64-unknown-linux-gnu
-release: 1.46.0-nightly
-LLVM version: 10.0
-```
-
-<!--
-Additional labels can be added to this issue by including the following command
-(without the space after the @ symbol):
-
-@ rustbot label +<label>
-
-Common labels for this issue type are:
-* I-suggestion-causes-error
--->
--- /dev/null
+name: Bug Report (False Positive)
+description: Create a bug report about a wrongly emitted lint warning
+labels: ["C-bug", "I-false-positive"]
+body:
+ - type: markdown
+ attributes:
+ value: Thank you for filing a bug report! 🐛
+ - type: textarea
+ id: problem
+ attributes:
+ label: Summary
+ description: >
+ Please provide a short summary of the bug, along with any information
+ you feel relevant to replicate the bug.
+ validations:
+ required: true
+ - type: input
+ id: lint-name
+ attributes:
+ label: Lint Name
+ description: Please provide the lint name.
+ - type: textarea
+ id: reproducer
+ attributes:
+ label: Reproducer
+ description: >
+ Please provide the code and steps to repoduce the bug together with the
+ output from Clippy.
+ value: |
+ I tried this code:
+
+ ```rust
+ <code>
+ ```
+
+ I saw this happen:
+
+ ```
+ <output>
+ ```
+
+ I expected to see this happen:
+ - type: textarea
+ id: version
+ attributes:
+ label: Version
+ description: "Rust version (`rustc -Vv`)"
+ placeholder: |
+ rustc 1.46.0-nightly (f455e46ea 2020-06-20)
+ binary: rustc
+ commit-hash: f455e46eae1a227d735091091144601b467e1565
+ commit-date: 2020-06-20
+ host: x86_64-unknown-linux-gnu
+ release: 1.46.0-nightly
+ LLVM version: 10.0
+ render: text
+ - type: textarea
+ id: labels
+ attributes:
+ label: Additional Labels
+ description: >
+ Additional labels can be added to this issue by including the following
+ command
+ placeholder: |
+ @rustbot label +<label>
+
+ Common labels for this issue type are:
+ * `I-suggestion-causes-error`
+++ /dev/null
----
-name: Internal Compiler Error
-about: Create a report for an internal compiler error in Clippy.
-labels: C-bug, I-ICE
----
-<!--
-Thank you for finding an Internal Compiler Error! 🧊 If possible, try to provide
-a minimal verifiable example. You can read "Rust Bug Minimization Patterns" for
-how to create smaller examples.
-
-http://blog.pnkfx.org/blog/2019/11/18/rust-bug-minimization-patterns/
-
--->
-
-### Code
-
-```rust
-<code>
-```
-
-### Meta
-
-**Rust version (`rustc -Vv`):**
-```
-rustc 1.46.0-nightly (f455e46ea 2020-06-20)
-binary: rustc
-commit-hash: f455e46eae1a227d735091091144601b467e1565
-commit-date: 2020-06-20
-host: x86_64-unknown-linux-gnu
-release: 1.46.0-nightly
-LLVM version: 10.0
-```
-
-### Error output
-
-```
-<output>
-```
-
-<!--
-Include a backtrace in the code block by setting `RUST_BACKTRACE=1` in your
-environment. E.g. `RUST_BACKTRACE=1 cargo clippy`.
--->
-<details><summary>Backtrace</summary>
- <p>
-
- ```
- <backtrace>
- ```
-
- </p>
-</details>
--- /dev/null
+name: Internal Compiler Error
+description: Create a report for an internal compiler error (ICE) in Clippy.
+labels: ["C-bug", "I-ICE"]
+body:
+ - type: markdown
+ attributes:
+ value: Thank you for finding an Internal Compiler Error! 🧊
+ - type: textarea
+ id: problem
+ attributes:
+ label: Summary
+ description: |
+ If possible, try to provide a minimal verifiable example. You can read ["Rust Bug Minimization Patterns"][mve] for how to create smaller examples. Otherwise, provide the crate where the ICE occured.
+
+ [mve]: http://blog.pnkfx.org/blog/2019/11/18/rust-bug-minimization-patterns/
+ validations:
+ required: true
+ - type: textarea
+ id: version
+ attributes:
+ label: Version
+ description: "Rust version (`rustc -Vv`)"
+ placeholder: |
+ rustc 1.46.0-nightly (f455e46ea 2020-06-20)
+ binary: rustc
+ commit-hash: f455e46eae1a227d735091091144601b467e1565
+ commit-date: 2020-06-20
+ host: x86_64-unknown-linux-gnu
+ release: 1.46.0-nightly
+ LLVM version: 10.0
+ render: text
+ - type: textarea
+ id: error
+ attributes:
+ label: Error output
+ description: >
+ Include a backtrace in the code block by setting `RUST_BACKTRACE=1` in
+ your environment. E.g. `RUST_BACKTRACE=1 cargo clippy`.
+ value: |
+ <details><summary>Backtrace</summary>
+ <p>
+
+ ```
+ <backtrace>
+ ```
+
+ </p>
+ </details>
+++ /dev/null
----
-name: New lint suggestion
-about: Suggest a new Clippy lint.
-labels: A-lint
----
-
-### What it does
-
-*What does this lint do?*
-
-### Categories (optional)
-
-- Kind: *See <https://github.com/rust-lang/rust-clippy/blob/master/README.md#clippy> for list of lint kinds*
-
-*What is the advantage of the recommended code over the original code*
-
-For example:
-- Remove bounds check inserted by ...
-- Remove the need to duplicate/store ...
-- Remove typo ...
-
-### Drawbacks
-
-None.
-
-### Example
-
-```rust
-<code>
-```
-
-Could be written as:
-
-```rust
-<code>
-```
--- /dev/null
+name: New lint suggestion
+description: Suggest a new Clippy lint.
+labels: ["A-lint"]
+body:
+ - type: markdown
+ attributes:
+ value: Thank you for your lint idea!
+ - type: textarea
+ id: what
+ attributes:
+ label: What it does
+ description: What does this lint do?
+ validations:
+ required: true
+ - type: input
+ id: lint-name
+ attributes:
+ label: Lint Name
+ description: Please provide the lint name.
+ - type: dropdown
+ id: category
+ attributes:
+ label: Category
+ description: >
+ What category should this lint go into? If you're unsure you can select
+ multiple categories. You can find a category description in the
+ `README`.
+ multiple: true
+ options:
+ - correctness
+ - suspicious
+ - style
+ - complexity
+ - perf
+ - pedantic
+ - restriction
+ - cargo
+ - type: textarea
+ id: advantage
+ attributes:
+ label: Advantage
+ description: >
+ What is the advantage of the recommended code over the original code?
+ placeholder: |
+ - Remove bounds check inserted by ...
+ - Remove the need to duplicate/store ...
+ - Remove typo ...
+ - type: textarea
+ id: drawbacks
+ attributes:
+ label: Drawbacks
+ description: What might be possible drawbacks of such a lint?
+ - type: textarea
+ id: example
+ attributes:
+ label: Example
+ description: >
+ Include a short example showing when the lint should trigger together
+ with the improved code.
+ value: |
+ ```rust
+ <code>
+ ```
+
+ Could be written as:
+
+ ```rust
+ <code>
+ ```
+ validations:
+ required: true
- name: Checkout
uses: actions/checkout@v2.3.3
- - name: remove toolchain file
- run: rm rust-toolchain
-
- - name: rust-toolchain
- uses: actions-rs/toolchain@v1.0.6
- with:
- toolchain: nightly
- target: x86_64-unknown-linux-gnu
- profile: minimal
- components: rustfmt
- default: true
-
# Run
- name: Build
run: cargo build --features deny-warnings
[#7560](https://github.com/rust-lang/rust-clippy/pull/7560)
* [`unnecessary_unwrap`]: Now also checks for `expect`s
[#7584](https://github.com/rust-lang/rust-clippy/pull/7584)
-* [`disallowed_method`]: Allow adding a reason that will be displayed with the
+* [`disallowed_methods`]: Allow adding a reason that will be displayed with the
lint message
[#7621](https://github.com/rust-lang/rust-clippy/pull/7621)
* [`approx_constant`]: Now checks the MSRV for `LOG10_2` and `LOG2_10`
* [`needless_continue`]: Now also lints in `loop { continue; }` case
[#7477](https://github.com/rust-lang/rust-clippy/pull/7477)
-* [`disallowed_type`]: Now also primitive types can be disallowed
+* [`disallowed_types`]: Now also primitive types can be disallowed
[#7488](https://github.com/rust-lang/rust-clippy/pull/7488)
* [`manual_swap`]: Now also lints on xor swaps
[#7506](https://github.com/rust-lang/rust-clippy/pull/7506)
[#7403](https://github.com/rust-lang/rust-clippy/pull/7403)
* [`disallowed_script_idents`]
[#7400](https://github.com/rust-lang/rust-clippy/pull/7400)
-* [`disallowed_type`]
+* [`disallowed_types`]
[#7315](https://github.com/rust-lang/rust-clippy/pull/7315)
* [`missing_enforced_import_renames`]
[#7300](https://github.com/rust-lang/rust-clippy/pull/7300)
[#7379](https://github.com/rust-lang/rust-clippy/pull/7379)
* [`redundant_closure`]: Suggests `&mut` for `FnMut`
[#7437](https://github.com/rust-lang/rust-clippy/pull/7437)
-* [`disallowed_method`], [`disallowed_type`]: The configuration values `disallowed-method` and `disallowed-type`
+* [`disallowed_methods`], [`disallowed_types`]: The configuration values `disallowed-method` and `disallowed-type`
no longer require fully qualified paths
[#7345](https://github.com/rust-lang/rust-clippy/pull/7345)
* [`zst_offset`]: Fixed lint invocation after it was accidentally suppressed
### Enhancements
-* [`disallowed_method`]: Now supports functions in addition to methods
+* [`disallowed_methods`]: Now supports functions in addition to methods
[#6674](https://github.com/rust-lang/rust-clippy/pull/6674)
* [`upper_case_acronyms`]: Added a new configuration `upper-case-acronyms-aggressive` to
trigger the lint if there is more than one uppercase character next to each other
* [`field_reassign_with_default`] [#5911](https://github.com/rust-lang/rust-clippy/pull/5911)
* [`await_holding_refcell_ref`] [#6029](https://github.com/rust-lang/rust-clippy/pull/6029)
-* [`disallowed_method`] [#6081](https://github.com/rust-lang/rust-clippy/pull/6081)
+* [`disallowed_methods`] [#6081](https://github.com/rust-lang/rust-clippy/pull/6081)
* [`inline_asm_x86_att_syntax`] [#6092](https://github.com/rust-lang/rust-clippy/pull/6092)
* [`inline_asm_x86_intel_syntax`] [#6092](https://github.com/rust-lang/rust-clippy/pull/6092)
* [`from_iter_instead_of_collect`] [#6101](https://github.com/rust-lang/rust-clippy/pull/6101)
[`derivable_impls`]: https://rust-lang.github.io/rust-clippy/master/index.html#derivable_impls
[`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq
[`derive_ord_xor_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_ord_xor_partial_ord
-[`disallowed_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_method
+[`disallowed_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_methods
[`disallowed_script_idents`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_script_idents
-[`disallowed_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_type
+[`disallowed_types`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types
[`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression
[`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown
[`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons
[`imprecise_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#imprecise_flops
[`inconsistent_digit_grouping`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_digit_grouping
[`inconsistent_struct_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_struct_constructor
+[`index_refutable_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice
[`indexing_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#indexing_slicing
[`ineffective_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#ineffective_bit_mask
[`inefficient_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inefficient_to_string
[`needless_continue`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_continue
[`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main
[`needless_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_for_each
+[`needless_late_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_late_init
[`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes
[`needless_option_as_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_as_deref
[`needless_pass_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value
[`needless_question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_question_mark
[`needless_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_range_loop
[`needless_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_return
+[`needless_splitn`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_splitn
[`needless_update`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_update
[`neg_cmp_op_on_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#neg_cmp_op_on_partial_ord
[`neg_multiply`]: https://rust-lang.github.io/rust-clippy/master/index.html#neg_multiply
[`nonsensical_open_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonsensical_open_options
[`nonstandard_macro_braces`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonstandard_macro_braces
[`not_unsafe_ptr_arg_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#not_unsafe_ptr_arg_deref
+[`octal_escapes`]: https://rust-lang.github.io/rust-clippy/master/index.html#octal_escapes
[`ok_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#ok_expect
[`op_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#op_ref
[`option_as_ref_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref
[package]
name = "clippy"
-version = "0.1.58"
+version = "0.1.59"
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
repository = "https://github.com/rust-lang/rust-clippy"
readme = "README.md"
quote = "1.0"
serde = { version = "1.0", features = ["derive"] }
syn = { version = "1.0", features = ["full"] }
+parking_lot = "0.11.2"
[build-dependencies]
rustc_tools_util = { version = "0.2", path = "rustc_tools_util" }
regex = "1.5"
shell-escape = "0.1"
walkdir = "2.3"
+cargo_metadata = "0.14"
[features]
deny-warnings = []
use crate::clippy_project_root;
+use itertools::Itertools;
use shell_escape::escape;
-use std::ffi::OsStr;
+use std::ffi::{OsStr, OsString};
use std::path::Path;
use std::process::{self, Command};
use std::{fs, io};
success &= cargo_fmt(context, &project_root.join("rustc_tools_util"))?;
success &= cargo_fmt(context, &project_root.join("lintcheck"))?;
- for entry in WalkDir::new(project_root.join("tests")) {
- let entry = entry?;
- let path = entry.path();
-
- if path.extension() != Some("rs".as_ref()) || entry.file_name() == "ice-3891.rs" {
- continue;
- }
-
- success &= rustfmt(context, path)?;
+ let chunks = WalkDir::new(project_root.join("tests"))
+ .into_iter()
+ .filter_map(|entry| {
+ let entry = entry.expect("failed to find tests");
+ let path = entry.path();
+
+ if path.extension() != Some("rs".as_ref()) || entry.file_name() == "ice-3891.rs" {
+ None
+ } else {
+ Some(entry.into_path().into_os_string())
+ }
+ })
+ .chunks(250);
+
+ for chunk in &chunks {
+ success &= rustfmt(context, chunk)?;
}
Ok(success)
}
fn cargo_fmt(context: &FmtContext, path: &Path) -> Result<bool, CliError> {
- let mut args = vec!["+nightly", "fmt", "--all"];
+ let mut args = vec!["fmt", "--all"];
if context.check {
args.push("--");
args.push("--check");
fn rustfmt_test(context: &FmtContext) -> Result<(), CliError> {
let program = "rustfmt";
let dir = std::env::current_dir()?;
- let args = &["+nightly", "--version"];
+ let args = &["--version"];
if context.verbose {
println!("{}", format_command(&program, &dir, args));
}
}
-fn rustfmt(context: &FmtContext, path: &Path) -> Result<bool, CliError> {
- let mut args = vec!["+nightly".as_ref(), path.as_os_str()];
+fn rustfmt(context: &FmtContext, paths: impl Iterator<Item = OsString>) -> Result<bool, CliError> {
+ let mut args = Vec::new();
if context.check {
- args.push("--check".as_ref());
+ args.push(OsString::from("--check"));
}
+ args.extend(paths);
+
let success = exec(context, "rustfmt", std::env::current_dir()?, &args)?;
- if !success {
- eprintln!("rustfmt failed on {}", path.display());
- }
+
Ok(success)
}
pub mod bless;
pub mod fmt;
+pub mod lint;
pub mod new_lint;
pub mod serve;
pub mod setup;
--- /dev/null
+use std::process::{self, Command};
+
+pub fn run(filename: &str) {
+ let code = Command::new("cargo")
+ .args(["run", "--bin", "clippy-driver", "--"])
+ .args(["-L", "./target/debug"])
+ .args(["-Z", "no-codegen"])
+ .args(["--edition", "2021"])
+ .arg(filename)
+ .env("__CLIPPY_INTERNAL_TESTS", "true")
+ .status()
+ .expect("failed to run cargo")
+ .code();
+
+ if code.is_none() {
+ eprintln!("Killed by signal");
+ }
+
+ process::exit(code.unwrap_or(1));
+}
#![warn(rust_2018_idioms, unused_lifetimes)]
use clap::{App, AppSettings, Arg, ArgMatches, SubCommand};
-use clippy_dev::{bless, fmt, new_lint, serve, setup, update_lints};
+use clippy_dev::{bless, fmt, lint, new_lint, serve, setup, update_lints};
fn main() {
let matches = get_clap_config();
let lint = matches.value_of("lint");
serve::run(port, lint);
},
+ ("lint", Some(matches)) => {
+ let filename = matches.value_of("filename").unwrap();
+ lint::run(filename);
+ },
_ => {},
}
}
)
.arg(Arg::with_name("lint").help("Which lint's page to load initially (optional)")),
)
+ .subcommand(
+ SubCommand::with_name("lint")
+ .about("Manually run clippy on a file")
+ .arg(
+ Arg::with_name("filename")
+ .required(true)
+ .help("The path to a file to lint"),
+ ),
+ )
.get_matches()
}
.collect()
}
+fn get_stabilisation_version() -> String {
+ let mut command = cargo_metadata::MetadataCommand::new();
+ command.no_deps();
+ if let Ok(metadata) = command.exec() {
+ if let Some(pkg) = metadata.packages.iter().find(|pkg| pkg.name == "clippy") {
+ return format!("{}.{}.0", pkg.version.minor, pkg.version.patch);
+ }
+ }
+
+ String::from("<TODO set version(see doc/adding_lints.md)>")
+}
+
fn get_test_file_contents(lint_name: &str, header_commands: Option<&str>) -> String {
let mut contents = format!(
indoc! {"
},
};
+ let version = get_stabilisation_version();
let lint_name = lint.name;
let category = lint.category;
let name_camel = to_camel_case(lint.name);
});
result.push_str(&format!(
- indoc! {"
+ indoc! {r#"
declare_clippy_lint! {{
/// ### What it does
///
/// ```rust
/// // example code which does not raise clippy warning
/// ```
+ #[clippy::version = "{version}"]
pub {name_upper},
{category},
- \"default lint description\"
+ "default lint description"
}}
- "},
+ "#},
+ version = version,
name_upper = name_upper,
category = category,
));
r#"(?x)
declare_clippy_lint!\s*[\{(]
(?:\s+///.*)*
+ (?:\s*\#\[clippy::version\s*=\s*"[^"]*"\])?
\s+pub\s+(?P<name>[A-Z_][A-Z_0-9]*)\s*,\s*
(?P<cat>[a-z_]+)\s*,\s*
"(?P<desc>(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})]
r#"(?x)
declare_deprecated_lint!\s*[{(]\s*
(?:\s+///.*)*
+ (?:\s*\#\[clippy::version\s*=\s*"[^"]*"\])?
\s+pub\s+(?P<name>[A-Z_][A-Z_0-9]*)\s*,\s*
"(?P<desc>(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})]
"#,
let result: Vec<Lint> = parse_contents(
r#"
declare_clippy_lint! {
+ #[clippy::version = "Hello Clippy!"]
pub PTR_ARG,
style,
"really long \
}
declare_clippy_lint!{
+ #[clippy::version = "Test version"]
pub DOC_MARKDOWN,
pedantic,
"single line"
/// some doc comment
declare_deprecated_lint! {
+ #[clippy::version = "I'm a version"]
pub SHOULD_ASSERT_EQ,
"`assert!()` will be more flexible with RFC 2011"
}
[package]
name = "clippy_lints"
-version = "0.1.58"
+version = "0.1.59"
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
repository = "https://github.com/rust-lang/rust-clippy"
readme = "README.md"
/// if vec.len() <= 0 {}
/// if 100 > i32::MAX {}
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub ABSURD_EXTREME_COMPARISONS,
correctness,
"a comparison with a maximum or minimum value that is always true or false"
/// let x = std::f32::consts::PI;
/// let y = std::f64::consts::FRAC_1_PI;
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub APPROX_CONSTANT,
correctness,
"the approximate of a known float constant (in `std::fXX::consts`)"
/// # let a = 0;
/// a + 1;
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub INTEGER_ARITHMETIC,
restriction,
"any integer arithmetic expression which could overflow or panic"
/// # let a = 0.0;
/// a + 1.0;
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub FLOAT_ARITHMETIC,
restriction,
"any floating-point arithmetic statement"
/// f(a.try_into().expect("Unexpected u16 overflow in f"));
/// ```
///
+ #[clippy::version = "1.41.0"]
pub AS_CONVERSIONS,
restriction,
"using a potentially dangerous silent `as` conversion"
/// asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax));
/// # }
/// ```
+ #[clippy::version = "1.49.0"]
pub INLINE_ASM_X86_INTEL_SYNTAX,
restriction,
"prefer AT&T x86 assembly syntax"
/// asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr);
/// # }
/// ```
+ #[clippy::version = "1.49.0"]
pub INLINE_ASM_X86_ATT_SYNTAX,
restriction,
"prefer Intel x86 assembly syntax"
/// const B: bool = false;
/// assert!(B)
/// ```
+ #[clippy::version = "1.34.0"]
pub ASSERTIONS_ON_CONSTANTS,
style,
"`assert!(true)` / `assert!(false)` will be optimized out by the compiler, and should probably be replaced by a `panic!()` or `unreachable!()`"
/// // Good
/// a += b;
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub ASSIGN_OP_PATTERN,
style,
"assigning the result of an operation on a variable to that same variable"
/// // ...
/// a += a + b;
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub MISREFACTORED_ASSIGN_OP,
suspicious,
"having a variable on both sides of an assign op"
/// };
/// }
/// ```
+ #[clippy::version = "1.48.0"]
pub ASYNC_YIELDS_ASYNC,
correctness,
"async blocks that return a type that can be awaited"
//! checks for attributes
use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::match_panic_def_id;
+use clippy_utils::msrvs;
use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments};
+use clippy_utils::{extract_msrv_attr, match_panic_def_id, meets_msrv};
use if_chain::if_chain;
use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem};
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty;
-use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_semver::RustcVersion;
+use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::Span;
use rustc_span::sym;
use rustc_span::symbol::{Symbol, SymbolStr};
/// #[inline(always)]
/// fn not_quite_hot_code(..) { ... }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub INLINE_ALWAYS,
pedantic,
"use of `#[inline(always)]`"
/// #[macro_use]
/// extern crate baz;
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub USELESS_ATTRIBUTE,
correctness,
"use of lint attributes on `extern crate` items"
/// #[deprecated(since = "forever")]
/// fn something_else() { /* ... */ }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub DEPRECATED_SEMVER,
correctness,
"use of `#[deprecated(since = \"x\")]` where x is not semver"
/// #[allow(dead_code)]
/// fn this_is_fine_too() { }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub EMPTY_LINE_AFTER_OUTER_ATTR,
nursery,
"empty line after outer attribute"
/// ```rust
/// #![deny(clippy::as_conversions)]
/// ```
+ #[clippy::version = "1.47.0"]
pub BLANKET_CLIPPY_RESTRICTION_LINTS,
suspicious,
"enabling the complete restriction group"
/// #[rustfmt::skip]
/// fn main() { }
/// ```
+ #[clippy::version = "1.32.0"]
pub DEPRECATED_CFG_ATTR,
complexity,
"usage of `cfg_attr(rustfmt)` instead of tool attributes"
/// fn conditional() { }
/// ```
/// Check the [Rust Reference](https://doc.rust-lang.org/reference/conditional-compilation.html#target_os) for more details.
+ #[clippy::version = "1.45.0"]
pub MISMATCHED_TARGET_OS,
correctness,
"usage of `cfg(operating_system)` instead of `cfg(target_os = \"operating_system\")`"
}
}
-declare_lint_pass!(EarlyAttributes => [
+pub struct EarlyAttributes {
+ pub msrv: Option<RustcVersion>,
+}
+
+impl_lint_pass!(EarlyAttributes => [
DEPRECATED_CFG_ATTR,
MISMATCHED_TARGET_OS,
EMPTY_LINE_AFTER_OUTER_ATTR,
}
fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {
- check_deprecated_cfg_attr(cx, attr);
+ check_deprecated_cfg_attr(cx, attr, self.msrv);
check_mismatched_target_os(cx, attr);
}
+
+ extract_msrv_attr!(EarlyContext);
}
fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::Item) {
}
}
-fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute) {
+fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: Option<RustcVersion>) {
if_chain! {
+ if meets_msrv(msrv.as_ref(), &msrvs::TOOL_ATTRIBUTES);
// check cfg_attr
if attr.has_name(sym::cfg_attr);
if let Some(items) = attr.meta_item_list();
/// bar.await;
/// }
/// ```
+ #[clippy::version = "1.45.0"]
pub AWAIT_HOLDING_LOCK,
pedantic,
"Inside an async function, holding a MutexGuard while calling await"
/// bar.await;
/// }
/// ```
+ #[clippy::version = "1.49.0"]
pub AWAIT_HOLDING_REFCELL_REF,
pedantic,
"Inside an async function, holding a RefCell ref while calling await"
/// # let x = 1;
/// if (x & 1 == 2) { }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub BAD_BIT_MASK,
correctness,
"expressions of the form `_ & mask == select` that will only ever return `true` or `false`"
/// # let x = 1;
/// if (x | 1 > 3) { }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub INEFFECTIVE_BIT_MASK,
correctness,
"expressions where a bit mask will be rendered useless by a comparison, e.g., `(x | 1) > 2`"
/// # let x = 1;
/// if x & 0b1111 == 0 { }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub VERBOSE_BIT_MASK,
pedantic,
"expressions where a bit mask is less readable than the corresponding method call"
/// ```rust
/// let foo = 3.14;
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub BLACKLISTED_NAME,
style,
"usage of a blacklisted/placeholder name"
/// let res = { let x = somefunc(); x };
/// if res { /* ... */ }
/// ```
+ #[clippy::version = "1.45.0"]
pub BLOCKS_IN_IF_CONDITIONS,
style,
"useless or complex blocks that can be eliminated in conditions"
/// // Good
/// assert!(!"a".is_empty());
/// ```
+ #[clippy::version = "1.53.0"]
pub BOOL_ASSERT_COMPARISON,
style,
"Using a boolean as comparison value in an assert_* macro when there is no need"
if let Some(span) = is_direct_expn_of(expr.span, mac) {
if let Some(args) = higher::extract_assert_macro_args(expr) {
if let [a, b, ..] = args[..] {
- let nb_bool_args = is_bool_lit(a) as usize + is_bool_lit(b) as usize;
+ let nb_bool_args = usize::from(is_bool_lit(a)) + usize::from(is_bool_lit(b));
if nb_bool_args != 1 {
// If there are two boolean arguments, we definitely don't understand
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
-use clippy_utils::{eq_expr_value, get_trait_def_id, in_macro, paths};
+use clippy_utils::{eq_expr_value, get_trait_def_id, paths};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
/// if a && true // should be: if a
/// if !(a == b) // should be: if a != b
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub NONMINIMAL_BOOL,
complexity,
"boolean expressions that can be written more concisely"
/// if a && b || a { ... }
/// ```
/// The `b` is unnecessary, the expression is equivalent to `if a`.
+ #[clippy::version = "pre 1.29.0"]
pub LOGIC_BUG,
correctness,
"boolean expressions that contain terminals which can be eliminated"
type Map = Map<'tcx>;
fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
- if in_macro(e.span) {
- return;
- }
- match &e.kind {
- ExprKind::Binary(binop, _, _) if binop.node == BinOpKind::Or || binop.node == BinOpKind::And => {
- self.bool_expr(e);
- },
- ExprKind::Unary(UnOp::Not, inner) => {
- if self.cx.typeck_results().node_types()[inner.hir_id].is_bool() {
+ if !e.span.from_expansion() {
+ match &e.kind {
+ ExprKind::Binary(binop, _, _) if binop.node == BinOpKind::Or || binop.node == BinOpKind::And => {
self.bool_expr(e);
- } else {
- walk_expr(self, e);
- }
- },
- _ => walk_expr(self, e),
+ },
+ ExprKind::Unary(UnOp::Not, inner) => {
+ if self.cx.typeck_results().node_types()[inner.hir_id].is_bool() {
+ self.bool_expr(e);
+ }
+ },
+ _ => {},
+ }
}
+ walk_expr(self, e);
}
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
NestedVisitorMap::None
/// # let vec = vec![1_u8];
/// &vec.iter().filter(|x| **x == 0u8).count(); // use bytecount::count instead
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub NAIVE_BYTECOUNT,
pedantic,
"use of naive `<slice>.filter(|&x| x == y).count()` to count byte values"
if (p == sym::iter || p == sym!(iter_mut)) && args.len() == 1 {
&args[0]
} else {
- &filter_recv
+ filter_recv
}
} else {
- &filter_recv
+ filter_recv
};
let mut applicability = Applicability::MaybeIncorrect;
span_lint_and_sugg(
/// keywords = ["clippy", "lint", "plugin"]
/// categories = ["development-tools", "development-tools::cargo-plugins"]
/// ```
+ #[clippy::version = "1.32.0"]
pub CARGO_COMMON_METADATA,
cargo,
"common metadata is defined in `Cargo.toml`"
/// filename.rsplit('.').next().map(|ext| ext.eq_ignore_ascii_case("rs")) == Some(true)
/// }
/// ```
+ #[clippy::version = "1.51.0"]
pub CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
pedantic,
"Checks for calls to ends_with with case-sensitive file extensions"
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::in_constant;
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::is_isize_or_usize;
+use clippy_utils::{in_constant, meets_msrv, msrvs};
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::LateContext;
use rustc_middle::ty::{self, FloatTy, Ty};
+use rustc_semver::RustcVersion;
use super::{utils, CAST_LOSSLESS};
-pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_op: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
- if !should_lint(cx, expr, cast_from, cast_to) {
+pub(super) fn check(
+ cx: &LateContext<'_>,
+ expr: &Expr<'_>,
+ cast_op: &Expr<'_>,
+ cast_from: Ty<'_>,
+ cast_to: Ty<'_>,
+ msrv: &Option<RustcVersion>,
+) {
+ if !should_lint(cx, expr, cast_from, cast_to, msrv) {
return;
}
},
);
+ let message = if cast_from.is_bool() {
+ format!(
+ "casting `{0:}` to `{1:}` is more cleanly stated with `{1:}::from(_)`",
+ cast_from, cast_to
+ )
+ } else {
+ format!(
+ "casting `{}` to `{}` may become silently lossy if you later change the type",
+ cast_from, cast_to
+ )
+ };
+
span_lint_and_sugg(
cx,
CAST_LOSSLESS,
expr.span,
- &format!(
- "casting `{}` to `{}` may become silently lossy if you later change the type",
- cast_from, cast_to
- ),
+ &message,
"try",
format!("{}::from({})", cast_to, sugg),
applicability,
);
}
-fn should_lint(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) -> bool {
+fn should_lint(
+ cx: &LateContext<'_>,
+ expr: &Expr<'_>,
+ cast_from: Ty<'_>,
+ cast_to: Ty<'_>,
+ msrv: &Option<RustcVersion>,
+) -> bool {
// Do not suggest using From in consts/statics until it is valid to do so (see #2267).
if in_constant(cx, expr.hir_id) {
return false;
};
from_nbits < to_nbits
},
-
+ (false, true) if matches!(cast_from.kind(), ty::Bool) && meets_msrv(msrv.as_ref(), &msrvs::FROM_BOOL) => true,
(_, _) => {
matches!(cast_from.kind(), ty::Float(FloatTy::F32)) && matches!(cast_to.kind(), ty::Float(FloatTy::F64))
},
/// let x = u64::MAX;
/// x as f64;
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub CAST_PRECISION_LOSS,
pedantic,
"casts that cause loss of precision, e.g., `x as f32` where `x: u64`"
/// let y: i8 = -1;
/// y as u128; // will return 18446744073709551615
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub CAST_SIGN_LOSS,
pedantic,
"casts from signed types to unsigned types, e.g., `x as u32` where `x: i32`"
/// x as u8
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub CAST_POSSIBLE_TRUNCATION,
pedantic,
"casts that may cause truncation of the value, e.g., `x as u8` where `x: u32`, or `x as i32` where `x: f32`"
/// ```rust
/// u32::MAX as i32; // will yield a value of `-1`
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub CAST_POSSIBLE_WRAP,
pedantic,
"casts that may cause wrapping around the value, e.g., `x as i32` where `x: u32` and `x > i32::MAX`"
/// u64::from(x)
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub CAST_LOSSLESS,
pedantic,
"casts using `as` that are known to be lossless, e.g., `x as u64` where `x: u8`"
/// let _ = 2_i32;
/// let _ = 0.5_f32;
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub UNNECESSARY_CAST,
complexity,
"cast to the same type, e.g., `x as i32` where `x: i32`"
/// (&1u8 as *const u8).cast::<u16>();
/// (&mut 1u8 as *mut u8).cast::<u16>();
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub CAST_PTR_ALIGNMENT,
pedantic,
"cast from a pointer to a more-strictly-aligned pointer"
/// fn fun2() -> i32 { 1 }
/// let a = fun2 as usize;
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub FN_TO_NUMERIC_CAST,
style,
"casting a function pointer to a numeric type other than usize"
/// let fn_ptr = fn2 as usize;
/// let fn_ptr_truncated = fn_ptr as i32;
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
style,
"casting a function pointer to a numeric type not wide enough to store the address"
/// }
/// let _ = fn3 as fn() -> u16;
/// ```
+ #[clippy::version = "1.58.0"]
pub FN_TO_NUMERIC_CAST_ANY,
restriction,
"casting a function pointer to any integer type"
/// }
/// }
/// ```
+ #[clippy::version = "1.33.0"]
pub CAST_REF_TO_MUT,
correctness,
"a cast of reference to a mutable pointer"
/// ```rust,ignore
/// b'x'
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub CHAR_LIT_AS_U8,
complexity,
"casting a character literal to `u8` truncates"
/// let _ = ptr.cast::<i32>();
/// let _ = mut_ptr.cast::<i32>();
/// ```
+ #[clippy::version = "1.51.0"]
pub PTR_AS_PTR,
pedantic,
"casting using `as` from and to raw pointers that doesn't change its mutability, where `pointer::cast` could take the place of `as`"
fn_to_numeric_cast_any::check(cx, expr, cast_expr, cast_from, cast_to);
fn_to_numeric_cast::check(cx, expr, cast_expr, cast_from, cast_to);
fn_to_numeric_cast_with_truncation::check(cx, expr, cast_expr, cast_from, cast_to);
- if cast_from.is_numeric() && cast_to.is_numeric() && !in_external_macro(cx.sess(), expr.span) {
- cast_possible_truncation::check(cx, expr, cast_expr, cast_from, cast_to);
- cast_possible_wrap::check(cx, expr, cast_from, cast_to);
- cast_precision_loss::check(cx, expr, cast_from, cast_to);
- cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to);
- cast_sign_loss::check(cx, expr, cast_expr, cast_from, cast_to);
+
+ if cast_to.is_numeric() && !in_external_macro(cx.sess(), expr.span) {
+ if cast_from.is_numeric() {
+ cast_possible_truncation::check(cx, expr, cast_expr, cast_from, cast_to);
+ cast_possible_wrap::check(cx, expr, cast_from, cast_to);
+ cast_precision_loss::check(cx, expr, cast_from, cast_to);
+ cast_sign_loss::check(cx, expr, cast_expr, cast_from, cast_to);
+ }
+
+ cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv);
}
}
/// i32::try_from(foo).is_ok()
/// # ;
/// ```
+ #[clippy::version = "1.37.0"]
pub CHECKED_CONVERSIONS,
pedantic,
"`try_from` could replace manual bounds checking when casting"
///
/// ### Example
/// No. You'll see it when you get the warning.
+ #[clippy::version = "1.35.0"]
pub COGNITIVE_COMPLEXITY,
nursery,
"functions that should be split up into multiple functions"
/// …
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub COLLAPSIBLE_IF,
style,
"nested `if`s that can be collapsed (e.g., `if x { if y { ... } }`"
/// …
/// }
/// ```
+ #[clippy::version = "1.51.0"]
pub COLLAPSIBLE_ELSE_IF,
style,
"nested `else`-`if` expressions that can be collapsed (e.g., `else { if x { ... } }`)"
/// };
/// }
/// ```
+ #[clippy::version = "1.50.0"]
pub COLLAPSIBLE_MATCH,
style,
"Nested `match` or `if let` expressions where the patterns may be \"collapsed\" together."
/// }
/// }
/// ```
+ #[clippy::version = "1.40.0"]
pub COMPARISON_CHAIN,
style,
"`if`s that can be rewritten with `match` and `cmp`"
use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then};
use clippy_utils::source::{first_line_of_span, indent_of, reindent_multiline, snippet, snippet_opt};
use clippy_utils::{
- both, count_eq, eq_expr_value, get_enclosing_block, get_parent_expr, if_sequence, in_macro, is_else_clause,
- is_lint_allowed, search_same, ContainsName, SpanlessEq, SpanlessHash,
+ both, count_eq, eq_expr_value, get_enclosing_block, get_parent_expr, if_sequence, is_else_clause, is_lint_allowed,
+ search_same, ContainsName, SpanlessEq, SpanlessHash,
};
use if_chain::if_chain;
use rustc_data_structures::fx::FxHashSet;
/// …
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub IFS_SAME_COND,
correctness,
"consecutive `if`s with the same condition"
/// }
/// }
/// ```
+ #[clippy::version = "1.41.0"]
pub SAME_FUNCTIONS_IN_IF_CONDITION,
pedantic,
"consecutive `if`s with the same function call"
/// 42
/// };
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub IF_SAME_THEN_ELSE,
correctness,
"`if` with the same `then` and `else` blocks"
/// 42
/// };
/// ```
+ #[clippy::version = "1.53.0"]
pub BRANCHES_SHARING_CODE,
nursery,
"`if` statement with shared code in all blocks"
let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool = &|&lhs, &rhs| -> bool {
// Do not lint if any expr originates from a macro
- if in_macro(lhs.span) || in_macro(rhs.span) {
+ if lhs.span.from_expansion() || rhs.span.from_expansion() {
return false;
}
// Do not spawn warning if `IFS_SAME_COND` already produced it.
/// let a: Vec<_> = my_iterator.take(1).collect();
/// let b: Vec<_> = my_iterator.collect();
/// ```
+ #[clippy::version = "1.30.0"]
pub COPY_ITERATOR,
pedantic,
"implementing `Iterator` on a `Copy` type"
/// ```rust
/// std::fs::create_dir_all("foo");
/// ```
+ #[clippy::version = "1.48.0"]
pub CREATE_DIR,
restriction,
"calling `std::fs::create_dir` instead of `std::fs::create_dir_all`"
/// `dbg!` macro is intended as a debugging tool. It
/// should not be in version control.
///
+ /// ### Known problems
+ /// * The lint level is unaffected by crate attributes. The level can still
+ /// be set for functions, modules and other items. To change the level for
+ /// the entire crate, please use command line flags. More information and a
+ /// configuration example can be found in [clippy#6610].
+ ///
+ /// [clippy#6610]: https://github.com/rust-lang/rust-clippy/issues/6610#issuecomment-977120558
+ ///
/// ### Example
/// ```rust,ignore
/// // Bad
/// // Good
/// true
/// ```
+ #[clippy::version = "1.34.0"]
pub DBG_MACRO,
restriction,
"`dbg!` macro is intended as a debugging tool"
use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_sugg};
use clippy_utils::source::snippet_with_macro_callsite;
use clippy_utils::ty::{has_drop, is_copy};
-use clippy_utils::{any_parent_is_automatically_derived, contains_name, in_macro, match_def_path, paths};
+use clippy_utils::{any_parent_is_automatically_derived, contains_name, match_def_path, paths};
use if_chain::if_chain;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
/// // Good
/// let s = String::default();
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub DEFAULT_TRAIT_ACCESS,
pedantic,
"checks for literal calls to `Default::default()`"
/// .. Default::default()
/// };
/// ```
+ #[clippy::version = "1.49.0"]
pub FIELD_REASSIGN_WITH_DEFAULT,
style,
"binding initialized with Default should have its fields set in the initializer"
impl LateLintPass<'_> for Default {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if_chain! {
- if !in_macro(expr.span);
+ if !expr.span.from_expansion();
// Avoid cases already linted by `field_reassign_with_default`
if !self.reassigned_linted.contains(&expr.span);
if let ExprKind::Call(path, ..) = expr.kind;
if let StmtKind::Local(local) = stmt.kind;
if let Some(expr) = local.init;
if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id);
- if !in_macro(expr.span);
+ if !expr.span.from_expansion();
// only take bindings to identifiers
if let PatKind::Binding(_, binding_id, ident, _) = local.pat.kind;
// only when assigning `... = Default::default()`
/// let i = 10i32;
/// let f = 1.23f64;
/// ```
+ #[clippy::version = "1.52.0"]
pub DEFAULT_NUMERIC_FALLBACK,
restriction,
"usage of unconstrained numeric literals which may cause default numeric fallback."
/// ### Deprecation reason
/// This used to check for `assert!(a == b)` and recommend
/// replacement with `assert_eq!(a, b)`, but this is no longer needed after RFC 2011.
+ #[clippy::version = "pre 1.29.0"]
pub SHOULD_ASSERT_EQ,
"`assert!()` will be more flexible with RFC 2011"
}
/// ### Deprecation reason
/// This used to check for `Vec::extend`, which was slower than
/// `Vec::extend_from_slice`. Thanks to specialization, this is no longer true.
+ #[clippy::version = "pre 1.29.0"]
pub EXTEND_FROM_SLICE,
"`.extend_from_slice(_)` is a faster way to extend a Vec by a slice"
}
/// an infinite iterator, which is better expressed by `iter::repeat`,
/// but the method has been removed for `Iterator::step_by` which panics
/// if given a zero
+ #[clippy::version = "pre 1.29.0"]
pub RANGE_STEP_BY_ZERO,
"`iterator.step_by(0)` panics nowadays"
}
/// ### Deprecation reason
/// This used to check for `Vec::as_slice`, which was unstable with good
/// stable alternatives. `Vec::as_slice` has now been stabilized.
+ #[clippy::version = "pre 1.29.0"]
pub UNSTABLE_AS_SLICE,
"`Vec::as_slice` has been stabilized in 1.7"
}
/// ### Deprecation reason
/// This used to check for `Vec::as_mut_slice`, which was unstable with good
/// stable alternatives. `Vec::as_mut_slice` has now been stabilized.
+ #[clippy::version = "pre 1.29.0"]
pub UNSTABLE_AS_MUT_SLICE,
"`Vec::as_mut_slice` has been stabilized in 1.7"
}
/// between non-pointer types of differing alignment is well-defined behavior (it's semantically
/// equivalent to a memcpy). This lint has thus been refactored into two separate lints:
/// cast_ptr_alignment and transmute_ptr_to_ptr.
+ #[clippy::version = "pre 1.29.0"]
pub MISALIGNED_TRANSMUTE,
"this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr"
}
/// This lint is too subjective, not having a good reason for being in clippy.
/// Additionally, compound assignment operators may be overloaded separately from their non-assigning
/// counterparts, so this lint may suggest a change in behavior or the code may not compile.
+ #[clippy::version = "1.30.0"]
pub ASSIGN_OPS,
"using compound assignment operators (e.g., `+=`) is harmless"
}
/// The original rule will only lint for `if let`. After
/// making it support to lint `match`, naming as `if let` is not suitable for it.
/// So, this lint is deprecated.
+ #[clippy::version = "pre 1.29.0"]
pub IF_LET_REDUNDANT_PATTERN_MATCHING,
"this lint has been changed to redundant_pattern_matching"
}
/// Vec::with_capacity(n); vec.set_len(n);` with `let vec = vec![0; n];`. The
/// replacement has very different performance characteristics so the lint is
/// deprecated.
+ #[clippy::version = "pre 1.29.0"]
pub UNSAFE_VECTOR_INITIALIZATION,
"the replacement suggested by this lint had substantially different behavior"
}
///
/// ### Deprecation reason
/// This lint has been superseded by #[must_use] in rustc.
+ #[clippy::version = "1.39.0"]
pub UNUSED_COLLECT,
"`collect` has been marked as #[must_use] in rustc and that covers all cases of this lint"
}
///
/// ### Deprecation reason
/// Associated-constants are now preferred.
+ #[clippy::version = "1.44.0"]
pub REPLACE_CONSTS,
"associated-constants `MIN`/`MAX` of integers are preferred to `{min,max}_value()` and module constants"
}
///
/// ### Deprecation reason
/// The regex! macro does not exist anymore.
+ #[clippy::version = "1.47.0"]
pub REGEX_MACRO,
"the regex! macro has been removed from the regex crate in 2018"
}
/// ### Deprecation reason
/// This lint has been replaced by `manual_find_map`, a
/// more specific lint.
+ #[clippy::version = "1.51.0"]
pub FIND_MAP,
"this lint has been replaced by `manual_find_map`, a more specific lint"
}
/// ### Deprecation reason
/// This lint has been replaced by `manual_filter_map`, a
/// more specific lint.
+ #[clippy::version = "1.53.0"]
pub FILTER_MAP,
"this lint has been replaced by `manual_filter_map`, a more specific lint"
}
/// The `avoid_breaking_exported_api` config option was added, which
/// enables the `enum_variant_names` lint for public items.
/// ```
+ #[clippy::version = "1.54.0"]
pub PUB_ENUM_VARIANT_NAMES,
"set the `avoid-breaking-exported-api` config option to `false` to enable the `enum_variant_names` lint for public items"
}
/// ### Deprecation reason
/// The `avoid_breaking_exported_api` config option was added, which
/// enables the `wrong_self_conversion` lint for public items.
+ #[clippy::version = "1.54.0"]
pub WRONG_PUB_SELF_CONVENTION,
"set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items"
}
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_with_context;
+use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
use clippy_utils::ty::peel_mid_ty_refs;
-use clippy_utils::{get_parent_node, in_macro, is_lint_allowed};
-use rustc_ast::util::parser::PREC_PREFIX;
+use clippy_utils::{get_parent_expr, get_parent_node, is_lint_allowed, path_to_local};
+use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX};
+use rustc_data_structures::fx::FxIndexMap;
use rustc_errors::Applicability;
-use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, MatchSource, Mutability, Node, UnOp};
+use rustc_hir::{
+ BindingAnnotation, Body, BodyId, BorrowKind, Destination, Expr, ExprKind, HirId, MatchSource, Mutability, Node,
+ Pat, PatKind, UnOp,
+};
use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
use rustc_middle::ty::{self, Ty, TyCtxt, TyS, TypeckResults};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{symbol::sym, Span};
+use std::iter;
declare_clippy_lint! {
/// ### What it does
/// ```rust,ignore
/// let _ = d.unwrap().deref();
/// ```
+ #[clippy::version = "1.44.0"]
pub EXPLICIT_DEREF_METHODS,
pedantic,
"Explicit use of deref or deref_mut method while not in a method chain."
}
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for address of operations (`&`) that are going to
+ /// be dereferenced immediately by the compiler.
+ ///
+ /// ### Why is this bad?
+ /// Suggests that the receiver of the expression borrows
+ /// the expression.
+ ///
+ /// ### Example
+ /// ```rust
+ /// fn fun(_a: &i32) {}
+ ///
+ /// // Bad
+ /// let x: &i32 = &&&&&&5;
+ /// fun(&x);
+ ///
+ /// // Good
+ /// let x: &i32 = &5;
+ /// fun(x);
+ /// ```
+ #[clippy::version = "pre 1.29.0"]
+ pub NEEDLESS_BORROW,
+ style,
+ "taking a reference that is going to be automatically dereferenced"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for `ref` bindings which create a reference to a reference.
+ ///
+ /// ### Why is this bad?
+ /// The address-of operator at the use site is clearer about the need for a reference.
+ ///
+ /// ### Example
+ /// ```rust
+ /// // Bad
+ /// let x = Some("");
+ /// if let Some(ref x) = x {
+ /// // use `x` here
+ /// }
+ ///
+ /// // Good
+ /// let x = Some("");
+ /// if let Some(x) = x {
+ /// // use `&x` here
+ /// }
+ /// ```
+ #[clippy::version = "1.54.0"]
+ pub REF_BINDING_TO_REFERENCE,
+ pedantic,
+ "`ref` binding to a reference"
+}
+
impl_lint_pass!(Dereferencing => [
EXPLICIT_DEREF_METHODS,
+ NEEDLESS_BORROW,
+ REF_BINDING_TO_REFERENCE,
]);
#[derive(Default)]
// expression. This is to store the id of that expression so it can be skipped when
// `check_expr` is called for it.
skip_expr: Option<HirId>,
+
+ /// The body the first local was found in. Used to emit lints when the traversal of the body has
+ /// been finished. Note we can't lint at the end of every body as they can be nested within each
+ /// other.
+ current_body: Option<BodyId>,
+ /// The list of locals currently being checked by the lint.
+ /// If the value is `None`, then the binding has been seen as a ref pattern, but is not linted.
+ /// This is needed for or patterns where one of the branches can be linted, but another can not
+ /// be.
+ ///
+ /// e.g. `m!(x) | Foo::Bar(ref x)`
+ ref_locals: FxIndexMap<HirId, Option<RefPat>>,
}
struct StateData {
ty_changed_count: usize,
is_final_ufcs: bool,
},
+ DerefedBorrow {
+ count: u32,
+ },
}
// A reference operation considered by this lint pass
AddrOf,
}
+struct RefPat {
+ /// Whether every usage of the binding is dereferenced.
+ always_deref: bool,
+ /// The spans of all the ref bindings for this local.
+ spans: Vec<Span>,
+ /// The applicability of this suggestion.
+ app: Applicability,
+ /// All the replacements which need to be made.
+ replacements: Vec<(Span, String)>,
+}
+
impl<'tcx> LateLintPass<'tcx> for Dereferencing {
+ #[allow(clippy::too_many_lines)]
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
// Skip path expressions from deref calls. e.g. `Deref::deref(e)`
if Some(expr.hir_id) == self.skip_expr.take() {
return;
}
+ if let Some(local) = path_to_local(expr) {
+ self.check_local_usage(cx, expr, local);
+ }
+
// Stop processing sub expressions when a macro call is seen
- if in_macro(expr.span) {
+ if expr.span.from_expansion() {
if let Some((state, data)) = self.state.take() {
report(cx, expr, state, data);
}
},
));
},
+ RefOp::AddrOf => {
+ // Find the number of times the borrow is auto-derefed.
+ let mut iter = find_adjustments(cx.tcx, typeck, expr).iter();
+ if let Some((i, adjust)) = iter.by_ref().enumerate().find_map(|(i, adjust)| {
+ if !matches!(adjust.kind, Adjust::Deref(_)) {
+ Some((i, adjust))
+ } else if !adjust.target.is_ref() {
+ // Add one to the number of references found.
+ Some((i + 1, adjust))
+ } else {
+ None
+ }
+ }) {
+ // Found two consecutive derefs. At least one can be removed.
+ if i > 1 {
+ let target_mut = iter::once(adjust)
+ .chain(iter)
+ .find_map(|adjust| match adjust.kind {
+ Adjust::Borrow(AutoBorrow::Ref(_, m)) => Some(m.into()),
+ _ => None,
+ })
+ // This default should never happen. Auto-deref always reborrows.
+ .unwrap_or(Mutability::Not);
+ self.state = Some((
+ // Subtract one for the current borrow expression, and one to cover the last
+ // reference which can't be removed (it's either reborrowed, or needed for
+ // auto-deref to happen).
+ State::DerefedBorrow {
+ count:
+ // Truncation here would require more than a `u32::MAX` level reference. The compiler
+ // does not support this.
+ #[allow(clippy::cast_possible_truncation)]
+ { i as u32 - 2 }
+ },
+ StateData {
+ span: expr.span,
+ target_mut,
+ },
+ ));
+ }
+ }
+ },
_ => (),
}
},
data,
));
},
+ (Some((State::DerefedBorrow { count }, data)), RefOp::AddrOf) if count != 0 => {
+ self.state = Some((State::DerefedBorrow { count: count - 1 }, data));
+ },
(Some((state, data)), _) => report(cx, expr, state, data),
}
}
+
+ fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
+ if let PatKind::Binding(BindingAnnotation::Ref, id, name, _) = pat.kind {
+ if let Some(opt_prev_pat) = self.ref_locals.get_mut(&id) {
+ // This binding id has been seen before. Add this pattern to the list of changes.
+ if let Some(prev_pat) = opt_prev_pat {
+ if pat.span.from_expansion() {
+ // Doesn't match the context of the previous pattern. Can't lint here.
+ *opt_prev_pat = None;
+ } else {
+ prev_pat.spans.push(pat.span);
+ prev_pat.replacements.push((
+ pat.span,
+ snippet_with_context(cx, name.span, pat.span.ctxt(), "..", &mut prev_pat.app)
+ .0
+ .into(),
+ ));
+ }
+ }
+ return;
+ }
+
+ if_chain! {
+ if !pat.span.from_expansion();
+ if let ty::Ref(_, tam, _) = *cx.typeck_results().pat_ty(pat).kind();
+ // only lint immutable refs, because borrowed `&mut T` cannot be moved out
+ if let ty::Ref(_, _, Mutability::Not) = *tam.kind();
+ then {
+ let mut app = Applicability::MachineApplicable;
+ let snip = snippet_with_context(cx, name.span, pat.span.ctxt(), "..", &mut app).0;
+ self.current_body = self.current_body.or(cx.enclosing_body);
+ self.ref_locals.insert(
+ id,
+ Some(RefPat {
+ always_deref: true,
+ spans: vec![pat.span],
+ app,
+ replacements: vec![(pat.span, snip.into())],
+ }),
+ );
+ }
+ }
+ }
+ }
+
+ fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
+ if Some(body.id()) == self.current_body {
+ for pat in self.ref_locals.drain(..).filter_map(|(_, x)| x) {
+ let replacements = pat.replacements;
+ let app = pat.app;
+ span_lint_and_then(
+ cx,
+ if pat.always_deref {
+ NEEDLESS_BORROW
+ } else {
+ REF_BINDING_TO_REFERENCE
+ },
+ pat.spans,
+ "this pattern creates a reference to a reference",
+ |diag| {
+ diag.multipart_suggestion("try this", replacements, app);
+ },
+ );
+ }
+ self.current_body = None;
+ }
+ }
}
fn try_parse_ref_op(
}
}
+/// Adjustments are sometimes made in the parent block rather than the expression itself.
+fn find_adjustments(
+ tcx: TyCtxt<'tcx>,
+ typeck: &'tcx TypeckResults<'_>,
+ expr: &'tcx Expr<'_>,
+) -> &'tcx [Adjustment<'tcx>] {
+ let map = tcx.hir();
+ let mut iter = map.parent_iter(expr.hir_id);
+ let mut prev = expr;
+
+ loop {
+ match typeck.expr_adjustments(prev) {
+ [] => (),
+ a => break a,
+ };
+
+ match iter.next().map(|(_, x)| x) {
+ Some(Node::Block(_)) => {
+ if let Some((_, Node::Expr(e))) = iter.next() {
+ prev = e;
+ } else {
+ // This shouldn't happen. Blocks are always contained in an expression.
+ break &[];
+ }
+ },
+ Some(Node::Expr(&Expr {
+ kind: ExprKind::Break(Destination { target_id: Ok(id), .. }, _),
+ ..
+ })) => {
+ if let Some(Node::Expr(e)) = map.find(id) {
+ prev = e;
+ iter = map.parent_iter(id);
+ } else {
+ // This shouldn't happen. The destination should exist.
+ break &[];
+ }
+ },
+ _ => break &[],
+ }
+ }
+}
+
#[allow(clippy::needless_pass_by_value)]
fn report(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data: StateData) {
match state {
app,
);
},
+ State::DerefedBorrow { .. } => {
+ let mut app = Applicability::MachineApplicable;
+ let snip = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app).0;
+ span_lint_and_sugg(
+ cx,
+ NEEDLESS_BORROW,
+ data.span,
+ &format!(
+ "this expression borrows a reference (`{}`) that is immediately dereferenced by the compiler",
+ cx.typeck_results().expr_ty(expr),
+ ),
+ "change this to",
+ snip.into(),
+ app,
+ );
+ },
+ }
+}
+
+impl Dereferencing {
+ fn check_local_usage(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, local: HirId) {
+ if let Some(outer_pat) = self.ref_locals.get_mut(&local) {
+ if let Some(pat) = outer_pat {
+ // Check for auto-deref
+ if !matches!(
+ cx.typeck_results().expr_adjustments(e),
+ [
+ Adjustment {
+ kind: Adjust::Deref(_),
+ ..
+ },
+ Adjustment {
+ kind: Adjust::Deref(_),
+ ..
+ },
+ ..
+ ]
+ ) {
+ match get_parent_expr(cx, e) {
+ // Field accesses are the same no matter the number of references.
+ Some(Expr {
+ kind: ExprKind::Field(..),
+ ..
+ }) => (),
+ Some(&Expr {
+ span,
+ kind: ExprKind::Unary(UnOp::Deref, _),
+ ..
+ }) if !span.from_expansion() => {
+ // Remove explicit deref.
+ let snip = snippet_with_context(cx, e.span, span.ctxt(), "..", &mut pat.app).0;
+ pat.replacements.push((span, snip.into()));
+ },
+ Some(parent) if !parent.span.from_expansion() => {
+ // Double reference might be needed at this point.
+ if parent.precedence().order() == PREC_POSTFIX {
+ // Parentheses would be needed here, don't lint.
+ *outer_pat = None;
+ } else {
+ pat.always_deref = false;
+ let snip = snippet_with_context(cx, e.span, parent.span.ctxt(), "..", &mut pat.app).0;
+ pat.replacements.push((e.span, format!("&{}", snip)));
+ }
+ },
+ _ if !e.span.from_expansion() => {
+ // Double reference might be needed at this point.
+ pat.always_deref = false;
+ let snip = snippet_with_applicability(cx, e.span, "..", &mut pat.app);
+ pat.replacements.push((e.span, format!("&{}", snip)));
+ },
+ // Edge case for macros. The span of the identifier will usually match the context of the
+ // binding, but not if the identifier was created in a macro. e.g. `concat_idents` and proc
+ // macros
+ _ => *outer_pat = None,
+ }
+ }
+ }
+ }
}
}
use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::{in_macro, is_automatically_derived, is_default_equivalent, remove_blocks};
+use clippy_utils::{is_automatically_derived, is_default_equivalent, remove_blocks};
use rustc_hir::{
def::{DefKind, Res},
Body, Expr, ExprKind, GenericArg, Impl, ImplItemKind, Item, ItemKind, Node, PathSegment, QPath, TyKind,
/// has exactly equal bounds, and therefore this lint is disabled for types with
/// generic parameters.
///
+ #[clippy::version = "1.57.0"]
pub DERIVABLE_IMPLS,
complexity,
"manual implementation of the `Default` trait which is equal to a derive"
}) = item.kind;
if let attrs = cx.tcx.hir().attrs(item.hir_id());
if !is_automatically_derived(attrs);
- if !in_macro(item.span);
+ if !item.span.from_expansion();
if let Some(def_id) = trait_ref.trait_def_id();
if cx.tcx.is_diagnostic_item(sym::Default, def_id);
if let impl_item_hir = child.id.hir_id();
/// ...
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub DERIVE_HASH_XOR_EQ,
correctness,
"deriving `Hash` but implementing `PartialEq` explicitly"
/// #[derive(Ord, PartialOrd, PartialEq, Eq)]
/// struct Foo;
/// ```
+ #[clippy::version = "1.47.0"]
pub DERIVE_ORD_XOR_PARTIAL_ORD,
correctness,
"deriving `Ord` but implementing `PartialOrd` explicitly"
/// // ..
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub EXPL_IMPL_CLONE_ON_COPY,
pedantic,
"implementing `Clone` explicitly on `Copy` types"
/// }
/// }
/// ```
+ #[clippy::version = "1.45.0"]
pub UNSAFE_DERIVE_DESERIALIZE,
pedantic,
"deriving `serde::Deserialize` on a type that has methods using `unsafe`"
+++ /dev/null
-use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::fn_def_id;
-
-use rustc_hir::{def::Res, def_id::DefIdMap, Expr};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_tool_lint, impl_lint_pass};
-
-use crate::utils::conf;
-
-declare_clippy_lint! {
- /// ### What it does
- /// Denies the configured methods and functions in clippy.toml
- ///
- /// ### Why is this bad?
- /// Some methods are undesirable in certain contexts, and it's beneficial to
- /// lint for them as needed.
- ///
- /// ### Example
- /// An example clippy.toml configuration:
- /// ```toml
- /// # clippy.toml
- /// disallowed-methods = [
- /// # Can use a string as the path of the disallowed method.
- /// "std::boxed::Box::new",
- /// # Can also use an inline table with a `path` key.
- /// { path = "std::time::Instant::now" },
- /// # When using an inline table, can add a `reason` for why the method
- /// # is disallowed.
- /// { path = "std::vec::Vec::leak", reason = "no leaking memory" },
- /// ]
- /// ```
- ///
- /// ```rust,ignore
- /// // Example code where clippy issues a warning
- /// let xs = vec![1, 2, 3, 4];
- /// xs.leak(); // Vec::leak is disallowed in the config.
- /// // The diagnostic contains the message "no leaking memory".
- ///
- /// let _now = Instant::now(); // Instant::now is disallowed in the config.
- ///
- /// let _box = Box::new(3); // Box::new is disallowed in the config.
- /// ```
- ///
- /// Use instead:
- /// ```rust,ignore
- /// // Example code which does not raise clippy warning
- /// let mut xs = Vec::new(); // Vec::new is _not_ disallowed in the config.
- /// xs.push(123); // Vec::push is _not_ disallowed in the config.
- /// ```
- pub DISALLOWED_METHOD,
- nursery,
- "use of a disallowed method call"
-}
-
-#[derive(Clone, Debug)]
-pub struct DisallowedMethod {
- conf_disallowed: Vec<conf::DisallowedMethod>,
- disallowed: DefIdMap<Option<String>>,
-}
-
-impl DisallowedMethod {
- pub fn new(conf_disallowed: Vec<conf::DisallowedMethod>) -> Self {
- Self {
- conf_disallowed,
- disallowed: DefIdMap::default(),
- }
- }
-}
-
-impl_lint_pass!(DisallowedMethod => [DISALLOWED_METHOD]);
-
-impl<'tcx> LateLintPass<'tcx> for DisallowedMethod {
- fn check_crate(&mut self, cx: &LateContext<'_>) {
- for conf in &self.conf_disallowed {
- let (path, reason) = match conf {
- conf::DisallowedMethod::Simple(path) => (path, None),
- conf::DisallowedMethod::WithReason { path, reason } => (
- path,
- reason.as_ref().map(|reason| format!("{} (from clippy.toml)", reason)),
- ),
- };
- let segs: Vec<_> = path.split("::").collect();
- if let Res::Def(_, id) = clippy_utils::path_to_res(cx, &segs) {
- self.disallowed.insert(id, reason);
- }
- }
- }
-
- fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- let def_id = match fn_def_id(cx, expr) {
- Some(def_id) => def_id,
- None => return,
- };
- let reason = match self.disallowed.get(&def_id) {
- Some(reason) => reason,
- None => return,
- };
- let func_path = cx.tcx.def_path_str(def_id);
- let msg = format!("use of a disallowed method `{}`", func_path);
- span_lint_and_then(cx, DISALLOWED_METHOD, expr.span, &msg, |diag| {
- if let Some(reason) = reason {
- diag.note(reason);
- }
- });
- }
-}
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::fn_def_id;
+
+use rustc_hir::{def::Res, def_id::DefIdMap, Expr};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+
+use crate::utils::conf;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Denies the configured methods and functions in clippy.toml
+ ///
+ /// ### Why is this bad?
+ /// Some methods are undesirable in certain contexts, and it's beneficial to
+ /// lint for them as needed.
+ ///
+ /// ### Example
+ /// An example clippy.toml configuration:
+ /// ```toml
+ /// # clippy.toml
+ /// disallowed-methods = [
+ /// # Can use a string as the path of the disallowed method.
+ /// "std::boxed::Box::new",
+ /// # Can also use an inline table with a `path` key.
+ /// { path = "std::time::Instant::now" },
+ /// # When using an inline table, can add a `reason` for why the method
+ /// # is disallowed.
+ /// { path = "std::vec::Vec::leak", reason = "no leaking memory" },
+ /// ]
+ /// ```
+ ///
+ /// ```rust,ignore
+ /// // Example code where clippy issues a warning
+ /// let xs = vec![1, 2, 3, 4];
+ /// xs.leak(); // Vec::leak is disallowed in the config.
+ /// // The diagnostic contains the message "no leaking memory".
+ ///
+ /// let _now = Instant::now(); // Instant::now is disallowed in the config.
+ ///
+ /// let _box = Box::new(3); // Box::new is disallowed in the config.
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust,ignore
+ /// // Example code which does not raise clippy warning
+ /// let mut xs = Vec::new(); // Vec::new is _not_ disallowed in the config.
+ /// xs.push(123); // Vec::push is _not_ disallowed in the config.
+ /// ```
+ #[clippy::version = "1.49.0"]
+ pub DISALLOWED_METHODS,
+ nursery,
+ "use of a disallowed method call"
+}
+
+#[derive(Clone, Debug)]
+pub struct DisallowedMethods {
+ conf_disallowed: Vec<conf::DisallowedMethod>,
+ disallowed: DefIdMap<Option<String>>,
+}
+
+impl DisallowedMethods {
+ pub fn new(conf_disallowed: Vec<conf::DisallowedMethod>) -> Self {
+ Self {
+ conf_disallowed,
+ disallowed: DefIdMap::default(),
+ }
+ }
+}
+
+impl_lint_pass!(DisallowedMethods => [DISALLOWED_METHODS]);
+
+impl<'tcx> LateLintPass<'tcx> for DisallowedMethods {
+ fn check_crate(&mut self, cx: &LateContext<'_>) {
+ for conf in &self.conf_disallowed {
+ let (path, reason) = match conf {
+ conf::DisallowedMethod::Simple(path) => (path, None),
+ conf::DisallowedMethod::WithReason { path, reason } => (
+ path,
+ reason.as_ref().map(|reason| format!("{} (from clippy.toml)", reason)),
+ ),
+ };
+ let segs: Vec<_> = path.split("::").collect();
+ if let Res::Def(_, id) = clippy_utils::path_to_res(cx, &segs) {
+ self.disallowed.insert(id, reason);
+ }
+ }
+ }
+
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+ let def_id = match fn_def_id(cx, expr) {
+ Some(def_id) => def_id,
+ None => return,
+ };
+ let reason = match self.disallowed.get(&def_id) {
+ Some(reason) => reason,
+ None => return,
+ };
+ let func_path = cx.tcx.def_path_str(def_id);
+ let msg = format!("use of a disallowed method `{}`", func_path);
+ span_lint_and_then(cx, DISALLOWED_METHODS, expr.span, &msg, |diag| {
+ if let Some(reason) = reason {
+ diag.note(reason);
+ }
+ });
+ }
+}
/// let zähler = 10; // OK, it's still latin.
/// let カウンタ = 10; // Will spawn the lint.
/// ```
+ #[clippy::version = "1.55.0"]
pub DISALLOWED_SCRIPT_IDENTS,
restriction,
"usage of non-allowed Unicode scripts"
+++ /dev/null
-use clippy_utils::diagnostics::span_lint_and_then;
-
-use rustc_data_structures::fx::FxHashMap;
-use rustc_hir::{
- def::Res, def_id::DefId, Item, ItemKind, PolyTraitRef, PrimTy, TraitBoundModifier, Ty, TyKind, UseKind,
-};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::Span;
-
-use crate::utils::conf;
-
-declare_clippy_lint! {
- /// ### What it does
- /// Denies the configured types in clippy.toml.
- ///
- /// ### Why is this bad?
- /// Some types are undesirable in certain contexts.
- ///
- /// ### Example:
- /// An example clippy.toml configuration:
- /// ```toml
- /// # clippy.toml
- /// disallowed-types = [
- /// # Can use a string as the path of the disallowed type.
- /// "std::collections::BTreeMap",
- /// # Can also use an inline table with a `path` key.
- /// { path = "std::net::TcpListener" },
- /// # When using an inline table, can add a `reason` for why the type
- /// # is disallowed.
- /// { path = "std::net::Ipv4Addr", reason = "no IPv4 allowed" },
- /// ]
- /// ```
- ///
- /// ```rust,ignore
- /// use std::collections::BTreeMap;
- /// // or its use
- /// let x = std::collections::BTreeMap::new();
- /// ```
- /// Use instead:
- /// ```rust,ignore
- /// // A similar type that is allowed by the config
- /// use std::collections::HashMap;
- /// ```
- pub DISALLOWED_TYPE,
- nursery,
- "use of a disallowed type"
-}
-#[derive(Clone, Debug)]
-pub struct DisallowedType {
- conf_disallowed: Vec<conf::DisallowedType>,
- def_ids: FxHashMap<DefId, Option<String>>,
- prim_tys: FxHashMap<PrimTy, Option<String>>,
-}
-
-impl DisallowedType {
- pub fn new(conf_disallowed: Vec<conf::DisallowedType>) -> Self {
- Self {
- conf_disallowed,
- def_ids: FxHashMap::default(),
- prim_tys: FxHashMap::default(),
- }
- }
-
- fn check_res_emit(&self, cx: &LateContext<'_>, res: &Res, span: Span) {
- match res {
- Res::Def(_, did) => {
- if let Some(reason) = self.def_ids.get(did) {
- emit(cx, &cx.tcx.def_path_str(*did), span, reason.as_deref());
- }
- },
- Res::PrimTy(prim) => {
- if let Some(reason) = self.prim_tys.get(prim) {
- emit(cx, prim.name_str(), span, reason.as_deref());
- }
- },
- _ => {},
- }
- }
-}
-
-impl_lint_pass!(DisallowedType => [DISALLOWED_TYPE]);
-
-impl<'tcx> LateLintPass<'tcx> for DisallowedType {
- fn check_crate(&mut self, cx: &LateContext<'_>) {
- for conf in &self.conf_disallowed {
- let (path, reason) = match conf {
- conf::DisallowedType::Simple(path) => (path, None),
- conf::DisallowedType::WithReason { path, reason } => (
- path,
- reason.as_ref().map(|reason| format!("{} (from clippy.toml)", reason)),
- ),
- };
- let segs: Vec<_> = path.split("::").collect();
- match clippy_utils::path_to_res(cx, &segs) {
- Res::Def(_, id) => {
- self.def_ids.insert(id, reason);
- },
- Res::PrimTy(ty) => {
- self.prim_tys.insert(ty, reason);
- },
- _ => {},
- }
- }
- }
-
- fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
- if let ItemKind::Use(path, UseKind::Single) = &item.kind {
- self.check_res_emit(cx, &path.res, item.span);
- }
- }
-
- fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) {
- if let TyKind::Path(path) = &ty.kind {
- self.check_res_emit(cx, &cx.qpath_res(path, ty.hir_id), ty.span);
- }
- }
-
- fn check_poly_trait_ref(&mut self, cx: &LateContext<'tcx>, poly: &'tcx PolyTraitRef<'tcx>, _: TraitBoundModifier) {
- self.check_res_emit(cx, &poly.trait_ref.path.res, poly.trait_ref.path.span);
- }
-}
-
-fn emit(cx: &LateContext<'_>, name: &str, span: Span, reason: Option<&str>) {
- span_lint_and_then(
- cx,
- DISALLOWED_TYPE,
- span,
- &format!("`{}` is not allowed according to config", name),
- |diag| {
- if let Some(reason) = reason {
- diag.note(reason);
- }
- },
- );
-}
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_then;
+
+use rustc_data_structures::fx::FxHashMap;
+use rustc_hir::{
+ def::Res, def_id::DefId, Item, ItemKind, PolyTraitRef, PrimTy, TraitBoundModifier, Ty, TyKind, UseKind,
+};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::Span;
+
+use crate::utils::conf;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Denies the configured types in clippy.toml.
+ ///
+ /// ### Why is this bad?
+ /// Some types are undesirable in certain contexts.
+ ///
+ /// ### Example:
+ /// An example clippy.toml configuration:
+ /// ```toml
+ /// # clippy.toml
+ /// disallowed-types = [
+ /// # Can use a string as the path of the disallowed type.
+ /// "std::collections::BTreeMap",
+ /// # Can also use an inline table with a `path` key.
+ /// { path = "std::net::TcpListener" },
+ /// # When using an inline table, can add a `reason` for why the type
+ /// # is disallowed.
+ /// { path = "std::net::Ipv4Addr", reason = "no IPv4 allowed" },
+ /// ]
+ /// ```
+ ///
+ /// ```rust,ignore
+ /// use std::collections::BTreeMap;
+ /// // or its use
+ /// let x = std::collections::BTreeMap::new();
+ /// ```
+ /// Use instead:
+ /// ```rust,ignore
+ /// // A similar type that is allowed by the config
+ /// use std::collections::HashMap;
+ /// ```
+ #[clippy::version = "1.55.0"]
+ pub DISALLOWED_TYPES,
+ nursery,
+ "use of disallowed types"
+}
+#[derive(Clone, Debug)]
+pub struct DisallowedTypes {
+ conf_disallowed: Vec<conf::DisallowedType>,
+ def_ids: FxHashMap<DefId, Option<String>>,
+ prim_tys: FxHashMap<PrimTy, Option<String>>,
+}
+
+impl DisallowedTypes {
+ pub fn new(conf_disallowed: Vec<conf::DisallowedType>) -> Self {
+ Self {
+ conf_disallowed,
+ def_ids: FxHashMap::default(),
+ prim_tys: FxHashMap::default(),
+ }
+ }
+
+ fn check_res_emit(&self, cx: &LateContext<'_>, res: &Res, span: Span) {
+ match res {
+ Res::Def(_, did) => {
+ if let Some(reason) = self.def_ids.get(did) {
+ emit(cx, &cx.tcx.def_path_str(*did), span, reason.as_deref());
+ }
+ },
+ Res::PrimTy(prim) => {
+ if let Some(reason) = self.prim_tys.get(prim) {
+ emit(cx, prim.name_str(), span, reason.as_deref());
+ }
+ },
+ _ => {},
+ }
+ }
+}
+
+impl_lint_pass!(DisallowedTypes => [DISALLOWED_TYPES]);
+
+impl<'tcx> LateLintPass<'tcx> for DisallowedTypes {
+ fn check_crate(&mut self, cx: &LateContext<'_>) {
+ for conf in &self.conf_disallowed {
+ let (path, reason) = match conf {
+ conf::DisallowedType::Simple(path) => (path, None),
+ conf::DisallowedType::WithReason { path, reason } => (
+ path,
+ reason.as_ref().map(|reason| format!("{} (from clippy.toml)", reason)),
+ ),
+ };
+ let segs: Vec<_> = path.split("::").collect();
+ match clippy_utils::path_to_res(cx, &segs) {
+ Res::Def(_, id) => {
+ self.def_ids.insert(id, reason);
+ },
+ Res::PrimTy(ty) => {
+ self.prim_tys.insert(ty, reason);
+ },
+ _ => {},
+ }
+ }
+ }
+
+ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
+ if let ItemKind::Use(path, UseKind::Single) = &item.kind {
+ self.check_res_emit(cx, &path.res, item.span);
+ }
+ }
+
+ fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) {
+ if let TyKind::Path(path) = &ty.kind {
+ self.check_res_emit(cx, &cx.qpath_res(path, ty.hir_id), ty.span);
+ }
+ }
+
+ fn check_poly_trait_ref(&mut self, cx: &LateContext<'tcx>, poly: &'tcx PolyTraitRef<'tcx>, _: TraitBoundModifier) {
+ self.check_res_emit(cx, &poly.trait_ref.path.res, poly.trait_ref.path.span);
+ }
+}
+
+fn emit(cx: &LateContext<'_>, name: &str, span: Span, reason: Option<&str>) {
+ span_lint_and_then(
+ cx,
+ DISALLOWED_TYPES,
+ span,
+ &format!("`{}` is not allowed according to config", name),
+ |diag| {
+ if let Some(reason) = reason {
+ diag.note(reason);
+ }
+ },
+ );
+}
use clippy_utils::attrs::is_doc_hidden;
-use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_sugg};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_then};
use clippy_utils::source::{first_line_of_span, snippet_with_applicability};
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
use clippy_utils::{is_entrypoint_fn, is_expn_of, match_panic_def_id, method_chain_args, return_ty};
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::sync::Lrc;
use rustc_errors::emitter::EmitterWriter;
-use rustc_errors::{Applicability, Handler};
+use rustc_errors::{Applicability, Handler, SuggestionStyle};
use rustc_hir as hir;
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_hir::{AnonConst, Expr, ExprKind, QPath};
/// /// [SmallVec]: SmallVec
/// fn main() {}
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub DOC_MARKDOWN,
pedantic,
"presence of `_`, `::` or camel-case outside backticks in documentation"
/// unimplemented!();
/// }
/// ```
+ #[clippy::version = "1.39.0"]
pub MISSING_SAFETY_DOC,
style,
"`pub unsafe fn` without `# Safety` docs"
/// unimplemented!();
/// }
/// ```
+ #[clippy::version = "1.41.0"]
pub MISSING_ERRORS_DOC,
pedantic,
"`pub fn` returns `Result` without `# Errors` in doc comment"
/// }
/// }
/// ```
+ #[clippy::version = "1.52.0"]
pub MISSING_PANICS_DOC,
pedantic,
"`pub fn` may panic without `# Panics` in doc comment"
/// unimplemented!();
/// }
/// ``````
+ #[clippy::version = "1.40.0"]
pub NEEDLESS_DOCTEST_MAIN,
style,
"presence of `fn main() {` in code examples"
| ItemKind::ExternCrate(..)
| ItemKind::ForeignMod(..) => return false,
// We found a main function ...
- ItemKind::Fn(box Fn { sig, body: Some(block), .. }) if item.ident.name == sym::main => {
+ ItemKind::Fn(box Fn {
+ sig, body: Some(block), ..
+ }) if item.ident.name == sym::main => {
let is_async = matches!(sig.header.asyncness, Async::Yes { .. });
let returns_nothing = match &sig.decl.output {
FnRetTy::Default(..) => true,
if has_underscore(word) || word.contains("::") || is_camel_case(word) {
let mut applicability = Applicability::MachineApplicable;
- span_lint_and_sugg(
+ span_lint_and_then(
cx,
DOC_MARKDOWN,
span,
"item in documentation is missing backticks",
- "try",
- format!("`{}`", snippet_with_applicability(cx, span, "..", &mut applicability)),
- applicability,
+ |diag| {
+ let snippet = snippet_with_applicability(cx, span, "..", &mut applicability);
+ diag.span_suggestion_with_style(
+ span,
+ "try",
+ format!("`{}`", snippet),
+ applicability,
+ // always show the suggestion in a separate line, since the
+ // inline presentation adds another pair of backticks
+ SuggestionStyle::ShowAlways,
+ );
+ },
);
}
}
/// # let y = 2;
/// if x <= y {}
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub DOUBLE_COMPARISONS,
complexity,
"unnecessary double comparisons that can be simplified"
/// // Good
/// foo(0);
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub DOUBLE_PARENS,
complexity,
"Warn on unnecessary double parentheses"
/// // still locked
/// operation_that_requires_mutex_to_be_unlocked();
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub DROP_REF,
correctness,
"calls to `std::mem::drop` with a reference instead of an owned value"
/// let x = Box::new(1);
/// std::mem::forget(&x) // Should have been forget(x), x will still be dropped
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub FORGET_REF,
correctness,
"calls to `std::mem::forget` with a reference instead of an owned value"
/// std::mem::drop(x) // A copy of x is passed to the function, leaving the
/// // original unaffected
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub DROP_COPY,
correctness,
"calls to `std::mem::drop` with a value that implements Copy"
/// std::mem::forget(x) // A copy of x is passed to the function, leaving the
/// // original unaffected
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub FORGET_COPY,
correctness,
"calls to `std::mem::forget` with a value that implements Copy"
/// let _micros = dur.subsec_micros();
/// let _millis = dur.subsec_millis();
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub DURATION_SUBSEC,
complexity,
"checks for calculation of subsecond microseconds or milliseconds"
/// // We don't care about zero.
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub ELSE_IF_WITHOUT_ELSE,
restriction,
"`if` expression with an `else if`, but without a final `else` branch"
///
/// struct Test(!);
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub EMPTY_ENUM,
pedantic,
"enum with no variants"
/// # let v = 1;
/// map.entry(k).or_insert(v);
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub MAP_ENTRY,
perf,
"use of `contains_key` followed by `insert` on a `HashMap` or `BTreeMap`"
/// Y = 0,
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub ENUM_CLIKE_UNPORTABLE_VARIANT,
correctness,
"C-like enums that are `repr(isize/usize)` and have values that don't fit into an `i32`"
/// Battenberg,
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub ENUM_VARIANT_NAMES,
style,
"enums where all variants share a prefix/postfix"
/// struct BlackForest;
/// }
/// ```
+ #[clippy::version = "1.33.0"]
pub MODULE_NAME_REPETITIONS,
pedantic,
"type names prefixed/postfixed with their containing module's name"
/// ...
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub MODULE_INCEPTION,
style,
"modules that have the same name as their parent module"
use clippy_utils::diagnostics::{multispan_sugg, span_lint, span_lint_and_then};
use clippy_utils::source::snippet;
use clippy_utils::ty::{implements_trait, is_copy};
-use clippy_utils::{
- ast_utils::is_useless_with_eq_exprs, eq_expr_value, higher, in_macro, is_expn_of, is_in_test_function,
-};
+use clippy_utils::{ast_utils::is_useless_with_eq_exprs, eq_expr_value, higher, is_expn_of, is_in_test_function};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, StmtKind};
/// # let b = 4;
/// assert_eq!(a, a);
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub EQ_OP,
correctness,
"equal operands on both sides of a comparison or bitwise combination (e.g., `x == x`)"
/// // Good
/// x == *y
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub OP_REF,
style,
"taking a reference to satisfy the type constraints on `==`"
}
let macro_with_not_op = |expr_kind: &ExprKind<'_>| {
if let ExprKind::Unary(_, expr) = *expr_kind {
- in_macro(expr.span)
+ expr.span.from_expansion()
} else {
false
}
/// do_thing();
/// }
/// ```
+ #[clippy::version = "1.57.0"]
pub EQUATABLE_IF_LET,
nursery,
"using pattern matching instead of equality"
/// 0 * x;
/// x & 0;
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub ERASING_OP,
correctness,
"using erasing operations, e.g., `x * 0` or `y & 0`"
/// foo(x);
/// println!("{}", x);
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub BOXED_LOCAL,
perf,
"using `Box<T>` where unnecessary"
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::higher::VecArgs;
use clippy_utils::source::snippet_opt;
-use clippy_utils::usage::UsedAfterExprVisitor;
-use clippy_utils::{get_enclosing_loop_or_closure, higher, path_to_local_id};
+use clippy_utils::usage::local_used_after_expr;
+use clippy_utils::{get_enclosing_loop_or_closure, higher, path_to_local, path_to_local_id};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::def_id::DefId;
/// ```
/// where `foo(_)` is a plain function that takes the exact argument type of
/// `x`.
+ #[clippy::version = "pre 1.29.0"]
pub REDUNDANT_CLOSURE,
style,
"redundant closures, i.e., `|a| foo(a)` (which can be written as just `foo`)"
/// ```rust,ignore
/// Some('a').map(char::to_uppercase);
/// ```
+ #[clippy::version = "1.35.0"]
pub REDUNDANT_CLOSURE_FOR_METHOD_CALLS,
pedantic,
"redundant closures for method calls"
if let ty::Closure(_, substs) = callee_ty.peel_refs().kind();
if substs.as_closure().kind() == ClosureKind::FnMut;
if get_enclosing_loop_or_closure(cx.tcx, expr).is_some()
- || UsedAfterExprVisitor::is_found(cx, callee);
+ || path_to_local(callee).map_or(false, |l| local_used_after_expr(cx, l, callee));
then {
// Mutable closure is used after current expr; we cannot consume it.
/// };
/// let a = tmp + x;
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub EVAL_ORDER_DEPENDENCE,
suspicious,
"whether a variable read occurs before a write depends on sub-expression evaluation order"
/// let x = (a, b, c, panic!());
/// // can simply be replaced by `panic!()`
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub DIVERGING_SUB_EXPRESSION,
complexity,
"whether an expression contains a diverging sub expression"
use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::in_macro;
use rustc_ast::ast::{AssocItemKind, Extern, Fn, FnSig, Impl, Item, ItemKind, Trait, Ty, TyKind};
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
/// Finished,
/// }
/// ```
+ #[clippy::version = "1.43.0"]
pub STRUCT_EXCESSIVE_BOOLS,
pedantic,
"using too many bools in a struct"
///
/// fn f(shape: Shape, temperature: Temperature) { ... }
/// ```
+ #[clippy::version = "1.43.0"]
pub FN_PARAMS_EXCESSIVE_BOOLS,
pedantic,
"using too many bools in function parameters"
impl EarlyLintPass for ExcessiveBools {
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
- if in_macro(item.span) {
+ if item.span.from_expansion() {
return;
}
match &item.kind {
/// Baz
/// }
/// ```
+ #[clippy::version = "1.51.0"]
pub EXHAUSTIVE_ENUMS,
restriction,
"detects exported enums that have not been marked #[non_exhaustive]"
/// baz: String,
/// }
/// ```
+ #[clippy::version = "1.51.0"]
pub EXHAUSTIVE_STRUCTS,
restriction,
"detects exported structs that have not been marked #[non_exhaustive]"
/// ```ignore
/// std::process::exit(0)
/// ```
+ #[clippy::version = "1.41.0"]
pub EXIT,
restriction,
"`std::process::exit` is called, terminating the program"
/// // this would be clearer as `eprintln!("foo: {:?}", bar);`
/// writeln!(&mut std::io::stderr(), "foo: {:?}", bar).unwrap();
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub EXPLICIT_WRITE,
complexity,
"using the `write!()` family of functions instead of the `print!()` family of functions, when using the latter would work"
/// }
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub FALLIBLE_IMPL_FROM,
nursery,
"Warn on impls of `From<..>` that contain `panic!()` or `unwrap()`"
/// ghi = []
/// ```
///
+ #[clippy::version = "1.57.0"]
pub REDUNDANT_FEATURE_NAMES,
cargo,
"usage of a redundant feature name"
/// def = []
///
/// ```
+ #[clippy::version = "1.57.0"]
pub NEGATIVE_FEATURE_NAMES,
cargo,
"usage of a negative feature name"
/// (a - b).abs() < f32::EPSILON
/// }
/// ```
+ #[clippy::version = "1.48.0"]
pub FLOAT_EQUALITY_WITHOUT_ABS,
suspicious,
"float equality check without `.abs()`"
/// let v: f64 = 0.123_456_789_9;
/// println!("{}", v); // 0.123_456_789_9
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub EXCESSIVE_PRECISION,
style,
"excessive precision for float literal"
/// let _: f32 = 16_777_216.0;
/// let _: f64 = 16_777_217.0;
/// ```
+ #[clippy::version = "1.43.0"]
pub LOSSY_FLOAT_LITERAL,
restriction,
"lossy whole number float literals"
};
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::higher;
-use clippy_utils::{eq_expr_value, get_parent_expr, numeric_literal, sugg};
+use clippy_utils::{eq_expr_value, get_parent_expr, in_constant, numeric_literal, sugg};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp};
/// let _ = a.ln_1p();
/// let _ = a.exp_m1();
/// ```
+ #[clippy::version = "1.43.0"]
pub IMPRECISE_FLOPS,
nursery,
"usage of imprecise floating point operations"
/// let _ = a.abs();
/// let _ = -a.abs();
/// ```
+ #[clippy::version = "1.43.0"]
pub SUBOPTIMAL_FLOPS,
nursery,
"usage of sub-optimal floating point operations"
impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+ // All of these operations are currently not const.
+ if in_constant(cx, expr.hir_id) {
+ return;
+ }
+
if let ExprKind::MethodCall(path, _, args, _) = &expr.kind {
let recv_ty = cx.typeck_results().expr_ty(&args[0]);
/// // Good
/// foo.to_owned();
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub USELESS_FORMAT,
complexity,
"useless use of `format!`"
use rustc_middle::ty::adjustment::{Adjust, Adjustment};
use rustc_middle::ty::Ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::{sym, BytePos, ExpnData, ExpnKind, Span, Symbol};
+use rustc_span::{sym, ExpnData, ExpnKind, Span, Symbol};
declare_clippy_lint! {
/// ### What it does
/// # use std::panic::Location;
/// println!("error: something failed at {}", Location::caller());
/// ```
+ #[clippy::version = "1.58.0"]
pub FORMAT_IN_FORMAT_ARGS,
perf,
"`format!` used in a macro that does formatting"
/// # use std::panic::Location;
/// println!("error: something failed at {}", Location::caller());
/// ```
+ #[clippy::version = "1.58.0"]
pub TO_STRING_IN_FORMAT_ARGS,
perf,
"`to_string` applied to a type that implements `Display` in format args"
span_lint_and_then(
cx,
FORMAT_IN_FORMAT_ARGS,
- trim_semicolon(cx, call_site),
+ call_site,
&format!("`format!` in `{}!` args", name),
|diag| {
diag.help(&format!(
.any(|(j, arg)| i != j && std::ptr::eq(value, arg.value))
}
-fn trim_semicolon(cx: &LateContext<'_>, span: Span) -> Span {
- snippet_opt(cx, span).map_or(span, |snippet| {
- let snippet = snippet.trim_end_matches(';');
- span.with_hi(span.lo() + BytePos(u32::try_from(snippet.len()).unwrap()))
- })
-}
-
fn count_needed_derefs<'tcx, I>(mut ty: Ty<'tcx>, mut iter: I) -> (usize, Ty<'tcx>)
where
I: Iterator<Item = &'tcx Adjustment<'tcx>>,
/// ```rust,ignore
/// a =- 42; // confusing, should it be `a -= 42` or `a = -42`?
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub SUSPICIOUS_ASSIGNMENT_FORMATTING,
suspicious,
"suspicious formatting of `*=`, `-=` or `!=`"
/// if foo &&! bar { // this should be `foo && !bar` but looks like a different operator
/// }
/// ```
+ #[clippy::version = "1.40.0"]
pub SUSPICIOUS_UNARY_OP_FORMATTING,
suspicious,
"suspicious formatting of unary `-` or `!` on the RHS of a BinOp"
/// if bar { // this is the `else` block of the previous `if`, but should it be?
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub SUSPICIOUS_ELSE_FORMATTING,
suspicious,
"suspicious formatting of `else`"
/// -4, -5, -6
/// ];
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub POSSIBLE_MISSING_COMMA,
correctness,
"possible missing comma in array"
/// }
/// }
/// ```
+ #[clippy::version = "1.51.0"]
pub FROM_OVER_INTO,
style,
"Warns on implementations of `Into<..>` to use `From<..>`"
/// let input: &str = get_input();
/// let num: u16 = input.parse()?;
/// ```
+ #[clippy::version = "1.52.0"]
pub FROM_STR_RADIX_10,
style,
"from_str_radix with radix 10"
/// // ..
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub TOO_MANY_ARGUMENTS,
complexity,
"functions with too many arguments"
/// println!("");
/// }
/// ```
+ #[clippy::version = "1.34.0"]
pub TOO_MANY_LINES,
pedantic,
"functions with too many lines"
/// println!("{}", unsafe { *x });
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub NOT_UNSAFE_PTR_ARG_DEREF,
correctness,
"public functions dereferencing raw pointer arguments but not marked `unsafe`"
/// #[must_use]
/// fn useless() { }
/// ```
+ #[clippy::version = "1.40.0"]
pub MUST_USE_UNIT,
style,
"`#[must_use]` attribute on a unit-returning function / method"
/// unimplemented!();
/// }
/// ```
+ #[clippy::version = "1.40.0"]
pub DOUBLE_MUST_USE,
style,
"`#[must_use]` attribute on a `#[must_use]`-returning function / method"
/// // this could be annotated with `#[must_use]`.
/// fn id<T>(t: T) -> T { t }
/// ```
+ #[clippy::version = "1.40.0"]
pub MUST_USE_CANDIDATE,
pedantic,
"function or method that could take a `#[must_use]` attribute"
///
/// Note that there are crates that simplify creating the error type, e.g.
/// [`thiserror`](https://docs.rs/thiserror).
+ #[clippy::version = "1.49.0"]
pub RESULT_UNIT_ERR,
style,
"public function returning `Result` with an `Err` type of `()`"
continue;
}
} else {
- let multi_idx = line.find("/*").unwrap_or_else(|| line.len());
- let single_idx = line.find("//").unwrap_or_else(|| line.len());
+ let multi_idx = line.find("/*").unwrap_or(line.len());
+ let single_idx = line.find("//").unwrap_or(line.len());
code_in_line |= multi_idx > 0 && single_idx > 0;
// Implies multi_idx is below line.len()
if multi_idx < single_idx {
/// ```rust
/// async fn is_send(bytes: std::sync::Arc<[u8]>) {}
/// ```
+ #[clippy::version = "1.44.0"]
pub FUTURE_NOT_SEND,
nursery,
"public Futures must be Send"
/// let x = vec![2, 3, 5];
/// let last_element = x.last();
/// ```
+ #[clippy::version = "1.37.0"]
pub GET_LAST_WITH_LEN,
complexity,
"Using `x.get(x.len() - 1)` when `x.last()` is correct and simpler"
/// # let x = 1;
/// x / 1 + 0 * 1 - 0 | 0;
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub IDENTITY_OP,
complexity,
"using identity operations, e.g., `x + 0` or `y / 1`"
/// use_locked(locked);
/// }
/// ```
+ #[clippy::version = "1.45.0"]
pub IF_LET_MUTEX,
correctness,
"locking a `Mutex` in an `if let` block can cause deadlocks"
/// a()
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub IF_NOT_ELSE,
pedantic,
"`if` branches that could be swapped so no negation operation is necessary on the condition"
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::source::snippet_with_macro_callsite;
-use clippy_utils::{higher, is_else_clause, is_lang_ctor, meets_msrv, msrvs};
+use clippy_utils::{contains_return, higher, is_else_clause, is_lang_ctor, meets_msrv, msrvs};
use if_chain::if_chain;
use rustc_hir::LangItem::{OptionNone, OptionSome};
-use rustc_hir::{Expr, ExprKind};
+use rustc_hir::{Expr, ExprKind, Stmt, StmtKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_semver::RustcVersion;
/// 42
/// });
/// ```
+ #[clippy::version = "1.53.0"]
pub IF_THEN_SOME_ELSE_NONE,
restriction,
"Finds if-else that could be written using `bool::then`"
if let Some(els_expr) = els_block.expr;
if let ExprKind::Path(ref qpath) = els_expr.kind;
if is_lang_ctor(cx, qpath, OptionNone);
+ if !stmts_contains_early_return(then_block.stmts);
then {
let cond_snip = snippet_with_macro_callsite(cx, cond.span, "[condition]");
let cond_snip = if matches!(cond.kind, ExprKind::Unary(_, _) | ExprKind::Binary(_, _, _)) {
extract_msrv_attr!(LateContext);
}
+
+fn stmts_contains_early_return(stmts: &[Stmt<'_>]) -> bool {
+ stmts.iter().any(|stmt| {
+ let Stmt { kind: StmtKind::Semi(e), .. } = stmt else { return false };
+
+ contains_return(e)
+ })
+}
///
/// pub fn foo<S: BuildHasher>(map: &mut HashMap<i32, i32, S>) { }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub IMPLICIT_HASHER,
pedantic,
"missing generalization over different hashers"
diagnostics::span_lint_and_sugg,
get_async_fn_body, is_async_fn,
source::{snippet_with_applicability, snippet_with_context, walk_span_to_context},
- visitors::visit_break_exprs,
+ visitors::expr_visitor_no_bodies,
};
use rustc_errors::Applicability;
-use rustc_hir::intravisit::FnKind;
+use rustc_hir::intravisit::{FnKind, Visitor};
use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, FnRetTy, HirId};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
/// return x;
/// }
/// ```
+ #[clippy::version = "1.33.0"]
pub IMPLICIT_RETURN,
restriction,
"use a return statement like `return expr` instead of an expression"
ExprKind::Loop(block, ..) => {
let mut add_return = false;
- visit_break_exprs(block, |break_expr, dest, sub_expr| {
- if dest.target_id.ok() == Some(expr.hir_id) {
- if call_site_span.is_none() && break_expr.span.ctxt() == ctxt {
- // At this point sub_expr can be `None` in async functions which either diverge, or return the
- // unit type.
- if let Some(sub_expr) = sub_expr {
- lint_break(cx, break_expr.span, sub_expr.span);
+ expr_visitor_no_bodies(|e| {
+ if let ExprKind::Break(dest, sub_expr) = e.kind {
+ if dest.target_id.ok() == Some(expr.hir_id) {
+ if call_site_span.is_none() && e.span.ctxt() == ctxt {
+ // At this point sub_expr can be `None` in async functions which either diverge, or return
+ // the unit type.
+ if let Some(sub_expr) = sub_expr {
+ lint_break(cx, e.span, sub_expr.span);
+ }
+ } else {
+ // the break expression is from a macro call, add a return to the loop
+ add_return = true;
}
- } else {
- // the break expression is from a macro call, add a return to the loop
- add_return = true;
}
}
- });
+ true
+ })
+ .visit_block(block);
if add_return {
#[allow(clippy::option_if_let_else)]
if let Some(span) = call_site_span {
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::higher;
-use clippy_utils::{in_macro, SpanlessEq};
+use clippy_utils::SpanlessEq;
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
/// // Good
/// i = i.saturating_sub(1);
/// ```
+ #[clippy::version = "1.44.0"]
pub IMPLICIT_SATURATING_SUB,
pedantic,
"Perform saturating subtraction instead of implicitly checking lower bound of data type"
impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
- if in_macro(expr.span) {
+ if expr.span.from_expansion() {
return;
}
if_chain! {
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::in_macro;
use clippy_utils::source::snippet;
use if_chain::if_chain;
use rustc_data_structures::fx::FxHashMap;
/// # let y = 2;
/// Foo { x, y };
/// ```
+ #[clippy::version = "1.52.0"]
pub INCONSISTENT_STRUCT_CONSTRUCTOR,
pedantic,
"the order of the field init shorthand is inconsistent with the order in the struct definition"
impl LateLintPass<'_> for InconsistentStructConstructor {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
if_chain! {
- if !in_macro(expr.span);
+ if !expr.span.from_expansion();
if let ExprKind::Struct(qpath, fields, base) = expr.kind;
let ty = cx.typeck_results().expr_ty(expr);
if let Some(adt_def) = ty.ty_adt_def();
--- /dev/null
+use clippy_utils::consts::{constant, Constant};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::higher::IfLet;
+use clippy_utils::ty::is_copy;
+use clippy_utils::{is_expn_of, is_lint_allowed, meets_msrv, msrvs, path_to_local};
+use if_chain::if_chain;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::hir::map::Map;
+use rustc_middle::ty;
+use rustc_semver::RustcVersion;
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::{symbol::Ident, Span};
+use std::convert::TryInto;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// The lint checks for slice bindings in patterns that are only used to
+ /// access individual slice values.
+ ///
+ /// ### Why is this bad?
+ /// Accessing slice values using indices can lead to panics. Using refutable
+ /// patterns can avoid these. Binding to individual values also improves the
+ /// readability as they can be named.
+ ///
+ /// ### Limitations
+ /// This lint currently only checks for immutable access inside `if let`
+ /// patterns.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let slice: Option<&[u32]> = Some(&[1, 2, 3]);
+ ///
+ /// if let Some(slice) = slice {
+ /// println!("{}", slice[0]);
+ /// }
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// let slice: Option<&[u32]> = Some(&[1, 2, 3]);
+ ///
+ /// if let Some(&[first, ..]) = slice {
+ /// println!("{}", first);
+ /// }
+ /// ```
+ #[clippy::version = "1.58.0"]
+ pub INDEX_REFUTABLE_SLICE,
+ nursery,
+ "avoid indexing on slices which could be destructed"
+}
+
+#[derive(Copy, Clone)]
+pub struct IndexRefutableSlice {
+ max_suggested_slice: u64,
+ msrv: Option<RustcVersion>,
+}
+
+impl IndexRefutableSlice {
+ pub fn new(max_suggested_slice_pattern_length: u64, msrv: Option<RustcVersion>) -> Self {
+ Self {
+ max_suggested_slice: max_suggested_slice_pattern_length,
+ msrv,
+ }
+ }
+}
+
+impl_lint_pass!(IndexRefutableSlice => [INDEX_REFUTABLE_SLICE]);
+
+impl LateLintPass<'_> for IndexRefutableSlice {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
+ if_chain! {
+ if !expr.span.from_expansion() || is_expn_of(expr.span, "if_chain").is_some();
+ if let Some(IfLet {let_pat, if_then, ..}) = IfLet::hir(cx, expr);
+ if !is_lint_allowed(cx, INDEX_REFUTABLE_SLICE, expr.hir_id);
+ if meets_msrv(self.msrv.as_ref(), &msrvs::SLICE_PATTERNS);
+
+ let found_slices = find_slice_values(cx, let_pat);
+ if !found_slices.is_empty();
+ let filtered_slices = filter_lintable_slices(cx, found_slices, self.max_suggested_slice, if_then);
+ if !filtered_slices.is_empty();
+ then {
+ for slice in filtered_slices.values() {
+ lint_slice(cx, slice);
+ }
+ }
+ }
+ }
+
+ extract_msrv_attr!(LateContext);
+}
+
+fn find_slice_values(cx: &LateContext<'_>, pat: &hir::Pat<'_>) -> FxHashMap<hir::HirId, SliceLintInformation> {
+ let mut removed_pat: FxHashSet<hir::HirId> = FxHashSet::default();
+ let mut slices: FxHashMap<hir::HirId, SliceLintInformation> = FxHashMap::default();
+ pat.walk_always(|pat| {
+ if let hir::PatKind::Binding(binding, value_hir_id, ident, sub_pat) = pat.kind {
+ // We'll just ignore mut and ref mut for simplicity sake right now
+ if let hir::BindingAnnotation::Mutable | hir::BindingAnnotation::RefMut = binding {
+ return;
+ }
+
+ // This block catches bindings with sub patterns. It would be hard to build a correct suggestion
+ // for them and it's likely that the user knows what they are doing in such a case.
+ if removed_pat.contains(&value_hir_id) {
+ return;
+ }
+ if sub_pat.is_some() {
+ removed_pat.insert(value_hir_id);
+ slices.remove(&value_hir_id);
+ return;
+ }
+
+ let bound_ty = cx.typeck_results().node_type(pat.hir_id);
+ if let ty::Slice(inner_ty) | ty::Array(inner_ty, _) = bound_ty.peel_refs().kind() {
+ // The values need to use the `ref` keyword if they can't be copied.
+ // This will need to be adjusted if the lint want to support multable access in the future
+ let src_is_ref = bound_ty.is_ref() && binding != hir::BindingAnnotation::Ref;
+ let needs_ref = !(src_is_ref || is_copy(cx, inner_ty));
+
+ let slice_info = slices
+ .entry(value_hir_id)
+ .or_insert_with(|| SliceLintInformation::new(ident, needs_ref));
+ slice_info.pattern_spans.push(pat.span);
+ }
+ }
+ });
+
+ slices
+}
+
+fn lint_slice(cx: &LateContext<'_>, slice: &SliceLintInformation) {
+ let used_indices = slice
+ .index_use
+ .iter()
+ .map(|(index, _)| *index)
+ .collect::<FxHashSet<_>>();
+
+ let value_name = |index| format!("{}_{}", slice.ident.name, index);
+
+ if let Some(max_index) = used_indices.iter().max() {
+ let opt_ref = if slice.needs_ref { "ref " } else { "" };
+ let pat_sugg_idents = (0..=*max_index)
+ .map(|index| {
+ if used_indices.contains(&index) {
+ format!("{}{}", opt_ref, value_name(index))
+ } else {
+ "_".to_string()
+ }
+ })
+ .collect::<Vec<_>>();
+ let pat_sugg = format!("[{}, ..]", pat_sugg_idents.join(", "));
+
+ span_lint_and_then(
+ cx,
+ INDEX_REFUTABLE_SLICE,
+ slice.ident.span,
+ "this binding can be a slice pattern to avoid indexing",
+ |diag| {
+ diag.multipart_suggestion(
+ "try using a slice pattern here",
+ slice
+ .pattern_spans
+ .iter()
+ .map(|span| (*span, pat_sugg.clone()))
+ .collect(),
+ Applicability::MaybeIncorrect,
+ );
+
+ diag.multipart_suggestion(
+ "and replace the index expressions here",
+ slice
+ .index_use
+ .iter()
+ .map(|(index, span)| (*span, value_name(*index)))
+ .collect(),
+ Applicability::MaybeIncorrect,
+ );
+
+ // The lint message doesn't contain a warning about the removed index expression,
+ // since `filter_lintable_slices` will only return slices where all access indices
+ // are known at compile time. Therefore, they can be removed without side effects.
+ },
+ );
+ }
+}
+
+#[derive(Debug)]
+struct SliceLintInformation {
+ ident: Ident,
+ needs_ref: bool,
+ pattern_spans: Vec<Span>,
+ index_use: Vec<(u64, Span)>,
+}
+
+impl SliceLintInformation {
+ fn new(ident: Ident, needs_ref: bool) -> Self {
+ Self {
+ ident,
+ needs_ref,
+ pattern_spans: Vec::new(),
+ index_use: Vec::new(),
+ }
+ }
+}
+
+fn filter_lintable_slices<'a, 'tcx>(
+ cx: &'a LateContext<'tcx>,
+ slice_lint_info: FxHashMap<hir::HirId, SliceLintInformation>,
+ max_suggested_slice: u64,
+ scope: &'tcx hir::Expr<'tcx>,
+) -> FxHashMap<hir::HirId, SliceLintInformation> {
+ let mut visitor = SliceIndexLintingVisitor {
+ cx,
+ slice_lint_info,
+ max_suggested_slice,
+ };
+
+ intravisit::walk_expr(&mut visitor, scope);
+
+ visitor.slice_lint_info
+}
+
+struct SliceIndexLintingVisitor<'a, 'tcx> {
+ cx: &'a LateContext<'tcx>,
+ slice_lint_info: FxHashMap<hir::HirId, SliceLintInformation>,
+ max_suggested_slice: u64,
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for SliceIndexLintingVisitor<'a, 'tcx> {
+ type Map = Map<'tcx>;
+
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
+ }
+
+ fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
+ if let Some(local_id) = path_to_local(expr) {
+ let Self {
+ cx,
+ ref mut slice_lint_info,
+ max_suggested_slice,
+ } = *self;
+
+ if_chain! {
+ // Check if this is even a local we're interested in
+ if let Some(use_info) = slice_lint_info.get_mut(&local_id);
+
+ let map = cx.tcx.hir();
+
+ // Checking for slice indexing
+ let parent_id = map.get_parent_node(expr.hir_id);
+ if let Some(hir::Node::Expr(parent_expr)) = map.find(parent_id);
+ if let hir::ExprKind::Index(_, index_expr) = parent_expr.kind;
+ if let Some((Constant::Int(index_value), _)) = constant(cx, cx.typeck_results(), index_expr);
+ if let Ok(index_value) = index_value.try_into();
+ if index_value < max_suggested_slice;
+
+ // Make sure that this slice index is read only
+ let maybe_addrof_id = map.get_parent_node(parent_id);
+ if let Some(hir::Node::Expr(maybe_addrof_expr)) = map.find(maybe_addrof_id);
+ if let hir::ExprKind::AddrOf(_kind, hir::Mutability::Not, _inner_expr) = maybe_addrof_expr.kind;
+ then {
+ use_info.index_use.push((index_value, map.span(parent_expr.hir_id)));
+ return;
+ }
+ }
+
+ // The slice was used for something other than indexing
+ self.slice_lint_info.remove(&local_id);
+ }
+ intravisit::walk_expr(self, expr);
+ }
+}
/// x[0];
/// x[3];
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub OUT_OF_BOUNDS_INDEXING,
correctness,
"out of bounds constant indexing"
/// y.get(10..);
/// y.get(..100);
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub INDEXING_SLICING,
restriction,
"indexing/slicing usage"
///
/// iter::repeat(1_u8).collect::<Vec<_>>();
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub INFINITE_ITER,
correctness,
"infinite iteration"
/// let infinite_iter = 0..;
/// [0..].iter().zip(infinite_iter.take_while(|x| *x > 5));
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub MAYBE_INFINITE_ITER,
pedantic,
"possible infinite iteration"
//! lint on inherent implementations
use clippy_utils::diagnostics::span_lint_and_note;
-use clippy_utils::{in_macro, is_lint_allowed};
+use clippy_utils::is_lint_allowed;
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::{def_id::LocalDefId, Item, ItemKind, Node};
use rustc_lint::{LateContext, LateLintPass};
/// fn other() {}
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub MULTIPLE_INHERENT_IMPL,
restriction,
"Multiple inherent impl that could be grouped"
..
}) = cx.tcx.hir().get(id)
{
- (!in_macro(span) && impl_item.generics.params.is_empty() && !is_lint_allowed(cx, MULTIPLE_INHERENT_IMPL, id))
- .then(|| span)
+ (!span.from_expansion()
+ && impl_item.generics.params.is_empty()
+ && !is_lint_allowed(cx, MULTIPLE_INHERENT_IMPL, id))
+ .then(|| span)
} else {
None
}
/// }
/// }
/// ```
+ #[clippy::version = "1.38.0"]
pub INHERENT_TO_STRING,
style,
"type implements inherent method `to_string()`, but should instead implement the `Display` trait"
/// }
/// }
/// ```
+ #[clippy::version = "1.38.0"]
pub INHERENT_TO_STRING_SHADOW_DISPLAY,
correctness,
"type implements inherent method `to_string()`, which gets shadowed by the implementation of the `Display` trait"
/// fn name(&self) -> &'static str;
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub INLINE_FN_WITHOUT_BODY,
correctness,
"use of `#[inline]` on trait methods without bodies"
/// # let y = 1;
/// if x > y {}
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub INT_PLUS_ONE,
complexity,
"instead of using `x >= y + 1`, use `x > y`"
/// let x = 3f32 / 2f32;
/// println!("{}", x);
/// ```
+ #[clippy::version = "1.37.0"]
pub INTEGER_DIVISION,
restriction,
"integer division may cause loss of precision"
/// let x: u8 = 1;
/// (x as u32) > 300;
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub INVALID_UPCAST_COMPARISONS,
pedantic,
"a comparison involving an upcast which is always true or false"
/// foo(); // prints "foo"
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub ITEMS_AFTER_STATEMENTS,
pedantic,
"blocks where an item comes after a statement"
/// }
/// }
/// ```
+ #[clippy::version = "1.57.0"]
pub ITER_NOT_RETURNING_ITERATOR,
pedantic,
"methods named `iter` or `iter_mut` that do not return an `Iterator`"
/// // Good
/// pub static a = [0u32; 1_000_000];
/// ```
+ #[clippy::version = "1.44.0"]
pub LARGE_CONST_ARRAYS,
perf,
"large non-scalar const array may cause performance overhead"
/// B(Box<[i32; 8000]>),
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub LARGE_ENUM_VARIANT,
perf,
"large size difference between variants on an enum"
/// ```rust,ignore
/// let a = [0u32; 1_000_000];
/// ```
+ #[clippy::version = "1.41.0"]
pub LARGE_STACK_ARRAYS,
pedantic,
"allocating large arrays on stack may cause stack overflow"
/// ..
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub LEN_ZERO,
style,
"checking `.len() == 0` or `.len() > 0` (or similar) when `.is_empty()` could be used instead"
/// }
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub LEN_WITHOUT_IS_EMPTY,
style,
"traits or impls with a public `len` method but no corresponding `is_empty` method"
/// ..
/// }
/// ```
+ #[clippy::version = "1.49.0"]
pub COMPARISON_TO_EMPTY,
style,
"checking `x == \"\"` or `x == []` (or similar) when `.is_empty()` could be used instead"
/// None
/// };
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub USELESS_LET_IF_SEQ,
nursery,
"unidiomatic `let mut` declaration followed by initialization in `if`"
/// // is_ok() is marked #[must_use]
/// let _ = f().is_ok();
/// ```
+ #[clippy::version = "1.42.0"]
pub LET_UNDERSCORE_MUST_USE,
restriction,
"non-binding let on a `#[must_use]` expression"
declare_clippy_lint! {
/// ### What it does
- /// Checks for `let _ = sync_lock`
+ /// Checks for `let _ = sync_lock`.
+ /// This supports `mutex` and `rwlock` in `std::sync` and `parking_lot`.
///
/// ### Why is this bad?
/// This statement immediately drops the lock instead of
/// ```rust,ignore
/// let _lock = mutex.lock();
/// ```
+ #[clippy::version = "1.43.0"]
pub LET_UNDERSCORE_LOCK,
correctness,
"non-binding let on a synchronization lock"
/// // dropped at end of scope
/// }
/// ```
+ #[clippy::version = "1.50.0"]
pub LET_UNDERSCORE_DROP,
pedantic,
"non-binding let on a type that implements `Drop`"
declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK, LET_UNDERSCORE_DROP]);
-const SYNC_GUARD_PATHS: [&[&str]; 3] = [
+const SYNC_GUARD_PATHS: [&[&str]; 5] = [
&paths::MUTEX_GUARD,
&paths::RWLOCK_READ_GUARD,
&paths::RWLOCK_WRITE_GUARD,
+ &paths::PARKING_LOT_RAWMUTEX,
+ &paths::PARKING_LOT_RAWRWLOCK,
];
impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
LintId::of(copies::IFS_SAME_COND),
LintId::of(copies::IF_SAME_THEN_ELSE),
LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT),
+ LintId::of(dereference::NEEDLESS_BORROW),
LintId::of(derivable_impls::DERIVABLE_IMPLS),
LintId::of(derive::DERIVE_HASH_XOR_EQ),
LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD),
LintId::of(methods::MANUAL_SPLIT_ONCE),
LintId::of(methods::MANUAL_STR_REPEAT),
LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
+ LintId::of(methods::MAP_FLATTEN),
LintId::of(methods::MAP_IDENTITY),
+ LintId::of(methods::NEEDLESS_SPLITN),
LintId::of(methods::NEW_RET_NO_SELF),
LintId::of(methods::OK_EXPECT),
LintId::of(methods::OPTION_AS_REF_DEREF),
LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE),
LintId::of(needless_bool::BOOL_COMPARISON),
LintId::of(needless_bool::NEEDLESS_BOOL),
- LintId::of(needless_borrow::NEEDLESS_BORROW),
LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
+ LintId::of(needless_late_init::NEEDLESS_LATE_INIT),
LintId::of(needless_option_as_deref::NEEDLESS_OPTION_AS_DEREF),
LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK),
LintId::of(needless_update::NEEDLESS_UPDATE),
LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
LintId::of(non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY),
+ LintId::of(octal_escapes::OCTAL_ESCAPES),
LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP),
LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL),
LintId::of(methods::MANUAL_FILTER_MAP),
LintId::of(methods::MANUAL_FIND_MAP),
LintId::of(methods::MANUAL_SPLIT_ONCE),
+ LintId::of(methods::MAP_FLATTEN),
LintId::of(methods::MAP_IDENTITY),
+ LintId::of(methods::NEEDLESS_SPLITN),
LintId::of(methods::OPTION_AS_REF_DEREF),
LintId::of(methods::OPTION_FILTER_MAP),
LintId::of(methods::SEARCH_IS_SOME),
LintId::of(utils::internal_lints::DEFAULT_LINT),
LintId::of(utils::internal_lints::IF_CHAIN_STYLE),
LintId::of(utils::internal_lints::INTERNING_DEFINED_SYMBOL),
+ LintId::of(utils::internal_lints::INVALID_CLIPPY_VERSION_ATTRIBUTE),
LintId::of(utils::internal_lints::INVALID_PATHS),
LintId::of(utils::internal_lints::LINT_WITHOUT_LINT_PASS),
LintId::of(utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM),
+ LintId::of(utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE),
LintId::of(utils::internal_lints::OUTER_EXPN_EXPN_DATA),
LintId::of(utils::internal_lints::PRODUCE_ICE),
LintId::of(utils::internal_lints::UNNECESSARY_SYMBOL_STR),
#[cfg(feature = "internal-lints")]
utils::internal_lints::INTERNING_DEFINED_SYMBOL,
#[cfg(feature = "internal-lints")]
+ utils::internal_lints::INVALID_CLIPPY_VERSION_ATTRIBUTE,
+ #[cfg(feature = "internal-lints")]
utils::internal_lints::INVALID_PATHS,
#[cfg(feature = "internal-lints")]
utils::internal_lints::LINT_WITHOUT_LINT_PASS,
#[cfg(feature = "internal-lints")]
utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
#[cfg(feature = "internal-lints")]
+ utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE,
+ #[cfg(feature = "internal-lints")]
utils::internal_lints::OUTER_EXPN_EXPN_DATA,
#[cfg(feature = "internal-lints")]
utils::internal_lints::PRODUCE_ICE,
default::FIELD_REASSIGN_WITH_DEFAULT,
default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK,
dereference::EXPLICIT_DEREF_METHODS,
+ dereference::NEEDLESS_BORROW,
+ dereference::REF_BINDING_TO_REFERENCE,
derivable_impls::DERIVABLE_IMPLS,
derive::DERIVE_HASH_XOR_EQ,
derive::DERIVE_ORD_XOR_PARTIAL_ORD,
derive::EXPL_IMPL_CLONE_ON_COPY,
derive::UNSAFE_DERIVE_DESERIALIZE,
- disallowed_method::DISALLOWED_METHOD,
+ disallowed_methods::DISALLOWED_METHODS,
disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS,
- disallowed_type::DISALLOWED_TYPE,
+ disallowed_types::DISALLOWED_TYPES,
doc::DOC_MARKDOWN,
doc::MISSING_ERRORS_DOC,
doc::MISSING_PANICS_DOC,
implicit_return::IMPLICIT_RETURN,
implicit_saturating_sub::IMPLICIT_SATURATING_SUB,
inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR,
+ index_refutable_slice::INDEX_REFUTABLE_SLICE,
indexing_slicing::INDEXING_SLICING,
indexing_slicing::OUT_OF_BOUNDS_INDEXING,
infinite_iter::INFINITE_ITER,
methods::MAP_FLATTEN,
methods::MAP_IDENTITY,
methods::MAP_UNWRAP_OR,
+ methods::NEEDLESS_SPLITN,
methods::NEW_RET_NO_SELF,
methods::OK_EXPECT,
methods::OPTION_AS_REF_DEREF,
needless_bitwise_bool::NEEDLESS_BITWISE_BOOL,
needless_bool::BOOL_COMPARISON,
needless_bool::NEEDLESS_BOOL,
- needless_borrow::NEEDLESS_BORROW,
- needless_borrow::REF_BINDING_TO_REFERENCE,
needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE,
needless_continue::NEEDLESS_CONTINUE,
needless_for_each::NEEDLESS_FOR_EACH,
+ needless_late_init::NEEDLESS_LATE_INIT,
needless_option_as_deref::NEEDLESS_OPTION_AS_DEREF,
needless_pass_by_value::NEEDLESS_PASS_BY_VALUE,
needless_question_mark::NEEDLESS_QUESTION_MARK,
non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS,
non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY,
nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES,
+ octal_escapes::OCTAL_ESCAPES,
open_options::NONSENSICAL_OPEN_OPTIONS,
option_env_unwrap::OPTION_ENV_UNWRAP,
option_if_let_else::OPTION_IF_LET_ELSE,
LintId::of(attrs::EMPTY_LINE_AFTER_OUTER_ATTR),
LintId::of(cognitive_complexity::COGNITIVE_COMPLEXITY),
LintId::of(copies::BRANCHES_SHARING_CODE),
- LintId::of(disallowed_method::DISALLOWED_METHOD),
- LintId::of(disallowed_type::DISALLOWED_TYPE),
+ LintId::of(disallowed_methods::DISALLOWED_METHODS),
+ LintId::of(disallowed_types::DISALLOWED_TYPES),
LintId::of(equatable_if_let::EQUATABLE_IF_LET),
LintId::of(fallible_impl_from::FALLIBLE_IMPL_FROM),
LintId::of(floating_point_arithmetic::IMPRECISE_FLOPS),
LintId::of(floating_point_arithmetic::SUBOPTIMAL_FLOPS),
LintId::of(future_not_send::FUTURE_NOT_SEND),
+ LintId::of(index_refutable_slice::INDEX_REFUTABLE_SLICE),
LintId::of(let_if_seq::USELESS_LET_IF_SEQ),
LintId::of(missing_const_for_fn::MISSING_CONST_FOR_FN),
LintId::of(mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL),
LintId::of(copy_iterator::COPY_ITERATOR),
LintId::of(default::DEFAULT_TRAIT_ACCESS),
LintId::of(dereference::EXPLICIT_DEREF_METHODS),
+ LintId::of(dereference::REF_BINDING_TO_REFERENCE),
LintId::of(derive::EXPL_IMPL_CLONE_ON_COPY),
LintId::of(derive::UNSAFE_DERIVE_DESERIALIZE),
LintId::of(doc::DOC_MARKDOWN),
LintId::of(methods::FROM_ITER_INSTEAD_OF_COLLECT),
LintId::of(methods::IMPLICIT_CLONE),
LintId::of(methods::INEFFICIENT_TO_STRING),
- LintId::of(methods::MAP_FLATTEN),
LintId::of(methods::MAP_UNWRAP_OR),
LintId::of(misc::FLOAT_CMP),
LintId::of(misc::USED_UNDERSCORE_BINDING),
LintId::of(mut_mut::MUT_MUT),
LintId::of(needless_bitwise_bool::NEEDLESS_BITWISE_BOOL),
- LintId::of(needless_borrow::REF_BINDING_TO_REFERENCE),
LintId::of(needless_continue::NEEDLESS_CONTINUE),
LintId::of(needless_for_each::NEEDLESS_FOR_EACH),
LintId::of(needless_pass_by_value::NEEDLESS_PASS_BY_VALUE),
LintId::of(collapsible_match::COLLAPSIBLE_MATCH),
LintId::of(comparison_chain::COMPARISON_CHAIN),
LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT),
+ LintId::of(dereference::NEEDLESS_BORROW),
LintId::of(doc::MISSING_SAFETY_DOC),
LintId::of(doc::NEEDLESS_DOCTEST_MAIN),
LintId::of(enum_variants::ENUM_VARIANT_NAMES),
LintId::of(misc_early::REDUNDANT_PATTERN),
LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK),
LintId::of(mut_reference::UNNECESSARY_MUT_PASSED),
- LintId::of(needless_borrow::NEEDLESS_BORROW),
+ LintId::of(needless_late_init::NEEDLESS_LATE_INIT),
LintId::of(neg_multiply::NEG_MULTIPLY),
LintId::of(new_without_default::NEW_WITHOUT_DEFAULT),
LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST),
LintId::of(methods::SUSPICIOUS_MAP),
LintId::of(mut_key::MUTABLE_KEY_TYPE),
LintId::of(non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY),
+ LintId::of(octal_escapes::OCTAL_ESCAPES),
LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
])
#![feature(rustc_private)]
#![feature(stmt_expr_attributes)]
#![feature(control_flow_enum)]
+#![feature(let_else)]
#![recursion_limit = "512"]
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
#![allow(clippy::missing_docs_in_private_items, clippy::must_use_candidate)]
#[cfg(feature = "metadata-collector-lint")]
mod deprecated_lints;
+#[cfg_attr(
+ any(feature = "internal-lints", feature = "metadata-collector-lint"),
+ allow(clippy::missing_clippy_version_attribute)
+)]
mod utils;
// begin lints modules, do not remove this comment, it’s used in `update_lints`
mod dereference;
mod derivable_impls;
mod derive;
-mod disallowed_method;
+mod disallowed_methods;
mod disallowed_script_idents;
-mod disallowed_type;
+mod disallowed_types;
mod doc;
mod double_comparison;
mod double_parens;
mod implicit_return;
mod implicit_saturating_sub;
mod inconsistent_struct_constructor;
+mod index_refutable_slice;
mod indexing_slicing;
mod infinite_iter;
mod inherent_impl;
mod needless_arbitrary_self_type;
mod needless_bitwise_bool;
mod needless_bool;
-mod needless_borrow;
mod needless_borrowed_ref;
mod needless_continue;
mod needless_for_each;
+mod needless_late_init;
mod needless_option_as_deref;
mod needless_pass_by_value;
mod needless_question_mark;
mod non_octal_unix_permissions;
mod non_send_fields_in_send_ty;
mod nonstandard_macro_braces;
+mod octal_escapes;
mod open_options;
mod option_env_unwrap;
mod option_if_let_else;
/// level (i.e `#![cfg_attr(...)]`) will still be expanded even when using a pre-expansion pass.
///
/// Used in `./src/driver.rs`.
-pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore) {
+pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) {
// NOTE: Do not add any more pre-expansion passes. These should be removed eventually.
+
+ let msrv = conf.msrv.as_ref().and_then(|s| {
+ parse_msrv(s, None, None).or_else(|| {
+ sess.err(&format!(
+ "error reading Clippy's configuration file. `{}` is not a valid Rust version",
+ s
+ ));
+ None
+ })
+ });
+
store.register_pre_expansion_pass(|| Box::new(write::Write::default()));
- store.register_pre_expansion_pass(|| Box::new(attrs::EarlyAttributes));
+ store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes { msrv }));
store.register_pre_expansion_pass(|| Box::new(dbg_macro::DbgMacro));
}
///
/// Used in `./src/driver.rs`.
#[allow(clippy::too_many_lines)]
-#[rustfmt::skip]
pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) {
register_removed_non_tool_lints(store);
let vec_box_size_threshold = conf.vec_box_size_threshold;
let type_complexity_threshold = conf.type_complexity_threshold;
let avoid_breaking_exported_api = conf.avoid_breaking_exported_api;
- store.register_late_pass(move || Box::new(types::Types::new(
- vec_box_size_threshold,
- type_complexity_threshold,
- avoid_breaking_exported_api,
- )));
+ store.register_late_pass(move || {
+ Box::new(types::Types::new(
+ vec_box_size_threshold,
+ type_complexity_threshold,
+ avoid_breaking_exported_api,
+ ))
+ });
store.register_late_pass(|| Box::new(booleans::NonminimalBool));
store.register_late_pass(|| Box::new(needless_bitwise_bool::NeedlessBitwiseBool));
store.register_late_pass(|| Box::new(eq_op::EqOp));
let msrv = conf.msrv.as_ref().and_then(|s| {
parse_msrv(s, None, None).or_else(|| {
- sess.err(&format!("error reading Clippy's configuration file. `{}` is not a valid Rust version", s));
+ sess.err(&format!(
+ "error reading Clippy's configuration file. `{}` is not a valid Rust version",
+ s
+ ));
None
})
});
store.register_late_pass(|| Box::new(size_of_in_element_count::SizeOfInElementCount));
store.register_late_pass(|| Box::new(same_name_method::SameNameMethod));
+ let max_suggested_slice_pattern_length = conf.max_suggested_slice_pattern_length;
+ store.register_late_pass(move || {
+ Box::new(index_refutable_slice::IndexRefutableSlice::new(
+ max_suggested_slice_pattern_length,
+ msrv,
+ ))
+ });
store.register_late_pass(|| Box::new(map_clone::MapClone));
store.register_late_pass(|| Box::new(map_err_ignore::MapErrIgnore));
store.register_late_pass(|| Box::new(shadow::Shadow::default()));
store.register_late_pass(|| Box::new(zero_div_zero::ZeroDiv));
store.register_late_pass(|| Box::new(mutex_atomic::Mutex));
store.register_late_pass(|| Box::new(needless_update::NeedlessUpdate));
- store.register_late_pass(|| Box::new(needless_borrow::NeedlessBorrow::default()));
store.register_late_pass(|| Box::new(needless_borrowed_ref::NeedlessBorrowedRef));
store.register_late_pass(|| Box::new(no_effect::NoEffect));
store.register_late_pass(|| Box::new(temporary_assignment::TemporaryAssignment));
store.register_late_pass(|| Box::new(transmute::Transmute));
let cognitive_complexity_threshold = conf.cognitive_complexity_threshold;
- store.register_late_pass(move || Box::new(cognitive_complexity::CognitiveComplexity::new(cognitive_complexity_threshold)));
+ store.register_late_pass(move || {
+ Box::new(cognitive_complexity::CognitiveComplexity::new(
+ cognitive_complexity_threshold,
+ ))
+ });
let too_large_for_stack = conf.too_large_for_stack;
- store.register_late_pass(move || Box::new(escape::BoxedLocal{too_large_for_stack}));
- store.register_late_pass(move || Box::new(vec::UselessVec{too_large_for_stack}));
+ store.register_late_pass(move || Box::new(escape::BoxedLocal { too_large_for_stack }));
+ store.register_late_pass(move || Box::new(vec::UselessVec { too_large_for_stack }));
store.register_late_pass(|| Box::new(panic_unimplemented::PanicUnimplemented));
store.register_late_pass(|| Box::new(strings::StringLitAsBytes));
store.register_late_pass(|| Box::new(derive::Derive));
store.register_late_pass(move || Box::new(blacklisted_name::BlacklistedName::new(blacklisted_names.clone())));
let too_many_arguments_threshold = conf.too_many_arguments_threshold;
let too_many_lines_threshold = conf.too_many_lines_threshold;
- store.register_late_pass(move || Box::new(functions::Functions::new(too_many_arguments_threshold, too_many_lines_threshold)));
+ store.register_late_pass(move || {
+ Box::new(functions::Functions::new(
+ too_many_arguments_threshold,
+ too_many_lines_threshold,
+ ))
+ });
let doc_valid_idents = conf.doc_valid_idents.iter().cloned().collect::<FxHashSet<_>>();
store.register_late_pass(move || Box::new(doc::DocMarkdown::new(doc_valid_idents.clone())));
store.register_late_pass(|| Box::new(neg_multiply::NegMultiply));
store.register_late_pass(|| Box::new(multiple_crate_versions::MultipleCrateVersions));
store.register_late_pass(|| Box::new(wildcard_dependencies::WildcardDependencies));
let literal_representation_lint_fraction_readability = conf.unreadable_literal_lint_fractions;
- store.register_early_pass(move || Box::new(literal_representation::LiteralDigitGrouping::new(literal_representation_lint_fraction_readability)));
+ store.register_early_pass(move || {
+ Box::new(literal_representation::LiteralDigitGrouping::new(
+ literal_representation_lint_fraction_readability,
+ ))
+ });
let literal_representation_threshold = conf.literal_representation_threshold;
- store.register_early_pass(move || Box::new(literal_representation::DecimalLiteralRepresentation::new(literal_representation_threshold)));
+ store.register_early_pass(move || {
+ Box::new(literal_representation::DecimalLiteralRepresentation::new(
+ literal_representation_threshold,
+ ))
+ });
let enum_variant_name_threshold = conf.enum_variant_name_threshold;
- store.register_late_pass(move || Box::new(enum_variants::EnumVariantNames::new(enum_variant_name_threshold, avoid_breaking_exported_api)));
+ store.register_late_pass(move || {
+ Box::new(enum_variants::EnumVariantNames::new(
+ enum_variant_name_threshold,
+ avoid_breaking_exported_api,
+ ))
+ });
store.register_early_pass(|| Box::new(tabs_in_doc_comments::TabsInDocComments));
let upper_case_acronyms_aggressive = conf.upper_case_acronyms_aggressive;
- store.register_late_pass(move || Box::new(upper_case_acronyms::UpperCaseAcronyms::new(avoid_breaking_exported_api, upper_case_acronyms_aggressive)));
+ store.register_late_pass(move || {
+ Box::new(upper_case_acronyms::UpperCaseAcronyms::new(
+ avoid_breaking_exported_api,
+ upper_case_acronyms_aggressive,
+ ))
+ });
store.register_late_pass(|| Box::new(default::Default::default()));
store.register_late_pass(|| Box::new(unused_self::UnusedSelf));
store.register_late_pass(|| Box::new(mutable_debug_assertion::DebugAssertWithMutCall));
store.register_early_pass(|| Box::new(single_component_path_imports::SingleComponentPathImports));
let max_fn_params_bools = conf.max_fn_params_bools;
let max_struct_bools = conf.max_struct_bools;
- store.register_early_pass(move || Box::new(excessive_bools::ExcessiveBools::new(max_struct_bools, max_fn_params_bools)));
+ store.register_early_pass(move || {
+ Box::new(excessive_bools::ExcessiveBools::new(
+ max_struct_bools,
+ max_fn_params_bools,
+ ))
+ });
store.register_early_pass(|| Box::new(option_env_unwrap::OptionEnvUnwrap));
let warn_on_all_wildcard_imports = conf.warn_on_all_wildcard_imports;
store.register_late_pass(move || Box::new(wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports)));
store.register_late_pass(|| Box::new(vec_resize_to_zero::VecResizeToZero));
store.register_late_pass(|| Box::new(panic_in_result_fn::PanicInResultFn));
let single_char_binding_names_threshold = conf.single_char_binding_names_threshold;
- store.register_early_pass(move || Box::new(non_expressive_names::NonExpressiveNames {
- single_char_binding_names_threshold,
- }));
+ store.register_early_pass(move || {
+ Box::new(non_expressive_names::NonExpressiveNames {
+ single_char_binding_names_threshold,
+ })
+ });
let macro_matcher = conf.standard_macro_braces.iter().cloned().collect::<FxHashSet<_>>();
store.register_early_pass(move || Box::new(nonstandard_macro_braces::MacroBraces::new(¯o_matcher)));
store.register_late_pass(|| Box::new(macro_use::MacroUseImports::default()));
store.register_late_pass(|| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned));
store.register_late_pass(|| Box::new(async_yields_async::AsyncYieldsAsync));
let disallowed_methods = conf.disallowed_methods.clone();
- store.register_late_pass(move || Box::new(disallowed_method::DisallowedMethod::new(disallowed_methods.clone())));
+ store.register_late_pass(move || Box::new(disallowed_methods::DisallowedMethods::new(disallowed_methods.clone())));
store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86AttSyntax));
store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86IntelSyntax));
store.register_late_pass(|| Box::new(undropped_manually_drops::UndroppedManuallyDrops));
store.register_late_pass(|| Box::new(strings::StringToString));
store.register_late_pass(|| Box::new(zero_sized_map_values::ZeroSizedMapValues));
store.register_late_pass(|| Box::new(vec_init_then_push::VecInitThenPush::default()));
- store.register_late_pass(|| Box::new(case_sensitive_file_extension_comparisons::CaseSensitiveFileExtensionComparisons));
+ store.register_late_pass(|| {
+ Box::new(case_sensitive_file_extension_comparisons::CaseSensitiveFileExtensionComparisons)
+ });
store.register_late_pass(|| Box::new(redundant_slicing::RedundantSlicing));
store.register_late_pass(|| Box::new(from_str_radix_10::FromStrRadix10));
store.register_late_pass(|| Box::new(manual_map::ManualMap));
store.register_early_pass(move || Box::new(module_style::ModStyle));
store.register_late_pass(|| Box::new(unused_async::UnusedAsync));
let disallowed_types = conf.disallowed_types.clone();
- store.register_late_pass(move || Box::new(disallowed_type::DisallowedType::new(disallowed_types.clone())));
+ store.register_late_pass(move || Box::new(disallowed_types::DisallowedTypes::new(disallowed_types.clone())));
let import_renames = conf.enforced_import_renames.clone();
- store.register_late_pass(move || Box::new(missing_enforced_import_rename::ImportRename::new(import_renames.clone())));
+ store.register_late_pass(move || {
+ Box::new(missing_enforced_import_rename::ImportRename::new(
+ import_renames.clone(),
+ ))
+ });
let scripts = conf.allowed_scripts.clone();
store.register_early_pass(move || Box::new(disallowed_script_idents::DisallowedScriptIdents::new(&scripts)));
store.register_late_pass(|| Box::new(strlen_on_c_strings::StrlenOnCStrings));
store.register_late_pass(move || Box::new(iter_not_returning_iterator::IterNotReturningIterator));
store.register_late_pass(move || Box::new(manual_assert::ManualAssert));
let enable_raw_pointer_heuristic_for_send = conf.enable_raw_pointer_heuristic_for_send;
- store.register_late_pass(move || Box::new(non_send_fields_in_send_ty::NonSendFieldInSendTy::new(enable_raw_pointer_heuristic_for_send)));
+ store.register_late_pass(move || {
+ Box::new(non_send_fields_in_send_ty::NonSendFieldInSendTy::new(
+ enable_raw_pointer_heuristic_for_send,
+ ))
+ });
store.register_late_pass(move || Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks::default()));
store.register_late_pass(|| Box::new(match_str_case_mismatch::MatchStrCaseMismatch));
store.register_late_pass(move || Box::new(format_args::FormatArgs));
store.register_late_pass(|| Box::new(trailing_empty_array::TrailingEmptyArray));
+ store.register_early_pass(|| Box::new(octal_escapes::OctalEscapes));
+ store.register_late_pass(|| Box::new(needless_late_init::NeedlessLateInit));
// add lints here, do not remove this comment, it's used in `new_lint`
}
ls.register_renamed("clippy::zero_width_space", "clippy::invisible_characters");
ls.register_renamed("clippy::single_char_push_str", "clippy::single_char_add_str");
ls.register_renamed("clippy::if_let_some_result", "clippy::match_result_ok");
+ ls.register_renamed("clippy::disallowed_type", "clippy::disallowed_types");
+ ls.register_renamed("clippy::disallowed_method", "clippy::disallowed_methods");
// uplifted lints
ls.register_renamed("clippy::invalid_ref", "invalid_value");
use clippy_utils::diagnostics::span_lint;
-use clippy_utils::{in_macro, trait_ref_of_method};
+use clippy_utils::trait_ref_of_method;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir::intravisit::{
walk_fn_decl, walk_generic_param, walk_generics, walk_item, walk_param_bound, walk_poly_trait_ref, walk_ty,
/// x
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub NEEDLESS_LIFETIMES,
complexity,
"using explicit lifetimes for references in function arguments when elision rules \
/// // ...
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub EXTRA_UNUSED_LIFETIMES,
complexity,
"unused lifetimes in function definitions"
span: Span,
report_extra_lifetimes: bool,
) {
- if in_macro(span) || has_where_lifetimes(cx, &generics.where_clause) {
+ if span.from_expansion() || has_where_lifetimes(cx, &generics.where_clause) {
return;
}
//! floating-point literal expressions.
use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::numeric_literal::{NumericLiteral, Radix};
use clippy_utils::source::snippet_opt;
-use clippy_utils::{
- in_macro,
- numeric_literal::{NumericLiteral, Radix},
-};
use if_chain::if_chain;
use rustc_ast::ast::{Expr, ExprKind, Lit, LitKind};
use rustc_errors::Applicability;
/// // Good
/// let x: u64 = 61_864_918_973_511;
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub UNREADABLE_LITERAL,
pedantic,
"long literal without underscores"
/// // Good
/// 2_i32;
/// ```
+ #[clippy::version = "1.30.0"]
pub MISTYPED_LITERAL_SUFFIXES,
correctness,
"mistyped literal suffix"
/// // Good
/// let x: u64 = 61_864_918_973_511;
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub INCONSISTENT_DIGIT_GROUPING,
style,
"integer literals with digits grouped inconsistently"
/// let x: u32 = 0xFFF_FFF;
/// let y: u8 = 0b01_011_101;
/// ```
+ #[clippy::version = "1.49.0"]
pub UNUSUAL_BYTE_GROUPINGS,
style,
"binary or hex literals that aren't grouped by four"
/// ```rust
/// let x: u64 = 6186491_8973511;
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub LARGE_DIGIT_GROUPS,
pedantic,
"grouping digits into groups that are too large"
/// `255` => `0xFF`
/// `65_535` => `0xFFFF`
/// `4_042_322_160` => `0xF0F0_F0F0`
+ #[clippy::version = "pre 1.29.0"]
pub DECIMAL_LITERAL_REPRESENTATION,
restriction,
"using decimal representation when hexadecimal would be better"
| WarningType::InconsistentDigitGrouping
| WarningType::UnusualByteGroupings
| WarningType::LargeDigitGroups => {
- !in_macro(lit.span)
+ !lit.span.from_expansion()
}
WarningType::DecimalRepresentation | WarningType::MistypedLiteralSuffix => {
true
use super::{make_iterator_snippet, IncrementVisitor, InitializeVisitor, EXPLICIT_COUNTER_LOOP};
-use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::{get_enclosing_block, is_integer_const};
use if_chain::if_chain;
use rustc_hir::intravisit::{walk_block, walk_expr};
use rustc_hir::{Expr, Pat};
use rustc_lint::LateContext;
+use rustc_middle::ty::{self, UintTy};
// To trigger the EXPLICIT_COUNTER_LOOP lint, a variable must be
// incremented exactly once in the loop body, and initialized to zero
walk_block(&mut initialize_visitor, block);
if_chain! {
- if let Some((name, initializer)) = initialize_visitor.get_result();
+ if let Some((name, ty, initializer)) = initialize_visitor.get_result();
if is_integer_const(cx, initializer, 0);
then {
let mut applicability = Applicability::MachineApplicable;
- span_lint_and_sugg(
+ let int_name = match ty.map(ty::TyS::kind) {
+ // usize or inferred
+ Some(ty::Uint(UintTy::Usize)) | None => {
+ span_lint_and_sugg(
+ cx,
+ EXPLICIT_COUNTER_LOOP,
+ expr.span.with_hi(arg.span.hi()),
+ &format!("the variable `{}` is used as a loop counter", name),
+ "consider using",
+ format!(
+ "for ({}, {}) in {}.enumerate()",
+ name,
+ snippet_with_applicability(cx, pat.span, "item", &mut applicability),
+ make_iterator_snippet(cx, arg, &mut applicability),
+ ),
+ applicability,
+ );
+ return;
+ }
+ Some(ty::Int(int_ty)) => int_ty.name_str(),
+ Some(ty::Uint(uint_ty)) => uint_ty.name_str(),
+ _ => return,
+ };
+
+ span_lint_and_then(
cx,
EXPLICIT_COUNTER_LOOP,
expr.span.with_hi(arg.span.hi()),
&format!("the variable `{}` is used as a loop counter", name),
- "consider using",
- format!(
- "for ({}, {}) in {}.enumerate()",
- name,
- snippet_with_applicability(cx, pat.span, "item", &mut applicability),
- make_iterator_snippet(cx, arg, &mut applicability),
- ),
- applicability,
+ |diag| {
+ diag.span_suggestion(
+ expr.span.with_hi(arg.span.hi()),
+ "consider using",
+ format!(
+ "for ({}, {}) in (0_{}..).zip({})",
+ name,
+ snippet_with_applicability(cx, pat.span, "item", &mut applicability),
+ int_name,
+ make_iterator_snippet(cx, arg, &mut applicability),
+ ),
+ applicability,
+ );
+
+ diag.note(&format!(
+ "`{}` is of type `{}`, making it ineligible for `Iterator::enumerate`",
+ name, int_name
+ ));
+ },
);
}
}
let mut initialize_visitor = InitializeVisitor::new(cx, expr, var_id);
walk_block(&mut initialize_visitor, block);
- initialize_visitor.get_result().map(|(_, initializer)| Start {
+ initialize_visitor.get_result().map(|(_, _, initializer)| Start {
id: var_id,
kind: StartKind::Counter { initializer },
})
/// # let mut dst = vec![0; 65];
/// dst[64..(src.len() + 64)].clone_from_slice(&src[..]);
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub MANUAL_MEMCPY,
perf,
"manually copying items between slices"
/// println!("{}", i);
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub NEEDLESS_RANGE_LOOP,
style,
"for-looping over a range of indices where an iterator over items would do"
/// // ..
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub EXPLICIT_ITER_LOOP,
pedantic,
"for-looping over `_.iter()` or `_.iter_mut()` when `&_` or `&mut _` would do"
/// // ..
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub EXPLICIT_INTO_ITER_LOOP,
pedantic,
"for-looping over `_.into_iter()` when `_` would do"
/// ..
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub ITER_NEXT_LOOP,
correctness,
"for-looping over `_.next()` which is probably not intended"
/// // ..
/// }
/// ```
+ #[clippy::version = "1.45.0"]
pub FOR_LOOPS_OVER_FALLIBLES,
suspicious,
"for-looping over an `Option` or a `Result`, which is more clearly expressed as an `if let`"
/// // .. do something with x
/// };
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub WHILE_LET_LOOP,
complexity,
"`loop { if let { ... } else break }`, which can be written as a `while let` loop"
/// // should be
/// let len = iterator.count();
/// ```
+ #[clippy::version = "1.30.0"]
pub NEEDLESS_COLLECT,
perf,
"collecting an iterator when collect is not needed"
/// # fn bar(bar: usize, baz: usize) {}
/// for (i, item) in v.iter().enumerate() { bar(i, *item); }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub EXPLICIT_COUNTER_LOOP,
complexity,
"for-looping with an explicit counter when `_.enumerate()` would do"
/// ```no_run
/// loop {}
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub EMPTY_LOOP,
suspicious,
"empty `loop {}`, which should block or sleep"
/// ..
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub WHILE_LET_ON_ITERATOR,
style,
"using a `while let` loop instead of a for loop on an iterator"
/// ..
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub FOR_KV_MAP,
style,
"looping on a map using `iter` when `keys` or `values` would do"
/// break;
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub NEVER_LOOP,
correctness,
"any loop that will always `break` or `return`"
/// println!("{}", i); // prints numbers from 0 to 42, not 0 to 21
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub MUT_RANGE_BOUND,
suspicious,
"for loop over a range where one of the bounds is a mutable variable"
/// println!("let me loop forever!");
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub WHILE_IMMUTABLE_CONDITION,
correctness,
"variables used within while expression are not mutated in the body"
/// let mut vec: Vec<u8> = vec![item1; 20];
/// vec.resize(20 + 30, item2);
/// ```
+ #[clippy::version = "1.47.0"]
pub SAME_ITEM_PUSH,
style,
"the same item is pushed inside of a for loop"
/// let item = &item1;
/// println!("{}", item);
/// ```
+ #[clippy::version = "1.49.0"]
pub SINGLE_ELEMENT_LOOP,
complexity,
"there is no reason to have a single element loop"
/// println!("{}", n);
/// }
/// ```
+ #[clippy::version = "1.52.0"]
pub MANUAL_FLATTEN,
complexity,
"for loops over `Option`s or `Result`s with a single expression can be simplified"
use clippy_utils::source::{snippet, snippet_with_applicability};
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{is_trait_method, path_to_local_id};
+use clippy_utils::{can_move_expr_to_closure, is_trait_method, path_to_local, path_to_local_id, CaptureKind};
use if_chain::if_chain;
+use rustc_data_structures::fx::FxHashMap;
use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_block, walk_expr, NestedVisitorMap, Visitor};
-use rustc_hir::{Block, Expr, ExprKind, HirId, PatKind, StmtKind};
+use rustc_hir::{Block, Expr, ExprKind, HirId, HirIdSet, Local, Mutability, Node, PatKind, Stmt, StmtKind};
use rustc_lint::LateContext;
use rustc_middle::hir::map::Map;
+use rustc_middle::ty::subst::GenericArgKind;
+use rustc_middle::ty::{self, TyS};
use rustc_span::sym;
use rustc_span::{MultiSpan, Span};
is_type_diagnostic_item(cx, ty, sym::VecDeque) ||
is_type_diagnostic_item(cx, ty, sym::BinaryHeap) ||
is_type_diagnostic_item(cx, ty, sym::LinkedList);
- if let Some(iter_calls) = detect_iter_and_into_iters(block, id);
+ let iter_ty = cx.typeck_results().expr_ty(iter_source);
+ if let Some(iter_calls) = detect_iter_and_into_iters(block, id, cx, get_captured_ids(cx, iter_ty));
if let [iter_call] = &*iter_calls;
then {
let mut used_count_visitor = UsedCountVisitor {
Contains(Span),
}
-struct IterFunctionVisitor {
- uses: Vec<IterFunction>,
+struct IterFunctionVisitor<'a, 'tcx> {
+ illegal_mutable_capture_ids: HirIdSet,
+ current_mutably_captured_ids: HirIdSet,
+ cx: &'a LateContext<'tcx>,
+ uses: Vec<Option<IterFunction>>,
+ hir_id_uses_map: FxHashMap<HirId, usize>,
+ current_statement_hir_id: Option<HirId>,
seen_other: bool,
target: HirId,
}
-impl<'tcx> Visitor<'tcx> for IterFunctionVisitor {
+impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> {
+ fn visit_block(&mut self, block: &'tcx Block<'tcx>) {
+ for (expr, hir_id) in block.stmts.iter().filter_map(get_expr_and_hir_id_from_stmt) {
+ self.visit_block_expr(expr, hir_id);
+ }
+ if let Some(expr) = block.expr {
+ self.visit_block_expr(expr, None);
+ }
+ }
+
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 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);
+ return;
+ }
+
if path_to_local_id(recv, self.target) {
- match &*method_name.ident.name.as_str() {
- "into_iter" => self.uses.push(IterFunction {
- func: IterFunctionKind::IntoIter,
- span: expr.span,
- }),
- "len" => self.uses.push(IterFunction {
- func: IterFunctionKind::Len,
- span: expr.span,
- }),
- "is_empty" => self.uses.push(IterFunction {
- func: IterFunctionKind::IsEmpty,
- span: expr.span,
- }),
- "contains" => self.uses.push(IterFunction {
- func: IterFunctionKind::Contains(args[0].span),
- span: expr.span,
- }),
- _ => self.seen_other = true,
+ if self
+ .illegal_mutable_capture_ids
+ .intersection(&self.current_mutably_captured_ids)
+ .next()
+ .is_none()
+ {
+ if let Some(hir_id) = self.current_statement_hir_id {
+ self.hir_id_uses_map.insert(hir_id, self.uses.len());
+ }
+ match &*method_name.ident.name.as_str() {
+ "into_iter" => self.uses.push(Some(IterFunction {
+ func: IterFunctionKind::IntoIter,
+ span: expr.span,
+ })),
+ "len" => self.uses.push(Some(IterFunction {
+ func: IterFunctionKind::Len,
+ span: expr.span,
+ })),
+ "is_empty" => self.uses.push(Some(IterFunction {
+ func: IterFunctionKind::IsEmpty,
+ span: expr.span,
+ })),
+ "contains" => self.uses.push(Some(IterFunction {
+ func: IterFunctionKind::Contains(args[0].span),
+ span: expr.span,
+ })),
+ _ => {
+ self.seen_other = true;
+ if let Some(hir_id) = self.current_statement_hir_id {
+ self.hir_id_uses_map.remove(&hir_id);
+ }
+ },
+ }
}
return;
}
+
+ if let Some(hir_id) = path_to_local(recv) {
+ if let Some(index) = self.hir_id_uses_map.remove(&hir_id) {
+ if self
+ .illegal_mutable_capture_ids
+ .intersection(&self.current_mutably_captured_ids)
+ .next()
+ .is_none()
+ {
+ if let Some(hir_id) = self.current_statement_hir_id {
+ self.hir_id_uses_map.insert(hir_id, index);
+ }
+ } else {
+ self.uses[index] = None;
+ }
+ }
+ }
}
// Check if the collection is used for anything else
if path_to_local_id(expr, self.target) {
}
}
+impl<'tcx> IterFunctionVisitor<'_, 'tcx> {
+ fn visit_block_expr(&mut self, expr: &'tcx Expr<'tcx>, hir_id: Option<HirId>) {
+ self.current_statement_hir_id = hir_id;
+ self.current_mutably_captured_ids = get_captured_ids(self.cx, self.cx.typeck_results().expr_ty(expr));
+ self.visit_expr(expr);
+ }
+}
+
+fn get_expr_and_hir_id_from_stmt<'v>(stmt: &'v Stmt<'v>) -> Option<(&'v Expr<'v>, Option<HirId>)> {
+ match stmt.kind {
+ StmtKind::Expr(expr) | StmtKind::Semi(expr) => Some((expr, None)),
+ StmtKind::Item(..) => None,
+ StmtKind::Local(Local { init, pat, .. }) => {
+ if let PatKind::Binding(_, hir_id, ..) = pat.kind {
+ init.map(|init_expr| (init_expr, Some(hir_id)))
+ } else {
+ init.map(|init_expr| (init_expr, None))
+ }
+ },
+ }
+}
+
struct UsedCountVisitor<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
id: HirId,
/// Detect the occurrences of calls to `iter` or `into_iter` for the
/// given identifier
-fn detect_iter_and_into_iters<'tcx>(block: &'tcx Block<'tcx>, id: HirId) -> Option<Vec<IterFunction>> {
+fn detect_iter_and_into_iters<'tcx: 'a, 'a>(
+ block: &'tcx Block<'tcx>,
+ id: HirId,
+ cx: &'a LateContext<'tcx>,
+ captured_ids: HirIdSet,
+) -> Option<Vec<IterFunction>> {
let mut visitor = IterFunctionVisitor {
uses: Vec::new(),
target: id,
seen_other: false,
+ cx,
+ current_mutably_captured_ids: HirIdSet::default(),
+ illegal_mutable_capture_ids: captured_ids,
+ hir_id_uses_map: FxHashMap::default(),
+ current_statement_hir_id: None,
};
visitor.visit_block(block);
- if visitor.seen_other { None } else { Some(visitor.uses) }
+ if visitor.seen_other {
+ None
+ } else {
+ Some(visitor.uses.into_iter().flatten().collect())
+ }
+}
+
+fn get_captured_ids(cx: &LateContext<'tcx>, ty: &'_ TyS<'_>) -> HirIdSet {
+ fn get_captured_ids_recursive(cx: &LateContext<'tcx>, ty: &'_ TyS<'_>, set: &mut HirIdSet) {
+ match ty.kind() {
+ ty::Adt(_, generics) => {
+ for generic in *generics {
+ if let GenericArgKind::Type(ty) = generic.unpack() {
+ get_captured_ids_recursive(cx, ty, set);
+ }
+ }
+ },
+ ty::Closure(def_id, _) => {
+ let closure_hir_node = cx.tcx.hir().get_if_local(*def_id).unwrap();
+ if let Node::Expr(closure_expr) = closure_hir_node {
+ can_move_expr_to_closure(cx, closure_expr)
+ .unwrap()
+ .into_iter()
+ .for_each(|(hir_id, capture_kind)| {
+ if matches!(capture_kind, CaptureKind::Ref(Mutability::Mut)) {
+ set.insert(hir_id);
+ }
+ });
+ }
+ },
+ _ => (),
+ }
+ }
+
+ let mut set = HirIdSet::default();
+
+ get_captured_ids_recursive(cx, ty, &mut set);
+
+ set
}
use clippy_utils::ty::{has_iter_method, implements_trait};
use clippy_utils::{get_parent_expr, is_integer_const, path_to_local, path_to_local_id, sugg};
use if_chain::if_chain;
+use rustc_ast::ast::{LitIntType, LitKind};
use rustc_errors::Applicability;
-use rustc_hir::intravisit::{walk_expr, walk_pat, walk_stmt, NestedVisitorMap, Visitor};
-use rustc_hir::HirIdMap;
-use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Stmt, StmtKind};
+use rustc_hir::intravisit::{walk_expr, walk_local, walk_pat, walk_stmt, NestedVisitorMap, Visitor};
+use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, HirId, HirIdMap, Local, Mutability, Pat, PatKind, Stmt};
use rustc_lint::LateContext;
use rustc_middle::hir::map::Map;
+use rustc_middle::ty::Ty;
+use rustc_span::source_map::Spanned;
use rustc_span::symbol::{sym, Symbol};
+use rustc_typeck::hir_ty_to_ty;
use std::iter::Iterator;
#[derive(Debug, PartialEq)]
}
enum InitializeVisitorState<'hir> {
- Initial, // Not examined yet
- Declared(Symbol), // Declared but not (yet) initialized
+ Initial, // Not examined yet
+ Declared(Symbol, Option<Ty<'hir>>), // Declared but not (yet) initialized
Initialized {
name: Symbol,
+ ty: Option<Ty<'hir>>,
initializer: &'hir Expr<'hir>,
},
DontWarn,
}
}
- pub(super) fn get_result(&self) -> Option<(Symbol, &'tcx Expr<'tcx>)> {
- if let InitializeVisitorState::Initialized { name, initializer } = self.state {
- Some((name, initializer))
+ pub(super) fn get_result(&self) -> Option<(Symbol, Option<Ty<'tcx>>, &'tcx Expr<'tcx>)> {
+ if let InitializeVisitorState::Initialized { name, ty, initializer } = self.state {
+ Some((name, ty, initializer))
} else {
None
}
impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> {
type Map = Map<'tcx>;
- fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) {
+ fn visit_local(&mut self, l: &'tcx Local<'_>) {
// Look for declarations of the variable
if_chain! {
- if let StmtKind::Local(local) = stmt.kind;
- if local.pat.hir_id == self.var_id;
- if let PatKind::Binding(.., ident, _) = local.pat.kind;
+ if l.pat.hir_id == self.var_id;
+ if let PatKind::Binding(.., ident, _) = l.pat.kind;
then {
- self.state = local.init.map_or(InitializeVisitorState::Declared(ident.name), |init| {
+ let ty = l.ty.map(|ty| hir_ty_to_ty(self.cx.tcx, ty));
+
+ self.state = l.init.map_or(InitializeVisitorState::Declared(ident.name, ty), |init| {
InitializeVisitorState::Initialized {
initializer: init,
+ ty,
name: ident.name,
}
})
}
}
- walk_stmt(self, stmt);
+
+ walk_local(self, l);
}
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
self.state = InitializeVisitorState::DontWarn;
},
ExprKind::Assign(lhs, rhs, _) if lhs.hir_id == expr.hir_id => {
- self.state = if_chain! {
- if self.depth == 0;
- if let InitializeVisitorState::Declared(name)
- | InitializeVisitorState::Initialized { name, ..} = self.state;
- then {
- InitializeVisitorState::Initialized { initializer: rhs, name }
- } else {
- InitializeVisitorState::DontWarn
+ self.state = if self.depth == 0 {
+ match self.state {
+ InitializeVisitorState::Declared(name, mut ty) => {
+ if ty.is_none() {
+ if let ExprKind::Lit(Spanned {
+ node: LitKind::Int(_, LitIntType::Unsuffixed),
+ ..
+ }) = rhs.kind
+ {
+ ty = None;
+ } else {
+ ty = self.cx.typeck_results().expr_ty_opt(rhs);
+ }
+ }
+
+ InitializeVisitorState::Initialized {
+ initializer: rhs,
+ ty,
+ name,
+ }
+ },
+ InitializeVisitorState::Initialized { ty, name, .. } => {
+ InitializeVisitorState::Initialized {
+ initializer: rhs,
+ ty,
+ name,
+ }
+ },
+ _ => InitializeVisitorState::DontWarn,
}
+ } else {
+ InitializeVisitorState::DontWarn
}
},
ExprKind::AddrOf(BorrowKind::Ref, mutability, _) if mutability == Mutability::Mut => {
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::in_macro;
use clippy_utils::source::snippet;
use hir::def::{DefKind, Res};
use if_chain::if_chain;
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::hygiene::ExpnKind;
use rustc_span::{edition::Edition, sym, Span};
declare_clippy_lint! {
/// #[macro_use]
/// use some_macro;
/// ```
+ #[clippy::version = "1.44.0"]
pub MACRO_USE_IMPORTS,
pedantic,
"#[macro_use] is no longer needed"
}
}
}
+
+fn in_macro(span: Span) -> bool {
+ span.from_expansion() && !matches!(span.ctxt().outer_expn_data().kind, ExpnKind::Desugaring(..))
+}
/// main();
/// }
/// ```
+ #[clippy::version = "1.38.0"]
pub MAIN_RECURSION,
style,
"recursion using the entrypoint"
/// let sad_people: Vec<&str> = vec![];
/// assert!(sad_people.is_empty(), "there are sad people: {:?}", sad_people);
/// ```
+ #[clippy::version = "1.57.0"]
pub MANUAL_ASSERT,
pedantic,
"`panic!` and only a `panic!` in `if`-then statement"
/// ```rust
/// async fn foo() -> i32 { 42 }
/// ```
+ #[clippy::version = "1.45.0"]
pub MANUAL_ASYNC_FN,
style,
"manual implementations of `async` functions can be simplified using the dedicated syntax"
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::higher::IfLetOrMatch;
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
-use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable};
+use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable, type_is_unsafe_function};
use clippy_utils::{
can_move_expr_to_closure, in_constant, is_else_clause, is_lang_ctor, is_lint_allowed, path_to_local_id,
peel_hir_expr_refs, peel_hir_expr_while, CaptureKind,
use rustc_errors::Applicability;
use rustc_hir::LangItem::{OptionNone, OptionSome};
use rustc_hir::{
- def::Res, Arm, BindingAnnotation, Block, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Path, QPath,
+ def::Res, Arm, BindingAnnotation, Block, BlockCheckMode, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Path,
+ QPath, UnsafeSource,
};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
/// ```rust
/// Some(0).map(|x| x + 1);
/// ```
+ #[clippy::version = "1.52.0"]
pub MANUAL_MAP,
style,
"reimplementation of `map`"
return;
}
- let some_expr = match get_some_expr(cx, some_expr, expr_ctxt) {
+ let some_expr = match get_some_expr(cx, some_expr, false, expr_ctxt) {
Some(expr) => expr,
None => return,
};
// These two lints will go back and forth with each other.
- if cx.typeck_results().expr_ty(some_expr) == cx.tcx.types.unit
+ if cx.typeck_results().expr_ty(some_expr.expr) == cx.tcx.types.unit
&& !is_lint_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id)
{
return;
}
// `map` won't perform any adjustments.
- if !cx.typeck_results().expr_adjustments(some_expr).is_empty() {
+ if !cx.typeck_results().expr_adjustments(some_expr.expr).is_empty() {
return;
}
None => "",
};
- match can_move_expr_to_closure(cx, some_expr) {
+ match can_move_expr_to_closure(cx, some_expr.expr) {
Some(captures) => {
// Check if captures the closure will need conflict with borrows made in the scrutinee.
// TODO: check all the references made in the scrutinee expression. This will require interacting
};
let body_str = if let PatKind::Binding(annotation, id, some_binding, None) = some_pat.kind {
- match can_pass_as_func(cx, id, some_expr) {
- Some(func) if func.span.ctxt() == some_expr.span.ctxt() => {
+ if_chain! {
+ if !some_expr.needs_unsafe_block;
+ if let Some(func) = can_pass_as_func(cx, id, some_expr.expr);
+ if func.span.ctxt() == some_expr.expr.span.ctxt();
+ then {
snippet_with_applicability(cx, func.span, "..", &mut app).into_owned()
- },
- _ => {
- if path_to_local_id(some_expr, id)
+ } else {
+ if path_to_local_id(some_expr.expr, id)
&& !is_lint_allowed(cx, MATCH_AS_REF, expr.hir_id)
&& binding_ref.is_some()
{
} else {
""
};
- format!(
- "|{}{}| {}",
- annotation,
- some_binding,
- snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app).0
- )
- },
+ let expr_snip = snippet_with_context(cx, some_expr.expr.span, expr_ctxt, "..", &mut app).0;
+ if some_expr.needs_unsafe_block {
+ format!("|{}{}| unsafe {{ {} }}", annotation, some_binding, expr_snip)
+ } else {
+ format!("|{}{}| {}", annotation, some_binding, expr_snip)
+ }
+ }
}
} else if !is_wild_none && explicit_ref.is_none() {
// TODO: handle explicit reference annotations.
- format!(
- "|{}| {}",
- snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0,
- snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app).0
- )
+ let pat_snip = snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0;
+ let expr_snip = snippet_with_context(cx, some_expr.expr.span, expr_ctxt, "..", &mut app).0;
+ if some_expr.needs_unsafe_block {
+ format!("|{}| unsafe {{ {} }}", pat_snip, expr_snip)
+ } else {
+ format!("|{}| {}", pat_snip, expr_snip)
+ }
} else {
// Refutable bindings and mixed reference annotations can't be handled by `map`.
return;
fn can_pass_as_func(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
match expr.kind {
ExprKind::Call(func, [arg])
- if path_to_local_id(arg, binding) && cx.typeck_results().expr_adjustments(arg).is_empty() =>
+ if path_to_local_id(arg, binding)
+ && cx.typeck_results().expr_adjustments(arg).is_empty()
+ && !type_is_unsafe_function(cx, cx.typeck_results().expr_ty(func).peel_refs()) =>
{
Some(func)
},
},
}
+struct SomeExpr<'tcx> {
+ expr: &'tcx Expr<'tcx>,
+ needs_unsafe_block: bool,
+}
+
// Try to parse into a recognized `Option` pattern.
// i.e. `_`, `None`, `Some(..)`, or a reference to any of those.
fn try_parse_pattern(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxContext) -> Option<OptionPat<'tcx>> {
}
// Checks for an expression wrapped by the `Some` constructor. Returns the contained expression.
-fn get_some_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ctxt: SyntaxContext) -> Option<&'tcx Expr<'tcx>> {
+fn get_some_expr(
+ cx: &LateContext<'tcx>,
+ expr: &'tcx Expr<'_>,
+ needs_unsafe_block: bool,
+ ctxt: SyntaxContext,
+) -> Option<SomeExpr<'tcx>> {
// TODO: Allow more complex expressions.
match expr.kind {
ExprKind::Call(
..
},
[arg],
- ) if ctxt == expr.span.ctxt() && is_lang_ctor(cx, qpath, OptionSome) => Some(arg),
+ ) if ctxt == expr.span.ctxt() && is_lang_ctor(cx, qpath, OptionSome) => Some(SomeExpr {
+ expr: arg,
+ needs_unsafe_block,
+ }),
ExprKind::Block(
Block {
stmts: [],
expr: Some(expr),
+ rules,
..
},
_,
- ) => get_some_expr(cx, expr, ctxt),
+ ) => get_some_expr(
+ cx,
+ expr,
+ needs_unsafe_block || *rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided),
+ ctxt,
+ ),
_ => None,
}
}
/// #[non_exhaustive]
/// struct T(pub i32, pub i32);
/// ```
+ #[clippy::version = "1.45.0"]
pub MANUAL_NON_EXHAUSTIVE,
style,
"manual implementations of the non-exhaustive pattern can be simplified using #[non_exhaustive]"
/// let foo: Option<i32> = None;
/// foo.ok_or("error");
/// ```
+ #[clippy::version = "1.49.0"]
pub MANUAL_OK_OR,
pedantic,
"finds patterns that can be encoded more concisely with `Option::ok_or`"
/// assert_eq!(end.to_uppercase(), "WORLD!");
/// }
/// ```
+ #[clippy::version = "1.48.0"]
pub MANUAL_STRIP,
complexity,
"suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing"
/// let foo: Option<i32> = None;
/// foo.unwrap_or(1);
/// ```
+ #[clippy::version = "1.49.0"]
pub MANUAL_UNWRAP_OR,
complexity,
"finds patterns that can be encoded more concisely with `Option::unwrap_or` or `Result::unwrap_or`"
/// let y = x.iter();
/// let z = y.cloned();
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub MAP_CLONE,
style,
"using `iterator.map(|x| x.clone())`, or dereferencing closures for `Copy` types"
/// })
/// }
/// ```
+ #[clippy::version = "1.48.0"]
pub MAP_ERR_IGNORE,
restriction,
"`map_err` should not ignore the original error"
/// log_err_msg(format_msg(msg));
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub OPTION_MAP_UNIT_FN,
complexity,
"using `option.map(f)`, where `f` is a function or closure that returns `()`"
/// log_err_msg(format_msg(msg));
/// };
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub RESULT_MAP_UNIT_FN,
complexity,
"using `result.map(f)`, where `f` is a function or closure that returns `()`"
/// Anything else will return `a`.
fn let_binding_name(cx: &LateContext<'_>, var_arg: &hir::Expr<'_>) -> String {
match &var_arg.kind {
- hir::ExprKind::Field(_, _) => snippet(cx, var_arg.span, "_").replace(".", "_"),
+ hir::ExprKind::Field(_, _) => snippet(cx, var_arg.span, "_").replace('.', "_"),
hir::ExprKind::Path(_) => format!("_{}", snippet(cx, var_arg.span, "")),
_ => "a".to_string(),
}
/// _ => {},
/// }
/// ```
+ #[clippy::version = "1.45.0"]
pub MATCH_ON_VEC_ITEMS,
pedantic,
"matching on vector elements can panic"
/// vec.push(value)
/// }
/// ```
+ #[clippy::version = "1.57.0"]
pub MATCH_RESULT_OK,
style,
"usage of `ok()` in `let Some(pat)` statements is unnecessary, match on `Ok(pat)` instead"
/// _ => {},
/// }
/// ```
+ #[clippy::version = "1.58.0"]
pub MATCH_STR_CASE_MISMATCH,
correctness,
"creation of a case altering match expression with non-compliant arms"
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type, peel_mid_ty_refs};
use clippy_utils::visitors::is_local_used;
use clippy_utils::{
- get_parent_expr, in_macro, is_expn_of, is_lang_ctor, is_lint_allowed, is_refutable, is_unit_expr, is_wild,
- meets_msrv, msrvs, path_to_local, path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns,
- remove_blocks, strip_pat_refs,
+ get_parent_expr, is_expn_of, is_lang_ctor, is_lint_allowed, is_refutable, is_unit_expr, is_wild, meets_msrv, msrvs,
+ path_to_local, path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns, remove_blocks,
+ strip_pat_refs,
};
use clippy_utils::{paths, search_same, SpanlessEq, SpanlessHash};
use core::iter::{once, ExactSizeIterator};
};
use rustc_hir::{HirIdMap, HirIdSet};
use rustc_lint::{LateContext, LateLintPass, LintContext};
-use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{self, Ty, TyS, VariantDef};
use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::sym;
use std::cmp::Ordering;
use std::collections::hash_map::Entry;
-use std::iter;
-use std::ops::Bound;
declare_clippy_lint! {
/// ### What it does
/// bar(foo);
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub SINGLE_MATCH,
style,
"a `match` statement with a single nontrivial arm (i.e., where the other arm is `_ => {}`) instead of `if let`"
/// bar(&other_ref);
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub SINGLE_MATCH_ELSE,
pedantic,
"a `match` statement with two arms where the second arm's pattern is a placeholder instead of a specific match pattern"
/// _ => frob(x),
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub MATCH_REF_PATS,
style,
"a `match` or `if let` with all arms prefixed with `&` instead of deref-ing the match expression"
/// bar();
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub MATCH_BOOL,
pedantic,
"a `match` on a boolean expression instead of an `if..else` block"
/// _ => (),
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub MATCH_OVERLAPPING_ARM,
style,
"a `match` with overlapping arms"
/// Err(_) => panic!("err"),
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub MATCH_WILD_ERR_ARM,
pedantic,
"a `match` with `Err(_)` arm and take drastic actions"
/// // Good
/// let r: Option<&()> = x.as_ref();
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub MATCH_AS_REF,
complexity,
"a `match` on an Option value instead of using `as_ref()` or `as_mut`"
/// Foo::B(_) => {},
/// }
/// ```
+ #[clippy::version = "1.34.0"]
pub WILDCARD_ENUM_MATCH_ARM,
restriction,
"a wildcard enum match arm using `_`"
/// Foo::C => {},
/// }
/// ```
+ #[clippy::version = "1.45.0"]
pub MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
pedantic,
"a wildcard enum match for a single variant"
/// _ => {},
/// }
/// ```
+ #[clippy::version = "1.42.0"]
pub WILDCARD_IN_OR_PATTERNS,
complexity,
"a wildcard pattern used with others patterns in same match arm"
/// let wrapper = Wrapper::Data(42);
/// let Wrapper::Data(data) = wrapper;
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub INFALLIBLE_DESTRUCTURING_MATCH,
style,
"a `match` statement with a single infallible arm instead of a `let`"
/// // Good
/// let (c, d) = (a, b);
/// ```
+ #[clippy::version = "1.43.0"]
pub MATCH_SINGLE_BINDING,
complexity,
"a match with a single binding instead of using `let` statement"
/// _ => {},
/// }
/// ```
+ #[clippy::version = "1.43.0"]
pub REST_PAT_IN_FULLY_BOUND_STRUCTS,
restriction,
"a match on a struct that binds all fields but still uses the wildcard pattern"
/// if IpAddr::V6(Ipv6Addr::LOCALHOST).is_ipv6() {}
/// Ok::<i32, i32>(42).is_ok();
/// ```
+ #[clippy::version = "1.31.0"]
pub REDUNDANT_PATTERN_MATCHING,
style,
"use the proper utility function avoiding an `if let`"
/// // Good
/// let a = matches!(x, Some(0));
/// ```
+ #[clippy::version = "1.47.0"]
pub MATCH_LIKE_MATCHES_MACRO,
style,
"a match that could be written with the matches! macro"
/// Quz => quz(),
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub MATCH_SAME_ARMS,
pedantic,
"`match` with identical arm bodies"
impl<'tcx> LateLintPass<'tcx> for Matches {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- if in_external_macro(cx.sess(), expr.span) || in_macro(expr.span) {
+ if expr.span.from_expansion() {
return;
}
if let ExprKind::Match(ex, arms, _) = expr.kind {
check_match_ref_pats(cx, ex, arms.iter().map(|el| el.pat), expr);
}
- if let Some(higher::IfLet { let_pat, let_expr, .. }) = higher::IfLet::hir(cx, expr) {
- check_match_ref_pats(cx, let_expr, once(let_pat), expr);
- }
}
fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {
if_chain! {
- if !in_external_macro(cx.sess(), local.span);
- if !in_macro(local.span);
+ if !local.span.from_expansion();
if let Some(expr) = local.init;
if let ExprKind::Match(target, arms, MatchSource::Normal) = expr.kind;
if arms.len() == 1 && arms[0].guard.is_none();
fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
if_chain! {
- if !in_external_macro(cx.sess(), pat.span);
- if !in_macro(pat.span);
+ if !pat.span.from_expansion();
if let PatKind::Struct(QPath::Resolved(_, path), fields, true) = pat.kind;
if let Some(def_id) = path.res.opt_def_id();
let ty = cx.tcx.type_of(def_id);
#[rustfmt::skip]
fn check_single_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() {
- if in_macro(expr.span) {
+ if expr.span.from_expansion() {
// Don't lint match expressions present in
// macro_rules! block
return;
#[allow(clippy::too_many_lines)]
fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], expr: &Expr<'_>) {
- if in_macro(expr.span) || arms.len() != 1 || is_refutable(cx, arms[0].pat) {
+ if expr.span.from_expansion() || arms.len() != 1 || is_refutable(cx, arms[0].pat) {
return;
}
None
}
-/// Gets all arms that are unbounded `PatRange`s.
+/// Gets the ranges for each range pattern arm. Applies `ty` bounds for open ranges.
fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) -> Vec<SpannedRange<FullInt>> {
arms.iter()
.filter_map(|arm| {
if let Arm { pat, guard: None, .. } = *arm {
if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind {
- let lhs = match lhs {
+ let lhs_const = match lhs {
Some(lhs) => constant(cx, cx.typeck_results(), lhs)?.0,
None => miri_to_const(ty.numeric_min_val(cx.tcx)?)?,
};
- let rhs = match rhs {
+ let rhs_const = match rhs {
Some(rhs) => constant(cx, cx.typeck_results(), rhs)?.0,
None => miri_to_const(ty.numeric_max_val(cx.tcx)?)?,
};
- let lhs_val = lhs.int_value(cx, ty)?;
- let rhs_val = rhs.int_value(cx, ty)?;
+ let lhs_val = lhs_const.int_value(cx, ty)?;
+ let rhs_val = rhs_const.int_value(cx, ty)?;
let rhs_bound = match range_end {
- RangeEnd::Included => Bound::Included(rhs_val),
- RangeEnd::Excluded => Bound::Excluded(rhs_val),
+ RangeEnd::Included => EndBound::Included(rhs_val),
+ RangeEnd::Excluded => EndBound::Excluded(rhs_val),
};
return Some(SpannedRange {
span: pat.span,
let value = constant_full_int(cx, cx.typeck_results(), value)?;
return Some(SpannedRange {
span: pat.span,
- node: (value, Bound::Included(value)),
+ node: (value, EndBound::Included(value)),
});
}
}
.collect()
}
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub enum EndBound<T> {
+ Included(T),
+ Excluded(T),
+}
+
#[derive(Debug, Eq, PartialEq)]
-pub struct SpannedRange<T> {
+struct SpannedRange<T> {
pub span: Span,
- pub node: (T, Bound<T>),
+ pub node: (T, EndBound<T>),
}
// Checks if arm has the form `None => None`
ref_count > 1
}
-pub fn overlapping<T>(ranges: &[SpannedRange<T>]) -> Option<(&SpannedRange<T>, &SpannedRange<T>)>
+fn overlapping<T>(ranges: &[SpannedRange<T>]) -> Option<(&SpannedRange<T>, &SpannedRange<T>)>
where
T: Copy + Ord,
{
- #[derive(Copy, Clone, Debug, Eq, PartialEq)]
- enum Kind<'a, T> {
- Start(T, &'a SpannedRange<T>),
- End(Bound<T>, &'a SpannedRange<T>),
+ #[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
+ enum BoundKind {
+ EndExcluded,
+ Start,
+ EndIncluded,
}
- impl<'a, T: Copy> Kind<'a, T> {
- fn range(&self) -> &'a SpannedRange<T> {
- match *self {
- Kind::Start(_, r) | Kind::End(_, r) => r,
- }
- }
-
- fn value(self) -> Bound<T> {
- match self {
- Kind::Start(t, _) => Bound::Included(t),
- Kind::End(t, _) => t,
- }
- }
- }
+ #[derive(Copy, Clone, Debug, Eq, PartialEq)]
+ struct RangeBound<'a, T>(T, BoundKind, &'a SpannedRange<T>);
- impl<'a, T: Copy + Ord> PartialOrd for Kind<'a, T> {
+ impl<'a, T: Copy + Ord> PartialOrd for RangeBound<'a, T> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
- impl<'a, T: Copy + Ord> Ord for Kind<'a, T> {
- fn cmp(&self, other: &Self) -> Ordering {
- match (self.value(), other.value()) {
- (Bound::Included(a), Bound::Included(b)) | (Bound::Excluded(a), Bound::Excluded(b)) => a.cmp(&b),
- // Range patterns cannot be unbounded (yet)
- (Bound::Unbounded, _) | (_, Bound::Unbounded) => unimplemented!(),
- (Bound::Included(a), Bound::Excluded(b)) => match a.cmp(&b) {
- Ordering::Equal => Ordering::Greater,
- other => other,
- },
- (Bound::Excluded(a), Bound::Included(b)) => match a.cmp(&b) {
- Ordering::Equal => Ordering::Less,
- other => other,
- },
- }
+ impl<'a, T: Copy + Ord> Ord for RangeBound<'a, T> {
+ fn cmp(&self, RangeBound(other_value, other_kind, _): &Self) -> Ordering {
+ let RangeBound(self_value, self_kind, _) = *self;
+ (self_value, self_kind).cmp(&(*other_value, *other_kind))
}
}
let mut values = Vec::with_capacity(2 * ranges.len());
- for r in ranges {
- values.push(Kind::Start(r.node.0, r));
- values.push(Kind::End(r.node.1, r));
+ for r @ SpannedRange { node: (start, end), .. } in ranges {
+ values.push(RangeBound(*start, BoundKind::Start, r));
+ values.push(match end {
+ EndBound::Excluded(val) => RangeBound(*val, BoundKind::EndExcluded, r),
+ EndBound::Included(val) => RangeBound(*val, BoundKind::EndIncluded, r),
+ });
}
values.sort();
- for (a, b) in iter::zip(&values, values.iter().skip(1)) {
- match (a, b) {
- (&Kind::Start(_, ra), &Kind::End(_, rb)) => {
- if ra.node != rb.node {
- return Some((ra, rb));
- }
- },
- (&Kind::End(a, _), &Kind::Start(b, _)) if a != Bound::Included(b) => (),
- _ => {
- // skip if the range `a` is completely included into the range `b`
- if let Ordering::Equal | Ordering::Less = a.cmp(b) {
- let kind_a = Kind::End(a.range().node.1, a.range());
- let kind_b = Kind::End(b.range().node.1, b.range());
- if let Ordering::Equal | Ordering::Greater = kind_a.cmp(&kind_b) {
- return None;
+ let mut started = vec![];
+
+ for RangeBound(_, kind, range) in values {
+ match kind {
+ BoundKind::Start => started.push(range),
+ BoundKind::EndExcluded | BoundKind::EndIncluded => {
+ let mut overlap = None;
+
+ while let Some(last_started) = started.pop() {
+ if last_started == range {
+ break;
}
+ overlap = Some(last_started);
+ }
+
+ if let Some(first_overlapping) = overlap {
+ return Some((range, first_overlapping));
}
- return Some((a.range(), b.range()));
},
}
}
use super::REDUNDANT_PATTERN_MATCHING;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::higher;
- use clippy_utils::source::{snippet, snippet_with_applicability};
+ use clippy_utils::source::snippet;
+ use clippy_utils::sugg::Sugg;
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, is_type_lang_item, match_type};
use clippy_utils::{is_lang_ctor, is_qpath_def_path, is_trait_method, paths};
use if_chain::if_chain;
use rustc_hir::LangItem::{OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk};
use rustc_hir::{
intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor},
- Arm, Block, Expr, ExprKind, LangItem, MatchSource, Node, Pat, PatKind, QPath,
+ Arm, Block, Expr, ExprKind, LangItem, MatchSource, Node, Pat, PatKind, QPath, UnOp,
};
use rustc_lint::LateContext;
use rustc_middle::ty::{self, subst::GenericArgKind, Ty};
let result_expr = match &let_expr.kind {
ExprKind::AddrOf(_, _, borrowed) => borrowed,
+ ExprKind::Unary(UnOp::Deref, deref) => deref,
_ => let_expr,
};
+
span_lint_and_then(
cx,
REDUNDANT_PATTERN_MATCHING,
// ^^^^^^^^^^^^^^^^^^^
let span = expr_span.until(op_span.shrink_to_hi());
- let mut app = if needs_drop {
+ let app = if needs_drop {
Applicability::MaybeIncorrect
} else {
Applicability::MachineApplicable
};
- let sugg = snippet_with_applicability(cx, op_span, "_", &mut app);
+
+ let sugg = Sugg::hir_with_macro_callsite(cx, result_expr, "_")
+ .maybe_par()
+ .to_string();
diag.span_suggestion(span, "try this", format!("{} {}.{}", keyword, sugg, good_method), app);
};
assert_eq!(None, overlapping::<u8>(&[]));
- assert_eq!(None, overlapping(&[sp(1, Bound::Included(4))]));
+ assert_eq!(None, overlapping(&[sp(1, EndBound::Included(4))]));
assert_eq!(
None,
- overlapping(&[sp(1, Bound::Included(4)), sp(5, Bound::Included(6))])
+ overlapping(&[sp(1, EndBound::Included(4)), sp(5, EndBound::Included(6))])
);
assert_eq!(
None,
overlapping(&[
- sp(1, Bound::Included(4)),
- sp(5, Bound::Included(6)),
- sp(10, Bound::Included(11))
+ sp(1, EndBound::Included(4)),
+ sp(5, EndBound::Included(6)),
+ sp(10, EndBound::Included(11))
],)
);
assert_eq!(
- Some((&sp(1, Bound::Included(4)), &sp(3, Bound::Included(6)))),
- overlapping(&[sp(1, Bound::Included(4)), sp(3, Bound::Included(6))])
+ Some((&sp(1, EndBound::Included(4)), &sp(3, EndBound::Included(6)))),
+ overlapping(&[sp(1, EndBound::Included(4)), sp(3, EndBound::Included(6))])
);
assert_eq!(
- Some((&sp(5, Bound::Included(6)), &sp(6, Bound::Included(11)))),
+ Some((&sp(5, EndBound::Included(6)), &sp(6, EndBound::Included(11)))),
overlapping(&[
- sp(1, Bound::Included(4)),
- sp(5, Bound::Included(6)),
- sp(6, Bound::Included(11))
+ sp(1, EndBound::Included(4)),
+ sp(5, EndBound::Included(6)),
+ sp(6, EndBound::Included(11))
],)
);
}
/// # use std::rc::Rc;
/// mem::forget(Rc::new(55))
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub MEM_FORGET,
restriction,
"`mem::forget` usage on `Drop` types, likely to cause memory leaks"
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::{snippet, snippet_with_applicability};
use clippy_utils::ty::is_non_aggregate_primitive_type;
-use clippy_utils::{in_macro, is_default_equivalent, is_lang_ctor, match_def_path, meets_msrv, msrvs, paths};
+use clippy_utils::{is_default_equivalent, is_lang_ctor, match_def_path, meets_msrv, msrvs, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::LangItem::OptionNone;
/// let mut an_option = Some(0);
/// let taken = an_option.take();
/// ```
+ #[clippy::version = "1.31.0"]
pub MEM_REPLACE_OPTION_WITH_NONE,
style,
"replacing an `Option` with `None` instead of `take()`"
/// The [take_mut](https://docs.rs/take_mut) crate offers a sound solution,
/// at the cost of either lazily creating a replacement value or aborting
/// on panic, to ensure that the uninitialized value cannot be observed.
+ #[clippy::version = "1.39.0"]
pub MEM_REPLACE_WITH_UNINIT,
correctness,
"`mem::replace(&mut _, mem::uninitialized())` or `mem::replace(&mut _, mem::zeroed())`"
/// let mut text = String::from("foo");
/// let taken = std::mem::take(&mut text);
/// ```
+ #[clippy::version = "1.42.0"]
pub MEM_REPLACE_WITH_DEFAULT,
style,
"replacing a value of type `T` with `T::default()` instead of using `std::mem::take`"
expr_span,
"replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`",
|diag| {
- if !in_macro(expr_span) {
+ if !expr_span.from_expansion() {
let suggestion = format!("std::mem::take({})", snippet(cx, dest.span, ""));
diag.span_suggestion(
use super::{contains_return, BIND_INSTEAD_OF_MAP};
use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::{snippet, snippet_with_macro_callsite};
-use clippy_utils::{in_macro, remove_blocks, visitors::find_all_ret_expressions};
+use clippy_utils::{remove_blocks, visitors::find_all_ret_expressions};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
let mut suggs = Vec::new();
let can_sugg: bool = find_all_ret_expressions(cx, closure_expr, |ret_expr| {
if_chain! {
- if !in_macro(ret_expr.span);
+ if !ret_expr.span.from_expansion();
if let hir::ExprKind::Call(func_path, [arg]) = ret_expr.kind;
if let hir::ExprKind::Path(QPath::Resolved(_, path)) = func_path.kind;
if Self::is_variant(cx, path.res);
// i.e.: 2 wildcards in `std::collections::BTreeMap<&i32, &char>`
let ty_str = ty.to_string();
let start = ty_str.find('<').unwrap_or(0);
- let end = ty_str.find('>').unwrap_or_else(|| ty_str.len());
+ let end = ty_str.find('>').unwrap_or(ty_str.len());
let nb_wildcard = ty_str[start..end].split(',').count();
let wildcards = format!("_{}", ", _".repeat(nb_wildcard - 1));
format!("{}<{}>", elements.join("::"), wildcards)
use super::ITER_CLONED_COLLECT;
-pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, recv: &'tcx hir::Expr<'_>) {
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, method_name: &str, expr: &hir::Expr<'_>, recv: &'tcx hir::Expr<'_>) {
if_chain! {
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Vec);
if let Some(slice) = derefs_to_slice(cx, recv, cx.typeck_results().expr_ty(recv));
cx,
ITER_CLONED_COLLECT,
to_replace,
- "called `iter().cloned().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and \
- more readable",
+ &format!("called `iter().{}().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and \
+ more readable", method_name),
"try",
".to_vec()".to_string(),
Applicability::MachineApplicable,
+++ /dev/null
-use clippy_utils::consts::{constant, Constant};
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_with_context;
-use clippy_utils::{is_diag_item_method, match_def_path, paths};
-use if_chain::if_chain;
-use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind, HirId, LangItem, Node, QPath};
-use rustc_lint::LateContext;
-use rustc_middle::ty::{self, adjustment::Adjust};
-use rustc_span::{symbol::sym, Span, SyntaxContext};
-
-use super::MANUAL_SPLIT_ONCE;
-
-pub(super) fn check(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, self_arg: &Expr<'_>, pat_arg: &Expr<'_>) {
- if !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() {
- return;
- }
-
- let ctxt = expr.span.ctxt();
- let (method_name, msg, reverse) = if method_name == "splitn" {
- ("split_once", "manual implementation of `split_once`", false)
- } else {
- ("rsplit_once", "manual implementation of `rsplit_once`", true)
- };
- let usage = match parse_iter_usage(cx, ctxt, cx.tcx.hir().parent_iter(expr.hir_id), reverse) {
- Some(x) => x,
- None => return,
- };
-
- let mut app = Applicability::MachineApplicable;
- let self_snip = snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0;
- let pat_snip = snippet_with_context(cx, pat_arg.span, ctxt, "..", &mut app).0;
-
- let sugg = match usage.kind {
- IterUsageKind::NextTuple => {
- format!("{}.{}({})", self_snip, method_name, pat_snip)
- },
- IterUsageKind::RNextTuple => format!("{}.{}({}).map(|(x, y)| (y, x))", self_snip, method_name, pat_snip),
- IterUsageKind::Next => {
- let self_deref = {
- let adjust = cx.typeck_results().expr_adjustments(self_arg);
- if adjust.is_empty() {
- String::new()
- } else if cx.typeck_results().expr_ty(self_arg).is_box()
- || adjust
- .iter()
- .any(|a| matches!(a.kind, Adjust::Deref(Some(_))) || a.target.is_box())
- {
- format!("&{}", "*".repeat(adjust.len() - 1))
- } else {
- "*".repeat(adjust.len() - 2)
- }
- };
- if usage.unwrap_kind.is_some() {
- format!(
- "{}.{}({}).map_or({}{}, |x| x.0)",
- &self_snip, method_name, pat_snip, self_deref, &self_snip
- )
- } else {
- format!(
- "Some({}.{}({}).map_or({}{}, |x| x.0))",
- &self_snip, method_name, pat_snip, self_deref, &self_snip
- )
- }
- },
- IterUsageKind::Second => {
- let access_str = match usage.unwrap_kind {
- Some(UnwrapKind::Unwrap) => ".unwrap().1",
- Some(UnwrapKind::QuestionMark) => "?.1",
- None => ".map(|x| x.1)",
- };
- format!("{}.{}({}){}", self_snip, method_name, pat_snip, access_str)
- },
- };
-
- span_lint_and_sugg(cx, MANUAL_SPLIT_ONCE, usage.span, msg, "try this", sugg, app);
-}
-
-enum IterUsageKind {
- Next,
- Second,
- NextTuple,
- RNextTuple,
-}
-
-enum UnwrapKind {
- Unwrap,
- QuestionMark,
-}
-
-struct IterUsage {
- kind: IterUsageKind,
- unwrap_kind: Option<UnwrapKind>,
- span: Span,
-}
-
-#[allow(clippy::too_many_lines)]
-fn parse_iter_usage(
- cx: &LateContext<'tcx>,
- ctxt: SyntaxContext,
- mut iter: impl Iterator<Item = (HirId, Node<'tcx>)>,
- reverse: bool,
-) -> 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 {
- (name, args)
- } else {
- return None;
- };
- let did = cx.typeck_results().type_dependent_def_id(e.hir_id)?;
- let iter_id = cx.tcx.get_diagnostic_item(sym::Iterator)?;
-
- match (&*name.ident.as_str(), args) {
- ("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => {
- if reverse {
- (IterUsageKind::Second, e.span)
- } else {
- (IterUsageKind::Next, e.span)
- }
- },
- ("next_tuple", []) => {
- return if_chain! {
- if match_def_path(cx, did, &paths::ITERTOOLS_NEXT_TUPLE);
- if let ty::Adt(adt_def, subs) = cx.typeck_results().expr_ty(e).kind();
- if cx.tcx.is_diagnostic_item(sym::Option, adt_def.did);
- if let ty::Tuple(subs) = subs.type_at(0).kind();
- if subs.len() == 2;
- then {
- Some(IterUsage {
- kind: if reverse { IterUsageKind::RNextTuple } else { IterUsageKind::NextTuple },
- span: e.span,
- unwrap_kind: None
- })
- } else {
- None
- }
- };
- },
- ("nth" | "skip", [idx_expr]) if cx.tcx.trait_of_item(did) == Some(iter_id) => {
- if let Some((Constant::Int(idx), _)) = constant(cx, cx.typeck_results(), idx_expr) {
- let span = if name.ident.as_str() == "nth" {
- e.span
- } else {
- if_chain! {
- if let Some((_, Node::Expr(next_expr))) = iter.next();
- 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 cx.tcx.trait_of_item(next_id) == Some(iter_id);
- then {
- next_expr.span
- } else {
- return None;
- }
- }
- };
- match if reverse { idx ^ 1 } else { idx } {
- 0 => (IterUsageKind::Next, span),
- 1 => (IterUsageKind::Second, span),
- _ => return None,
- }
- } else {
- return None;
- }
- },
- _ => return None,
- }
- },
- _ => return None,
- };
-
- let (unwrap_kind, span) = if let Some((_, Node::Expr(e))) = iter.next() {
- match e.kind {
- ExprKind::Call(
- Expr {
- kind: ExprKind::Path(QPath::LangItem(LangItem::TryTraitBranch, _)),
- ..
- },
- _,
- ) => {
- let parent_span = e.span.parent_callsite().unwrap();
- if parent_span.ctxt() == ctxt {
- (Some(UnwrapKind::QuestionMark), parent_span)
- } else {
- (None, span)
- }
- },
- _ if e.span.ctxt() != ctxt => (None, span),
- ExprKind::MethodCall(name, _, [_], _)
- if name.ident.name == sym::unwrap
- && cx
- .typeck_results()
- .type_dependent_def_id(e.hir_id)
- .map_or(false, |id| is_diag_item_method(cx, id, sym::Option)) =>
- {
- (Some(UnwrapKind::Unwrap), e.span)
- },
- _ => (None, span),
- }
- } else {
- (None, span)
- };
-
- Some(IterUsage {
- kind,
- unwrap_kind,
- span,
- })
-}
mod iter_skip_next;
mod iterator_step_by_zero;
mod manual_saturating_arithmetic;
-mod manual_split_once;
mod manual_str_repeat;
mod map_collect_result_unit;
mod map_flatten;
mod single_char_pattern;
mod single_char_push_string;
mod skip_while_next;
+mod str_splitn;
mod string_extend_chars;
mod suspicious_map;
mod suspicious_splitn;
use clippy_utils::consts::{constant, Constant};
use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
use clippy_utils::ty::{contains_adt_constructor, contains_ty, implements_trait, is_copy, is_type_diagnostic_item};
-use clippy_utils::{contains_return, get_trait_def_id, in_macro, iter_input_pats, meets_msrv, msrvs, paths, return_ty};
+use clippy_utils::{contains_return, get_trait_def_id, iter_input_pats, meets_msrv, msrvs, paths, return_ty};
use if_chain::if_chain;
use rustc_hir as hir;
use rustc_hir::def::Res;
/// ```rust
/// [1, 2, 3].iter().copied();
/// ```
+ #[clippy::version = "1.53.0"]
pub CLONED_INSTEAD_OF_COPIED,
pedantic,
"used `cloned` where `copied` could be used instead"
/// ```rust
/// let nums: Vec<i32> = ["1", "2", "whee!"].iter().filter_map(|x| x.parse().ok()).collect();
/// ```
+ #[clippy::version = "1.53.0"]
pub FLAT_MAP_OPTION,
pedantic,
"used `flat_map` where `filter_map` could be used instead"
/// // Good
/// res.expect("more helpful message");
/// ```
+ #[clippy::version = "1.45.0"]
pub UNWRAP_USED,
restriction,
"using `.unwrap()` on `Result` or `Option`, which should at least get a better message using `expect()`"
/// res?;
/// # Ok::<(), ()>(())
/// ```
+ #[clippy::version = "1.45.0"]
pub EXPECT_USED,
restriction,
"using `.expect()` on `Result` or `Option`, which might be better handled"
/// }
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub SHOULD_IMPLEMENT_TRAIT,
style,
"defining a method that should be implementing a std trait"
/// }
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub WRONG_SELF_CONVENTION,
style,
"defining a method named with an established prefix (like \"into_\") that takes `self` with the wrong convention"
/// // Good
/// x.expect("why did I do this again?");
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub OK_EXPECT,
style,
"using `ok().expect()`, which gives worse error messages than calling `expect` directly on the Result"
/// // Good
/// x.unwrap_or_default();
/// ```
+ #[clippy::version = "1.56.0"]
pub UNWRAP_OR_ELSE_DEFAULT,
style,
"using `.unwrap_or_else(Default::default)`, which is more succinctly expressed as `.unwrap_or_default()`"
/// // Good
/// x.map_or_else(some_function, |a| a + 1);
/// ```
+ #[clippy::version = "1.45.0"]
pub MAP_UNWRAP_OR,
pedantic,
"using `.map(f).unwrap_or(a)` or `.map(f).unwrap_or_else(func)`, which are more succinctly expressed as `map_or(a, f)` or `map_or_else(a, f)`"
/// // Good
/// opt.and_then(|a| Some(a + 1));
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub OPTION_MAP_OR_NONE,
style,
"using `Option.map_or(None, f)`, which is more succinctly expressed as `and_then(f)`"
/// # let r: Result<u32, &str> = Ok(1);
/// assert_eq!(Some(1), r.ok());
/// ```
+ #[clippy::version = "1.44.0"]
pub RESULT_MAP_OR_INTO_OPTION,
style,
"using `Result.map_or(None, Some)`, which is more succinctly expressed as `ok()`"
/// let _ = res().map(|s| if s.len() == 42 { 10 } else { 20 });
/// let _ = res().map_err(|s| if s.len() == 42 { 10 } else { 20 });
/// ```
+ #[clippy::version = "1.45.0"]
pub BIND_INSTEAD_OF_MAP,
complexity,
"using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`"
/// # let vec = vec![1];
/// vec.iter().find(|x| **x == 0);
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub FILTER_NEXT,
complexity,
"using `filter(p).next()`, which is more succinctly expressed as `.find(p)`"
/// # let vec = vec![1];
/// vec.iter().find(|x| **x != 0);
/// ```
+ #[clippy::version = "1.42.0"]
pub SKIP_WHILE_NEXT,
complexity,
"using `skip_while(p).next()`, which is more succinctly expressed as `.find(!p)`"
/// // Good
/// vec.iter().flat_map(|x| x.iter());
/// ```
+ #[clippy::version = "1.31.0"]
pub MAP_FLATTEN,
- pedantic,
+ complexity,
"using combinations of `flatten` and `map` which can usually be written as a single method call"
}
/// ```rust
/// (0_i32..10).filter_map(|n| n.checked_add(1));
/// ```
+ #[clippy::version = "1.51.0"]
pub MANUAL_FILTER_MAP,
complexity,
"using `_.filter(_).map(_)` in a way that can be written more simply as `filter_map(_)`"
/// ```rust
/// (0_i32..10).find_map(|n| n.checked_add(1));
/// ```
+ #[clippy::version = "1.51.0"]
pub MANUAL_FIND_MAP,
complexity,
"using `_.find(_).map(_)` in a way that can be written more simply as `find_map(_)`"
/// ```rust
/// (0..3).find_map(|x| if x == 2 { Some(x) } else { None });
/// ```
+ #[clippy::version = "1.36.0"]
pub FILTER_MAP_NEXT,
pedantic,
"using combination of `filter_map` and `next` which can usually be written as a single method call"
/// # let iter = vec![vec![0]].into_iter();
/// iter.flatten();
/// ```
+ #[clippy::version = "1.39.0"]
pub FLAT_MAP_IDENTITY,
complexity,
"call to `flat_map` where `flatten` is sufficient"
///
/// let _ = !"hello world".contains("world");
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub SEARCH_IS_SOME,
complexity,
"using an iterator or string search followed by `is_some()` or `is_none()`, which is more succinctly expressed as a call to `any()` or `contains()` (with negation in case of `is_none()`)"
/// let name = "foo";
/// if name.starts_with('_') {};
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub CHARS_NEXT_CMP,
style,
"using `.chars().next()` to check if a string starts with a char"
/// # let foo = Some(String::new());
/// foo.unwrap_or_default();
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub OR_FUN_CALL,
perf,
"using any `*or` method with a function call, which suggests `*or_else`"
/// # let err_msg = "I'm a teapot";
/// foo.unwrap_or_else(|| panic!("Err {}: {}", err_code, err_msg));
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub EXPECT_FUN_CALL,
perf,
"using any `expect` method with a function call"
/// ```rust
/// 42u64.clone();
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub CLONE_ON_COPY,
complexity,
"using `clone` on a `Copy` type"
/// // Good
/// Rc::clone(&x);
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub CLONE_ON_REF_PTR,
restriction,
"using 'clone' on a ref-counted pointer"
/// println!("{:p} {:p}", *y, z); // prints out the same pointer
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub CLONE_DOUBLE_REF,
correctness,
"using `clone` on `&&T`"
/// // OK, the specialized impl is used
/// ["foo", "bar"].iter().map(|&s| s.to_string());
/// ```
+ #[clippy::version = "1.40.0"]
pub INEFFICIENT_TO_STRING,
pedantic,
"using `to_string` on `&&T` where `T: ToString`"
/// fn new() -> Self;
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub NEW_RET_NO_SELF,
style,
"not returning type containing `Self` in a `new` method"
///
/// // Good
/// _.split('x');
+ #[clippy::version = "pre 1.29.0"]
pub SINGLE_CHAR_PATTERN,
perf,
"using a single-character str where a char could be used, e.g., `_.split(\"x\")`"
/// //..
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub ITERATOR_STEP_BY_ZERO,
correctness,
"using `Iterator::step_by(0)`, which will panic at runtime"
/// ```rust
/// let _ = std::iter::empty::<Option<i32>>().flatten();
/// ```
+ #[clippy::version = "1.53.0"]
pub OPTION_FILTER_MAP,
complexity,
"filtering `Option` for `Some` then force-unwrapping, which can be one type-safe operation"
/// # s.insert(1);
/// let x = s.iter().next();
/// ```
+ #[clippy::version = "1.42.0"]
pub ITER_NTH_ZERO,
style,
"replace `iter.nth(0)` with `iter.next()`"
/// let bad_vec = some_vec.get(3);
/// let bad_slice = &some_vec[..].get(3);
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub ITER_NTH,
perf,
"using `.iter().nth()` on a standard library type with O(1) element access"
/// let bad_vec = some_vec.iter().nth(3);
/// let bad_slice = &some_vec[..].iter().nth(3);
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub ITER_SKIP_NEXT,
style,
"using `.skip(x).next()` on an iterator"
/// let last = some_vec[3];
/// some_vec[0] = 1;
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub GET_UNWRAP,
restriction,
"using `.get().unwrap()` or `.get_mut().unwrap()` when using `[]` would work instead"
/// // Good
/// a.append(&mut b);
/// ```
+ #[clippy::version = "1.55.0"]
pub EXTEND_WITH_DRAIN,
perf,
"using vec.append(&mut vec) to move the full range of a vecor to another"
/// s.push_str(abc);
/// s.push_str(&def);
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub STRING_EXTEND_CHARS,
style,
"using `x.extend(s.chars())` where s is a `&str` or `String`"
/// let s = [1, 2, 3, 4, 5];
/// let s2: Vec<isize> = s.to_vec();
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub ITER_CLONED_COLLECT,
style,
"using `.cloned().collect()` on slice to create a `Vec`"
/// // Good
/// name.ends_with('_') || name.ends_with('-');
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub CHARS_LAST_CMP,
style,
"using `.chars().last()` or `.chars().next_back()` to check if a string ends with a char"
/// let x: &[i32] = &[1, 2, 3, 4, 5];
/// do_stuff(x);
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub USELESS_ASREF,
complexity,
"using `as_ref` where the types before and after the call are the same"
/// ```rust
/// let _ = (0..3).any(|x| x > 2);
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub UNNECESSARY_FOLD,
style,
"using `fold` when a more succinct alternative exists"
/// // As there is no conditional check on the argument this could be written as:
/// let _ = (0..4).map(|x| x + 1);
/// ```
+ #[clippy::version = "1.31.0"]
pub UNNECESSARY_FILTER_MAP,
complexity,
"using `filter_map` when a more succinct alternative exists"
/// // Good
/// let _ = (&vec![3, 4, 5]).iter();
/// ```
+ #[clippy::version = "1.32.0"]
pub INTO_ITER_ON_REF,
style,
"using `.into_iter()` on a reference"
/// ```rust
/// let _ = (0..3).map(|x| x + 2).count();
/// ```
+ #[clippy::version = "1.39.0"]
pub SUSPICIOUS_MAP,
suspicious,
"suspicious usage of map"
/// MaybeUninit::uninit().assume_init()
/// };
/// ```
+ #[clippy::version = "1.39.0"]
pub UNINIT_ASSUMED_INIT,
correctness,
"`MaybeUninit::uninit().assume_init()`"
/// let add = x.saturating_add(y);
/// let sub = x.saturating_sub(y);
/// ```
+ #[clippy::version = "1.39.0"]
pub MANUAL_SATURATING_ARITHMETIC,
style,
"`.chcked_add/sub(x).unwrap_or(MAX/MIN)`"
/// ```rust
/// unsafe { (&() as *const ()).offset(1) };
/// ```
+ #[clippy::version = "1.41.0"]
pub ZST_OFFSET,
correctness,
"Check for offset calculations on raw pointers to zero-sized types"
/// # Ok::<_, std::io::Error>(())
/// # };
/// ```
+ #[clippy::version = "1.42.0"]
pub FILETYPE_IS_FILE,
restriction,
"`FileType::is_file` is not recommended to test for readable file type"
/// opt.as_deref()
/// # ;
/// ```
+ #[clippy::version = "1.42.0"]
pub OPTION_AS_REF_DEREF,
complexity,
"using `as_ref().map(Deref::deref)`, which is more succinctly expressed as `as_deref()`"
/// a.get(2);
/// b.get(0);
/// ```
+ #[clippy::version = "1.46.0"]
pub ITER_NEXT_SLICE,
style,
"using `.iter().next()` on a sliced array, which can be shortened to just `.get()`"
/// string.insert(0, 'R');
/// string.push('R');
/// ```
+ #[clippy::version = "1.49.0"]
pub SINGLE_CHAR_ADD_STR,
style,
"`push_str()` or `insert_str()` used with a single-character string literal as parameter"
///
/// opt.unwrap_or(42);
/// ```
+ #[clippy::version = "1.48.0"]
pub UNNECESSARY_LAZY_EVALUATIONS,
style,
"using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation"
/// ```rust
/// (0..3).try_for_each(|t| Err(t));
/// ```
+ #[clippy::version = "1.49.0"]
pub MAP_COLLECT_RESULT_UNIT,
style,
"using `.map(_).collect::<Result<(),_>()`, which can be replaced with `try_for_each`"
///
/// assert_eq!(v, vec![5, 5, 5, 5, 5]);
/// ```
+ #[clippy::version = "1.49.0"]
pub FROM_ITER_INSTEAD_OF_COLLECT,
pedantic,
"use `.collect()` instead of `::from_iter()`"
/// assert!(x >= 0);
/// });
/// ```
+ #[clippy::version = "1.51.0"]
pub INSPECT_FOR_EACH,
complexity,
"using `.inspect().for_each()`, which can be replaced with `.for_each()`"
/// # let iter = vec![Some(1)].into_iter();
/// iter.flatten();
/// ```
+ #[clippy::version = "1.52.0"]
pub FILTER_MAP_IDENTITY,
complexity,
"call to `filter_map` where `flatten` is sufficient"
/// let x = [1, 2, 3];
/// let y: Vec<_> = x.iter().map(|x| 2*x).collect();
/// ```
+ #[clippy::version = "1.52.0"]
pub MAP_IDENTITY,
complexity,
"using iterator.map(|x| x)"
/// // Good
/// let _ = "Hello".as_bytes().get(3);
/// ```
+ #[clippy::version = "1.52.0"]
pub BYTES_NTH,
style,
"replace `.bytes().nth()` with `.as_bytes().get()`"
/// let b = a.clone();
/// let c = a.clone();
/// ```
+ #[clippy::version = "1.52.0"]
pub IMPLICIT_CLONE,
pedantic,
"implicitly cloning a value by invoking a function on its dereferenced type"
/// let _ = some_vec.len();
/// let _ = &some_vec[..].len();
/// ```
+ #[clippy::version = "1.52.0"]
pub ITER_COUNT,
complexity,
"replace `.iter().count()` with `.len()`"
/// // use x
/// }
/// ```
+ #[clippy::version = "1.54.0"]
pub SUSPICIOUS_SPLITN,
correctness,
"checks for `.splitn(0, ..)` and `.splitn(1, ..)`"
/// // Good
/// let x: String = "x".repeat(10);
/// ```
+ #[clippy::version = "1.54.0"]
pub MANUAL_STR_REPEAT,
perf,
"manual implementation of `str::repeat`"
/// let (key, value) = _.split_once('=')?;
/// let value = _.split_once('=')?.1;
/// ```
+ #[clippy::version = "1.57.0"]
pub MANUAL_SPLIT_ONCE,
complexity,
"replace `.splitn(2, pat)` with `.split_once(pat)`"
}
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for usages of `str::splitn` (or `str::rsplitn`) where using `str::split` would be the same.
+ /// ### Why is this bad?
+ /// The function `split` is simpler and there is no performance difference in these cases, considering
+ /// that both functions return a lazy iterator.
+ /// ### Example
+ /// ```rust
+ /// // Bad
+ /// let str = "key=value=add";
+ /// let _ = str.splitn(3, '=').next().unwrap();
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// // Good
+ /// let str = "key=value=add";
+ /// let _ = str.split('=').next().unwrap();
+ /// ```
+ #[clippy::version = "1.58.0"]
+ pub NEEDLESS_SPLITN,
+ complexity,
+ "usages of `str::splitn` that can be replaced with `str::split`"
+}
+
pub struct Methods {
avoid_breaking_exported_api: bool,
msrv: Option<RustcVersion>,
SUSPICIOUS_SPLITN,
MANUAL_STR_REPEAT,
EXTEND_WITH_DRAIN,
- MANUAL_SPLIT_ONCE
+ MANUAL_SPLIT_ONCE,
+ NEEDLESS_SPLITN
]);
/// Extracts a method call name, args, and `Span` of the method name.
impl<'tcx> LateLintPass<'tcx> for Methods {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
- if in_macro(expr.span) {
+ if expr.span.from_expansion() {
return;
}
("assume_init", []) => uninit_assumed_init::check(cx, expr, recv),
("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, msrv),
("collect", []) => match method_call!(recv) {
- Some(("cloned", [recv2], _)) => iter_cloned_collect::check(cx, expr, recv2),
+ Some((name @ ("cloned" | "copied"), [recv2], _)) => {
+ iter_cloned_collect::check(cx, name, expr, recv2);
+ },
Some(("map", [m_recv, m_arg], _)) => {
map_collect_result_unit::check(cx, expr, m_recv, m_arg, recv);
},
if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) {
suspicious_splitn::check(cx, name, expr, recv, count);
if count == 2 && meets_msrv(msrv, &msrvs::STR_SPLIT_ONCE) {
- manual_split_once::check(cx, name, expr, recv, pat_arg);
+ str_splitn::check_manual_split_once(cx, name, expr, recv, pat_arg);
+ }
+ if count >= 2 {
+ str_splitn::check_needless_splitn(cx, name, expr, recv, pat_arg, count);
}
}
},
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::is_lang_ctor;
use clippy_utils::source::snippet;
use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{is_lang_ctor, single_segment_path};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::LangItem::{OptionNone, OptionSome};
use super::OPTION_MAP_OR_NONE;
use super::RESULT_MAP_OR_INTO_OPTION;
+// The expression inside a closure may or may not have surrounding braces
+// which causes problems when generating a suggestion.
+fn reduce_unit_expression<'a>(
+ cx: &LateContext<'_>,
+ expr: &'a hir::Expr<'_>,
+) -> Option<(&'a hir::Expr<'a>, &'a [hir::Expr<'a>])> {
+ match expr.kind {
+ hir::ExprKind::Call(func, arg_char) => Some((func, arg_char)),
+ hir::ExprKind::Block(block, _) => {
+ match (block.stmts, block.expr) {
+ (&[], Some(inner_expr)) => {
+ // If block only contains an expression,
+ // reduce `|x| { x + 1 }` to `|x| x + 1`
+ reduce_unit_expression(cx, inner_expr)
+ },
+ _ => None,
+ }
+ },
+ _ => None,
+ }
+}
+
/// lint use of `_.map_or(None, _)` for `Option`s and `Result`s
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
return;
}
- let (lint_name, msg, instead, hint) = {
- let default_arg_is_none = if let hir::ExprKind::Path(ref qpath) = def_arg.kind {
- is_lang_ctor(cx, qpath, OptionNone)
- } else {
- return;
- };
+ let default_arg_is_none = if let hir::ExprKind::Path(ref qpath) = def_arg.kind {
+ is_lang_ctor(cx, qpath, OptionNone)
+ } else {
+ return;
+ };
- if !default_arg_is_none {
- // nothing to lint!
- return;
- }
+ if !default_arg_is_none {
+ // nothing to lint!
+ return;
+ }
- let f_arg_is_some = if let hir::ExprKind::Path(ref qpath) = map_arg.kind {
- is_lang_ctor(cx, qpath, OptionSome)
- } else {
- false
- };
+ let f_arg_is_some = if let hir::ExprKind::Path(ref qpath) = map_arg.kind {
+ is_lang_ctor(cx, qpath, OptionSome)
+ } else {
+ false
+ };
+
+ if is_option {
+ let self_snippet = snippet(cx, recv.span, "..");
+ if_chain! {
+ if let hir::ExprKind::Closure(_, _, id, span, _) = map_arg.kind;
+ let arg_snippet = snippet(cx, span, "..");
+ let body = cx.tcx.hir().body(id);
+ if let Some((func, arg_char)) = reduce_unit_expression(cx, &body.value);
+ if arg_char.len() == 1;
+ if let hir::ExprKind::Path(ref qpath) = func.kind;
+ if let Some(segment) = single_segment_path(qpath);
+ if segment.ident.name == sym::Some;
+ then {
+ let func_snippet = snippet(cx, arg_char[0].span, "..");
+ let msg = "called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling \
+ `map(..)` instead";
+ return span_lint_and_sugg(
+ cx,
+ OPTION_MAP_OR_NONE,
+ expr.span,
+ msg,
+ "try using `map` instead",
+ format!("{0}.map({1} {2})", self_snippet, arg_snippet,func_snippet),
+ Applicability::MachineApplicable,
+ );
+ }
- if is_option {
- let self_snippet = snippet(cx, recv.span, "..");
- let func_snippet = snippet(cx, map_arg.span, "..");
- let msg = "called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling \
- `and_then(..)` instead";
- (
- OPTION_MAP_OR_NONE,
- msg,
- "try using `and_then` instead",
- format!("{0}.and_then({1})", self_snippet, func_snippet),
- )
- } else if f_arg_is_some {
- let msg = "called `map_or(None, Some)` on a `Result` value. This can be done more directly by calling \
- `ok()` instead";
- let self_snippet = snippet(cx, recv.span, "..");
- (
- RESULT_MAP_OR_INTO_OPTION,
- msg,
- "try using `ok` instead",
- format!("{0}.ok()", self_snippet),
- )
- } else {
- // nothing to lint!
- return;
}
- };
- span_lint_and_sugg(
- cx,
- lint_name,
- expr.span,
- msg,
- instead,
- hint,
- Applicability::MachineApplicable,
- );
+ let func_snippet = snippet(cx, map_arg.span, "..");
+ let msg = "called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling \
+ `and_then(..)` instead";
+ return span_lint_and_sugg(
+ cx,
+ OPTION_MAP_OR_NONE,
+ expr.span,
+ msg,
+ "try using `and_then` instead",
+ format!("{0}.and_then({1})", self_snippet, func_snippet),
+ Applicability::MachineApplicable,
+ );
+ } else if f_arg_is_some {
+ let msg = "called `map_or(None, Some)` on a `Result` value. This can be done more directly by calling \
+ `ok()` instead";
+ let self_snippet = snippet(cx, recv.span, "..");
+ return span_lint_and_sugg(
+ cx,
+ RESULT_MAP_OR_INTO_OPTION,
+ expr.span,
+ msg,
+ "try using `ok` instead",
+ format!("{0}.ok()", self_snippet),
+ Applicability::MachineApplicable,
+ );
+ }
}
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::eager_or_lazy::is_lazyness_candidate;
-use clippy_utils::is_trait_item;
+use clippy_utils::eager_or_lazy::switch_to_lazy_eval;
use clippy_utils::source::{snippet, snippet_with_applicability, snippet_with_macro_callsite};
-use clippy_utils::ty::implements_trait;
-use clippy_utils::ty::{is_type_diagnostic_item, match_type};
-use clippy_utils::{contains_return, last_path_segment, paths};
+use clippy_utils::ty::{implements_trait, match_type};
+use clippy_utils::{contains_return, is_trait_item, last_path_segment, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
-use rustc_hir::{BlockCheckMode, UnsafeSource};
use rustc_lint::LateContext;
-use rustc_middle::ty;
use rustc_span::source_map::Span;
use rustc_span::symbol::{kw, sym};
use std::borrow::Cow;
(&paths::RESULT, true, &["or", "unwrap_or"], "else"),
];
- if let hir::ExprKind::MethodCall(path, _, [self_arg, ..], _) = &arg.kind {
- if path.ident.name == sym::len {
- let ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
-
- match ty.kind() {
- ty::Slice(_) | ty::Array(_, _) | ty::Str => return,
- _ => (),
- }
-
- if is_type_diagnostic_item(cx, ty, sym::Vec) {
- return;
- }
- }
- }
-
if_chain! {
if KNOW_TYPES.iter().any(|k| k.2.contains(&name));
- if is_lazyness_candidate(cx, arg);
+ if switch_to_lazy_eval(cx, arg);
if !contains_return(arg);
let self_ty = cx.typeck_results().expr_ty(self_expr);
}
}
- if args.len() == 2 {
- match args[1].kind {
+ if let [self_arg, arg] = args {
+ let inner_arg = if let hir::ExprKind::Block(
+ hir::Block {
+ stmts: [],
+ expr: Some(expr),
+ ..
+ },
+ _,
+ ) = arg.kind
+ {
+ expr
+ } else {
+ arg
+ };
+ match inner_arg.kind {
hir::ExprKind::Call(fun, or_args) => {
let or_has_args = !or_args.is_empty();
- if !check_unwrap_or_default(cx, name, fun, &args[0], &args[1], or_has_args, expr.span) {
+ if !check_unwrap_or_default(cx, name, fun, self_arg, arg, or_has_args, expr.span) {
let fun_span = if or_has_args { None } else { Some(fun.span) };
- check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, fun_span);
+ check_general_case(cx, name, method_span, self_arg, arg, expr.span, fun_span);
}
},
hir::ExprKind::Index(..) | hir::ExprKind::MethodCall(..) => {
- check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, None);
- },
- hir::ExprKind::Block(block, _)
- if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) =>
- {
- if let Some(block_expr) = block.expr {
- if let hir::ExprKind::MethodCall(..) = block_expr.kind {
- check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, None);
- }
- }
+ check_general_case(cx, name, method_span, self_arg, arg, expr.span, None);
},
_ => (),
}
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
use clippy_utils::source::{snippet, snippet_with_applicability};
+use clippy_utils::sugg::deref_closure_args;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{is_trait_method, strip_pat_refs};
use if_chain::if_chain;
if search_snippet.lines().count() <= 1 {
// suggest `any(|x| ..)` instead of `any(|&x| ..)` for `find(|&x| ..).is_some()`
// suggest `any(|..| *..)` instead of `any(|..| **..)` for `find(|..| **..).is_some()`
+ let mut applicability = Applicability::MachineApplicable;
let any_search_snippet = if_chain! {
if search_method == "find";
if let hir::ExprKind::Closure(_, _, body_id, ..) = search_arg.kind;
then {
if let hir::PatKind::Ref(..) = closure_arg.pat.kind {
Some(search_snippet.replacen('&', "", 1))
- } else if let PatKind::Binding(_, _, ident, _) = strip_pat_refs(closure_arg.pat).kind {
- let name = &*ident.name.as_str();
- Some(search_snippet.replace(&format!("*{}", name), name))
+ } else if let PatKind::Binding(..) = strip_pat_refs(closure_arg.pat).kind {
+ // `find()` provides a reference to the item, but `any` does not,
+ // so we should fix item usages for suggestion
+ if let Some(closure_sugg) = deref_closure_args(cx, search_arg) {
+ applicability = closure_sugg.applicability;
+ Some(closure_sugg.suggestion)
+ } else {
+ Some(search_snippet.to_string())
+ }
} else {
None
}
"any({})",
any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str)
),
- Applicability::MachineApplicable,
+ applicability,
);
} else {
let iter = snippet(cx, search_recv.span, "..");
iter,
any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str)
),
- Applicability::MachineApplicable,
+ applicability,
);
}
} else {
use super::SINGLE_CHAR_PATTERN;
-const PATTERN_METHODS: [(&str, usize); 19] = [
+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),
("strip_suffix", 1),
("trim_start_matches", 1),
("trim_end_matches", 1),
+ ("replace", 1),
+ ("replacen", 1),
];
/// lint for length-1 `str`s for methods in `PATTERN_METHODS`
--- /dev/null
+use clippy_utils::consts::{constant, Constant};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_context;
+use clippy_utils::{is_diag_item_method, match_def_path, paths};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind, HirId, LangItem, Node, QPath};
+use rustc_lint::LateContext;
+use rustc_middle::ty::{self, adjustment::Adjust};
+use rustc_span::{symbol::sym, Span, SyntaxContext};
+
+use super::MANUAL_SPLIT_ONCE;
+
+pub(super) fn check_manual_split_once(
+ cx: &LateContext<'_>,
+ method_name: &str,
+ expr: &Expr<'_>,
+ self_arg: &Expr<'_>,
+ pat_arg: &Expr<'_>,
+) {
+ if !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() {
+ return;
+ }
+
+ let ctxt = expr.span.ctxt();
+ let (method_name, msg, reverse) = if method_name == "splitn" {
+ ("split_once", "manual implementation of `split_once`", false)
+ } else {
+ ("rsplit_once", "manual implementation of `rsplit_once`", true)
+ };
+ let usage = match parse_iter_usage(cx, ctxt, cx.tcx.hir().parent_iter(expr.hir_id), reverse) {
+ Some(x) => x,
+ None => return,
+ };
+
+ let mut app = Applicability::MachineApplicable;
+ let self_snip = snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0;
+ let pat_snip = snippet_with_context(cx, pat_arg.span, ctxt, "..", &mut app).0;
+
+ let sugg = match usage.kind {
+ IterUsageKind::NextTuple => {
+ format!("{}.{}({})", self_snip, method_name, pat_snip)
+ },
+ IterUsageKind::RNextTuple => format!("{}.{}({}).map(|(x, y)| (y, x))", self_snip, method_name, pat_snip),
+ IterUsageKind::Next | IterUsageKind::Second => {
+ let self_deref = {
+ let adjust = cx.typeck_results().expr_adjustments(self_arg);
+ if adjust.is_empty() {
+ String::new()
+ } else if cx.typeck_results().expr_ty(self_arg).is_box()
+ || adjust
+ .iter()
+ .any(|a| matches!(a.kind, Adjust::Deref(Some(_))) || a.target.is_box())
+ {
+ format!("&{}", "*".repeat(adjust.len() - 1))
+ } else {
+ "*".repeat(adjust.len() - 2)
+ }
+ };
+ if matches!(usage.kind, IterUsageKind::Next) {
+ match usage.unwrap_kind {
+ Some(UnwrapKind::Unwrap) => {
+ if reverse {
+ format!("{}.{}({}).unwrap().0", self_snip, method_name, pat_snip)
+ } else {
+ format!(
+ "{}.{}({}).map_or({}{}, |x| x.0)",
+ self_snip, method_name, pat_snip, self_deref, &self_snip
+ )
+ }
+ },
+ Some(UnwrapKind::QuestionMark) => {
+ format!(
+ "{}.{}({}).map_or({}{}, |x| x.0)",
+ self_snip, method_name, pat_snip, self_deref, &self_snip
+ )
+ },
+ None => {
+ format!(
+ "Some({}.{}({}).map_or({}{}, |x| x.0))",
+ &self_snip, method_name, pat_snip, self_deref, &self_snip
+ )
+ },
+ }
+ } else {
+ match usage.unwrap_kind {
+ Some(UnwrapKind::Unwrap) => {
+ if reverse {
+ // In this case, no better suggestion is offered.
+ return;
+ }
+ format!("{}.{}({}).unwrap().1", self_snip, method_name, pat_snip)
+ },
+ Some(UnwrapKind::QuestionMark) => {
+ format!("{}.{}({})?.1", self_snip, method_name, pat_snip)
+ },
+ None => {
+ format!("{}.{}({}).map(|x| x.1)", self_snip, method_name, pat_snip)
+ },
+ }
+ }
+ },
+ };
+
+ span_lint_and_sugg(cx, MANUAL_SPLIT_ONCE, usage.span, msg, "try this", sugg, app);
+}
+
+enum IterUsageKind {
+ Next,
+ Second,
+ NextTuple,
+ RNextTuple,
+}
+
+enum UnwrapKind {
+ Unwrap,
+ QuestionMark,
+}
+
+struct IterUsage {
+ kind: IterUsageKind,
+ unwrap_kind: Option<UnwrapKind>,
+ span: Span,
+}
+
+#[allow(clippy::too_many_lines)]
+fn parse_iter_usage(
+ cx: &LateContext<'tcx>,
+ ctxt: SyntaxContext,
+ mut iter: impl Iterator<Item = (HirId, Node<'tcx>)>,
+ reverse: bool,
+) -> 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 {
+ (name, args)
+ } else {
+ return None;
+ };
+ let did = cx.typeck_results().type_dependent_def_id(e.hir_id)?;
+ let iter_id = cx.tcx.get_diagnostic_item(sym::Iterator)?;
+
+ match (&*name.ident.as_str(), args) {
+ ("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => {
+ if reverse {
+ (IterUsageKind::Second, e.span)
+ } else {
+ (IterUsageKind::Next, e.span)
+ }
+ },
+ ("next_tuple", []) => {
+ return if_chain! {
+ if match_def_path(cx, did, &paths::ITERTOOLS_NEXT_TUPLE);
+ if let ty::Adt(adt_def, subs) = cx.typeck_results().expr_ty(e).kind();
+ if cx.tcx.is_diagnostic_item(sym::Option, adt_def.did);
+ if let ty::Tuple(subs) = subs.type_at(0).kind();
+ if subs.len() == 2;
+ then {
+ Some(IterUsage {
+ kind: if reverse { IterUsageKind::RNextTuple } else { IterUsageKind::NextTuple },
+ span: e.span,
+ unwrap_kind: None
+ })
+ } else {
+ None
+ }
+ };
+ },
+ ("nth" | "skip", [idx_expr]) if cx.tcx.trait_of_item(did) == Some(iter_id) => {
+ if let Some((Constant::Int(idx), _)) = constant(cx, cx.typeck_results(), idx_expr) {
+ let span = if name.ident.as_str() == "nth" {
+ e.span
+ } else {
+ if_chain! {
+ if let Some((_, Node::Expr(next_expr))) = iter.next();
+ 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 cx.tcx.trait_of_item(next_id) == Some(iter_id);
+ then {
+ next_expr.span
+ } else {
+ return None;
+ }
+ }
+ };
+ match if reverse { idx ^ 1 } else { idx } {
+ 0 => (IterUsageKind::Next, span),
+ 1 => (IterUsageKind::Second, span),
+ _ => return None,
+ }
+ } else {
+ return None;
+ }
+ },
+ _ => return None,
+ }
+ },
+ _ => return None,
+ };
+
+ let (unwrap_kind, span) = if let Some((_, Node::Expr(e))) = iter.next() {
+ match e.kind {
+ ExprKind::Call(
+ Expr {
+ kind: ExprKind::Path(QPath::LangItem(LangItem::TryTraitBranch, _)),
+ ..
+ },
+ _,
+ ) => {
+ let parent_span = e.span.parent_callsite().unwrap();
+ if parent_span.ctxt() == ctxt {
+ (Some(UnwrapKind::QuestionMark), parent_span)
+ } else {
+ (None, span)
+ }
+ },
+ _ if e.span.ctxt() != ctxt => (None, span),
+ ExprKind::MethodCall(name, _, [_], _)
+ if name.ident.name == sym::unwrap
+ && cx
+ .typeck_results()
+ .type_dependent_def_id(e.hir_id)
+ .map_or(false, |id| is_diag_item_method(cx, id, sym::Option)) =>
+ {
+ (Some(UnwrapKind::Unwrap), e.span)
+ },
+ _ => (None, span),
+ }
+ } else {
+ (None, span)
+ };
+
+ Some(IterUsage {
+ kind,
+ unwrap_kind,
+ span,
+ })
+}
+
+use super::NEEDLESS_SPLITN;
+
+pub(super) fn check_needless_splitn(
+ cx: &LateContext<'_>,
+ method_name: &str,
+ expr: &Expr<'_>,
+ self_arg: &Expr<'_>,
+ pat_arg: &Expr<'_>,
+ count: u128,
+) {
+ if !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() {
+ return;
+ }
+ let ctxt = expr.span.ctxt();
+ let mut app = Applicability::MachineApplicable;
+ let (reverse, message) = if method_name == "splitn" {
+ (false, "unnecessary use of `splitn`")
+ } else {
+ (true, "unnecessary use of `rsplitn`")
+ };
+ if_chain! {
+ if count >= 2;
+ if check_iter(cx, ctxt, cx.tcx.hir().parent_iter(expr.hir_id), count);
+ then {
+ span_lint_and_sugg(
+ cx,
+ NEEDLESS_SPLITN,
+ expr.span,
+ message,
+ "try this",
+ format!(
+ "{}.{}({})",
+ snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0,
+ if reverse {"rsplit"} else {"split"},
+ snippet_with_context(cx, pat_arg.span, ctxt, "..", &mut app).0
+ ),
+ app,
+ );
+ }
+ }
+}
+
+fn check_iter(
+ cx: &LateContext<'tcx>,
+ ctxt: SyntaxContext,
+ mut iter: impl Iterator<Item = (HirId, Node<'tcx>)>,
+ count: u128,
+) -> bool {
+ match iter.next() {
+ Some((_, Node::Expr(e))) if e.span.ctxt() == ctxt => {
+ let (name, args) = if let ExprKind::MethodCall(name, _, [_, args @ ..], _) = e.kind {
+ (name, args)
+ } else {
+ return false;
+ };
+ if_chain! {
+ if let Some(did) = cx.typeck_results().type_dependent_def_id(e.hir_id);
+ if let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
+ then {
+ match (&*name.ident.as_str(), args) {
+ ("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => {
+ return true;
+ },
+ ("next_tuple", []) if count > 2 => {
+ return true;
+ },
+ ("nth", [idx_expr]) if cx.tcx.trait_of_item(did) == Some(iter_id) => {
+ if let Some((Constant::Int(idx), _)) = constant(cx, cx.typeck_results(), idx_expr) {
+ if count > idx + 1 {
+ return true;
+ }
+ }
+ },
+ _ => return false,
+ }
+ }
+ }
+ },
+ _ => return false,
+ };
+ false
+}
return;
}
- if eager_or_lazy::is_eagerness_candidate(cx, body_expr) {
+ if eager_or_lazy::switch_to_eager_eval(cx, body_expr) {
let msg = if is_option {
"unnecessary closure used to substitute value for `Option::None`"
} else {
// for regular string: "a"
&snip[1..(snip.len() - 1)]
};
- let hint = format!("'{}'", if ch == "'" { "\\'" } else { ch });
+
+ let hint = format!("'{}'", match ch {
+ "'" => "\\'" ,
+ r"\" => "\\\\",
+ _ => ch,
+ });
+
Some(hint)
} else {
None
/// ```
/// It will always be equal to `0`. Probably the author meant to clamp the value
/// between 0 and 100, but has erroneously swapped `min` and `max`.
+ #[clippy::version = "pre 1.29.0"]
pub MIN_MAX,
correctness,
"`min(_, max(_, _))` (or vice versa) with bounds clamping the result to a constant"
/// true
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub TOPLEVEL_REF_ARG,
style,
"an entire binding declared as `ref`, in a function argument or a `let` statement"
/// // Good
/// if x.is_nan() { }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub CMP_NAN,
correctness,
"comparisons to `NAN`, which will always return false, probably not intended"
/// if (y - 1.23f64).abs() < error_margin { }
/// if (y - x).abs() > error_margin { }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub FLOAT_CMP,
pedantic,
"using `==` or `!=` on float values instead of comparing difference with an epsilon"
/// # let y = String::from("foo");
/// if x == y {}
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub CMP_OWNED,
perf,
"creating owned instances for comparing with others, e.g., `x == \"foo\".to_string()`"
/// let a = x % 1;
/// let a = x % -1;
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub MODULO_ONE,
correctness,
"taking a number modulo +/-1, which can either panic/overflow or always returns 0"
/// let y = _x + 1; // Here we are using `_x`, even though it has a leading
/// // underscore. We should rename `_x` to `x`
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub USED_UNDERSCORE_BINDING,
pedantic,
"using a binding which is prefixed with an underscore"
/// ```rust,ignore
/// f() && g(); // We should write `if f() { g(); }`.
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub SHORT_CIRCUIT_STATEMENT,
complexity,
"using a short circuit boolean condition as a statement"
/// // Good
/// let a = std::ptr::null::<u32>();
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub ZERO_PTR,
style,
"using `0 as *{const, mut} T`"
/// // let error_margin = std::f64::EPSILON;
/// if (x - ONE).abs() < error_margin { }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub FLOAT_CMP_CONST,
restriction,
"using `==` or `!=` on float constants instead of comparing difference with an epsilon"
/// Foo { .. } => {},
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub UNNEEDED_FIELD_PATTERN,
restriction,
"struct fields bound to a wildcard instead of using `..`"
/// // Good
/// fn bar(a: i32, _b: i32) {}
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub DUPLICATE_UNDERSCORE_ARGUMENT,
style,
"function arguments having names which only differ by an underscore"
/// let mut x = 3;
/// --x;
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub DOUBLE_NEG,
style,
"`--x`, which is a double negation of `x` and not a pre-decrement as in C/C++"
/// // Good
/// let y = 0x1A9BACD;
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub MIXED_CASE_HEX_LITERALS,
style,
"hex literals whose letter digits are not consistently upper- or lowercased"
/// // Good
/// let y = 123832_i32;
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub UNSEPARATED_LITERAL_SUFFIX,
restriction,
"literals whose suffix is not separated by an underscore"
/// // Good
/// let y = 123832i32;
/// ```
+ #[clippy::version = "1.58.0"]
pub SEPARATED_LITERAL_SUFFIX,
restriction,
"literals whose suffix is separated by an underscore"
/// ```
///
/// prints `83` (as `83 == 0o123` while `123 == 0o173`).
+ #[clippy::version = "pre 1.29.0"]
pub ZERO_PREFIXED_LITERAL,
complexity,
"integer literals starting with `0`"
/// }
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub BUILTIN_TYPE_SHADOW,
style,
"shadowing a builtin type"
/// y => (),
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub REDUNDANT_PATTERN,
style,
"using `name @ _` in a pattern"
/// _ => (),
/// }
/// ```
+ #[clippy::version = "1.40.0"]
pub UNNEEDED_WILDCARD_PATTERN,
complexity,
"tuple patterns with a wildcard pattern (`_`) is next to a rest pattern (`..`)"
/// }
/// # }
/// ```
+ #[clippy::version = "1.34.0"]
pub MISSING_CONST_FOR_FN,
nursery,
"Lint functions definitions that could be made `const fn`"
/// allowed-by-default lint for
/// public members, but has no way to enforce documentation of private items.
/// This lint fixes that.
+ #[clippy::version = "pre 1.29.0"]
pub MISSING_DOCS_IN_PRIVATE_ITEMS,
restriction,
"detects missing documentation for public and private members"
/// ```rust,ignore
/// use serde_json::Value as JsonValue;
/// ```
+ #[clippy::version = "1.55.0"]
pub MISSING_ENFORCED_IMPORT_RENAMES,
restriction,
"enforce import renames"
/// fn def_bar() {} // missing #[inline]
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub MISSING_INLINE_IN_PUBLIC_ITEMS,
restriction,
"detects missing `#[inline]` attribute for public callables (functions, trait methods, methods...)"
/// stuff.rs
/// lib.rs
/// ```
+ #[clippy::version = "1.57.0"]
pub MOD_MODULE_FILES,
restriction,
"checks that module layout is consistent"
/// lib.rs
/// ```
+ #[clippy::version = "1.57.0"]
pub SELF_NAMED_MODULE_FILES,
restriction,
"checks that module layout is consistent"
/// ```rust
/// let x = -17 % 3;
/// ```
+ #[clippy::version = "1.42.0"]
pub MODULO_ARITHMETIC,
restriction,
"any modulo arithmetic statement"
/// ctrlc = "=3.1.0"
/// ansi_term = "=0.11.0"
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub MULTIPLE_CRATE_VERSIONS,
cargo,
"multiple versions of the same crate being used"
/// let _: HashSet<Bad> = HashSet::new();
/// }
/// ```
+ #[clippy::version = "1.42.0"]
pub MUTABLE_KEY_TYPE,
suspicious,
"Check for mutable `Map`/`Set` key type"
/// # let mut y = 1;
/// let x = &mut &mut y;
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub MUT_MUT,
pedantic,
"usage of double-mut refs, e.g., `&mut &mut ...`"
/// let value = value_mutex.get_mut().unwrap();
/// *value += 1;
/// ```
+ #[clippy::version = "1.49.0"]
pub MUT_MUTEX_LOCK,
style,
"`&mut Mutex::lock` does unnecessary locking"
/// // Good
/// my_vec.push(&value)
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub UNNECESSARY_MUT_PASSED,
style,
"an argument passed as a mutable reference although the callee only demands an immutable reference"
/// fn take_a_mut_parameter(_: &mut u32) -> bool { unimplemented!() }
/// debug_assert!(take_a_mut_parameter(&mut 5));
/// ```
+ #[clippy::version = "1.40.0"]
pub DEBUG_ASSERT_WITH_MUT_CALL,
nursery,
"mutable arguments in `debug_assert{,_ne,_eq}!`"
/// # use std::sync::atomic::AtomicBool;
/// let x = AtomicBool::new(y);
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub MUTEX_ATOMIC,
perf,
"using a mutex where an atomic value could be used instead"
/// # use std::sync::atomic::AtomicUsize;
/// let x = AtomicUsize::new(0usize);
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub MUTEX_INTEGER,
nursery,
"using a mutex for an integer type"
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::in_macro;
use if_chain::if_chain;
use rustc_ast::ast::{BindingMode, Lifetime, Mutability, Param, PatKind, Path, TyKind};
use rustc_errors::Applicability;
/// }
/// }
/// ```
+ #[clippy::version = "1.47.0"]
pub NEEDLESS_ARBITRARY_SELF_TYPE,
complexity,
"type of `self` parameter is already by default `Self`"
let self_param = match (binding_mode, mutbl) {
(Mode::Ref(None), Mutability::Mut) => "&mut self".to_string(),
(Mode::Ref(Some(lifetime)), Mutability::Mut) => {
- if in_macro(lifetime.ident.span) {
+ if lifetime.ident.span.from_expansion() {
applicability = Applicability::HasPlaceholders;
"&'_ mut self".to_string()
} else {
},
(Mode::Ref(None), Mutability::Not) => "&self".to_string(),
(Mode::Ref(Some(lifetime)), Mutability::Not) => {
- if in_macro(lifetime.ident.span) {
+ if lifetime.ident.span.from_expansion() {
applicability = Applicability::HasPlaceholders;
"&'_ self".to_string()
} else {
impl EarlyLintPass for NeedlessArbitrarySelfType {
fn check_param(&mut self, cx: &EarlyContext<'_>, p: &Param) {
// Bail out if the parameter it's not a receiver or was not written by the user
- if !p.is_self() || in_macro(p.span) {
+ if !p.is_self() || p.span.from_expansion() {
return;
}
use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::in_macro;
use clippy_utils::source::snippet_opt;
use if_chain::if_chain;
use rustc_errors::Applicability;
/// let (x,y) = (true, false);
/// if x && !y {}
/// ```
+ #[clippy::version = "1.54.0"]
pub NEEDLESS_BITWISE_BOOL,
pedantic,
"Boolean expressions that use bitwise rather than lazy operators"
fn is_bitwise_operation(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
let ty = cx.typeck_results().expr_ty(expr);
if_chain! {
- if !in_macro(expr.span);
+ if !expr.span.from_expansion();
if let (&ExprKind::Binary(ref op, _, right), &ty::Bool) = (&expr.kind, &ty.kind());
if op.node == BinOpKind::BitAnd || op.node == BinOpKind::BitOr;
if let ExprKind::Call(..) | ExprKind::MethodCall(..) | ExprKind::Binary(..) | ExprKind::Unary(..) = right.kind;
/// ```rust,ignore
/// !x
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub NEEDLESS_BOOL,
complexity,
"if-statements with plain booleans in the then- and else-clause, e.g., `if p { true } else { false }`"
/// if x {}
/// if !y {}
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub BOOL_COMPARISON,
complexity,
"comparing a variable to a boolean, e.g., `if x == true` or `if x != true`"
+++ /dev/null
-//! Checks for needless address of operations (`&`)
-//!
-//! This lint is **warn** by default
-
-use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::{snippet_opt, snippet_with_applicability, snippet_with_context};
-use clippy_utils::{get_parent_expr, in_macro, path_to_local};
-use if_chain::if_chain;
-use rustc_ast::util::parser::PREC_POSTFIX;
-use rustc_data_structures::fx::FxIndexMap;
-use rustc_errors::Applicability;
-use rustc_hir::{BindingAnnotation, Body, BodyId, BorrowKind, Expr, ExprKind, HirId, Mutability, Pat, PatKind, UnOp};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty;
-use rustc_middle::ty::adjustment::{Adjust, Adjustment};
-use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::Span;
-
-declare_clippy_lint! {
- /// ### What it does
- /// Checks for address of operations (`&`) that are going to
- /// be dereferenced immediately by the compiler.
- ///
- /// ### Why is this bad?
- /// Suggests that the receiver of the expression borrows
- /// the expression.
- ///
- /// ### Example
- /// ```rust
- /// fn fun(_a: &i32) {}
- ///
- /// // Bad
- /// let x: &i32 = &&&&&&5;
- /// fun(&x);
- ///
- /// // Good
- /// let x: &i32 = &5;
- /// fun(x);
- /// ```
- pub NEEDLESS_BORROW,
- style,
- "taking a reference that is going to be automatically dereferenced"
-}
-
-declare_clippy_lint! {
- /// ### What it does
- /// Checks for `ref` bindings which create a reference to a reference.
- ///
- /// ### Why is this bad?
- /// The address-of operator at the use site is clearer about the need for a reference.
- ///
- /// ### Example
- /// ```rust
- /// // Bad
- /// let x = Some("");
- /// if let Some(ref x) = x {
- /// // use `x` here
- /// }
- ///
- /// // Good
- /// let x = Some("");
- /// if let Some(x) = x {
- /// // use `&x` here
- /// }
- /// ```
- pub REF_BINDING_TO_REFERENCE,
- pedantic,
- "`ref` binding to a reference"
-}
-
-impl_lint_pass!(NeedlessBorrow => [NEEDLESS_BORROW, REF_BINDING_TO_REFERENCE]);
-#[derive(Default)]
-pub struct NeedlessBorrow {
- /// The body the first local was found in. Used to emit lints when the traversal of the body has
- /// been finished. Note we can't lint at the end of every body as they can be nested within each
- /// other.
- current_body: Option<BodyId>,
- /// The list of locals currently being checked by the lint.
- /// If the value is `None`, then the binding has been seen as a ref pattern, but is not linted.
- /// This is needed for or patterns where one of the branches can be linted, but another can not
- /// be.
- ///
- /// e.g. `m!(x) | Foo::Bar(ref x)`
- ref_locals: FxIndexMap<HirId, Option<RefPat>>,
-}
-
-struct RefPat {
- /// Whether every usage of the binding is dereferenced.
- always_deref: bool,
- /// The spans of all the ref bindings for this local.
- spans: Vec<Span>,
- /// The applicability of this suggestion.
- app: Applicability,
- /// All the replacements which need to be made.
- replacements: Vec<(Span, String)>,
-}
-
-impl<'tcx> LateLintPass<'tcx> for NeedlessBorrow {
- fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
- if let Some(local) = path_to_local(e) {
- self.check_local_usage(cx, e, local);
- }
-
- if e.span.from_expansion() {
- return;
- }
- if let ExprKind::AddrOf(BorrowKind::Ref, mutability, inner) = e.kind {
- if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(inner).kind() {
- for adj3 in cx.typeck_results().expr_adjustments(e).windows(3) {
- if let [
- Adjustment {
- kind: Adjust::Deref(_), ..
- },
- Adjustment {
- kind: Adjust::Deref(_), ..
- },
- Adjustment {
- kind: Adjust::Borrow(_),
- ..
- },
- ] = *adj3
- {
- let help_msg_ty = if matches!(mutability, Mutability::Not) {
- format!("&{}", ty)
- } else {
- format!("&mut {}", ty)
- };
-
- span_lint_and_then(
- cx,
- NEEDLESS_BORROW,
- e.span,
- &format!(
- "this expression borrows a reference (`{}`) that is immediately dereferenced \
- by the compiler",
- help_msg_ty
- ),
- |diag| {
- if let Some(snippet) = snippet_opt(cx, inner.span) {
- diag.span_suggestion(
- e.span,
- "change this to",
- snippet,
- Applicability::MachineApplicable,
- );
- }
- },
- );
- }
- }
- }
- }
- }
-
- fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
- if let PatKind::Binding(BindingAnnotation::Ref, id, name, _) = pat.kind {
- if let Some(opt_prev_pat) = self.ref_locals.get_mut(&id) {
- // This binding id has been seen before. Add this pattern to the list of changes.
- if let Some(prev_pat) = opt_prev_pat {
- if in_macro(pat.span) {
- // Doesn't match the context of the previous pattern. Can't lint here.
- *opt_prev_pat = None;
- } else {
- prev_pat.spans.push(pat.span);
- prev_pat.replacements.push((
- pat.span,
- snippet_with_context(cx, name.span, pat.span.ctxt(), "..", &mut prev_pat.app)
- .0
- .into(),
- ));
- }
- }
- return;
- }
-
- if_chain! {
- if !in_macro(pat.span);
- if let ty::Ref(_, tam, _) = *cx.typeck_results().pat_ty(pat).kind();
- // only lint immutable refs, because borrowed `&mut T` cannot be moved out
- if let ty::Ref(_, _, Mutability::Not) = *tam.kind();
- then {
- let mut app = Applicability::MachineApplicable;
- let snip = snippet_with_context(cx, name.span, pat.span.ctxt(), "..", &mut app).0;
- self.current_body = self.current_body.or(cx.enclosing_body);
- self.ref_locals.insert(
- id,
- Some(RefPat {
- always_deref: true,
- spans: vec![pat.span],
- app,
- replacements: vec![(pat.span, snip.into())],
- }),
- );
- }
- }
- }
- }
-
- fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
- if Some(body.id()) == self.current_body {
- for pat in self.ref_locals.drain(..).filter_map(|(_, x)| x) {
- let replacements = pat.replacements;
- let app = pat.app;
- span_lint_and_then(
- cx,
- if pat.always_deref {
- NEEDLESS_BORROW
- } else {
- REF_BINDING_TO_REFERENCE
- },
- pat.spans,
- "this pattern creates a reference to a reference",
- |diag| {
- diag.multipart_suggestion("try this", replacements, app);
- },
- );
- }
- self.current_body = None;
- }
- }
-}
-impl NeedlessBorrow {
- fn check_local_usage(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, local: HirId) {
- if let Some(outer_pat) = self.ref_locals.get_mut(&local) {
- if let Some(pat) = outer_pat {
- // Check for auto-deref
- if !matches!(
- cx.typeck_results().expr_adjustments(e),
- [
- Adjustment {
- kind: Adjust::Deref(_),
- ..
- },
- Adjustment {
- kind: Adjust::Deref(_),
- ..
- },
- ..
- ]
- ) {
- match get_parent_expr(cx, e) {
- // Field accesses are the same no matter the number of references.
- Some(Expr {
- kind: ExprKind::Field(..),
- ..
- }) => (),
- Some(&Expr {
- span,
- kind: ExprKind::Unary(UnOp::Deref, _),
- ..
- }) if !in_macro(span) => {
- // Remove explicit deref.
- let snip = snippet_with_context(cx, e.span, span.ctxt(), "..", &mut pat.app).0;
- pat.replacements.push((span, snip.into()));
- },
- Some(parent) if !in_macro(parent.span) => {
- // Double reference might be needed at this point.
- if parent.precedence().order() == PREC_POSTFIX {
- // Parentheses would be needed here, don't lint.
- *outer_pat = None;
- } else {
- pat.always_deref = false;
- let snip = snippet_with_context(cx, e.span, parent.span.ctxt(), "..", &mut pat.app).0;
- pat.replacements.push((e.span, format!("&{}", snip)));
- }
- },
- _ if !in_macro(e.span) => {
- // Double reference might be needed at this point.
- pat.always_deref = false;
- let snip = snippet_with_applicability(cx, e.span, "..", &mut pat.app);
- pat.replacements.push((e.span, format!("&{}", snip)));
- },
- // Edge case for macros. The span of the identifier will usually match the context of the
- // binding, but not if the identifier was created in a macro. e.g. `concat_idents` and proc
- // macros
- _ => *outer_pat = None,
- }
- }
- }
- }
- }
-}
/// let mut v = Vec::<String>::new();
/// let _ = v.iter_mut().filter(|a| a.is_empty());
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub NEEDLESS_BORROWED_REFERENCE,
complexity,
"destructuring a reference and borrowing the inner value"
/// # break;
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub NEEDLESS_CONTINUE,
pedantic,
"`continue` statements that can be replaced by a rearrangement of code"
/// println!("{}", elem);
/// }
/// ```
+ #[clippy::version = "1.53.0"]
pub NEEDLESS_FOR_EACH,
pedantic,
"using `for_each` where a `for` loop would be simpler"
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::path_to_local;
+use clippy_utils::source::snippet_opt;
+use clippy_utils::visitors::{expr_visitor, is_local_used};
+use rustc_errors::Applicability;
+use rustc_hir::intravisit::Visitor;
+use rustc_hir::{Block, Expr, ExprKind, HirId, Local, LocalSource, MatchSource, Node, Pat, PatKind, Stmt, StmtKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::Span;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for late initializations that can be replaced by a `let` statement
+ /// with an initializer.
+ ///
+ /// ### Why is this bad?
+ /// Assigning in the `let` statement is less repetitive.
+ ///
+ /// ### Example
+ /// ```rust
+ /// let a;
+ /// a = 1;
+ ///
+ /// let b;
+ /// match 3 {
+ /// 0 => b = "zero",
+ /// 1 => b = "one",
+ /// _ => b = "many",
+ /// }
+ ///
+ /// let c;
+ /// if true {
+ /// c = 1;
+ /// } else {
+ /// c = -1;
+ /// }
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// let a = 1;
+ ///
+ /// let b = match 3 {
+ /// 0 => "zero",
+ /// 1 => "one",
+ /// _ => "many",
+ /// };
+ ///
+ /// let c = if true {
+ /// 1
+ /// } else {
+ /// -1
+ /// };
+ /// ```
+ #[clippy::version = "1.58.0"]
+ pub NEEDLESS_LATE_INIT,
+ style,
+ "late initializations that can be replaced by a `let` statement with an initializer"
+}
+declare_lint_pass!(NeedlessLateInit => [NEEDLESS_LATE_INIT]);
+
+fn contains_assign_expr<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) -> bool {
+ let mut seen = false;
+ expr_visitor(cx, |expr| {
+ if let ExprKind::Assign(..) = expr.kind {
+ seen = true;
+ }
+
+ !seen
+ })
+ .visit_stmt(stmt);
+
+ seen
+}
+
+#[derive(Debug)]
+struct LocalAssign {
+ lhs_id: HirId,
+ lhs_span: Span,
+ rhs_span: Span,
+ span: Span,
+}
+
+impl LocalAssign {
+ fn from_expr(expr: &Expr<'_>, span: Span) -> Option<Self> {
+ if let ExprKind::Assign(lhs, rhs, _) = expr.kind {
+ if lhs.span.from_expansion() {
+ return None;
+ }
+
+ Some(Self {
+ lhs_id: path_to_local(lhs)?,
+ lhs_span: lhs.span,
+ rhs_span: rhs.span.source_callsite(),
+ span,
+ })
+ } else {
+ None
+ }
+ }
+
+ fn new<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, binding_id: HirId) -> Option<LocalAssign> {
+ let assign = match expr.kind {
+ ExprKind::Block(Block { expr: Some(expr), .. }, _) => Self::from_expr(expr, expr.span),
+ ExprKind::Block(block, _) => {
+ if_chain! {
+ if let Some((last, other_stmts)) = block.stmts.split_last();
+ if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = last.kind;
+
+ let assign = Self::from_expr(expr, last.span)?;
+
+ // avoid visiting if not needed
+ if assign.lhs_id == binding_id;
+ if other_stmts.iter().all(|stmt| !contains_assign_expr(cx, stmt));
+
+ then {
+ Some(assign)
+ } else {
+ None
+ }
+ }
+ },
+ ExprKind::Assign(..) => Self::from_expr(expr, expr.span),
+ _ => None,
+ }?;
+
+ if assign.lhs_id == binding_id {
+ Some(assign)
+ } else {
+ None
+ }
+ }
+}
+
+fn assignment_suggestions<'tcx>(
+ cx: &LateContext<'tcx>,
+ binding_id: HirId,
+ exprs: impl IntoIterator<Item = &'tcx Expr<'tcx>>,
+) -> Option<(Applicability, Vec<(Span, String)>)> {
+ let mut assignments = Vec::new();
+
+ for expr in exprs {
+ let ty = cx.typeck_results().expr_ty(expr);
+
+ if ty.is_never() {
+ continue;
+ }
+ if !ty.is_unit() {
+ return None;
+ }
+
+ let assign = LocalAssign::new(cx, expr, binding_id)?;
+
+ assignments.push(assign);
+ }
+
+ let suggestions = assignments
+ .into_iter()
+ .map(|assignment| Some((assignment.span, snippet_opt(cx, assignment.rhs_span)?)))
+ .collect::<Option<Vec<(Span, String)>>>()?;
+
+ let applicability = if suggestions.len() > 1 {
+ // multiple suggestions don't work with rustfix in multipart_suggest
+ // https://github.com/rust-lang/rustfix/issues/141
+ Applicability::Unspecified
+ } else {
+ Applicability::MachineApplicable
+ };
+ Some((applicability, suggestions))
+}
+
+struct Usage<'tcx> {
+ stmt: &'tcx Stmt<'tcx>,
+ expr: &'tcx Expr<'tcx>,
+ needs_semi: bool,
+}
+
+fn first_usage<'tcx>(
+ cx: &LateContext<'tcx>,
+ binding_id: HirId,
+ local_stmt_id: HirId,
+ block: &'tcx Block<'tcx>,
+) -> Option<Usage<'tcx>> {
+ block
+ .stmts
+ .iter()
+ .skip_while(|stmt| stmt.hir_id != local_stmt_id)
+ .skip(1)
+ .find(|&stmt| is_local_used(cx, stmt, binding_id))
+ .and_then(|stmt| match stmt.kind {
+ StmtKind::Expr(expr) => Some(Usage {
+ stmt,
+ expr,
+ needs_semi: true,
+ }),
+ StmtKind::Semi(expr) => Some(Usage {
+ stmt,
+ expr,
+ needs_semi: false,
+ }),
+ _ => None,
+ })
+}
+
+fn local_snippet_without_semicolon(cx: &LateContext<'_>, local: &Local<'_>) -> Option<String> {
+ let span = local.span.with_hi(match local.ty {
+ // let <pat>: <ty>;
+ // ~~~~~~~~~~~~~~~
+ Some(ty) => ty.span.hi(),
+ // let <pat>;
+ // ~~~~~~~~~
+ None => local.pat.span.hi(),
+ });
+
+ snippet_opt(cx, span)
+}
+
+fn check<'tcx>(
+ cx: &LateContext<'tcx>,
+ local: &'tcx Local<'tcx>,
+ local_stmt: &'tcx Stmt<'tcx>,
+ block: &'tcx Block<'tcx>,
+ binding_id: HirId,
+) -> Option<()> {
+ let usage = first_usage(cx, binding_id, local_stmt.hir_id, block)?;
+ let binding_name = cx.tcx.hir().opt_name(binding_id)?;
+ let let_snippet = local_snippet_without_semicolon(cx, local)?;
+
+ match usage.expr.kind {
+ ExprKind::Assign(..) => {
+ let assign = LocalAssign::new(cx, usage.expr, binding_id)?;
+
+ span_lint_and_then(
+ cx,
+ NEEDLESS_LATE_INIT,
+ local_stmt.span,
+ "unneeded late initalization",
+ |diag| {
+ diag.tool_only_span_suggestion(
+ local_stmt.span,
+ "remove the local",
+ String::new(),
+ Applicability::MachineApplicable,
+ );
+
+ diag.span_suggestion(
+ assign.lhs_span,
+ &format!("declare `{}` here", binding_name),
+ let_snippet,
+ Applicability::MachineApplicable,
+ );
+ },
+ );
+ },
+ ExprKind::If(_, then_expr, Some(else_expr)) => {
+ let (applicability, suggestions) = assignment_suggestions(cx, binding_id, [then_expr, else_expr])?;
+
+ span_lint_and_then(
+ cx,
+ NEEDLESS_LATE_INIT,
+ local_stmt.span,
+ "unneeded late initalization",
+ |diag| {
+ diag.tool_only_span_suggestion(local_stmt.span, "remove the local", String::new(), applicability);
+
+ diag.span_suggestion_verbose(
+ usage.stmt.span.shrink_to_lo(),
+ &format!("declare `{}` here", binding_name),
+ format!("{} = ", let_snippet),
+ applicability,
+ );
+
+ diag.multipart_suggestion("remove the assignments from the branches", suggestions, applicability);
+
+ if usage.needs_semi {
+ diag.span_suggestion(
+ usage.stmt.span.shrink_to_hi(),
+ "add a semicolon after the `if` expression",
+ ";".to_string(),
+ applicability,
+ );
+ }
+ },
+ );
+ },
+ ExprKind::Match(_, arms, MatchSource::Normal) => {
+ let (applicability, suggestions) = assignment_suggestions(cx, binding_id, arms.iter().map(|arm| arm.body))?;
+
+ span_lint_and_then(
+ cx,
+ NEEDLESS_LATE_INIT,
+ local_stmt.span,
+ "unneeded late initalization",
+ |diag| {
+ diag.tool_only_span_suggestion(local_stmt.span, "remove the local", String::new(), applicability);
+
+ diag.span_suggestion_verbose(
+ usage.stmt.span.shrink_to_lo(),
+ &format!("declare `{}` here", binding_name),
+ format!("{} = ", let_snippet),
+ applicability,
+ );
+
+ diag.multipart_suggestion(
+ "remove the assignments from the `match` arms",
+ suggestions,
+ applicability,
+ );
+
+ if usage.needs_semi {
+ diag.span_suggestion(
+ usage.stmt.span.shrink_to_hi(),
+ "add a semicolon after the `match` expression",
+ ";".to_string(),
+ applicability,
+ );
+ }
+ },
+ );
+ },
+ _ => {},
+ };
+
+ Some(())
+}
+
+impl LateLintPass<'tcx> for NeedlessLateInit {
+ fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) {
+ let mut parents = cx.tcx.hir().parent_iter(local.hir_id);
+
+ if_chain! {
+ if let Local {
+ init: None,
+ pat: &Pat {
+ kind: PatKind::Binding(_, binding_id, _, None),
+ ..
+ },
+ source: LocalSource::Normal,
+ ..
+ } = local;
+ if let Some((_, Node::Stmt(local_stmt))) = parents.next();
+ if let Some((_, Node::Block(block))) = parents.next();
+
+ then {
+ check(cx, local, local_stmt, block, binding_id);
+ }
+ }
+ }
+}
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::in_macro;
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::is_type_diagnostic_item;
use rustc_errors::Applicability;
/// let a = Some(&1);
/// let b = a;
/// ```
+ #[clippy::version = "1.57.0"]
pub NEEDLESS_OPTION_AS_DEREF,
complexity,
"no-op use of `deref` or `deref_mut` method to `Option`."
impl<'tcx> LateLintPass<'tcx> for OptionNeedlessDeref {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- if expr.span.from_expansion() || in_macro(expr.span) {
+ if expr.span.from_expansion() {
return;
}
let typeck = cx.typeck_results();
/// assert_eq!(v.len(), 42);
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub NEEDLESS_PASS_BY_VALUE,
pedantic,
"functions taking arguments by value, but not consuming them in its body"
/// tr.and_then(|t| t.magic)
/// }
/// ```
+ #[clippy::version = "1.51.0"]
pub NEEDLESS_QUESTION_MARK,
complexity,
"Suggest `value.inner_option` instead of `Some(value.inner_option?)`. The same goes for `Result<T, E>`."
}
fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
- let inner_expr = if_chain! {
+ if_chain! {
if let ExprKind::Call(path, [arg]) = &expr.kind;
if let ExprKind::Path(ref qpath) = &path.kind;
- if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk);
+ let sugg_remove = if is_lang_ctor(cx, qpath, OptionSome) {
+ "Some()"
+ } else if is_lang_ctor(cx, qpath, ResultOk) {
+ "Ok()"
+ } else {
+ return;
+ };
if let ExprKind::Match(inner_expr_with_q, _, MatchSource::TryDesugar) = &arg.kind;
if let ExprKind::Call(called, [inner_expr]) = &inner_expr_with_q.kind;
if let ExprKind::Path(QPath::LangItem(LangItem::TryTraitBranch, _)) = &called.kind;
let expr_ty = cx.typeck_results().expr_ty(expr);
let inner_ty = cx.typeck_results().expr_ty(inner_expr);
if TyS::same_type(expr_ty, inner_ty);
- then { inner_expr } else { return; }
- };
- span_lint_and_sugg(
- cx,
- NEEDLESS_QUESTION_MARK,
- expr.span,
- "question mark operator is useless here",
- "try",
- format!("{}", snippet(cx, inner_expr.span, r#""...""#)),
- Applicability::MachineApplicable,
- );
+ then {
+ span_lint_and_sugg(
+ cx,
+ NEEDLESS_QUESTION_MARK,
+ expr.span,
+ "question mark operator is useless here",
+ &format!("try removing question mark and `{}`", sugg_remove),
+ format!("{}", snippet(cx, inner_expr.span, r#""...""#)),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
}
/// ..zero_point
/// };
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub NEEDLESS_UPDATE,
complexity,
"using `Foo { ..base }` when there are no missing fields"
/// _ => false,
/// };
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub NEG_CMP_OP_ON_PARTIAL_ORD,
complexity,
"The use of negated comparison operators on partially ordered types may produce confusing code."
/// ```ignore
/// x * -1
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub NEG_MULTIPLY,
style,
"multiplying integers with `-1`"
/// }
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub NEW_WITHOUT_DEFAULT,
style,
"`fn new() -> Self` method without `Default` implementation"
/// ```rust
/// 0;
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub NO_EFFECT,
complexity,
"statements with no effect"
/// ```rust,ignore
/// let _i_serve_no_purpose = 1;
/// ```
+ #[clippy::version = "1.58.0"]
pub NO_EFFECT_UNDERSCORE_BINDING,
pedantic,
"binding to `_` prefixed variable with no side-effect"
/// ```rust,ignore
/// compute_array()[0];
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub UNNECESSARY_OPERATION,
complexity,
"outer expressions with no effect"
},
ExprKind::Call(callee, args) => {
if let ExprKind::Path(ref qpath) = callee.kind {
- let res = cx.qpath_res(qpath, callee.hir_id);
+ if cx.typeck_results().type_dependent_def(expr.hir_id).is_some() {
+ // type-dependent function call like `impl FnOnce for X`
+ return false;
+ }
let def_matched = matches!(
- res,
+ cx.qpath_res(qpath, callee.hir_id),
Res::Def(DefKind::Struct | DefKind::Variant | DefKind::Ctor(..), ..)
);
if def_matched || is_range_literal(expr) {
if !&reduced.iter().any(|e| e.span.from_expansion());
then {
if let ExprKind::Index(..) = &expr.kind {
- let snippet;
- if let (Some(arr), Some(func)) = (snippet_opt(cx, reduced[0].span), snippet_opt(cx, reduced[1].span)) {
- snippet = format!("assert!({}.len() > {});", &arr, &func);
+ let snippet = if let (Some(arr), Some(func)) =
+ (snippet_opt(cx, reduced[0].span), snippet_opt(cx, reduced[1].span))
+ {
+ format!("assert!({}.len() > {});", &arr, &func)
} else {
return;
- }
+ };
span_lint_hir_and_then(
cx,
UNNECESSARY_OPERATION,
},
ExprKind::Call(callee, args) => {
if let ExprKind::Path(ref qpath) = callee.kind {
+ if cx.typeck_results().type_dependent_def(expr.hir_id).is_some() {
+ // type-dependent function call like `impl FnOnce for X`
+ return None;
+ }
let res = cx.qpath_res(qpath, callee.hir_id);
match res {
Res::Def(DefKind::Struct | DefKind::Variant | DefKind::Ctor(..), ..)
/// STATIC_ATOM.store(9, SeqCst);
/// assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub DECLARE_INTERIOR_MUTABLE_CONST,
style,
"declaring `const` with interior mutability"
/// STATIC_ATOM.store(9, SeqCst);
/// assert_eq!(STATIC_ATOM.load(SeqCst), 9); // use a `static` item to refer to the same instance
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub BORROW_INTERIOR_MUTABLE_CONST,
style,
"referencing `const` with interior mutability"
/// let checked_exp = something;
/// let checked_expr = something_else;
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub SIMILAR_NAMES,
pedantic,
"similarly named items and bindings"
/// ```ignore
/// let (a, b, c, d, e, f, g) = (...);
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub MANY_SINGLE_CHAR_NAMES,
pedantic,
"too many single character bindings"
/// let ___1 = 1;
/// let __1___2 = 11;
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub JUST_UNDERSCORES_AND_DIGITS,
style,
"unclear name"
return;
}
- if let ItemKind::Fn(box ast::Fn { ref sig, body: Some(ref blk), .. }) = item.kind {
+ if let ItemKind::Fn(box ast::Fn {
+ ref sig,
+ body: Some(ref blk),
+ ..
+ }) = item.kind
+ {
do_check(self, cx, &item.attrs, &sig.decl, blk);
}
}
return;
}
- if let AssocItemKind::Fn(box ast::Fn { ref sig, body: Some(ref blk), .. }) = item.kind {
+ if let AssocItemKind::Fn(box ast::Fn {
+ ref sig,
+ body: Some(ref blk),
+ ..
+ }) = item.kind
+ {
do_check(self, cx, &item.attrs, &sig.decl, blk);
}
}
/// let mut options = OpenOptions::new();
/// options.mode(0o644);
/// ```
+ #[clippy::version = "1.53.0"]
pub NON_OCTAL_UNIX_PERMISSIONS,
correctness,
"use of non-octal value to set unix file permissions, which will be translated into octal"
use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::is_lint_allowed;
use clippy_utils::source::snippet;
use clippy_utils::ty::{implements_trait, is_copy};
+use clippy_utils::{is_lint_allowed, match_def_path, paths};
use rustc_ast::ImplPolarity;
use rustc_hir::def_id::DefId;
use rustc_hir::{FieldDef, Item, ItemKind, Node};
use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{self, subst::GenericArgKind, Ty};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::sym;
/// ```
/// Use thread-safe types like [`std::sync::Arc`](https://doc.rust-lang.org/std/sync/struct.Arc.html)
/// or specify correct bounds on generic type parameters (`T: Send`).
+ #[clippy::version = "1.57.0"]
pub NON_SEND_FIELDS_IN_SEND_TY,
suspicious,
"there is field that does not implement `Send` in a `Send` struct"
// single `AdtDef` may have multiple `Send` impls due to generic
// parameters, and the lint is much easier to implement in this way.
if_chain! {
+ if !in_external_macro(cx.tcx.sess, item.span);
if let Some(send_trait) = cx.tcx.get_diagnostic_item(sym::Send);
if let ItemKind::Impl(hir_impl) = &item.kind;
if let Some(trait_ref) = &hir_impl.of_trait;
return true;
}
- if is_copy(cx, ty) && !contains_raw_pointer(cx, ty) {
+ if is_copy(cx, ty) && !contains_pointer_like(cx, ty) {
return true;
}
.all(|ty| ty_allowed_with_raw_pointer_heuristic(cx, ty, send_trait)),
ty::Array(ty, _) | ty::Slice(ty) => ty_allowed_with_raw_pointer_heuristic(cx, ty, send_trait),
ty::Adt(_, substs) => {
- if contains_raw_pointer(cx, ty) {
+ if contains_pointer_like(cx, ty) {
// descends only if ADT contains any raw pointers
substs.iter().all(|generic_arg| match generic_arg.unpack() {
GenericArgKind::Type(ty) => ty_allowed_with_raw_pointer_heuristic(cx, ty, send_trait),
}
}
-/// Checks if the type contains any raw pointers in substs (including nested ones).
-fn contains_raw_pointer<'tcx>(cx: &LateContext<'tcx>, target_ty: Ty<'tcx>) -> bool {
+/// Checks if the type contains any pointer-like types in substs (including nested ones)
+fn contains_pointer_like<'tcx>(cx: &LateContext<'tcx>, target_ty: Ty<'tcx>) -> bool {
for ty_node in target_ty.walk(cx.tcx) {
- if_chain! {
- if let GenericArgKind::Type(inner_ty) = ty_node.unpack();
- if let ty::RawPtr(_) = inner_ty.kind();
- then {
- return true;
+ if let GenericArgKind::Type(inner_ty) = ty_node.unpack() {
+ match inner_ty.kind() {
+ ty::RawPtr(_) => {
+ return true;
+ },
+ ty::Adt(adt_def, _) => {
+ if match_def_path(cx, adt_def.did, &paths::PTR_NON_NULL) {
+ return true;
+ }
+ },
+ _ => (),
}
}
}
hash::{Hash, Hasher},
};
-use clippy_utils::{diagnostics::span_lint_and_help, in_macro, is_direct_expn_of, source::snippet_opt};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::source::snippet_opt;
use if_chain::if_chain;
use rustc_ast::ast;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir::def_id::DefId;
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::Span;
+use rustc_span::hygiene::{ExpnKind, MacroKind};
+use rustc_span::{Span, Symbol};
use serde::{de, Deserialize};
declare_clippy_lint! {
/// ```rust
/// vec![1, 2, 3];
/// ```
+ #[clippy::version = "1.55.0"]
pub NONSTANDARD_MACRO_BRACES,
nursery,
"check consistent use of braces in macro"
const BRACES: &[(&str, &str)] = &[("(", ")"), ("{", "}"), ("[", "]")];
/// The (name, (open brace, close brace), source snippet)
-type MacroInfo<'a> = (&'a str, &'a (String, String), String);
+type MacroInfo<'a> = (Symbol, &'a (String, String), String);
#[derive(Clone, Debug, Default)]
pub struct MacroBraces {
fn is_offending_macro<'a>(cx: &EarlyContext<'_>, span: Span, mac_braces: &'a MacroBraces) -> Option<MacroInfo<'a>> {
let unnested_or_local = || {
- let nested = in_macro(span.ctxt().outer_expn_data().call_site);
- !nested
+ !span.ctxt().outer_expn_data().call_site.from_expansion()
|| span
.macro_backtrace()
.last()
.map_or(false, |e| e.macro_def_id.map_or(false, DefId::is_local))
};
if_chain! {
- // Make sure we are only one level deep otherwise there are to many FP's
- if in_macro(span);
- if let Some((name, braces)) = find_matching_macro(span, &mac_braces.macro_braces);
+ if let ExpnKind::Macro(MacroKind::Bang, mac_name) = span.ctxt().outer_expn_data().kind;
+ let name = &*mac_name.as_str();
+ if let Some(braces) = mac_braces.macro_braces.get(name);
if let Some(snip) = snippet_opt(cx, span.ctxt().outer_expn_data().call_site);
// we must check only invocation sites
// https://github.com/rust-lang/rust-clippy/issues/7422
if snip.starts_with(&format!("{}!", name));
if unnested_or_local();
// make formatting consistent
- let c = snip.replace(" ", "");
+ let c = snip.replace(' ', "");
if !c.starts_with(&format!("{}!{}", name, braces.0));
if !mac_braces.done.contains(&span.ctxt().outer_expn_data().call_site);
then {
- Some((name, braces, snip))
+ Some((mac_name, braces, snip))
} else {
None
}
}
}
-fn emit_help(cx: &EarlyContext<'_>, snip: String, braces: &(String, String), name: &str, span: Span) {
+fn emit_help(cx: &EarlyContext<'_>, snip: String, braces: &(String, String), name: Symbol, span: Span) {
let with_space = &format!("! {}", braces.0);
let without_space = &format!("!{}", braces.0);
let mut help = snip;
);
}
-fn find_matching_macro(
- span: Span,
- braces: &FxHashMap<String, (String, String)>,
-) -> Option<(&String, &(String, String))> {
- braces
- .iter()
- .find(|(macro_name, _)| is_direct_expn_of(span, macro_name).is_some())
-}
-
fn macro_braces(conf: FxHashSet<MacroMatcher>) -> FxHashMap<String, (String, String)> {
let mut braces = vec![
macro_matcher!(
--- /dev/null
+use clippy_utils::diagnostics::span_lint_and_then;
+use rustc_ast::ast::{Expr, ExprKind};
+use rustc_ast::token::{Lit, LitKind};
+use rustc_errors::Applicability;
+use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_middle::lint::in_external_macro;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::Span;
+use std::fmt::Write;
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for `\0` escapes in string and byte literals that look like octal
+ /// character escapes in C.
+ ///
+ /// ### Why is this bad?
+ ///
+ /// C and other languages support octal character escapes in strings, where
+ /// a backslash is followed by up to three octal digits. For example, `\033`
+ /// stands for the ASCII character 27 (ESC). Rust does not support this
+ /// notation, but has the escape code `\0` which stands for a null
+ /// byte/character, and any following digits do not form part of the escape
+ /// sequence. Therefore, `\033` is not a compiler error but the result may
+ /// be surprising.
+ ///
+ /// ### Known problems
+ /// The actual meaning can be the intended one. `\x00` can be used in these
+ /// cases to be unambigious.
+ ///
+ /// The lint does not trigger for format strings in `print!()`, `write!()`
+ /// and friends since the string is already preprocessed when Clippy lints
+ /// can see it.
+ ///
+ /// # Example
+ /// ```rust
+ /// // Bad
+ /// let one = "\033[1m Bold? \033[0m"; // \033 intended as escape
+ /// let two = "\033\0"; // \033 intended as null-3-3
+ ///
+ /// // Good
+ /// let one = "\x1b[1mWill this be bold?\x1b[0m";
+ /// let two = "\x0033\x00";
+ /// ```
+ #[clippy::version = "1.58.0"]
+ pub OCTAL_ESCAPES,
+ suspicious,
+ "string escape sequences looking like octal characters"
+}
+
+declare_lint_pass!(OctalEscapes => [OCTAL_ESCAPES]);
+
+impl EarlyLintPass for OctalEscapes {
+ fn check_expr(&mut self, cx: &EarlyContext<'tcx>, expr: &Expr) {
+ if in_external_macro(cx.sess, expr.span) {
+ return;
+ }
+
+ if let ExprKind::Lit(lit) = &expr.kind {
+ if matches!(lit.token.kind, LitKind::Str) {
+ check_lit(cx, &lit.token, lit.span, true);
+ } else if matches!(lit.token.kind, LitKind::ByteStr) {
+ check_lit(cx, &lit.token, lit.span, false);
+ }
+ }
+ }
+}
+
+fn check_lit(cx: &EarlyContext<'tcx>, lit: &Lit, span: Span, is_string: bool) {
+ let contents = lit.symbol.as_str();
+ let mut iter = contents.char_indices().peekable();
+ let mut found = vec![];
+
+ // go through the string, looking for \0[0-7][0-7]?
+ while let Some((from, ch)) = iter.next() {
+ if ch == '\\' {
+ if let Some((_, '0')) = iter.next() {
+ // collect up to two further octal digits
+ if let Some((mut to, '0'..='7')) = iter.next() {
+ if let Some((_, '0'..='7')) = iter.peek() {
+ to += 1;
+ }
+ found.push((from, to + 1));
+ }
+ }
+ }
+ }
+
+ if found.is_empty() {
+ return;
+ }
+
+ // construct two suggestion strings, one with \x escapes with octal meaning
+ // as in C, and one with \x00 for null bytes.
+ let mut suggest_1 = if is_string { "\"" } else { "b\"" }.to_string();
+ let mut suggest_2 = suggest_1.clone();
+ let mut index = 0;
+ for (from, to) in found {
+ suggest_1.push_str(&contents[index..from]);
+ suggest_2.push_str(&contents[index..from]);
+
+ // construct a replacement escape
+ // the maximum value is \077, or \x3f, so u8 is sufficient here
+ if let Ok(n) = u8::from_str_radix(&contents[from + 1..to], 8) {
+ write!(&mut suggest_1, "\\x{:02x}", n).unwrap();
+ }
+
+ // append the null byte as \x00 and the following digits literally
+ suggest_2.push_str("\\x00");
+ suggest_2.push_str(&contents[from + 2..to]);
+
+ index = to;
+ }
+ suggest_1.push_str(&contents[index..]);
+ suggest_1.push('"');
+ suggest_2.push_str(&contents[index..]);
+ suggest_2.push('"');
+
+ span_lint_and_then(
+ cx,
+ OCTAL_ESCAPES,
+ span,
+ &format!(
+ "octal-looking escape in {} literal",
+ if is_string { "string" } else { "byte string" }
+ ),
+ |diag| {
+ diag.help(&format!(
+ "octal escapes are not supported, `\\0` is always a null {}",
+ if is_string { "character" } else { "byte" }
+ ));
+ // suggestion 1: equivalent hex escape
+ diag.span_suggestion(
+ span,
+ "if an octal escape was intended, use the hexadecimal representation instead",
+ suggest_1,
+ Applicability::MaybeIncorrect,
+ );
+ // suggestion 2: unambiguous null byte
+ diag.span_suggestion(
+ span,
+ &format!(
+ "if the null {} is intended, disambiguate using",
+ if is_string { "character" } else { "byte" }
+ ),
+ suggest_2,
+ Applicability::MaybeIncorrect,
+ );
+ },
+ );
+}
///
/// OpenOptions::new().read(true).truncate(true);
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub NONSENSICAL_OPEN_OPTIONS,
correctness,
"nonsensical combination of options for opening a file"
/// ```rust,no_run
/// let _ = env!("HOME");
/// ```
+ #[clippy::version = "1.43.0"]
pub OPTION_ENV_UNWRAP,
correctness,
"using `option_env!(...).unwrap()` to get environment variable"
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{
- can_move_expr_to_closure, eager_or_lazy, in_constant, in_macro, is_else_clause, is_lang_ctor, peel_hir_expr_while,
+ can_move_expr_to_closure, eager_or_lazy, in_constant, is_else_clause, is_lang_ctor, peel_hir_expr_while,
CaptureKind,
};
use if_chain::if_chain;
/// y*y
/// }, |foo| foo);
/// ```
+ #[clippy::version = "1.47.0"]
pub OPTION_IF_LET_ELSE,
nursery,
"reimplementation of Option::map_or"
fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: bool, as_mut: bool) -> String {
format!(
"{}{}",
- Sugg::hir(cx, cond_expr, "..").maybe_par(),
+ Sugg::hir_with_macro_callsite(cx, cond_expr, "..").maybe_par(),
if as_mut {
".as_mut()"
} else if as_ref {
/// this construct is found, or None if this construct is not found.
fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<OptionIfLetElseOccurence> {
if_chain! {
- if !in_macro(expr.span); // Don't lint macros, because it behaves weirdly
+ if !expr.span.from_expansion(); // Don't lint macros, because it behaves weirdly
if !in_constant(cx, expr.hir_id);
if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: Some(if_else) })
= higher::IfLet::hir(cx, expr);
let capture_mut = if bind_annotation == &BindingAnnotation::Mutable { "mut " } else { "" };
let some_body = extract_body_from_expr(if_then)?;
let none_body = extract_body_from_expr(if_else)?;
- let method_sugg = if eager_or_lazy::is_eagerness_candidate(cx, none_body) {
- "map_or"
- } else {
- "map_or_else"
- };
+ let method_sugg = if eager_or_lazy::switch_to_eager_eval(cx, none_body) { "map_or" } else { "map_or_else" };
let capture_name = id.name.to_ident_string();
let (as_ref, as_mut) = match &let_expr.kind {
ExprKind::AddrOf(_, Mutability::Not, _) => (true, false),
Some(OptionIfLetElseOccurence {
option: format_option_in_sugg(cx, cond_expr, as_ref, as_mut),
method_sugg: method_sugg.to_string(),
- some_expr: format!("|{}{}| {}", capture_mut, capture_name, Sugg::hir(cx, some_body, "..")),
- none_expr: format!("{}{}", if method_sugg == "map_or" { "" } else { "|| " }, Sugg::hir(cx, none_body, "..")),
+ some_expr: format!("|{}{}| {}", capture_mut, capture_name, Sugg::hir_with_macro_callsite(cx, some_body, "..")),
+ none_expr: format!("{}{}", if method_sugg == "map_or" { "" } else { "|| " }, Sugg::hir_with_macro_callsite(cx, none_body, "..")),
})
} else {
None
/// # let b = 2;
/// a + b < a;
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub OVERFLOW_CHECK_CONDITIONAL,
complexity,
"overflow checks inspired by C which are likely to panic"
/// Err(String::from("error"))
/// }
/// ```
+ #[clippy::version = "1.48.0"]
pub PANIC_IN_RESULT_FN,
restriction,
"functions of type `Result<..>` that contain `panic!()`, `todo!()`, `unreachable()`, `unimplemented()` or assertion"
/// ```no_run
/// panic!("even with a good reason");
/// ```
+ #[clippy::version = "1.40.0"]
pub PANIC,
restriction,
"usage of the `panic!` macro"
/// ```no_run
/// unimplemented!();
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub UNIMPLEMENTED,
restriction,
"`unimplemented!` should not be present in production code"
/// ```no_run
/// todo!();
/// ```
+ #[clippy::version = "1.40.0"]
pub TODO,
restriction,
"`todo!` should not be present in production code"
/// ```no_run
/// unreachable!();
/// ```
+ #[clippy::version = "1.40.0"]
pub UNREACHABLE,
restriction,
"usage of the `unreachable!` macro"
/// fn ne(&self, other: &Foo) -> bool { !(self == other) }
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub PARTIALEQ_NE_IMPL,
complexity,
"re-implementing `PartialEq::ne`"
/// // Better
/// fn foo(v: u32) {}
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub TRIVIALLY_COPY_PASS_BY_REF,
pedantic,
"functions taking small copyable arguments by reference"
/// // Good
/// fn foo(v: &TooLarge) {}
/// ```
+ #[clippy::version = "1.49.0"]
pub LARGE_TYPES_PASSED_BY_VALUE,
pedantic,
"functions taking large arguments by value"
/// x.push("bar");
/// assert_eq!(x, PathBuf::from("/foo/bar"));
/// ```
+ #[clippy::version = "1.36.0"]
pub PATH_BUF_PUSH_OVERWRITE,
nursery,
"calling `push` with file system root on `PathBuf` can overwrite it"
/// *a += b;
/// }
/// ```
+ #[clippy::version = "1.47.0"]
pub PATTERN_TYPE_MISMATCH,
restriction,
"type of pattern does not match the expression type"
/// ### Example
/// * `1 << 2 + 3` equals 32, while `(1 << 2) + 3` equals 7
/// * `-1i32.abs()` equals -1, while `(-1i32).abs()` equals 1
+ #[clippy::version = "pre 1.29.0"]
pub PRECEDENCE,
complexity,
"operations where precedence may be unclear"
/// // Good
/// fn foo(&[u32]) { .. }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub PTR_ARG,
style,
"fn arguments of the type `&Vec<...>` or `&String`, suggesting to use `&[...]` or `&str` instead, respectively"
/// ..
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub CMP_NULL,
style,
"comparing a pointer to a null pointer, suggesting to use `.is_null()` instead"
/// ```ignore
/// fn foo(&Foo) -> &mut Bar { .. }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub MUT_FROM_REF,
correctness,
"fns that create mutable refs from immutable ref args"
/// // Good
/// unsafe { std::slice::from_raw_parts(NonNull::dangling().as_ptr(), 0); }
/// ```
+ #[clippy::version = "1.53.0"]
pub INVALID_NULL_PTR_USAGE,
correctness,
"invalid usage of a null pointer, suggesting `NonNull::dangling()` instead"
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::in_macro;
use clippy_utils::source::snippet_opt;
use if_chain::if_chain;
use rustc_errors::Applicability;
///
/// assert!(std::ptr::eq(a, b));
/// ```
+ #[clippy::version = "1.49.0"]
pub PTR_EQ,
style,
"use `std::ptr::eq` when comparing raw pointers"
impl LateLintPass<'_> for PtrEq {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- if in_macro(expr.span) {
+ if expr.span.from_expansion() {
return;
}
/// ptr.add(offset);
/// }
/// ```
+ #[clippy::version = "1.30.0"]
pub PTR_OFFSET_WITH_CAST,
complexity,
"unneeded pointer offset cast"
/// ```ignore
/// option?;
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub QUESTION_MARK,
style,
"checks for expressions that could be replaced by the question mark operator"
/// # let x = vec![1];
/// x.iter().enumerate();
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub RANGE_ZIP_WITH_LEN,
complexity,
"zipping iterator with a range when `enumerate()` would do"
/// ```rust,ignore
/// for x..=y { .. }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub RANGE_PLUS_ONE,
pedantic,
"`x..(y+1)` reads better as `x..=y`"
/// ```rust,ignore
/// for x..y { .. }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub RANGE_MINUS_ONE,
pedantic,
"`x..=(y-1)` reads better as `x..y`"
/// let sub = &arr[1..3];
/// }
/// ```
+ #[clippy::version = "1.45.0"]
pub REVERSED_EMPTY_RANGES,
correctness,
"reversing the limits of range expressions, resulting in empty ranges"
///# let x = 6;
/// assert!((3..8).contains(&x));
/// ```
+ #[clippy::version = "1.49.0"]
pub MANUAL_RANGE_CONTAINS,
style,
"manually reimplementing {`Range`, `RangeInclusive`}`::contains`"
///
/// Path::new("/a/b").join("c").to_path_buf();
/// ```
+ #[clippy::version = "1.32.0"]
pub REDUNDANT_CLONE,
perf,
"`clone()` of an owned value that is going to be dropped immediately"
/// // Good
/// let a = 42
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub REDUNDANT_CLOSURE_CALL,
complexity,
"throwaway closures called in the expression they are defined"
/// print!("Moving on...");
/// }
/// ```
+ #[clippy::version = "1.50.0"]
pub REDUNDANT_ELSE,
pedantic,
"`else` branch that can be removed without changing semantics"
/// ```ignore
/// let foo = Foo { bar };
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub REDUNDANT_FIELD_NAMES,
style,
"checks for fields in struct literals where shorthands could be used"
/// pub fn internal_fn() { }
/// }
/// ```
+ #[clippy::version = "1.44.0"]
pub REDUNDANT_PUB_CRATE,
nursery,
"Using `pub(crate)` visibility on items that are not crate visible due to the visibility of the module that contains them."
use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::get_parent_expr;
use clippy_utils::source::snippet_with_context;
use clippy_utils::ty::is_type_lang_item;
-use clippy_utils::{get_parent_expr, in_macro};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability};
/// x
/// }
/// ```
+ #[clippy::version = "1.51.0"]
pub REDUNDANT_SLICING,
complexity,
"redundant slicing of the whole range of a type"
impl LateLintPass<'_> for RedundantSlicing {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- if in_macro(expr.span) {
+ if expr.span.from_expansion() {
return;
}
/// const FOO: &[(&str, &str, fn(&Bar) -> bool)] = &[...]
/// static FOO: &[(&str, &str, fn(&Bar) -> bool)] = &[...]
/// ```
+ #[clippy::version = "1.37.0"]
pub REDUNDANT_STATIC_LIFETIMES,
style,
"Using explicit `'static` lifetime for constants or statics when elision rules would allow omitting them."
/// ```rust,ignore
/// let x: Option<&u32> = Some(&0u32);
/// ```
+ #[clippy::version = "1.49.0"]
pub REF_OPTION_REF,
pedantic,
"use `Option<&T>` instead of `&Option<&T>`"
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::in_macro;
use clippy_utils::source::{snippet_opt, snippet_with_applicability};
use clippy_utils::sugg::Sugg;
use if_chain::if_chain;
/// let a = f(b);
/// let c = d;
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub DEREF_ADDROF,
complexity,
"use of `*&` or `*&mut` in an expression"
if_chain! {
if let ExprKind::Unary(UnOp::Deref, ref deref_target) = e.kind;
if let ExprKind::AddrOf(_, ref mutability, ref addrof_target) = without_parens(deref_target).kind;
- if !in_macro(addrof_target.span);
+ if !addrof_target.span.from_expansion();
then {
let mut applicability = Applicability::MachineApplicable;
let sugg = if e.span.from_expansion() {
/// # let point = Point(30, 20);
/// let x = point.0;
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub REF_IN_DEREF,
complexity,
"Use of reference in auto dereference expression."
/// ```ignore
/// Regex::new("|")
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub INVALID_REGEX,
correctness,
"invalid regular expressions"
/// ```ignore
/// Regex::new("^foobar")
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub TRIVIAL_REGEX,
nursery,
"trivial regular expressions"
use clippy_utils::consts::{constant_context, Constant};
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::in_macro;
use clippy_utils::source::snippet;
use clippy_utils::ty::is_type_diagnostic_item;
use if_chain::if_chain;
/// let x = String::from("hello world").clone();
/// }
/// ```
+ #[clippy::version = "1.47.0"]
pub REPEAT_ONCE,
complexity,
"using `.repeat(1)` instead of `String.clone()`, `str.to_string()` or `slice.to_vec()` "
if let ExprKind::MethodCall(path, _, [receiver, count], _) = &expr.kind;
if path.ident.name == sym!(repeat);
if constant_context(cx, cx.typeck_results()).expr(count) == Some(Constant::Int(1));
- if !in_macro(receiver.span);
+ if !receiver.span.from_expansion();
then {
let ty = cx.typeck_results().expr_ty(receiver).peel_refs();
if ty.is_str() {
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::snippet_opt;
-use clippy_utils::{fn_def_id, in_macro, path_to_local_id};
+use clippy_utils::{fn_def_id, path_to_local_id};
use if_chain::if_chain;
use rustc_ast::ast::Attribute;
use rustc_errors::Applicability;
/// String::new()
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub LET_AND_RETURN,
style,
"creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block"
/// x
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub NEEDLESS_RETURN,
style,
"using a return statement like `return expr;` where an expression would suffice"
if !last_statement_borrows(cx, initexpr);
if !in_external_macro(cx.sess(), initexpr.span);
if !in_external_macro(cx.sess(), retexpr.span);
- if !in_external_macro(cx.sess(), local.span);
- if !in_macro(local.span);
+ if !local.span.from_expansion();
then {
span_lint_and_then(
cx,
declare_clippy_lint! {
/// ### What it does
- /// It lints if a struct has two method with same time:
+ /// It lints if a struct has two methods with the same name:
/// one from a trait, another not from trait.
///
/// ### Why is this bad?
/// fn foo(&self) {}
/// }
/// ```
+ #[clippy::version = "1.57.0"]
pub SAME_NAME_METHOD,
restriction,
"two method with same name"
cx,
SAME_NAME_METHOD,
*impl_span,
- "method's name is same to an existing method in a trait",
+ "method's name is the same as an existing method in a trait",
|diag| {
diag.span_note(
trait_method_span,
cx,
SAME_NAME_METHOD,
impl_span,
- "method's name is same to an existing method in a trait",
+ "method's name is the same as an existing method in a trait",
|diag| {
// TODO should we `span_note` on every trait?
// iterate on trait_spans?
/// a.y = a.y;
/// }
/// ```
+ #[clippy::version = "1.48.0"]
pub SELF_ASSIGNMENT,
correctness,
"explicit self-assignment"
/// }
/// }
/// ```
+ #[clippy::version = "1.55.0"]
pub SELF_NAMED_CONSTRUCTORS,
style,
"method should not have the same name as the type it is implemented for"
let self_id = cx.tcx.hir().local_def_id_to_hir_id(self_local_did);
if let Some(Node::Item(x)) = cx.tcx.hir().find(self_id);
let type_name = x.ident.name.as_str().to_lowercase();
- if impl_item.ident.name.as_str() == type_name || impl_item.ident.name.as_str().replace("_", "") == type_name;
+ if impl_item.ident.name.as_str() == type_name || impl_item.ident.name.as_str().replace('_', "") == type_name;
then {
span_lint(
/// println!("Hello world");
/// }
/// ```
+ #[clippy::version = "1.52.0"]
pub SEMICOLON_IF_NOTHING_RETURNED,
pedantic,
"add a semicolon if nothing is returned"
let t_expr = cx.typeck_results().expr_ty(expr);
if t_expr.is_unit();
if let snippet = snippet_with_macro_callsite(cx, expr.span, "}");
- if !snippet.ends_with('}');
+ if !snippet.ends_with('}') && !snippet.ends_with(';');
if cx.sess().source_map().is_multiline(block.span);
then {
// filter out the desugared `for` loop
/// ### Example
/// Implementing `Visitor::visit_string` but not
/// `Visitor::visit_str`.
+ #[clippy::version = "pre 1.29.0"]
pub SERDE_API_MISUSE,
correctness,
"various things that will negatively affect your serde experience"
/// // Good
/// let y = &x; // use different variable name
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub SHADOW_SAME,
restriction,
"rebinding a name to itself, e.g., `let mut x = &mut x`"
/// let x = 2;
/// let y = x + 1;
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub SHADOW_REUSE,
restriction,
"rebinding a name to an expression that re-uses the original value, e.g., `let x = x + 1`"
/// // Good
/// let w = z; // use different variable name
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub SHADOW_UNRELATED,
restriction,
"rebinding a name without even using the original value"
PatKind::Binding(_, hir_id, ident, _) => (hir_id, ident),
_ => return,
};
+
+ if pat.span.desugaring_kind().is_some() {
+ return;
+ }
+
if ident.span.from_expansion() || ident.span.is_dummy() {
return;
}
- let HirId { owner, local_id } = id;
+ let HirId { owner, local_id } = id;
// get (or insert) the list of items for this owner and symbol
let data = self.bindings.last_mut().unwrap();
let items_with_name = data.entry(ident.name).or_default();
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
-use clippy_utils::in_macro;
use rustc_ast::{ptr::P, Crate, Item, ItemKind, MacroDef, ModKind, UseTreeKind, VisibilityKind};
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass};
/// regex::Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap();
/// }
/// ```
+ #[clippy::version = "1.43.0"]
pub SINGLE_COMPONENT_PATH_IMPORTS,
style,
"imports with single component path are redundant"
single_use_usages: &mut Vec<(Symbol, Span, bool)>,
macros: &mut Vec<Symbol>,
) {
- if in_macro(item.span) || item.vis.kind.is_pub() {
+ if item.span.from_expansion() || item.vis.kind.is_pub() {
return;
}
/// let mut y = [2u8; SIZE];
/// unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::<u8>() * SIZE) };
/// ```
+ #[clippy::version = "1.50.0"]
pub SIZE_OF_IN_ELEMENT_COUNT,
correctness,
"using `size_of::<T>` or `size_of_val::<T>` where a count of elements of `T` is expected"
/// let mut vec1 = vec![0; len];
/// let mut vec2 = vec![0; len];
/// ```
+ #[clippy::version = "1.32.0"]
pub SLOW_VECTOR_INITIALIZATION,
perf,
"slow vector initialization"
/// let mut vec = vec![2, 1, 3];
/// vec.sort_unstable();
/// ```
+ #[clippy::version = "1.47.0"]
pub STABLE_SORT_PRIMITIVE,
perf,
"use of sort() when sort_unstable() is equivalent"
/// x += ", World";
/// x.push_str(", World");
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub STRING_ADD_ASSIGN,
pedantic,
"using `x = x + ..` where x is a `String` instead of `push_str()`"
/// let x = "Hello".to_owned();
/// x + ", World";
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub STRING_ADD,
restriction,
"using `x + ..` where x is a `String` instead of `push_str()`"
/// // Good
/// let bs = b"a byte string";
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub STRING_LIT_AS_BYTES,
nursery,
"calling `as_bytes` on a string literal instead of using a byte string literal"
/// ```rust,should_panic
/// &"Ölkanne"[1..];
/// ```
+ #[clippy::version = "1.58.0"]
pub STRING_SLICE,
restriction,
"slicing a string"
/// ```rust
/// let _ = &"Hello World!"[6..11];
/// ```
+ #[clippy::version = "1.50.0"]
pub STRING_FROM_UTF8_AS_BYTES,
complexity,
"casting string slices to byte slices and back"
/// // example code which does not raise clippy warning
/// let _ = "str".to_owned();
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub STR_TO_STRING,
restriction,
"using `to_string()` on a `&str`, which should be `to_owned()`"
/// let msg = String::from("Hello World");
/// let _ = msg.clone();
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub STRING_TO_STRING,
restriction,
"using `to_string()` on a `String`, which should be `clone()`"
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::in_macro;
-use clippy_utils::paths;
-use clippy_utils::source::snippet_with_macro_callsite;
-use clippy_utils::ty::{is_type_diagnostic_item, is_type_ref_to_diagnostic_item};
+use clippy_utils::source::snippet_with_context;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::visitors::is_expr_unsafe;
+use clippy_utils::{get_parent_node, match_libc_symbol};
use if_chain::if_chain;
use rustc_errors::Applicability;
-use rustc_hir as hir;
+use rustc_hir::{Block, BlockCheckMode, Expr, ExprKind, Node, UnsafeSource};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::symbol::{sym, Symbol};
+use rustc_span::symbol::sym;
declare_clippy_lint! {
/// ### What it does
/// let cstring = CString::new("foo").expect("CString::new failed");
/// let len = cstring.as_bytes().len();
/// ```
+ #[clippy::version = "1.55.0"]
pub STRLEN_ON_C_STRINGS,
complexity,
"using `libc::strlen` on a `CString` or `CStr` value, while `as_bytes().len()` or `to_bytes().len()` respectively can be used instead"
declare_lint_pass!(StrlenOnCStrings => [STRLEN_ON_C_STRINGS]);
impl LateLintPass<'tcx> for StrlenOnCStrings {
- fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
- if in_macro(expr.span) {
- return;
- }
-
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if_chain! {
- if let hir::ExprKind::Call(func, [recv]) = expr.kind;
- if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = func.kind;
-
- if (&paths::LIBC_STRLEN).iter().map(|x| Symbol::intern(x)).eq(
- path.segments.iter().map(|seg| seg.ident.name));
- if let hir::ExprKind::MethodCall(path, _, args, _) = recv.kind;
- if args.len() == 1;
- if !args.iter().any(|e| e.span.from_expansion());
+ if !expr.span.from_expansion();
+ if let ExprKind::Call(func, [recv]) = expr.kind;
+ 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 !recv.span.from_expansion();
if path.ident.name == sym::as_ptr;
then {
- let cstring = &args[0];
- let ty = cx.typeck_results().expr_ty(cstring);
- let val_name = snippet_with_macro_callsite(cx, cstring.span, "..");
- let sugg = if is_type_diagnostic_item(cx, ty, sym::cstring_type){
- format!("{}.as_bytes().len()", val_name)
- } else if is_type_ref_to_diagnostic_item(cx, ty, sym::CStr){
- format!("{}.to_bytes().len()", val_name)
+ let ctxt = expr.span.ctxt();
+ let span = match get_parent_node(cx.tcx, expr.hir_id) {
+ Some(Node::Block(&Block {
+ rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided), span, ..
+ }))
+ if span.ctxt() == ctxt && !is_expr_unsafe(cx, self_arg) => {
+ span
+ }
+ _ => expr.span,
+ };
+
+ let ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
+ let mut app = Applicability::MachineApplicable;
+ let val_name = snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0;
+ let method_name = if is_type_diagnostic_item(cx, ty, sym::cstring_type) {
+ "as_bytes"
+ } else if is_type_diagnostic_item(cx, ty, sym::CStr) {
+ "to_bytes"
} else {
return;
};
span_lint_and_sugg(
cx,
STRLEN_ON_C_STRINGS,
- expr.span,
+ span,
"using `libc::strlen` on a `CString` or `CStr` value",
- "try this (you might also need to get rid of `unsafe` block in some cases):",
- sugg,
- Applicability::Unspecified // Sometimes unnecessary `unsafe` block
+ "try this",
+ format!("{}.{}().len()", val_name, method_name),
+ app,
);
}
}
/// }
/// }
/// ```
+ #[clippy::version = "1.50.0"]
pub SUSPICIOUS_OPERATION_GROUPINGS,
nursery,
"groupings of binary operations that look suspiciously like typos"
/// }
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub SUSPICIOUS_ARITHMETIC_IMPL,
suspicious,
"suspicious use of operators in impl of arithmetic trait"
/// }
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub SUSPICIOUS_OP_ASSIGN_IMPL,
suspicious,
"suspicious use of operators in impl of OpAssign trait"
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{can_mut_borrow_both, differing_macro_contexts, eq_expr_value};
+use clippy_utils::{can_mut_borrow_both, differing_macro_contexts, eq_expr_value, std_or_core};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind};
/// let mut b = 2;
/// std::mem::swap(&mut a, &mut b);
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub MANUAL_SWAP,
complexity,
"manual swap of two variables"
/// # let mut b = 2;
/// std::mem::swap(&mut a, &mut b);
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub ALMOST_SWAPPED,
correctness,
"`foo = bar; bar = foo` sequence"
let first = Sugg::hir_with_applicability(cx, e1, "..", &mut applicability);
let second = Sugg::hir_with_applicability(cx, e2, "..", &mut applicability);
+ let Some(sugg) = std_or_core(cx) else { return };
+
span_lint_and_then(
cx,
MANUAL_SWAP,
diag.span_suggestion(
span,
"try",
- format!("std::mem::swap({}, {})", first.mut_addr(), second.mut_addr()),
+ format!("{}::mem::swap({}, {})", sugg, first.mut_addr(), second.mut_addr()),
applicability,
);
if !is_xor_based {
- diag.note("or maybe you should use `std::mem::replace`?");
+ diag.note(&format!("or maybe you should use `{}::mem::replace`?", sugg));
}
},
);
};
let span = first.span.to(second.span);
+ let Some(sugg) = std_or_core(cx) else { return };
span_lint_and_then(cx,
- ALMOST_SWAPPED,
- span,
- &format!("this looks like you are trying to swap{}", what),
- |diag| {
- if !what.is_empty() {
- diag.span_suggestion(
- span,
- "try",
- format!(
- "std::mem::swap({}, {})",
- lhs,
- rhs,
- ),
- Applicability::MaybeIncorrect,
- );
- diag.note("or maybe you should use `std::mem::replace`?");
- }
- });
+ ALMOST_SWAPPED,
+ span,
+ &format!("this looks like you are trying to swap{}", what),
+ |diag| {
+ if !what.is_empty() {
+ diag.span_suggestion(
+ span,
+ "try",
+ format!(
+ "{}::mem::swap({}, {})",
+ sugg,
+ lhs,
+ rhs,
+ ),
+ Applicability::MaybeIncorrect,
+ );
+ diag.note(
+ &format!("or maybe you should use `{}::mem::replace`?", sugg)
+ );
+ }
+ });
}
}
}
/// second_string: String,
///}
/// ```
+ #[clippy::version = "1.41.0"]
pub TABS_IN_DOC_COMMENTS,
style,
"using tabs in doc comments is not recommended"
/// ```rust
/// (0, 0).0 = 1
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub TEMPORARY_ASSIGNMENT,
complexity,
"assignments to temporaries"
/// # let radix = 10;
/// let is_digit = c.is_digit(radix);
/// ```
+ #[clippy::version = "1.41.0"]
pub TO_DIGIT_IS_SOME,
style,
"`char.is_digit()` is clearer"
/// }
/// }
/// ```
+ #[clippy::version = "1.48.0"]
pub TO_STRING_IN_DISPLAY,
correctness,
"`to_string` method used while implementing `Display` trait"
/// last: [u32; 0],
/// }
/// ```
+ #[clippy::version = "1.58.0"]
pub TRAILING_EMPTY_ARRAY,
nursery,
"struct with a trailing zero-sized array but without `#[repr(C)]` or another `repr` attribute"
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::source::{snippet, snippet_with_applicability};
-use clippy_utils::{in_macro, SpanlessHash};
+use clippy_utils::SpanlessHash;
use if_chain::if_chain;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::unhash::UnhashMap;
/// ```rust
/// pub fn foo<T>(t: T) where T: Copy + Clone {}
/// ```
+ #[clippy::version = "1.38.0"]
pub TYPE_REPETITION_IN_BOUNDS,
pedantic,
"Types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`"
/// ```rust
/// fn func<T>(arg: T) where T: Clone + Default {}
/// ```
+ #[clippy::version = "1.47.0"]
pub TRAIT_DUPLICATION_IN_BOUNDS,
pedantic,
"Check if the same trait bounds are specified twice during a function declaration"
impl TraitBounds {
fn check_type_repetition(self, cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
- if in_macro(gen.span) {
+ if gen.span.from_expansion() {
return;
}
let hash = |ty| -> u64 {
if_chain! {
if let WherePredicate::BoundPredicate(ref p) = bound;
if p.bounds.len() as u64 <= self.max_trait_bounds;
- if !in_macro(p.span);
+ if !p.span.from_expansion();
let h = hash(p.bounded_ty);
if let Some(ref v) = map.insert(h, p.bounds.iter().collect::<Vec<_>>());
}
fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
- if in_macro(gen.span) || gen.params.is_empty() || gen.where_clause.predicates.is_empty() {
+ if gen.span.from_expansion() || gen.params.is_empty() || gen.where_clause.predicates.is_empty() {
return;
}
for predicate in gen.where_clause.predicates {
if_chain! {
if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate;
- if !in_macro(bound_predicate.span);
+ if !bound_predicate.span.from_expansion();
if let TyKind::Path(QPath::Resolved(_, Path { segments, .. })) = bound_predicate.bounded_ty.kind;
if let Some(segment) = segments.first();
if let Some(trait_resolutions_direct) = map.get(&segment.ident);
/// ```ignore
/// let ptr: *const T = core::intrinsics::transmute('x')
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub WRONG_TRANSMUTE,
correctness,
"transmutes that are confusing at best, undefined behaviour at worst and always useless"
/// ```rust,ignore
/// core::intrinsics::transmute(t); // where the result type is the same as `t`'s
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub USELESS_TRANSMUTE,
nursery,
"transmutes that have the same to and from types or could be a cast/coercion"
/// # let p: *const [i32] = &[];
/// p as *const [u16];
/// ```
+ #[clippy::version = "1.47.0"]
pub TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
complexity,
"transmutes that could be a pointer cast"
/// core::intrinsics::transmute(t) // where the result type is the same as
/// // `*t` or `&t`'s
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub CROSSPOINTER_TRANSMUTE,
complexity,
"transmutes that have to or from types that are a pointer to the other"
/// // can be written:
/// let _: &T = &*p;
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub TRANSMUTE_PTR_TO_REF,
complexity,
"transmutes from a pointer to a reference type"
/// // should be:
/// let _ = std::char::from_u32(x).unwrap();
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub TRANSMUTE_INT_TO_CHAR,
complexity,
"transmutes from an integer to a `char`"
/// // should be:
/// let _ = std::str::from_utf8(b).unwrap();
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub TRANSMUTE_BYTES_TO_STR,
complexity,
"transmutes from a `&[u8]` to a `&str`"
/// // should be:
/// let _: bool = x != 0;
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub TRANSMUTE_INT_TO_BOOL,
complexity,
"transmutes from an integer to a `bool`"
/// // should be:
/// let _: f32 = f32::from_bits(1_u32);
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub TRANSMUTE_INT_TO_FLOAT,
complexity,
"transmutes from an integer to a float"
/// // should be:
/// let _: u32 = 1f32.to_bits();
/// ```
+ #[clippy::version = "1.41.0"]
pub TRANSMUTE_FLOAT_TO_INT,
complexity,
"transmutes from a float to an integer"
/// // should be
/// let x: [u8; 8] = 0i64.to_ne_bytes();
/// ```
+ #[clippy::version = "1.58.0"]
pub TRANSMUTE_NUM_TO_BYTES,
complexity,
"transmutes from a number to an array of `u8`"
/// let _ = ptr as *const f32;
/// let _ = unsafe{ &*(&1u32 as *const u32 as *const f32) };
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub TRANSMUTE_PTR_TO_PTR,
pedantic,
"transmutes from a pointer to a pointer / a reference to a reference"
/// ```rust
/// vec![2_u16].into_iter().map(u32::from).collect::<Vec<_>>();
/// ```
+ #[clippy::version = "1.40.0"]
pub UNSOUND_COLLECTION_TRANSMUTE,
correctness,
"transmute between collections of layout-incompatible types"
/// ```rust
/// let null_ref: &u64 = unsafe { std::mem::transmute(0 as *const u64) };
/// ```
+ #[clippy::version = "1.35.0"]
pub TRANSMUTING_NULL,
correctness,
"transmutes from a null pointer to a reference, which is undefined behavior"
use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::{snippet, snippet_with_macro_callsite};
+use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{differing_macro_contexts, get_parent_expr, in_macro, is_lang_ctor, match_def_path, paths};
+use clippy_utils::{get_parent_expr, is_lang_ctor, match_def_path, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::LangItem::ResultErr;
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{self, Ty};
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::sym;
+use rustc_span::{hygiene, sym};
declare_clippy_lint! {
/// ### What it does
/// Ok(0)
/// }
/// ```
+ #[clippy::version = "1.38.0"]
pub TRY_ERR,
style,
"return errors explicitly rather than hiding them behind a `?`"
};
let expr_err_ty = cx.typeck_results().expr_ty(err_arg);
- let differing_contexts = differing_macro_contexts(expr.span, err_arg.span);
-
- let origin_snippet = if in_macro(expr.span) && in_macro(err_arg.span) && differing_contexts {
- snippet(cx, err_arg.span.ctxt().outer_expn_data().call_site, "_")
- } else if err_arg.span.from_expansion() && !in_macro(expr.span) {
- snippet_with_macro_callsite(cx, err_arg.span, "_")
- } else {
- snippet(cx, err_arg.span, "_")
- };
+ let span = hygiene::walk_chain(err_arg.span, try_arg.span.ctxt());
+ let mut applicability = Applicability::MachineApplicable;
+ let origin_snippet = snippet_with_applicability(cx, span, "_", &mut applicability);
let ret_prefix = if get_parent_expr(cx, expr).map_or(false, |e| matches!(e.kind, ExprKind::Ret(_))) {
"" // already returns
} else {
"returning an `Err(_)` with the `?` operator",
"try this",
suggestion,
- Applicability::MachineApplicable
+ applicability,
);
}
}
/// values: Vec<Foo>,
/// }
/// ```
+ #[clippy::version = "1.57.0"]
pub BOX_COLLECTION,
perf,
"usage of `Box<Vec<T>>`, vector elements are already on the heap"
/// values: Vec<i32>,
/// }
/// ```
+ #[clippy::version = "1.33.0"]
pub VEC_BOX,
complexity,
"usage of `Vec<Box<T>>` where T: Sized, vector elements are already on the heap"
/// Contents::None
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub OPTION_OPTION,
pedantic,
"usage of `Option<Option<T>>`"
/// # use std::collections::LinkedList;
/// let x: LinkedList<usize> = LinkedList::new();
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub LINKEDLIST,
pedantic,
"usage of LinkedList, usually a vector is faster, or a more specialized data structure like a `VecDeque`"
/// ```rust,ignore
/// fn foo(bar: &T) { ... }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub BORROWED_BOX,
complexity,
"a borrow of a boxed type"
/// ```rust
/// fn foo(bar: &usize) {}
/// ```
+ #[clippy::version = "1.44.0"]
pub REDUNDANT_ALLOCATION,
perf,
"redundant allocation"
/// ```rust,ignore
/// fn foo(interned: Rc<str>) { ... }
/// ```
+ #[clippy::version = "1.48.0"]
pub RC_BUFFER,
restriction,
"shared ownership of a buffer type"
/// inner: Rc<Vec<Vec<Box<(u32, u32, u32, u32)>>>>,
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub TYPE_COMPLEXITY,
complexity,
"usage of very complex types that might be better factored into `type` definitions"
/// use std::cell::RefCell
/// fn foo(interned: Rc<RefCell<i32>>) { ... }
/// ```
+ #[clippy::version = "1.55.0"]
pub RC_MUTEX,
restriction,
"usage of `Rc<Mutex<T>>`"
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
+use clippy_utils::is_lint_allowed;
use clippy_utils::source::{indent_of, reindent_multiline, snippet};
-use clippy_utils::{in_macro, is_lint_allowed};
use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
use rustc_hir::{Block, BlockCheckMode, Expr, ExprKind, HirId, Local, UnsafeSource};
/// // Safety: references are guaranteed to be non-null.
/// let ptr = unsafe { NonNull::new_unchecked(a) };
/// ```
+ #[clippy::version = "1.58.0"]
pub UNDOCUMENTED_UNSAFE_BLOCKS,
restriction,
"creating an unsafe block without explaining why it is safe"
let enclosing_scope_span = map.opt_span(enclosing_hir_id)?;
- let between_span = if in_macro(block_span) {
+ let between_span = if block_span.from_expansion() {
self.macro_expansion = true;
- enclosing_scope_span.with_hi(block_span.hi())
+ enclosing_scope_span.with_hi(block_span.hi()).source_callsite()
} else {
self.macro_expansion = false;
- enclosing_scope_span.to(block_span)
+ enclosing_scope_span.to(block_span).source_callsite()
};
let file_name = source_map.span_to_filename(between_span);
let lex_end = (between_span.hi().0 - source_file.start_pos.0) as usize;
let src_str = source_file.src.as_ref()?[lex_start..lex_end].to_string();
+ let source_start_pos = source_file.start_pos.0 as usize + lex_start;
+
let mut pos = 0;
let mut comment = false;
if comment {
// Get the line number of the "comment" (really wherever the trailing whitespace ended)
let comment_line_num = source_file
- .lookup_file_pos_with_col_display(BytePos((lex_start + pos).try_into().unwrap()))
+ .lookup_file_pos(BytePos((source_start_pos + pos).try_into().unwrap()))
.0;
// Find the block/local's line number
let block_line_num = tcx.sess.source_map().lookup_char_pos(block_span.lo()).line;
/// std::mem::ManuallyDrop::drop(&mut std::mem::ManuallyDrop::new(S));
/// }
/// ```
+ #[clippy::version = "1.49.0"]
pub UNDROPPED_MANUALLY_DROPS,
correctness,
"use of safe `std::mem::drop` function to drop a std::mem::ManuallyDrop, which will not drop the inner value"
/// ### Example
/// You don't see it, but there may be a zero-width space or soft hyphen
/// somewhere in this text.
+ #[clippy::version = "1.49.0"]
pub INVISIBLE_CHARACTERS,
correctness,
"using an invisible character in a string literal, which is confusing"
declare_clippy_lint! {
/// ### What it does
- /// Checks for non-ASCII characters in string literals.
+ /// Checks for non-ASCII characters in string and char literals.
///
/// ### Why is this bad?
/// Yeah, we know, the 90's called and wanted their charset
/// ```rust
/// let x = String::from("\u{20ac}");
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub NON_ASCII_LITERAL,
restriction,
"using any literal non-ASCII chars in a string literal instead of using the `\\u` escape"
/// ### Example
/// You may not see it, but "à"" and "à"" aren't the same string. The
/// former when escaped is actually `"a\u{300}"` while the latter is `"\u{e0}"`.
+ #[clippy::version = "pre 1.29.0"]
pub UNICODE_NOT_NFC,
pedantic,
"using a Unicode literal not in NFC normal form (see [Unicode tr15](http://www.unicode.org/reports/tr15/) for further information)"
impl LateLintPass<'_> for Unicode {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
if let ExprKind::Lit(ref lit) = expr.kind {
- if let LitKind::Str(_, _) = lit.node {
+ if let LitKind::Str(_, _) | LitKind::Char(_) = lit.node {
check_str(cx, lit.span, expr.hir_id);
}
}
"invisible character detected",
"consider replacing the string with",
string
- .replace("\u{200B}", "\\u{200B}")
- .replace("\u{ad}", "\\u{AD}")
- .replace("\u{2060}", "\\u{2060}"),
+ .replace('\u{200B}', "\\u{200B}")
+ .replace('\u{ad}', "\\u{AD}")
+ .replace('\u{2060}', "\\u{2060}"),
Applicability::MachineApplicable,
);
}
/// // perform initialization with `remaining`
/// vec.set_len(...); // Safe to call `set_len()` on initialized part
/// ```
+ #[clippy::version = "1.58.0"]
pub UNINIT_VEC,
correctness,
"Vec with uninitialized data"
/// WithValue(x) => x.hash(&mut state),
/// }
/// ```
+ #[clippy::version = "1.58.0"]
pub UNIT_HASH,
correctness,
"hashing a unit value, which does nothing"
/// let mut twins = vec!((1, 1), (2, 2));
/// twins.sort_by_key(|x| { x.1; });
/// ```
+ #[clippy::version = "1.47.0"]
pub UNIT_RETURN_EXPECTING_ORD,
correctness,
"fn arguments of type Fn(...) -> Ord returning the unit type ()."
/// 1;
/// };
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub LET_UNIT_VALUE,
pedantic,
"creating a `let` binding to a value of unit type, which usually can't be used afterwards"
/// assert_eq!({ foo(); }, { bar(); });
/// ```
/// will always succeed
+ #[clippy::version = "pre 1.29.0"]
pub UNIT_CMP,
correctness,
"comparing unit values"
/// baz(a);
/// })
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub UNIT_ARG,
complexity,
"passing unit to a function"
/// // ...
/// }
/// ```
+ #[clippy::version = "1.44.0"]
pub FN_ADDRESS_COMPARISONS,
correctness,
"comparison with an address of a function item"
/// ...
/// }
/// ```
+ #[clippy::version = "1.44.0"]
pub VTABLE_ADDRESS_COMPARISONS,
correctness,
"comparison with an address of a trait vtable"
/// ```rust
/// use std::io;
/// ```
+ #[clippy::version = "1.53.0"]
pub UNNECESSARY_SELF_IMPORTS,
restriction,
"imports ending in `::{self}`, which can be omitted"
/// # let mut vec: Vec<A> = Vec::new();
/// vec.sort_by_key(|a| a.foo());
/// ```
+ #[clippy::version = "1.46.0"]
pub UNNECESSARY_SORT_BY,
complexity,
"Use of `Vec::sort_by` when `Vec::sort_by_key` or `Vec::sort` would be clearer"
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet;
-use clippy_utils::{contains_return, in_macro, is_lang_ctor, return_ty, visitors::find_all_ret_expressions};
+use clippy_utils::{contains_return, is_lang_ctor, return_ty, visitors::find_all_ret_expressions};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::intravisit::FnKind;
/// }
/// }
/// ```
+ #[clippy::version = "1.50.0"]
pub UNNECESSARY_WRAPS,
pedantic,
"functions that only return `Ok` or `Some`"
let mut suggs = Vec::new();
let can_sugg = find_all_ret_expressions(cx, &body.value, |ret_expr| {
if_chain! {
- if !in_macro(ret_expr.span);
+ if !ret_expr.span.from_expansion();
// Check if a function call.
if let ExprKind::Call(func, [arg]) = ret_expr.kind;
// Check if OPTION_SOME or RESULT_OK, depending on return type.
/// if let Some(0 | 2) = Some(0) {}
/// }
/// ```
+ #[clippy::version = "1.46.0"]
pub UNNESTED_OR_PATTERNS,
pedantic,
"unnested or-patterns, e.g., `Foo(Bar) | Foo(Baz) instead of `Foo(Bar | Baz)`"
/// extern crate crossbeam;
/// use crossbeam::{spawn_unsafe as spawn};
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub UNSAFE_REMOVED_FROM_NAME,
style,
"`unsafe` removed from API names on import"
/// }
/// let number_future = async { get_random_number_improved() };
/// ```
+ #[clippy::version = "1.54.0"]
pub UNUSED_ASYNC,
pedantic,
"finds async functions with no await statements"
/// Ok(())
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub UNUSED_IO_AMOUNT,
correctness,
"unused written/read amount"
/// fn method() {}
/// }
/// ```
+ #[clippy::version = "1.40.0"]
pub UNUSED_SELF,
pedantic,
"methods that contain a `self` argument but don't use it"
/// ```rust
/// fn return_unit() {}
/// ```
+ #[clippy::version = "1.31.0"]
pub UNUSED_UNIT,
style,
"needless unit expression"
/// do_something_with(value)
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub UNNECESSARY_UNWRAP,
complexity,
"checks for calls of `unwrap[_err]()` that cannot fail"
/// ```
///
/// This code will always panic. The if condition should probably be inverted.
+ #[clippy::version = "pre 1.29.0"]
pub PANICKING_UNWRAP,
correctness,
"checks for calls of `unwrap[_err]()` that will always fail"
} else {
// find `unwrap[_err]()` calls:
if_chain! {
- if let ExprKind::MethodCall(method_name, _, args, _) = expr.kind;
- if let Some(id) = path_to_local(&args[0]);
+ 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);
if let Some(unwrappable) = self.unwrappables.iter()
/// Ok(())
/// }
/// ```
+ #[clippy::version = "1.48.0"]
pub UNWRAP_IN_RESULT,
restriction,
"functions of type `Result<..>` or `Option`<...> that contain `expect()` or `unwrap()`"
/// ```rust
/// struct HttpResponse;
/// ```
+ #[clippy::version = "1.51.0"]
pub UPPER_CASE_ACRONYMS,
style,
"capitalized acronyms are against the naming convention"
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::ty::same_type_and_consts;
-use clippy_utils::{in_macro, meets_msrv, msrvs};
+use clippy_utils::{meets_msrv, msrvs};
use if_chain::if_chain;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
/// }
/// }
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub USE_SELF,
nursery,
"unnecessary structure name repetition whereas `Self` is applicable"
fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) {
if_chain! {
- if !in_macro(hir_ty.span);
+ if !hir_ty.span.from_expansion();
if meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS);
if let Some(&StackItem::Check {
impl_id,
};
if same_type_and_consts(ty, cx.tcx.type_of(impl_id));
let hir = cx.tcx.hir();
- let id = hir.get_parent_node(hir_ty.hir_id);
- if !hir.opt_span(id).map_or(false, in_macro);
+ // prevents false positive on `#[derive(serde::Deserialize)]`
+ if !hir.span(hir.get_parent_node(hir_ty.hir_id)).in_derive_expansion();
then {
span_lint(cx, hir_ty.span);
}
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
if_chain! {
- if !in_macro(expr.span);
+ if !expr.span.from_expansion();
if meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS);
if let Some(&StackItem::Check { impl_id, .. }) = self.stack.last();
if cx.typeck_results().expr_ty(expr) == cx.tcx.type_of(impl_id);
/// // Good
/// let s: String = format!("hello");
/// ```
+ #[clippy::version = "1.45.0"]
pub USELESS_CONVERSION,
complexity,
"calls to `Into`, `TryInto`, `From`, `TryFrom`, or `IntoIter` which perform useless conversions to the same type"
//! A group of attributes that can be attached to Rust code in order
//! to generate a clippy lint detecting said code automatically.
-use clippy_utils::get_attr;
+use clippy_utils::{get_attr, higher};
use rustc_ast::ast::{LitFloatType, LitKind};
-use rustc_ast::walk_list;
+use rustc_ast::LitIntType;
use rustc_data_structures::fx::FxHashMap;
use rustc_hir as hir;
-use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
-use rustc_hir::{Block, Expr, ExprKind, Pat, PatKind, QPath, Stmt, StmtKind, TyKind};
+use rustc_hir::{ExprKind, FnRetTy, HirId, Lit, PatKind, QPath, StmtKind, TyKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
-use rustc_middle::hir::map::Map;
use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::symbol::{Ident, Symbol};
+use std::fmt::{Display, Formatter, Write as _};
declare_clippy_lint! {
/// ### What it does
declare_lint_pass!(Author => [LINT_AUTHOR]);
+/// Writes a line of output with indentation added
+macro_rules! out {
+ ($($t:tt)*) => {
+ println!(" {}", format_args!($($t)*))
+ };
+}
+
+/// The variables passed in are replaced with `&Binding`s where the `value` field is set
+/// to the original value of the variable. The `name` field is set to the name of the variable
+/// (using `stringify!`) and is adjusted to avoid duplicate names.
+/// Note that the `Binding` may be printed directly to output the `name`.
+macro_rules! bind {
+ ($self:ident $(, $name:ident)+) => {
+ $(let $name = & $self.bind(stringify!($name), $name);)+
+ };
+}
+
+/// Transforms the given `Option<T>` varibles into `OptionPat<Binding<T>>`.
+/// This displays as `Some($name)` or `None` when printed. The name of the inner binding
+/// is set to the name of the variable passed to the macro.
+macro_rules! opt_bind {
+ ($self:ident $(, $name:ident)+) => {
+ $(let $name = OptionPat::new($name.map(|o| $self.bind(stringify!($name), o)));)+
+ };
+}
+
+/// Creates a `Binding` that accesses the field of an existing `Binding`
+macro_rules! field {
+ ($binding:ident.$field:ident) => {
+ &Binding {
+ name: $binding.name.to_string() + stringify!(.$field),
+ value: $binding.value.$field,
+ }
+ };
+}
+
fn prelude() {
println!("if_chain! {{");
}
impl<'tcx> LateLintPass<'tcx> for Author {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
- if !has_attr(cx, item.hir_id()) {
- return;
- }
- prelude();
- PrintVisitor::new("item").visit_item(item);
- done();
+ check_item(cx, item.hir_id());
}
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
- if !has_attr(cx, item.hir_id()) {
- return;
- }
- prelude();
- PrintVisitor::new("item").visit_impl_item(item);
- done();
+ check_item(cx, item.hir_id());
}
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
- if !has_attr(cx, item.hir_id()) {
- return;
- }
- prelude();
- PrintVisitor::new("item").visit_trait_item(item);
- done();
+ check_item(cx, item.hir_id());
}
- fn check_variant(&mut self, cx: &LateContext<'tcx>, var: &'tcx hir::Variant<'_>) {
- if !has_attr(cx, var.id) {
- return;
- }
- prelude();
- let parent_hir_id = cx.tcx.hir().get_parent_node(var.id);
- PrintVisitor::new("var").visit_variant(var, &hir::Generics::empty(), parent_hir_id);
- done();
- }
-
- fn check_field_def(&mut self, cx: &LateContext<'tcx>, field: &'tcx hir::FieldDef<'_>) {
- if !has_attr(cx, field.hir_id) {
- return;
- }
- prelude();
- PrintVisitor::new("field").visit_field_def(field);
- done();
+ fn check_arm(&mut self, cx: &LateContext<'tcx>, arm: &'tcx hir::Arm<'_>) {
+ check_node(cx, arm.hir_id, |v| {
+ v.arm(&v.bind("arm", arm));
+ });
}
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
- if !has_attr(cx, expr.hir_id) {
- return;
- }
- prelude();
- PrintVisitor::new("expr").visit_expr(expr);
- done();
- }
-
- fn check_arm(&mut self, cx: &LateContext<'tcx>, arm: &'tcx hir::Arm<'_>) {
- if !has_attr(cx, arm.hir_id) {
- return;
- }
- prelude();
- PrintVisitor::new("arm").visit_arm(arm);
- done();
+ check_node(cx, expr.hir_id, |v| {
+ v.expr(&v.bind("expr", expr));
+ });
}
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx hir::Stmt<'_>) {
- if !has_attr(cx, stmt.hir_id) {
- return;
- }
match stmt.kind {
StmtKind::Expr(e) | StmtKind::Semi(e) if has_attr(cx, e.hir_id) => return,
_ => {},
}
+ check_node(cx, stmt.hir_id, |v| {
+ v.stmt(&v.bind("stmt", stmt));
+ });
+ }
+}
+
+fn check_item(cx: &LateContext<'_>, hir_id: HirId) {
+ let hir = cx.tcx.hir();
+ if let Some(body_id) = hir.maybe_body_owned_by(hir_id) {
+ check_node(cx, hir_id, |v| {
+ v.expr(&v.bind("expr", &hir.body(body_id).value));
+ });
+ }
+}
+
+fn check_node(cx: &LateContext<'_>, hir_id: HirId, f: impl Fn(&PrintVisitor<'_, '_>)) {
+ if has_attr(cx, hir_id) {
prelude();
- PrintVisitor::new("stmt").visit_stmt(stmt);
+ f(&PrintVisitor::new(cx));
done();
}
+}
- fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ForeignItem<'_>) {
- if !has_attr(cx, item.hir_id()) {
- return;
+struct Binding<T> {
+ name: String,
+ value: T,
+}
+
+impl<T> Display for Binding<T> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ f.write_str(&self.name)
+ }
+}
+
+struct OptionPat<T> {
+ pub opt: Option<T>,
+}
+
+impl<T> OptionPat<T> {
+ fn new(opt: Option<T>) -> Self {
+ Self { opt }
+ }
+
+ fn if_some(&self, f: impl Fn(&T)) {
+ if let Some(t) = &self.opt {
+ f(t);
+ }
+ }
+}
+
+impl<T: Display> Display for OptionPat<T> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ match &self.opt {
+ None => f.write_str("None"),
+ Some(node) => write!(f, "Some({node})"),
}
- prelude();
- PrintVisitor::new("item").visit_foreign_item(item);
- done();
}
}
-impl PrintVisitor {
- #[must_use]
- fn new(s: &'static str) -> Self {
+struct PrintVisitor<'a, 'tcx> {
+ cx: &'a LateContext<'tcx>,
+ /// Fields are the current index that needs to be appended to pattern
+ /// binding names
+ ids: std::cell::Cell<FxHashMap<&'static str, u32>>,
+}
+
+#[allow(clippy::unused_self)]
+impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
+ fn new(cx: &'a LateContext<'tcx>) -> Self {
Self {
- ids: FxHashMap::default(),
- current: s.to_owned(),
+ cx,
+ ids: std::cell::Cell::default(),
}
}
- fn next(&mut self, s: &'static str) -> String {
- use std::collections::hash_map::Entry::{Occupied, Vacant};
- match self.ids.entry(s) {
- // already there: start numbering from `1`
- Occupied(mut occ) => {
- let val = occ.get_mut();
- *val += 1;
- format!("{}{}", s, *val)
- },
- // not there: insert and return name as given
- Vacant(vac) => {
- vac.insert(0);
- s.to_owned()
+ fn next(&self, s: &'static str) -> String {
+ let mut ids = self.ids.take();
+ let out = match *ids.entry(s).and_modify(|n| *n += 1).or_default() {
+ // first usage of the name, use it as is
+ 0 => s.to_string(),
+ // append a number starting with 1
+ n => format!("{s}{n}"),
+ };
+ self.ids.set(ids);
+ out
+ }
+
+ fn bind<T>(&self, name: &'static str, value: T) -> Binding<T> {
+ let name = self.next(name);
+ Binding { name, value }
+ }
+
+ fn option<T: Copy>(&self, option: &Binding<Option<T>>, name: &'static str, f: impl Fn(&Binding<T>)) {
+ match option.value {
+ None => out!("if {option}.is_none();"),
+ Some(value) => {
+ let value = &self.bind(name, value);
+ out!("if let Some({value}) = {option};");
+ f(value);
},
}
}
- fn print_qpath(&mut self, path: &QPath<'_>) {
- if let QPath::LangItem(lang_item, _) = *path {
- println!(
- " if matches!({}, QPath::LangItem(LangItem::{:?}, _));",
- self.current, lang_item,
- );
+ fn slice<T>(&self, slice: &Binding<&[T]>, f: impl Fn(&Binding<&T>)) {
+ if slice.value.is_empty() {
+ out!("if {slice}.is_empty();");
} else {
- print!(" if match_qpath({}, &[", self.current);
- print_path(path, &mut true);
- println!("]);");
+ out!("if {slice}.len() == {};", slice.value.len());
+ for (i, value) in slice.value.iter().enumerate() {
+ let name = format!("{slice}[{i}]");
+ f(&Binding { name, value });
+ }
}
}
-}
-struct PrintVisitor {
- /// Fields are the current index that needs to be appended to pattern
- /// binding names
- ids: FxHashMap<&'static str, usize>,
- /// the name that needs to be destructured
- current: String,
-}
+ fn destination(&self, destination: &Binding<hir::Destination>) {
+ self.option(field!(destination.label), "label", |label| {
+ self.ident(field!(label.ident));
+ });
+ }
+
+ fn ident(&self, ident: &Binding<Ident>) {
+ out!("if {ident}.as_str() == {:?};", ident.value.as_str());
+ }
+
+ fn symbol(&self, symbol: &Binding<Symbol>) {
+ out!("if {symbol}.as_str() == {:?};", symbol.value.as_str());
+ }
-impl<'tcx> Visitor<'tcx> for PrintVisitor {
- type Map = Map<'tcx>;
+ fn qpath(&self, qpath: &Binding<&QPath<'_>>) {
+ if let QPath::LangItem(lang_item, _) = *qpath.value {
+ out!("if matches!({qpath}, QPath::LangItem(LangItem::{lang_item:?}, _));");
+ } else {
+ out!("if match_qpath({qpath}, &[{}]);", path_to_string(qpath.value));
+ }
+ }
+
+ fn lit(&self, lit: &Binding<&Lit>) {
+ let kind = |kind| out!("if let LitKind::{kind} = {lit}.node;");
+ macro_rules! kind {
+ ($($t:tt)*) => (kind(format_args!($($t)*)));
+ }
+
+ match lit.value.node {
+ LitKind::Bool(val) => kind!("Bool({val:?})"),
+ LitKind::Char(c) => kind!("Char({c:?})"),
+ LitKind::Err(val) => kind!("Err({val})"),
+ LitKind::Byte(b) => kind!("Byte({b})"),
+ LitKind::Int(i, suffix) => {
+ let int_ty = match suffix {
+ LitIntType::Signed(int_ty) => format!("LitIntType::Signed(IntTy::{int_ty:?})"),
+ LitIntType::Unsigned(uint_ty) => format!("LitIntType::Unsigned(UintTy::{uint_ty:?})"),
+ LitIntType::Unsuffixed => String::from("LitIntType::Unsuffixed"),
+ };
+ kind!("Int({i}, {int_ty})");
+ },
+ LitKind::Float(_, suffix) => {
+ let float_ty = match suffix {
+ LitFloatType::Suffixed(suffix_ty) => format!("LitFloatType::Suffixed(FloatTy::{suffix_ty:?})"),
+ LitFloatType::Unsuffixed => String::from("LitFloatType::Unsuffixed"),
+ };
+ kind!("Float(_, {float_ty})");
+ },
+ LitKind::ByteStr(ref vec) => {
+ bind!(self, vec);
+ kind!("ByteStr(ref {vec})");
+ out!("if let [{:?}] = **{vec};", vec.value);
+ },
+ LitKind::Str(s, _) => {
+ bind!(self, s);
+ kind!("Str({s}, _)");
+ self.symbol(s);
+ },
+ }
+ }
+
+ fn arm(&self, arm: &Binding<&hir::Arm<'_>>) {
+ self.pat(field!(arm.pat));
+ match arm.value.guard {
+ None => out!("if {arm}.guard.is_none();"),
+ Some(hir::Guard::If(expr)) => {
+ bind!(self, expr);
+ out!("if let Some(Guard::If({expr})) = {arm}.guard;");
+ self.expr(expr);
+ },
+ Some(hir::Guard::IfLet(pat, expr)) => {
+ bind!(self, pat, expr);
+ out!("if let Some(Guard::IfLet({pat}, {expr}) = {arm}.guard;");
+ self.pat(pat);
+ self.expr(expr);
+ },
+ }
+ self.expr(field!(arm.body));
+ }
#[allow(clippy::too_many_lines)]
- fn visit_expr(&mut self, expr: &Expr<'_>) {
- print!(" if let ExprKind::");
- let current = format!("{}.kind", self.current);
- match expr.kind {
+ fn expr(&self, expr: &Binding<&hir::Expr<'_>>) {
+ if let Some(higher::While { condition, body }) = higher::While::hir(expr.value) {
+ bind!(self, condition, body);
+ out!(
+ "if let Some(higher::While {{ condition: {condition}, body: {body} }}) \
+ = higher::While::hir({expr});"
+ );
+ self.expr(condition);
+ self.expr(body);
+ return;
+ }
+
+ if let Some(higher::WhileLet {
+ let_pat,
+ let_expr,
+ if_then,
+ }) = higher::WhileLet::hir(expr.value)
+ {
+ bind!(self, let_pat, let_expr, if_then);
+ out!(
+ "if let Some(higher::WhileLet {{ let_pat: {let_pat}, let_expr: {let_expr}, if_then: {if_then} }}) \
+ = higher::WhileLet::hir({expr});"
+ );
+ self.pat(let_pat);
+ self.expr(let_expr);
+ self.expr(if_then);
+ return;
+ }
+
+ if let Some(higher::ForLoop { pat, arg, body, .. }) = higher::ForLoop::hir(expr.value) {
+ bind!(self, pat, arg, body);
+ out!(
+ "if let Some(higher::ForLoop {{ pat: {pat}, arg: {arg}, body: {body}, .. }}) \
+ = higher::ForLoop::hir({expr});"
+ );
+ self.pat(pat);
+ self.expr(arg);
+ self.expr(body);
+ return;
+ }
+
+ let kind = |kind| out!("if let ExprKind::{kind} = {expr}.kind;");
+ macro_rules! kind {
+ ($($t:tt)*) => (kind(format_args!($($t)*)));
+ }
+
+ match expr.value.kind {
ExprKind::Let(pat, expr, _) => {
- let let_pat = self.next("pat");
- let let_expr = self.next("expr");
- println!(" Let(ref {}, ref {}, _) = {};", let_pat, let_expr, current);
- self.current = let_expr;
- self.visit_expr(expr);
- self.current = let_pat;
- self.visit_pat(pat);
+ bind!(self, pat, expr);
+ kind!("Let({pat}, {expr}, _)");
+ self.pat(pat);
+ self.expr(expr);
},
ExprKind::Box(inner) => {
- let inner_pat = self.next("inner");
- println!("Box(ref {}) = {};", inner_pat, current);
- self.current = inner_pat;
- self.visit_expr(inner);
+ bind!(self, inner);
+ kind!("Box({inner})");
+ self.expr(inner);
},
ExprKind::Array(elements) => {
- let elements_pat = self.next("elements");
- println!("Array(ref {}) = {};", elements_pat, current);
- println!(" if {}.len() == {};", elements_pat, elements.len());
- for (i, element) in elements.iter().enumerate() {
- self.current = format!("{}[{}]", elements_pat, i);
- self.visit_expr(element);
- }
+ bind!(self, elements);
+ kind!("Array({elements})");
+ self.slice(elements, |e| self.expr(e));
},
ExprKind::Call(func, args) => {
- let func_pat = self.next("func");
- let args_pat = self.next("args");
- println!("Call(ref {}, ref {}) = {};", func_pat, args_pat, current);
- self.current = func_pat;
- self.visit_expr(func);
- println!(" if {}.len() == {};", args_pat, args.len());
- for (i, arg) in args.iter().enumerate() {
- self.current = format!("{}[{}]", args_pat, i);
- self.visit_expr(arg);
- }
+ bind!(self, func, args);
+ kind!("Call({func}, {args})");
+ self.expr(func);
+ self.slice(args, |e| self.expr(e));
},
- ExprKind::MethodCall(_method_name, ref _generics, _args, ref _fn_span) => {
- println!(
- "MethodCall(ref method_name, ref generics, ref args, ref fn_span) = {};",
- current
- );
- println!(" // unimplemented: `ExprKind::MethodCall` is not further destructured at the moment");
+ ExprKind::MethodCall(method_name, _, args, _) => {
+ bind!(self, method_name, args);
+ kind!("MethodCall({method_name}, _, {args}, _)");
+ self.ident(field!(method_name.ident));
+ self.slice(args, |e| self.expr(e));
},
ExprKind::Tup(elements) => {
- let elements_pat = self.next("elements");
- println!("Tup(ref {}) = {};", elements_pat, current);
- println!(" if {}.len() == {};", elements_pat, elements.len());
- for (i, element) in elements.iter().enumerate() {
- self.current = format!("{}[{}]", elements_pat, i);
- self.visit_expr(element);
- }
- },
- ExprKind::Binary(ref op, left, right) => {
- let op_pat = self.next("op");
- let left_pat = self.next("left");
- let right_pat = self.next("right");
- println!(
- "Binary(ref {}, ref {}, ref {}) = {};",
- op_pat, left_pat, right_pat, current
- );
- println!(" if BinOpKind::{:?} == {}.node;", op.node, op_pat);
- self.current = left_pat;
- self.visit_expr(left);
- self.current = right_pat;
- self.visit_expr(right);
- },
- ExprKind::Unary(ref op, inner) => {
- let inner_pat = self.next("inner");
- println!("Unary(UnOp::{:?}, ref {}) = {};", op, inner_pat, current);
- self.current = inner_pat;
- self.visit_expr(inner);
+ bind!(self, elements);
+ kind!("Tup({elements})");
+ self.slice(elements, |e| self.expr(e));
+ },
+ ExprKind::Binary(op, left, right) => {
+ bind!(self, op, left, right);
+ kind!("Binary({op}, {left}, {right})");
+ out!("if BinOpKind::{:?} == {op}.node;", op.value.node);
+ self.expr(left);
+ self.expr(right);
+ },
+ ExprKind::Unary(op, inner) => {
+ bind!(self, inner);
+ kind!("Unary(UnOp::{op:?}, {inner})");
+ self.expr(inner);
},
ExprKind::Lit(ref lit) => {
- let lit_pat = self.next("lit");
- println!("Lit(ref {}) = {};", lit_pat, current);
- match lit.node {
- LitKind::Bool(val) => println!(" if let LitKind::Bool({:?}) = {}.node;", val, lit_pat),
- LitKind::Char(c) => println!(" if let LitKind::Char({:?}) = {}.node;", c, lit_pat),
- LitKind::Err(val) => println!(" if let LitKind::Err({}) = {}.node;", val, lit_pat),
- LitKind::Byte(b) => println!(" if let LitKind::Byte({}) = {}.node;", b, lit_pat),
- // FIXME: also check int type
- LitKind::Int(i, _) => println!(" if let LitKind::Int({}, _) = {}.node;", i, lit_pat),
- LitKind::Float(_, LitFloatType::Suffixed(_)) => println!(
- " if let LitKind::Float(_, LitFloatType::Suffixed(_)) = {}.node;",
- lit_pat
- ),
- LitKind::Float(_, LitFloatType::Unsuffixed) => println!(
- " if let LitKind::Float(_, LitFloatType::Unsuffixed) = {}.node;",
- lit_pat
- ),
- LitKind::ByteStr(ref vec) => {
- let vec_pat = self.next("vec");
- println!(" if let LitKind::ByteStr(ref {}) = {}.node;", vec_pat, lit_pat);
- println!(" if let [{:?}] = **{};", vec, vec_pat);
- },
- LitKind::Str(ref text, _) => {
- let str_pat = self.next("s");
- println!(" if let LitKind::Str(ref {}, _) = {}.node;", str_pat, lit_pat);
- println!(" if {}.as_str() == {:?}", str_pat, &*text.as_str());
- },
+ bind!(self, lit);
+ kind!("Lit(ref {lit})");
+ self.lit(lit);
+ },
+ ExprKind::Cast(expr, cast_ty) => {
+ bind!(self, expr, cast_ty);
+ kind!("Cast({expr}, {cast_ty})");
+ if let TyKind::Path(ref qpath) = cast_ty.value.kind {
+ bind!(self, qpath);
+ out!("if let TyKind::Path(ref {qpath}) = {cast_ty}.kind;");
+ self.qpath(qpath);
}
- },
- ExprKind::Cast(expr, ty) => {
- let cast_pat = self.next("expr");
- let cast_ty = self.next("cast_ty");
- let qp_label = self.next("qp");
-
- println!("Cast(ref {}, ref {}) = {};", cast_pat, cast_ty, current);
- if let TyKind::Path(ref qp) = ty.kind {
- println!(" if let TyKind::Path(ref {}) = {}.kind;", qp_label, cast_ty);
- self.current = qp_label;
- self.print_qpath(qp);
- }
- self.current = cast_pat;
- self.visit_expr(expr);
+ self.expr(expr);
},
ExprKind::Type(expr, _ty) => {
- let cast_pat = self.next("expr");
- println!("Type(ref {}, _) = {};", cast_pat, current);
- self.current = cast_pat;
- self.visit_expr(expr);
- },
- ExprKind::Loop(body, _, des, _) => {
- let body_pat = self.next("body");
- let label_pat = self.next("label");
- println!(
- "Loop(ref {}, ref {}, LoopSource::{:?}) = {};",
- body_pat, label_pat, des, current
- );
- self.current = body_pat;
- self.visit_block(body);
- },
- ExprKind::If(cond, then, ref opt_else) => {
- let cond_pat = self.next("cond");
- let then_pat = self.next("then");
- if let Some(else_) = *opt_else {
- let else_pat = self.next("else_");
- println!(
- "If(ref {}, ref {}, Some(ref {})) = {};",
- cond_pat, then_pat, else_pat, current
- );
- self.current = else_pat;
- self.visit_expr(else_);
- } else {
- println!("If(ref {}, ref {}, None) = {};", cond_pat, then_pat, current);
- }
- self.current = cond_pat;
- self.visit_expr(cond);
- self.current = then_pat;
- self.visit_expr(then);
- },
- ExprKind::Match(expr, arms, des) => {
- let expr_pat = self.next("expr");
- let arms_pat = self.next("arms");
- println!(
- "Match(ref {}, ref {}, MatchSource::{:?}) = {};",
- expr_pat, arms_pat, des, current
- );
- self.current = expr_pat;
- self.visit_expr(expr);
- println!(" if {}.len() == {};", arms_pat, arms.len());
- for (i, arm) in arms.iter().enumerate() {
- self.current = format!("{}[{}].body", arms_pat, i);
- self.visit_expr(arm.body);
- if let Some(ref guard) = arm.guard {
- let guard_pat = self.next("guard");
- println!(" if let Some(ref {}) = {}[{}].guard;", guard_pat, arms_pat, i);
- match guard {
- hir::Guard::If(if_expr) => {
- let if_expr_pat = self.next("expr");
- println!(" if let Guard::If(ref {}) = {};", if_expr_pat, guard_pat);
- self.current = if_expr_pat;
- self.visit_expr(if_expr);
- },
- hir::Guard::IfLet(if_let_pat, if_let_expr) => {
- let if_let_pat_pat = self.next("pat");
- let if_let_expr_pat = self.next("expr");
- println!(
- " if let Guard::IfLet(ref {}, ref {}) = {};",
- if_let_pat_pat, if_let_expr_pat, guard_pat
- );
- self.current = if_let_expr_pat;
- self.visit_expr(if_let_expr);
- self.current = if_let_pat_pat;
- self.visit_pat(if_let_pat);
- },
- }
- }
- self.current = format!("{}[{}].pat", arms_pat, i);
- self.visit_pat(arm.pat);
- }
- },
- ExprKind::Closure(ref _capture_clause, _func, _, _, _) => {
- println!("Closure(ref capture_clause, ref func, _, _, _) = {};", current);
- println!(" // unimplemented: `ExprKind::Closure` is not further destructured at the moment");
- },
- ExprKind::Yield(sub, _) => {
- let sub_pat = self.next("sub");
- println!("Yield(ref sub) = {};", current);
- self.current = sub_pat;
- self.visit_expr(sub);
- },
- ExprKind::Block(block, _) => {
- let block_pat = self.next("block");
- println!("Block(ref {}) = {};", block_pat, current);
- self.current = block_pat;
- self.visit_block(block);
+ bind!(self, expr);
+ kind!("Type({expr}, _)");
+ self.expr(expr);
+ },
+ ExprKind::Loop(body, label, des, _) => {
+ bind!(self, body);
+ opt_bind!(self, label);
+ kind!("Loop({body}, {label}, LoopSource::{des:?}, _)");
+ self.block(body);
+ label.if_some(|l| self.ident(field!(l.ident)));
+ },
+ ExprKind::If(cond, then, else_expr) => {
+ bind!(self, cond, then);
+ opt_bind!(self, else_expr);
+ kind!("If({cond}, {then}, {else_expr})");
+ self.expr(cond);
+ self.expr(then);
+ else_expr.if_some(|e| self.expr(e));
+ },
+ ExprKind::Match(scrutinee, arms, des) => {
+ bind!(self, scrutinee, arms);
+ kind!("Match({scrutinee}, {arms}, MatchSource::{des:?})");
+ self.expr(scrutinee);
+ self.slice(arms, |arm| self.arm(arm));
+ },
+ ExprKind::Closure(capture_by, fn_decl, body_id, _, movability) => {
+ let movability = OptionPat::new(movability.map(|m| format!("Movability::{m:?}")));
+
+ let ret_ty = match fn_decl.output {
+ FnRetTy::DefaultReturn(_) => "FnRetTy::DefaultReturn(_)",
+ FnRetTy::Return(_) => "FnRetTy::Return(_ty)",
+ };
+
+ bind!(self, fn_decl, body_id);
+ kind!("Closure(CaptureBy::{capture_by:?}, {fn_decl}, {body_id}, _, {movability})");
+ out!("if let {ret_ty} = {fn_decl}.output;");
+ self.body(body_id);
+ },
+ ExprKind::Yield(sub, source) => {
+ bind!(self, sub);
+ kind!("Yield(sub, YieldSource::{source:?})");
+ self.expr(sub);
+ },
+ ExprKind::Block(block, label) => {
+ bind!(self, block);
+ opt_bind!(self, label);
+ kind!("Block({block}, {label})");
+ self.block(block);
+ label.if_some(|l| self.ident(field!(l.ident)));
},
ExprKind::Assign(target, value, _) => {
- let target_pat = self.next("target");
- let value_pat = self.next("value");
- println!(
- "Assign(ref {}, ref {}, ref _span) = {};",
- target_pat, value_pat, current
- );
- self.current = target_pat;
- self.visit_expr(target);
- self.current = value_pat;
- self.visit_expr(value);
- },
- ExprKind::AssignOp(ref op, target, value) => {
- let op_pat = self.next("op");
- let target_pat = self.next("target");
- let value_pat = self.next("value");
- println!(
- "AssignOp(ref {}, ref {}, ref {}) = {};",
- op_pat, target_pat, value_pat, current
- );
- println!(" if BinOpKind::{:?} == {}.node;", op.node, op_pat);
- self.current = target_pat;
- self.visit_expr(target);
- self.current = value_pat;
- self.visit_expr(value);
- },
- ExprKind::Field(object, ref field_ident) => {
- let obj_pat = self.next("object");
- let field_name_pat = self.next("field_name");
- println!("Field(ref {}, ref {}) = {};", obj_pat, field_name_pat, current);
- println!(" if {}.as_str() == {:?}", field_name_pat, field_ident.as_str());
- self.current = obj_pat;
- self.visit_expr(object);
+ bind!(self, target, value);
+ kind!("Assign({target}, {value}, _span)");
+ self.expr(target);
+ self.expr(value);
+ },
+ ExprKind::AssignOp(op, target, value) => {
+ bind!(self, op, target, value);
+ kind!("AssignOp({op}, {target}, {value})");
+ out!("if BinOpKind::{:?} == {op}.node;", op.value.node);
+ self.expr(target);
+ self.expr(value);
+ },
+ ExprKind::Field(object, field_name) => {
+ bind!(self, object, field_name);
+ kind!("Field({object}, {field_name})");
+ self.ident(field_name);
+ self.expr(object);
},
ExprKind::Index(object, index) => {
- let object_pat = self.next("object");
- let index_pat = self.next("index");
- println!("Index(ref {}, ref {}) = {};", object_pat, index_pat, current);
- self.current = object_pat;
- self.visit_expr(object);
- self.current = index_pat;
- self.visit_expr(index);
- },
- ExprKind::Path(ref path) => {
- let path_pat = self.next("path");
- println!("Path(ref {}) = {};", path_pat, current);
- self.current = path_pat;
- self.print_qpath(path);
+ bind!(self, object, index);
+ kind!("Index({object}, {index})");
+ self.expr(object);
+ self.expr(index);
+ },
+ ExprKind::Path(ref qpath) => {
+ bind!(self, qpath);
+ kind!("Path(ref {qpath})");
+ self.qpath(qpath);
},
ExprKind::AddrOf(kind, mutability, inner) => {
- let inner_pat = self.next("inner");
- println!(
- "AddrOf(BorrowKind::{:?}, Mutability::{:?}, ref {}) = {};",
- kind, mutability, inner_pat, current
- );
- self.current = inner_pat;
- self.visit_expr(inner);
- },
- ExprKind::Break(ref _destination, ref opt_value) => {
- let destination_pat = self.next("destination");
- if let Some(value) = *opt_value {
- let value_pat = self.next("value");
- println!("Break(ref {}, Some(ref {})) = {};", destination_pat, value_pat, current);
- self.current = value_pat;
- self.visit_expr(value);
- } else {
- println!("Break(ref {}, None) = {};", destination_pat, current);
- }
- // FIXME: implement label printing
- },
- ExprKind::Continue(ref _destination) => {
- let destination_pat = self.next("destination");
- println!("Again(ref {}) = {};", destination_pat, current);
- // FIXME: implement label printing
- },
- ExprKind::Ret(ref opt_value) => {
- if let Some(value) = *opt_value {
- let value_pat = self.next("value");
- println!("Ret(Some(ref {})) = {};", value_pat, current);
- self.current = value_pat;
- self.visit_expr(value);
- } else {
- println!("Ret(None) = {};", current);
- }
+ bind!(self, inner);
+ kind!("AddrOf(BorrowKind::{kind:?}, Mutability::{mutability:?}, {inner})");
+ self.expr(inner);
+ },
+ ExprKind::Break(destination, value) => {
+ bind!(self, destination);
+ opt_bind!(self, value);
+ kind!("Break({destination}, {value})");
+ self.destination(destination);
+ value.if_some(|e| self.expr(e));
+ },
+ ExprKind::Continue(destination) => {
+ bind!(self, destination);
+ kind!("Continue({destination})");
+ self.destination(destination);
+ },
+ ExprKind::Ret(value) => {
+ opt_bind!(self, value);
+ kind!("Ret({value})");
+ value.if_some(|e| self.expr(e));
},
ExprKind::InlineAsm(_) => {
- println!("InlineAsm(_) = {};", current);
- println!(" // unimplemented: `ExprKind::InlineAsm` is not further destructured at the moment");
+ kind!("InlineAsm(_)");
+ out!("// unimplemented: `ExprKind::InlineAsm` is not further destructured at the moment");
},
ExprKind::LlvmInlineAsm(_) => {
- println!("LlvmInlineAsm(_) = {};", current);
- println!(" // unimplemented: `ExprKind::LlvmInlineAsm` is not further destructured at the moment");
- },
- ExprKind::Struct(path, fields, ref opt_base) => {
- let path_pat = self.next("path");
- let fields_pat = self.next("fields");
- if let Some(base) = *opt_base {
- let base_pat = self.next("base");
- println!(
- "Struct(ref {}, ref {}, Some(ref {})) = {};",
- path_pat, fields_pat, base_pat, current
- );
- self.current = base_pat;
- self.visit_expr(base);
- } else {
- println!("Struct(ref {}, ref {}, None) = {};", path_pat, fields_pat, current);
- }
- self.current = path_pat;
- self.print_qpath(path);
- println!(" if {}.len() == {};", fields_pat, fields.len());
- println!(" // unimplemented: field checks");
- },
- ExprKind::ConstBlock(_) => {
- let value_pat = self.next("value");
- println!("Const({})", value_pat);
- self.current = value_pat;
- },
- // FIXME: compute length (needs type info)
- ExprKind::Repeat(value, _) => {
- let value_pat = self.next("value");
- println!("Repeat(ref {}, _) = {};", value_pat, current);
- println!("// unimplemented: repeat count check");
- self.current = value_pat;
- self.visit_expr(value);
- },
- ExprKind::Err => {
- println!("Err = {}", current);
- },
+ kind!("LlvmInlineAsm(_)");
+ out!("// unimplemented: `ExprKind::LlvmInlineAsm` is not further destructured at the moment");
+ },
+ ExprKind::Struct(qpath, fields, base) => {
+ bind!(self, qpath, fields);
+ opt_bind!(self, base);
+ kind!("Struct({qpath}, {fields}, {base})");
+ self.qpath(qpath);
+ self.slice(fields, |field| {
+ self.ident(field!(field.ident));
+ self.expr(field!(field.expr));
+ });
+ base.if_some(|e| self.expr(e));
+ },
+ ExprKind::ConstBlock(_) => kind!("ConstBlock(_)"),
+ ExprKind::Repeat(value, length) => {
+ bind!(self, value, length);
+ kind!("Repeat({value}, {length})");
+ self.expr(value);
+ self.body(field!(length.body));
+ },
+ ExprKind::Err => kind!("Err"),
ExprKind::DropTemps(expr) => {
- let expr_pat = self.next("expr");
- println!("DropTemps(ref {}) = {};", expr_pat, current);
- self.current = expr_pat;
- self.visit_expr(expr);
+ bind!(self, expr);
+ kind!("DropTemps({expr})");
+ self.expr(expr);
},
}
}
- fn visit_block(&mut self, block: &Block<'_>) {
- println!(" if {}.stmts.len() == {};", self.current, block.stmts.len());
- let block_name = self.current.clone();
- for (i, stmt) in block.stmts.iter().enumerate() {
- self.current = format!("{}.stmts[{}]", block_name, i);
- self.visit_stmt(stmt);
- }
- if let Some(expr) = block.expr {
- self.current = self.next("trailing_expr");
- println!(" if let Some({}) = &{}.expr;", self.current, block_name);
- self.visit_expr(expr);
- } else {
- println!(" if {}.expr.is_none();", block_name);
- }
+ fn block(&self, block: &Binding<&hir::Block<'_>>) {
+ self.slice(field!(block.stmts), |stmt| self.stmt(stmt));
+ self.option(field!(block.expr), "trailing_expr", |expr| {
+ self.expr(expr);
+ });
}
- #[allow(clippy::too_many_lines)]
- fn visit_pat(&mut self, pat: &Pat<'_>) {
- print!(" if let PatKind::");
- let current = format!("{}.kind", self.current);
- match pat.kind {
- PatKind::Wild => println!("Wild = {};", current),
- PatKind::Binding(anno, .., ident, ref sub) => {
- let anno_pat = &format!("BindingAnnotation::{:?}", anno);
- let name_pat = self.next("name");
- if let Some(sub) = *sub {
- let sub_pat = self.next("sub");
- println!(
- "Binding({}, _, {}, Some(ref {})) = {};",
- anno_pat, name_pat, sub_pat, current
- );
- self.current = sub_pat;
- self.visit_pat(sub);
- } else {
- println!("Binding({}, _, {}, None) = {};", anno_pat, name_pat, current);
- }
- println!(" if {}.as_str() == \"{}\";", name_pat, ident.as_str());
- },
- PatKind::Struct(ref path, fields, ignore) => {
- let path_pat = self.next("path");
- let fields_pat = self.next("fields");
- println!(
- "Struct(ref {}, ref {}, {}) = {};",
- path_pat, fields_pat, ignore, current
- );
- self.current = path_pat;
- self.print_qpath(path);
- println!(" if {}.len() == {};", fields_pat, fields.len());
- println!(" // unimplemented: field checks");
+ fn body(&self, body_id: &Binding<hir::BodyId>) {
+ 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);
+ }
+
+ fn pat(&self, pat: &Binding<&hir::Pat<'_>>) {
+ let kind = |kind| out!("if let PatKind::{kind} = {pat}.kind;");
+ macro_rules! kind {
+ ($($t:tt)*) => (kind(format_args!($($t)*)));
+ }
+
+ match pat.value.kind {
+ PatKind::Wild => kind!("Wild"),
+ PatKind::Binding(anno, .., name, sub) => {
+ bind!(self, name);
+ opt_bind!(self, sub);
+ kind!("Binding(BindingAnnotation::{anno:?}, _, {name}, {sub})");
+ self.ident(name);
+ sub.if_some(|p| self.pat(p));
+ },
+ PatKind::Struct(ref qpath, fields, ignore) => {
+ bind!(self, qpath, fields);
+ kind!("Struct(ref {qpath}, {fields}, {ignore})");
+ self.qpath(qpath);
+ self.slice(fields, |field| {
+ self.ident(field!(field.ident));
+ self.pat(field!(field.pat));
+ });
},
PatKind::Or(fields) => {
- let fields_pat = self.next("fields");
- println!("Or(ref {}) = {};", fields_pat, current);
- println!(" if {}.len() == {};", fields_pat, fields.len());
- println!(" // unimplemented: field checks");
- },
- PatKind::TupleStruct(ref path, fields, skip_pos) => {
- let path_pat = self.next("path");
- let fields_pat = self.next("fields");
- println!(
- "TupleStruct(ref {}, ref {}, {:?}) = {};",
- path_pat, fields_pat, skip_pos, current
- );
- self.current = path_pat;
- self.print_qpath(path);
- println!(" if {}.len() == {};", fields_pat, fields.len());
- println!(" // unimplemented: field checks");
- },
- PatKind::Path(ref path) => {
- let path_pat = self.next("path");
- println!("Path(ref {}) = {};", path_pat, current);
- self.current = path_pat;
- self.print_qpath(path);
+ bind!(self, fields);
+ kind!("Or({fields})");
+ self.slice(fields, |pat| self.pat(pat));
+ },
+ PatKind::TupleStruct(ref qpath, fields, skip_pos) => {
+ bind!(self, qpath, fields);
+ kind!("TupleStruct(ref {qpath}, {fields}, {skip_pos:?})");
+ self.qpath(qpath);
+ self.slice(fields, |pat| self.pat(pat));
+ },
+ PatKind::Path(ref qpath) => {
+ bind!(self, qpath);
+ kind!("Path(ref {qpath})");
+ self.qpath(qpath);
},
PatKind::Tuple(fields, skip_pos) => {
- let fields_pat = self.next("fields");
- println!("Tuple(ref {}, {:?}) = {};", fields_pat, skip_pos, current);
- println!(" if {}.len() == {};", fields_pat, fields.len());
- println!(" // unimplemented: field checks");
+ bind!(self, fields);
+ kind!("Tuple({fields}, {skip_pos:?})");
+ self.slice(fields, |field| self.pat(field));
},
PatKind::Box(pat) => {
- let pat_pat = self.next("pat");
- println!("Box(ref {}) = {};", pat_pat, current);
- self.current = pat_pat;
- self.visit_pat(pat);
+ bind!(self, pat);
+ kind!("Box({pat})");
+ self.pat(pat);
},
PatKind::Ref(pat, muta) => {
- let pat_pat = self.next("pat");
- println!("Ref(ref {}, Mutability::{:?}) = {};", pat_pat, muta, current);
- self.current = pat_pat;
- self.visit_pat(pat);
+ bind!(self, pat);
+ kind!("Ref({pat}, Mutability::{muta:?})");
+ self.pat(pat);
},
PatKind::Lit(lit_expr) => {
- let lit_expr_pat = self.next("lit_expr");
- println!("Lit(ref {}) = {}", lit_expr_pat, current);
- self.current = lit_expr_pat;
- self.visit_expr(lit_expr);
- },
- PatKind::Range(ref start, ref end, end_kind) => {
- let start_pat = self.next("start");
- let end_pat = self.next("end");
- println!(
- "Range(ref {}, ref {}, RangeEnd::{:?}) = {};",
- start_pat, end_pat, end_kind, current
- );
- self.current = start_pat;
- walk_list!(self, visit_expr, start);
- self.current = end_pat;
- walk_list!(self, visit_expr, end);
- },
- PatKind::Slice(start, ref middle, end) => {
- let start_pat = self.next("start");
- let end_pat = self.next("end");
- if let Some(middle) = middle {
- let middle_pat = self.next("middle");
- println!(
- "Slice(ref {}, Some(ref {}), ref {}) = {};",
- start_pat, middle_pat, end_pat, current
- );
- self.current = middle_pat;
- self.visit_pat(middle);
- } else {
- println!("Slice(ref {}, None, ref {}) = {};", start_pat, end_pat, current);
- }
- println!(" if {}.len() == {};", start_pat, start.len());
- for (i, pat) in start.iter().enumerate() {
- self.current = format!("{}[{}]", start_pat, i);
- self.visit_pat(pat);
- }
- println!(" if {}.len() == {};", end_pat, end.len());
- for (i, pat) in end.iter().enumerate() {
- self.current = format!("{}[{}]", end_pat, i);
- self.visit_pat(pat);
- }
+ bind!(self, lit_expr);
+ kind!("Lit({lit_expr})");
+ self.expr(lit_expr);
+ },
+ PatKind::Range(start, end, end_kind) => {
+ opt_bind!(self, start, end);
+ kind!("Range({start}, {end}, RangeEnd::{end_kind:?})");
+ start.if_some(|e| self.expr(e));
+ end.if_some(|e| self.expr(e));
+ },
+ PatKind::Slice(start, middle, end) => {
+ bind!(self, start, end);
+ opt_bind!(self, middle);
+ kind!("Slice({start}, {middle}, {end})");
+ middle.if_some(|p| self.pat(p));
+ self.slice(start, |pat| self.pat(pat));
+ self.slice(end, |pat| self.pat(pat));
},
}
}
- fn visit_stmt(&mut self, s: &Stmt<'_>) {
- print!(" if let StmtKind::");
- let current = format!("{}.kind", self.current);
- match s.kind {
- // A local (let) binding:
- StmtKind::Local(local) => {
- let local_pat = self.next("local");
- println!("Local(ref {}) = {};", local_pat, current);
- if let Some(init) = local.init {
- let init_pat = self.next("init");
- println!(" if let Some(ref {}) = {}.init;", init_pat, local_pat);
- self.current = init_pat;
- self.visit_expr(init);
- }
- self.current = format!("{}.pat", local_pat);
- self.visit_pat(local.pat);
- },
- // An item binding:
- StmtKind::Item(_) => {
- println!("Item(item_id) = {};", current);
- },
+ fn stmt(&self, stmt: &Binding<&hir::Stmt<'_>>) {
+ let kind = |kind| out!("if let StmtKind::{kind} = {stmt}.kind;");
+ macro_rules! kind {
+ ($($t:tt)*) => (kind(format_args!($($t)*)));
+ }
- // Expr without trailing semi-colon (must have unit type):
+ match stmt.value.kind {
+ StmtKind::Local(local) => {
+ bind!(self, local);
+ kind!("Local({local})");
+ self.option(field!(local.init), "init", |init| {
+ self.expr(init);
+ });
+ self.pat(field!(local.pat));
+ },
+ StmtKind::Item(_) => kind!("Item(item_id)"),
StmtKind::Expr(e) => {
- let e_pat = self.next("e");
- println!("Expr(ref {}, _) = {}", e_pat, current);
- self.current = e_pat;
- self.visit_expr(e);
+ bind!(self, e);
+ kind!("Expr({e})");
+ self.expr(e);
},
-
- // Expr with trailing semi-colon (may have any type):
StmtKind::Semi(e) => {
- let e_pat = self.next("e");
- println!("Semi(ref {}, _) = {}", e_pat, current);
- self.current = e_pat;
- self.visit_expr(e);
+ bind!(self, e);
+ kind!("Semi({e})");
+ self.expr(e);
},
}
}
-
- fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
- NestedVisitorMap::None
- }
}
fn has_attr(cx: &LateContext<'_>, hir_id: hir::HirId) -> bool {
get_attr(cx.sess(), attrs, "author").count() > 0
}
-fn print_path(path: &QPath<'_>, first: &mut bool) {
- match *path {
- QPath::Resolved(_, path) => {
- for segment in path.segments {
- if *first {
- *first = false;
- } else {
- print!(", ");
- }
- print!("{:?}", segment.ident.as_str());
- }
- },
- QPath::TypeRelative(ty, segment) => match ty.kind {
- hir::TyKind::Path(ref inner_path) => {
- print_path(inner_path, first);
- if *first {
- *first = false;
- } else {
- print!(", ");
+fn path_to_string(path: &QPath<'_>) -> String {
+ fn inner(s: &mut String, path: &QPath<'_>) {
+ match *path {
+ QPath::Resolved(_, path) => {
+ for (i, segment) in path.segments.iter().enumerate() {
+ if i > 0 {
+ *s += ", ";
+ }
+ write!(s, "{:?}", segment.ident.as_str()).unwrap();
}
- print!("{:?}", segment.ident.as_str());
},
- ref other => print!("/* unimplemented: {:?}*/", other),
- },
- QPath::LangItem(..) => panic!("print_path: called for lang item qpath"),
+ QPath::TypeRelative(ty, segment) => match &ty.kind {
+ hir::TyKind::Path(inner_path) => {
+ inner(s, inner_path);
+ *s += ", ";
+ write!(s, "{:?}", segment.ident.as_str()).unwrap();
+ },
+ other => write!(s, "/* unimplemented: {:?}*/", other).unwrap(),
+ },
+ QPath::LangItem(..) => panic!("path_to_string: called for lang item qpath"),
+ }
}
+ let mut s = String::new();
+ inner(&mut s, path);
+ s
}
pub rename: String,
}
-/// A single disallowed method, used by the `DISALLOWED_METHOD` lint.
+/// A single disallowed method, used by the `DISALLOWED_METHODS` lint.
#[derive(Clone, Debug, Deserialize)]
#[serde(untagged)]
pub enum DisallowedMethod {
WithReason { path: String, reason: Option<String> },
}
-/// A single disallowed type, used by the `DISALLOWED_TYPE` lint.
+/// A single disallowed type, used by the `DISALLOWED_TYPES` lint.
#[derive(Clone, Debug, Deserialize)]
#[serde(untagged)]
pub enum DisallowedType {
///
/// Suppress lints whenever the suggested change would cause breakage for other crates.
(avoid_breaking_exported_api: bool = true),
- /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT.
+ /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE.
///
/// The minimum rust version that the project supports
(msrv: Option<String> = None),
///
/// Whether to allow certain wildcard imports (prelude, super in tests).
(warn_on_all_wildcard_imports: bool = false),
- /// Lint: DISALLOWED_METHOD.
+ /// Lint: DISALLOWED_METHODS.
///
/// The list of disallowed methods, written as fully qualified paths.
(disallowed_methods: Vec<crate::utils::conf::DisallowedMethod> = Vec::new()),
- /// Lint: DISALLOWED_TYPE.
+ /// Lint: DISALLOWED_TYPES.
///
/// The list of disallowed types, written as fully qualified paths.
(disallowed_types: Vec<crate::utils::conf::DisallowedType> = Vec::new()),
///
/// Whether to apply the raw pointer heuristic to determine if a type is `Send`.
(enable_raw_pointer_heuristic_for_send: bool = true),
+ /// Lint: INDEX_REFUTABLE_SLICE.
+ ///
+ /// When Clippy suggests using a slice pattern, this is the maximum number of elements allowed in
+ /// the slice pattern that is suggested. If more elements would be necessary, the lint is suppressed.
+ /// For example, `[_, _, _, e, ..]` is a slice pattern with 4 elements.
+ (max_suggested_slice_pattern_length: u64 = 3),
}
/// Search for the configuration file.
paths, SpanlessEq,
};
use if_chain::if_chain;
+use rustc_ast as ast;
use rustc_ast::ast::{Crate, ItemKind, LitKind, ModKind, NodeId};
use rustc_ast::visit::FnKind;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_middle::hir::map::Map;
use rustc_middle::mir::interpret::ConstValue;
use rustc_middle::ty;
+use rustc_semver::RustcVersion;
use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::Spanned;
use rustc_span::symbol::{Symbol, SymbolStr};
-use rustc_span::{BytePos, Span};
+use rustc_span::{sym, BytePos, Span};
use rustc_typeck::hir_ty_to_ty;
use std::borrow::{Borrow, Cow};
"non-idiomatic `if_chain!` usage"
}
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for invalid `clippy::version` attributes.
+ ///
+ /// Valid values are:
+ /// * "pre 1.29.0"
+ /// * any valid semantic version
+ pub INVALID_CLIPPY_VERSION_ATTRIBUTE,
+ internal,
+ "found an invalid `clippy::version` attribute"
+}
+
+declare_clippy_lint! {
+ /// ### What it does
+ /// Checks for declared clippy lints without the `clippy::version` attribute.
+ ///
+ pub MISSING_CLIPPY_VERSION_ATTRIBUTE,
+ internal,
+ "found clippy lint without `clippy::version` attribute"
+}
+
declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]);
impl EarlyLintPass for ClippyLintsInternal {
registered_lints: FxHashSet<Symbol>,
}
-impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS]);
+impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS, INVALID_CLIPPY_VERSION_ATTRIBUTE, MISSING_CLIPPY_VERSION_ATTRIBUTE]);
impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
if let hir::ItemKind::Static(ty, Mutability::Not, body_id) = item.kind {
if is_lint_ref_type(cx, ty) {
+ check_invalid_clippy_version_attribute(cx, item);
+
let expr = &cx.tcx.hir().body(body_id).value;
if_chain! {
if let ExprKind::AddrOf(_, _, inner_exp) = expr.kind;
false
}
+fn check_invalid_clippy_version_attribute(cx: &LateContext<'_>, item: &'_ Item<'_>) {
+ if let Some(value) = extract_clippy_version_value(cx, item) {
+ // The `sym!` macro doesn't work as it only expects a single token.
+ // It's better to keep it this way and have a direct `Symbol::intern` call here.
+ if value == Symbol::intern("pre 1.29.0") {
+ return;
+ }
+
+ if RustcVersion::parse(&*value.as_str()).is_err() {
+ span_lint_and_help(
+ cx,
+ INVALID_CLIPPY_VERSION_ATTRIBUTE,
+ item.span,
+ "this item has an invalid `clippy::version` attribute",
+ None,
+ "please use a valid sematic version, see `doc/adding_lints.md`",
+ );
+ }
+ } else {
+ span_lint_and_help(
+ cx,
+ MISSING_CLIPPY_VERSION_ATTRIBUTE,
+ item.span,
+ "this lint is missing the `clippy::version` attribute or version value",
+ None,
+ "please use a `clippy::version` attribute, see `doc/adding_lints.md`",
+ );
+ }
+}
+
+/// This function extracts the version value of a `clippy::version` attribute if the given value has
+/// one
+fn extract_clippy_version_value(cx: &LateContext<'_>, item: &'_ Item<'_>) -> Option<Symbol> {
+ let attrs = cx.tcx.hir().attrs(item.hir_id());
+ attrs.iter().find_map(|attr| {
+ if_chain! {
+ // Identify attribute
+ if let ast::AttrKind::Normal(ref attr_kind, _) = &attr.kind;
+ if let [tool_name, attr_name] = &attr_kind.path.segments[..];
+ if tool_name.ident.name == sym::clippy;
+ if attr_name.ident.name == sym::version;
+ if let Some(version) = attr.value_str();
+ then {
+ Some(version)
+ } else {
+ None
+ }
+ }
+ })
+}
+
struct LintCollector<'a, 'tcx> {
output: &'a mut FxHashSet<Symbol>,
cx: &'a LateContext<'tcx>,
use std::io::prelude::*;
use std::path::Path;
-use crate::utils::internal_lints::is_lint_ref_type;
+use crate::utils::internal_lints::{extract_clippy_version_value, is_lint_ref_type};
use clippy_utils::{
diagnostics::span_lint, last_path_segment, match_def_path, match_function_call, match_path, paths, ty::match_type,
ty::walk_ptrs_ty_depth,
const APPLICABILITY_NAME_INDEX: usize = 2;
/// This applicability will be set for unresolved applicability values.
const APPLICABILITY_UNRESOLVED_STR: &str = "Unresolved";
+/// The version that will be displayed if none has been defined
+const VERION_DEFAULT_STR: &str = "Unknown";
declare_clippy_lint! {
/// ### What it does
/// "docs": " ### What it does\nCollects metadata about clippy lints for the website. [...] "
/// }
/// ```
+ #[clippy::version = "1.56.0"]
pub INTERNAL_METADATA_COLLECTOR,
internal_warn,
"A busy bee collection metadata about lints"
group: String,
level: String,
docs: String,
+ version: String,
/// This field is only used in the output and will only be
/// mapped shortly before the actual output.
applicability: Option<ApplicabilityInfo>,
}
impl LintMetadata {
- fn new(id: String, id_span: SerializableSpan, group: String, level: &'static str, docs: String) -> Self {
+ fn new(
+ id: String,
+ id_span: SerializableSpan,
+ group: String,
+ level: &'static str,
+ version: String,
+ docs: String,
+ ) -> Self {
Self {
id,
id_span,
group,
level: level.to_string(),
+ version,
docs,
applicability: None,
}
if let Some(configuration_section) = self.get_lint_configs(&lint_name) {
docs.push_str(&configuration_section);
}
+ let version = get_lint_version(cx, item);
self.lints.push(LintMetadata::new(
lint_name,
SerializableSpan::from_item(cx, item),
group,
level,
+ version,
docs,
));
}
// Metadata the little we can get from a deprecated lint
if let Some(docs) = extract_attr_docs_or_lint(cx, item);
then {
+ let version = get_lint_version(cx, item);
+
self.lints.push(LintMetadata::new(
lint_name,
SerializableSpan::from_item(cx, item),
DEPRECATED_LINT_GROUP_STR.to_string(),
DEPRECATED_LINT_LEVEL,
+ version,
docs,
));
}
Some(docs)
}
+fn get_lint_version(cx: &LateContext<'_>, item: &Item<'_>) -> String {
+ extract_clippy_version_value(cx, item).map_or_else(
+ || VERION_DEFAULT_STR.to_string(),
+ |version| version.as_str().to_string(),
+ )
+}
+
fn get_lint_group_and_level_or_lint(
cx: &LateContext<'_>,
lint_name: &str,
applicability = resolve_applicability(cx, arg);
} else if arg_ty.is_closure() {
multi_part |= check_is_multi_part(cx, arg);
- // TODO xFrednet 2021-03-01: don't use or_else but rather a comparison
applicability = applicability.or_else(|| resolve_applicability(cx, arg));
}
}
/// // Good
/// foo(&[1, 2]);
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub USELESS_VEC,
perf,
"useless `vec!`"
/// ```rust
/// let v = vec![0];
/// ```
+ #[clippy::version = "1.51.0"]
pub VEC_INIT_THEN_PUSH,
perf,
"`push` immediately after `Vec` creation"
/// ```rust
/// vec!(1, 2, 3, 4, 5).resize(0, 5)
/// ```
+ #[clippy::version = "1.46.0"]
pub VEC_RESIZE_TO_ZERO,
correctness,
"emptying a vector with `resize(0, an_int)` instead of `clear()` is probably an argument inversion mistake"
/// # use std::fs;
/// let mut bytes = fs::read("foo.txt").unwrap();
/// ```
+ #[clippy::version = "1.44.0"]
pub VERBOSE_FILE_READS,
restriction,
"use of `File::read_to_end` or `File::read_to_string`"
/// [dependencies]
/// regex = "*"
/// ```
+ #[clippy::version = "1.32.0"]
pub WILDCARD_DEPENDENCIES,
cargo,
"wildcard dependencies being used"
use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_test_module_or_function;
use clippy_utils::source::{snippet, snippet_with_applicability};
-use clippy_utils::{in_macro, is_test_module_or_function};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{
/// use std::cmp::Ordering;
/// foo(Ordering::Less)
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub ENUM_GLOB_USE,
pedantic,
"use items that import all variants of an enum"
///
/// foo();
/// ```
+ #[clippy::version = "1.43.0"]
pub WILDCARD_IMPORTS,
pedantic,
"lint `use _::*` statements"
impl WildcardImports {
fn check_exceptions(&self, item: &Item<'_>, segments: &[PathSegment<'_>]) -> bool {
- in_macro(item.span)
+ item.span.from_expansion()
|| is_prelude_import(segments)
|| (is_super_only_import(segments) && self.test_modules_deep > 0)
}
/// // Good
/// println!();
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub PRINTLN_EMPTY_STRING,
style,
"using `println!(\"\")` with an empty string"
/// # let name = "World";
/// println!("Hello {}!", name);
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub PRINT_WITH_NEWLINE,
style,
"using `print!()` with a format string that ends in a single newline"
/// application and might forget to remove those prints afterward.
///
/// ### Known problems
- /// Only catches `print!` and `println!` calls.
+ /// * Only catches `print!` and `println!` calls.
+ /// * The lint level is unaffected by crate attributes. The level can still
+ /// be set for functions, modules and other items. To change the level for
+ /// the entire crate, please use command line flags. More information and a
+ /// configuration example can be found in [clippy#6610].
+ ///
+ /// [clippy#6610]: https://github.com/rust-lang/rust-clippy/issues/6610#issuecomment-977120558
///
/// ### Example
/// ```rust
/// println!("Hello world!");
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub PRINT_STDOUT,
restriction,
"printing on stdout"
/// application and might forget to remove those prints afterward.
///
/// ### Known problems
- /// Only catches `eprint!` and `eprintln!` calls.
+ /// * Only catches `eprint!` and `eprintln!` calls.
+ /// * The lint level is unaffected by crate attributes. The level can still
+ /// be set for functions, modules and other items. To change the level for
+ /// the entire crate, please use command line flags. More information and a
+ /// configuration example can be found in [clippy#6610].
+ ///
+ /// [clippy#6610]: https://github.com/rust-lang/rust-clippy/issues/6610#issuecomment-977120558
///
/// ### Example
/// ```rust
/// eprintln!("Hello world!");
/// ```
+ #[clippy::version = "1.50.0"]
pub PRINT_STDERR,
restriction,
"printing on stderr"
/// # let foo = "bar";
/// println!("{:?}", foo);
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub USE_DEBUG,
restriction,
"use of `Debug`-based formatting"
/// ```rust
/// println!("foo");
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub PRINT_LITERAL,
style,
"printing a literal with a format string"
/// // Good
/// writeln!(buf);
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub WRITELN_EMPTY_STRING,
style,
"using `writeln!(buf, \"\")` with an empty string"
/// // Good
/// writeln!(buf, "Hello {}!", name);
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub WRITE_WITH_NEWLINE,
style,
"using `write!()` with a format string that ends in a single newline"
/// // Good
/// writeln!(buf, "foo");
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub WRITE_LITERAL,
style,
"writing a literal with a format string"
let replacement: String = match lit.token.kind {
LitKind::Integer | LitKind::Float | LitKind::Err => continue,
LitKind::StrRaw(_) | LitKind::ByteStrRaw(_) if matches!(fmtstr.style, StrStyle::Raw(_)) => {
- lit.token.symbol.as_str().replace("{", "{{").replace("}", "}}")
+ lit.token.symbol.as_str().replace('{', "{{").replace('}', "}}")
},
LitKind::Str | LitKind::ByteStr if matches!(fmtstr.style, StrStyle::Cooked) => {
- lit.token.symbol.as_str().replace("{", "{{").replace("}", "}}")
+ lit.token.symbol.as_str().replace('{', "{{").replace('}', "}}")
},
LitKind::StrRaw(_) | LitKind::Str | LitKind::ByteStrRaw(_) | LitKind::ByteStr => continue,
LitKind::Byte | LitKind::Char => match &*lit.token.symbol.as_str() {
/// // Good
/// let nan = f32::NAN;
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub ZERO_DIVIDED_BY_ZERO,
complexity,
"usage of `0.0 / 0.0` to obtain NaN instead of `f32::NAN` or `f64::NAN`"
/// todo!();
/// }
/// ```
+ #[clippy::version = "1.50.0"]
pub ZERO_SIZED_MAP_VALUES,
pedantic,
"usage of map with zero-sized value type"
[package]
name = "clippy_utils"
-version = "0.1.58"
+version = "0.1.59"
edition = "2021"
publish = false
eq_id(l.ident, r.ident) && over(&l.attrs, &r.attrs, eq_attr) && eq_vis(&l.vis, &r.vis) && eq_kind(&l.kind, &r.kind)
}
+#[allow(clippy::too_many_lines)] // Just a big match statement
pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
use ItemKind::*;
match (l, r) {
(Use(l), Use(r)) => eq_use_tree(l, r),
(Static(lt, lm, le), Static(rt, rm, re)) => lm == rm && eq_ty(lt, rt) && eq_expr_opt(le, re),
(Const(ld, lt, le), Const(rd, rt, re)) => eq_defaultness(*ld, *rd) && eq_ty(lt, rt) && eq_expr_opt(le, re),
- (Fn(box ast::Fn { defaultness: ld, sig: lf, generics: lg, body: lb }),
- Fn(box ast::Fn { defaultness: rd, sig: rf, generics: rg, body: rb })) => {
+ (
+ Fn(box ast::Fn {
+ defaultness: ld,
+ sig: lf,
+ generics: lg,
+ body: lb,
+ }),
+ Fn(box ast::Fn {
+ defaultness: rd,
+ sig: rf,
+ generics: rg,
+ body: rb,
+ }),
+ ) => {
eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) && eq_generics(lg, rg) && both(lb, rb, |l, r| eq_block(l, r))
},
(Mod(lu, lmk), Mod(ru, rmk)) => {
(ForeignMod(l), ForeignMod(r)) => {
both(&l.abi, &r.abi, eq_str_lit) && over(&l.items, &r.items, |l, r| eq_item(l, r, eq_foreign_item_kind))
},
- (TyAlias(box ast::TyAlias { defaultness: ld, generics: lg, bounds: lb, ty: lt }),
- TyAlias(box ast::TyAlias { defaultness: rd, generics: rg, bounds: rb, ty: rt })) => {
+ (
+ TyAlias(box ast::TyAlias {
+ defaultness: ld,
+ generics: lg,
+ bounds: lb,
+ ty: lt,
+ }),
+ TyAlias(box ast::TyAlias {
+ defaultness: rd,
+ generics: rg,
+ bounds: rb,
+ ty: rt,
+ }),
+ ) => {
eq_defaultness(*ld, *rd)
&& eq_generics(lg, rg)
&& over(lb, rb, eq_generic_bound)
(Struct(lv, lg), Struct(rv, rg)) | (Union(lv, lg), Union(rv, rg)) => {
eq_variant_data(lv, rv) && eq_generics(lg, rg)
},
- (Trait(box ast::Trait { is_auto: la, unsafety: lu, generics: lg, bounds: lb, items: li }),
- Trait(box ast::Trait { is_auto: ra, unsafety: ru, generics: rg, bounds: rb, items: ri })) => {
+ (
+ Trait(box ast::Trait {
+ is_auto: la,
+ unsafety: lu,
+ generics: lg,
+ bounds: lb,
+ items: li,
+ }),
+ Trait(box ast::Trait {
+ is_auto: ra,
+ unsafety: ru,
+ generics: rg,
+ bounds: rb,
+ items: ri,
+ }),
+ ) => {
la == ra
&& matches!(lu, Unsafe::No) == matches!(ru, Unsafe::No)
&& eq_generics(lg, rg)
use ForeignItemKind::*;
match (l, r) {
(Static(lt, lm, le), Static(rt, rm, re)) => lm == rm && eq_ty(lt, rt) && eq_expr_opt(le, re),
- (Fn(box ast::Fn { defaultness: ld, sig: lf, generics: lg, body: lb }),
- Fn(box ast::Fn { defaultness: rd, sig: rf, generics: rg, body: rb })) => {
+ (
+ Fn(box ast::Fn {
+ defaultness: ld,
+ sig: lf,
+ generics: lg,
+ body: lb,
+ }),
+ Fn(box ast::Fn {
+ defaultness: rd,
+ sig: rf,
+ generics: rg,
+ body: rb,
+ }),
+ ) => {
eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) && eq_generics(lg, rg) && both(lb, rb, |l, r| eq_block(l, r))
},
- (TyAlias(box ast::TyAlias { defaultness: ld, generics: lg, bounds: lb, ty: lt }),
- TyAlias(box ast::TyAlias { defaultness: rd, generics: rg, bounds: rb, ty: rt })) => {
+ (
+ TyAlias(box ast::TyAlias {
+ defaultness: ld,
+ generics: lg,
+ bounds: lb,
+ ty: lt,
+ }),
+ TyAlias(box ast::TyAlias {
+ defaultness: rd,
+ generics: rg,
+ bounds: rb,
+ ty: rt,
+ }),
+ ) => {
eq_defaultness(*ld, *rd)
&& eq_generics(lg, rg)
&& over(lb, rb, eq_generic_bound)
use AssocItemKind::*;
match (l, r) {
(Const(ld, lt, le), Const(rd, rt, re)) => eq_defaultness(*ld, *rd) && eq_ty(lt, rt) && eq_expr_opt(le, re),
- (Fn(box ast::Fn { defaultness: ld, sig: lf, generics: lg, body: lb }),
- Fn(box ast::Fn { defaultness: rd, sig: rf, generics: rg, body: rb })) => {
+ (
+ Fn(box ast::Fn {
+ defaultness: ld,
+ sig: lf,
+ generics: lg,
+ body: lb,
+ }),
+ Fn(box ast::Fn {
+ defaultness: rd,
+ sig: rf,
+ generics: rg,
+ body: rb,
+ }),
+ ) => {
eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) && eq_generics(lg, rg) && both(lb, rb, |l, r| eq_block(l, r))
},
- (TyAlias(box ast::TyAlias { defaultness: ld, generics: lg, bounds: lb, ty: lt }),
- TyAlias(box ast::TyAlias { defaultness: rd, generics: rg, bounds: rb, ty: rt })) => {
+ (
+ TyAlias(box ast::TyAlias {
+ defaultness: ld,
+ generics: lg,
+ bounds: lb,
+ ty: lt,
+ }),
+ TyAlias(box ast::TyAlias {
+ defaultness: rd,
+ generics: rg,
+ bounds: rb,
+ ty: rt,
+ }),
+ ) => {
eq_defaultness(*ld, *rd)
&& eq_generics(lg, rg)
&& over(lb, rb, eq_generic_bound)
None,
}
+#[rustfmt::skip]
pub const BUILTIN_ATTRIBUTES: &[(&str, DeprecationStatus)] = &[
- ("author", DeprecationStatus::None),
- ("cognitive_complexity", DeprecationStatus::None),
- (
- "cyclomatic_complexity",
- DeprecationStatus::Replaced("cognitive_complexity"),
- ),
- ("dump", DeprecationStatus::None),
- ("msrv", DeprecationStatus::None),
+ ("author", DeprecationStatus::None),
+ ("version", DeprecationStatus::None),
+ ("cognitive_complexity", DeprecationStatus::None),
+ ("cyclomatic_complexity", DeprecationStatus::Replaced("cognitive_complexity")),
+ ("dump", DeprecationStatus::None),
+ ("msrv", DeprecationStatus::None),
];
pub struct LimitStack {
//! - or-fun-call
//! - option-if-let-else
-use crate::is_ctor_or_promotable_const_function;
-use crate::ty::is_type_diagnostic_item;
+use crate::ty::{all_predicates_of, is_copy};
+use crate::visitors::is_const_evaluatable;
use rustc_hir::def::{DefKind, Res};
-
-use rustc_hir::intravisit;
-use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
-
-use rustc_hir::{Block, Expr, ExprKind, Path, QPath};
+use rustc_hir::intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor};
+use rustc_hir::{def_id::DefId, Block, Expr, ExprKind, QPath, UnOp};
use rustc_lint::LateContext;
-use rustc_middle::hir::map::Map;
-use rustc_span::sym;
-
-/// Is the expr pure (is it free from side-effects)?
-/// This function is named so to stress that it isn't exhaustive and returns FNs.
-fn identify_some_pure_patterns(expr: &Expr<'_>) -> bool {
- match expr.kind {
- ExprKind::Lit(..) | ExprKind::ConstBlock(..) | ExprKind::Path(..) | ExprKind::Field(..) => true,
- ExprKind::AddrOf(_, _, addr_of_expr) => identify_some_pure_patterns(addr_of_expr),
- ExprKind::Tup(tup_exprs) => tup_exprs.iter().all(identify_some_pure_patterns),
- ExprKind::Struct(_, fields, expr) => {
- fields.iter().all(|f| identify_some_pure_patterns(f.expr)) && expr.map_or(true, identify_some_pure_patterns)
- },
- ExprKind::Call(
- &Expr {
- kind:
- ExprKind::Path(QPath::Resolved(
- _,
- Path {
- res: Res::Def(DefKind::Ctor(..) | DefKind::Variant, ..),
- ..
- },
- )),
- ..
- },
- args,
- ) => args.iter().all(identify_some_pure_patterns),
- ExprKind::Block(
- &Block {
- stmts,
- expr: Some(expr),
- ..
- },
- _,
- ) => stmts.is_empty() && identify_some_pure_patterns(expr),
- ExprKind::Box(..)
- | ExprKind::Array(..)
- | ExprKind::Call(..)
- | ExprKind::MethodCall(..)
- | ExprKind::Binary(..)
- | ExprKind::Unary(..)
- | ExprKind::Let(..)
- | ExprKind::Cast(..)
- | ExprKind::Type(..)
- | ExprKind::DropTemps(..)
- | ExprKind::Loop(..)
- | ExprKind::If(..)
- | ExprKind::Match(..)
- | ExprKind::Closure(..)
- | ExprKind::Block(..)
- | ExprKind::Assign(..)
- | ExprKind::AssignOp(..)
- | ExprKind::Index(..)
- | ExprKind::Break(..)
- | ExprKind::Continue(..)
- | ExprKind::Ret(..)
- | ExprKind::InlineAsm(..)
- | ExprKind::LlvmInlineAsm(..)
- | ExprKind::Repeat(..)
- | ExprKind::Yield(..)
- | ExprKind::Err => false,
+use rustc_middle::ty::{self, PredicateKind};
+use rustc_span::{sym, Symbol};
+use std::cmp;
+use std::ops;
+
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+enum EagernessSuggestion {
+ // The expression is cheap and should be evaluated eagerly
+ Eager,
+ // The expression may be cheap, so don't suggested lazy evaluation; or the expression may not be safe to switch to
+ // eager evaluation.
+ NoChange,
+ // The expression is likely expensive and should be evaluated lazily.
+ Lazy,
+ // The expression cannot be placed into a closure.
+ ForceNoChange,
+}
+impl ops::BitOr for EagernessSuggestion {
+ type Output = Self;
+ fn bitor(self, rhs: Self) -> Self {
+ cmp::max(self, rhs)
}
}
-
-/// Identify some potentially computationally expensive patterns.
-/// This function is named so to stress that its implementation is non-exhaustive.
-/// It returns FNs and FPs.
-fn identify_some_potentially_expensive_patterns<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
- // Searches an expression for method calls or function calls that aren't ctors
- struct FunCallFinder<'a, 'tcx> {
- cx: &'a LateContext<'tcx>,
- found: bool,
+impl ops::BitOrAssign for EagernessSuggestion {
+ fn bitor_assign(&mut self, rhs: Self) {
+ *self = *self | rhs;
}
+}
- impl<'a, 'tcx> intravisit::Visitor<'tcx> for FunCallFinder<'a, 'tcx> {
- type Map = Map<'tcx>;
-
- fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
- let call_found = match &expr.kind {
- // ignore enum and struct constructors
- ExprKind::Call(..) => !is_ctor_or_promotable_const_function(self.cx, expr),
- ExprKind::Index(obj, _) => {
- let ty = self.cx.typeck_results().expr_ty(obj);
- is_type_diagnostic_item(self.cx, ty, sym::HashMap)
- || is_type_diagnostic_item(self.cx, ty, sym::BTreeMap)
- },
- ExprKind::MethodCall(..) => true,
- _ => false,
- };
+/// Determine the eagerness of the given function call.
+fn fn_eagerness(cx: &LateContext<'tcx>, fn_id: DefId, name: Symbol, args: &'tcx [Expr<'_>]) -> EagernessSuggestion {
+ use EagernessSuggestion::{Eager, Lazy, NoChange};
+ let name = &*name.as_str();
- if call_found {
- self.found |= true;
- }
+ let ty = match cx.tcx.impl_of_method(fn_id) {
+ Some(id) => cx.tcx.type_of(id),
+ None => return Lazy,
+ };
- if !self.found {
- intravisit::walk_expr(self, expr);
+ if (name.starts_with("as_") || name == "len" || name == "is_empty") && args.len() == 1 {
+ if matches!(
+ cx.tcx.crate_name(fn_id.krate),
+ sym::std | sym::core | sym::alloc | sym::proc_macro
+ ) {
+ Eager
+ } else {
+ NoChange
+ }
+ } else if let ty::Adt(def, subs) = ty.kind() {
+ // Types where the only fields are generic types (or references to) with no trait bounds other
+ // than marker traits.
+ // Due to the limited operations on these types functions should be fairly cheap.
+ if def
+ .variants
+ .iter()
+ .flat_map(|v| v.fields.iter())
+ .any(|x| matches!(cx.tcx.type_of(x.did).peel_refs().kind(), ty::Param(_)))
+ && all_predicates_of(cx.tcx, fn_id).all(|(pred, _)| match pred.kind().skip_binder() {
+ PredicateKind::Trait(pred) => cx.tcx.trait_def(pred.trait_ref.def_id).is_marker,
+ _ => true,
+ })
+ && subs.types().all(|x| matches!(x.peel_refs().kind(), ty::Param(_)))
+ {
+ // Limit the function to either `(self) -> bool` or `(&self) -> bool`
+ match &**cx.tcx.fn_sig(fn_id).skip_binder().inputs_and_output {
+ [arg, res] if !arg.is_mutable_ptr() && arg.peel_refs() == ty && res.is_bool() => NoChange,
+ _ => Lazy,
}
+ } else {
+ Lazy
}
+ } else {
+ Lazy
+ }
+}
+#[allow(clippy::too_many_lines)]
+fn expr_eagerness(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessSuggestion {
+ struct V<'cx, 'tcx> {
+ cx: &'cx LateContext<'tcx>,
+ eagerness: EagernessSuggestion,
+ }
+
+ impl<'cx, 'tcx> Visitor<'tcx> for V<'cx, 'tcx> {
+ type Map = ErasedMap<'tcx>;
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
NestedVisitorMap::None
}
+
+ fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
+ use EagernessSuggestion::{ForceNoChange, Lazy, NoChange};
+ if self.eagerness == ForceNoChange {
+ return;
+ }
+ match e.kind {
+ ExprKind::Call(
+ &Expr {
+ kind: ExprKind::Path(ref path),
+ hir_id,
+ ..
+ },
+ args,
+ ) => match self.cx.qpath_res(path, hir_id) {
+ Res::Def(DefKind::Ctor(..) | DefKind::Variant, _) | Res::SelfCtor(_) => (),
+ Res::Def(_, id) if self.cx.tcx.is_promotable_const_fn(id) => (),
+ // No need to walk the arguments here, `is_const_evaluatable` already did
+ Res::Def(..) if is_const_evaluatable(self.cx, e) => {
+ self.eagerness |= NoChange;
+ return;
+ },
+ Res::Def(_, id) => match path {
+ QPath::Resolved(_, p) => {
+ self.eagerness |= fn_eagerness(self.cx, id, p.segments.last().unwrap().ident.name, args);
+ },
+ QPath::TypeRelative(_, name) => {
+ self.eagerness |= fn_eagerness(self.cx, id, name.ident.name, args);
+ },
+ QPath::LangItem(..) => self.eagerness = Lazy,
+ },
+ _ => self.eagerness = Lazy,
+ },
+ // No need to walk the arguments here, `is_const_evaluatable` already did
+ ExprKind::MethodCall(..) if is_const_evaluatable(self.cx, e) => {
+ self.eagerness |= NoChange;
+ return;
+ },
+ ExprKind::MethodCall(name, _, args, _) => {
+ 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));
+ },
+ ExprKind::Index(_, e) => {
+ let ty = self.cx.typeck_results().expr_ty_adjusted(e);
+ if is_copy(self.cx, ty) && !ty.is_ref() {
+ self.eagerness |= NoChange;
+ } else {
+ self.eagerness = Lazy;
+ }
+ },
+
+ // Dereferences should be cheap, but dereferencing a raw pointer earlier may not be safe.
+ ExprKind::Unary(UnOp::Deref, e) if !self.cx.typeck_results().expr_ty(e).is_unsafe_ptr() => (),
+ ExprKind::Unary(UnOp::Deref, _) => self.eagerness |= NoChange,
+
+ ExprKind::Unary(_, e)
+ if matches!(
+ self.cx.typeck_results().expr_ty(e).kind(),
+ ty::Bool | ty::Int(_) | ty::Uint(_),
+ ) => {},
+ ExprKind::Binary(_, lhs, rhs)
+ if self.cx.typeck_results().expr_ty(lhs).is_primitive()
+ && self.cx.typeck_results().expr_ty(rhs).is_primitive() => {},
+
+ // Can't be moved into a closure
+ ExprKind::Break(..)
+ | ExprKind::Continue(_)
+ | ExprKind::Ret(_)
+ | ExprKind::InlineAsm(_)
+ | ExprKind::LlvmInlineAsm(_)
+ | ExprKind::Yield(..)
+ | ExprKind::Err => {
+ self.eagerness = ForceNoChange;
+ return;
+ },
+
+ // Memory allocation, custom operator, loop, or call to an unknown function
+ ExprKind::Box(_)
+ | ExprKind::Unary(..)
+ | ExprKind::Binary(..)
+ | ExprKind::Loop(..)
+ | ExprKind::Call(..) => self.eagerness = Lazy,
+
+ ExprKind::ConstBlock(_)
+ | ExprKind::Array(_)
+ | ExprKind::Tup(_)
+ | ExprKind::Lit(_)
+ | ExprKind::Cast(..)
+ | ExprKind::Type(..)
+ | ExprKind::DropTemps(_)
+ | ExprKind::Let(..)
+ | ExprKind::If(..)
+ | ExprKind::Match(..)
+ | ExprKind::Closure(..)
+ | ExprKind::Field(..)
+ | ExprKind::Path(_)
+ | ExprKind::AddrOf(..)
+ | ExprKind::Struct(..)
+ | ExprKind::Repeat(..)
+ | ExprKind::Block(Block { stmts: [], .. }, _) => (),
+
+ // Assignment might be to a local defined earlier, so don't eagerly evaluate.
+ // Blocks with multiple statements might be expensive, so don't eagerly evaluate.
+ // TODO: Actually check if either of these are true here.
+ ExprKind::Assign(..) | ExprKind::AssignOp(..) | ExprKind::Block(..) => self.eagerness |= NoChange,
+ }
+ walk_expr(self, e);
+ }
}
- let mut finder = FunCallFinder { cx, found: false };
- finder.visit_expr(expr);
- finder.found
+ let mut v = V {
+ cx,
+ eagerness: EagernessSuggestion::Eager,
+ };
+ v.visit_expr(e);
+ v.eagerness
}
-pub fn is_eagerness_candidate<'a, 'tcx>(cx: &'a LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
- !identify_some_potentially_expensive_patterns(cx, expr) && identify_some_pure_patterns(expr)
+/// Whether the given expression should be changed to evaluate eagerly
+pub fn switch_to_eager_eval(cx: &'_ LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
+ expr_eagerness(cx, expr) == EagernessSuggestion::Eager
}
-pub fn is_lazyness_candidate<'a, 'tcx>(cx: &'a LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
- identify_some_potentially_expensive_patterns(cx, expr)
+/// Whether the given expression should be changed to evaluate lazily
+pub fn switch_to_lazy_eval(cx: &'_ LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
+ expr_eagerness(cx, expr) == EagernessSuggestion::Lazy
}
#![feature(box_patterns)]
#![feature(in_band_lifetimes)]
#![feature(iter_zip)]
+#![feature(let_else)]
#![feature(rustc_private)]
#![feature(control_flow_enum)]
#![recursion_limit = "512"]
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::hir_id::{HirIdMap, HirIdSet};
-use rustc_hir::intravisit::{self, walk_expr, ErasedMap, FnKind, NestedVisitorMap, Visitor};
+use rustc_hir::intravisit::{walk_expr, ErasedMap, FnKind, NestedVisitorMap, Visitor};
use rustc_hir::itemlikevisit::ItemLikeVisitor;
use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk};
use rustc_hir::{
use crate::consts::{constant, Constant};
use crate::ty::{can_partially_move_ty, is_copy, is_recursively_primitive_type};
+use crate::visitors::expr_visitor_no_bodies;
pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Option<RustcVersion> {
if let Ok(version) = RustcVersion::parse(msrv) {
false
}
-/// Returns `true` if this `span` was expanded by any macro.
-#[must_use]
-pub fn in_macro(span: Span) -> bool {
- span.from_expansion() && !matches!(span.ctxt().outer_expn_data().kind, ExpnKind::Desugaring(..))
-}
-
pub fn is_unit_expr(expr: &Expr<'_>) -> bool {
matches!(
expr.kind,
/// Returns `true` if `expr` contains a return expression
pub fn contains_return(expr: &hir::Expr<'_>) -> bool {
- struct RetCallFinder {
- found: bool,
- }
-
- impl<'tcx> hir::intravisit::Visitor<'tcx> for RetCallFinder {
- type Map = Map<'tcx>;
-
- fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
- if self.found {
- return;
- }
+ let mut found = false;
+ expr_visitor_no_bodies(|expr| {
+ if !found {
if let hir::ExprKind::Ret(..) = &expr.kind {
- self.found = true;
- } else {
- hir::intravisit::walk_expr(self, expr);
+ found = true;
}
}
-
- fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap<Self::Map> {
- hir::intravisit::NestedVisitorMap::None
- }
- }
-
- let mut visitor = RetCallFinder { found: false };
- visitor.visit_expr(expr);
- visitor.found
-}
-
-struct FindMacroCalls<'a, 'b> {
- names: &'a [&'b str],
- result: Vec<Span>,
-}
-
-impl<'a, 'b, 'tcx> Visitor<'tcx> for FindMacroCalls<'a, 'b> {
- type Map = Map<'tcx>;
-
- fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
- if self.names.iter().any(|fun| is_expn_of(expr.span, fun).is_some()) {
- self.result.push(expr.span);
- }
- // and check sub-expressions
- intravisit::walk_expr(self, expr);
- }
-
- fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
- NestedVisitorMap::None
- }
+ !found
+ })
+ .visit_expr(expr);
+ found
}
/// Finds calls of the specified macros in a function body.
pub fn find_macro_calls(names: &[&str], body: &Body<'_>) -> Vec<Span> {
- let mut fmc = FindMacroCalls {
- names,
- result: Vec::new(),
- };
- fmc.visit_expr(&body.value);
- fmc.result
+ let mut result = Vec::new();
+ expr_visitor_no_bodies(|expr| {
+ if names.iter().any(|fun| is_expn_of(expr.span, fun).is_some()) {
+ result.push(expr.span);
+ }
+ true
+ })
+ .visit_expr(&body.value);
+ result
}
/// Extends the span to the beginning of the spans line, incl. whitespaces.
/// Checks for the `#[automatically_derived]` attribute all `#[derive]`d
/// implementations have.
pub fn is_automatically_derived(attrs: &[ast::Attribute]) -> bool {
- attrs.iter().any(|attr| attr.has_name(sym::automatically_derived))
+ has_attr(attrs, sym::automatically_derived)
}
/// Remove blocks around an expression.
(u << amt) >> amt
}
-pub fn any_parent_is_automatically_derived(tcx: TyCtxt<'_>, node: HirId) -> bool {
+pub fn has_attr(attrs: &[ast::Attribute], symbol: Symbol) -> bool {
+ attrs.iter().any(|attr| attr.has_name(symbol))
+}
+
+pub fn any_parent_has_attr(tcx: TyCtxt<'_>, node: HirId, symbol: Symbol) -> bool {
let map = &tcx.hir();
let mut prev_enclosing_node = None;
let mut enclosing_node = node;
while Some(enclosing_node) != prev_enclosing_node {
- if is_automatically_derived(map.attrs(enclosing_node)) {
+ if has_attr(map.attrs(enclosing_node), symbol) {
return true;
}
prev_enclosing_node = Some(enclosing_node);
enclosing_node = map.get_parent_item(enclosing_node);
}
+
false
}
+pub fn any_parent_is_automatically_derived(tcx: TyCtxt<'_>, node: HirId) -> bool {
+ any_parent_has_attr(tcx, node, sym::automatically_derived)
+}
+
/// Matches a function call with the given path and returns the arguments.
///
/// Usage:
syms.iter().map(|x| Symbol::intern(x)).eq(path.iter().copied())
}
+/// Checks if the given `DefId` matches the `libc` item.
+pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: &str) -> bool {
+ let path = cx.get_def_path(did);
+ // libc is meant to be used as a flat list of names, but they're all actually defined in different
+ // modules based on the target platform. Ignore everything but crate name and the item name.
+ path.first().map_or(false, |s| s.as_str() == "libc") && path.last().map_or(false, |s| s.as_str() == name)
+}
+
pub fn match_panic_call(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
if let ExprKind::Call(func, [arg]) = expr.kind {
expr_path_res(cx, func)
matches!(get_parent_node(tcx, expr.hir_id), Some(Node::Block(..)))
}
+pub fn std_or_core(cx: &LateContext<'_>) -> Option<&'static str> {
+ if !is_no_std_crate(cx) {
+ Some("std")
+ } else if !is_no_core_crate(cx) {
+ Some("core")
+ } else {
+ None
+ }
+}
+
pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool {
cx.tcx.hir().attrs(hir::CRATE_HIR_ID).iter().any(|attr| {
if let ast::AttrKind::Normal(ref attr, _) = attr.kind {
})
}
+pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool {
+ cx.tcx.hir().attrs(hir::CRATE_HIR_ID).iter().any(|attr| {
+ if let ast::AttrKind::Normal(ref attr, _) = attr.kind {
+ attr.path == sym::no_core
+ } else {
+ false
+ }
+ })
+}
+
/// Check if parent of a hir node is a trait implementation block.
/// For example, `f` in
/// ```rust,ignore
1,46,0 { CONST_IF_MATCH }
1,45,0 { STR_STRIP_PREFIX }
1,43,0 { LOG2_10, LOG10_2 }
- 1,42,0 { MATCHES_MACRO }
+ 1,42,0 { MATCHES_MACRO, SLICE_PATTERNS }
1,41,0 { RE_REBALANCING_COHERENCE, RESULT_MAP_OR_ELSE }
1,40,0 { MEM_TAKE, NON_EXHAUSTIVE, OPTION_AS_DEREF }
1,38,0 { POINTER_CAST }
1,36,0 { ITERATOR_COPIED }
1,35,0 { OPTION_COPIED, RANGE_CONTAINS }
1,34,0 { TRY_FROM }
- 1,30,0 { ITERATOR_FIND_MAP }
+ 1,30,0 { ITERATOR_FIND_MAP, TOOL_ATTRIBUTES }
+ 1,28,0 { FROM_BOOL }
1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST }
1,16,0 { STR_REPEAT }
}
pub(super) const BEGIN_PANIC: [&str; 3] = ["std", "panicking", "begin_panic"];
/// Preferably use the diagnostic item `sym::Borrow` where possible
pub const BORROW_TRAIT: [&str; 3] = ["core", "borrow", "Borrow"];
+pub const BORROW_MUT_TRAIT: [&str; 3] = ["core", "borrow", "BorrowMut"];
pub const BTREEMAP_CONTAINS_KEY: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "contains_key"];
pub const BTREEMAP_ENTRY: [&str; 6] = ["alloc", "collections", "btree", "map", "entry", "Entry"];
pub const BTREEMAP_INSERT: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "insert"];
pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"];
#[cfg(feature = "internal-lints")]
pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"];
-pub const LIBC_STRLEN: [&str; 2] = ["libc", "strlen"];
#[cfg(any(feature = "internal-lints", feature = "metadata-collector-lint"))]
pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"];
pub const MEM_DISCRIMINANT: [&str; 3] = ["core", "mem", "discriminant"];
pub(super) const PANICKING_PANIC_FMT: [&str; 3] = ["core", "panicking", "panic_fmt"];
pub(super) const PANICKING_PANIC_STR: [&str; 3] = ["core", "panicking", "panic_str"];
pub(super) const PANIC_ANY: [&str; 3] = ["std", "panic", "panic_any"];
+pub const PARKING_LOT_RAWMUTEX: [&str; 3] = ["parking_lot", "raw_mutex", "RawMutex"];
+pub const PARKING_LOT_RAWRWLOCK: [&str; 3] = ["parking_lot", "raw_rwlock", "RawRwLock"];
pub const PARKING_LOT_MUTEX_GUARD: [&str; 2] = ["parking_lot", "MutexGuard"];
pub const PARKING_LOT_RWLOCK_READ_GUARD: [&str; 2] = ["parking_lot", "RwLockReadGuard"];
pub const PARKING_LOT_RWLOCK_WRITE_GUARD: [&str; 2] = ["parking_lot", "RwLockWriteGuard"];
pub const WRITE_MACRO: [&str; 3] = ["core", "macros", "write"];
#[allow(clippy::invalid_paths)] // `check_path` does not seem to work for macros
pub const WRITELN_MACRO: [&str; 3] = ["core", "macros", "writeln"];
+pub const PTR_NON_NULL: [&str; 4] = ["core", "ptr", "non_null", "NonNull"];
use crate::source::snippet;
+use crate::visitors::expr_visitor_no_bodies;
use crate::{path_to_local_id, strip_pat_refs};
-use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
-use rustc_hir::{Body, BodyId, Expr, ExprKind, HirId, PatKind};
+use rustc_hir::intravisit::Visitor;
+use rustc_hir::{Body, BodyId, ExprKind, HirId, PatKind};
use rustc_lint::LateContext;
-use rustc_middle::hir::map::Map;
use rustc_span::Span;
use std::borrow::Cow;
replace: &[(&'static str, &'static str)],
body: &'tcx Body<'_>,
) -> Option<Vec<(Span, Cow<'static, str>)>> {
- let mut visitor = PtrCloneVisitor {
- cx,
- id,
- replace,
- spans: vec![],
- abort: false,
- };
- visitor.visit_body(body);
- if visitor.abort { None } else { Some(visitor.spans) }
-}
-
-struct PtrCloneVisitor<'a, 'tcx> {
- cx: &'a LateContext<'tcx>,
- id: HirId,
- replace: &'a [(&'static str, &'static str)],
- spans: Vec<(Span, Cow<'static, str>)>,
- abort: bool,
-}
-
-impl<'a, 'tcx> Visitor<'tcx> for PtrCloneVisitor<'a, 'tcx> {
- type Map = Map<'tcx>;
-
- fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
- if self.abort {
- return;
+ let mut abort = false;
+ let mut spans = Vec::new();
+ expr_visitor_no_bodies(|expr| {
+ if abort {
+ return false;
}
if let ExprKind::MethodCall(seg, _, [recv], _) = expr.kind {
- if path_to_local_id(recv, self.id) {
+ if path_to_local_id(recv, id) {
if seg.ident.name.as_str() == "capacity" {
- self.abort = true;
- return;
+ abort = true;
+ return false;
}
- for &(fn_name, suffix) in self.replace {
+ for &(fn_name, suffix) in replace {
if seg.ident.name.as_str() == fn_name {
- self.spans.push((expr.span, snippet(self.cx, recv.span, "_") + suffix));
- return;
+ spans.push((expr.span, snippet(cx, recv.span, "_") + suffix));
+ return false;
}
}
}
}
- walk_expr(self, expr);
- }
-
- fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
- NestedVisitorMap::None
- }
+ !abort
+ })
+ .visit_body(body);
+ if abort { None } else { Some(spans) }
}
fn reindent_multiline_inner(s: &str, ignore_first: bool, indent: Option<usize>, ch: char) -> String {
let x = s
.lines()
- .skip(ignore_first as usize)
+ .skip(usize::from(ignore_first))
.filter_map(|l| {
if l.is_empty() {
None
.join("\n")
}
-/// Converts a span to a code snippet if available, otherwise use default.
+/// Converts a span to a code snippet if available, otherwise returns the default.
///
/// This is useful if you want to provide suggestions for your lint or more generally, if you want
-/// to convert a given `Span` to a `str`.
+/// to convert a given `Span` to a `str`. To create suggestions consider using
+/// [`snippet_with_applicability`] to ensure that the applicability stays correct.
///
/// # Example
/// ```rust,ignore
-/// snippet(cx, expr.span, "..")
+/// // Given two spans one for `value` and one for the `init` expression.
+/// let value = Vec::new();
+/// // ^^^^^ ^^^^^^^^^^
+/// // span1 span2
+///
+/// // The snipped call would return the corresponding code snippet
+/// snippet(cx, span1, "..") // -> "value"
+/// snippet(cx, span2, "..") // -> "Vec::new()"
/// ```
pub fn snippet<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> {
snippet_opt(cx, span).map_or_else(|| Cow::Borrowed(default), From::from)
//! Contains utility functions to generate suggestions.
#![deny(clippy::missing_docs_in_private_items)]
-use crate::higher;
-use crate::source::{snippet, snippet_opt, snippet_with_context, snippet_with_macro_callsite};
+use crate::source::{
+ snippet, snippet_opt, snippet_with_applicability, snippet_with_context, snippet_with_macro_callsite,
+};
+use crate::{get_parent_expr_for_hir, higher};
use rustc_ast::util::parser::AssocOp;
use rustc_ast::{ast, token};
use rustc_ast_pretty::pprust::token_kind_to_string;
use rustc_errors::Applicability;
use rustc_hir as hir;
+use rustc_hir::{ExprKind, HirId, MutTy, TyKind};
+use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::{EarlyContext, LateContext, LintContext};
-use rustc_span::source_map::{CharPos, Span};
-use rustc_span::{BytePos, Pos, SyntaxContext};
+use rustc_middle::hir::place::ProjectionKind;
+use rustc_middle::mir::{FakeReadCause, Mutability};
+use rustc_middle::ty;
+use rustc_span::source_map::{BytePos, CharPos, Pos, Span, SyntaxContext};
+use rustc_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
use std::borrow::Cow;
use std::convert::TryInto;
use std::fmt::Display;
+use std::iter;
use std::ops::{Add, Neg, Not, Sub};
/// A helper type to build suggestion correctly handling parentheses.
}
}
+/// Suggestion results for handling closure
+/// args dereferencing and borrowing
+pub struct DerefClosure {
+ /// confidence on the built suggestion
+ pub applicability: Applicability,
+ /// gradually built suggestion
+ pub suggestion: String,
+}
+
+/// Build suggestion gradually by handling closure arg specific usages,
+/// such as explicit deref and borrowing cases.
+/// Returns `None` if no such use cases have been triggered in closure body
+///
+/// note: this only works on single line immutable closures with exactly one input parameter.
+pub fn deref_closure_args<'tcx>(cx: &LateContext<'_>, closure: &'tcx hir::Expr<'_>) -> Option<DerefClosure> {
+ if let hir::ExprKind::Closure(_, fn_decl, body_id, ..) = closure.kind {
+ let closure_body = cx.tcx.hir().body(body_id);
+ // is closure arg a type annotated double reference (i.e.: `|x: &&i32| ...`)
+ // a type annotation is present if param `kind` is different from `TyKind::Infer`
+ let closure_arg_is_type_annotated_double_ref = if let TyKind::Rptr(_, MutTy { ty, .. }) = fn_decl.inputs[0].kind
+ {
+ matches!(ty.kind, TyKind::Rptr(_, MutTy { .. }))
+ } else {
+ false
+ };
+
+ let mut visitor = DerefDelegate {
+ cx,
+ closure_span: closure.span,
+ closure_arg_is_type_annotated_double_ref,
+ next_pos: closure.span.lo(),
+ suggestion_start: String::new(),
+ applicability: Applicability::MaybeIncorrect,
+ };
+
+ let fn_def_id = cx.tcx.hir().local_def_id(closure.hir_id);
+ cx.tcx.infer_ctxt().enter(|infcx| {
+ ExprUseVisitor::new(&mut visitor, &infcx, fn_def_id, cx.param_env, cx.typeck_results())
+ .consume_body(closure_body);
+ });
+
+ if !visitor.suggestion_start.is_empty() {
+ return Some(DerefClosure {
+ applicability: visitor.applicability,
+ suggestion: visitor.finish(),
+ });
+ }
+ }
+ None
+}
+
+/// Visitor struct used for tracking down
+/// dereferencing and borrowing of closure's args
+struct DerefDelegate<'a, 'tcx> {
+ /// The late context of the lint
+ cx: &'a LateContext<'tcx>,
+ /// The span of the input closure to adapt
+ closure_span: Span,
+ /// Indicates if the arg of the closure is a type annotated double reference
+ closure_arg_is_type_annotated_double_ref: bool,
+ /// last position of the span to gradually build the suggestion
+ next_pos: BytePos,
+ /// starting part of the gradually built suggestion
+ suggestion_start: String,
+ /// confidence on the built suggestion
+ applicability: Applicability,
+}
+
+impl DerefDelegate<'_, 'tcx> {
+ /// build final suggestion:
+ /// - create the ending part of suggestion
+ /// - concatenate starting and ending parts
+ /// - potentially remove needless borrowing
+ pub fn finish(&mut self) -> String {
+ let end_span = Span::new(self.next_pos, self.closure_span.hi(), self.closure_span.ctxt(), None);
+ let end_snip = snippet_with_applicability(self.cx, end_span, "..", &mut self.applicability);
+ let sugg = format!("{}{}", self.suggestion_start, end_snip);
+ if self.closure_arg_is_type_annotated_double_ref {
+ sugg.replacen('&', "", 1)
+ } else {
+ sugg
+ }
+ }
+
+ /// 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 (call_args, inputs) = match parent_expr.kind {
+ ExprKind::MethodCall(_, _, call_args, _) => {
+ if let Some(method_did) = self.cx.typeck_results().type_dependent_def_id(parent_expr.hir_id) {
+ (call_args, self.cx.tcx.fn_sig(method_did).skip_binder().inputs())
+ } else {
+ return false;
+ }
+ },
+ ExprKind::Call(func, call_args) => {
+ let typ = self.cx.typeck_results().expr_ty(func);
+ (call_args, typ.fn_sig(self.cx.tcx).skip_binder().inputs())
+ },
+ _ => return false,
+ };
+
+ iter::zip(call_args, inputs)
+ .any(|(arg, ty)| arg.hir_id == cmt_hir_id && matches!(ty.kind(), ty::Ref(_, inner, _) if inner.is_ref()))
+ }
+}
+
+impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> {
+ fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {}
+
+ #[allow(clippy::too_many_lines)]
+ fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId, _: ty::BorrowKind) {
+ if let PlaceBase::Local(id) = cmt.place.base {
+ let map = self.cx.tcx.hir();
+ let span = map.span(cmt.hir_id);
+ let start_span = Span::new(self.next_pos, span.lo(), span.ctxt(), None);
+ let mut start_snip = snippet_with_applicability(self.cx, start_span, "..", &mut self.applicability);
+
+ // identifier referring to the variable currently triggered (i.e.: `fp`)
+ let ident_str = map.name(id).to_string();
+ // full identifier that includes projection (i.e.: `fp.field`)
+ let ident_str_with_proj = snippet(self.cx, span, "..").to_string();
+
+ if cmt.place.projections.is_empty() {
+ // handle item without any projection, that needs an explicit borrowing
+ // i.e.: suggest `&x` instead of `x`
+ self.suggestion_start.push_str(&format!("{}&{}", start_snip, ident_str));
+ } else {
+ // cases where a parent `Call` or `MethodCall` is using the item
+ // i.e.: suggest `.contains(&x)` for `.find(|x| [1, 2, 3].contains(x)).is_none()`
+ //
+ // Note about method calls:
+ // - compiler automatically dereference references if the target type is a reference (works also for
+ // function call)
+ // - `self` arguments in the case of `x.is_something()` are also automatically (de)referenced, and
+ // no projection should be suggested
+ if let Some(parent_expr) = get_parent_expr_for_hir(self.cx, cmt.hir_id) {
+ 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 => {
+ self.suggestion_start
+ .push_str(&format!("{}{}", 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 @ ..], _) => {
+ let expr = self.cx.tcx.hir().expect_expr(cmt.hir_id);
+ let arg_ty_kind = self.cx.typeck_results().expr_ty(expr).kind();
+
+ if matches!(arg_ty_kind, ty::Ref(_, _, Mutability::Not)) {
+ // suggest ampersand if call function is taking args by double reference
+ let takes_arg_by_double_ref =
+ self.func_takes_arg_by_double_ref(parent_expr, cmt.hir_id);
+
+ // compiler will automatically dereference field or index projection, so no need
+ // to suggest ampersand, but full identifier that includes projection is required
+ let has_field_or_index_projection =
+ cmt.place.projections.iter().any(|proj| {
+ matches!(proj.kind, ProjectionKind::Field(..) | ProjectionKind::Index)
+ });
+
+ // no need to bind again if the function doesn't take arg by double ref
+ // and if the item is already a double ref
+ let ident_sugg = if !call_args.is_empty()
+ && !takes_arg_by_double_ref
+ && (self.closure_arg_is_type_annotated_double_ref || has_field_or_index_projection)
+ {
+ let ident = if has_field_or_index_projection {
+ ident_str_with_proj
+ } else {
+ ident_str
+ };
+ format!("{}{}", start_snip, ident)
+ } else {
+ format!("{}&{}", start_snip, ident_str)
+ };
+ self.suggestion_start.push_str(&ident_sugg);
+ self.next_pos = span.hi();
+ return;
+ }
+
+ self.applicability = Applicability::Unspecified;
+ },
+ _ => (),
+ }
+ }
+
+ let mut replacement_str = ident_str;
+ let mut projections_handled = false;
+ cmt.place.projections.iter().enumerate().for_each(|(i, proj)| {
+ match proj.kind {
+ // Field projection like `|v| v.foo`
+ // no adjustment needed here, as field projections are handled by the compiler
+ ProjectionKind::Field(..) => match cmt.place.ty_before_projection(i).kind() {
+ ty::Adt(..) | ty::Tuple(_) => {
+ replacement_str = ident_str_with_proj.clone();
+ projections_handled = true;
+ },
+ _ => (),
+ },
+ // Index projection like `|x| foo[x]`
+ // the index is dropped so we can't get it to build the suggestion,
+ // so the span is set-up again to get more code, using `span.hi()` (i.e.: `foo[x]`)
+ // instead of `span.lo()` (i.e.: `foo`)
+ ProjectionKind::Index => {
+ let start_span = Span::new(self.next_pos, span.hi(), span.ctxt(), None);
+ start_snip = snippet_with_applicability(self.cx, start_span, "..", &mut self.applicability);
+ replacement_str.clear();
+ projections_handled = true;
+ },
+ // note: unable to trigger `Subslice` kind in tests
+ ProjectionKind::Subslice => (),
+ ProjectionKind::Deref => {
+ // Explicit derefs are typically handled later on, but
+ // some items do not need explicit deref, such as array accesses,
+ // so we mark them as already processed
+ // i.e.: don't suggest `*sub[1..4].len()` for `|sub| sub[1..4].len() == 3`
+ if let ty::Ref(_, inner, _) = cmt.place.ty_before_projection(i).kind() {
+ if matches!(inner.kind(), ty::Ref(_, innermost, _) if innermost.is_array()) {
+ projections_handled = true;
+ }
+ }
+ },
+ }
+ });
+
+ // handle `ProjectionKind::Deref` by removing one explicit deref
+ // if no special case was detected (i.e.: suggest `*x` instead of `**x`)
+ if !projections_handled {
+ let last_deref = cmt
+ .place
+ .projections
+ .iter()
+ .rposition(|proj| proj.kind == ProjectionKind::Deref);
+
+ if let Some(pos) = last_deref {
+ let mut projections = cmt.place.projections.clone();
+ projections.truncate(pos);
+
+ for item in projections {
+ if item.kind == ProjectionKind::Deref {
+ replacement_str = format!("*{}", replacement_str);
+ }
+ }
+ }
+ }
+
+ self.suggestion_start
+ .push_str(&format!("{}{}", start_snip, replacement_str));
+ }
+ self.next_pos = span.hi();
+ }
+ }
+
+ fn mutate(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {}
+
+ fn fake_read(&mut self, _: rustc_typeck::expr_use_visitor::Place<'tcx>, _: FakeReadCause, _: HirId) {}
+}
+
#[cfg(test)]
mod test {
use super::Sugg;
use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::LateContext;
use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
-use rustc_middle::ty::{self, AdtDef, IntTy, Ty, TyCtxt, TypeFoldable, UintTy};
-use rustc_span::sym;
-use rustc_span::symbol::{Ident, Symbol};
-use rustc_span::DUMMY_SP;
+use rustc_middle::ty::{self, AdtDef, IntTy, Predicate, Ty, TyCtxt, TypeFoldable, UintTy};
+use rustc_span::symbol::Ident;
+use rustc_span::{sym, Span, Symbol, DUMMY_SP};
use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::traits::query::normalize::AtExt;
+use std::iter;
use crate::{match_def_path, must_use_attr};
+// Checks if the given type implements copy.
pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
ty.is_copy_modulo_regions(cx.tcx.at(DUMMY_SP), cx.param_env)
}
/// Checks whether a type implements a trait.
/// The function returns false in case the type contains an inference variable.
-/// See also [`get_trait_def_id`](super::get_trait_def_id).
+///
+/// See:
+/// * [`get_trait_def_id`](super::get_trait_def_id) to get a trait [`DefId`].
+/// * [Common tools for writing lints] for an example how to use this function and other options.
+///
+/// [Common tools for writing lints]: https://github.com/rust-lang/rust-clippy/blob/master/doc/common_tools_writing_lints.md#checking-if-a-type-implements-a-specific-trait
pub fn implements_trait<'tcx>(
cx: &LateContext<'tcx>,
ty: Ty<'tcx>,
}
}
-/// Checks if the type is equal to a diagnostic item
+/// Checks if the type is equal to a diagnostic item. To check if a type implements a
+/// trait marked with a diagnostic item use [`implements_trait`].
+///
+/// For a further exploitation what diagnostic items are see [diagnostic items] in
+/// rustc-dev-guide.
+///
+/// ---
///
/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem`
+///
+/// [Diagnostic Items]: https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html
pub fn is_type_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool {
match ty.kind() {
ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did),
_ => false,
}
}
+
+/// Gets an iterator over all predicates which apply to the given item.
+pub fn all_predicates_of(tcx: TyCtxt<'_>, id: DefId) -> impl Iterator<Item = &(Predicate<'_>, Span)> {
+ let mut next_id = Some(id);
+ iter::from_fn(move || {
+ next_id.take().map(|id| {
+ let preds = tcx.predicates_of(id);
+ next_id = preds.parent;
+ preds.predicates.iter()
+ })
+ })
+ .flatten()
+}
use crate as utils;
+use crate::visitors::{expr_visitor, expr_visitor_no_bodies};
use rustc_hir as hir;
-use rustc_hir::intravisit;
-use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
+use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::HirIdSet;
use rustc_hir::{Expr, ExprKind, HirId};
use rustc_infer::infer::TyCtxtInferExt;
}
}
-struct ReturnBreakContinueMacroVisitor {
- seen_return_break_continue: bool,
-}
-
-impl ReturnBreakContinueMacroVisitor {
- fn new() -> ReturnBreakContinueMacroVisitor {
- ReturnBreakContinueMacroVisitor {
- seen_return_break_continue: false,
- }
- }
-}
-
-impl<'tcx> Visitor<'tcx> for ReturnBreakContinueMacroVisitor {
- type Map = Map<'tcx>;
- fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
- NestedVisitorMap::None
- }
-
- fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
- if self.seen_return_break_continue {
- // No need to look farther if we've already seen one of them
- return;
+pub fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool {
+ let mut seen_return_break_continue = false;
+ expr_visitor_no_bodies(|ex| {
+ if seen_return_break_continue {
+ return false;
}
match &ex.kind {
ExprKind::Ret(..) | ExprKind::Break(..) | ExprKind::Continue(..) => {
- self.seen_return_break_continue = true;
+ seen_return_break_continue = true;
},
// Something special could be done here to handle while or for loop
// desugaring, as this will detect a break if there's a while loop
// or a for loop inside the expression.
_ => {
- if utils::in_macro(ex.span) {
- self.seen_return_break_continue = true;
- } else {
- rustc_hir::intravisit::walk_expr(self, ex);
+ if ex.span.from_expansion() {
+ seen_return_break_continue = true;
}
},
}
- }
-}
-
-pub fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool {
- let mut recursive_visitor = ReturnBreakContinueMacroVisitor::new();
- recursive_visitor.visit_expr(expression);
- recursive_visitor.seen_return_break_continue
-}
-
-pub struct UsedAfterExprVisitor<'a, 'tcx> {
- cx: &'a LateContext<'tcx>,
- expr: &'tcx Expr<'tcx>,
- definition: HirId,
- past_expr: bool,
- used_after_expr: bool,
-}
-impl<'a, 'tcx> UsedAfterExprVisitor<'a, 'tcx> {
- pub fn is_found(cx: &'a LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
- utils::path_to_local(expr).map_or(false, |definition| {
- let mut visitor = UsedAfterExprVisitor {
- cx,
- expr,
- definition,
- past_expr: false,
- used_after_expr: false,
- };
- utils::get_enclosing_block(cx, definition).map_or(false, |block| {
- visitor.visit_block(block);
- visitor.used_after_expr
- })
- })
- }
-}
-
-impl<'a, 'tcx> intravisit::Visitor<'tcx> for UsedAfterExprVisitor<'a, 'tcx> {
- type Map = Map<'tcx>;
-
- fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
- NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
- }
-
- fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
- if self.used_after_expr {
- return;
+ !seen_return_break_continue
+ })
+ .visit_expr(expression);
+ seen_return_break_continue
+}
+
+pub fn local_used_after_expr(cx: &LateContext<'_>, local_id: HirId, after: &Expr<'_>) -> bool {
+ let Some(block) = utils::get_enclosing_block(cx, local_id) else { return false };
+ let mut used_after_expr = false;
+ let mut past_expr = false;
+ expr_visitor(cx, |expr| {
+ if used_after_expr {
+ return false;
}
- if expr.hir_id == self.expr.hir_id {
- self.past_expr = true;
- } else if self.past_expr && utils::path_to_local_id(expr, self.definition) {
- self.used_after_expr = true;
- } else {
- intravisit::walk_expr(self, expr);
+ if expr.hir_id == after.hir_id {
+ past_expr = true;
+ } else if past_expr && utils::path_to_local_id(expr, local_id) {
+ used_after_expr = true;
}
- }
+ !used_after_expr
+ })
+ .visit_block(block);
+ used_after_expr
}
use crate::path_to_local_id;
use rustc_hir as hir;
-use rustc_hir::intravisit::{self, walk_expr, ErasedMap, NestedVisitorMap, Visitor};
-use rustc_hir::{def::Res, Arm, Block, Body, BodyId, Destination, Expr, ExprKind, HirId, Stmt};
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::intravisit::{self, walk_block, walk_expr, NestedVisitorMap, Visitor};
+use rustc_hir::{
+ Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, Stmt, UnOp, Unsafety,
+};
use rustc_lint::LateContext;
use rustc_middle::hir::map::Map;
-use std::ops::ControlFlow;
+use rustc_middle::ty;
-/// returns `true` if expr contains match expr desugared from try
-fn contains_try(expr: &hir::Expr<'_>) -> bool {
- struct TryFinder {
- found: bool,
+/// Convenience method for creating a `Visitor` with just `visit_expr` overridden and nested
+/// bodies (i.e. closures) are visited.
+/// If the callback returns `true`, the expr just provided to the callback is walked.
+#[must_use]
+pub fn expr_visitor<'tcx>(cx: &LateContext<'tcx>, f: impl FnMut(&'tcx Expr<'tcx>) -> bool) -> impl Visitor<'tcx> {
+ struct V<'tcx, F> {
+ hir: Map<'tcx>,
+ f: F,
}
+ impl<'tcx, F: FnMut(&'tcx Expr<'tcx>) -> bool> Visitor<'tcx> for V<'tcx, F> {
+ type Map = Map<'tcx>;
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::OnlyBodies(self.hir)
+ }
- impl<'hir> intravisit::Visitor<'hir> for TryFinder {
- type Map = Map<'hir>;
+ fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
+ if (self.f)(expr) {
+ walk_expr(self, expr);
+ }
+ }
+ }
+ V { hir: cx.tcx.hir(), f }
+}
- fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
- intravisit::NestedVisitorMap::None
+/// Convenience method for creating a `Visitor` with just `visit_expr` overridden and nested
+/// bodies (i.e. closures) are not visited.
+/// If the callback returns `true`, the expr just provided to the callback is walked.
+#[must_use]
+pub fn expr_visitor_no_bodies<'tcx>(f: impl FnMut(&'tcx Expr<'tcx>) -> bool) -> impl Visitor<'tcx> {
+ struct V<F>(F);
+ impl<'tcx, F: FnMut(&'tcx Expr<'tcx>) -> bool> Visitor<'tcx> for V<F> {
+ type Map = intravisit::ErasedMap<'tcx>;
+ fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
+ NestedVisitorMap::None
}
- fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
- if self.found {
- return;
- }
- match expr.kind {
- hir::ExprKind::Match(_, _, hir::MatchSource::TryDesugar) => self.found = true,
- _ => intravisit::walk_expr(self, expr),
+ fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
+ if (self.0)(e) {
+ walk_expr(self, e);
}
}
}
+ V(f)
+}
- let mut visitor = TryFinder { found: false };
- visitor.visit_expr(expr);
- visitor.found
+/// returns `true` if expr contains match expr desugared from try
+fn contains_try(expr: &hir::Expr<'_>) -> bool {
+ let mut found = false;
+ expr_visitor_no_bodies(|e| {
+ if !found {
+ found = matches!(e.kind, hir::ExprKind::Match(_, _, hir::MatchSource::TryDesugar));
+ }
+ !found
+ })
+ .visit_expr(expr);
+ found
}
pub fn find_all_ret_expressions<'hir, F>(_cx: &LateContext<'_>, expr: &'hir hir::Expr<'hir>, callback: F) -> bool
// }
// }
-/// Calls the given function for each break expression.
-pub fn visit_break_exprs<'tcx>(
- node: impl Visitable<'tcx>,
- f: impl FnMut(&'tcx Expr<'tcx>, Destination, Option<&'tcx Expr<'tcx>>),
-) {
- struct V<F>(F);
- impl<'tcx, F: FnMut(&'tcx Expr<'tcx>, Destination, Option<&'tcx Expr<'tcx>>)> Visitor<'tcx> for V<F> {
- type Map = ErasedMap<'tcx>;
- fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
- NestedVisitorMap::None
+/// Checks if the given resolved path is used in the given body.
+pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool {
+ let mut found = false;
+ expr_visitor(cx, |e| {
+ if found {
+ return false;
}
- fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
- if let ExprKind::Break(dest, sub_expr) = e.kind {
- self.0(e, dest, sub_expr);
+ if let ExprKind::Path(p) = &e.kind {
+ if cx.qpath_res(p, e.hir_id) == res {
+ found = true;
}
- walk_expr(self, e);
}
- }
+ !found
+ })
+ .visit_expr(&cx.tcx.hir().body(body).value);
+ found
+}
- node.visit(&mut V(f));
+/// Checks if the given local is used.
+pub fn is_local_used(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id: HirId) -> bool {
+ let mut is_used = false;
+ let mut visitor = expr_visitor(cx, |expr| {
+ if !is_used {
+ is_used = path_to_local_id(expr, id);
+ }
+ !is_used
+ });
+ visitable.visit(&mut visitor);
+ drop(visitor);
+ is_used
}
-/// Checks if the given resolved path is used in the given body.
-pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool {
+/// Checks if the given expression is a constant.
+pub fn is_const_evaluatable(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
struct V<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
- res: Res,
- found: bool,
+ is_const: bool,
}
- impl Visitor<'tcx> for V<'_, 'tcx> {
+ impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
type Map = Map<'tcx>;
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
}
fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
- if self.found {
+ if !self.is_const {
return;
}
+ match e.kind {
+ ExprKind::ConstBlock(_) => return,
+ ExprKind::Call(
+ &Expr {
+ kind: ExprKind::Path(ref p),
+ hir_id,
+ ..
+ },
+ _,
+ ) if self
+ .cx
+ .qpath_res(p, hir_id)
+ .opt_def_id()
+ .map_or(false, |id| self.cx.tcx.is_const_fn_raw(id)) => {},
+ ExprKind::MethodCall(..)
+ if self
+ .cx
+ .typeck_results()
+ .type_dependent_def_id(e.hir_id)
+ .map_or(false, |id| self.cx.tcx.is_const_fn_raw(id)) => {},
+ ExprKind::Binary(_, lhs, rhs)
+ if self.cx.typeck_results().expr_ty(lhs).peel_refs().is_primitive_ty()
+ && self.cx.typeck_results().expr_ty(rhs).peel_refs().is_primitive_ty() => {},
+ ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_ref() => (),
+ ExprKind::Unary(_, e) if self.cx.typeck_results().expr_ty(e).peel_refs().is_primitive_ty() => (),
+ ExprKind::Index(base, _)
+ if matches!(
+ self.cx.typeck_results().expr_ty(base).peel_refs().kind(),
+ ty::Slice(_) | ty::Array(..)
+ ) => {},
+ ExprKind::Path(ref p)
+ if matches!(
+ self.cx.qpath_res(p, e.hir_id),
+ Res::Def(
+ DefKind::Const
+ | DefKind::AssocConst
+ | DefKind::AnonConst
+ | DefKind::ConstParam
+ | DefKind::Ctor(..)
+ | DefKind::Fn
+ | DefKind::AssocFn,
+ _
+ ) | Res::SelfCtor(_)
+ ) => {},
- if let ExprKind::Path(p) = &e.kind {
- if self.cx.qpath_res(p, e.hir_id) == self.res {
- self.found = true;
- }
- } else {
- walk_expr(self, e);
+ ExprKind::AddrOf(..)
+ | ExprKind::Array(_)
+ | ExprKind::Block(..)
+ | ExprKind::Cast(..)
+ | ExprKind::DropTemps(_)
+ | ExprKind::Field(..)
+ | ExprKind::If(..)
+ | ExprKind::Let(..)
+ | ExprKind::Lit(_)
+ | ExprKind::Match(..)
+ | ExprKind::Repeat(..)
+ | ExprKind::Struct(..)
+ | ExprKind::Tup(_)
+ | ExprKind::Type(..) => (),
+
+ _ => {
+ self.is_const = false;
+ return;
+ },
}
+ walk_expr(self, e);
}
}
- let mut v = V { cx, res, found: false };
- v.visit_expr(&cx.tcx.hir().body(body).value);
- v.found
+ let mut v = V { cx, is_const: true };
+ v.visit_expr(e);
+ v.is_const
}
-/// Calls the given function for each usage of the given local.
-pub fn for_each_local_usage<'tcx, B>(
- cx: &LateContext<'tcx>,
- visitable: impl Visitable<'tcx>,
- id: HirId,
- f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
-) -> ControlFlow<B> {
- struct V<'tcx, B, F> {
- map: Map<'tcx>,
- id: HirId,
- f: F,
- res: ControlFlow<B>,
+/// Checks if the given expression performs an unsafe operation outside of an unsafe block.
+pub fn is_expr_unsafe(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
+ struct V<'a, 'tcx> {
+ cx: &'a LateContext<'tcx>,
+ is_unsafe: bool,
}
- impl<'tcx, B, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>> Visitor<'tcx> for V<'tcx, B, F> {
+ impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
type Map = Map<'tcx>;
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
- NestedVisitorMap::OnlyBodies(self.map)
+ NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
}
-
fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
- if self.res.is_continue() {
- if path_to_local_id(e, self.id) {
- self.res = (self.f)(e);
- } else {
- walk_expr(self, e);
- }
+ if self.is_unsafe {
+ return;
+ }
+ match e.kind {
+ ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_unsafe_ptr() => {
+ self.is_unsafe = true;
+ },
+ ExprKind::MethodCall(..)
+ if self
+ .cx
+ .typeck_results()
+ .type_dependent_def_id(e.hir_id)
+ .map_or(false, |id| self.cx.tcx.fn_sig(id).unsafety() == Unsafety::Unsafe) =>
+ {
+ self.is_unsafe = true;
+ },
+ ExprKind::Call(func, _) => match *self.cx.typeck_results().expr_ty(func).peel_refs().kind() {
+ ty::FnDef(id, _) if self.cx.tcx.fn_sig(id).unsafety() == Unsafety::Unsafe => self.is_unsafe = true,
+ ty::FnPtr(sig) if sig.unsafety() == Unsafety::Unsafe => self.is_unsafe = true,
+ _ => walk_expr(self, e),
+ },
+ ExprKind::Path(ref p)
+ if self
+ .cx
+ .qpath_res(p, e.hir_id)
+ .opt_def_id()
+ .map_or(false, |id| self.cx.tcx.is_mutable_static(id)) =>
+ {
+ self.is_unsafe = true;
+ },
+ _ => walk_expr(self, e),
+ }
+ }
+ fn visit_block(&mut self, b: &'tcx Block<'_>) {
+ if !matches!(b.rules, BlockCheckMode::UnsafeBlock(_)) {
+ walk_block(self, b);
+ }
+ }
+ fn visit_nested_item(&mut self, id: ItemId) {
+ if let ItemKind::Impl(i) = &self.cx.tcx.hir().item(id).kind {
+ self.is_unsafe = i.unsafety == Unsafety::Unsafe;
}
}
}
-
- let mut v = V {
- map: cx.tcx.hir(),
- id,
- f,
- res: ControlFlow::CONTINUE,
- };
- visitable.visit(&mut v);
- v.res
-}
-
-/// Checks if the given local is used.
-pub fn is_local_used(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id: HirId) -> bool {
- for_each_local_usage(cx, visitable, id, |_| ControlFlow::BREAK).is_break()
+ let mut v = V { cx, is_unsafe: false };
+ v.visit_expr(e);
+ v.is_unsafe
}
your local modifications, run
```
-env __CLIPPY_INTERNAL_TESTS=true cargo run --bin clippy-driver -- -L ./target/debug input.rs
+cargo dev lint input.rs
```
from the working copy root. With tests in place, let's have a look at
/// ```rust
/// // example code
/// ```
+ #[clippy::version = "1.29.0"]
pub FOO_FUNCTIONS,
pedantic,
"function named `foo`, which is not a descriptive name"
section. This is the default documentation style and will be displayed
[like this][example_lint_page]. To render and open this documentation locally
in a browser, run `cargo dev serve`.
+* The `#[clippy::version]` attribute will be rendered as part of the lint documentation.
+ The value should be set to the current Rust version that the lint is developed in,
+ it can be retrieved by running `rustc -vV` in the rust-clippy directory. The version
+ is listed under *release*. (Use the version without the `-nightly`) suffix.
* `FOO_FUNCTIONS` is the name of our lint. Be sure to follow the
[lint naming guidelines][lint_naming] here when naming your lint.
In short, the name should state the thing that is being checked for and
/// // Good
/// Insert a short example of improved code that doesn't trigger the lint
/// ```
+ #[clippy::version = "1.29.0"]
pub FOO_FUNCTIONS,
pedantic,
"function named `foo`, which is not a descriptive name"
Here are some pointers to things you are likely going to need for every lint:
* [Clippy utils][utils] - Various helper functions. Maybe the function you need
- is already in here (`implements_trait`, `match_def_path`, `snippet`, etc)
+ is already in here ([`is_type_diagnostic_item`], [`implements_trait`], [`snippet`], etc)
* [Clippy diagnostics][diagnostics]
* [The `if_chain` macro][if_chain]
* [`from_expansion`][from_expansion] and [`in_external_macro`][in_external_macro]
get away with copying things from existing similar lints. If you are stuck,
don't hesitate to ask on [Zulip] or in the issue/PR.
-[utils]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_utils/src/lib.rs
+[utils]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/index.html
+[`is_type_diagnostic_item`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/ty/fn.is_type_diagnostic_item.html
+[`implements_trait`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/ty/fn.implements_trait.html
+[`snippet`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/source/fn.snippet.html
[if_chain]: https://docs.rs/if_chain/*/if_chain/
[from_expansion]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html#method.from_expansion
[in_external_macro]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/lint/fn.in_external_macro.html
need to check out the Rust release tag of the stable release.
[Link][rust_stable_tools]
-Usually you want to wirte the changelog of the **upcoming stable release**. Make
+Usually you want to write the changelog of the **upcoming stable release**. Make
sure though, that `beta` was already branched in the Rust repository.
To find the commit hash, issue the following command when in a `rust-lang/rust` checkout:
- [Common tools for writing lints](#common-tools-for-writing-lints)
- [Retrieving the type of an expression](#retrieving-the-type-of-an-expression)
- - [Checking if an expression is calling a specific method](#checking-if-an-expr-is-calling-a-specific-method)
+ - [Checking if an expr is calling a specific method](#checking-if-an-expr-is-calling-a-specific-method)
+ - [Checking for a specific type](#checking-for-a-specific-type)
- [Checking if a type implements a specific trait](#checking-if-a-type-implements-a-specific-trait)
- [Checking if a type defines a specific method](#checking-if-a-type-defines-a-specific-method)
- - [Dealing with macros](#dealing-with-macros)
+ - [Dealing with macros](#dealing-with-macros-and-expansions)
Useful Rustc dev guide links:
- [Stages of compilation](https://rustc-dev-guide.rust-lang.org/compiler-src.html#the-main-stages-of-compilation)
- [Type checking](https://rustc-dev-guide.rust-lang.org/type-checking.html)
- [Ty module](https://rustc-dev-guide.rust-lang.org/ty.html)
-# Retrieving the type of an expression
+## Retrieving the type of an expression
Sometimes you may want to retrieve the type `Ty` of an expression `Expr`, for example to answer following questions:
created by type checking step, it includes useful information such as types
of expressions, ways to resolve methods and so on.
-# Checking if an expr is calling a specific method
+## Checking if an expr is calling a specific method
Starting with an `expr`, you can check whether it is calling a specific method `some_method`:
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
if_chain! {
// Check our expr is calling a method
- if let hir::ExprKind::MethodCall(path, _, _args, _) = &expr.kind;
+ if let hir::ExprKind::MethodCall(path, _, [_self_arg, ..], _) = &expr.kind;
// Check the name of this method is `some_method`
if path.ident.name == sym!(some_method);
+ // Optionally, check the type of the self argument.
+ // - See "Checking for a specific type"
then {
// ...
}
}
```
-# Checking if a type implements a specific trait
+## Checking for a specific type
+
+There are three ways to check if an expression type is a specific type we want to check for.
+All of these methods only check for the base type, generic arguments have to be checked separately.
+
+```rust
+use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
+use clippy_utils::{paths, match_def_path};
+use rustc_span::symbol::sym;
+use rustc_hir::LangItem;
+
+impl LateLintPass<'_> for MyStructLint {
+ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
+ // Getting the expression type
+ let ty = cx.typeck_results().expr_ty(expr);
+
+ // 1. Using diagnostic items
+ // The last argument is the diagnostic item to check for
+ if is_type_diagnostic_item(cx, ty, sym::Option) {
+ // The type is an `Option`
+ }
+
+ // 2. Using lang items
+ if is_type_lang_item(cx, ty, LangItem::RangeFull) {
+ // The type is a full range like `.drain(..)`
+ }
+
+ // 3. Using the type path
+ // This method should be avoided if possible
+ if match_def_path(cx, def_id, &paths::RESULT) {
+ // The type is a `core::result::Result`
+ }
+ }
+}
+```
+
+Prefer using diagnostic items and lang items where possible.
+
+## Checking if a type implements a specific trait
There are three ways to do this, depending on if the target trait has a diagnostic item, lang item or neither.
// 3. Using the type path with the expression
// we use `match_trait_method` function from Clippy's utils
+ // (This method should be avoided if possible)
if match_trait_method(cx, expr, &paths::INTO) {
// `expr` implements `Into` trait
}
We access lang items through the type context `tcx`. `tcx` is of type [`TyCtxt`][TyCtxt] and is defined in the `rustc_middle` crate.
A list of defined paths for Clippy can be found in [paths.rs][paths]
-# Checking if a type defines a specific method
+## Checking if a type defines a specific method
To check if our type defines a method called `some_method`:
}
```
-# Dealing with macros
+## Dealing with macros and expansions
-There are several helpers in [`clippy_utils`][utils] to deal with macros:
+Keep in mind that macros are already expanded and desugaring is already applied
+to the code representation that you are working with in Clippy. This unfortunately causes a lot of
+false positives because macro expansions are "invisible" unless you actively check for them.
+Generally speaking, code with macro expansions should just be ignored by Clippy because that code can be
+dynamic in ways that are difficult or impossible to see.
+Use the following functions to deal with macros:
-- `in_macro()`: detect if the given span is expanded by a macro
+- `span.from_expansion()`: detects if a span is from macro expansion or desugaring.
+ Checking this is a common first step in a lint.
-You may want to use this for example to not start linting in any macro.
+ ```rust
+ if expr.span.from_expansion() {
+ // just forget it
+ return;
+ }
+ ```
-```rust
-macro_rules! foo {
- ($param:expr) => {
- match $param {
- "bar" => println!("whatever"),
- _ => ()
- }
- };
-}
+- `span.ctxt()`: the span's context represents whether it is from expansion, and if so, which macro call expanded it.
+ It is sometimes useful to check if the context of two spans are equal.
-foo!("bar");
+ ```rust
+ // expands to `1 + 0`, but don't lint
+ 1 + mac!()
+ ```
+ ```rust
+ if left.span.ctxt() != right.span.ctxt() {
+ // the coder most likely cannot modify this expression
+ return;
+ }
+ ```
+ Note: Code that is not from expansion is in the "root" context. So any spans where `from_expansion` returns `true` can
+ be assumed to have the same context. And so just using `span.from_expansion()` is often good enough.
-// if we lint the `match` of `foo` call and test its span
-assert_eq!(in_macro(match_span), true);
-```
-- `in_external_macro()`: detect if the given span is from an external macro, defined in a foreign crate
+- `in_external_macro(span)`: detect if the given span is from a macro defined in a foreign crate.
+ If you want the lint to work with macro-generated code, this is the next line of defense to avoid macros
+ not defined in the current crate. It doesn't make sense to lint code that the coder can't change.
-You may want to use it for example to not start linting in macros from other crates
+ You may want to use it for example to not start linting in macros from other crates
-```rust
-#[macro_use]
-extern crate a_crate_with_macros;
+ ```rust
+ #[macro_use]
+ extern crate a_crate_with_macros;
-// `foo` is defined in `a_crate_with_macros`
-foo!("bar");
+ // `foo` is defined in `a_crate_with_macros`
+ foo!("bar");
-// if we lint the `match` of `foo` call and test its span
-assert_eq!(in_external_macro(cx.sess(), match_span), true);
-```
+ // if we lint the `match` of `foo` call and test its span
+ assert_eq!(in_external_macro(cx.sess(), match_span), true);
+ ```
- `differing_macro_contexts()`: returns true if the two given spans are not from the same context
-```rust
-macro_rules! m {
- ($a:expr, $b:expr) => {
- if $a.is_some() {
- $b;
- }
- }
-}
+ ```rust
+ macro_rules! m {
+ ($a:expr, $b:expr) => {
+ if $a.is_some() {
+ $b;
+ }
+ }
+ }
-let x: Option<u32> = Some(42);
-m!(x, x.unwrap());
+ let x: Option<u32> = Some(42);
+ m!(x, x.unwrap());
-// These spans are not from the same context
-// x.is_some() is from inside the macro
-// x.unwrap() is from outside the macro
-assert_eq!(differing_macro_contexts(x_is_some_span, x_unwrap_span), true);
-```
+ // These spans are not from the same context
+ // x.is_some() is from inside the macro
+ // x.unwrap() is from outside the macro
+ assert_eq!(differing_macro_contexts(x_is_some_span, x_unwrap_span), true);
+ ```
[TyS]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyS.html
[TyKind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.TyKind.html
[TyCtxt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html
[pat_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TypeckResults.html#method.pat_ty
[paths]: ../clippy_utils/src/paths.rs
-[utils]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_utils/src/lib.rs
}
}
-/// Generate a short list of occuring lints-types and their count
+/// Generate a short list of occurring lints-types and their count
fn gather_stats(clippy_warnings: &[ClippyWarning]) -> (String, HashMap<&String, usize>) {
// count lint type occurrences
let mut counter: HashMap<&String, usize> = HashMap::new();
// quarter of the time which might result in a longer wall clock runtime
// This helps when we check many small crates with dep-trees that don't have a lot of branches in
- // order to achive some kind of parallelism
+ // order to achieve some kind of parallelism
// by default, use a single thread
let num_cpus = config.max_jobs;
[toolchain]
-channel = "nightly-2021-11-04"
-components = ["llvm-tools-preview", "rustc-dev", "rust-src"]
+channel = "nightly-2021-12-02"
+components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
let conf = clippy_lints::read_conf(sess);
clippy_lints::register_plugins(lint_store, sess, &conf);
- clippy_lints::register_pre_expansion_lints(lint_store);
+ clippy_lints::register_pre_expansion_lints(lint_store, sess, &conf);
clippy_lints::register_renamed(lint_store);
}));
"serde",
"serde_derive",
"syn",
+ "parking_lot",
];
// Test dependencies may need an `extern crate` here to ensure that they show up
#[allow(unused_extern_crates)]
extern crate itertools;
#[allow(unused_extern_crates)]
+extern crate parking_lot;
+#[allow(unused_extern_crates)]
extern crate quote;
#[allow(unused_extern_crates)]
extern crate syn;
}
// Skip this test if nightly rustfmt is unavailable
- let rustup_output = Command::new("rustup")
- .args(&["component", "list", "--toolchain", "nightly"])
- .output()
- .unwrap();
+ let rustup_output = Command::new("rustup").args(&["component", "list"]).output().unwrap();
assert!(rustup_output.status.success());
let component_output = String::from_utf8_lossy(&rustup_output.stdout);
if !component_output.contains("rustfmt") {
--- /dev/null
+#![deny(clippy::internal)]
+#![feature(rustc_private)]
+
+#[macro_use]
+extern crate rustc_middle;
+#[macro_use]
+extern crate rustc_session;
+extern crate rustc_lint;
+
+///////////////////////
+// Valid descriptions
+///////////////////////
+declare_tool_lint! {
+ #[clippy::version = "pre 1.29.0"]
+ pub clippy::VALID_ONE,
+ Warn,
+ "One",
+ report_in_external_macro: true
+}
+
+declare_tool_lint! {
+ #[clippy::version = "1.29.0"]
+ pub clippy::VALID_TWO,
+ Warn,
+ "Two",
+ report_in_external_macro: true
+}
+
+declare_tool_lint! {
+ #[clippy::version = "1.59.0"]
+ pub clippy::VALID_THREE,
+ Warn,
+ "Three",
+ report_in_external_macro: true
+}
+
+///////////////////////
+// Invalid attributes
+///////////////////////
+declare_tool_lint! {
+ #[clippy::version = "1.2.3.4.5.6"]
+ pub clippy::INVALID_ONE,
+ Warn,
+ "One",
+ report_in_external_macro: true
+}
+
+declare_tool_lint! {
+ #[clippy::version = "I'm a string"]
+ pub clippy::INVALID_TWO,
+ Warn,
+ "Two",
+ report_in_external_macro: true
+}
+
+///////////////////////
+// Missing attribute test
+///////////////////////
+declare_tool_lint! {
+ #[clippy::version]
+ pub clippy::MISSING_ATTRIBUTE_ONE,
+ Warn,
+ "Two",
+ report_in_external_macro: true
+}
+
+declare_tool_lint! {
+ pub clippy::MISSING_ATTRIBUTE_TWO,
+ Warn,
+ "Two",
+ report_in_external_macro: true
+}
+
+#[allow(clippy::missing_clippy_version_attribute)]
+mod internal_clippy_lints {
+ declare_tool_lint! {
+ pub clippy::ALLOW_MISSING_ATTRIBUTE_ONE,
+ Warn,
+ "Two",
+ report_in_external_macro: true
+ }
+}
+
+use crate::internal_clippy_lints::ALLOW_MISSING_ATTRIBUTE_ONE;
+declare_lint_pass!(Pass2 => [VALID_ONE, VALID_TWO, VALID_THREE, INVALID_ONE, INVALID_TWO, MISSING_ATTRIBUTE_ONE, MISSING_ATTRIBUTE_TWO, ALLOW_MISSING_ATTRIBUTE_ONE]);
+
+fn main() {}
--- /dev/null
+error: this item has an invalid `clippy::version` attribute
+ --> $DIR/check_clippy_version_attribute.rs:40:1
+ |
+LL | / declare_tool_lint! {
+LL | | #[clippy::version = "1.2.3.4.5.6"]
+LL | | pub clippy::INVALID_ONE,
+LL | | Warn,
+LL | | "One",
+LL | | report_in_external_macro: true
+LL | | }
+ | |_^
+ |
+note: the lint level is defined here
+ --> $DIR/check_clippy_version_attribute.rs:1:9
+ |
+LL | #![deny(clippy::internal)]
+ | ^^^^^^^^^^^^^^^^
+ = note: `#[deny(clippy::invalid_clippy_version_attribute)]` implied by `#[deny(clippy::internal)]`
+ = help: please use a valid sematic version, see `doc/adding_lints.md`
+ = note: this error originates in the macro `$crate::declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: this item has an invalid `clippy::version` attribute
+ --> $DIR/check_clippy_version_attribute.rs:48:1
+ |
+LL | / declare_tool_lint! {
+LL | | #[clippy::version = "I'm a string"]
+LL | | pub clippy::INVALID_TWO,
+LL | | Warn,
+LL | | "Two",
+LL | | report_in_external_macro: true
+LL | | }
+ | |_^
+ |
+ = help: please use a valid sematic version, see `doc/adding_lints.md`
+ = note: this error originates in the macro `$crate::declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: this lint is missing the `clippy::version` attribute or version value
+ --> $DIR/check_clippy_version_attribute.rs:59:1
+ |
+LL | / declare_tool_lint! {
+LL | | #[clippy::version]
+LL | | pub clippy::MISSING_ATTRIBUTE_ONE,
+LL | | Warn,
+LL | | "Two",
+LL | | report_in_external_macro: true
+LL | | }
+ | |_^
+ |
+note: the lint level is defined here
+ --> $DIR/check_clippy_version_attribute.rs:1:9
+ |
+LL | #![deny(clippy::internal)]
+ | ^^^^^^^^^^^^^^^^
+ = note: `#[deny(clippy::missing_clippy_version_attribute)]` implied by `#[deny(clippy::internal)]`
+ = help: please use a `clippy::version` attribute, see `doc/adding_lints.md`
+ = note: this error originates in the macro `$crate::declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: this lint is missing the `clippy::version` attribute or version value
+ --> $DIR/check_clippy_version_attribute.rs:67:1
+ |
+LL | / declare_tool_lint! {
+LL | | pub clippy::MISSING_ATTRIBUTE_TWO,
+LL | | Warn,
+LL | | "Two",
+LL | | report_in_external_macro: true
+LL | | }
+ | |_^
+ |
+ = help: please use a `clippy::version` attribute, see `doc/adding_lints.md`
+ = note: this error originates in the macro `$crate::declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 4 previous errors
+
// run-rustfix
#![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
#![feature(rustc_private)]
extern crate clippy_utils;
// run-rustfix
#![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
#![feature(rustc_private)]
extern crate clippy_utils;
error: this call is collapsible
- --> $DIR/collapsible_span_lint_calls.rs:35:9
+ --> $DIR/collapsible_span_lint_calls.rs:36:9
|
LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
LL | | db.span_suggestion(expr.span, help_msg, sugg.to_string(), Applicability::MachineApplicable);
= note: `#[deny(clippy::collapsible_span_lint_calls)]` implied by `#[deny(clippy::internal)]`
error: this call is collapsible
- --> $DIR/collapsible_span_lint_calls.rs:38:9
+ --> $DIR/collapsible_span_lint_calls.rs:39:9
|
LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
LL | | db.span_help(expr.span, help_msg);
| |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg)`
error: this call is collapsible
- --> $DIR/collapsible_span_lint_calls.rs:41:9
+ --> $DIR/collapsible_span_lint_calls.rs:42:9
|
LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
LL | | db.help(help_msg);
| |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg)`
error: this call is collspible
- --> $DIR/collapsible_span_lint_calls.rs:44:9
+ --> $DIR/collapsible_span_lint_calls.rs:45:9
|
LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
LL | | db.span_note(expr.span, note_msg);
| |__________^ help: collapse into: `span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg)`
error: this call is collspible
- --> $DIR/collapsible_span_lint_calls.rs:47:9
+ --> $DIR/collapsible_span_lint_calls.rs:48:9
|
LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
LL | | db.note(note_msg);
// normalize-stderr-test: "', .*clippy_lints" -> "', clippy_lints"
#![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
fn it_looks_like_you_are_trying_to_kill_clippy() {}
#![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
#![feature(rustc_private)]
#[macro_use]
error: the lint `TEST_LINT_DEFAULT` has the default lint description
- --> $DIR/default_lint.rs:17:1
+ --> $DIR/default_lint.rs:18:1
|
LL | / declare_tool_lint! {
LL | | pub clippy::TEST_LINT_DEFAULT,
#![warn(clippy::if_chain_style)]
-#![allow(clippy::no_effect)]
+#![allow(clippy::no_effect, clippy::nonminimal_bool, clippy::missing_clippy_version_attribute)]
extern crate if_chain;
// run-rustfix
#![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
#![feature(rustc_private)]
extern crate rustc_span;
// run-rustfix
#![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
#![feature(rustc_private)]
extern crate rustc_span;
error: interning a defined symbol
- --> $DIR/interning_defined_symbol.rs:17:13
+ --> $DIR/interning_defined_symbol.rs:18:13
|
LL | let _ = Symbol::intern("f32");
| ^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::f32`
= note: `#[deny(clippy::interning_defined_symbol)]` implied by `#[deny(clippy::internal)]`
error: interning a defined symbol
- --> $DIR/interning_defined_symbol.rs:20:13
+ --> $DIR/interning_defined_symbol.rs:21:13
|
LL | let _ = sym!(f32);
| ^^^^^^^^^ help: try: `rustc_span::sym::f32`
error: interning a defined symbol
- --> $DIR/interning_defined_symbol.rs:23:13
+ --> $DIR/interning_defined_symbol.rs:24:13
|
LL | let _ = Symbol::intern("proc-macro");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::proc_dash_macro`
error: interning a defined symbol
- --> $DIR/interning_defined_symbol.rs:26:13
+ --> $DIR/interning_defined_symbol.rs:27:13
|
LL | let _ = Symbol::intern("self");
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::symbol::kw::SelfLower`
#![warn(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
mod paths {
// Good path
error: invalid path
- --> $DIR/invalid_paths.rs:17:5
+ --> $DIR/invalid_paths.rs:18:5
|
LL | pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: `-D clippy::invalid-paths` implied by `-D warnings`
error: invalid path
- --> $DIR/invalid_paths.rs:20:5
+ --> $DIR/invalid_paths.rs:21:5
|
LL | pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
#![feature(rustc_private)]
#[macro_use]
error: the lint `TEST_LINT` is not added to any `LintPass`
- --> $DIR/lint_without_lint_pass.rs:11:1
+ --> $DIR/lint_without_lint_pass.rs:12:1
|
LL | / declare_tool_lint! {
LL | | pub clippy::TEST_LINT,
#![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
#![feature(rustc_private)]
extern crate clippy_utils;
error: usage of `clippy_utils::ty::match_type()` on a type diagnostic item
- --> $DIR/match_type_on_diag_item.rs:30:17
+ --> $DIR/match_type_on_diag_item.rs:31:17
|
LL | let _ = match_type(cx, ty, &OPTION);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clippy_utils::ty::is_type_diagnostic_item(cx, ty, sym::Option)`
= note: `#[deny(clippy::match_type_on_diagnostic_item)]` implied by `#[deny(clippy::internal)]`
error: usage of `clippy_utils::ty::match_type()` on a type diagnostic item
- --> $DIR/match_type_on_diag_item.rs:31:17
+ --> $DIR/match_type_on_diag_item.rs:32:17
|
LL | let _ = match_type(cx, ty, &["core", "result", "Result"]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clippy_utils::ty::is_type_diagnostic_item(cx, ty, sym::Result)`
error: usage of `clippy_utils::ty::match_type()` on a type diagnostic item
- --> $DIR/match_type_on_diag_item.rs:34:17
+ --> $DIR/match_type_on_diag_item.rs:35:17
|
LL | let _ = clippy_utils::ty::match_type(cx, ty, rc_path);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clippy_utils::ty::is_type_diagnostic_item(cx, ty, sym::Rc)`
// run-rustfix
#![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
#![feature(rustc_private)]
extern crate rustc_hir;
// run-rustfix
#![deny(clippy::internal)]
+#![allow(clippy::missing_clippy_version_attribute)]
#![feature(rustc_private)]
extern crate rustc_hir;
error: usage of `outer_expn().expn_data()`
- --> $DIR/outer_expn_data.rs:24:34
+ --> $DIR/outer_expn_data.rs:25:34
|
LL | let _ = expr.span.ctxt().outer_expn().expn_data();
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `outer_expn_data()`
// run-rustfix
#![feature(rustc_private)]
#![deny(clippy::internal)]
-#![allow(clippy::unnecessary_operation, unused_must_use)]
+#![allow(
+ clippy::unnecessary_operation,
+ unused_must_use,
+ clippy::missing_clippy_version_attribute
+)]
extern crate rustc_span;
// run-rustfix
#![feature(rustc_private)]
#![deny(clippy::internal)]
-#![allow(clippy::unnecessary_operation, unused_must_use)]
+#![allow(
+ clippy::unnecessary_operation,
+ unused_must_use,
+ clippy::missing_clippy_version_attribute
+)]
extern crate rustc_span;
error: unnecessary `Symbol` to string conversion
- --> $DIR/unnecessary_symbol_str.rs:11:5
+ --> $DIR/unnecessary_symbol_str.rs:15:5
|
LL | Symbol::intern("foo").as_str() == "clippy";
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") == rustc_span::sym::clippy`
= note: `#[deny(clippy::unnecessary_symbol_str)]` implied by `#[deny(clippy::internal)]`
error: unnecessary `Symbol` to string conversion
- --> $DIR/unnecessary_symbol_str.rs:12:5
+ --> $DIR/unnecessary_symbol_str.rs:16:5
|
LL | Symbol::intern("foo").to_string() == "self";
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") == rustc_span::symbol::kw::SelfLower`
error: unnecessary `Symbol` to string conversion
- --> $DIR/unnecessary_symbol_str.rs:13:5
+ --> $DIR/unnecessary_symbol_str.rs:17:5
|
LL | Symbol::intern("foo").to_ident_string() != "Self";
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") != rustc_span::symbol::kw::SelfUpper`
error: unnecessary `Symbol` to string conversion
- --> $DIR/unnecessary_symbol_str.rs:14:5
+ --> $DIR/unnecessary_symbol_str.rs:18:5
|
LL | &*Ident::empty().as_str() == "clippy";
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Ident::empty().name == rustc_span::sym::clippy`
error: unnecessary `Symbol` to string conversion
- --> $DIR/unnecessary_symbol_str.rs:15:5
+ --> $DIR/unnecessary_symbol_str.rs:19:5
|
LL | "clippy" == Ident::empty().to_string();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::clippy == Ident::empty().name`
--- /dev/null
+max-suggested-slice-pattern-length = 8
--- /dev/null
+#![deny(clippy::index_refutable_slice)]
+
+fn below_limit() {
+ let slice: Option<&[u32]> = Some(&[1, 2, 3]);
+ if let Some(slice) = slice {
+ // This would usually not be linted but is included now due to the
+ // index limit in the config file
+ println!("{}", slice[7]);
+ }
+}
+
+fn above_limit() {
+ let slice: Option<&[u32]> = Some(&[1, 2, 3]);
+ if let Some(slice) = slice {
+ // This will not be linted as 8 is above the limit
+ println!("{}", slice[8]);
+ }
+}
+
+fn main() {
+ below_limit();
+ above_limit();
+}
--- /dev/null
+error: this binding can be a slice pattern to avoid indexing
+ --> $DIR/index_refutable_slice.rs:5:17
+ |
+LL | if let Some(slice) = slice {
+ | ^^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/index_refutable_slice.rs:1:9
+ |
+LL | #![deny(clippy::index_refutable_slice)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: try using a slice pattern here
+ |
+LL | if let Some([_, _, _, _, _, _, _, slice_7, ..]) = slice {
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+help: and replace the index expressions here
+ |
+LL | println!("{}", slice_7);
+ | ~~~~~~~
+
+error: aborting due to previous error
+
}
}
+fn check_index_refutable_slice() {
+ // This shouldn't trigger `clippy::index_refutable_slice` as the suggestion
+ // would only be valid from 1.42.0 onward
+ let slice: Option<&[u32]> = Some(&[1]);
+ if let Some(slice) = slice {
+ println!("{}", slice[0]);
+ }
+}
+
fn main() {
option_as_ref_deref();
match_like_matches();
match_same_arms();
match_same_arms2();
manual_strip_msrv();
+ check_index_refutable_slice();
}
+++ /dev/null
-disallowed-methods = [
- # just a string is shorthand for path only
- "std::iter::Iterator::sum",
- # can give path and reason with an inline table
- { path = "regex::Regex::is_match", reason = "no matching allowed" },
- # can use an inline table but omit reason
- { path = "regex::Regex::new" },
-]
+++ /dev/null
-#![warn(clippy::disallowed_method)]
-
-extern crate regex;
-use regex::Regex;
-
-fn main() {
- let re = Regex::new(r"ab.*c").unwrap();
- re.is_match("abc");
-
- let a = vec![1, 2, 3, 4];
- a.iter().sum::<i32>();
-}
+++ /dev/null
-error: use of a disallowed method `regex::Regex::new`
- --> $DIR/conf_disallowed_method.rs:7:14
- |
-LL | let re = Regex::new(r"ab.*c").unwrap();
- | ^^^^^^^^^^^^^^^^^^^^
- |
- = note: `-D clippy::disallowed-method` implied by `-D warnings`
-
-error: use of a disallowed method `regex::Regex::is_match`
- --> $DIR/conf_disallowed_method.rs:8:5
- |
-LL | re.is_match("abc");
- | ^^^^^^^^^^^^^^^^^^
- |
- = note: no matching allowed (from clippy.toml)
-
-error: use of a disallowed method `std::iter::Iterator::sum`
- --> $DIR/conf_disallowed_method.rs:11:5
- |
-LL | a.iter().sum::<i32>();
- | ^^^^^^^^^^^^^^^^^^^^^
-
-error: aborting due to 3 previous errors
-
--- /dev/null
+disallowed-methods = [
+ # just a string is shorthand for path only
+ "std::iter::Iterator::sum",
+ # can give path and reason with an inline table
+ { path = "regex::Regex::is_match", reason = "no matching allowed" },
+ # can use an inline table but omit reason
+ { path = "regex::Regex::new" },
+]
--- /dev/null
+#![warn(clippy::disallowed_methods)]
+
+extern crate regex;
+use regex::Regex;
+
+fn main() {
+ let re = Regex::new(r"ab.*c").unwrap();
+ re.is_match("abc");
+
+ let a = vec![1, 2, 3, 4];
+ a.iter().sum::<i32>();
+}
--- /dev/null
+error: use of a disallowed method `regex::Regex::new`
+ --> $DIR/conf_disallowed_methods.rs:7:14
+ |
+LL | let re = Regex::new(r"ab.*c").unwrap();
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::disallowed-methods` implied by `-D warnings`
+
+error: use of a disallowed method `regex::Regex::is_match`
+ --> $DIR/conf_disallowed_methods.rs:8:5
+ |
+LL | re.is_match("abc");
+ | ^^^^^^^^^^^^^^^^^^
+ |
+ = note: no matching allowed (from clippy.toml)
+
+error: use of a disallowed method `std::iter::Iterator::sum`
+ --> $DIR/conf_disallowed_methods.rs:11:5
+ |
+LL | a.iter().sum::<i32>();
+ | ^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 3 previous errors
+
+++ /dev/null
-disallowed-types = [
- "std::collections::HashMap",
- "std::sync::atomic::AtomicU32",
- "syn::TypePath",
- "proc_macro2::Ident",
- "std::thread::Thread",
- "std::time::Instant",
- "std::io::Read",
- "std::primitive::usize",
- "bool",
- # can give path and reason with an inline table
- { path = "std::net::Ipv4Addr", reason = "no IPv4 allowed" },
- # can use an inline table but omit reason
- { path = "std::net::TcpListener" },
-]
+++ /dev/null
-#![warn(clippy::disallowed_type)]
-
-extern crate quote;
-extern crate syn;
-
-use std::sync as foo;
-use std::sync::atomic::AtomicU32;
-use std::time::Instant as Sneaky;
-
-struct HashMap;
-
-fn bad_return_type() -> fn() -> Sneaky {
- todo!()
-}
-
-fn bad_arg_type(_: impl Fn(Sneaky) -> foo::atomic::AtomicU32) {}
-
-fn trait_obj(_: &dyn std::io::Read) {}
-
-fn full_and_single_path_prim(_: usize, _: bool) {}
-
-fn const_generics<const C: usize>() {}
-
-struct GenArg<const U: usize>([u8; U]);
-
-static BAD: foo::atomic::AtomicPtr<()> = foo::atomic::AtomicPtr::new(std::ptr::null_mut());
-
-fn ip(_: std::net::Ipv4Addr) {}
-
-fn listener(_: std::net::TcpListener) {}
-
-#[allow(clippy::diverging_sub_expression)]
-fn main() {
- let _: std::collections::HashMap<(), ()> = std::collections::HashMap::new();
- let _ = Sneaky::now();
- let _ = foo::atomic::AtomicU32::new(0);
- static FOO: std::sync::atomic::AtomicU32 = foo::atomic::AtomicU32::new(1);
- let _: std::collections::BTreeMap<(), syn::TypePath> = Default::default();
- let _ = syn::Ident::new("", todo!());
- let _ = HashMap;
- let _: usize = 64_usize;
-}
+++ /dev/null
-error: `std::sync::atomic::AtomicU32` is not allowed according to config
- --> $DIR/conf_disallowed_type.rs:7:1
- |
-LL | use std::sync::atomic::AtomicU32;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: `-D clippy::disallowed-type` implied by `-D warnings`
-
-error: `std::time::Instant` is not allowed according to config
- --> $DIR/conf_disallowed_type.rs:8:1
- |
-LL | use std::time::Instant as Sneaky;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: `std::time::Instant` is not allowed according to config
- --> $DIR/conf_disallowed_type.rs:12:33
- |
-LL | fn bad_return_type() -> fn() -> Sneaky {
- | ^^^^^^
-
-error: `std::time::Instant` is not allowed according to config
- --> $DIR/conf_disallowed_type.rs:16:28
- |
-LL | fn bad_arg_type(_: impl Fn(Sneaky) -> foo::atomic::AtomicU32) {}
- | ^^^^^^
-
-error: `std::sync::atomic::AtomicU32` is not allowed according to config
- --> $DIR/conf_disallowed_type.rs:16:39
- |
-LL | fn bad_arg_type(_: impl Fn(Sneaky) -> foo::atomic::AtomicU32) {}
- | ^^^^^^^^^^^^^^^^^^^^^^
-
-error: `std::io::Read` is not allowed according to config
- --> $DIR/conf_disallowed_type.rs:18:22
- |
-LL | fn trait_obj(_: &dyn std::io::Read) {}
- | ^^^^^^^^^^^^^
-
-error: `usize` is not allowed according to config
- --> $DIR/conf_disallowed_type.rs:20:33
- |
-LL | fn full_and_single_path_prim(_: usize, _: bool) {}
- | ^^^^^
-
-error: `bool` is not allowed according to config
- --> $DIR/conf_disallowed_type.rs:20:43
- |
-LL | fn full_and_single_path_prim(_: usize, _: bool) {}
- | ^^^^
-
-error: `usize` is not allowed according to config
- --> $DIR/conf_disallowed_type.rs:22:28
- |
-LL | fn const_generics<const C: usize>() {}
- | ^^^^^
-
-error: `usize` is not allowed according to config
- --> $DIR/conf_disallowed_type.rs:24:24
- |
-LL | struct GenArg<const U: usize>([u8; U]);
- | ^^^^^
-
-error: `std::net::Ipv4Addr` is not allowed according to config
- --> $DIR/conf_disallowed_type.rs:28:10
- |
-LL | fn ip(_: std::net::Ipv4Addr) {}
- | ^^^^^^^^^^^^^^^^^^
- |
- = note: no IPv4 allowed (from clippy.toml)
-
-error: `std::net::TcpListener` is not allowed according to config
- --> $DIR/conf_disallowed_type.rs:30:16
- |
-LL | fn listener(_: std::net::TcpListener) {}
- | ^^^^^^^^^^^^^^^^^^^^^
-
-error: `std::collections::HashMap` is not allowed according to config
- --> $DIR/conf_disallowed_type.rs:34:48
- |
-LL | let _: std::collections::HashMap<(), ()> = std::collections::HashMap::new();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: `std::collections::HashMap` is not allowed according to config
- --> $DIR/conf_disallowed_type.rs:34:12
- |
-LL | let _: std::collections::HashMap<(), ()> = std::collections::HashMap::new();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: `std::time::Instant` is not allowed according to config
- --> $DIR/conf_disallowed_type.rs:35:13
- |
-LL | let _ = Sneaky::now();
- | ^^^^^^
-
-error: `std::sync::atomic::AtomicU32` is not allowed according to config
- --> $DIR/conf_disallowed_type.rs:36:13
- |
-LL | let _ = foo::atomic::AtomicU32::new(0);
- | ^^^^^^^^^^^^^^^^^^^^^^
-
-error: `std::sync::atomic::AtomicU32` is not allowed according to config
- --> $DIR/conf_disallowed_type.rs:37:17
- |
-LL | static FOO: std::sync::atomic::AtomicU32 = foo::atomic::AtomicU32::new(1);
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: `std::sync::atomic::AtomicU32` is not allowed according to config
- --> $DIR/conf_disallowed_type.rs:37:48
- |
-LL | static FOO: std::sync::atomic::AtomicU32 = foo::atomic::AtomicU32::new(1);
- | ^^^^^^^^^^^^^^^^^^^^^^
-
-error: `syn::TypePath` is not allowed according to config
- --> $DIR/conf_disallowed_type.rs:38:43
- |
-LL | let _: std::collections::BTreeMap<(), syn::TypePath> = Default::default();
- | ^^^^^^^^^^^^^
-
-error: `syn::Ident` is not allowed according to config
- --> $DIR/conf_disallowed_type.rs:39:13
- |
-LL | let _ = syn::Ident::new("", todo!());
- | ^^^^^^^^^^
-
-error: `usize` is not allowed according to config
- --> $DIR/conf_disallowed_type.rs:41:12
- |
-LL | let _: usize = 64_usize;
- | ^^^^^
-
-error: aborting due to 21 previous errors
-
--- /dev/null
+disallowed-types = [
+ "std::collections::HashMap",
+ "std::sync::atomic::AtomicU32",
+ "syn::TypePath",
+ "proc_macro2::Ident",
+ "std::thread::Thread",
+ "std::time::Instant",
+ "std::io::Read",
+ "std::primitive::usize",
+ "bool",
+ # can give path and reason with an inline table
+ { path = "std::net::Ipv4Addr", reason = "no IPv4 allowed" },
+ # can use an inline table but omit reason
+ { path = "std::net::TcpListener" },
+]
--- /dev/null
+#![warn(clippy::disallowed_types)]
+
+extern crate quote;
+extern crate syn;
+
+use std::sync as foo;
+use std::sync::atomic::AtomicU32;
+use std::time::Instant as Sneaky;
+
+struct HashMap;
+
+fn bad_return_type() -> fn() -> Sneaky {
+ todo!()
+}
+
+fn bad_arg_type(_: impl Fn(Sneaky) -> foo::atomic::AtomicU32) {}
+
+fn trait_obj(_: &dyn std::io::Read) {}
+
+fn full_and_single_path_prim(_: usize, _: bool) {}
+
+fn const_generics<const C: usize>() {}
+
+struct GenArg<const U: usize>([u8; U]);
+
+static BAD: foo::atomic::AtomicPtr<()> = foo::atomic::AtomicPtr::new(std::ptr::null_mut());
+
+fn ip(_: std::net::Ipv4Addr) {}
+
+fn listener(_: std::net::TcpListener) {}
+
+#[allow(clippy::diverging_sub_expression)]
+fn main() {
+ let _: std::collections::HashMap<(), ()> = std::collections::HashMap::new();
+ let _ = Sneaky::now();
+ let _ = foo::atomic::AtomicU32::new(0);
+ static FOO: std::sync::atomic::AtomicU32 = foo::atomic::AtomicU32::new(1);
+ let _: std::collections::BTreeMap<(), syn::TypePath> = Default::default();
+ let _ = syn::Ident::new("", todo!());
+ let _ = HashMap;
+ let _: usize = 64_usize;
+}
--- /dev/null
+error: `std::sync::atomic::AtomicU32` is not allowed according to config
+ --> $DIR/conf_disallowed_types.rs:7:1
+ |
+LL | use std::sync::atomic::AtomicU32;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::disallowed-types` implied by `-D warnings`
+
+error: `std::time::Instant` is not allowed according to config
+ --> $DIR/conf_disallowed_types.rs:8:1
+ |
+LL | use std::time::Instant as Sneaky;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: `std::time::Instant` is not allowed according to config
+ --> $DIR/conf_disallowed_types.rs:12:33
+ |
+LL | fn bad_return_type() -> fn() -> Sneaky {
+ | ^^^^^^
+
+error: `std::time::Instant` is not allowed according to config
+ --> $DIR/conf_disallowed_types.rs:16:28
+ |
+LL | fn bad_arg_type(_: impl Fn(Sneaky) -> foo::atomic::AtomicU32) {}
+ | ^^^^^^
+
+error: `std::sync::atomic::AtomicU32` is not allowed according to config
+ --> $DIR/conf_disallowed_types.rs:16:39
+ |
+LL | fn bad_arg_type(_: impl Fn(Sneaky) -> foo::atomic::AtomicU32) {}
+ | ^^^^^^^^^^^^^^^^^^^^^^
+
+error: `std::io::Read` is not allowed according to config
+ --> $DIR/conf_disallowed_types.rs:18:22
+ |
+LL | fn trait_obj(_: &dyn std::io::Read) {}
+ | ^^^^^^^^^^^^^
+
+error: `usize` is not allowed according to config
+ --> $DIR/conf_disallowed_types.rs:20:33
+ |
+LL | fn full_and_single_path_prim(_: usize, _: bool) {}
+ | ^^^^^
+
+error: `bool` is not allowed according to config
+ --> $DIR/conf_disallowed_types.rs:20:43
+ |
+LL | fn full_and_single_path_prim(_: usize, _: bool) {}
+ | ^^^^
+
+error: `usize` is not allowed according to config
+ --> $DIR/conf_disallowed_types.rs:22:28
+ |
+LL | fn const_generics<const C: usize>() {}
+ | ^^^^^
+
+error: `usize` is not allowed according to config
+ --> $DIR/conf_disallowed_types.rs:24:24
+ |
+LL | struct GenArg<const U: usize>([u8; U]);
+ | ^^^^^
+
+error: `std::net::Ipv4Addr` is not allowed according to config
+ --> $DIR/conf_disallowed_types.rs:28:10
+ |
+LL | fn ip(_: std::net::Ipv4Addr) {}
+ | ^^^^^^^^^^^^^^^^^^
+ |
+ = note: no IPv4 allowed (from clippy.toml)
+
+error: `std::net::TcpListener` is not allowed according to config
+ --> $DIR/conf_disallowed_types.rs:30:16
+ |
+LL | fn listener(_: std::net::TcpListener) {}
+ | ^^^^^^^^^^^^^^^^^^^^^
+
+error: `std::collections::HashMap` is not allowed according to config
+ --> $DIR/conf_disallowed_types.rs:34:48
+ |
+LL | let _: std::collections::HashMap<(), ()> = std::collections::HashMap::new();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: `std::collections::HashMap` is not allowed according to config
+ --> $DIR/conf_disallowed_types.rs:34:12
+ |
+LL | let _: std::collections::HashMap<(), ()> = std::collections::HashMap::new();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: `std::time::Instant` is not allowed according to config
+ --> $DIR/conf_disallowed_types.rs:35:13
+ |
+LL | let _ = Sneaky::now();
+ | ^^^^^^
+
+error: `std::sync::atomic::AtomicU32` is not allowed according to config
+ --> $DIR/conf_disallowed_types.rs:36:13
+ |
+LL | let _ = foo::atomic::AtomicU32::new(0);
+ | ^^^^^^^^^^^^^^^^^^^^^^
+
+error: `std::sync::atomic::AtomicU32` is not allowed according to config
+ --> $DIR/conf_disallowed_types.rs:37:17
+ |
+LL | static FOO: std::sync::atomic::AtomicU32 = foo::atomic::AtomicU32::new(1);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: `std::sync::atomic::AtomicU32` is not allowed according to config
+ --> $DIR/conf_disallowed_types.rs:37:48
+ |
+LL | static FOO: std::sync::atomic::AtomicU32 = foo::atomic::AtomicU32::new(1);
+ | ^^^^^^^^^^^^^^^^^^^^^^
+
+error: `syn::TypePath` is not allowed according to config
+ --> $DIR/conf_disallowed_types.rs:38:43
+ |
+LL | let _: std::collections::BTreeMap<(), syn::TypePath> = Default::default();
+ | ^^^^^^^^^^^^^
+
+error: `syn::Ident` is not allowed according to config
+ --> $DIR/conf_disallowed_types.rs:39:13
+ |
+LL | let _ = syn::Ident::new("", todo!());
+ | ^^^^^^^^^^
+
+error: `usize` is not allowed according to config
+ --> $DIR/conf_disallowed_types.rs:41:12
+ |
+LL | let _: usize = 64_usize;
+ | ^^^^^
+
+error: aborting due to 21 previous errors
+
-error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `disallowed-types`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `standard-macro-braces`, `enforced-import-renames`, `allowed-scripts`, `enable-raw-pointer-heuristic-for-send`, `third-party` at line 5 column 1
+error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `disallowed-types`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `standard-macro-braces`, `enforced-import-renames`, `allowed-scripts`, `enable-raw-pointer-heuristic-for-send`, `max-suggested-slice-pattern-length`, `third-party` at line 5 column 1
error: aborting due to previous error
if_chain! {
- if let StmtKind::Local(ref local) = stmt.kind;
- if let Some(ref init) = local.init;
- if let ExprKind::Cast(ref expr, ref cast_ty) = init.kind;
- if let TyKind::Path(ref qp) = cast_ty.kind;
- if match_qpath(qp, &["char"]);
+ if let StmtKind::Local(local) = stmt.kind;
+ if let Some(init) = local.init;
+ if let ExprKind::Cast(expr, cast_ty) = init.kind;
+ if let TyKind::Path(ref qpath) = cast_ty.kind;
+ if match_qpath(qpath, &["char"]);
if let ExprKind::Lit(ref lit) = expr.kind;
- if let LitKind::Int(69, _) = lit.node;
+ if let LitKind::Int(69, LitIntType::Unsuffixed) = lit.node;
if let PatKind::Binding(BindingAnnotation::Unannotated, _, name, None) = local.pat.kind;
if name.as_str() == "x";
then {
+// edition:2018
+
#![allow(redundant_semicolons, clippy::no_effect)]
+#![feature(stmt_expr_attributes)]
+#![feature(async_closure)]
#[rustfmt::skip]
fn main() {
#[clippy::author]
{
let x = 42i32;
+ let _t = 1f32;
+
-x;
};
#[clippy::author]
let expr = String::new();
drop(expr)
};
+
+ #[clippy::author]
+ async move || {};
}
if_chain! {
- if let ExprKind::Block(ref block) = expr.kind;
- if block.stmts.len() == 2;
- if let StmtKind::Local(ref local) = block.stmts[0].kind;
- if let Some(ref init) = local.init;
+ if let ExprKind::Block(block, None) = expr.kind;
+ if block.stmts.len() == 3;
+ if let StmtKind::Local(local) = block.stmts[0].kind;
+ if let Some(init) = local.init;
if let ExprKind::Lit(ref lit) = init.kind;
- if let LitKind::Int(42, _) = lit.node;
+ if let LitKind::Int(42, LitIntType::Signed(IntTy::I32)) = lit.node;
if let PatKind::Binding(BindingAnnotation::Unannotated, _, name, None) = local.pat.kind;
if name.as_str() == "x";
- if let StmtKind::Semi(ref e, _) = block.stmts[1].kind
- if let ExprKind::Unary(UnOp::Neg, ref inner) = e.kind;
- if let ExprKind::Path(ref path) = inner.kind;
- if match_qpath(path, &["x"]);
+ if let StmtKind::Local(local1) = block.stmts[1].kind;
+ if let Some(init1) = local1.init;
+ if let ExprKind::Lit(ref lit1) = init1.kind;
+ if let LitKind::Float(_, LitFloatType::Suffixed(FloatTy::F32)) = lit1.node;
+ if let PatKind::Binding(BindingAnnotation::Unannotated, _, name1, None) = local1.pat.kind;
+ if name1.as_str() == "_t";
+ if let StmtKind::Semi(e) = block.stmts[2].kind;
+ if let ExprKind::Unary(UnOp::Neg, inner) = e.kind;
+ if let ExprKind::Path(ref qpath) = inner.kind;
+ if match_qpath(qpath, &["x"]);
if block.expr.is_none();
then {
// report your lint here
}
}
if_chain! {
- if let ExprKind::Block(ref block) = expr.kind;
+ if let ExprKind::Block(block, None) = expr.kind;
if block.stmts.len() == 1;
- if let StmtKind::Local(ref local) = block.stmts[0].kind;
- if let Some(ref init) = local.init;
- if let ExprKind::Call(ref func, ref args) = init.kind;
- if let ExprKind::Path(ref path) = func.kind;
- if match_qpath(path, &["String", "new"]);
- if args.len() == 0;
+ if let StmtKind::Local(local) = block.stmts[0].kind;
+ if let Some(init) = local.init;
+ if let ExprKind::Call(func, args) = init.kind;
+ if let ExprKind::Path(ref qpath) = func.kind;
+ if match_qpath(qpath, &["String", "new"]);
+ if args.is_empty();
if let PatKind::Binding(BindingAnnotation::Unannotated, _, name, None) = local.pat.kind;
if name.as_str() == "expr";
- if let Some(trailing_expr) = &block.expr;
- if let ExprKind::Call(ref func1, ref args1) = trailing_expr.kind;
- if let ExprKind::Path(ref path1) = func1.kind;
- if match_qpath(path1, &["drop"]);
+ if let Some(trailing_expr) = block.expr;
+ if let ExprKind::Call(func1, args1) = trailing_expr.kind;
+ if let ExprKind::Path(ref qpath1) = func1.kind;
+ if match_qpath(qpath1, &["drop"]);
if args1.len() == 1;
- if let ExprKind::Path(ref path2) = args1[0].kind;
- if match_qpath(path2, &["expr"]);
+ if let ExprKind::Path(ref qpath2) = args1[0].kind;
+ if match_qpath(qpath2, &["expr"]);
+ then {
+ // report your lint here
+ }
+}
+if_chain! {
+ if let ExprKind::Closure(CaptureBy::Value, fn_decl, body_id, _, None) = expr.kind;
+ if let FnRetTy::DefaultReturn(_) = fn_decl.output;
+ let expr1 = &cx.tcx.hir().body(body_id).value;
+ if let ExprKind::Call(func, args) = expr1.kind;
+ if let ExprKind::Path(ref qpath) = func.kind;
+ if matches!(qpath, QPath::LangItem(LangItem::FromGenerator, _));
+ if args.len() == 1;
+ if let ExprKind::Closure(CaptureBy::Value, fn_decl1, body_id1, _, Some(Movability::Static)) = args[0].kind;
+ if let FnRetTy::DefaultReturn(_) = fn_decl1.output;
+ let expr2 = &cx.tcx.hir().body(body_id1).value;
+ if let ExprKind::Block(block, None) = expr2.kind;
+ if block.stmts.is_empty();
+ if block.expr.is_none();
then {
// report your lint here
}
if_chain! {
- if let StmtKind::Local(ref local) = stmt.kind;
- if let Some(ref init) = local.init;
- if let ExprKind::Call(ref func, ref args) = init.kind;
- if let ExprKind::Path(ref path) = func.kind;
- if match_qpath(path, &["{{root}}", "std", "cmp", "min"]);
+ if let StmtKind::Local(local) = stmt.kind;
+ if let Some(init) = local.init;
+ if let ExprKind::Call(func, args) = init.kind;
+ if let ExprKind::Path(ref qpath) = func.kind;
+ if match_qpath(qpath, &["{{root}}", "std", "cmp", "min"]);
if args.len() == 2;
if let ExprKind::Lit(ref lit) = args[0].kind;
- if let LitKind::Int(3, _) = lit.node;
+ if let LitKind::Int(3, LitIntType::Unsuffixed) = lit.node;
if let ExprKind::Lit(ref lit1) = args[1].kind;
- if let LitKind::Int(4, _) = lit1.node;
+ if let LitKind::Int(4, LitIntType::Unsuffixed) = lit1.node;
if let PatKind::Wild = local.pat.kind;
then {
// report your lint here
+++ /dev/null
-#![feature(stmt_expr_attributes)]
-
-fn main() {
- #[clippy::author]
- for y in 0..10 {
- let z = y;
- }
-}
+++ /dev/null
-if_chain! {
- if let ExprKind::DropTemps(ref expr) = expr.kind;
- if let ExprKind::Match(ref expr1, ref arms, MatchSource::ForLoopDesugar) = expr.kind;
- if let ExprKind::Call(ref func, ref args) = expr1.kind;
- if let ExprKind::Path(ref path) = func.kind;
- if matches!(path, QPath::LangItem(LangItem::IntoIterIntoIter, _));
- if args.len() == 1;
- if let ExprKind::Struct(ref path1, ref fields, None) = args[0].kind;
- if matches!(path1, QPath::LangItem(LangItem::Range, _));
- if fields.len() == 2;
- // unimplemented: field checks
- if arms.len() == 1;
- if let ExprKind::Loop(ref body, ref label, LoopSource::ForLoop) = arms[0].body.kind;
- if body.stmts.len() == 1;
- if let StmtKind::Expr(ref e, _) = body.stmts[0].kind
- if let ExprKind::Match(ref expr2, ref arms1, MatchSource::ForLoopDesugar) = e.kind;
- if let ExprKind::Call(ref func1, ref args1) = expr2.kind;
- if let ExprKind::Path(ref path2) = func1.kind;
- if matches!(path2, QPath::LangItem(LangItem::IteratorNext, _));
- if args1.len() == 1;
- if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, ref inner) = args1[0].kind;
- if let ExprKind::Path(ref path3) = inner.kind;
- if match_qpath(path3, &["iter"]);
- if arms1.len() == 2;
- if let ExprKind::Break(ref destination, None) = arms1[0].body.kind;
- if let PatKind::Struct(ref path4, ref fields1, false) = arms1[0].pat.kind;
- if matches!(path4, QPath::LangItem(LangItem::OptionNone, _));
- if fields1.len() == 0;
- // unimplemented: field checks
- if let ExprKind::Block(ref block) = arms1[1].body.kind;
- if block.stmts.len() == 1;
- if let StmtKind::Local(ref local) = block.stmts[0].kind;
- if let Some(ref init) = local.init;
- if let ExprKind::Path(ref path5) = init.kind;
- if match_qpath(path5, &["y"]);
- if let PatKind::Binding(BindingAnnotation::Unannotated, _, name, None) = local.pat.kind;
- if name.as_str() == "z";
- if block.expr.is_none();
- if let PatKind::Struct(ref path6, ref fields2, false) = arms1[1].pat.kind;
- if matches!(path6, QPath::LangItem(LangItem::OptionSome, _));
- if fields2.len() == 1;
- // unimplemented: field checks
- if body.expr.is_none();
- if let PatKind::Binding(BindingAnnotation::Mutable, _, name1, None) = arms[0].pat.kind;
- if name1.as_str() == "iter";
- then {
- // report your lint here
- }
-}
} else {
2 == 2;
};
+
+ let a = true;
+
+ #[clippy::author]
+ if let true = a {
+ } else {
+ };
}
if_chain! {
- if let StmtKind::Local(ref local) = stmt.kind;
- if let Some(ref init) = local.init;
- if let ExprKind::If(ref cond, ref then, Some(ref else_)) = init.kind;
- if let ExprKind::Block(ref block) = else_.kind;
+ if let StmtKind::Local(local) = stmt.kind;
+ if let Some(init) = local.init;
+ if let ExprKind::If(cond, then, Some(else_expr)) = init.kind;
+ if let ExprKind::DropTemps(expr) = cond.kind;
+ if let ExprKind::Lit(ref lit) = expr.kind;
+ if let LitKind::Bool(true) = lit.node;
+ if let ExprKind::Block(block, None) = then.kind;
if block.stmts.len() == 1;
- if let StmtKind::Semi(ref e, _) = block.stmts[0].kind
- if let ExprKind::Binary(ref op, ref left, ref right) = e.kind;
+ if let StmtKind::Semi(e) = block.stmts[0].kind;
+ if let ExprKind::Binary(op, left, right) = e.kind;
if BinOpKind::Eq == op.node;
- if let ExprKind::Lit(ref lit) = left.kind;
- if let LitKind::Int(2, _) = lit.node;
- if let ExprKind::Lit(ref lit1) = right.kind;
- if let LitKind::Int(2, _) = lit1.node;
+ if let ExprKind::Lit(ref lit1) = left.kind;
+ if let LitKind::Int(1, LitIntType::Unsuffixed) = lit1.node;
+ if let ExprKind::Lit(ref lit2) = right.kind;
+ if let LitKind::Int(1, LitIntType::Unsuffixed) = lit2.node;
if block.expr.is_none();
- if let ExprKind::DropTemps(ref expr) = cond.kind;
- if let ExprKind::Lit(ref lit2) = expr.kind;
- if let LitKind::Bool(true) = lit2.node;
- if let ExprKind::Block(ref block1) = then.kind;
+ if let ExprKind::Block(block1, None) = else_expr.kind;
if block1.stmts.len() == 1;
- if let StmtKind::Semi(ref e1, _) = block1.stmts[0].kind
- if let ExprKind::Binary(ref op1, ref left1, ref right1) = e1.kind;
+ if let StmtKind::Semi(e1) = block1.stmts[0].kind;
+ if let ExprKind::Binary(op1, left1, right1) = e1.kind;
if BinOpKind::Eq == op1.node;
if let ExprKind::Lit(ref lit3) = left1.kind;
- if let LitKind::Int(1, _) = lit3.node;
+ if let LitKind::Int(2, LitIntType::Unsuffixed) = lit3.node;
if let ExprKind::Lit(ref lit4) = right1.kind;
- if let LitKind::Int(1, _) = lit4.node;
+ if let LitKind::Int(2, LitIntType::Unsuffixed) = lit4.node;
if block1.expr.is_none();
if let PatKind::Wild = local.pat.kind;
then {
// report your lint here
}
}
+if_chain! {
+ if let ExprKind::If(cond, then, Some(else_expr)) = expr.kind;
+ if let ExprKind::Let(pat, expr1, _) = cond.kind;
+ if let PatKind::Lit(lit_expr) = pat.kind;
+ if let ExprKind::Lit(ref lit) = lit_expr.kind;
+ if let LitKind::Bool(true) = lit.node;
+ if let ExprKind::Path(ref qpath) = expr1.kind;
+ if match_qpath(qpath, &["a"]);
+ if let ExprKind::Block(block, None) = then.kind;
+ if block.stmts.is_empty();
+ if block.expr.is_none();
+ if let ExprKind::Block(block1, None) = else_expr.kind;
+ if block1.stmts.is_empty();
+ if block1.expr.is_none();
+ then {
+ // report your lint here
+ }
+}
if_chain! {
- if let StmtKind::Local(ref local) = stmt.kind;
- if let Some(ref init) = local.init;
- if let ExprKind::Call(ref func, ref args) = init.kind;
- if let ExprKind::Path(ref path) = func.kind;
- if match_qpath(path, &["std", "mem", "transmute"]);
+ if let StmtKind::Local(local) = stmt.kind;
+ if let Some(init) = local.init;
+ if let ExprKind::Call(func, args) = init.kind;
+ if let ExprKind::Path(ref qpath) = func.kind;
+ if match_qpath(qpath, &["std", "mem", "transmute"]);
if args.len() == 1;
- if let ExprKind::Path(ref path1) = args[0].kind;
- if match_qpath(path1, &["ZPTR"]);
+ if let ExprKind::Path(ref qpath1) = args[0].kind;
+ if match_qpath(qpath1, &["ZPTR"]);
if let PatKind::Wild = local.pat.kind;
then {
// report your lint here
--- /dev/null
+#![feature(stmt_expr_attributes)]
+#![allow(clippy::never_loop, clippy::while_immutable_condition)]
+
+fn main() {
+ #[clippy::author]
+ for y in 0..10 {
+ let z = y;
+ }
+
+ #[clippy::author]
+ for _ in 0..10 {
+ break;
+ }
+
+ #[clippy::author]
+ 'label: for _ in 0..10 {
+ break 'label;
+ }
+
+ let a = true;
+
+ #[clippy::author]
+ while a {
+ break;
+ }
+
+ #[clippy::author]
+ while let true = a {
+ break;
+ }
+
+ #[clippy::author]
+ loop {
+ break;
+ }
+}
--- /dev/null
+if_chain! {
+ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr);
+ if let PatKind::Binding(BindingAnnotation::Unannotated, _, name, None) = pat.kind;
+ if name.as_str() == "y";
+ if let ExprKind::Struct(qpath, fields, None) = arg.kind;
+ if matches!(qpath, QPath::LangItem(LangItem::Range, _));
+ if fields.len() == 2;
+ if fields[0].ident.as_str() == "start";
+ if let ExprKind::Lit(ref lit) = fields[0].expr.kind;
+ if let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node;
+ if fields[1].ident.as_str() == "end";
+ if let ExprKind::Lit(ref lit1) = fields[1].expr.kind;
+ if let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node;
+ if let ExprKind::Block(block, None) = body.kind;
+ if block.stmts.len() == 1;
+ if let StmtKind::Local(local) = block.stmts[0].kind;
+ if let Some(init) = local.init;
+ if let ExprKind::Path(ref qpath1) = init.kind;
+ if match_qpath(qpath1, &["y"]);
+ if let PatKind::Binding(BindingAnnotation::Unannotated, _, name1, None) = local.pat.kind;
+ if name1.as_str() == "z";
+ if block.expr.is_none();
+ then {
+ // report your lint here
+ }
+}
+if_chain! {
+ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr);
+ if let PatKind::Wild = pat.kind;
+ if let ExprKind::Struct(qpath, fields, None) = arg.kind;
+ if matches!(qpath, QPath::LangItem(LangItem::Range, _));
+ if fields.len() == 2;
+ if fields[0].ident.as_str() == "start";
+ if let ExprKind::Lit(ref lit) = fields[0].expr.kind;
+ if let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node;
+ if fields[1].ident.as_str() == "end";
+ if let ExprKind::Lit(ref lit1) = fields[1].expr.kind;
+ if let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node;
+ if let ExprKind::Block(block, None) = body.kind;
+ if block.stmts.len() == 1;
+ if let StmtKind::Semi(e) = block.stmts[0].kind;
+ if let ExprKind::Break(destination, None) = e.kind;
+ if destination.label.is_none();
+ if block.expr.is_none();
+ then {
+ // report your lint here
+ }
+}
+if_chain! {
+ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::ForLoop::hir(expr);
+ if let PatKind::Wild = pat.kind;
+ if let ExprKind::Struct(qpath, fields, None) = arg.kind;
+ if matches!(qpath, QPath::LangItem(LangItem::Range, _));
+ if fields.len() == 2;
+ if fields[0].ident.as_str() == "start";
+ if let ExprKind::Lit(ref lit) = fields[0].expr.kind;
+ if let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node;
+ if fields[1].ident.as_str() == "end";
+ if let ExprKind::Lit(ref lit1) = fields[1].expr.kind;
+ if let LitKind::Int(10, LitIntType::Unsuffixed) = lit1.node;
+ if let ExprKind::Block(block, None) = body.kind;
+ if block.stmts.len() == 1;
+ if let StmtKind::Semi(e) = block.stmts[0].kind;
+ if let ExprKind::Break(destination, None) = e.kind;
+ if let Some(label) = destination.label;
+ if label.ident.as_str() == "'label";
+ if block.expr.is_none();
+ then {
+ // report your lint here
+ }
+}
+if_chain! {
+ if let Some(higher::While { condition: condition, body: body }) = higher::While::hir(expr);
+ if let ExprKind::Path(ref qpath) = condition.kind;
+ if match_qpath(qpath, &["a"]);
+ if let ExprKind::Block(block, None) = body.kind;
+ if block.stmts.len() == 1;
+ if let StmtKind::Semi(e) = block.stmts[0].kind;
+ if let ExprKind::Break(destination, None) = e.kind;
+ if destination.label.is_none();
+ if block.expr.is_none();
+ then {
+ // report your lint here
+ }
+}
+if_chain! {
+ if let Some(higher::WhileLet { let_pat: let_pat, let_expr: let_expr, if_then: if_then }) = higher::WhileLet::hir(expr);
+ if let PatKind::Lit(lit_expr) = let_pat.kind;
+ if let ExprKind::Lit(ref lit) = lit_expr.kind;
+ if let LitKind::Bool(true) = lit.node;
+ if let ExprKind::Path(ref qpath) = let_expr.kind;
+ if match_qpath(qpath, &["a"]);
+ if let ExprKind::Block(block, None) = if_then.kind;
+ if block.stmts.len() == 1;
+ if let StmtKind::Semi(e) = block.stmts[0].kind;
+ if let ExprKind::Break(destination, None) = e.kind;
+ if destination.label.is_none();
+ if block.expr.is_none();
+ then {
+ // report your lint here
+ }
+}
+if_chain! {
+ if let ExprKind::Loop(body, None, LoopSource::Loop, _) = expr.kind;
+ if body.stmts.len() == 1;
+ if let StmtKind::Semi(e) = body.stmts[0].kind;
+ if let ExprKind::Break(destination, None) = e.kind;
+ if destination.label.is_none();
+ if body.expr.is_none();
+ then {
+ // report your lint here
+ }
+}
if_chain! {
- if let StmtKind::Local(ref local) = stmt.kind;
- if let Some(ref init) = local.init;
- if let ExprKind::Match(ref expr, ref arms, MatchSource::Normal) = init.kind;
- if let ExprKind::Lit(ref lit) = expr.kind;
- if let LitKind::Int(42, _) = lit.node;
+ if let StmtKind::Local(local) = stmt.kind;
+ if let Some(init) = local.init;
+ if let ExprKind::Match(scrutinee, arms, MatchSource::Normal) = init.kind;
+ if let ExprKind::Lit(ref lit) = scrutinee.kind;
+ if let LitKind::Int(42, LitIntType::Unsuffixed) = lit.node;
if arms.len() == 3;
- if let ExprKind::Lit(ref lit1) = arms[0].body.kind;
- if let LitKind::Int(5, _) = lit1.node;
- if let PatKind::Lit(ref lit_expr) = arms[0].pat.kind
- if let ExprKind::Lit(ref lit2) = lit_expr.kind;
- if let LitKind::Int(16, _) = lit2.node;
- if let ExprKind::Block(ref block) = arms[1].body.kind;
+ if let PatKind::Lit(lit_expr) = arms[0].pat.kind;
+ if let ExprKind::Lit(ref lit1) = lit_expr.kind;
+ if let LitKind::Int(16, LitIntType::Unsuffixed) = lit1.node;
+ if arms[0].guard.is_none();
+ if let ExprKind::Lit(ref lit2) = arms[0].body.kind;
+ if let LitKind::Int(5, LitIntType::Unsuffixed) = lit2.node;
+ if let PatKind::Lit(lit_expr1) = arms[1].pat.kind;
+ if let ExprKind::Lit(ref lit3) = lit_expr1.kind;
+ if let LitKind::Int(17, LitIntType::Unsuffixed) = lit3.node;
+ if arms[1].guard.is_none();
+ if let ExprKind::Block(block, None) = arms[1].body.kind;
if block.stmts.len() == 1;
- if let StmtKind::Local(ref local1) = block.stmts[0].kind;
- if let Some(ref init1) = local1.init;
- if let ExprKind::Lit(ref lit3) = init1.kind;
- if let LitKind::Int(3, _) = lit3.node;
+ if let StmtKind::Local(local1) = block.stmts[0].kind;
+ if let Some(init1) = local1.init;
+ if let ExprKind::Lit(ref lit4) = init1.kind;
+ if let LitKind::Int(3, LitIntType::Unsuffixed) = lit4.node;
if let PatKind::Binding(BindingAnnotation::Unannotated, _, name, None) = local1.pat.kind;
if name.as_str() == "x";
- if let Some(trailing_expr) = &block.expr;
- if let ExprKind::Path(ref path) = trailing_expr.kind;
- if match_qpath(path, &["x"]);
- if let PatKind::Lit(ref lit_expr1) = arms[1].pat.kind
- if let ExprKind::Lit(ref lit4) = lit_expr1.kind;
- if let LitKind::Int(17, _) = lit4.node;
- if let ExprKind::Lit(ref lit5) = arms[2].body.kind;
- if let LitKind::Int(1, _) = lit5.node;
+ if let Some(trailing_expr) = block.expr;
+ if let ExprKind::Path(ref qpath) = trailing_expr.kind;
+ if match_qpath(qpath, &["x"]);
if let PatKind::Wild = arms[2].pat.kind;
+ if arms[2].guard.is_none();
+ if let ExprKind::Lit(ref lit5) = arms[2].body.kind;
+ if let LitKind::Int(1, LitIntType::Unsuffixed) = lit5.node;
if let PatKind::Binding(BindingAnnotation::Unannotated, _, name1, None) = local.pat.kind;
if name1.as_str() == "a";
then {
--- /dev/null
+#[allow(clippy::no_effect)]
+fn main() {
+ #[clippy::author]
+ [1_u8; 5];
+}
--- /dev/null
+if_chain! {
+ if let ExprKind::Repeat(value, length) = expr.kind;
+ if let ExprKind::Lit(ref lit) = value.kind;
+ if let LitKind::Int(1, LitIntType::Unsigned(UintTy::U8)) = lit.node;
+ let expr1 = &cx.tcx.hir().body(length.body).value;
+ if let ExprKind::Lit(ref lit1) = expr1.kind;
+ if let LitKind::Int(5, LitIntType::Unsuffixed) = lit1.node;
+ then {
+ // report your lint here
+ }
+}
--- /dev/null
+#[allow(clippy::unnecessary_operation, clippy::single_match)]
+fn main() {
+ struct Test {
+ field: u32,
+ }
+
+ #[clippy::author]
+ Test {
+ field: if true { 1 } else { 0 },
+ };
+
+ let test = Test { field: 1 };
+
+ match test {
+ #[clippy::author]
+ Test { field: 1 } => {},
+ _ => {},
+ }
+
+ struct TestTuple(u32);
+
+ let test_tuple = TestTuple(1);
+
+ match test_tuple {
+ #[clippy::author]
+ TestTuple(1) => {},
+ _ => {},
+ }
+
+ struct TestMethodCall(u32);
+
+ impl TestMethodCall {
+ fn test(&self) {}
+ }
+
+ let test_method_call = TestMethodCall(1);
+
+ #[clippy::author]
+ test_method_call.test();
+}
--- /dev/null
+if_chain! {
+ if let ExprKind::Struct(qpath, fields, None) = expr.kind;
+ if match_qpath(qpath, &["Test"]);
+ if fields.len() == 1;
+ if fields[0].ident.as_str() == "field";
+ if let ExprKind::If(cond, then, Some(else_expr)) = fields[0].expr.kind;
+ if let ExprKind::DropTemps(expr1) = cond.kind;
+ if let ExprKind::Lit(ref lit) = expr1.kind;
+ if let LitKind::Bool(true) = lit.node;
+ if let ExprKind::Block(block, None) = then.kind;
+ if block.stmts.is_empty();
+ if let Some(trailing_expr) = block.expr;
+ if let ExprKind::Lit(ref lit1) = trailing_expr.kind;
+ if let LitKind::Int(1, LitIntType::Unsuffixed) = lit1.node;
+ if let ExprKind::Block(block1, None) = else_expr.kind;
+ if block1.stmts.is_empty();
+ if let Some(trailing_expr1) = block1.expr;
+ if let ExprKind::Lit(ref lit2) = trailing_expr1.kind;
+ if let LitKind::Int(0, LitIntType::Unsuffixed) = lit2.node;
+ then {
+ // report your lint here
+ }
+}
+if_chain! {
+ if let PatKind::Struct(ref qpath, fields, false) = arm.pat.kind;
+ if match_qpath(qpath, &["Test"]);
+ if fields.len() == 1;
+ if fields[0].ident.as_str() == "field";
+ if let PatKind::Lit(lit_expr) = fields[0].pat.kind;
+ if let ExprKind::Lit(ref lit) = lit_expr.kind;
+ if let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node;
+ if arm.guard.is_none();
+ if let ExprKind::Block(block, None) = arm.body.kind;
+ if block.stmts.is_empty();
+ if block.expr.is_none();
+ then {
+ // report your lint here
+ }
+}
+if_chain! {
+ if let PatKind::TupleStruct(ref qpath, fields, None) = arm.pat.kind;
+ if match_qpath(qpath, &["TestTuple"]);
+ if fields.len() == 1;
+ if let PatKind::Lit(lit_expr) = fields[0].kind;
+ if let ExprKind::Lit(ref lit) = lit_expr.kind;
+ if let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node;
+ if arm.guard.is_none();
+ if let ExprKind::Block(block, None) = arm.body.kind;
+ if block.stmts.is_empty();
+ if block.expr.is_none();
+ then {
+ // report your lint here
+ }
+}
+if_chain! {
+ if let ExprKind::MethodCall(method_name, _, args, _) = expr.kind;
+ if method_name.ident.as_str() == "test";
+ if args.len() == 1;
+ if let ExprKind::Path(ref qpath) = args[0].kind;
+ if match_qpath(qpath, &["test_method_call"]);
+ then {
+ // report your lint here
+ }
+}
--- /dev/null
+// run-rustfix
+
+#![allow(dead_code)]
+#![warn(clippy::cast_lossless)]
+
+fn main() {
+ // Test clippy::cast_lossless with casts to integer types
+ let _ = u8::from(true);
+ let _ = u16::from(true);
+ let _ = u32::from(true);
+ let _ = u64::from(true);
+ let _ = u128::from(true);
+ let _ = usize::from(true);
+
+ let _ = i8::from(true);
+ let _ = i16::from(true);
+ let _ = i32::from(true);
+ let _ = i64::from(true);
+ let _ = i128::from(true);
+ let _ = isize::from(true);
+
+ // Test with an expression wrapped in parens
+ let _ = u16::from(true | false);
+}
+
+// The lint would suggest using `u32::from(input)` here but the `XX::from` function is not const,
+// so we skip the lint if the expression is in a const fn.
+// See #3656
+const fn abc(input: bool) -> u32 {
+ input as u32
+}
+
+// Same as the above issue. We can't suggest `::from` in const fns in impls
+mod cast_lossless_in_impl {
+ struct A;
+
+ impl A {
+ pub const fn convert(x: bool) -> u64 {
+ x as u64
+ }
+ }
+}
--- /dev/null
+// run-rustfix
+
+#![allow(dead_code)]
+#![warn(clippy::cast_lossless)]
+
+fn main() {
+ // Test clippy::cast_lossless with casts to integer types
+ let _ = true as u8;
+ let _ = true as u16;
+ let _ = true as u32;
+ let _ = true as u64;
+ let _ = true as u128;
+ let _ = true as usize;
+
+ let _ = true as i8;
+ let _ = true as i16;
+ let _ = true as i32;
+ let _ = true as i64;
+ let _ = true as i128;
+ let _ = true as isize;
+
+ // Test with an expression wrapped in parens
+ let _ = (true | false) as u16;
+}
+
+// The lint would suggest using `u32::from(input)` here but the `XX::from` function is not const,
+// so we skip the lint if the expression is in a const fn.
+// See #3656
+const fn abc(input: bool) -> u32 {
+ input as u32
+}
+
+// Same as the above issue. We can't suggest `::from` in const fns in impls
+mod cast_lossless_in_impl {
+ struct A;
+
+ impl A {
+ pub const fn convert(x: bool) -> u64 {
+ x as u64
+ }
+ }
+}
--- /dev/null
+error: casting `bool` to `u8` is more cleanly stated with `u8::from(_)`
+ --> $DIR/cast_lossless_bool.rs:8:13
+ |
+LL | let _ = true as u8;
+ | ^^^^^^^^^^ help: try: `u8::from(true)`
+ |
+ = note: `-D clippy::cast-lossless` implied by `-D warnings`
+
+error: casting `bool` to `u16` is more cleanly stated with `u16::from(_)`
+ --> $DIR/cast_lossless_bool.rs:9:13
+ |
+LL | let _ = true as u16;
+ | ^^^^^^^^^^^ help: try: `u16::from(true)`
+
+error: casting `bool` to `u32` is more cleanly stated with `u32::from(_)`
+ --> $DIR/cast_lossless_bool.rs:10:13
+ |
+LL | let _ = true as u32;
+ | ^^^^^^^^^^^ help: try: `u32::from(true)`
+
+error: casting `bool` to `u64` is more cleanly stated with `u64::from(_)`
+ --> $DIR/cast_lossless_bool.rs:11:13
+ |
+LL | let _ = true as u64;
+ | ^^^^^^^^^^^ help: try: `u64::from(true)`
+
+error: casting `bool` to `u128` is more cleanly stated with `u128::from(_)`
+ --> $DIR/cast_lossless_bool.rs:12:13
+ |
+LL | let _ = true as u128;
+ | ^^^^^^^^^^^^ help: try: `u128::from(true)`
+
+error: casting `bool` to `usize` is more cleanly stated with `usize::from(_)`
+ --> $DIR/cast_lossless_bool.rs:13:13
+ |
+LL | let _ = true as usize;
+ | ^^^^^^^^^^^^^ help: try: `usize::from(true)`
+
+error: casting `bool` to `i8` is more cleanly stated with `i8::from(_)`
+ --> $DIR/cast_lossless_bool.rs:15:13
+ |
+LL | let _ = true as i8;
+ | ^^^^^^^^^^ help: try: `i8::from(true)`
+
+error: casting `bool` to `i16` is more cleanly stated with `i16::from(_)`
+ --> $DIR/cast_lossless_bool.rs:16:13
+ |
+LL | let _ = true as i16;
+ | ^^^^^^^^^^^ help: try: `i16::from(true)`
+
+error: casting `bool` to `i32` is more cleanly stated with `i32::from(_)`
+ --> $DIR/cast_lossless_bool.rs:17:13
+ |
+LL | let _ = true as i32;
+ | ^^^^^^^^^^^ help: try: `i32::from(true)`
+
+error: casting `bool` to `i64` is more cleanly stated with `i64::from(_)`
+ --> $DIR/cast_lossless_bool.rs:18:13
+ |
+LL | let _ = true as i64;
+ | ^^^^^^^^^^^ help: try: `i64::from(true)`
+
+error: casting `bool` to `i128` is more cleanly stated with `i128::from(_)`
+ --> $DIR/cast_lossless_bool.rs:19:13
+ |
+LL | let _ = true as i128;
+ | ^^^^^^^^^^^^ help: try: `i128::from(true)`
+
+error: casting `bool` to `isize` is more cleanly stated with `isize::from(_)`
+ --> $DIR/cast_lossless_bool.rs:20:13
+ |
+LL | let _ = true as isize;
+ | ^^^^^^^^^^^^^ help: try: `isize::from(true)`
+
+error: casting `bool` to `u16` is more cleanly stated with `u16::from(_)`
+ --> $DIR/cast_lossless_bool.rs:23:13
+ |
+LL | let _ = (true | false) as u16;
+ | ^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::from(true | false)`
+
+error: aborting due to 13 previous errors
+
--- /dev/null
+fn zero() {
+ // SAFETY:
+ unsafe { 0 };
+}
--- /dev/null
+#![warn(clippy::undocumented_unsafe_blocks)]
+#![allow(clippy::no_effect)]
+
+#[path = "auxiliary/ice-7934-aux.rs"]
+mod zero;
+
+fn main() {}
--- /dev/null
+#![no_std]
+#![feature(lang_items, start, libc)]
+#![crate_type = "lib"]
+
+use core::panic::PanicInfo;
+
+#[warn(clippy::all)]
+fn main() {
+ let mut a = 42;
+ let mut b = 1337;
+
+ a = b;
+ b = a;
+}
--- /dev/null
+error: this looks like you are trying to swap `a` and `b`
+ --> $DIR/no_std_swap.rs:12:5
+ |
+LL | / a = b;
+LL | | b = a;
+ | |_________^ help: try: `core::mem::swap(&mut a, &mut b)`
+ |
+ = note: `-D clippy::almost-swapped` implied by `-D warnings`
+ = note: or maybe you should use `core::mem::replace`?
+
+error: aborting due to previous error
+
--> $DIR/doc-fixable.rs:9:9
|
LL | /// The foo_bar function does _nothing_. See also foo::bar. (note the dot there)
- | ^^^^^^^ help: try: ``foo_bar``
+ | ^^^^^^^
|
= note: `-D clippy::doc-markdown` implied by `-D warnings`
+help: try
+ |
+LL | /// The `foo_bar` function does _nothing_. See also foo::bar. (note the dot there)
+ | ~~~~~~~~~
error: item in documentation is missing backticks
--> $DIR/doc-fixable.rs:9:51
|
LL | /// The foo_bar function does _nothing_. See also foo::bar. (note the dot there)
- | ^^^^^^^^ help: try: ``foo::bar``
+ | ^^^^^^^^
+ |
+help: try
+ |
+LL | /// The foo_bar function does _nothing_. See also `foo::bar`. (note the dot there)
+ | ~~~~~~~~~~
error: item in documentation is missing backticks
--> $DIR/doc-fixable.rs:10:83
|
LL | /// Markdown is _weird_. I mean _really weird_. This /_ is ok. So is `_`. But not Foo::some_fun
- | ^^^^^^^^^^^^^ help: try: ``Foo::some_fun``
+ | ^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | /// Markdown is _weird_. I mean _really weird_. This /_ is ok. So is `_`. But not `Foo::some_fun`
+ | ~~~~~~~~~~~~~~~
error: item in documentation is missing backticks
--> $DIR/doc-fixable.rs:12:13
|
LL | /// Here be ::a::global:path, and _::another::global::path_. :: is not a path though.
- | ^^^^^^^^^^^^^^^^ help: try: ``::a::global:path``
+ | ^^^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | /// Here be `::a::global:path`, and _::another::global::path_. :: is not a path though.
+ | ~~~~~~~~~~~~~~~~~~
error: item in documentation is missing backticks
--> $DIR/doc-fixable.rs:12:36
|
LL | /// Here be ::a::global:path, and _::another::global::path_. :: is not a path though.
- | ^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``::another::global::path``
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | /// Here be ::a::global:path, and _`::another::global::path`_. :: is not a path though.
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~
error: item in documentation is missing backticks
--> $DIR/doc-fixable.rs:13:25
|
LL | /// Import an item from ::awesome::global::blob:: (Intended postfix)
- | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``::awesome::global::blob::``
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | /// Import an item from `::awesome::global::blob::` (Intended postfix)
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: item in documentation is missing backticks
--> $DIR/doc-fixable.rs:14:31
|
LL | /// These are the options for ::Cat: (Intended trailing single colon, shouldn't be linted)
- | ^^^^^ help: try: ``::Cat``
+ | ^^^^^
+ |
+help: try
+ |
+LL | /// These are the options for `::Cat`: (Intended trailing single colon, shouldn't be linted)
+ | ~~~~~~~
error: item in documentation is missing backticks
--> $DIR/doc-fixable.rs:15:22
|
LL | /// That's not code ~NotInCodeBlock~.
- | ^^^^^^^^^^^^^^ help: try: ``NotInCodeBlock``
+ | ^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | /// That's not code ~`NotInCodeBlock`~.
+ | ~~~~~~~~~~~~~~~~
error: item in documentation is missing backticks
--> $DIR/doc-fixable.rs:16:5
|
LL | /// be_sure_we_got_to_the_end_of_it
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``be_sure_we_got_to_the_end_of_it``
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | /// `be_sure_we_got_to_the_end_of_it`
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: item in documentation is missing backticks
--> $DIR/doc-fixable.rs:30:5
|
LL | /// be_sure_we_got_to_the_end_of_it
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``be_sure_we_got_to_the_end_of_it``
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | /// `be_sure_we_got_to_the_end_of_it`
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: item in documentation is missing backticks
--> $DIR/doc-fixable.rs:37:5
|
LL | /// be_sure_we_got_to_the_end_of_it
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``be_sure_we_got_to_the_end_of_it``
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | /// `be_sure_we_got_to_the_end_of_it`
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: item in documentation is missing backticks
--> $DIR/doc-fixable.rs:51:5
|
LL | /// be_sure_we_got_to_the_end_of_it
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``be_sure_we_got_to_the_end_of_it``
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | /// `be_sure_we_got_to_the_end_of_it`
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: item in documentation is missing backticks
--> $DIR/doc-fixable.rs:74:5
|
LL | /// be_sure_we_got_to_the_end_of_it
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``be_sure_we_got_to_the_end_of_it``
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | /// `be_sure_we_got_to_the_end_of_it`
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: item in documentation is missing backticks
--> $DIR/doc-fixable.rs:78:22
|
LL | /// This test has [a link_with_underscores][chunked-example] inside it. See #823.
- | ^^^^^^^^^^^^^^^^^^^^^ help: try: ``link_with_underscores``
+ | ^^^^^^^^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | /// This test has [a `link_with_underscores`][chunked-example] inside it. See #823.
+ | ~~~~~~~~~~~~~~~~~~~~~~~
error: item in documentation is missing backticks
--> $DIR/doc-fixable.rs:81:21
|
LL | /// It can also be [inline_link2].
- | ^^^^^^^^^^^^ help: try: ``inline_link2``
+ | ^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | /// It can also be [`inline_link2`].
+ | ~~~~~~~~~~~~~~
error: item in documentation is missing backticks
--> $DIR/doc-fixable.rs:91:5
|
LL | /// be_sure_we_got_to_the_end_of_it
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``be_sure_we_got_to_the_end_of_it``
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | /// `be_sure_we_got_to_the_end_of_it`
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: item in documentation is missing backticks
--> $DIR/doc-fixable.rs:99:8
|
LL | /// ## CamelCaseThing
- | ^^^^^^^^^^^^^^ help: try: ``CamelCaseThing``
+ | ^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | /// ## `CamelCaseThing`
+ | ~~~~~~~~~~~~~~~~
error: item in documentation is missing backticks
--> $DIR/doc-fixable.rs:102:7
|
LL | /// # CamelCaseThing
- | ^^^^^^^^^^^^^^ help: try: ``CamelCaseThing``
+ | ^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | /// # `CamelCaseThing`
+ | ~~~~~~~~~~~~~~~~
error: item in documentation is missing backticks
--> $DIR/doc-fixable.rs:104:22
|
LL | /// Not a title #897 CamelCaseThing
- | ^^^^^^^^^^^^^^ help: try: ``CamelCaseThing``
+ | ^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | /// Not a title #897 `CamelCaseThing`
+ | ~~~~~~~~~~~~~~~~
error: item in documentation is missing backticks
--> $DIR/doc-fixable.rs:105:5
|
LL | /// be_sure_we_got_to_the_end_of_it
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``be_sure_we_got_to_the_end_of_it``
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | /// `be_sure_we_got_to_the_end_of_it`
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: item in documentation is missing backticks
--> $DIR/doc-fixable.rs:112:5
|
LL | /// be_sure_we_got_to_the_end_of_it
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``be_sure_we_got_to_the_end_of_it``
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | /// `be_sure_we_got_to_the_end_of_it`
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: item in documentation is missing backticks
--> $DIR/doc-fixable.rs:125:5
|
LL | /// be_sure_we_got_to_the_end_of_it
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``be_sure_we_got_to_the_end_of_it``
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | /// `be_sure_we_got_to_the_end_of_it`
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: item in documentation is missing backticks
--> $DIR/doc-fixable.rs:136:43
|
LL | /** E.g., serialization of an empty list: FooBar
- | ^^^^^^ help: try: ``FooBar``
+ | ^^^^^^
+ |
+help: try
+ |
+LL | /** E.g., serialization of an empty list: `FooBar`
+ | ~~~~~~~~
error: item in documentation is missing backticks
--> $DIR/doc-fixable.rs:141:5
|
LL | And BarQuz too.
- | ^^^^^^ help: try: ``BarQuz``
+ | ^^^^^^
+ |
+help: try
+ |
+LL | And `BarQuz` too.
+ | ~~~~~~~~
error: item in documentation is missing backticks
--> $DIR/doc-fixable.rs:142:1
|
LL | be_sure_we_got_to_the_end_of_it
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``be_sure_we_got_to_the_end_of_it``
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | `be_sure_we_got_to_the_end_of_it`
+ |
error: item in documentation is missing backticks
--> $DIR/doc-fixable.rs:147:43
|
LL | /** E.g., serialization of an empty list: FooBar
- | ^^^^^^ help: try: ``FooBar``
+ | ^^^^^^
+ |
+help: try
+ |
+LL | /** E.g., serialization of an empty list: `FooBar`
+ | ~~~~~~~~
error: item in documentation is missing backticks
--> $DIR/doc-fixable.rs:152:5
|
LL | And BarQuz too.
- | ^^^^^^ help: try: ``BarQuz``
+ | ^^^^^^
+ |
+help: try
+ |
+LL | And `BarQuz` too.
+ | ~~~~~~~~
error: item in documentation is missing backticks
--> $DIR/doc-fixable.rs:153:1
|
LL | be_sure_we_got_to_the_end_of_it
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``be_sure_we_got_to_the_end_of_it``
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | `be_sure_we_got_to_the_end_of_it`
+ |
error: item in documentation is missing backticks
--> $DIR/doc-fixable.rs:164:5
|
LL | /// be_sure_we_got_to_the_end_of_it
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: ``be_sure_we_got_to_the_end_of_it``
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | /// `be_sure_we_got_to_the_end_of_it`
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: item in documentation is missing backticks
--> $DIR/doc-fixable.rs:183:22
|
LL | /// An iterator over mycrate::Collection's values.
- | ^^^^^^^^^^^^^^^^^^^ help: try: ``mycrate::Collection``
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | /// An iterator over `mycrate::Collection`'s values.
+ | ~~~~~~~~~~~~~~~~~~~~~
error: aborting due to 30 previous errors
--> $DIR/unbalanced_ticks.rs:15:32
|
LL | /// This paragraph is fine and should_be linted normally.
- | ^^^^^^^^^ help: try: ``should_be``
+ | ^^^^^^^^^
+ |
+help: try
+ |
+LL | /// This paragraph is fine and `should_be` linted normally.
+ | ~~~~~~~~~~~
error: backticks are unbalanced
--> $DIR/unbalanced_ticks.rs:17:1
--> $DIR/unbalanced_ticks.rs:30:8
|
LL | /// ## not_fine
- | ^^^^^^^^ help: try: ``not_fine``
+ | ^^^^^^^^
+ |
+help: try
+ |
+LL | /// ## `not_fine`
+ | ~~~~~~~~~~
error: backticks are unbalanced
--> $DIR/unbalanced_ticks.rs:32:1
--> $DIR/unbalanced_ticks.rs:35:23
|
LL | /// - This item needs backticks_here
- | ^^^^^^^^^^^^^^ help: try: ``backticks_here``
+ | ^^^^^^^^^^^^^^
+ |
+help: try
+ |
+LL | /// - This item needs `backticks_here`
+ | ~~~~~~~~~~~~~~~~
error: aborting due to 8 previous errors
}
}
}
+
+mod issue_7920 {
+ pub fn test() {
+ let slice = &[1, 2, 3];
+
+ let index_usize: usize = 0;
+ let mut idx_usize: usize = 0;
+
+ // should suggest `enumerate`
+ for _item in slice {
+ if idx_usize == index_usize {
+ break;
+ }
+
+ idx_usize += 1;
+ }
+
+ let index_u32: u32 = 0;
+ let mut idx_u32: u32 = 0;
+
+ // should suggest `zip`
+ for _item in slice {
+ if idx_u32 == index_u32 {
+ break;
+ }
+
+ idx_u32 += 1;
+ }
+ }
+}
LL | for _i in 3..10 {
| ^^^^^^^^^^^^^^^ help: consider using: `for (count, _i) in (3..10).enumerate()`
-error: aborting due to 7 previous errors
+error: the variable `idx_usize` is used as a loop counter
+ --> $DIR/explicit_counter_loop.rs:170:9
+ |
+LL | for _item in slice {
+ | ^^^^^^^^^^^^^^^^^^ help: consider using: `for (idx_usize, _item) in slice.into_iter().enumerate()`
+
+error: the variable `idx_u32` is used as a loop counter
+ --> $DIR/explicit_counter_loop.rs:182:9
+ |
+LL | for _item in slice {
+ | ^^^^^^^^^^^^^^^^^^ help: consider using: `for (idx_u32, _item) in (0_u32..).zip(slice.into_iter())`
+ |
+ = note: `idx_u32` is of type `u32`, making it ineligible for `Iterator::enumerate`
+
+error: aborting due to 9 previous errors
// run-rustfix
+#![feature(const_fn_floating_point_arithmetic)]
#![warn(clippy::suboptimal_flops)]
+/// Allow suboptimal ops in constant context
+pub const fn in_const_context(num: f64) -> f64 {
+ if num >= 0.0 { num } else { -num }
+}
+
struct A {
a: f64,
b: f64,
// run-rustfix
+#![feature(const_fn_floating_point_arithmetic)]
#![warn(clippy::suboptimal_flops)]
+/// Allow suboptimal ops in constant context
+pub const fn in_const_context(num: f64) -> f64 {
+ if num >= 0.0 { num } else { -num }
+}
+
struct A {
a: f64,
b: f64,
error: manual implementation of `abs` method
- --> $DIR/floating_point_abs.rs:10:5
+ --> $DIR/floating_point_abs.rs:16:5
|
LL | if num >= 0.0 { num } else { -num }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.abs()`
= note: `-D clippy::suboptimal-flops` implied by `-D warnings`
error: manual implementation of `abs` method
- --> $DIR/floating_point_abs.rs:14:5
+ --> $DIR/floating_point_abs.rs:20:5
|
LL | if 0.0 < num { num } else { -num }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.abs()`
error: manual implementation of `abs` method
- --> $DIR/floating_point_abs.rs:18:5
+ --> $DIR/floating_point_abs.rs:24:5
|
LL | if a.a > 0.0 { a.a } else { -a.a }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `a.a.abs()`
error: manual implementation of `abs` method
- --> $DIR/floating_point_abs.rs:22:5
+ --> $DIR/floating_point_abs.rs:28:5
|
LL | if 0.0 >= num { -num } else { num }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.abs()`
error: manual implementation of `abs` method
- --> $DIR/floating_point_abs.rs:26:5
+ --> $DIR/floating_point_abs.rs:32:5
|
LL | if a.a < 0.0 { -a.a } else { a.a }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `a.a.abs()`
error: manual implementation of negation of `abs` method
- --> $DIR/floating_point_abs.rs:30:5
+ --> $DIR/floating_point_abs.rs:36:5
|
LL | if num < 0.0 { num } else { -num }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `-num.abs()`
error: manual implementation of negation of `abs` method
- --> $DIR/floating_point_abs.rs:34:5
+ --> $DIR/floating_point_abs.rs:40:5
|
LL | if 0.0 >= num { num } else { -num }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `-num.abs()`
error: manual implementation of negation of `abs` method
- --> $DIR/floating_point_abs.rs:39:12
+ --> $DIR/floating_point_abs.rs:45:12
|
LL | a: if a.a >= 0.0 { -a.a } else { a.a },
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `-a.a.abs()`
// run-rustfix
+#![feature(const_fn_floating_point_arithmetic)]
#![warn(clippy::suboptimal_flops)]
+/// Allow suboptimal_ops in constant context
+pub const fn in_const_context() {
+ let a: f64 = 1234.567;
+ let b: f64 = 45.67834;
+ let c: f64 = 0.0004;
+
+ let _ = a * b + c;
+ let _ = c + a * b;
+}
+
fn main() {
let a: f64 = 1234.567;
let b: f64 = 45.67834;
// run-rustfix
+#![feature(const_fn_floating_point_arithmetic)]
#![warn(clippy::suboptimal_flops)]
+/// Allow suboptimal_ops in constant context
+pub const fn in_const_context() {
+ let a: f64 = 1234.567;
+ let b: f64 = 45.67834;
+ let c: f64 = 0.0004;
+
+ let _ = a * b + c;
+ let _ = c + a * b;
+}
+
fn main() {
let a: f64 = 1234.567;
let b: f64 = 45.67834;
error: multiply and add expressions can be calculated more efficiently and accurately
- --> $DIR/floating_point_mul_add.rs:10:13
+ --> $DIR/floating_point_mul_add.rs:21:13
|
LL | let _ = a * b + c;
| ^^^^^^^^^ help: consider using: `a.mul_add(b, c)`
= note: `-D clippy::suboptimal-flops` implied by `-D warnings`
error: multiply and add expressions can be calculated more efficiently and accurately
- --> $DIR/floating_point_mul_add.rs:11:13
+ --> $DIR/floating_point_mul_add.rs:22:13
|
LL | let _ = c + a * b;
| ^^^^^^^^^ help: consider using: `a.mul_add(b, c)`
error: multiply and add expressions can be calculated more efficiently and accurately
- --> $DIR/floating_point_mul_add.rs:12:13
+ --> $DIR/floating_point_mul_add.rs:23:13
|
LL | let _ = a + 2.0 * 4.0;
| ^^^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(4.0, a)`
error: multiply and add expressions can be calculated more efficiently and accurately
- --> $DIR/floating_point_mul_add.rs:13:13
+ --> $DIR/floating_point_mul_add.rs:24:13
|
LL | let _ = a + 2. * 4.;
| ^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(4., a)`
error: multiply and add expressions can be calculated more efficiently and accurately
- --> $DIR/floating_point_mul_add.rs:15:13
+ --> $DIR/floating_point_mul_add.rs:26:13
|
LL | let _ = (a * b) + c;
| ^^^^^^^^^^^ help: consider using: `a.mul_add(b, c)`
error: multiply and add expressions can be calculated more efficiently and accurately
- --> $DIR/floating_point_mul_add.rs:16:13
+ --> $DIR/floating_point_mul_add.rs:27:13
|
LL | let _ = c + (a * b);
| ^^^^^^^^^^^ help: consider using: `a.mul_add(b, c)`
error: multiply and add expressions can be calculated more efficiently and accurately
- --> $DIR/floating_point_mul_add.rs:17:13
+ --> $DIR/floating_point_mul_add.rs:28:13
|
LL | let _ = a * b * c + d;
| ^^^^^^^^^^^^^ help: consider using: `(a * b).mul_add(c, d)`
error: multiply and add expressions can be calculated more efficiently and accurately
- --> $DIR/floating_point_mul_add.rs:19:13
+ --> $DIR/floating_point_mul_add.rs:30:13
|
LL | let _ = a.mul_add(b, c) * a.mul_add(b, c) + a.mul_add(b, c) + c;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `a.mul_add(b, c).mul_add(a.mul_add(b, c), a.mul_add(b, c))`
error: multiply and add expressions can be calculated more efficiently and accurately
- --> $DIR/floating_point_mul_add.rs:20:13
+ --> $DIR/floating_point_mul_add.rs:31:13
|
LL | let _ = 1234.567_f64 * 45.67834_f64 + 0.0004_f64;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1234.567_f64.mul_add(45.67834_f64, 0.0004_f64)`
error: multiply and add expressions can be calculated more efficiently and accurately
- --> $DIR/floating_point_mul_add.rs:22:13
+ --> $DIR/floating_point_mul_add.rs:33:13
|
LL | let _ = (a * a + b).sqrt();
| ^^^^^^^^^^^ help: consider using: `a.mul_add(a, b)`
// run-rustfix
+#![feature(const_fn_floating_point_arithmetic)]
#![warn(clippy::suboptimal_flops)]
+/// Allow suboptimal_flops in constant context
+pub const fn const_context() {
+ let x = 3f32;
+ let _ = x * 180f32 / std::f32::consts::PI;
+}
+
fn main() {
let x = 3f32;
let _ = x.to_degrees();
// run-rustfix
+#![feature(const_fn_floating_point_arithmetic)]
#![warn(clippy::suboptimal_flops)]
+/// Allow suboptimal_flops in constant context
+pub const fn const_context() {
+ let x = 3f32;
+ let _ = x * 180f32 / std::f32::consts::PI;
+}
+
fn main() {
let x = 3f32;
let _ = x * 180f32 / std::f32::consts::PI;
error: conversion to degrees can be done more accurately
- --> $DIR/floating_point_rad.rs:6:13
+ --> $DIR/floating_point_rad.rs:13:13
|
LL | let _ = x * 180f32 / std::f32::consts::PI;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.to_degrees()`
= note: `-D clippy::suboptimal-flops` implied by `-D warnings`
error: conversion to radians can be done more accurately
- --> $DIR/floating_point_rad.rs:7:13
+ --> $DIR/floating_point_rad.rs:14:13
|
LL | let _ = x * std::f32::consts::PI / 180f32;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.to_radians()`
fn into_none<T>() -> Option<T> {
None
}
+
+// Should not warn
+fn f(b: bool, v: Option<()>) -> Option<()> {
+ if b {
+ v?; // This is a potential early return, is not equivalent with `bool::then`
+
+ Some(())
+ } else {
+ None
+ }
+}
--- /dev/null
+#![deny(clippy::index_refutable_slice)]
+
+enum SomeEnum<T> {
+ One(T),
+ Two(T),
+ Three(T),
+ Four(T),
+}
+
+fn lintable_examples() {
+ // Try with reference
+ let slice: Option<&[u32]> = Some(&[1, 2, 3]);
+ if let Some(slice) = slice {
+ println!("{}", slice[0]);
+ }
+
+ // Try with copy
+ let slice: Option<[u32; 3]> = Some([1, 2, 3]);
+ if let Some(slice) = slice {
+ println!("{}", slice[0]);
+ }
+
+ // Try with long slice and small indices
+ let slice: Option<[u32; 9]> = Some([1, 2, 3, 4, 5, 6, 7, 8, 9]);
+ if let Some(slice) = slice {
+ println!("{}", slice[2]);
+ println!("{}", slice[0]);
+ }
+
+ // Multiple bindings
+ let slice_wrapped: SomeEnum<[u32; 3]> = SomeEnum::One([5, 6, 7]);
+ if let SomeEnum::One(slice) | SomeEnum::Three(slice) = slice_wrapped {
+ println!("{}", slice[0]);
+ }
+
+ // Two lintable slices in one if let
+ let a_wrapped: SomeEnum<[u32; 3]> = SomeEnum::One([9, 5, 1]);
+ let b_wrapped: Option<[u32; 2]> = Some([4, 6]);
+ if let (SomeEnum::Three(a), Some(b)) = (a_wrapped, b_wrapped) {
+ println!("{} -> {}", a[2], b[1]);
+ }
+
+ // This requires the slice values to be borrowed as the slice values can only be
+ // borrowed and `String` doesn't implement copy
+ let slice: Option<[String; 2]> = Some([String::from("1"), String::from("2")]);
+ if let Some(ref slice) = slice {
+ println!("{:?}", slice[1]);
+ }
+ println!("{:?}", slice);
+
+ // This should not suggest using the `ref` keyword as the scrutinee is already
+ // a reference
+ let slice: Option<[String; 2]> = Some([String::from("1"), String::from("2")]);
+ if let Some(slice) = &slice {
+ println!("{:?}", slice[0]);
+ }
+ println!("{:?}", slice);
+}
+
+fn slice_index_above_limit() {
+ let slice: Option<&[u32]> = Some(&[1, 2, 3]);
+
+ if let Some(slice) = slice {
+ // Would cause a panic, IDK
+ println!("{}", slice[7]);
+ }
+}
+
+fn slice_is_used() {
+ let slice: Option<&[u32]> = Some(&[1, 2, 3]);
+ if let Some(slice) = slice {
+ println!("{:?}", slice.len());
+ }
+
+ let slice: Option<&[u32]> = Some(&[1, 2, 3]);
+ if let Some(slice) = slice {
+ println!("{:?}", slice.to_vec());
+ }
+
+ let opt: Option<[String; 2]> = Some([String::from("Hello"), String::from("world")]);
+ if let Some(slice) = opt {
+ if !slice.is_empty() {
+ println!("first: {}", slice[0]);
+ }
+ }
+}
+
+/// The slice is used by an external function and should therefore not be linted
+fn check_slice_as_arg() {
+ fn is_interesting<T>(slice: &[T; 2]) -> bool {
+ !slice.is_empty()
+ }
+
+ let slice_wrapped: Option<[String; 2]> = Some([String::from("Hello"), String::from("world")]);
+ if let Some(slice) = &slice_wrapped {
+ if is_interesting(slice) {
+ println!("This is interesting {}", slice[0]);
+ }
+ }
+ println!("{:?}", slice_wrapped);
+}
+
+fn check_slice_in_struct() {
+ #[derive(Debug)]
+ struct Wrapper<'a> {
+ inner: Option<&'a [String]>,
+ is_awesome: bool,
+ }
+
+ impl<'a> Wrapper<'a> {
+ fn is_super_awesome(&self) -> bool {
+ self.is_awesome
+ }
+ }
+
+ let inner = &[String::from("New"), String::from("World")];
+ let wrap = Wrapper {
+ inner: Some(inner),
+ is_awesome: true,
+ };
+
+ // Test 1: Field access
+ if let Some(slice) = wrap.inner {
+ if wrap.is_awesome {
+ println!("This is awesome! {}", slice[0]);
+ }
+ }
+
+ // Test 2: function access
+ if let Some(slice) = wrap.inner {
+ if wrap.is_super_awesome() {
+ println!("This is super awesome! {}", slice[0]);
+ }
+ }
+ println!("Complete wrap: {:?}", wrap);
+}
+
+/// This would be a nice additional feature to have in the future, but adding it
+/// now would make the PR too large. This is therefore only a test that we don't
+/// lint cases we can't make a reasonable suggestion for
+fn mutable_slice_index() {
+ // Mut access
+ let mut slice: Option<[String; 1]> = Some([String::from("Penguin")]);
+ if let Some(ref mut slice) = slice {
+ slice[0] = String::from("Mr. Penguin");
+ }
+ println!("Use after modification: {:?}", slice);
+
+ // Mut access on reference
+ let mut slice: Option<[String; 1]> = Some([String::from("Cat")]);
+ if let Some(slice) = &mut slice {
+ slice[0] = String::from("Lord Meow Meow");
+ }
+ println!("Use after modification: {:?}", slice);
+}
+
+/// The lint will ignore bindings with sub patterns as it would be hard
+/// to build correct suggestions for these instances :)
+fn binding_with_sub_pattern() {
+ let slice: Option<&[u32]> = Some(&[1, 2, 3]);
+ if let Some(slice @ [_, _, _]) = slice {
+ println!("{:?}", slice[2]);
+ }
+}
+
+fn main() {}
--- /dev/null
+error: this binding can be a slice pattern to avoid indexing
+ --> $DIR/if_let_slice_binding.rs:13:17
+ |
+LL | if let Some(slice) = slice {
+ | ^^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/if_let_slice_binding.rs:1:9
+ |
+LL | #![deny(clippy::index_refutable_slice)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: try using a slice pattern here
+ |
+LL | if let Some([slice_0, ..]) = slice {
+ | ~~~~~~~~~~~~~
+help: and replace the index expressions here
+ |
+LL | println!("{}", slice_0);
+ | ~~~~~~~
+
+error: this binding can be a slice pattern to avoid indexing
+ --> $DIR/if_let_slice_binding.rs:19:17
+ |
+LL | if let Some(slice) = slice {
+ | ^^^^^
+ |
+help: try using a slice pattern here
+ |
+LL | if let Some([slice_0, ..]) = slice {
+ | ~~~~~~~~~~~~~
+help: and replace the index expressions here
+ |
+LL | println!("{}", slice_0);
+ | ~~~~~~~
+
+error: this binding can be a slice pattern to avoid indexing
+ --> $DIR/if_let_slice_binding.rs:25:17
+ |
+LL | if let Some(slice) = slice {
+ | ^^^^^
+ |
+help: try using a slice pattern here
+ |
+LL | if let Some([slice_0, _, slice_2, ..]) = slice {
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~
+help: and replace the index expressions here
+ |
+LL ~ println!("{}", slice_2);
+LL ~ println!("{}", slice_0);
+ |
+
+error: this binding can be a slice pattern to avoid indexing
+ --> $DIR/if_let_slice_binding.rs:32:26
+ |
+LL | if let SomeEnum::One(slice) | SomeEnum::Three(slice) = slice_wrapped {
+ | ^^^^^
+ |
+help: try using a slice pattern here
+ |
+LL | if let SomeEnum::One([slice_0, ..]) | SomeEnum::Three([slice_0, ..]) = slice_wrapped {
+ | ~~~~~~~~~~~~~ ~~~~~~~~~~~~~
+help: and replace the index expressions here
+ |
+LL | println!("{}", slice_0);
+ | ~~~~~~~
+
+error: this binding can be a slice pattern to avoid indexing
+ --> $DIR/if_let_slice_binding.rs:39:29
+ |
+LL | if let (SomeEnum::Three(a), Some(b)) = (a_wrapped, b_wrapped) {
+ | ^
+ |
+help: try using a slice pattern here
+ |
+LL | if let (SomeEnum::Three([_, _, a_2, ..]), Some(b)) = (a_wrapped, b_wrapped) {
+ | ~~~~~~~~~~~~~~~
+help: and replace the index expressions here
+ |
+LL | println!("{} -> {}", a_2, b[1]);
+ | ~~~
+
+error: this binding can be a slice pattern to avoid indexing
+ --> $DIR/if_let_slice_binding.rs:39:38
+ |
+LL | if let (SomeEnum::Three(a), Some(b)) = (a_wrapped, b_wrapped) {
+ | ^
+ |
+help: try using a slice pattern here
+ |
+LL | if let (SomeEnum::Three(a), Some([_, b_1, ..])) = (a_wrapped, b_wrapped) {
+ | ~~~~~~~~~~~~
+help: and replace the index expressions here
+ |
+LL | println!("{} -> {}", a[2], b_1);
+ | ~~~
+
+error: this binding can be a slice pattern to avoid indexing
+ --> $DIR/if_let_slice_binding.rs:46:21
+ |
+LL | if let Some(ref slice) = slice {
+ | ^^^^^
+ |
+help: try using a slice pattern here
+ |
+LL | if let Some([_, ref slice_1, ..]) = slice {
+ | ~~~~~~~~~~~~~~~~~~~~
+help: and replace the index expressions here
+ |
+LL | println!("{:?}", slice_1);
+ | ~~~~~~~
+
+error: this binding can be a slice pattern to avoid indexing
+ --> $DIR/if_let_slice_binding.rs:54:17
+ |
+LL | if let Some(slice) = &slice {
+ | ^^^^^
+ |
+help: try using a slice pattern here
+ |
+LL | if let Some([slice_0, ..]) = &slice {
+ | ~~~~~~~~~~~~~
+help: and replace the index expressions here
+ |
+LL | println!("{:?}", slice_0);
+ | ~~~~~~~
+
+error: this binding can be a slice pattern to avoid indexing
+ --> $DIR/if_let_slice_binding.rs:123:17
+ |
+LL | if let Some(slice) = wrap.inner {
+ | ^^^^^
+ |
+help: try using a slice pattern here
+ |
+LL | if let Some([slice_0, ..]) = wrap.inner {
+ | ~~~~~~~~~~~~~
+help: and replace the index expressions here
+ |
+LL | println!("This is awesome! {}", slice_0);
+ | ~~~~~~~
+
+error: this binding can be a slice pattern to avoid indexing
+ --> $DIR/if_let_slice_binding.rs:130:17
+ |
+LL | if let Some(slice) = wrap.inner {
+ | ^^^^^
+ |
+help: try using a slice pattern here
+ |
+LL | if let Some([slice_0, ..]) = wrap.inner {
+ | ~~~~~~~~~~~~~
+help: and replace the index expressions here
+ |
+LL | println!("This is super awesome! {}", slice_0);
+ | ~~~~~~~
+
+error: aborting due to 10 previous errors
+
--- /dev/null
+#![deny(clippy::index_refutable_slice)]
+
+extern crate if_chain;
+use if_chain::if_chain;
+
+macro_rules! if_let_slice_macro {
+ () => {
+ // This would normally be linted
+ let slice: Option<&[u32]> = Some(&[1, 2, 3]);
+ if let Some(slice) = slice {
+ println!("{}", slice[0]);
+ }
+ };
+}
+
+fn main() {
+ // Don't lint this
+ if_let_slice_macro!();
+
+ // Do lint this
+ if_chain! {
+ let slice: Option<&[u32]> = Some(&[1, 2, 3]);
+ if let Some(slice) = slice;
+ then {
+ println!("{}", slice[0]);
+ }
+ }
+}
--- /dev/null
+error: this binding can be a slice pattern to avoid indexing
+ --> $DIR/slice_indexing_in_macro.rs:23:21
+ |
+LL | if let Some(slice) = slice;
+ | ^^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/slice_indexing_in_macro.rs:1:9
+ |
+LL | #![deny(clippy::index_refutable_slice)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: try using a slice pattern here
+ |
+LL | if let Some([slice_0, ..]) = slice;
+ | ~~~~~~~~~~~~~
+help: and replace the index expressions here
+ |
+LL | println!("{}", slice_0);
+ | ~~~~~~~
+
+error: aborting due to previous error
+
// Issue #6808
let arr: [u8; 64] = [0; 64];
let _: Vec<_> = arr.to_vec();
+
+ // Issue #6703
+ let _: Vec<isize> = v.to_vec();
}
// Issue #6808
let arr: [u8; 64] = [0; 64];
let _: Vec<_> = arr.iter().cloned().collect();
+
+ // Issue #6703
+ let _: Vec<isize> = v.iter().copied().collect();
}
LL | let _: Vec<_> = arr.iter().cloned().collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.to_vec()`
-error: aborting due to 4 previous errors
+error: called `iter().copied().collect()` on a slice to create a `Vec`. Calling `to_vec()` is both faster and more readable
+ --> $DIR/iter_cloned_collect.rs:31:26
+ |
+LL | let _: Vec<isize> = v.iter().copied().collect();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.to_vec()`
+
+error: aborting due to 5 previous errors
unused_assignments,
clippy::similar_names,
clippy::blacklisted_name,
- clippy::branches_sharing_code
+ clippy::branches_sharing_code,
+ clippy::needless_late_init
)]
#![warn(clippy::useless_let_if_seq)]
error: `if _ { .. } else { .. }` is an expression
- --> $DIR/let_if_seq.rs:65:5
+ --> $DIR/let_if_seq.rs:66:5
|
LL | / let mut foo = 0;
LL | | if f() {
= note: you might not need `mut` at all
error: `if _ { .. } else { .. }` is an expression
- --> $DIR/let_if_seq.rs:70:5
+ --> $DIR/let_if_seq.rs:71:5
|
LL | / let mut bar = 0;
LL | | if f() {
= note: you might not need `mut` at all
error: `if _ { .. } else { .. }` is an expression
- --> $DIR/let_if_seq.rs:78:5
+ --> $DIR/let_if_seq.rs:79:5
|
LL | / let quz;
LL | | if f() {
| |_____^ help: it is more idiomatic to write: `let quz = if f() { 42 } else { 0 };`
error: `if _ { .. } else { .. }` is an expression
- --> $DIR/let_if_seq.rs:107:5
+ --> $DIR/let_if_seq.rs:108:5
|
LL | / let mut baz = 0;
LL | | if f() {
#![warn(clippy::let_underscore_lock)]
+extern crate parking_lot;
+
fn main() {
let m = std::sync::Mutex::new(());
let rw = std::sync::RwLock::new(());
let _ = m.try_lock();
let _ = rw.try_read();
let _ = rw.try_write();
+
+ use parking_lot::{lock_api::RawMutex, Mutex, RwLock};
+
+ let p_m: Mutex<()> = Mutex::const_new(RawMutex::INIT, ());
+ let _ = p_m.lock();
+
+ let p_m1 = Mutex::new(0);
+ let _ = p_m1.lock();
+
+ let p_rw = RwLock::new(0);
+ let _ = p_rw.read();
+ let _ = p_rw.write();
}
error: non-binding let on a synchronization lock
- --> $DIR/let_underscore_lock.rs:7:5
+ --> $DIR/let_underscore_lock.rs:9:5
|
LL | let _ = m.lock();
| ^^^^^^^^^^^^^^^^^
= help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
error: non-binding let on a synchronization lock
- --> $DIR/let_underscore_lock.rs:8:5
+ --> $DIR/let_underscore_lock.rs:10:5
|
LL | let _ = rw.read();
| ^^^^^^^^^^^^^^^^^^
= help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
error: non-binding let on a synchronization lock
- --> $DIR/let_underscore_lock.rs:9:5
+ --> $DIR/let_underscore_lock.rs:11:5
|
LL | let _ = rw.write();
| ^^^^^^^^^^^^^^^^^^^
= help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
error: non-binding let on a synchronization lock
- --> $DIR/let_underscore_lock.rs:10:5
+ --> $DIR/let_underscore_lock.rs:12:5
|
LL | let _ = m.try_lock();
| ^^^^^^^^^^^^^^^^^^^^^
= help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
error: non-binding let on a synchronization lock
- --> $DIR/let_underscore_lock.rs:11:5
+ --> $DIR/let_underscore_lock.rs:13:5
|
LL | let _ = rw.try_read();
| ^^^^^^^^^^^^^^^^^^^^^^
= help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
error: non-binding let on a synchronization lock
- --> $DIR/let_underscore_lock.rs:12:5
+ --> $DIR/let_underscore_lock.rs:14:5
|
LL | let _ = rw.try_write();
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
-error: aborting due to 6 previous errors
+error: non-binding let on a synchronization lock
+ --> $DIR/let_underscore_lock.rs:19:5
+ |
+LL | let _ = p_m.lock();
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
+
+error: non-binding let on a synchronization lock
+ --> $DIR/let_underscore_lock.rs:22:5
+ |
+LL | let _ = p_m1.lock();
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
+
+error: non-binding let on a synchronization lock
+ --> $DIR/let_underscore_lock.rs:25:5
+ |
+LL | let _ = p_rw.read();
+ | ^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
+
+error: non-binding let on a synchronization lock
+ --> $DIR/let_underscore_lock.rs:26:5
+ |
+LL | let _ = p_rw.write();
+ | ^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
+
+error: aborting due to 10 previous errors
// [edition2018] edition:2018
// [edition2021] edition:2021
// run-rustfix
+
#![warn(clippy::manual_assert)]
+#![allow(clippy::nonminimal_bool)]
fn main() {
let a = vec![1, 2, 3];
error: only a `panic!` in `if`-then statement
- --> $DIR/manual_assert.rs:22:5
+ --> $DIR/manual_assert.rs:24:5
|
LL | / if !a.is_empty() {
LL | | panic!("qaqaq{:?}", a);
= note: `-D clippy::manual-assert` implied by `-D warnings`
error: only a `panic!` in `if`-then statement
- --> $DIR/manual_assert.rs:25:5
+ --> $DIR/manual_assert.rs:27:5
|
LL | / if !a.is_empty() {
LL | | panic!("qwqwq");
| |_____^ help: try: `assert!(a.is_empty(), "qwqwq");`
error: only a `panic!` in `if`-then statement
- --> $DIR/manual_assert.rs:42:5
+ --> $DIR/manual_assert.rs:44:5
|
LL | / if b.is_empty() {
LL | | panic!("panic1");
| |_____^ help: try: `assert!(!b.is_empty(), "panic1");`
error: only a `panic!` in `if`-then statement
- --> $DIR/manual_assert.rs:45:5
+ --> $DIR/manual_assert.rs:47:5
|
LL | / if b.is_empty() && a.is_empty() {
LL | | panic!("panic2");
| |_____^ help: try: `assert!(!(b.is_empty() && a.is_empty()), "panic2");`
error: only a `panic!` in `if`-then statement
- --> $DIR/manual_assert.rs:48:5
+ --> $DIR/manual_assert.rs:50:5
|
LL | / if a.is_empty() && !b.is_empty() {
LL | | panic!("panic3");
| |_____^ help: try: `assert!(!(a.is_empty() && !b.is_empty()), "panic3");`
error: only a `panic!` in `if`-then statement
- --> $DIR/manual_assert.rs:51:5
+ --> $DIR/manual_assert.rs:53:5
|
LL | / if b.is_empty() || a.is_empty() {
LL | | panic!("panic4");
| |_____^ help: try: `assert!(!(b.is_empty() || a.is_empty()), "panic4");`
error: only a `panic!` in `if`-then statement
- --> $DIR/manual_assert.rs:54:5
+ --> $DIR/manual_assert.rs:56:5
|
LL | / if a.is_empty() || !b.is_empty() {
LL | | panic!("panic5");
// [edition2018] edition:2018
// [edition2021] edition:2021
// run-rustfix
+
#![warn(clippy::manual_assert)]
+#![allow(clippy::nonminimal_bool)]
fn main() {
let a = vec![1, 2, 3];
error: only a `panic!` in `if`-then statement
- --> $DIR/manual_assert.rs:22:5
+ --> $DIR/manual_assert.rs:24:5
|
LL | / if !a.is_empty() {
LL | | panic!("qaqaq{:?}", a);
= note: `-D clippy::manual-assert` implied by `-D warnings`
error: only a `panic!` in `if`-then statement
- --> $DIR/manual_assert.rs:25:5
+ --> $DIR/manual_assert.rs:27:5
|
LL | / if !a.is_empty() {
LL | | panic!("qwqwq");
| |_____^ help: try: `assert!(a.is_empty(), "qwqwq");`
error: only a `panic!` in `if`-then statement
- --> $DIR/manual_assert.rs:42:5
+ --> $DIR/manual_assert.rs:44:5
|
LL | / if b.is_empty() {
LL | | panic!("panic1");
| |_____^ help: try: `assert!(!b.is_empty(), "panic1");`
error: only a `panic!` in `if`-then statement
- --> $DIR/manual_assert.rs:45:5
+ --> $DIR/manual_assert.rs:47:5
|
LL | / if b.is_empty() && a.is_empty() {
LL | | panic!("panic2");
| |_____^ help: try: `assert!(!(b.is_empty() && a.is_empty()), "panic2");`
error: only a `panic!` in `if`-then statement
- --> $DIR/manual_assert.rs:48:5
+ --> $DIR/manual_assert.rs:50:5
|
LL | / if a.is_empty() && !b.is_empty() {
LL | | panic!("panic3");
| |_____^ help: try: `assert!(!(a.is_empty() && !b.is_empty()), "panic3");`
error: only a `panic!` in `if`-then statement
- --> $DIR/manual_assert.rs:51:5
+ --> $DIR/manual_assert.rs:53:5
|
LL | / if b.is_empty() || a.is_empty() {
LL | | panic!("panic4");
| |_____^ help: try: `assert!(!(b.is_empty() || a.is_empty()), "panic4");`
error: only a `panic!` in `if`-then statement
- --> $DIR/manual_assert.rs:54:5
+ --> $DIR/manual_assert.rs:56:5
|
LL | / if a.is_empty() || !b.is_empty() {
LL | | panic!("panic5");
// [edition2018] edition:2018
// [edition2021] edition:2021
// run-rustfix
+
#![warn(clippy::manual_assert)]
+#![allow(clippy::nonminimal_bool)]
fn main() {
let a = vec![1, 2, 3];
// [edition2018] edition:2018
// [edition2021] edition:2021
// run-rustfix
+
#![warn(clippy::manual_assert)]
+#![allow(clippy::nonminimal_bool)]
fn main() {
let a = vec![1, 2, 3];
let _ = s.as_ref().map(|x| {
if let Some(ref s) = s { (x.clone(), s) } else { panic!() }
});
+
+ // Issue #7820
+ unsafe fn f(x: u32) -> u32 {
+ x
+ }
+ unsafe {
+ let _ = Some(0).map(|x| f(x));
+ }
+ let _ = Some(0).map(|x| unsafe { f(x) });
+ let _ = Some(0).map(|x| unsafe { f(x) });
}
}),
None => None,
};
+
+ // Issue #7820
+ unsafe fn f(x: u32) -> u32 {
+ x
+ }
+ unsafe {
+ let _ = match Some(0) {
+ Some(x) => Some(f(x)),
+ None => None,
+ };
+ }
+ let _ = match Some(0) {
+ Some(x) => unsafe { Some(f(x)) },
+ None => None,
+ };
+ let _ = match Some(0) {
+ Some(x) => Some(unsafe { f(x) }),
+ None => None,
+ };
}
LL ~ });
|
-error: aborting due to 2 previous errors
+error: manual implementation of `Option::map`
+ --> $DIR/manual_map_option_2.rs:62:17
+ |
+LL | let _ = match Some(0) {
+ | _________________^
+LL | | Some(x) => Some(f(x)),
+LL | | None => None,
+LL | | };
+ | |_________^ help: try this: `Some(0).map(|x| f(x))`
+
+error: manual implementation of `Option::map`
+ --> $DIR/manual_map_option_2.rs:67:13
+ |
+LL | let _ = match Some(0) {
+ | _____________^
+LL | | Some(x) => unsafe { Some(f(x)) },
+LL | | None => None,
+LL | | };
+ | |_____^ help: try this: `Some(0).map(|x| unsafe { f(x) })`
+
+error: manual implementation of `Option::map`
+ --> $DIR/manual_map_option_2.rs:71:13
+ |
+LL | let _ = match Some(0) {
+ | _____________^
+LL | | Some(x) => Some(unsafe { f(x) }),
+LL | | None => None,
+LL | | };
+ | |_____^ help: try this: `Some(0).map(|x| unsafe { f(x) })`
+
+error: aborting due to 5 previous errors
#![feature(custom_inner_attributes)]
#![warn(clippy::manual_split_once)]
-#![allow(clippy::iter_skip_next, clippy::iter_nth_zero)]
+#![allow(clippy::iter_skip_next, clippy::iter_nth_zero, clippy::needless_splitn)]
extern crate itertools;
let _ = [0, 1, 2].splitn(2, |&x| x == 1).nth(1).unwrap();
// `rsplitn` gives the results in the reverse order of `rsplit_once`
- let _ = "key=value".rsplit_once('=').unwrap().1;
- let _ = "key=value".rsplit_once('=').map_or("key=value", |x| x.0);
+ let _ = "key=value".rsplitn(2, '=').next().unwrap();
+ let _ = "key=value".rsplit_once('=').unwrap().0;
let _ = "key=value".rsplit_once('=').map(|x| x.1);
let (_, _) = "key=value".rsplit_once('=').map(|(x, y)| (y, x)).unwrap();
}
#![feature(custom_inner_attributes)]
#![warn(clippy::manual_split_once)]
-#![allow(clippy::iter_skip_next, clippy::iter_nth_zero)]
+#![allow(clippy::iter_skip_next, clippy::iter_nth_zero, clippy::needless_splitn)]
extern crate itertools;
LL | let _ = s.splitn(2, "key=value").skip(1).next()?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once("key=value")?.1`
-error: manual implementation of `rsplit_once`
- --> $DIR/manual_split_once.rs:41:13
- |
-LL | let _ = "key=value".rsplitn(2, '=').next().unwrap();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').unwrap().1`
-
error: manual implementation of `rsplit_once`
--> $DIR/manual_split_once.rs:42:13
|
LL | let _ = "key=value".rsplitn(2, '=').nth(1).unwrap();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').map_or("key=value", |x| x.0)`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').unwrap().0`
error: manual implementation of `rsplit_once`
--> $DIR/manual_split_once.rs:43:13
LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1`
-error: aborting due to 17 previous errors
+error: aborting due to 16 previous errors
_ => (),
}
+ // Issue #7816 - overlap after included range
+ match 42 {
+ 5..=10 => (),
+ 0..=20 => (),
+ 21..=30 => (),
+ 21..=40 => (),
+ _ => (),
+ }
+
// Issue #7829
match 0 {
-1..=1 => (),
_ => (),
}
+ // Only warn about the first if there are multiple overlaps
+ match 42u128 {
+ 0..=0x0000_0000_0000_00ff => (),
+ 0..=0x0000_0000_0000_ffff => (),
+ 0..=0x0000_0000_ffff_ffff => (),
+ 0..=0xffff_ffff_ffff_ffff => (),
+ _ => (),
+ }
+
if let None = Some(42) {
// nothing
} else if let None = Some(42) {
LL | ..26 => println!("..26"),
| ^^^^
-error: aborting due to 6 previous errors
+error: some ranges overlap
+ --> $DIR/match_overlapping_arm.rs:107:9
+ |
+LL | 21..=30 => (),
+ | ^^^^^^^
+ |
+note: overlaps with this
+ --> $DIR/match_overlapping_arm.rs:108:9
+ |
+LL | 21..=40 => (),
+ | ^^^^^^^
+
+error: some ranges overlap
+ --> $DIR/match_overlapping_arm.rs:121:9
+ |
+LL | 0..=0x0000_0000_0000_00ff => (),
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: overlaps with this
+ --> $DIR/match_overlapping_arm.rs:122:9
+ |
+LL | 0..=0x0000_0000_0000_ffff => (),
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 8 previous errors
}
fn main() {
- let x;
- x = 2usize;
+ let x = 2usize;
min(1, max(3, x));
min(max(3, x), 1);
max(min(x, 1), 3);
let y = 2isize;
min(max(y, -1), 3);
- let s;
- s = "Hello";
-
+ let s = "Hello";
min("Apple", max("Zoo", s));
max(min(s, "Apple"), "Zoo");
error: this `min`/`max` combination leads to constant result
- --> $DIR/min_max.rs:24:5
+ --> $DIR/min_max.rs:23:5
|
LL | min(1, max(3, x));
| ^^^^^^^^^^^^^^^^^
= note: `-D clippy::min-max` implied by `-D warnings`
error: this `min`/`max` combination leads to constant result
- --> $DIR/min_max.rs:25:5
+ --> $DIR/min_max.rs:24:5
|
LL | min(max(3, x), 1);
| ^^^^^^^^^^^^^^^^^
error: this `min`/`max` combination leads to constant result
- --> $DIR/min_max.rs:26:5
+ --> $DIR/min_max.rs:25:5
|
LL | max(min(x, 1), 3);
| ^^^^^^^^^^^^^^^^^
error: this `min`/`max` combination leads to constant result
- --> $DIR/min_max.rs:27:5
+ --> $DIR/min_max.rs:26:5
|
LL | max(3, min(x, 1));
| ^^^^^^^^^^^^^^^^^
error: this `min`/`max` combination leads to constant result
- --> $DIR/min_max.rs:29:5
+ --> $DIR/min_max.rs:28:5
|
LL | my_max(3, my_min(x, 1));
| ^^^^^^^^^^^^^^^^^^^^^^^
error: this `min`/`max` combination leads to constant result
- --> $DIR/min_max.rs:41:5
+ --> $DIR/min_max.rs:38:5
|
LL | min("Apple", max("Zoo", s));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: this `min`/`max` combination leads to constant result
- --> $DIR/min_max.rs:42:5
+ --> $DIR/min_max.rs:39:5
|
LL | max(min(s, "Apple"), "Zoo");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: this `min`/`max` combination leads to constant result
- --> $DIR/min_max.rs:47:5
+ --> $DIR/min_max.rs:44:5
|
LL | x.min(1).max(3);
| ^^^^^^^^^^^^^^^
error: this `min`/`max` combination leads to constant result
- --> $DIR/min_max.rs:48:5
+ --> $DIR/min_max.rs:45:5
|
LL | x.max(3).min(1);
| ^^^^^^^^^^^^^^^
error: this `min`/`max` combination leads to constant result
- --> $DIR/min_max.rs:49:5
+ --> $DIR/min_max.rs:46:5
|
LL | f.max(3f32).min(1f32);
| ^^^^^^^^^^^^^^^^^^^^^
error: this `min`/`max` combination leads to constant result
- --> $DIR/min_max.rs:55:5
+ --> $DIR/min_max.rs:52:5
|
LL | max(x.min(1), 3);
| ^^^^^^^^^^^^^^^^
error: this `min`/`max` combination leads to constant result
- --> $DIR/min_max.rs:58:5
+ --> $DIR/min_max.rs:55:5
|
LL | s.max("Zoo").min("Apple");
| ^^^^^^^^^^^^^^^^^^^^^^^^^
error: this `min`/`max` combination leads to constant result
- --> $DIR/min_max.rs:59:5
+ --> $DIR/min_max.rs:56:5
|
LL | s.min("Apple").max("Zoo");
| ^^^^^^^^^^^^^^^^^^^^^^^^^
if let TS(0, x) | TS(1, x) = TS(0, 0) {}
}
+#[cfg_attr(rustfmt, rustfmt_skip)]
+fn deprecated_cfg_attr() {}
+
+#[warn(clippy::cast_lossless)]
+fn int_from_bool() -> u8 {
+ true as u8
+}
+
fn main() {
filter_map_next();
checked_conversion();
map_unwrap_or();
missing_const_for_fn();
unnest_or_patterns();
+ int_from_bool();
}
-mod meets_msrv {
+mod just_under_msrv {
#![feature(custom_inner_attributes)]
- #![clippy::msrv = "1.45.0"]
+ #![clippy::msrv = "1.44.0"]
fn main() {
let s = "hello, world!";
}
}
-mod just_under_msrv {
+mod meets_msrv {
#![feature(custom_inner_attributes)]
- #![clippy::msrv = "1.46.0"]
+ #![clippy::msrv = "1.45.0"]
fn main() {
let s = "hello, world!";
mod just_above_msrv {
#![feature(custom_inner_attributes)]
- #![clippy::msrv = "1.44.0"]
+ #![clippy::msrv = "1.46.0"]
fn main() {
let s = "hello, world!";
error: stripping a prefix manually
- --> $DIR/min_rust_version_attr.rs:165:24
+ --> $DIR/min_rust_version_attr.rs:186:24
|
LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
| ^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::manual-strip` implied by `-D warnings`
note: the prefix was tested here
- --> $DIR/min_rust_version_attr.rs:164:9
+ --> $DIR/min_rust_version_attr.rs:185:9
|
LL | if s.starts_with("hello, ") {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
error: stripping a prefix manually
- --> $DIR/min_rust_version_attr.rs:177:24
+ --> $DIR/min_rust_version_attr.rs:198:24
|
LL | assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
| ^^^^^^^^^^^^^^^^^^^^
|
note: the prefix was tested here
- --> $DIR/min_rust_version_attr.rs:176:9
+ --> $DIR/min_rust_version_attr.rs:197:9
|
LL | if s.starts_with("hello, ") {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// run-rustfix
#[warn(clippy::all, clippy::needless_borrow)]
-#[allow(unused_variables)]
+#[allow(unused_variables, clippy::unnecessary_mut_passed)]
fn main() {
let a = 5;
+ let ref_a = &a;
let _ = x(&a); // no warning
let _ = x(&a); // warn
44 => &a,
45 => {
println!("foo");
- &&a // FIXME: this should lint, too
+ &a
},
46 => &a,
+ 47 => {
+ println!("foo");
+ loop {
+ println!("{}", a);
+ if a == 25 {
+ break ref_a;
+ }
+ }
+ },
_ => panic!(),
};
+
+ let _ = x(&a);
+ let _ = x(&a);
+ let _ = x(&mut b);
+ let _ = x(ref_a);
+ {
+ let b = &mut b;
+ x(b);
+ }
}
#[allow(clippy::needless_borrowed_reference)]
// run-rustfix
#[warn(clippy::all, clippy::needless_borrow)]
-#[allow(unused_variables)]
+#[allow(unused_variables, clippy::unnecessary_mut_passed)]
fn main() {
let a = 5;
+ let ref_a = &a;
let _ = x(&a); // no warning
let _ = x(&&a); // warn
44 => &a,
45 => {
println!("foo");
- &&a // FIXME: this should lint, too
+ &&a
},
46 => &&a,
+ 47 => {
+ println!("foo");
+ loop {
+ println!("{}", a);
+ if a == 25 {
+ break &ref_a;
+ }
+ }
+ },
_ => panic!(),
};
+
+ let _ = x(&&&a);
+ let _ = x(&mut &&a);
+ let _ = x(&&&mut b);
+ let _ = x(&&ref_a);
+ {
+ let b = &mut b;
+ x(&b);
+ }
}
#[allow(clippy::needless_borrowed_reference)]
error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler
- --> $DIR/needless_borrow.rs:8:15
+ --> $DIR/needless_borrow.rs:9:15
|
LL | let _ = x(&&a); // warn
| ^^^ help: change this to: `&a`
= note: `-D clippy::needless-borrow` implied by `-D warnings`
error: this expression borrows a reference (`&mut i32`) that is immediately dereferenced by the compiler
- --> $DIR/needless_borrow.rs:12:13
+ --> $DIR/needless_borrow.rs:13:13
|
LL | mut_ref(&mut &mut b); // warn
| ^^^^^^^^^^^ help: change this to: `&mut b`
error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler
- --> $DIR/needless_borrow.rs:26:15
+ --> $DIR/needless_borrow.rs:25:13
+ |
+LL | &&a
+ | ^^^ help: change this to: `&a`
+
+error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler
+ --> $DIR/needless_borrow.rs:27:15
|
LL | 46 => &&a,
| ^^^ help: change this to: `&a`
-error: aborting due to 3 previous errors
+error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler
+ --> $DIR/needless_borrow.rs:33:27
+ |
+LL | break &ref_a;
+ | ^^^^^^ help: change this to: `ref_a`
+
+error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler
+ --> $DIR/needless_borrow.rs:40:15
+ |
+LL | let _ = x(&&&a);
+ | ^^^^ help: change this to: `&a`
+
+error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler
+ --> $DIR/needless_borrow.rs:41:15
+ |
+LL | let _ = x(&mut &&a);
+ | ^^^^^^^^ help: change this to: `&a`
+
+error: this expression borrows a reference (`&mut i32`) that is immediately dereferenced by the compiler
+ --> $DIR/needless_borrow.rs:42:15
+ |
+LL | let _ = x(&&&mut b);
+ | ^^^^^^^^ help: change this to: `&mut b`
+
+error: this expression borrows a reference (`&i32`) that is immediately dereferenced by the compiler
+ --> $DIR/needless_borrow.rs:43:15
+ |
+LL | let _ = x(&&ref_a);
+ | ^^^^^^^ help: change this to: `ref_a`
+
+error: this expression borrows a reference (`&mut i32`) that is immediately dereferenced by the compiler
+ --> $DIR/needless_borrow.rs:46:11
+ |
+LL | x(&b);
+ | ^^ help: change this to: `b`
+
+error: aborting due to 10 previous errors
}
}
+mod issue7975 {
+ use super::*;
+
+ fn direct_mapping_with_used_mutable_reference() -> Vec<()> {
+ let test_vec: Vec<()> = vec![];
+ let mut vec_2: Vec<()> = vec![];
+ let mut_ref = &mut vec_2;
+ let collected_vec: Vec<_> = test_vec.into_iter().map(|_| mut_ref.push(())).collect();
+ collected_vec.into_iter().map(|_| mut_ref.push(())).collect()
+ }
+
+ fn indirectly_mapping_with_used_mutable_reference() -> Vec<()> {
+ let test_vec: Vec<()> = vec![];
+ let mut vec_2: Vec<()> = vec![];
+ let mut_ref = &mut vec_2;
+ let collected_vec: Vec<_> = test_vec.into_iter().map(|_| mut_ref.push(())).collect();
+ let iter = collected_vec.into_iter();
+ iter.map(|_| mut_ref.push(())).collect()
+ }
+
+ fn indirect_collect_after_indirect_mapping_with_used_mutable_reference() -> Vec<()> {
+ let test_vec: Vec<()> = vec![];
+ let mut vec_2: Vec<()> = vec![];
+ let mut_ref = &mut vec_2;
+ let collected_vec: Vec<_> = test_vec.into_iter().map(|_| mut_ref.push(())).collect();
+ let iter = collected_vec.into_iter();
+ let mapped_iter = iter.map(|_| mut_ref.push(()));
+ mapped_iter.collect()
+ }
+}
+
fn allow_test() {
#[allow(clippy::needless_collect)]
let v = [1].iter().collect::<Vec<_>>();
--- /dev/null
+#![allow(unused)]
+
+fn main() {
+ let a;
+ let n = 1;
+ match n {
+ 1 => a = "one",
+ _ => {
+ a = "two";
+ },
+ }
+
+ let b;
+ if n == 3 {
+ b = "four";
+ } else {
+ b = "five"
+ }
+
+ let c;
+ if let Some(n) = Some(5) {
+ c = n;
+ } else {
+ c = -50;
+ }
+
+ let d;
+ if true {
+ let temp = 5;
+ d = temp;
+ } else {
+ d = 15;
+ }
+
+ let e;
+ if true {
+ e = format!("{} {}", a, b);
+ } else {
+ e = format!("{}", c);
+ }
+
+ println!("{}", a);
+}
+
+async fn in_async() -> &'static str {
+ async fn f() -> &'static str {
+ "one"
+ }
+
+ let a;
+ let n = 1;
+ match n {
+ 1 => a = f().await,
+ _ => {
+ a = "two";
+ },
+ }
+
+ a
+}
+
+const fn in_const() -> &'static str {
+ const fn f() -> &'static str {
+ "one"
+ }
+
+ let a;
+ let n = 1;
+ match n {
+ 1 => a = f(),
+ _ => {
+ a = "two";
+ },
+ }
+
+ a
+}
+
+fn does_not_lint() {
+ let z;
+ if false {
+ z = 1;
+ }
+
+ let x;
+ let y;
+ if true {
+ x = 1;
+ } else {
+ y = 1;
+ }
+
+ let mut x;
+ if true {
+ x = 5;
+ x = 10 / x;
+ } else {
+ x = 2;
+ }
+
+ let x;
+ let _ = match 1 {
+ 1 => x = 10,
+ _ => x = 20,
+ };
+
+ // using tuples would be possible, but not always preferable
+ let x;
+ let y;
+ if true {
+ x = 1;
+ y = 2;
+ } else {
+ x = 3;
+ y = 4;
+ }
+
+ // could match with a smarter heuristic to avoid multiple assignments
+ let x;
+ if true {
+ let mut y = 5;
+ y = 6;
+ x = y;
+ } else {
+ x = 2;
+ }
+
+ let (x, y);
+ if true {
+ x = 1;
+ } else {
+ x = 2;
+ }
+ y = 3;
+
+ macro_rules! assign {
+ ($i:ident) => {
+ $i = 1;
+ };
+ }
+ let x;
+ assign!(x);
+
+ let x;
+ if true {
+ assign!(x);
+ } else {
+ x = 2;
+ }
+
+ macro_rules! in_macro {
+ () => {
+ let x;
+ x = 1;
+
+ let x;
+ if true {
+ x = 1;
+ } else {
+ x = 2;
+ }
+ };
+ }
+ in_macro!();
+
+ println!("{}", x);
+}
--- /dev/null
+error: unneeded late initalization
+ --> $DIR/needless_late_init.rs:4:5
+ |
+LL | let a;
+ | ^^^^^^
+ |
+ = note: `-D clippy::needless-late-init` implied by `-D warnings`
+help: declare `a` here
+ |
+LL | let a = match n {
+ | +++++++
+help: remove the assignments from the `match` arms
+ |
+LL ~ 1 => "one",
+LL | _ => {
+LL ~ "two"
+ |
+help: add a semicolon after the `match` expression
+ |
+LL | };
+ | +
+
+error: unneeded late initalization
+ --> $DIR/needless_late_init.rs:13:5
+ |
+LL | let b;
+ | ^^^^^^
+ |
+help: declare `b` here
+ |
+LL | let b = if n == 3 {
+ | +++++++
+help: remove the assignments from the branches
+ |
+LL ~ "four"
+LL | } else {
+LL ~ "five"
+ |
+help: add a semicolon after the `if` expression
+ |
+LL | };
+ | +
+
+error: unneeded late initalization
+ --> $DIR/needless_late_init.rs:20:5
+ |
+LL | let c;
+ | ^^^^^^
+ |
+help: declare `c` here
+ |
+LL | let c = if let Some(n) = Some(5) {
+ | +++++++
+help: remove the assignments from the branches
+ |
+LL ~ n
+LL | } else {
+LL ~ -50
+ |
+help: add a semicolon after the `if` expression
+ |
+LL | };
+ | +
+
+error: unneeded late initalization
+ --> $DIR/needless_late_init.rs:27:5
+ |
+LL | let d;
+ | ^^^^^^
+ |
+help: declare `d` here
+ |
+LL | let d = if true {
+ | +++++++
+help: remove the assignments from the branches
+ |
+LL ~ temp
+LL | } else {
+LL ~ 15
+ |
+help: add a semicolon after the `if` expression
+ |
+LL | };
+ | +
+
+error: unneeded late initalization
+ --> $DIR/needless_late_init.rs:35:5
+ |
+LL | let e;
+ | ^^^^^^
+ |
+help: declare `e` here
+ |
+LL | let e = if true {
+ | +++++++
+help: remove the assignments from the branches
+ |
+LL ~ format!("{} {}", a, b)
+LL | } else {
+LL ~ format!("{}", c)
+ |
+help: add a semicolon after the `if` expression
+ |
+LL | };
+ | +
+
+error: unneeded late initalization
+ --> $DIR/needless_late_init.rs:50:5
+ |
+LL | let a;
+ | ^^^^^^
+ |
+help: declare `a` here
+ |
+LL | let a = match n {
+ | +++++++
+help: remove the assignments from the `match` arms
+ |
+LL ~ 1 => f().await,
+LL | _ => {
+LL ~ "two"
+ |
+help: add a semicolon after the `match` expression
+ |
+LL | };
+ | +
+
+error: unneeded late initalization
+ --> $DIR/needless_late_init.rs:67:5
+ |
+LL | let a;
+ | ^^^^^^
+ |
+help: declare `a` here
+ |
+LL | let a = match n {
+ | +++++++
+help: remove the assignments from the `match` arms
+ |
+LL ~ 1 => f(),
+LL | _ => {
+LL ~ "two"
+ |
+help: add a semicolon after the `match` expression
+ |
+LL | };
+ | +
+
+error: aborting due to 7 previous errors
+
--- /dev/null
+// run-rustfix
+
+#![allow(unused, clippy::assign_op_pattern)]
+
+fn main() {
+
+ let a = "zero";
+
+
+
+ let b = 1;
+ let c = 2;
+
+
+ let d: usize = 1;
+
+
+ let mut e = 1;
+ e = 2;
+
+
+ let f = match 1 {
+ 1 => "three",
+ _ => return,
+ }; // has semi
+
+
+ let g: usize = if true {
+ 5
+ } else {
+ panic!();
+ };
+
+
+ let h = format!("{}", e);
+
+ println!("{}", a);
+}
--- /dev/null
+// run-rustfix
+
+#![allow(unused, clippy::assign_op_pattern)]
+
+fn main() {
+ let a;
+ a = "zero";
+
+ let b;
+ let c;
+ b = 1;
+ c = 2;
+
+ let d: usize;
+ d = 1;
+
+ let mut e;
+ e = 1;
+ e = 2;
+
+ let f;
+ match 1 {
+ 1 => f = "three",
+ _ => return,
+ }; // has semi
+
+ let g: usize;
+ if true {
+ g = 5;
+ } else {
+ panic!();
+ }
+
+ let h;
+ h = format!("{}", e);
+
+ println!("{}", a);
+}
--- /dev/null
+error: unneeded late initalization
+ --> $DIR/needless_late_init_fixable.rs:6:5
+ |
+LL | let a;
+ | ^^^^^^
+ |
+ = note: `-D clippy::needless-late-init` implied by `-D warnings`
+help: declare `a` here
+ |
+LL | let a = "zero";
+ | ~~~~~
+
+error: unneeded late initalization
+ --> $DIR/needless_late_init_fixable.rs:9:5
+ |
+LL | let b;
+ | ^^^^^^
+ |
+help: declare `b` here
+ |
+LL | let b = 1;
+ | ~~~~~
+
+error: unneeded late initalization
+ --> $DIR/needless_late_init_fixable.rs:10:5
+ |
+LL | let c;
+ | ^^^^^^
+ |
+help: declare `c` here
+ |
+LL | let c = 2;
+ | ~~~~~
+
+error: unneeded late initalization
+ --> $DIR/needless_late_init_fixable.rs:14:5
+ |
+LL | let d: usize;
+ | ^^^^^^^^^^^^^
+ |
+help: declare `d` here
+ |
+LL | let d: usize = 1;
+ | ~~~~~~~~~~~~
+
+error: unneeded late initalization
+ --> $DIR/needless_late_init_fixable.rs:17:5
+ |
+LL | let mut e;
+ | ^^^^^^^^^^
+ |
+help: declare `e` here
+ |
+LL | let mut e = 1;
+ | ~~~~~~~~~
+
+error: unneeded late initalization
+ --> $DIR/needless_late_init_fixable.rs:21:5
+ |
+LL | let f;
+ | ^^^^^^
+ |
+help: declare `f` here
+ |
+LL | let f = match 1 {
+ | +++++++
+help: remove the assignments from the `match` arms
+ |
+LL | 1 => "three",
+ | ~~~~~~~
+
+error: unneeded late initalization
+ --> $DIR/needless_late_init_fixable.rs:27:5
+ |
+LL | let g: usize;
+ | ^^^^^^^^^^^^^
+ |
+help: declare `g` here
+ |
+LL | let g: usize = if true {
+ | ++++++++++++++
+help: remove the assignments from the branches
+ |
+LL | 5
+ |
+help: add a semicolon after the `if` expression
+ |
+LL | };
+ | +
+
+error: unneeded late initalization
+ --> $DIR/needless_late_init_fixable.rs:34:5
+ |
+LL | let h;
+ | ^^^^^^
+ |
+help: declare `h` here
+ |
+LL | let h = format!("{}", e);
+ | ~~~~~
+
+error: aborting due to 8 previous errors
+
--> $DIR/needless_question_mark.rs:23:12
|
LL | return Some(to.magic?);
- | ^^^^^^^^^^^^^^^ help: try: `to.magic`
+ | ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic`
|
= note: `-D clippy::needless-question-mark` implied by `-D warnings`
--> $DIR/needless_question_mark.rs:31:12
|
LL | return Some(to.magic?)
- | ^^^^^^^^^^^^^^^ help: try: `to.magic`
+ | ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic`
error: question mark operator is useless here
--> $DIR/needless_question_mark.rs:36:5
|
LL | Some(to.magic?)
- | ^^^^^^^^^^^^^^^ help: try: `to.magic`
+ | ^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `to.magic`
error: question mark operator is useless here
--> $DIR/needless_question_mark.rs:41:21
|
LL | to.and_then(|t| Some(t.magic?))
- | ^^^^^^^^^^^^^^ help: try: `t.magic`
+ | ^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `t.magic`
error: question mark operator is useless here
--> $DIR/needless_question_mark.rs:50:9
|
LL | Some(t.magic?)
- | ^^^^^^^^^^^^^^ help: try: `t.magic`
+ | ^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `t.magic`
error: question mark operator is useless here
--> $DIR/needless_question_mark.rs:55:12
|
LL | return Ok(tr.magic?);
- | ^^^^^^^^^^^^^ help: try: `tr.magic`
+ | ^^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `tr.magic`
error: question mark operator is useless here
--> $DIR/needless_question_mark.rs:62:12
|
LL | return Ok(tr.magic?)
- | ^^^^^^^^^^^^^ help: try: `tr.magic`
+ | ^^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `tr.magic`
error: question mark operator is useless here
--> $DIR/needless_question_mark.rs:66:5
|
LL | Ok(tr.magic?)
- | ^^^^^^^^^^^^^ help: try: `tr.magic`
+ | ^^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `tr.magic`
error: question mark operator is useless here
--> $DIR/needless_question_mark.rs:70:21
|
LL | tr.and_then(|t| Ok(t.magic?))
- | ^^^^^^^^^^^^ help: try: `t.magic`
+ | ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `t.magic`
error: question mark operator is useless here
--> $DIR/needless_question_mark.rs:78:9
|
LL | Ok(t.magic?)
- | ^^^^^^^^^^^^ help: try: `t.magic`
+ | ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `t.magic`
error: question mark operator is useless here
--> $DIR/needless_question_mark.rs:85:16
|
LL | return Ok(t.magic?);
- | ^^^^^^^^^^^^ help: try: `t.magic`
+ | ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `t.magic`
error: question mark operator is useless here
--> $DIR/needless_question_mark.rs:120:27
|
LL | || -> Option<_> { Some(Some($expr)?) }()
- | ^^^^^^^^^^^^^^^^^^ help: try: `Some($expr)`
+ | ^^^^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `Some($expr)`
...
LL | let _x = some_and_qmark_in_macro!(x?);
| ---------------------------- in this macro invocation
}
mod issue6501 {
+ #[allow(clippy::unnecessary_lazy_evaluations)]
fn foo(bar: Result<(), ()>) {
bar.unwrap_or_else(|_| {})
}
}
mod issue6501 {
+ #[allow(clippy::unnecessary_lazy_evaluations)]
fn foo(bar: Result<(), ()>) {
bar.unwrap_or_else(|_| return)
}
| ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:112:32
+ --> $DIR/needless_return.rs:113:32
|
LL | bar.unwrap_or_else(|_| return)
| ^^^^^^ help: replace `return` with an empty block: `{}`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:117:13
+ --> $DIR/needless_return.rs:118:13
|
LL | return;
| ^^^^^^^ help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:119:20
+ --> $DIR/needless_return.rs:120:20
|
LL | let _ = || return;
| ^^^^^^ help: replace `return` with an empty block: `{}`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:125:32
+ --> $DIR/needless_return.rs:126:32
|
LL | res.unwrap_or_else(|_| return Foo)
| ^^^^^^^^^^ help: remove `return`: `Foo`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:134:5
+ --> $DIR/needless_return.rs:135:5
|
LL | return true;
| ^^^^^^^^^^^^ help: remove `return`: `true`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:138:5
+ --> $DIR/needless_return.rs:139:5
|
LL | return true;
| ^^^^^^^^^^^^ help: remove `return`: `true`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:143:9
+ --> $DIR/needless_return.rs:144:9
|
LL | return true;
| ^^^^^^^^^^^^ help: remove `return`: `true`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:145:9
+ --> $DIR/needless_return.rs:146:9
|
LL | return false;
| ^^^^^^^^^^^^^ help: remove `return`: `false`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:151:17
+ --> $DIR/needless_return.rs:152:17
|
LL | true => return false,
| ^^^^^^^^^^^^ help: remove `return`: `false`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:153:13
+ --> $DIR/needless_return.rs:154:13
|
LL | return true;
| ^^^^^^^^^^^^ help: remove `return`: `true`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:160:9
+ --> $DIR/needless_return.rs:161:9
|
LL | return true;
| ^^^^^^^^^^^^ help: remove `return`: `true`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:162:16
+ --> $DIR/needless_return.rs:163:16
|
LL | let _ = || return true;
| ^^^^^^^^^^^ help: remove `return`: `true`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:170:5
+ --> $DIR/needless_return.rs:171:5
|
LL | return;
| ^^^^^^^ help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:175:9
+ --> $DIR/needless_return.rs:176:9
|
LL | return;
| ^^^^^^^ help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:177:9
+ --> $DIR/needless_return.rs:178:9
|
LL | return;
| ^^^^^^^ help: remove `return`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:184:14
+ --> $DIR/needless_return.rs:185:14
|
LL | _ => return,
| ^^^^^^ help: replace `return` with an empty block: `{}`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:199:9
+ --> $DIR/needless_return.rs:200:9
|
LL | return String::from("test");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")`
error: unneeded `return` statement
- --> $DIR/needless_return.rs:201:9
+ --> $DIR/needless_return.rs:202:9
|
LL | return String::new();
| ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()`
--- /dev/null
+// run-rustfix
+// edition:2018
+
+#![feature(custom_inner_attributes)]
+#![warn(clippy::needless_splitn)]
+#![allow(clippy::iter_skip_next, clippy::iter_nth_zero, clippy::manual_split_once)]
+
+extern crate itertools;
+
+#[allow(unused_imports)]
+use itertools::Itertools;
+
+fn main() {
+ let str = "key=value=end";
+ let _ = str.split('=').next();
+ let _ = str.split('=').nth(0);
+ let _ = str.splitn(2, '=').nth(1);
+ let (_, _) = str.splitn(2, '=').next_tuple().unwrap();
+ let (_, _) = str.split('=').next_tuple().unwrap();
+ let _: Vec<&str> = str.splitn(3, '=').collect();
+
+ let _ = str.rsplit('=').next();
+ let _ = str.rsplit('=').nth(0);
+ let _ = str.rsplitn(2, '=').nth(1);
+ let (_, _) = str.rsplitn(2, '=').next_tuple().unwrap();
+ let (_, _) = str.rsplit('=').next_tuple().unwrap();
+}
--- /dev/null
+// run-rustfix
+// edition:2018
+
+#![feature(custom_inner_attributes)]
+#![warn(clippy::needless_splitn)]
+#![allow(clippy::iter_skip_next, clippy::iter_nth_zero, clippy::manual_split_once)]
+
+extern crate itertools;
+
+#[allow(unused_imports)]
+use itertools::Itertools;
+
+fn main() {
+ let str = "key=value=end";
+ let _ = str.splitn(2, '=').next();
+ let _ = str.splitn(2, '=').nth(0);
+ let _ = str.splitn(2, '=').nth(1);
+ let (_, _) = str.splitn(2, '=').next_tuple().unwrap();
+ let (_, _) = str.splitn(3, '=').next_tuple().unwrap();
+ let _: Vec<&str> = str.splitn(3, '=').collect();
+
+ let _ = str.rsplitn(2, '=').next();
+ let _ = str.rsplitn(2, '=').nth(0);
+ let _ = str.rsplitn(2, '=').nth(1);
+ let (_, _) = str.rsplitn(2, '=').next_tuple().unwrap();
+ let (_, _) = str.rsplitn(3, '=').next_tuple().unwrap();
+}
--- /dev/null
+error: unnecessary use of `splitn`
+ --> $DIR/needless_splitn.rs:15:13
+ |
+LL | let _ = str.splitn(2, '=').next();
+ | ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')`
+ |
+ = note: `-D clippy::needless-splitn` implied by `-D warnings`
+
+error: unnecessary use of `splitn`
+ --> $DIR/needless_splitn.rs:16:13
+ |
+LL | let _ = str.splitn(2, '=').nth(0);
+ | ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')`
+
+error: unnecessary use of `splitn`
+ --> $DIR/needless_splitn.rs:19:18
+ |
+LL | let (_, _) = str.splitn(3, '=').next_tuple().unwrap();
+ | ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')`
+
+error: unnecessary use of `rsplitn`
+ --> $DIR/needless_splitn.rs:22:13
+ |
+LL | let _ = str.rsplitn(2, '=').next();
+ | ^^^^^^^^^^^^^^^^^^^ help: try this: `str.rsplit('=')`
+
+error: unnecessary use of `rsplitn`
+ --> $DIR/needless_splitn.rs:23:13
+ |
+LL | let _ = str.rsplitn(2, '=').nth(0);
+ | ^^^^^^^^^^^^^^^^^^^ help: try this: `str.rsplit('=')`
+
+error: unnecessary use of `rsplitn`
+ --> $DIR/needless_splitn.rs:26:18
+ |
+LL | let (_, _) = str.rsplitn(3, '=').next_tuple().unwrap();
+ | ^^^^^^^^^^^^^^^^^^^ help: try this: `str.rsplit('=')`
+
+error: aborting due to 6 previous errors
+
-#![feature(box_syntax)]
+#![feature(box_syntax, fn_traits, unboxed_closures)]
#![warn(clippy::no_effect_underscore_binding)]
#![allow(dead_code)]
#![allow(path_statements)]
0
}
+struct GreetStruct1;
+
+impl FnOnce<(&str,)> for GreetStruct1 {
+ type Output = ();
+
+ extern "rust-call" fn call_once(self, (who,): (&str,)) -> Self::Output {
+ println!("hello {}", who);
+ }
+}
+
+struct GreetStruct2();
+
+impl FnOnce<(&str,)> for GreetStruct2 {
+ type Output = ();
+
+ extern "rust-call" fn call_once(self, (who,): (&str,)) -> Self::Output {
+ println!("hello {}", who);
+ }
+}
+
+struct GreetStruct3 {}
+
+impl FnOnce<(&str,)> for GreetStruct3 {
+ type Output = ();
+
+ extern "rust-call" fn call_once(self, (who,): (&str,)) -> Self::Output {
+ println!("hello {}", who);
+ }
+}
+
fn main() {
let s = get_struct();
let s2 = get_struct();
DropTuple(0);
DropEnum::Tuple(0);
DropEnum::Struct { field: 0 };
+ GreetStruct1("world");
+ GreetStruct2()("world");
+ GreetStruct3 {}("world");
}
error: statement with no effect
- --> $DIR/no_effect.rs:65:5
+ --> $DIR/no_effect.rs:95:5
|
LL | 0;
| ^^
= note: `-D clippy::no-effect` implied by `-D warnings`
error: statement with no effect
- --> $DIR/no_effect.rs:66:5
+ --> $DIR/no_effect.rs:96:5
|
LL | s2;
| ^^^
error: statement with no effect
- --> $DIR/no_effect.rs:67:5
+ --> $DIR/no_effect.rs:97:5
|
LL | Unit;
| ^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:68:5
+ --> $DIR/no_effect.rs:98:5
|
LL | Tuple(0);
| ^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:69:5
+ --> $DIR/no_effect.rs:99:5
|
LL | Struct { field: 0 };
| ^^^^^^^^^^^^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:70:5
+ --> $DIR/no_effect.rs:100:5
|
LL | Struct { ..s };
| ^^^^^^^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:71:5
+ --> $DIR/no_effect.rs:101:5
|
LL | Union { a: 0 };
| ^^^^^^^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:72:5
+ --> $DIR/no_effect.rs:102:5
|
LL | Enum::Tuple(0);
| ^^^^^^^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:73:5
+ --> $DIR/no_effect.rs:103:5
|
LL | Enum::Struct { field: 0 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:74:5
+ --> $DIR/no_effect.rs:104:5
|
LL | 5 + 6;
| ^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:75:5
+ --> $DIR/no_effect.rs:105:5
|
LL | *&42;
| ^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:76:5
+ --> $DIR/no_effect.rs:106:5
|
LL | &6;
| ^^^
error: statement with no effect
- --> $DIR/no_effect.rs:77:5
+ --> $DIR/no_effect.rs:107:5
|
LL | (5, 6, 7);
| ^^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:78:5
+ --> $DIR/no_effect.rs:108:5
|
LL | box 42;
| ^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:79:5
+ --> $DIR/no_effect.rs:109:5
|
LL | ..;
| ^^^
error: statement with no effect
- --> $DIR/no_effect.rs:80:5
+ --> $DIR/no_effect.rs:110:5
|
LL | 5..;
| ^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:81:5
+ --> $DIR/no_effect.rs:111:5
|
LL | ..5;
| ^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:82:5
+ --> $DIR/no_effect.rs:112:5
|
LL | 5..6;
| ^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:83:5
+ --> $DIR/no_effect.rs:113:5
|
LL | 5..=6;
| ^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:84:5
+ --> $DIR/no_effect.rs:114:5
|
LL | [42, 55];
| ^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:85:5
+ --> $DIR/no_effect.rs:115:5
|
LL | [42, 55][1];
| ^^^^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:86:5
+ --> $DIR/no_effect.rs:116:5
|
LL | (42, 55).1;
| ^^^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:87:5
+ --> $DIR/no_effect.rs:117:5
|
LL | [42; 55];
| ^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:88:5
+ --> $DIR/no_effect.rs:118:5
|
LL | [42; 55][13];
| ^^^^^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:90:5
+ --> $DIR/no_effect.rs:120:5
|
LL | || x += 5;
| ^^^^^^^^^^
error: statement with no effect
- --> $DIR/no_effect.rs:92:5
+ --> $DIR/no_effect.rs:122:5
|
LL | FooString { s: s };
| ^^^^^^^^^^^^^^^^^^^
error: binding to `_` prefixed variable with no side-effect
- --> $DIR/no_effect.rs:93:5
+ --> $DIR/no_effect.rs:123:5
|
LL | let _unused = 1;
| ^^^^^^^^^^^^^^^^
= note: `-D clippy::no-effect-underscore-binding` implied by `-D warnings`
error: binding to `_` prefixed variable with no side-effect
- --> $DIR/no_effect.rs:94:5
+ --> $DIR/no_effect.rs:124:5
|
LL | let _penguin = || println!("Some helpful closure");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: binding to `_` prefixed variable with no side-effect
- --> $DIR/no_effect.rs:95:5
+ --> $DIR/no_effect.rs:125:5
|
LL | let _duck = Struct { field: 0 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: binding to `_` prefixed variable with no side-effect
- --> $DIR/no_effect.rs:96:5
+ --> $DIR/no_effect.rs:126:5
|
LL | let _cat = [2, 4, 6, 8][2];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
unsafe impl<T> Send for MyOption<T> {}
+// Test types that contain `NonNull` instead of raw pointers (#8045)
+pub struct WrappedNonNull(UnsafeCell<NonNull<()>>);
+
+unsafe impl Send for WrappedNonNull {}
+
// Multiple type parameters
pub struct MultiParam<A, B> {
vec: Vec<(A, B)>,
= help: add `T: Send` bound in `Send` impl
error: this implementation is unsound, as some fields in `MultiParam<A, B>` are `!Send`
- --> $DIR/non_send_fields_in_send_ty.rs:77:1
+ --> $DIR/non_send_fields_in_send_ty.rs:82:1
|
LL | unsafe impl<A, B> Send for MultiParam<A, B> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: the type of field `vec` is `!Send`
- --> $DIR/non_send_fields_in_send_ty.rs:74:5
+ --> $DIR/non_send_fields_in_send_ty.rs:79:5
|
LL | vec: Vec<(A, B)>,
| ^^^^^^^^^^^^^^^^
= help: add bounds on type parameters `A, B` that satisfy `Vec<(A, B)>: Send`
error: this implementation is unsound, as some fields in `HeuristicTest` are `!Send`
- --> $DIR/non_send_fields_in_send_ty.rs:95:1
+ --> $DIR/non_send_fields_in_send_ty.rs:100:1
|
LL | unsafe impl Send for HeuristicTest {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: the type of field `field4` is `!Send`
- --> $DIR/non_send_fields_in_send_ty.rs:90:5
+ --> $DIR/non_send_fields_in_send_ty.rs:95:5
|
LL | field4: (*const NonSend, Rc<u8>),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: use a thread-safe type that implements `Send`
error: this implementation is unsound, as some fields in `AttrTest3<T>` are `!Send`
- --> $DIR/non_send_fields_in_send_ty.rs:114:1
+ --> $DIR/non_send_fields_in_send_ty.rs:119:1
|
LL | unsafe impl<T> Send for AttrTest3<T> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: the type of field `0` is `!Send`
- --> $DIR/non_send_fields_in_send_ty.rs:109:11
+ --> $DIR/non_send_fields_in_send_ty.rs:114:11
|
LL | Enum2(T),
| ^
= help: add `T: Send` bound in `Send` impl
error: this implementation is unsound, as some fields in `Complex<P, u32>` are `!Send`
- --> $DIR/non_send_fields_in_send_ty.rs:122:1
+ --> $DIR/non_send_fields_in_send_ty.rs:127:1
|
LL | unsafe impl<P> Send for Complex<P, u32> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: the type of field `field1` is `!Send`
- --> $DIR/non_send_fields_in_send_ty.rs:118:5
+ --> $DIR/non_send_fields_in_send_ty.rs:123:5
|
LL | field1: A,
| ^^^^^^^^^
= help: add `P: Send` bound in `Send` impl
error: this implementation is unsound, as some fields in `Complex<Q, MutexGuard<'static, bool>>` are `!Send`
- --> $DIR/non_send_fields_in_send_ty.rs:125:1
+ --> $DIR/non_send_fields_in_send_ty.rs:130:1
|
LL | unsafe impl<Q: Send> Send for Complex<Q, MutexGuard<'static, bool>> {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: the type of field `field2` is `!Send`
- --> $DIR/non_send_fields_in_send_ty.rs:119:5
+ --> $DIR/non_send_fields_in_send_ty.rs:124:5
|
LL | field2: B,
| ^^^^^^^^^
--- /dev/null
+#![warn(clippy::octal_escapes)]
+
+fn main() {
+ let _bad1 = "\033[0m";
+ let _bad2 = b"\033[0m";
+ let _bad3 = "\\\033[0m";
+ // maximum 3 digits (\012 is the escape)
+ let _bad4 = "\01234567";
+ let _bad5 = "\0\03";
+ let _bad6 = "Text-\055\077-MoreText";
+ let _bad7 = "EvenMoreText-\01\02-ShortEscapes";
+ let _bad8 = "锈\01锈";
+ let _bad9 = "锈\011锈";
+
+ let _good1 = "\\033[0m";
+ let _good2 = "\0\\0";
+ let _good3 = "\0\0";
+ let _good4 = "X\0\0X";
+ let _good5 = "锈\0锈";
+}
--- /dev/null
+error: octal-looking escape in string literal
+ --> $DIR/octal_escapes.rs:4:17
+ |
+LL | let _bad1 = "/033[0m";
+ | ^^^^^^^^^
+ |
+ = note: `-D clippy::octal-escapes` implied by `-D warnings`
+ = help: octal escapes are not supported, `/0` is always a null character
+help: if an octal escape was intended, use the hexadecimal representation instead
+ |
+LL | let _bad1 = "/x1b[0m";
+ | ~~~~~~~~~
+help: if the null character is intended, disambiguate using
+ |
+LL | let _bad1 = "/x0033[0m";
+ | ~~~~~~~~~~~
+
+error: octal-looking escape in byte string literal
+ --> $DIR/octal_escapes.rs:5:17
+ |
+LL | let _bad2 = b"/033[0m";
+ | ^^^^^^^^^^
+ |
+ = help: octal escapes are not supported, `/0` is always a null byte
+help: if an octal escape was intended, use the hexadecimal representation instead
+ |
+LL | let _bad2 = b"/x1b[0m";
+ | ~~~~~~~~~~
+help: if the null byte is intended, disambiguate using
+ |
+LL | let _bad2 = b"/x0033[0m";
+ | ~~~~~~~~~~~~
+
+error: octal-looking escape in string literal
+ --> $DIR/octal_escapes.rs:6:17
+ |
+LL | let _bad3 = "//033[0m";
+ | ^^^^^^^^^^^
+ |
+ = help: octal escapes are not supported, `/0` is always a null character
+help: if an octal escape was intended, use the hexadecimal representation instead
+ |
+LL | let _bad3 = "//x1b[0m";
+ | ~~~~~~~~~~~
+help: if the null character is intended, disambiguate using
+ |
+LL | let _bad3 = "//x0033[0m";
+ | ~~~~~~~~~~~~~
+
+error: octal-looking escape in string literal
+ --> $DIR/octal_escapes.rs:8:17
+ |
+LL | let _bad4 = "/01234567";
+ | ^^^^^^^^^^^
+ |
+ = help: octal escapes are not supported, `/0` is always a null character
+help: if an octal escape was intended, use the hexadecimal representation instead
+ |
+LL | let _bad4 = "/x0a34567";
+ | ~~~~~~~~~~~
+help: if the null character is intended, disambiguate using
+ |
+LL | let _bad4 = "/x001234567";
+ | ~~~~~~~~~~~~~
+
+error: octal-looking escape in string literal
+ --> $DIR/octal_escapes.rs:10:17
+ |
+LL | let _bad6 = "Text-/055/077-MoreText";
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: octal escapes are not supported, `/0` is always a null character
+help: if an octal escape was intended, use the hexadecimal representation instead
+ |
+LL | let _bad6 = "Text-/x2d/x3f-MoreText";
+ | ~~~~~~~~~~~~~~~~~~~~~~~~
+help: if the null character is intended, disambiguate using
+ |
+LL | let _bad6 = "Text-/x0055/x0077-MoreText";
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: octal-looking escape in string literal
+ --> $DIR/octal_escapes.rs:11:17
+ |
+LL | let _bad7 = "EvenMoreText-/01/02-ShortEscapes";
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: octal escapes are not supported, `/0` is always a null character
+help: if an octal escape was intended, use the hexadecimal representation instead
+ |
+LL | let _bad7 = "EvenMoreText-/x01/x02-ShortEscapes";
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+help: if the null character is intended, disambiguate using
+ |
+LL | let _bad7 = "EvenMoreText-/x001/x002-ShortEscapes";
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: octal-looking escape in string literal
+ --> $DIR/octal_escapes.rs:12:17
+ |
+LL | let _bad8 = "锈/01锈";
+ | ^^^^^^^^^
+ |
+ = help: octal escapes are not supported, `/0` is always a null character
+help: if an octal escape was intended, use the hexadecimal representation instead
+ |
+LL | let _bad8 = "锈/x01锈";
+ | ~~~~~~~~~~
+help: if the null character is intended, disambiguate using
+ |
+LL | let _bad8 = "锈/x001锈";
+ | ~~~~~~~~~~~
+
+error: octal-looking escape in string literal
+ --> $DIR/octal_escapes.rs:13:17
+ |
+LL | let _bad9 = "锈/011锈";
+ | ^^^^^^^^^^
+ |
+ = help: octal escapes are not supported, `/0` is always a null character
+help: if an octal escape was intended, use the hexadecimal representation instead
+ |
+LL | let _bad9 = "锈/x09锈";
+ | ~~~~~~~~~~
+help: if the null character is intended, disambiguate using
+ |
+LL | let _bad9 = "锈/x0011锈";
+ | ~~~~~~~~~~~~
+
+error: aborting due to 8 previous errors
+
// aux-build:macro_rules.rs
#![warn(clippy::option_env_unwrap)]
+#![allow(clippy::map_flatten)]
#[macro_use]
extern crate macro_rules;
error: this will panic at run-time if the environment variable doesn't exist at compile-time
- --> $DIR/option_env_unwrap.rs:17:13
+ --> $DIR/option_env_unwrap.rs:18:13
|
LL | let _ = option_env!("PATH").unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: consider using the `env!` macro instead
error: this will panic at run-time if the environment variable doesn't exist at compile-time
- --> $DIR/option_env_unwrap.rs:18:13
+ --> $DIR/option_env_unwrap.rs:19:13
|
LL | let _ = option_env!("PATH").expect("environment variable PATH isn't set");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: consider using the `env!` macro instead
error: this will panic at run-time if the environment variable doesn't exist at compile-time
- --> $DIR/option_env_unwrap.rs:9:9
+ --> $DIR/option_env_unwrap.rs:10:9
|
LL | option_env!($env).unwrap()
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: this error originates in the macro `option_env_unwrap` (in Nightly builds, run with -Z macro-backtrace for more info)
error: this will panic at run-time if the environment variable doesn't exist at compile-time
- --> $DIR/option_env_unwrap.rs:12:9
+ --> $DIR/option_env_unwrap.rs:13:9
|
LL | option_env!($env).expect($message)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: this error originates in the macro `option_env_unwrap` (in Nightly builds, run with -Z macro-backtrace for more info)
error: this will panic at run-time if the environment variable doesn't exist at compile-time
- --> $DIR/option_env_unwrap.rs:21:13
+ --> $DIR/option_env_unwrap.rs:22:13
|
LL | let _ = option_env_unwrap_external!("PATH");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: this error originates in the macro `option_env_unwrap_external` (in Nightly builds, run with -Z macro-backtrace for more info)
error: this will panic at run-time if the environment variable doesn't exist at compile-time
- --> $DIR/option_env_unwrap.rs:22:13
+ --> $DIR/option_env_unwrap.rs:23:13
|
LL | let _ = option_env_unwrap_external!("PATH", "environment variable PATH isn't set");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-#![warn(clippy::option_filter_map)]
// run-rustfix
-fn odds_out(x: i32) -> Option<i32> {
- if x % 2 == 0 { Some(x) } else { None }
-}
+#![warn(clippy::option_filter_map)]
+#![allow(clippy::map_flatten)]
fn main() {
let _ = Some(Some(1)).flatten();
.map(odds_out)
.flatten();
}
+
+fn odds_out(x: i32) -> Option<i32> {
+ if x % 2 == 0 { Some(x) } else { None }
+}
-#![warn(clippy::option_filter_map)]
// run-rustfix
-fn odds_out(x: i32) -> Option<i32> {
- if x % 2 == 0 { Some(x) } else { None }
-}
+#![warn(clippy::option_filter_map)]
+#![allow(clippy::map_flatten)]
fn main() {
let _ = Some(Some(1)).filter(Option::is_some).map(Option::unwrap);
.filter(|o| o.is_some())
.map(|o| o.unwrap());
}
+
+fn odds_out(x: i32) -> Option<i32> {
+ if x % 2 == 0 { Some(x) } else { None }
+}
error: `filter` for `Some` followed by `unwrap`
- --> $DIR/option_filter_map.rs:8:27
+ --> $DIR/option_filter_map.rs:6:27
|
LL | let _ = Some(Some(1)).filter(Option::is_some).map(Option::unwrap);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()`
= note: `-D clippy::option-filter-map` implied by `-D warnings`
error: `filter` for `Some` followed by `unwrap`
- --> $DIR/option_filter_map.rs:9:27
+ --> $DIR/option_filter_map.rs:7:27
|
LL | let _ = Some(Some(1)).filter(|o| o.is_some()).map(|o| o.unwrap());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()`
error: `filter` for `Some` followed by `unwrap`
- --> $DIR/option_filter_map.rs:10:35
+ --> $DIR/option_filter_map.rs:8:35
|
LL | let _ = Some(1).map(odds_out).filter(Option::is_some).map(Option::unwrap);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()`
error: `filter` for `Some` followed by `unwrap`
- --> $DIR/option_filter_map.rs:11:35
+ --> $DIR/option_filter_map.rs:9:35
|
LL | let _ = Some(1).map(odds_out).filter(|o| o.is_some()).map(|o| o.unwrap());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()`
error: `filter` for `Some` followed by `unwrap`
- --> $DIR/option_filter_map.rs:13:39
+ --> $DIR/option_filter_map.rs:11:39
|
LL | let _ = vec![Some(1)].into_iter().filter(Option::is_some).map(Option::unwrap);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()`
error: `filter` for `Some` followed by `unwrap`
- --> $DIR/option_filter_map.rs:14:39
+ --> $DIR/option_filter_map.rs:12:39
|
LL | let _ = vec![Some(1)].into_iter().filter(|o| o.is_some()).map(|o| o.unwrap());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `flatten` instead: `flatten()`
error: `filter` for `Some` followed by `unwrap`
- --> $DIR/option_filter_map.rs:18:10
+ --> $DIR/option_filter_map.rs:16:10
|
LL | .filter(Option::is_some)
| __________^
| |____________________________^ help: consider using `flatten` instead: `flatten()`
error: `filter` for `Some` followed by `unwrap`
- --> $DIR/option_filter_map.rs:23:10
+ --> $DIR/option_filter_map.rs:21:10
|
LL | .filter(|o| o.is_some())
| __________^
7
}
+// #7973
+fn pattern_to_vec(pattern: &str) -> Vec<String> {
+ pattern
+ .trim_matches('/')
+ .split('/')
+ .flat_map(|s| {
+ s.find('.').map_or_else(|| vec![s.to_string()], |idx| vec![s[..idx].to_string(), s[idx..].to_string()])
+ })
+ .collect::<Vec<_>>()
+}
+
fn main() {
let optional = Some(5);
let _ = optional.map_or(5, |x| x + 2);
let s = String::new();
// Lint, both branches immutably borrow `s`.
- let _ = Some(0).map_or_else(|| s.len(), |x| s.len() + x);
+ let _ = Some(0).map_or(s.len(), |x| s.len() + x);
let s = String::new();
// Lint, `Some` branch consumes `s`, but else branch doesn't use `s`.
// Don't lint. `await` can't be moved into a closure.
let _ = if let Some(x) = Some(0) { _f1(x).await } else { 0 };
}
+
+ let _ = pattern_to_vec("hello world");
}
7
}
+// #7973
+fn pattern_to_vec(pattern: &str) -> Vec<String> {
+ pattern
+ .trim_matches('/')
+ .split('/')
+ .flat_map(|s| {
+ if let Some(idx) = s.find('.') {
+ vec![s[..idx].to_string(), s[idx..].to_string()]
+ } else {
+ vec![s.to_string()]
+ }
+ })
+ .collect::<Vec<_>>()
+}
+
fn main() {
let optional = Some(5);
let _ = if let Some(x) = optional { x + 2 } else { 5 };
// Don't lint. `await` can't be moved into a closure.
let _ = if let Some(x) = Some(0) { _f1(x).await } else { 0 };
}
+
+ let _ = pattern_to_vec("hello world");
}
LL ~ }, |x| x * x * x * x);
|
+error: use Option::map_or_else instead of an if let/else
+ --> $DIR/option_if_let_else.rs:103:13
+ |
+LL | / if let Some(idx) = s.find('.') {
+LL | | vec![s[..idx].to_string(), s[idx..].to_string()]
+LL | | } else {
+LL | | vec![s.to_string()]
+LL | | }
+ | |_____________^ help: try: `s.find('.').map_or_else(|| vec![s.to_string()], |idx| vec![s[..idx].to_string(), s[idx..].to_string()])`
+
error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:99:13
+ --> $DIR/option_if_let_else.rs:114:13
|
LL | let _ = if let Some(x) = optional { x + 2 } else { 5 };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)`
error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:108:13
+ --> $DIR/option_if_let_else.rs:123:13
|
LL | let _ = if let Some(x) = Some(0) {
| _____________^
LL ~ });
|
-error: use Option::map_or_else instead of an if let/else
- --> $DIR/option_if_let_else.rs:136:13
+error: use Option::map_or instead of an if let/else
+ --> $DIR/option_if_let_else.rs:151:13
|
LL | let _ = if let Some(x) = Some(0) { s.len() + x } else { s.len() };
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(0).map_or_else(|| s.len(), |x| s.len() + x)`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(0).map_or(s.len(), |x| s.len() + x)`
error: use Option::map_or instead of an if let/else
- --> $DIR/option_if_let_else.rs:140:13
+ --> $DIR/option_if_let_else.rs:155:13
|
LL | let _ = if let Some(x) = Some(0) {
| _____________^
LL ~ });
|
-error: aborting due to 14 previous errors
+error: aborting due to 15 previous errors
fn main() {
let opt = Some(1);
+ let r: Result<i32, i32> = Ok(1);
+ let bar = |_| Some(1);
// Check `OPTION_MAP_OR_NONE`.
// Single line case.
- let _ = opt.and_then(|x| Some(x + 1));
+ let _: Option<i32> = opt.map(|x| x + 1);
// Multi-line case.
#[rustfmt::skip]
- let _ = opt.and_then(|x| {
- Some(x + 1)
- });
+ let _: Option<i32> = opt.map(|x| x + 1);
+ // function returning `Option`
+ let _: Option<i32> = opt.and_then(bar);
+ let _: Option<i32> = opt.and_then(|x| {
+ let offset = 0;
+ let height = x;
+ Some(offset + height)
+ });
+
+ // Check `RESULT_MAP_OR_INTO_OPTION`.
+ let _: Option<i32> = r.ok();
}
fn main() {
let opt = Some(1);
+ let r: Result<i32, i32> = Ok(1);
+ let bar = |_| Some(1);
// Check `OPTION_MAP_OR_NONE`.
// Single line case.
- let _ = opt.map_or(None, |x| Some(x + 1));
+ let _: Option<i32> = opt.map_or(None, |x| Some(x + 1));
// Multi-line case.
#[rustfmt::skip]
- let _ = opt.map_or(None, |x| {
+ let _: Option<i32> = opt.map_or(None, |x| {
Some(x + 1)
});
+ // function returning `Option`
+ let _: Option<i32> = opt.map_or(None, bar);
+ let _: Option<i32> = opt.map_or(None, |x| {
+ let offset = 0;
+ let height = x;
+ Some(offset + height)
+ });
+
+ // Check `RESULT_MAP_OR_INTO_OPTION`.
+ let _: Option<i32> = r.map_or(None, Some);
}
-error: called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling `and_then(..)` instead
- --> $DIR/option_map_or_none.rs:10:13
+error: called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling `map(..)` instead
+ --> $DIR/option_map_or_none.rs:12:26
|
-LL | let _ = opt.map_or(None, |x| Some(x + 1));
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `opt.and_then(|x| Some(x + 1))`
+LL | let _: Option<i32> = opt.map_or(None, |x| Some(x + 1));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using `map` instead: `opt.map(|x| x + 1)`
|
= note: `-D clippy::option-map-or-none` implied by `-D warnings`
-error: called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling `and_then(..)` instead
- --> $DIR/option_map_or_none.rs:13:13
+error: called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling `map(..)` instead
+ --> $DIR/option_map_or_none.rs:15:26
|
-LL | let _ = opt.map_or(None, |x| {
- | _____________^
+LL | let _: Option<i32> = opt.map_or(None, |x| {
+ | __________________________^
LL | | Some(x + 1)
LL | | });
- | |_________________________^
+ | |_________________________^ help: try using `map` instead: `opt.map(|x| x + 1)`
+
+error: called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling `and_then(..)` instead
+ --> $DIR/option_map_or_none.rs:19:26
+ |
+LL | let _: Option<i32> = opt.map_or(None, bar);
+ | ^^^^^^^^^^^^^^^^^^^^^ help: try using `and_then` instead: `opt.and_then(bar)`
+
+error: called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling `and_then(..)` instead
+ --> $DIR/option_map_or_none.rs:20:26
+ |
+LL | let _: Option<i32> = opt.map_or(None, |x| {
+ | __________________________^
+LL | | let offset = 0;
+LL | | let height = x;
+LL | | Some(offset + height)
+LL | | });
+ | |______^
|
help: try using `and_then` instead
|
-LL ~ let _ = opt.and_then(|x| {
-LL + Some(x + 1)
-LL ~ });
+LL ~ let _: Option<i32> = opt.and_then(|x| {
+LL + let offset = 0;
+LL + let height = x;
+LL + Some(offset + height)
+LL ~ });
+ |
+
+error: called `map_or(None, Some)` on a `Result` value. This can be done more directly by calling `ok()` instead
+ --> $DIR/option_map_or_none.rs:27:26
+ |
+LL | let _: Option<i32> = r.map_or(None, Some);
+ | ^^^^^^^^^^^^^^^^^^^^ help: try using `ok` instead: `r.ok()`
|
+ = note: `-D clippy::result-map-or-into-option` implied by `-D warnings`
-error: aborting due to 2 previous errors
+error: aborting due to 5 previous errors
with_enum.unwrap_or(Enum::A(5));
let with_const_fn = Some(Duration::from_secs(1));
- with_const_fn.unwrap_or_else(|| Duration::from_secs(5));
+ with_const_fn.unwrap_or(Duration::from_secs(5));
let with_constructor = Some(vec![1]);
with_constructor.unwrap_or_else(make);
without_default.unwrap_or_else(Foo::new);
let mut map = HashMap::<u64, String>::new();
- map.entry(42).or_insert_with(String::new);
+ map.entry(42).or_insert(String::new());
let mut map_vec = HashMap::<u64, Vec<i32>>::new();
- map_vec.entry(42).or_insert_with(Vec::new);
+ map_vec.entry(42).or_insert(vec![]);
let mut btree = BTreeMap::<u64, String>::new();
- btree.entry(42).or_insert_with(String::new);
+ btree.entry(42).or_insert(String::new());
let mut btree_vec = BTreeMap::<u64, Vec<i32>>::new();
- btree_vec.entry(42).or_insert_with(Vec::new);
+ btree_vec.entry(42).or_insert(vec![]);
let stringy = Some(String::from(""));
let _ = stringy.unwrap_or_else(|| "".to_owned());
let b = "b".to_string();
let _ = Some(Bar("a".to_string(), Duration::from_secs(1)))
- .or_else(|| Some(Bar(b, Duration::from_secs(2))));
+ .or(Some(Bar(b, Duration::from_secs(2))));
let vec = vec!["foo"];
let _ = opt.ok_or(vec.len());
}
mod issue6675 {
+ unsafe fn ptr_to_ref<'a, T>(p: *const T) -> &'a T {
+ #[allow(unused)]
+ let x = vec![0; 1000]; // future-proofing, make this function expensive.
+ &*p
+ }
+
unsafe fn foo() {
- let mut s = "test".to_owned();
- None.unwrap_or_else(|| s.as_mut_vec());
+ let s = "test".to_owned();
+ let s = &s as *const _;
+ None.unwrap_or_else(|| ptr_to_ref(s));
}
fn bar() {
- let mut s = "test".to_owned();
- None.unwrap_or_else(|| unsafe { s.as_mut_vec() });
+ let s = "test".to_owned();
+ let s = &s as *const _;
+ None.unwrap_or_else(|| unsafe { ptr_to_ref(s) });
#[rustfmt::skip]
- None.unwrap_or_else(|| unsafe { s.as_mut_vec() });
+ None.unwrap_or_else(|| unsafe { ptr_to_ref(s) });
}
}
}
mod issue6675 {
+ unsafe fn ptr_to_ref<'a, T>(p: *const T) -> &'a T {
+ #[allow(unused)]
+ let x = vec![0; 1000]; // future-proofing, make this function expensive.
+ &*p
+ }
+
unsafe fn foo() {
- let mut s = "test".to_owned();
- None.unwrap_or(s.as_mut_vec());
+ let s = "test".to_owned();
+ let s = &s as *const _;
+ None.unwrap_or(ptr_to_ref(s));
}
fn bar() {
- let mut s = "test".to_owned();
- None.unwrap_or(unsafe { s.as_mut_vec() });
+ let s = "test".to_owned();
+ let s = &s as *const _;
+ None.unwrap_or(unsafe { ptr_to_ref(s) });
#[rustfmt::skip]
- None.unwrap_or( unsafe { s.as_mut_vec() } );
+ None.unwrap_or( unsafe { ptr_to_ref(s) } );
}
}
-error: use of `unwrap_or` followed by a function call
- --> $DIR/or_fun_call.rs:46:19
- |
-LL | with_const_fn.unwrap_or(Duration::from_secs(5));
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| Duration::from_secs(5))`
- |
- = note: `-D clippy::or-fun-call` implied by `-D warnings`
-
error: use of `unwrap_or` followed by a function call
--> $DIR/or_fun_call.rs:49:22
|
LL | with_constructor.unwrap_or(make());
| ^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(make)`
+ |
+ = note: `-D clippy::or-fun-call` implied by `-D warnings`
error: use of `unwrap_or` followed by a call to `new`
--> $DIR/or_fun_call.rs:52:5
LL | without_default.unwrap_or(Foo::new());
| ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(Foo::new)`
-error: use of `or_insert` followed by a function call
- --> $DIR/or_fun_call.rs:82:19
- |
-LL | map.entry(42).or_insert(String::new());
- | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(String::new)`
-
-error: use of `or_insert` followed by a function call
- --> $DIR/or_fun_call.rs:85:23
- |
-LL | map_vec.entry(42).or_insert(vec![]);
- | ^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(Vec::new)`
-
-error: use of `or_insert` followed by a function call
- --> $DIR/or_fun_call.rs:88:21
- |
-LL | btree.entry(42).or_insert(String::new());
- | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(String::new)`
-
-error: use of `or_insert` followed by a function call
- --> $DIR/or_fun_call.rs:91:25
- |
-LL | btree_vec.entry(42).or_insert(vec![]);
- | ^^^^^^^^^^^^^^^^^ help: try this: `or_insert_with(Vec::new)`
-
error: use of `unwrap_or` followed by a function call
--> $DIR/or_fun_call.rs:94:21
|
LL | let _ = Some("a".to_string()).or(Some("b".to_string()));
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some("b".to_string()))`
-error: use of `or` followed by a function call
- --> $DIR/or_fun_call.rs:132:10
- |
-LL | .or(Some(Bar(b, Duration::from_secs(2))));
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some(Bar(b, Duration::from_secs(2))))`
-
error: use of `unwrap_or` followed by a function call
- --> $DIR/or_fun_call.rs:160:14
+ --> $DIR/or_fun_call.rs:167:14
|
-LL | None.unwrap_or(s.as_mut_vec());
- | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| s.as_mut_vec())`
+LL | None.unwrap_or(ptr_to_ref(s));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| ptr_to_ref(s))`
error: use of `unwrap_or` followed by a function call
- --> $DIR/or_fun_call.rs:165:14
+ --> $DIR/or_fun_call.rs:173:14
|
-LL | None.unwrap_or(unsafe { s.as_mut_vec() });
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| unsafe { s.as_mut_vec() })`
+LL | None.unwrap_or(unsafe { ptr_to_ref(s) });
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })`
error: use of `unwrap_or` followed by a function call
- --> $DIR/or_fun_call.rs:167:14
+ --> $DIR/or_fun_call.rs:175:14
|
-LL | None.unwrap_or( unsafe { s.as_mut_vec() } );
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| unsafe { s.as_mut_vec() })`
+LL | None.unwrap_or( unsafe { ptr_to_ref(s) } );
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })`
-error: aborting due to 24 previous errors
+error: aborting due to 18 previous errors
Some(_) => (),
_ => (),
}
+
+ const FOO: &str = "foo";
+
+ fn foo(s: &str) -> i32 {
+ match s {
+ FOO => 1,
+ _ => 0,
+ }
+ }
}
// non rustfixable, see redundant_closure_call_fixable.rs
#![warn(clippy::redundant_closure_call)]
+#![allow(clippy::needless_late_init)]
fn main() {
let mut i = 1;
error: closure called just once immediately after it was declared
- --> $DIR/redundant_closure_call_late.rs:15:5
+ --> $DIR/redundant_closure_call_late.rs:16:5
|
LL | i = redun_closure();
| ^^^^^^^^^^^^^^^^^^^
= note: `-D clippy::redundant-closure-call` implied by `-D warnings`
error: closure called just once immediately after it was declared
- --> $DIR/redundant_closure_call_late.rs:19:5
+ --> $DIR/redundant_closure_call_late.rs:20:5
|
LL | i = shadowed_closure();
| ^^^^^^^^^^^^^^^^^^^^^^
error: closure called just once immediately after it was declared
- --> $DIR/redundant_closure_call_late.rs:21:5
+ --> $DIR/redundant_closure_call_late.rs:22:5
|
LL | i = shadowed_closure();
| ^^^^^^^^^^^^^^^^^^^^^^
#![warn(clippy::redundant_else)]
-#![allow(clippy::needless_return, clippy::if_same_then_else)]
+#![allow(clippy::needless_return, clippy::if_same_then_else, clippy::needless_late_init)]
fn main() {
loop {
None::<()>.is_none();
}
+
+#[allow(clippy::deref_addrof, dead_code)]
+fn issue7921() {
+ if (&None::<()>).is_none() {}
+ if (&None::<()>).is_none() {}
+}
None => true,
};
}
+
+#[allow(clippy::deref_addrof, dead_code)]
+fn issue7921() {
+ if let None = *(&None::<()>) {}
+ if let None = *&None::<()> {}
+}
LL | | };
| |_____^ help: try this: `None::<()>.is_none()`
-error: aborting due to 19 previous errors
+error: redundant pattern matching, consider using `is_none()`
+ --> $DIR/redundant_pattern_matching_option.rs:101:12
+ |
+LL | if let None = *(&None::<()>) {}
+ | -------^^^^----------------- help: try this: `if (&None::<()>).is_none()`
+
+error: redundant pattern matching, consider using `is_none()`
+ --> $DIR/redundant_pattern_matching_option.rs:102:12
+ |
+LL | if let None = *&None::<()> {}
+ | -------^^^^--------------- help: try this: `if (&None::<()>).is_none()`
+
+error: aborting due to 21 previous errors
}
fn try_result_opt() -> Result<i32, i32> {
- while r#try!(result_opt()).is_some() {}
- if r#try!(result_opt()).is_some() {}
+ while (r#try!(result_opt())).is_some() {}
+ if (r#try!(result_opt())).is_some() {}
Ok(42)
}
--> $DIR/redundant_pattern_matching_result.rs:85:19
|
LL | while let Some(_) = r#try!(result_opt()) {}
- | ----------^^^^^^^----------------------- help: try this: `while r#try!(result_opt()).is_some()`
+ | ----------^^^^^^^----------------------- help: try this: `while (r#try!(result_opt())).is_some()`
error: redundant pattern matching, consider using `is_some()`
--> $DIR/redundant_pattern_matching_result.rs:86:16
|
LL | if let Some(_) = r#try!(result_opt()) {}
- | -------^^^^^^^----------------------- help: try this: `if r#try!(result_opt()).is_some()`
+ | -------^^^^^^^----------------------- help: try this: `if (r#try!(result_opt())).is_some()`
error: redundant pattern matching, consider using `is_some()`
--> $DIR/redundant_pattern_matching_result.rs:92:12
#![allow(clippy::module_name_repetitions)]
#![allow(clippy::new_without_default)]
#![allow(clippy::redundant_static_lifetimes)]
+#![allow(clippy::cognitive_complexity)]
#![allow(clippy::bind_instead_of_map)]
#![allow(clippy::box_collection)]
#![allow(clippy::blocks_in_if_conditions)]
#![allow(clippy::invisible_characters)]
#![allow(clippy::single_char_add_str)]
#![allow(clippy::match_result_ok)]
+#![allow(clippy::disallowed_types)]
+#![allow(clippy::disallowed_methods)]
// uplifted lints
#![allow(invalid_value)]
#![allow(array_into_iter)]
#![warn(clippy::invisible_characters)]
#![warn(clippy::single_char_add_str)]
#![warn(clippy::match_result_ok)]
+#![warn(clippy::disallowed_types)]
+#![warn(clippy::disallowed_methods)]
// uplifted lints
#![warn(invalid_value)]
#![warn(array_into_iter)]
#![allow(clippy::module_name_repetitions)]
#![allow(clippy::new_without_default)]
#![allow(clippy::redundant_static_lifetimes)]
+#![allow(clippy::cognitive_complexity)]
#![allow(clippy::bind_instead_of_map)]
#![allow(clippy::box_collection)]
#![allow(clippy::blocks_in_if_conditions)]
#![allow(clippy::invisible_characters)]
#![allow(clippy::single_char_add_str)]
#![allow(clippy::match_result_ok)]
+#![allow(clippy::disallowed_types)]
+#![allow(clippy::disallowed_methods)]
// uplifted lints
#![allow(invalid_value)]
#![allow(array_into_iter)]
#![warn(clippy::zero_width_space)]
#![warn(clippy::single_char_push_str)]
#![warn(clippy::if_let_some_result)]
+#![warn(clippy::disallowed_type)]
+#![warn(clippy::disallowed_method)]
// uplifted lints
#![warn(clippy::invalid_ref)]
#![warn(clippy::into_iter_on_array)]
error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions`
- --> $DIR/rename.rs:31:9
+ --> $DIR/rename.rs:34:9
|
LL | #![warn(clippy::stutter)]
| ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions`
= note: `-D renamed-and-removed-lints` implied by `-D warnings`
error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default`
- --> $DIR/rename.rs:32:9
+ --> $DIR/rename.rs:35:9
|
LL | #![warn(clippy::new_without_default_derive)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default`
error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes`
- --> $DIR/rename.rs:33:9
+ --> $DIR/rename.rs:36:9
|
LL | #![warn(clippy::const_static_lifetime)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes`
error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity`
- --> $DIR/rename.rs:34:9
+ --> $DIR/rename.rs:37:9
|
LL | #![warn(clippy::cyclomatic_complexity)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity`
error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map`
- --> $DIR/rename.rs:35:9
+ --> $DIR/rename.rs:38:9
|
LL | #![warn(clippy::option_and_then_some)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map`
error: lint `clippy::box_vec` has been renamed to `clippy::box_collection`
- --> $DIR/rename.rs:36:9
+ --> $DIR/rename.rs:39:9
|
LL | #![warn(clippy::box_vec)]
| ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection`
error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions`
- --> $DIR/rename.rs:37:9
+ --> $DIR/rename.rs:40:9
|
LL | #![warn(clippy::block_in_if_condition_expr)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions`
error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_if_conditions`
- --> $DIR/rename.rs:38:9
+ --> $DIR/rename.rs:41:9
|
LL | #![warn(clippy::block_in_if_condition_stmt)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions`
error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or`
- --> $DIR/rename.rs:39:9
+ --> $DIR/rename.rs:42:9
|
LL | #![warn(clippy::option_map_unwrap_or)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or`
- --> $DIR/rename.rs:40:9
+ --> $DIR/rename.rs:43:9
|
LL | #![warn(clippy::option_map_unwrap_or_else)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or`
- --> $DIR/rename.rs:41:9
+ --> $DIR/rename.rs:44:9
|
LL | #![warn(clippy::result_map_unwrap_or_else)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used`
- --> $DIR/rename.rs:42:9
+ --> $DIR/rename.rs:45:9
|
LL | #![warn(clippy::option_unwrap_used)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used`
error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used`
- --> $DIR/rename.rs:43:9
+ --> $DIR/rename.rs:46:9
|
LL | #![warn(clippy::result_unwrap_used)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used`
error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used`
- --> $DIR/rename.rs:44:9
+ --> $DIR/rename.rs:47:9
|
LL | #![warn(clippy::option_expect_used)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used`
error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used`
- --> $DIR/rename.rs:45:9
+ --> $DIR/rename.rs:48:9
|
LL | #![warn(clippy::result_expect_used)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used`
error: lint `clippy::for_loop_over_option` has been renamed to `clippy::for_loops_over_fallibles`
- --> $DIR/rename.rs:46:9
+ --> $DIR/rename.rs:49:9
|
LL | #![warn(clippy::for_loop_over_option)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles`
error: lint `clippy::for_loop_over_result` has been renamed to `clippy::for_loops_over_fallibles`
- --> $DIR/rename.rs:47:9
+ --> $DIR/rename.rs:50:9
|
LL | #![warn(clippy::for_loop_over_result)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles`
error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion`
- --> $DIR/rename.rs:48:9
+ --> $DIR/rename.rs:51:9
|
LL | #![warn(clippy::identity_conversion)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion`
error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters`
- --> $DIR/rename.rs:49:9
+ --> $DIR/rename.rs:52:9
|
LL | #![warn(clippy::zero_width_space)]
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters`
error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str`
- --> $DIR/rename.rs:50:9
+ --> $DIR/rename.rs:53:9
|
LL | #![warn(clippy::single_char_push_str)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str`
error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok`
- --> $DIR/rename.rs:51:9
+ --> $DIR/rename.rs:54:9
|
LL | #![warn(clippy::if_let_some_result)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok`
+error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types`
+ --> $DIR/rename.rs:55:9
+ |
+LL | #![warn(clippy::disallowed_type)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types`
+
+error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods`
+ --> $DIR/rename.rs:56:9
+ |
+LL | #![warn(clippy::disallowed_method)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods`
+
error: lint `clippy::invalid_ref` has been renamed to `invalid_value`
- --> $DIR/rename.rs:53:9
+ --> $DIR/rename.rs:58:9
|
LL | #![warn(clippy::invalid_ref)]
| ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value`
error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter`
- --> $DIR/rename.rs:54:9
+ --> $DIR/rename.rs:59:9
|
LL | #![warn(clippy::into_iter_on_array)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter`
error: lint `clippy::unused_label` has been renamed to `unused_labels`
- --> $DIR/rename.rs:55:9
+ --> $DIR/rename.rs:60:9
|
LL | #![warn(clippy::unused_label)]
| ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels`
error: lint `clippy::drop_bounds` has been renamed to `drop_bounds`
- --> $DIR/rename.rs:56:9
+ --> $DIR/rename.rs:61:9
|
LL | #![warn(clippy::drop_bounds)]
| ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds`
error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr`
- --> $DIR/rename.rs:57:9
+ --> $DIR/rename.rs:62:9
|
LL | #![warn(clippy::temporary_cstring_as_ptr)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr`
error: lint `clippy::panic_params` has been renamed to `non_fmt_panics`
- --> $DIR/rename.rs:58:9
+ --> $DIR/rename.rs:63:9
|
LL | #![warn(clippy::panic_params)]
| ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics`
error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints`
- --> $DIR/rename.rs:59:9
+ --> $DIR/rename.rs:64:9
|
LL | #![warn(clippy::unknown_clippy_lints)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints`
error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering`
- --> $DIR/rename.rs:60:9
+ --> $DIR/rename.rs:65:9
|
LL | #![warn(clippy::invalid_atomic_ordering)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering`
error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums`
- --> $DIR/rename.rs:61:9
+ --> $DIR/rename.rs:66:9
|
LL | #![warn(clippy::mem_discriminant_non_enum)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums`
-error: aborting due to 30 previous errors
+error: aborting due to 32 previous errors
-error: method's name is same to an existing method in a trait
+error: method's name is the same as an existing method in a trait
--> $DIR/same_name_method.rs:20:13
|
LL | fn foo() {}
LL | fn foo() {}
| ^^^^^^^^^^^
-error: method's name is same to an existing method in a trait
+error: method's name is the same as an existing method in a trait
--> $DIR/same_name_method.rs:44:13
|
LL | fn foo() {}
LL | fn foo() {}
| ^^^^^^^^^^^
-error: method's name is same to an existing method in a trait
+error: method's name is the same as an existing method in a trait
--> $DIR/same_name_method.rs:58:13
|
LL | fn foo() {}
LL | impl T1 for S {}
| ^^^^^^^^^^^^^^^^
-error: method's name is same to an existing method in a trait
+error: method's name is the same as an existing method in a trait
--> $DIR/same_name_method.rs:70:13
|
LL | fn foo() {}
LL | impl T1 for S {}
| ^^^^^^^^^^^^^^^^
-error: method's name is same to an existing method in a trait
+error: method's name is the same as an existing method in a trait
--> $DIR/same_name_method.rs:34:13
|
LL | fn clone() {}
// check that we don't lint if `find()` is called with
// `Pattern` that is not a string
let _ = "hello world".find(|c: char| c == 'o' || c == 'l').is_some();
+
+ let some_closure = |x: &u32| *x == 0;
+ let _ = (0..1).find(some_closure).is_some();
}
#[rustfmt::skip]
// check that we don't lint if `find()` is called with
// `Pattern` that is not a string
let _ = "hello world".find(|c: char| c == 'o' || c == 'l').is_none();
+
+ let some_closure = |x: &u32| *x == 0;
+ let _ = (0..1).find(some_closure).is_none();
}
|
= help: this is more succinctly expressed by calling `any()`
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some.rs:41:20
+ |
+LL | let _ = (0..1).find(some_closure).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(some_closure)`
+
error: called `is_none()` after searching an `Iterator` with `find`
- --> $DIR/search_is_some.rs:48:13
+ --> $DIR/search_is_some.rs:51:13
|
LL | let _ = v.iter().find(|&x| {
| _____________^
= help: this is more succinctly expressed by calling `any()` with negation
error: called `is_none()` after searching an `Iterator` with `position`
- --> $DIR/search_is_some.rs:54:13
+ --> $DIR/search_is_some.rs:57:13
|
LL | let _ = v.iter().position(|&x| {
| _____________^
= help: this is more succinctly expressed by calling `any()` with negation
error: called `is_none()` after searching an `Iterator` with `rposition`
- --> $DIR/search_is_some.rs:60:13
+ --> $DIR/search_is_some.rs:63:13
|
LL | let _ = v.iter().rposition(|&x| {
| _____________^
|
= help: this is more succinctly expressed by calling `any()` with negation
-error: aborting due to 6 previous errors
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some.rs:78:13
+ |
+LL | let _ = (0..1).find(some_closure).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(0..1).any(some_closure)`
+
+error: aborting due to 8 previous errors
+++ /dev/null
-// run-rustfix
-#![allow(dead_code)]
-#![warn(clippy::search_is_some)]
-
-fn main() {
- let v = vec![3, 2, 1, 0, -1, -2, -3];
- let y = &&42;
-
- // Check `find().is_some()`, single-line case.
- let _ = v.iter().any(|x| *x < 0);
- let _ = (0..1).any(|x| **y == x); // one dereference less
- let _ = (0..1).any(|x| x == 0);
- let _ = v.iter().any(|x| *x == 0);
-
- // Check `position().is_some()`, single-line case.
- let _ = v.iter().any(|&x| x < 0);
-
- // Check `rposition().is_some()`, single-line case.
- let _ = v.iter().any(|&x| x < 0);
-
- let s1 = String::from("hello world");
- let s2 = String::from("world");
- // caller of `find()` is a `&`static str`
- let _ = "hello world".contains("world");
- let _ = "hello world".contains(&s2);
- let _ = "hello world".contains(&s2[2..]);
- // caller of `find()` is a `String`
- let _ = s1.contains("world");
- let _ = s1.contains(&s2);
- let _ = s1.contains(&s2[2..]);
- // caller of `find()` is slice of `String`
- let _ = s1[2..].contains("world");
- let _ = s1[2..].contains(&s2);
- let _ = s1[2..].contains(&s2[2..]);
-}
-
-fn is_none() {
- let v = vec![3, 2, 1, 0, -1, -2, -3];
- let y = &&42;
-
- // Check `find().is_none()`, single-line case.
- let _ = !v.iter().any(|x| *x < 0);
- let _ = !(0..1).any(|x| **y == x); // one dereference less
- let _ = !(0..1).any(|x| x == 0);
- let _ = !v.iter().any(|x| *x == 0);
-
- // Check `position().is_none()`, single-line case.
- let _ = !v.iter().any(|&x| x < 0);
-
- // Check `rposition().is_none()`, single-line case.
- let _ = !v.iter().any(|&x| x < 0);
-
- let s1 = String::from("hello world");
- let s2 = String::from("world");
-
- // caller of `find()` is a `&`static str`
- let _ = !"hello world".contains("world");
- let _ = !"hello world".contains(&s2);
- let _ = !"hello world".contains(&s2[2..]);
- // caller of `find()` is a `String`
- let _ = !s1.contains("world");
- let _ = !s1.contains(&s2);
- let _ = !s1.contains(&s2[2..]);
- // caller of `find()` is slice of `String`
- let _ = !s1[2..].contains("world");
- let _ = !s1[2..].contains(&s2);
- let _ = !s1[2..].contains(&s2[2..]);
-}
+++ /dev/null
-// run-rustfix
-#![allow(dead_code)]
-#![warn(clippy::search_is_some)]
-
-fn main() {
- let v = vec![3, 2, 1, 0, -1, -2, -3];
- let y = &&42;
-
- // Check `find().is_some()`, single-line case.
- let _ = v.iter().find(|&x| *x < 0).is_some();
- let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less
- let _ = (0..1).find(|x| *x == 0).is_some();
- let _ = v.iter().find(|x| **x == 0).is_some();
-
- // Check `position().is_some()`, single-line case.
- let _ = v.iter().position(|&x| x < 0).is_some();
-
- // Check `rposition().is_some()`, single-line case.
- let _ = v.iter().rposition(|&x| x < 0).is_some();
-
- let s1 = String::from("hello world");
- let s2 = String::from("world");
- // caller of `find()` is a `&`static str`
- let _ = "hello world".find("world").is_some();
- let _ = "hello world".find(&s2).is_some();
- let _ = "hello world".find(&s2[2..]).is_some();
- // caller of `find()` is a `String`
- let _ = s1.find("world").is_some();
- let _ = s1.find(&s2).is_some();
- let _ = s1.find(&s2[2..]).is_some();
- // caller of `find()` is slice of `String`
- let _ = s1[2..].find("world").is_some();
- let _ = s1[2..].find(&s2).is_some();
- let _ = s1[2..].find(&s2[2..]).is_some();
-}
-
-fn is_none() {
- let v = vec![3, 2, 1, 0, -1, -2, -3];
- let y = &&42;
-
- // Check `find().is_none()`, single-line case.
- let _ = v.iter().find(|&x| *x < 0).is_none();
- let _ = (0..1).find(|x| **y == *x).is_none(); // one dereference less
- let _ = (0..1).find(|x| *x == 0).is_none();
- let _ = v.iter().find(|x| **x == 0).is_none();
-
- // Check `position().is_none()`, single-line case.
- let _ = v.iter().position(|&x| x < 0).is_none();
-
- // Check `rposition().is_none()`, single-line case.
- let _ = v.iter().rposition(|&x| x < 0).is_none();
-
- let s1 = String::from("hello world");
- let s2 = String::from("world");
-
- // caller of `find()` is a `&`static str`
- let _ = "hello world".find("world").is_none();
- let _ = "hello world".find(&s2).is_none();
- let _ = "hello world".find(&s2[2..]).is_none();
- // caller of `find()` is a `String`
- let _ = s1.find("world").is_none();
- let _ = s1.find(&s2).is_none();
- let _ = s1.find(&s2[2..]).is_none();
- // caller of `find()` is slice of `String`
- let _ = s1[2..].find("world").is_none();
- let _ = s1[2..].find(&s2).is_none();
- let _ = s1[2..].find(&s2[2..]).is_none();
-}
+++ /dev/null
-error: called `is_some()` after searching an `Iterator` with `find`
- --> $DIR/search_is_some_fixable.rs:10:22
- |
-LL | let _ = v.iter().find(|&x| *x < 0).is_some();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| *x < 0)`
- |
- = note: `-D clippy::search-is-some` implied by `-D warnings`
-
-error: called `is_some()` after searching an `Iterator` with `find`
- --> $DIR/search_is_some_fixable.rs:11:20
- |
-LL | let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| **y == x)`
-
-error: called `is_some()` after searching an `Iterator` with `find`
- --> $DIR/search_is_some_fixable.rs:12:20
- |
-LL | let _ = (0..1).find(|x| *x == 0).is_some();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| x == 0)`
-
-error: called `is_some()` after searching an `Iterator` with `find`
- --> $DIR/search_is_some_fixable.rs:13:22
- |
-LL | let _ = v.iter().find(|x| **x == 0).is_some();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| *x == 0)`
-
-error: called `is_some()` after searching an `Iterator` with `position`
- --> $DIR/search_is_some_fixable.rs:16:22
- |
-LL | let _ = v.iter().position(|&x| x < 0).is_some();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|&x| x < 0)`
-
-error: called `is_some()` after searching an `Iterator` with `rposition`
- --> $DIR/search_is_some_fixable.rs:19:22
- |
-LL | let _ = v.iter().rposition(|&x| x < 0).is_some();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|&x| x < 0)`
-
-error: called `is_some()` after calling `find()` on a string
- --> $DIR/search_is_some_fixable.rs:24:27
- |
-LL | let _ = "hello world".find("world").is_some();
- | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains("world")`
-
-error: called `is_some()` after calling `find()` on a string
- --> $DIR/search_is_some_fixable.rs:25:27
- |
-LL | let _ = "hello world".find(&s2).is_some();
- | ^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2)`
-
-error: called `is_some()` after calling `find()` on a string
- --> $DIR/search_is_some_fixable.rs:26:27
- |
-LL | let _ = "hello world".find(&s2[2..]).is_some();
- | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2[2..])`
-
-error: called `is_some()` after calling `find()` on a string
- --> $DIR/search_is_some_fixable.rs:28:16
- |
-LL | let _ = s1.find("world").is_some();
- | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains("world")`
-
-error: called `is_some()` after calling `find()` on a string
- --> $DIR/search_is_some_fixable.rs:29:16
- |
-LL | let _ = s1.find(&s2).is_some();
- | ^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2)`
-
-error: called `is_some()` after calling `find()` on a string
- --> $DIR/search_is_some_fixable.rs:30:16
- |
-LL | let _ = s1.find(&s2[2..]).is_some();
- | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2[2..])`
-
-error: called `is_some()` after calling `find()` on a string
- --> $DIR/search_is_some_fixable.rs:32:21
- |
-LL | let _ = s1[2..].find("world").is_some();
- | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains("world")`
-
-error: called `is_some()` after calling `find()` on a string
- --> $DIR/search_is_some_fixable.rs:33:21
- |
-LL | let _ = s1[2..].find(&s2).is_some();
- | ^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2)`
-
-error: called `is_some()` after calling `find()` on a string
- --> $DIR/search_is_some_fixable.rs:34:21
- |
-LL | let _ = s1[2..].find(&s2[2..]).is_some();
- | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2[2..])`
-
-error: called `is_none()` after searching an `Iterator` with `find`
- --> $DIR/search_is_some_fixable.rs:42:13
- |
-LL | let _ = v.iter().find(|&x| *x < 0).is_none();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x| *x < 0)`
-
-error: called `is_none()` after searching an `Iterator` with `find`
- --> $DIR/search_is_some_fixable.rs:43:13
- |
-LL | let _ = (0..1).find(|x| **y == *x).is_none(); // one dereference less
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(0..1).any(|x| **y == x)`
-
-error: called `is_none()` after searching an `Iterator` with `find`
- --> $DIR/search_is_some_fixable.rs:44:13
- |
-LL | let _ = (0..1).find(|x| *x == 0).is_none();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(0..1).any(|x| x == 0)`
-
-error: called `is_none()` after searching an `Iterator` with `find`
- --> $DIR/search_is_some_fixable.rs:45:13
- |
-LL | let _ = v.iter().find(|x| **x == 0).is_none();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x| *x == 0)`
-
-error: called `is_none()` after searching an `Iterator` with `position`
- --> $DIR/search_is_some_fixable.rs:48:13
- |
-LL | let _ = v.iter().position(|&x| x < 0).is_none();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|&x| x < 0)`
-
-error: called `is_none()` after searching an `Iterator` with `rposition`
- --> $DIR/search_is_some_fixable.rs:51:13
- |
-LL | let _ = v.iter().rposition(|&x| x < 0).is_none();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|&x| x < 0)`
-
-error: called `is_none()` after calling `find()` on a string
- --> $DIR/search_is_some_fixable.rs:57:13
- |
-LL | let _ = "hello world".find("world").is_none();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!"hello world".contains("world")`
-
-error: called `is_none()` after calling `find()` on a string
- --> $DIR/search_is_some_fixable.rs:58:13
- |
-LL | let _ = "hello world".find(&s2).is_none();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!"hello world".contains(&s2)`
-
-error: called `is_none()` after calling `find()` on a string
- --> $DIR/search_is_some_fixable.rs:59:13
- |
-LL | let _ = "hello world".find(&s2[2..]).is_none();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!"hello world".contains(&s2[2..])`
-
-error: called `is_none()` after calling `find()` on a string
- --> $DIR/search_is_some_fixable.rs:61:13
- |
-LL | let _ = s1.find("world").is_none();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1.contains("world")`
-
-error: called `is_none()` after calling `find()` on a string
- --> $DIR/search_is_some_fixable.rs:62:13
- |
-LL | let _ = s1.find(&s2).is_none();
- | ^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1.contains(&s2)`
-
-error: called `is_none()` after calling `find()` on a string
- --> $DIR/search_is_some_fixable.rs:63:13
- |
-LL | let _ = s1.find(&s2[2..]).is_none();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1.contains(&s2[2..])`
-
-error: called `is_none()` after calling `find()` on a string
- --> $DIR/search_is_some_fixable.rs:65:13
- |
-LL | let _ = s1[2..].find("world").is_none();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1[2..].contains("world")`
-
-error: called `is_none()` after calling `find()` on a string
- --> $DIR/search_is_some_fixable.rs:66:13
- |
-LL | let _ = s1[2..].find(&s2).is_none();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1[2..].contains(&s2)`
-
-error: called `is_none()` after calling `find()` on a string
- --> $DIR/search_is_some_fixable.rs:67:13
- |
-LL | let _ = s1[2..].find(&s2[2..]).is_none();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1[2..].contains(&s2[2..])`
-
-error: aborting due to 30 previous errors
-
--- /dev/null
+// run-rustfix
+#![allow(dead_code)]
+#![warn(clippy::search_is_some)]
+
+fn main() {
+ let v = vec![3, 2, 1, 0, -1, -2, -3];
+ let y = &&42;
+
+ // Check `find().is_none()`, single-line case.
+ let _ = !v.iter().any(|x| *x < 0);
+ let _ = !(0..1).any(|x| **y == x); // one dereference less
+ let _ = !(0..1).any(|x| x == 0);
+ let _ = !v.iter().any(|x| *x == 0);
+ let _ = !(4..5).any(|x| x == 1 || x == 3 || x == 5);
+ let _ = !(1..3).any(|x| [1, 2, 3].contains(&x));
+ let _ = !(1..3).any(|x| x == 0 || [1, 2, 3].contains(&x));
+ let _ = !(1..3).any(|x| [1, 2, 3].contains(&x) || x == 0);
+ let _ = !(1..3).any(|x| [1, 2, 3].contains(&x) || x == 0 || [4, 5, 6].contains(&x) || x == -1);
+
+ // Check `position().is_none()`, single-line case.
+ let _ = !v.iter().any(|&x| x < 0);
+
+ // Check `rposition().is_none()`, single-line case.
+ let _ = !v.iter().any(|&x| x < 0);
+
+ let s1 = String::from("hello world");
+ let s2 = String::from("world");
+
+ // caller of `find()` is a `&`static str`
+ let _ = !"hello world".contains("world");
+ let _ = !"hello world".contains(&s2);
+ let _ = !"hello world".contains(&s2[2..]);
+ // caller of `find()` is a `String`
+ let _ = !s1.contains("world");
+ let _ = !s1.contains(&s2);
+ let _ = !s1.contains(&s2[2..]);
+ // caller of `find()` is slice of `String`
+ let _ = !s1[2..].contains("world");
+ let _ = !s1[2..].contains(&s2);
+ let _ = !s1[2..].contains(&s2[2..]);
+}
+
+#[allow(clippy::clone_on_copy, clippy::map_clone)]
+mod issue7392 {
+ struct Player {
+ hand: Vec<usize>,
+ }
+ fn filter() {
+ let p = Player {
+ hand: vec![1, 2, 3, 4, 5],
+ };
+ let filter_hand = vec![5];
+ let _ = p
+ .hand
+ .iter()
+ .filter(|c| !filter_hand.iter().any(|cc| c == &cc))
+ .map(|c| c.clone())
+ .collect::<Vec<_>>();
+ }
+
+ struct PlayerTuple {
+ hand: Vec<(usize, char)>,
+ }
+ fn filter_tuple() {
+ let p = PlayerTuple {
+ hand: vec![(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), (5, 'e')],
+ };
+ let filter_hand = vec![5];
+ let _ = p
+ .hand
+ .iter()
+ .filter(|(c, _)| !filter_hand.iter().any(|cc| c == cc))
+ .map(|c| c.clone())
+ .collect::<Vec<_>>();
+ }
+
+ fn field_projection() {
+ struct Foo {
+ foo: i32,
+ bar: u32,
+ }
+ let vfoo = vec![Foo { foo: 1, bar: 2 }];
+ let _ = !vfoo.iter().any(|v| v.foo == 1 && v.bar == 2);
+
+ let vfoo = vec![(42, Foo { foo: 1, bar: 2 })];
+ let _ = !vfoo
+ .iter().any(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2);
+ }
+
+ fn index_projection() {
+ let vfoo = vec![[0, 1, 2, 3]];
+ let _ = !vfoo.iter().any(|a| a[0] == 42);
+ }
+
+ #[allow(clippy::match_like_matches_macro)]
+ fn slice_projection() {
+ let vfoo = vec![[0, 1, 2, 3, 0, 1, 2, 3]];
+ let _ = !vfoo.iter().any(|sub| sub[1..4].len() == 3);
+ }
+
+ fn please(x: &u32) -> bool {
+ *x == 9
+ }
+
+ fn deref_enough(x: u32) -> bool {
+ x == 78
+ }
+
+ fn arg_no_deref(x: &&u32) -> bool {
+ **x == 78
+ }
+
+ fn more_projections() {
+ let x = 19;
+ let ppx: &u32 = &x;
+ let _ = ![ppx].iter().any(|ppp_x: &&u32| please(ppp_x));
+ let _ = ![String::from("Hey hey")].iter().any(|s| s.len() == 2);
+
+ let v = vec![3, 2, 1, 0];
+ let _ = !v.iter().any(|x| deref_enough(*x));
+ let _ = !v.iter().any(|x: &u32| deref_enough(*x));
+
+ #[allow(clippy::redundant_closure)]
+ let _ = !v.iter().any(|x| arg_no_deref(&x));
+ #[allow(clippy::redundant_closure)]
+ let _ = !v.iter().any(|x: &u32| arg_no_deref(&x));
+ }
+
+ fn field_index_projection() {
+ struct FooDouble {
+ bar: Vec<Vec<i32>>,
+ }
+ struct Foo {
+ bar: Vec<i32>,
+ }
+ struct FooOuter {
+ inner: Foo,
+ inner_double: FooDouble,
+ }
+ let vfoo = vec![FooOuter {
+ inner: Foo { bar: vec![0, 1, 2, 3] },
+ inner_double: FooDouble {
+ bar: vec![vec![0, 1, 2, 3]],
+ },
+ }];
+ let _ = !vfoo
+ .iter().any(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2);
+ }
+
+ fn index_field_projection() {
+ struct Foo {
+ bar: i32,
+ }
+ struct FooOuter {
+ inner: Vec<Foo>,
+ }
+ let vfoo = vec![FooOuter {
+ inner: vec![Foo { bar: 0 }],
+ }];
+ let _ = !vfoo.iter().any(|v| v.inner[0].bar == 2);
+ }
+
+ fn double_deref_index_projection() {
+ let vfoo = vec![&&[0, 1, 2, 3]];
+ let _ = !vfoo.iter().any(|x| (**x)[0] == 9);
+ }
+
+ fn method_call_by_ref() {
+ struct Foo {
+ bar: u32,
+ }
+ impl Foo {
+ pub fn by_ref(&self, x: &u32) -> bool {
+ *x == self.bar
+ }
+ }
+ let vfoo = vec![Foo { bar: 1 }];
+ let _ = !vfoo.iter().any(|v| v.by_ref(&v.bar));
+ }
+
+ fn ref_bindings() {
+ let _ = ![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y);
+ let _ = ![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y);
+ }
+
+ fn test_string_1(s: &str) -> bool {
+ s.is_empty()
+ }
+
+ fn test_u32_1(s: &u32) -> bool {
+ s.is_power_of_two()
+ }
+
+ fn test_u32_2(s: u32) -> bool {
+ s.is_power_of_two()
+ }
+
+ fn projection_in_args_test() {
+ // Index projections
+ let lst = &[String::from("Hello"), String::from("world")];
+ let v: Vec<&[String]> = vec![lst];
+ let _ = !v.iter().any(|s| s[0].is_empty());
+ let _ = !v.iter().any(|s| test_string_1(&s[0]));
+
+ // Field projections
+ struct FieldProjection<'a> {
+ field: &'a u32,
+ }
+ let field = 123456789;
+ let instance = FieldProjection { field: &field };
+ let v = vec![instance];
+ let _ = !v.iter().any(|fp| fp.field.is_power_of_two());
+ let _ = !v.iter().any(|fp| test_u32_1(fp.field));
+ let _ = !v.iter().any(|fp| test_u32_2(*fp.field));
+ }
+}
--- /dev/null
+// run-rustfix
+#![allow(dead_code)]
+#![warn(clippy::search_is_some)]
+
+fn main() {
+ let v = vec![3, 2, 1, 0, -1, -2, -3];
+ let y = &&42;
+
+ // Check `find().is_none()`, single-line case.
+ let _ = v.iter().find(|&x| *x < 0).is_none();
+ let _ = (0..1).find(|x| **y == *x).is_none(); // one dereference less
+ let _ = (0..1).find(|x| *x == 0).is_none();
+ let _ = v.iter().find(|x| **x == 0).is_none();
+ let _ = (4..5).find(|x| *x == 1 || *x == 3 || *x == 5).is_none();
+ let _ = (1..3).find(|x| [1, 2, 3].contains(x)).is_none();
+ let _ = (1..3).find(|x| *x == 0 || [1, 2, 3].contains(x)).is_none();
+ let _ = (1..3).find(|x| [1, 2, 3].contains(x) || *x == 0).is_none();
+ let _ = (1..3)
+ .find(|x| [1, 2, 3].contains(x) || *x == 0 || [4, 5, 6].contains(x) || *x == -1)
+ .is_none();
+
+ // Check `position().is_none()`, single-line case.
+ let _ = v.iter().position(|&x| x < 0).is_none();
+
+ // Check `rposition().is_none()`, single-line case.
+ let _ = v.iter().rposition(|&x| x < 0).is_none();
+
+ let s1 = String::from("hello world");
+ let s2 = String::from("world");
+
+ // caller of `find()` is a `&`static str`
+ let _ = "hello world".find("world").is_none();
+ let _ = "hello world".find(&s2).is_none();
+ let _ = "hello world".find(&s2[2..]).is_none();
+ // caller of `find()` is a `String`
+ let _ = s1.find("world").is_none();
+ let _ = s1.find(&s2).is_none();
+ let _ = s1.find(&s2[2..]).is_none();
+ // caller of `find()` is slice of `String`
+ let _ = s1[2..].find("world").is_none();
+ let _ = s1[2..].find(&s2).is_none();
+ let _ = s1[2..].find(&s2[2..]).is_none();
+}
+
+#[allow(clippy::clone_on_copy, clippy::map_clone)]
+mod issue7392 {
+ struct Player {
+ hand: Vec<usize>,
+ }
+ fn filter() {
+ let p = Player {
+ hand: vec![1, 2, 3, 4, 5],
+ };
+ let filter_hand = vec![5];
+ let _ = p
+ .hand
+ .iter()
+ .filter(|c| filter_hand.iter().find(|cc| c == cc).is_none())
+ .map(|c| c.clone())
+ .collect::<Vec<_>>();
+ }
+
+ struct PlayerTuple {
+ hand: Vec<(usize, char)>,
+ }
+ fn filter_tuple() {
+ let p = PlayerTuple {
+ hand: vec![(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), (5, 'e')],
+ };
+ let filter_hand = vec![5];
+ let _ = p
+ .hand
+ .iter()
+ .filter(|(c, _)| filter_hand.iter().find(|cc| c == *cc).is_none())
+ .map(|c| c.clone())
+ .collect::<Vec<_>>();
+ }
+
+ fn field_projection() {
+ struct Foo {
+ foo: i32,
+ bar: u32,
+ }
+ let vfoo = vec![Foo { foo: 1, bar: 2 }];
+ let _ = vfoo.iter().find(|v| v.foo == 1 && v.bar == 2).is_none();
+
+ let vfoo = vec![(42, Foo { foo: 1, bar: 2 })];
+ let _ = vfoo
+ .iter()
+ .find(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2)
+ .is_none();
+ }
+
+ fn index_projection() {
+ let vfoo = vec![[0, 1, 2, 3]];
+ let _ = vfoo.iter().find(|a| a[0] == 42).is_none();
+ }
+
+ #[allow(clippy::match_like_matches_macro)]
+ fn slice_projection() {
+ let vfoo = vec![[0, 1, 2, 3, 0, 1, 2, 3]];
+ let _ = vfoo.iter().find(|sub| sub[1..4].len() == 3).is_none();
+ }
+
+ fn please(x: &u32) -> bool {
+ *x == 9
+ }
+
+ fn deref_enough(x: u32) -> bool {
+ x == 78
+ }
+
+ fn arg_no_deref(x: &&u32) -> bool {
+ **x == 78
+ }
+
+ fn more_projections() {
+ let x = 19;
+ let ppx: &u32 = &x;
+ let _ = [ppx].iter().find(|ppp_x: &&&u32| please(**ppp_x)).is_none();
+ let _ = [String::from("Hey hey")].iter().find(|s| s.len() == 2).is_none();
+
+ let v = vec![3, 2, 1, 0];
+ let _ = v.iter().find(|x| deref_enough(**x)).is_none();
+ let _ = v.iter().find(|x: &&u32| deref_enough(**x)).is_none();
+
+ #[allow(clippy::redundant_closure)]
+ let _ = v.iter().find(|x| arg_no_deref(x)).is_none();
+ #[allow(clippy::redundant_closure)]
+ let _ = v.iter().find(|x: &&u32| arg_no_deref(x)).is_none();
+ }
+
+ fn field_index_projection() {
+ struct FooDouble {
+ bar: Vec<Vec<i32>>,
+ }
+ struct Foo {
+ bar: Vec<i32>,
+ }
+ struct FooOuter {
+ inner: Foo,
+ inner_double: FooDouble,
+ }
+ let vfoo = vec![FooOuter {
+ inner: Foo { bar: vec![0, 1, 2, 3] },
+ inner_double: FooDouble {
+ bar: vec![vec![0, 1, 2, 3]],
+ },
+ }];
+ let _ = vfoo
+ .iter()
+ .find(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2)
+ .is_none();
+ }
+
+ fn index_field_projection() {
+ struct Foo {
+ bar: i32,
+ }
+ struct FooOuter {
+ inner: Vec<Foo>,
+ }
+ let vfoo = vec![FooOuter {
+ inner: vec![Foo { bar: 0 }],
+ }];
+ let _ = vfoo.iter().find(|v| v.inner[0].bar == 2).is_none();
+ }
+
+ fn double_deref_index_projection() {
+ let vfoo = vec![&&[0, 1, 2, 3]];
+ let _ = vfoo.iter().find(|x| (**x)[0] == 9).is_none();
+ }
+
+ fn method_call_by_ref() {
+ struct Foo {
+ bar: u32,
+ }
+ impl Foo {
+ pub fn by_ref(&self, x: &u32) -> bool {
+ *x == self.bar
+ }
+ }
+ let vfoo = vec![Foo { bar: 1 }];
+ let _ = vfoo.iter().find(|v| v.by_ref(&v.bar)).is_none();
+ }
+
+ fn ref_bindings() {
+ let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_none();
+ let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_none();
+ }
+
+ fn test_string_1(s: &String) -> bool {
+ s.is_empty()
+ }
+
+ fn test_u32_1(s: &u32) -> bool {
+ s.is_power_of_two()
+ }
+
+ fn test_u32_2(s: u32) -> bool {
+ s.is_power_of_two()
+ }
+
+ fn projection_in_args_test() {
+ // Index projections
+ let lst = &[String::from("Hello"), String::from("world")];
+ let v: Vec<&[String]> = vec![lst];
+ let _ = v.iter().find(|s| s[0].is_empty()).is_none();
+ let _ = v.iter().find(|s| test_string_1(&s[0])).is_none();
+
+ // Field projections
+ struct FieldProjection<'a> {
+ field: &'a u32,
+ }
+ let field = 123456789;
+ let instance = FieldProjection { field: &field };
+ let v = vec![instance];
+ let _ = v.iter().find(|fp| fp.field.is_power_of_two()).is_none();
+ let _ = v.iter().find(|fp| test_u32_1(fp.field)).is_none();
+ let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_none();
+ }
+}
--- /dev/null
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:10:13
+ |
+LL | let _ = v.iter().find(|&x| *x < 0).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x| *x < 0)`
+ |
+ = note: `-D clippy::search-is-some` implied by `-D warnings`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:11:13
+ |
+LL | let _ = (0..1).find(|x| **y == *x).is_none(); // one dereference less
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(0..1).any(|x| **y == x)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:12:13
+ |
+LL | let _ = (0..1).find(|x| *x == 0).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(0..1).any(|x| x == 0)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:13:13
+ |
+LL | let _ = v.iter().find(|x| **x == 0).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x| *x == 0)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:14:13
+ |
+LL | let _ = (4..5).find(|x| *x == 1 || *x == 3 || *x == 5).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(4..5).any(|x| x == 1 || x == 3 || x == 5)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:15:13
+ |
+LL | let _ = (1..3).find(|x| [1, 2, 3].contains(x)).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(1..3).any(|x| [1, 2, 3].contains(&x))`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:16:13
+ |
+LL | let _ = (1..3).find(|x| *x == 0 || [1, 2, 3].contains(x)).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(1..3).any(|x| x == 0 || [1, 2, 3].contains(&x))`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:17:13
+ |
+LL | let _ = (1..3).find(|x| [1, 2, 3].contains(x) || *x == 0).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(1..3).any(|x| [1, 2, 3].contains(&x) || x == 0)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:18:13
+ |
+LL | let _ = (1..3)
+ | _____________^
+LL | | .find(|x| [1, 2, 3].contains(x) || *x == 0 || [4, 5, 6].contains(x) || *x == -1)
+LL | | .is_none();
+ | |__________________^ help: use `!_.any()` instead: `!(1..3).any(|x| [1, 2, 3].contains(&x) || x == 0 || [4, 5, 6].contains(&x) || x == -1)`
+
+error: called `is_none()` after searching an `Iterator` with `position`
+ --> $DIR/search_is_some_fixable_none.rs:23:13
+ |
+LL | let _ = v.iter().position(|&x| x < 0).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|&x| x < 0)`
+
+error: called `is_none()` after searching an `Iterator` with `rposition`
+ --> $DIR/search_is_some_fixable_none.rs:26:13
+ |
+LL | let _ = v.iter().rposition(|&x| x < 0).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|&x| x < 0)`
+
+error: called `is_none()` after calling `find()` on a string
+ --> $DIR/search_is_some_fixable_none.rs:32:13
+ |
+LL | let _ = "hello world".find("world").is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!"hello world".contains("world")`
+
+error: called `is_none()` after calling `find()` on a string
+ --> $DIR/search_is_some_fixable_none.rs:33:13
+ |
+LL | let _ = "hello world".find(&s2).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!"hello world".contains(&s2)`
+
+error: called `is_none()` after calling `find()` on a string
+ --> $DIR/search_is_some_fixable_none.rs:34:13
+ |
+LL | let _ = "hello world".find(&s2[2..]).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!"hello world".contains(&s2[2..])`
+
+error: called `is_none()` after calling `find()` on a string
+ --> $DIR/search_is_some_fixable_none.rs:36:13
+ |
+LL | let _ = s1.find("world").is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1.contains("world")`
+
+error: called `is_none()` after calling `find()` on a string
+ --> $DIR/search_is_some_fixable_none.rs:37:13
+ |
+LL | let _ = s1.find(&s2).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1.contains(&s2)`
+
+error: called `is_none()` after calling `find()` on a string
+ --> $DIR/search_is_some_fixable_none.rs:38:13
+ |
+LL | let _ = s1.find(&s2[2..]).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1.contains(&s2[2..])`
+
+error: called `is_none()` after calling `find()` on a string
+ --> $DIR/search_is_some_fixable_none.rs:40:13
+ |
+LL | let _ = s1[2..].find("world").is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1[2..].contains("world")`
+
+error: called `is_none()` after calling `find()` on a string
+ --> $DIR/search_is_some_fixable_none.rs:41:13
+ |
+LL | let _ = s1[2..].find(&s2).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1[2..].contains(&s2)`
+
+error: called `is_none()` after calling `find()` on a string
+ --> $DIR/search_is_some_fixable_none.rs:42:13
+ |
+LL | let _ = s1[2..].find(&s2[2..]).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1[2..].contains(&s2[2..])`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:58:25
+ |
+LL | .filter(|c| filter_hand.iter().find(|cc| c == cc).is_none())
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!filter_hand.iter().any(|cc| c == &cc)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:74:30
+ |
+LL | .filter(|(c, _)| filter_hand.iter().find(|cc| c == *cc).is_none())
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!filter_hand.iter().any(|cc| c == cc)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:85:17
+ |
+LL | let _ = vfoo.iter().find(|v| v.foo == 1 && v.bar == 2).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!vfoo.iter().any(|v| v.foo == 1 && v.bar == 2)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:88:17
+ |
+LL | let _ = vfoo
+ | _________________^
+LL | | .iter()
+LL | | .find(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2)
+LL | | .is_none();
+ | |______________________^
+ |
+help: use `!_.any()` instead
+ |
+LL ~ let _ = !vfoo
+LL ~ .iter().any(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2);
+ |
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:96:17
+ |
+LL | let _ = vfoo.iter().find(|a| a[0] == 42).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!vfoo.iter().any(|a| a[0] == 42)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:102:17
+ |
+LL | let _ = vfoo.iter().find(|sub| sub[1..4].len() == 3).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!vfoo.iter().any(|sub| sub[1..4].len() == 3)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:120:17
+ |
+LL | let _ = [ppx].iter().find(|ppp_x: &&&u32| please(**ppp_x)).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `![ppx].iter().any(|ppp_x: &&u32| please(ppp_x))`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:121:17
+ |
+LL | let _ = [String::from("Hey hey")].iter().find(|s| s.len() == 2).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `![String::from("Hey hey")].iter().any(|s| s.len() == 2)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:124:17
+ |
+LL | let _ = v.iter().find(|x| deref_enough(**x)).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x| deref_enough(*x))`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:125:17
+ |
+LL | let _ = v.iter().find(|x: &&u32| deref_enough(**x)).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x: &u32| deref_enough(*x))`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:128:17
+ |
+LL | let _ = v.iter().find(|x| arg_no_deref(x)).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x| arg_no_deref(&x))`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:130:17
+ |
+LL | let _ = v.iter().find(|x: &&u32| arg_no_deref(x)).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x: &u32| arg_no_deref(&x))`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:150:17
+ |
+LL | let _ = vfoo
+ | _________________^
+LL | | .iter()
+LL | | .find(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2)
+LL | | .is_none();
+ | |______________________^
+ |
+help: use `!_.any()` instead
+ |
+LL ~ let _ = !vfoo
+LL ~ .iter().any(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2);
+ |
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:166:17
+ |
+LL | let _ = vfoo.iter().find(|v| v.inner[0].bar == 2).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!vfoo.iter().any(|v| v.inner[0].bar == 2)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:171:17
+ |
+LL | let _ = vfoo.iter().find(|x| (**x)[0] == 9).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!vfoo.iter().any(|x| (**x)[0] == 9)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:184:17
+ |
+LL | let _ = vfoo.iter().find(|v| v.by_ref(&v.bar)).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!vfoo.iter().any(|v| v.by_ref(&v.bar))`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:188:17
+ |
+LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:189:17
+ |
+LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `![&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y)`
+
+error: writing `&String` instead of `&str` involves a new object where a slice will do
+ --> $DIR/search_is_some_fixable_none.rs:192:25
+ |
+LL | fn test_string_1(s: &String) -> bool {
+ | ^^^^^^^ help: change this to: `&str`
+ |
+ = note: `-D clippy::ptr-arg` implied by `-D warnings`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:208:17
+ |
+LL | let _ = v.iter().find(|s| s[0].is_empty()).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|s| s[0].is_empty())`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:209:17
+ |
+LL | let _ = v.iter().find(|s| test_string_1(&s[0])).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|s| test_string_1(&s[0]))`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:218:17
+ |
+LL | let _ = v.iter().find(|fp| fp.field.is_power_of_two()).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|fp| fp.field.is_power_of_two())`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:219:17
+ |
+LL | let _ = v.iter().find(|fp| test_u32_1(fp.field)).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|fp| test_u32_1(fp.field))`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_none.rs:220:17
+ |
+LL | let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_none();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|fp| test_u32_2(*fp.field))`
+
+error: aborting due to 44 previous errors
+
--- /dev/null
+// run-rustfix
+#![allow(dead_code)]
+#![warn(clippy::search_is_some)]
+
+fn main() {
+ let v = vec![3, 2, 1, 0, -1, -2, -3];
+ let y = &&42;
+
+ // Check `find().is_some()`, single-line case.
+ let _ = v.iter().any(|x| *x < 0);
+ let _ = (0..1).any(|x| **y == x); // one dereference less
+ let _ = (0..1).any(|x| x == 0);
+ let _ = v.iter().any(|x| *x == 0);
+ let _ = (4..5).any(|x| x == 1 || x == 3 || x == 5);
+ let _ = (1..3).any(|x| [1, 2, 3].contains(&x));
+ let _ = (1..3).any(|x| x == 0 || [1, 2, 3].contains(&x));
+ let _ = (1..3).any(|x| [1, 2, 3].contains(&x) || x == 0);
+ let _ = (1..3)
+ .any(|x| [1, 2, 3].contains(&x) || x == 0 || [4, 5, 6].contains(&x) || x == -1);
+
+ // Check `position().is_some()`, single-line case.
+ let _ = v.iter().any(|&x| x < 0);
+
+ // Check `rposition().is_some()`, single-line case.
+ let _ = v.iter().any(|&x| x < 0);
+
+ let s1 = String::from("hello world");
+ let s2 = String::from("world");
+ // caller of `find()` is a `&`static str`
+ let _ = "hello world".contains("world");
+ let _ = "hello world".contains(&s2);
+ let _ = "hello world".contains(&s2[2..]);
+ // caller of `find()` is a `String`
+ let _ = s1.contains("world");
+ let _ = s1.contains(&s2);
+ let _ = s1.contains(&s2[2..]);
+ // caller of `find()` is slice of `String`
+ let _ = s1[2..].contains("world");
+ let _ = s1[2..].contains(&s2);
+ let _ = s1[2..].contains(&s2[2..]);
+}
+
+#[allow(clippy::clone_on_copy, clippy::map_clone)]
+mod issue7392 {
+ struct Player {
+ hand: Vec<usize>,
+ }
+ fn filter() {
+ let p = Player {
+ hand: vec![1, 2, 3, 4, 5],
+ };
+ let filter_hand = vec![5];
+ let _ = p
+ .hand
+ .iter()
+ .filter(|c| filter_hand.iter().any(|cc| c == &cc))
+ .map(|c| c.clone())
+ .collect::<Vec<_>>();
+ }
+
+ struct PlayerTuple {
+ hand: Vec<(usize, char)>,
+ }
+ fn filter_tuple() {
+ let p = PlayerTuple {
+ hand: vec![(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), (5, 'e')],
+ };
+ let filter_hand = vec![5];
+ let _ = p
+ .hand
+ .iter()
+ .filter(|(c, _)| filter_hand.iter().any(|cc| c == cc))
+ .map(|c| c.clone())
+ .collect::<Vec<_>>();
+ }
+
+ fn field_projection() {
+ struct Foo {
+ foo: i32,
+ bar: u32,
+ }
+ let vfoo = vec![Foo { foo: 1, bar: 2 }];
+ let _ = vfoo.iter().any(|v| v.foo == 1 && v.bar == 2);
+
+ let vfoo = vec![(42, Foo { foo: 1, bar: 2 })];
+ let _ = vfoo
+ .iter()
+ .any(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2);
+ }
+
+ fn index_projection() {
+ let vfoo = vec![[0, 1, 2, 3]];
+ let _ = vfoo.iter().any(|a| a[0] == 42);
+ }
+
+ #[allow(clippy::match_like_matches_macro)]
+ fn slice_projection() {
+ let vfoo = vec![[0, 1, 2, 3, 0, 1, 2, 3]];
+ let _ = vfoo.iter().any(|sub| sub[1..4].len() == 3);
+ }
+
+ fn please(x: &u32) -> bool {
+ *x == 9
+ }
+
+ fn deref_enough(x: u32) -> bool {
+ x == 78
+ }
+
+ fn arg_no_deref(x: &&u32) -> bool {
+ **x == 78
+ }
+
+ fn more_projections() {
+ let x = 19;
+ let ppx: &u32 = &x;
+ let _ = [ppx].iter().any(|ppp_x: &&u32| please(ppp_x));
+ let _ = [String::from("Hey hey")].iter().any(|s| s.len() == 2);
+
+ let v = vec![3, 2, 1, 0];
+ let _ = v.iter().any(|x| deref_enough(*x));
+ let _ = v.iter().any(|x: &u32| deref_enough(*x));
+
+ #[allow(clippy::redundant_closure)]
+ let _ = v.iter().any(|x| arg_no_deref(&x));
+ #[allow(clippy::redundant_closure)]
+ let _ = v.iter().any(|x: &u32| arg_no_deref(&x));
+ }
+
+ fn field_index_projection() {
+ struct FooDouble {
+ bar: Vec<Vec<i32>>,
+ }
+ struct Foo {
+ bar: Vec<i32>,
+ }
+ struct FooOuter {
+ inner: Foo,
+ inner_double: FooDouble,
+ }
+ let vfoo = vec![FooOuter {
+ inner: Foo { bar: vec![0, 1, 2, 3] },
+ inner_double: FooDouble {
+ bar: vec![vec![0, 1, 2, 3]],
+ },
+ }];
+ let _ = vfoo
+ .iter()
+ .any(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2);
+ }
+
+ fn index_field_projection() {
+ struct Foo {
+ bar: i32,
+ }
+ struct FooOuter {
+ inner: Vec<Foo>,
+ }
+ let vfoo = vec![FooOuter {
+ inner: vec![Foo { bar: 0 }],
+ }];
+ let _ = vfoo.iter().any(|v| v.inner[0].bar == 2);
+ }
+
+ fn double_deref_index_projection() {
+ let vfoo = vec![&&[0, 1, 2, 3]];
+ let _ = vfoo.iter().any(|x| (**x)[0] == 9);
+ }
+
+ fn method_call_by_ref() {
+ struct Foo {
+ bar: u32,
+ }
+ impl Foo {
+ pub fn by_ref(&self, x: &u32) -> bool {
+ *x == self.bar
+ }
+ }
+ let vfoo = vec![Foo { bar: 1 }];
+ let _ = vfoo.iter().any(|v| v.by_ref(&v.bar));
+ }
+
+ fn ref_bindings() {
+ let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y);
+ let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().any(|(&x, y)| x == *y);
+ }
+
+ fn test_string_1(s: &str) -> bool {
+ s.is_empty()
+ }
+
+ fn test_u32_1(s: &u32) -> bool {
+ s.is_power_of_two()
+ }
+
+ fn test_u32_2(s: u32) -> bool {
+ s.is_power_of_two()
+ }
+
+ fn projection_in_args_test() {
+ // Index projections
+ let lst = &[String::from("Hello"), String::from("world")];
+ let v: Vec<&[String]> = vec![lst];
+ let _ = v.iter().any(|s| s[0].is_empty());
+ let _ = v.iter().any(|s| test_string_1(&s[0]));
+
+ // Field projections
+ struct FieldProjection<'a> {
+ field: &'a u32,
+ }
+ let field = 123456789;
+ let instance = FieldProjection { field: &field };
+ let v = vec![instance];
+ let _ = v.iter().any(|fp| fp.field.is_power_of_two());
+ let _ = v.iter().any(|fp| test_u32_1(fp.field));
+ let _ = v.iter().any(|fp| test_u32_2(*fp.field));
+ }
+}
--- /dev/null
+// run-rustfix
+#![allow(dead_code)]
+#![warn(clippy::search_is_some)]
+
+fn main() {
+ let v = vec![3, 2, 1, 0, -1, -2, -3];
+ let y = &&42;
+
+ // Check `find().is_some()`, single-line case.
+ let _ = v.iter().find(|&x| *x < 0).is_some();
+ let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less
+ let _ = (0..1).find(|x| *x == 0).is_some();
+ let _ = v.iter().find(|x| **x == 0).is_some();
+ let _ = (4..5).find(|x| *x == 1 || *x == 3 || *x == 5).is_some();
+ let _ = (1..3).find(|x| [1, 2, 3].contains(x)).is_some();
+ let _ = (1..3).find(|x| *x == 0 || [1, 2, 3].contains(x)).is_some();
+ let _ = (1..3).find(|x| [1, 2, 3].contains(x) || *x == 0).is_some();
+ let _ = (1..3)
+ .find(|x| [1, 2, 3].contains(x) || *x == 0 || [4, 5, 6].contains(x) || *x == -1)
+ .is_some();
+
+ // Check `position().is_some()`, single-line case.
+ let _ = v.iter().position(|&x| x < 0).is_some();
+
+ // Check `rposition().is_some()`, single-line case.
+ let _ = v.iter().rposition(|&x| x < 0).is_some();
+
+ let s1 = String::from("hello world");
+ let s2 = String::from("world");
+ // caller of `find()` is a `&`static str`
+ let _ = "hello world".find("world").is_some();
+ let _ = "hello world".find(&s2).is_some();
+ let _ = "hello world".find(&s2[2..]).is_some();
+ // caller of `find()` is a `String`
+ let _ = s1.find("world").is_some();
+ let _ = s1.find(&s2).is_some();
+ let _ = s1.find(&s2[2..]).is_some();
+ // caller of `find()` is slice of `String`
+ let _ = s1[2..].find("world").is_some();
+ let _ = s1[2..].find(&s2).is_some();
+ let _ = s1[2..].find(&s2[2..]).is_some();
+}
+
+#[allow(clippy::clone_on_copy, clippy::map_clone)]
+mod issue7392 {
+ struct Player {
+ hand: Vec<usize>,
+ }
+ fn filter() {
+ let p = Player {
+ hand: vec![1, 2, 3, 4, 5],
+ };
+ let filter_hand = vec![5];
+ let _ = p
+ .hand
+ .iter()
+ .filter(|c| filter_hand.iter().find(|cc| c == cc).is_some())
+ .map(|c| c.clone())
+ .collect::<Vec<_>>();
+ }
+
+ struct PlayerTuple {
+ hand: Vec<(usize, char)>,
+ }
+ fn filter_tuple() {
+ let p = PlayerTuple {
+ hand: vec![(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd'), (5, 'e')],
+ };
+ let filter_hand = vec![5];
+ let _ = p
+ .hand
+ .iter()
+ .filter(|(c, _)| filter_hand.iter().find(|cc| c == *cc).is_some())
+ .map(|c| c.clone())
+ .collect::<Vec<_>>();
+ }
+
+ fn field_projection() {
+ struct Foo {
+ foo: i32,
+ bar: u32,
+ }
+ let vfoo = vec![Foo { foo: 1, bar: 2 }];
+ let _ = vfoo.iter().find(|v| v.foo == 1 && v.bar == 2).is_some();
+
+ let vfoo = vec![(42, Foo { foo: 1, bar: 2 })];
+ let _ = vfoo
+ .iter()
+ .find(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2)
+ .is_some();
+ }
+
+ fn index_projection() {
+ let vfoo = vec![[0, 1, 2, 3]];
+ let _ = vfoo.iter().find(|a| a[0] == 42).is_some();
+ }
+
+ #[allow(clippy::match_like_matches_macro)]
+ fn slice_projection() {
+ let vfoo = vec![[0, 1, 2, 3, 0, 1, 2, 3]];
+ let _ = vfoo.iter().find(|sub| sub[1..4].len() == 3).is_some();
+ }
+
+ fn please(x: &u32) -> bool {
+ *x == 9
+ }
+
+ fn deref_enough(x: u32) -> bool {
+ x == 78
+ }
+
+ fn arg_no_deref(x: &&u32) -> bool {
+ **x == 78
+ }
+
+ fn more_projections() {
+ let x = 19;
+ let ppx: &u32 = &x;
+ let _ = [ppx].iter().find(|ppp_x: &&&u32| please(**ppp_x)).is_some();
+ let _ = [String::from("Hey hey")].iter().find(|s| s.len() == 2).is_some();
+
+ let v = vec![3, 2, 1, 0];
+ let _ = v.iter().find(|x| deref_enough(**x)).is_some();
+ let _ = v.iter().find(|x: &&u32| deref_enough(**x)).is_some();
+
+ #[allow(clippy::redundant_closure)]
+ let _ = v.iter().find(|x| arg_no_deref(x)).is_some();
+ #[allow(clippy::redundant_closure)]
+ let _ = v.iter().find(|x: &&u32| arg_no_deref(x)).is_some();
+ }
+
+ fn field_index_projection() {
+ struct FooDouble {
+ bar: Vec<Vec<i32>>,
+ }
+ struct Foo {
+ bar: Vec<i32>,
+ }
+ struct FooOuter {
+ inner: Foo,
+ inner_double: FooDouble,
+ }
+ let vfoo = vec![FooOuter {
+ inner: Foo { bar: vec![0, 1, 2, 3] },
+ inner_double: FooDouble {
+ bar: vec![vec![0, 1, 2, 3]],
+ },
+ }];
+ let _ = vfoo
+ .iter()
+ .find(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2)
+ .is_some();
+ }
+
+ fn index_field_projection() {
+ struct Foo {
+ bar: i32,
+ }
+ struct FooOuter {
+ inner: Vec<Foo>,
+ }
+ let vfoo = vec![FooOuter {
+ inner: vec![Foo { bar: 0 }],
+ }];
+ let _ = vfoo.iter().find(|v| v.inner[0].bar == 2).is_some();
+ }
+
+ fn double_deref_index_projection() {
+ let vfoo = vec![&&[0, 1, 2, 3]];
+ let _ = vfoo.iter().find(|x| (**x)[0] == 9).is_some();
+ }
+
+ fn method_call_by_ref() {
+ struct Foo {
+ bar: u32,
+ }
+ impl Foo {
+ pub fn by_ref(&self, x: &u32) -> bool {
+ *x == self.bar
+ }
+ }
+ let vfoo = vec![Foo { bar: 1 }];
+ let _ = vfoo.iter().find(|v| v.by_ref(&v.bar)).is_some();
+ }
+
+ fn ref_bindings() {
+ let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_some();
+ let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_some();
+ }
+
+ fn test_string_1(s: &String) -> bool {
+ s.is_empty()
+ }
+
+ fn test_u32_1(s: &u32) -> bool {
+ s.is_power_of_two()
+ }
+
+ fn test_u32_2(s: u32) -> bool {
+ s.is_power_of_two()
+ }
+
+ fn projection_in_args_test() {
+ // Index projections
+ let lst = &[String::from("Hello"), String::from("world")];
+ let v: Vec<&[String]> = vec![lst];
+ let _ = v.iter().find(|s| s[0].is_empty()).is_some();
+ let _ = v.iter().find(|s| test_string_1(&s[0])).is_some();
+
+ // Field projections
+ struct FieldProjection<'a> {
+ field: &'a u32,
+ }
+ let field = 123456789;
+ let instance = FieldProjection { field: &field };
+ let v = vec![instance];
+ let _ = v.iter().find(|fp| fp.field.is_power_of_two()).is_some();
+ let _ = v.iter().find(|fp| test_u32_1(fp.field)).is_some();
+ let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_some();
+ }
+}
--- /dev/null
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:10:22
+ |
+LL | let _ = v.iter().find(|&x| *x < 0).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| *x < 0)`
+ |
+ = note: `-D clippy::search-is-some` implied by `-D warnings`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:11:20
+ |
+LL | let _ = (0..1).find(|x| **y == *x).is_some(); // one dereference less
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| **y == x)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:12:20
+ |
+LL | let _ = (0..1).find(|x| *x == 0).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| x == 0)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:13:22
+ |
+LL | let _ = v.iter().find(|x| **x == 0).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| *x == 0)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:14:20
+ |
+LL | let _ = (4..5).find(|x| *x == 1 || *x == 3 || *x == 5).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| x == 1 || x == 3 || x == 5)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:15:20
+ |
+LL | let _ = (1..3).find(|x| [1, 2, 3].contains(x)).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| [1, 2, 3].contains(&x))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:16:20
+ |
+LL | let _ = (1..3).find(|x| *x == 0 || [1, 2, 3].contains(x)).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| x == 0 || [1, 2, 3].contains(&x))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:17:20
+ |
+LL | let _ = (1..3).find(|x| [1, 2, 3].contains(x) || *x == 0).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| [1, 2, 3].contains(&x) || x == 0)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:19:10
+ |
+LL | .find(|x| [1, 2, 3].contains(x) || *x == 0 || [4, 5, 6].contains(x) || *x == -1)
+ | __________^
+LL | | .is_some();
+ | |__________________^ help: use `any()` instead: `any(|x| [1, 2, 3].contains(&x) || x == 0 || [4, 5, 6].contains(&x) || x == -1)`
+
+error: called `is_some()` after searching an `Iterator` with `position`
+ --> $DIR/search_is_some_fixable_some.rs:23:22
+ |
+LL | let _ = v.iter().position(|&x| x < 0).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|&x| x < 0)`
+
+error: called `is_some()` after searching an `Iterator` with `rposition`
+ --> $DIR/search_is_some_fixable_some.rs:26:22
+ |
+LL | let _ = v.iter().rposition(|&x| x < 0).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|&x| x < 0)`
+
+error: called `is_some()` after calling `find()` on a string
+ --> $DIR/search_is_some_fixable_some.rs:31:27
+ |
+LL | let _ = "hello world".find("world").is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains("world")`
+
+error: called `is_some()` after calling `find()` on a string
+ --> $DIR/search_is_some_fixable_some.rs:32:27
+ |
+LL | let _ = "hello world".find(&s2).is_some();
+ | ^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2)`
+
+error: called `is_some()` after calling `find()` on a string
+ --> $DIR/search_is_some_fixable_some.rs:33:27
+ |
+LL | let _ = "hello world".find(&s2[2..]).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2[2..])`
+
+error: called `is_some()` after calling `find()` on a string
+ --> $DIR/search_is_some_fixable_some.rs:35:16
+ |
+LL | let _ = s1.find("world").is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains("world")`
+
+error: called `is_some()` after calling `find()` on a string
+ --> $DIR/search_is_some_fixable_some.rs:36:16
+ |
+LL | let _ = s1.find(&s2).is_some();
+ | ^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2)`
+
+error: called `is_some()` after calling `find()` on a string
+ --> $DIR/search_is_some_fixable_some.rs:37:16
+ |
+LL | let _ = s1.find(&s2[2..]).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2[2..])`
+
+error: called `is_some()` after calling `find()` on a string
+ --> $DIR/search_is_some_fixable_some.rs:39:21
+ |
+LL | let _ = s1[2..].find("world").is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains("world")`
+
+error: called `is_some()` after calling `find()` on a string
+ --> $DIR/search_is_some_fixable_some.rs:40:21
+ |
+LL | let _ = s1[2..].find(&s2).is_some();
+ | ^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2)`
+
+error: called `is_some()` after calling `find()` on a string
+ --> $DIR/search_is_some_fixable_some.rs:41:21
+ |
+LL | let _ = s1[2..].find(&s2[2..]).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2[2..])`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:57:44
+ |
+LL | .filter(|c| filter_hand.iter().find(|cc| c == cc).is_some())
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|cc| c == &cc)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:73:49
+ |
+LL | .filter(|(c, _)| filter_hand.iter().find(|cc| c == *cc).is_some())
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|cc| c == cc)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:84:29
+ |
+LL | let _ = vfoo.iter().find(|v| v.foo == 1 && v.bar == 2).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|v| v.foo == 1 && v.bar == 2)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:89:14
+ |
+LL | .find(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2)
+ | ______________^
+LL | | .is_some();
+ | |______________________^ help: use `any()` instead: `any(|(i, v)| *i == 42 && v.foo == 1 && v.bar == 2)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:95:29
+ |
+LL | let _ = vfoo.iter().find(|a| a[0] == 42).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|a| a[0] == 42)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:101:29
+ |
+LL | let _ = vfoo.iter().find(|sub| sub[1..4].len() == 3).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|sub| sub[1..4].len() == 3)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:119:30
+ |
+LL | let _ = [ppx].iter().find(|ppp_x: &&&u32| please(**ppp_x)).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|ppp_x: &&u32| please(ppp_x))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:120:50
+ |
+LL | let _ = [String::from("Hey hey")].iter().find(|s| s.len() == 2).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|s| s.len() == 2)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:123:26
+ |
+LL | let _ = v.iter().find(|x| deref_enough(**x)).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| deref_enough(*x))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:124:26
+ |
+LL | let _ = v.iter().find(|x: &&u32| deref_enough(**x)).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x: &u32| deref_enough(*x))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:127:26
+ |
+LL | let _ = v.iter().find(|x| arg_no_deref(x)).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| arg_no_deref(&x))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:129:26
+ |
+LL | let _ = v.iter().find(|x: &&u32| arg_no_deref(x)).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x: &u32| arg_no_deref(&x))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:151:14
+ |
+LL | .find(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2)
+ | ______________^
+LL | | .is_some();
+ | |______________________^ help: use `any()` instead: `any(|v| v.inner_double.bar[0][0] == 2 && v.inner.bar[0] == 2)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:165:29
+ |
+LL | let _ = vfoo.iter().find(|v| v.inner[0].bar == 2).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|v| v.inner[0].bar == 2)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:170:29
+ |
+LL | let _ = vfoo.iter().find(|x| (**x)[0] == 9).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x| (**x)[0] == 9)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:183:29
+ |
+LL | let _ = vfoo.iter().find(|v| v.by_ref(&v.bar)).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|v| v.by_ref(&v.bar))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:187:55
+ |
+LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|(&x, y)| x == *y).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|(&x, y)| x == *y)`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:188:55
+ |
+LL | let _ = [&(&1, 2), &(&3, 4), &(&5, 4)].iter().find(|&(&x, y)| x == *y).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|(&x, y)| x == *y)`
+
+error: writing `&String` instead of `&str` involves a new object where a slice will do
+ --> $DIR/search_is_some_fixable_some.rs:191:25
+ |
+LL | fn test_string_1(s: &String) -> bool {
+ | ^^^^^^^ help: change this to: `&str`
+ |
+ = note: `-D clippy::ptr-arg` implied by `-D warnings`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:207:26
+ |
+LL | let _ = v.iter().find(|s| s[0].is_empty()).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|s| s[0].is_empty())`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:208:26
+ |
+LL | let _ = v.iter().find(|s| test_string_1(&s[0])).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|s| test_string_1(&s[0]))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:217:26
+ |
+LL | let _ = v.iter().find(|fp| fp.field.is_power_of_two()).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|fp| fp.field.is_power_of_two())`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:218:26
+ |
+LL | let _ = v.iter().find(|fp| test_u32_1(fp.field)).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|fp| test_u32_1(fp.field))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+ --> $DIR/search_is_some_fixable_some.rs:219:26
+ |
+LL | let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_some();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|fp| test_u32_2(*fp.field))`
+
+error: aborting due to 44 previous errors
+
#![warn(clippy::semicolon_if_nothing_returned)]
#![allow(clippy::redundant_closure)]
#![feature(label_break_value)]
+#![feature(let_else)]
fn get_unit() {}
}
repro!();
}
+
+fn function_returning_option() -> Option<i32> {
+ Some(1)
+}
+
+// No warning
+fn let_else_stmts() {
+ let Some(x) = function_returning_option() else { return; };
+}
error: consider adding a `;` to the last statement for consistent formatting
- --> $DIR/semicolon_if_nothing_returned.rs:9:5
+ --> $DIR/semicolon_if_nothing_returned.rs:10:5
|
LL | println!("Hello")
| ^^^^^^^^^^^^^^^^^ help: add a `;` here: `println!("Hello");`
= note: `-D clippy::semicolon-if-nothing-returned` implied by `-D warnings`
error: consider adding a `;` to the last statement for consistent formatting
- --> $DIR/semicolon_if_nothing_returned.rs:13:5
+ --> $DIR/semicolon_if_nothing_returned.rs:14:5
|
LL | get_unit()
| ^^^^^^^^^^ help: add a `;` here: `get_unit();`
error: consider adding a `;` to the last statement for consistent formatting
- --> $DIR/semicolon_if_nothing_returned.rs:18:5
+ --> $DIR/semicolon_if_nothing_returned.rs:19:5
|
LL | y = x + 1
| ^^^^^^^^^ help: add a `;` here: `y = x + 1;`
error: consider adding a `;` to the last statement for consistent formatting
- --> $DIR/semicolon_if_nothing_returned.rs:24:9
+ --> $DIR/semicolon_if_nothing_returned.rs:25:9
|
LL | hello()
| ^^^^^^^ help: add a `;` here: `hello();`
error: consider adding a `;` to the last statement for consistent formatting
- --> $DIR/semicolon_if_nothing_returned.rs:35:9
+ --> $DIR/semicolon_if_nothing_returned.rs:36:9
|
LL | ptr::drop_in_place(s.as_mut_ptr())
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add a `;` here: `ptr::drop_in_place(s.as_mut_ptr());`
None
}
+pub async fn foo1(_a: i32) {}
+
+pub async fn foo2(_a: i32, _b: i64) {
+ let _b = _a;
+}
+
fn main() {}
LL | let _ = |[x]: [u32; 1]| {
| ^
-error: aborting due to 20 previous errors
+error: `_b` shadows a previous, unrelated binding
+ --> $DIR/shadow.rs:85:9
+ |
+LL | let _b = _a;
+ | ^^
+ |
+note: previous binding is here
+ --> $DIR/shadow.rs:84:28
+ |
+LL | pub async fn foo2(_a: i32, _b: i64) {
+ | ^^
+
+error: aborting due to 21 previous errors
x.split('💣');
// Can't use this lint for unicode code points which don't fit in a char
x.split("❤️");
+ x.split_inclusive('x');
x.contains('x');
x.starts_with('x');
x.ends_with('x');
x.rsplit_terminator('x');
x.splitn(2, 'x');
x.rsplitn(2, 'x');
+ x.split_once('x');
+ x.rsplit_once('x');
x.matches('x');
x.rmatches('x');
x.match_indices('x');
x.trim_end_matches('x');
x.strip_prefix('x');
x.strip_suffix('x');
+ x.replace('x', "y");
+ x.replacen('x', "y", 3);
// Make sure we escape characters correctly.
x.split('\n');
x.split('\'');
let h = HashSet::<String>::new();
h.contains("X"); // should not warn
- x.replace(";", ",").split(','); // issue #2978
+ x.replace(';', ",").split(','); // issue #2978
x.starts_with('\x03'); // issue #2996
// Issue #3204
x.split('a');
x.split('\'');
x.split('#');
+ // Must escape backslash in raw strings when converting to char #8060
+ x.split('\\');
+ x.split('\\');
}
x.split("💣");
// Can't use this lint for unicode code points which don't fit in a char
x.split("❤️");
+ x.split_inclusive("x");
x.contains("x");
x.starts_with("x");
x.ends_with("x");
x.rsplit_terminator("x");
x.splitn(2, "x");
x.rsplitn(2, "x");
+ x.split_once("x");
+ x.rsplit_once("x");
x.matches("x");
x.rmatches("x");
x.match_indices("x");
x.trim_end_matches("x");
x.strip_prefix("x");
x.strip_suffix("x");
+ x.replace("x", "y");
+ x.replacen("x", "y", 3);
// Make sure we escape characters correctly.
x.split("\n");
x.split("'");
let h = HashSet::<String>::new();
h.contains("X"); // should not warn
- x.replace(";", ",").split(","); // issue #2978
+ x.replace(';', ",").split(","); // issue #2978
x.starts_with("\x03"); // issue #2996
// Issue #3204
x.split(r###"a"###);
x.split(r###"'"###);
x.split(r###"#"###);
+ // Must escape backslash in raw strings when converting to char #8060
+ x.split(r#"\"#);
+ x.split(r"\");
}
| ^^^^ help: try using a `char` instead: `'💣'`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:20:16
+ --> $DIR/single_char_pattern.rs:20:23
+ |
+LL | x.split_inclusive("x");
+ | ^^^ help: try using a `char` instead: `'x'`
+
+error: single-character string constant used as pattern
+ --> $DIR/single_char_pattern.rs:21:16
|
LL | x.contains("x");
| ^^^ help: try using a `char` instead: `'x'`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:21:19
+ --> $DIR/single_char_pattern.rs:22:19
|
LL | x.starts_with("x");
| ^^^ help: try using a `char` instead: `'x'`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:22:17
+ --> $DIR/single_char_pattern.rs:23:17
|
LL | x.ends_with("x");
| ^^^ help: try using a `char` instead: `'x'`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:23:12
+ --> $DIR/single_char_pattern.rs:24:12
|
LL | x.find("x");
| ^^^ help: try using a `char` instead: `'x'`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:24:13
+ --> $DIR/single_char_pattern.rs:25:13
|
LL | x.rfind("x");
| ^^^ help: try using a `char` instead: `'x'`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:25:14
+ --> $DIR/single_char_pattern.rs:26:14
|
LL | x.rsplit("x");
| ^^^ help: try using a `char` instead: `'x'`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:26:24
+ --> $DIR/single_char_pattern.rs:27:24
|
LL | x.split_terminator("x");
| ^^^ help: try using a `char` instead: `'x'`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:27:25
+ --> $DIR/single_char_pattern.rs:28:25
|
LL | x.rsplit_terminator("x");
| ^^^ help: try using a `char` instead: `'x'`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:28:17
+ --> $DIR/single_char_pattern.rs:29:17
|
LL | x.splitn(2, "x");
| ^^^ help: try using a `char` instead: `'x'`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:29:18
+ --> $DIR/single_char_pattern.rs:30:18
|
LL | x.rsplitn(2, "x");
| ^^^ help: try using a `char` instead: `'x'`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:30:15
+ --> $DIR/single_char_pattern.rs:31:18
+ |
+LL | x.split_once("x");
+ | ^^^ help: try using a `char` instead: `'x'`
+
+error: single-character string constant used as pattern
+ --> $DIR/single_char_pattern.rs:32:19
+ |
+LL | x.rsplit_once("x");
+ | ^^^ help: try using a `char` instead: `'x'`
+
+error: single-character string constant used as pattern
+ --> $DIR/single_char_pattern.rs:33:15
|
LL | x.matches("x");
| ^^^ help: try using a `char` instead: `'x'`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:31:16
+ --> $DIR/single_char_pattern.rs:34:16
|
LL | x.rmatches("x");
| ^^^ help: try using a `char` instead: `'x'`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:32:21
+ --> $DIR/single_char_pattern.rs:35:21
|
LL | x.match_indices("x");
| ^^^ help: try using a `char` instead: `'x'`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:33:22
+ --> $DIR/single_char_pattern.rs:36:22
|
LL | x.rmatch_indices("x");
| ^^^ help: try using a `char` instead: `'x'`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:34:26
+ --> $DIR/single_char_pattern.rs:37:26
|
LL | x.trim_start_matches("x");
| ^^^ help: try using a `char` instead: `'x'`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:35:24
+ --> $DIR/single_char_pattern.rs:38:24
|
LL | x.trim_end_matches("x");
| ^^^ help: try using a `char` instead: `'x'`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:36:20
+ --> $DIR/single_char_pattern.rs:39:20
|
LL | x.strip_prefix("x");
| ^^^ help: try using a `char` instead: `'x'`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:37:20
+ --> $DIR/single_char_pattern.rs:40:20
|
LL | x.strip_suffix("x");
| ^^^ help: try using a `char` instead: `'x'`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:39:13
+ --> $DIR/single_char_pattern.rs:41:15
+ |
+LL | x.replace("x", "y");
+ | ^^^ help: try using a `char` instead: `'x'`
+
+error: single-character string constant used as pattern
+ --> $DIR/single_char_pattern.rs:42:16
+ |
+LL | x.replacen("x", "y", 3);
+ | ^^^ help: try using a `char` instead: `'x'`
+
+error: single-character string constant used as pattern
+ --> $DIR/single_char_pattern.rs:44:13
|
LL | x.split("/n");
| ^^^^ help: try using a `char` instead: `'/n'`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:40:13
+ --> $DIR/single_char_pattern.rs:45:13
|
LL | x.split("'");
| ^^^ help: try using a `char` instead: `'/''`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:41:13
+ --> $DIR/single_char_pattern.rs:46:13
|
LL | x.split("/'");
| ^^^^ help: try using a `char` instead: `'/''`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:46:31
+ --> $DIR/single_char_pattern.rs:51:31
|
-LL | x.replace(";", ",").split(","); // issue #2978
+LL | x.replace(';', ",").split(","); // issue #2978
| ^^^ help: try using a `char` instead: `','`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:47:19
+ --> $DIR/single_char_pattern.rs:52:19
|
LL | x.starts_with("/x03"); // issue #2996
| ^^^^^^ help: try using a `char` instead: `'/x03'`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:54:13
+ --> $DIR/single_char_pattern.rs:59:13
|
LL | x.split(r"a");
| ^^^^ help: try using a `char` instead: `'a'`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:55:13
+ --> $DIR/single_char_pattern.rs:60:13
|
LL | x.split(r#"a"#);
| ^^^^^^ help: try using a `char` instead: `'a'`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:56:13
+ --> $DIR/single_char_pattern.rs:61:13
|
LL | x.split(r###"a"###);
| ^^^^^^^^^^ help: try using a `char` instead: `'a'`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:57:13
+ --> $DIR/single_char_pattern.rs:62:13
|
LL | x.split(r###"'"###);
| ^^^^^^^^^^ help: try using a `char` instead: `'/''`
error: single-character string constant used as pattern
- --> $DIR/single_char_pattern.rs:58:13
+ --> $DIR/single_char_pattern.rs:63:13
|
LL | x.split(r###"#"###);
| ^^^^^^^^^^ help: try using a `char` instead: `'#'`
-error: aborting due to 32 previous errors
+error: single-character string constant used as pattern
+ --> $DIR/single_char_pattern.rs:65:13
+ |
+LL | x.split(r#"/"#);
+ | ^^^^^^ help: try using a `char` instead: `'/'`
+
+error: single-character string constant used as pattern
+ --> $DIR/single_char_pattern.rs:66:13
+ |
+LL | x.split(r"/");
+ | ^^^^ help: try using a `char` instead: `'/'`
+
+error: aborting due to 39 previous errors
--- /dev/null
+// run-rustfix
+
+#![warn(clippy::strlen_on_c_strings)]
+#![allow(dead_code)]
+#![feature(rustc_private)]
+extern crate libc;
+
+#[allow(unused)]
+use libc::strlen;
+use std::ffi::{CStr, CString};
+
+fn main() {
+ // CString
+ let cstring = CString::new("foo").expect("CString::new failed");
+ let _ = cstring.as_bytes().len();
+
+ // CStr
+ let cstr = CStr::from_bytes_with_nul(b"foo\0").expect("CStr::from_bytes_with_nul failed");
+ let _ = cstr.to_bytes().len();
+
+ let _ = cstr.to_bytes().len();
+
+ let pcstr: *const &CStr = &cstr;
+ let _ = unsafe { (*pcstr).to_bytes().len() };
+
+ unsafe fn unsafe_identity<T>(x: T) -> T {
+ x
+ }
+ let _ = unsafe { unsafe_identity(cstr).to_bytes().len() };
+ let _ = unsafe { unsafe_identity(cstr) }.to_bytes().len();
+
+ let f: unsafe fn(_) -> _ = unsafe_identity;
+ let _ = unsafe { f(cstr).to_bytes().len() };
+}
+// run-rustfix
+
#![warn(clippy::strlen_on_c_strings)]
#![allow(dead_code)]
#![feature(rustc_private)]
extern crate libc;
+#[allow(unused)]
+use libc::strlen;
use std::ffi::{CStr, CString};
fn main() {
// CString
let cstring = CString::new("foo").expect("CString::new failed");
- let len = unsafe { libc::strlen(cstring.as_ptr()) };
+ let _ = unsafe { libc::strlen(cstring.as_ptr()) };
// CStr
let cstr = CStr::from_bytes_with_nul(b"foo\0").expect("CStr::from_bytes_with_nul failed");
- let len = unsafe { libc::strlen(cstr.as_ptr()) };
+ let _ = unsafe { libc::strlen(cstr.as_ptr()) };
+
+ let _ = unsafe { strlen(cstr.as_ptr()) };
+
+ let pcstr: *const &CStr = &cstr;
+ let _ = unsafe { strlen((*pcstr).as_ptr()) };
+
+ unsafe fn unsafe_identity<T>(x: T) -> T {
+ x
+ }
+ let _ = unsafe { strlen(unsafe_identity(cstr).as_ptr()) };
+ let _ = unsafe { strlen(unsafe { unsafe_identity(cstr) }.as_ptr()) };
+
+ let f: unsafe fn(_) -> _ = unsafe_identity;
+ let _ = unsafe { strlen(f(cstr).as_ptr()) };
}
error: using `libc::strlen` on a `CString` or `CStr` value
- --> $DIR/strlen_on_c_strings.rs:11:24
+ --> $DIR/strlen_on_c_strings.rs:15:13
|
-LL | let len = unsafe { libc::strlen(cstring.as_ptr()) };
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | let _ = unsafe { libc::strlen(cstring.as_ptr()) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `cstring.as_bytes().len()`
|
= note: `-D clippy::strlen-on-c-strings` implied by `-D warnings`
-help: try this (you might also need to get rid of `unsafe` block in some cases):
+
+error: using `libc::strlen` on a `CString` or `CStr` value
+ --> $DIR/strlen_on_c_strings.rs:19:13
+ |
+LL | let _ = unsafe { libc::strlen(cstr.as_ptr()) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `cstr.to_bytes().len()`
+
+error: using `libc::strlen` on a `CString` or `CStr` value
+ --> $DIR/strlen_on_c_strings.rs:21:13
+ |
+LL | let _ = unsafe { strlen(cstr.as_ptr()) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `cstr.to_bytes().len()`
+
+error: using `libc::strlen` on a `CString` or `CStr` value
+ --> $DIR/strlen_on_c_strings.rs:24:22
|
-LL | let len = unsafe { cstring.as_bytes().len() };
- | ~~~~~~~~~~~~~~~~~~~~~~~~
+LL | let _ = unsafe { strlen((*pcstr).as_ptr()) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `(*pcstr).to_bytes().len()`
error: using `libc::strlen` on a `CString` or `CStr` value
- --> $DIR/strlen_on_c_strings.rs:15:24
+ --> $DIR/strlen_on_c_strings.rs:29:22
|
-LL | let len = unsafe { libc::strlen(cstr.as_ptr()) };
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | let _ = unsafe { strlen(unsafe_identity(cstr).as_ptr()) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unsafe_identity(cstr).to_bytes().len()`
+
+error: using `libc::strlen` on a `CString` or `CStr` value
+ --> $DIR/strlen_on_c_strings.rs:30:13
|
-help: try this (you might also need to get rid of `unsafe` block in some cases):
+LL | let _ = unsafe { strlen(unsafe { unsafe_identity(cstr) }.as_ptr()) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unsafe { unsafe_identity(cstr) }.to_bytes().len()`
+
+error: using `libc::strlen` on a `CString` or `CStr` value
+ --> $DIR/strlen_on_c_strings.rs:33:22
|
-LL | let len = unsafe { cstr.to_bytes().len() };
- | ~~~~~~~~~~~~~~~~~~~~~
+LL | let _ = unsafe { strlen(f(cstr).as_ptr()) };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `f(cstr).to_bytes().len()`
-error: aborting due to 2 previous errors
+error: aborting due to 7 previous errors
#![warn(clippy::suspicious_splitn)]
+#![allow(clippy::needless_splitn)]
fn main() {
let _ = "a,b,c".splitn(3, ',');
error: `splitn` called with `0` splits
- --> $DIR/suspicious_splitn.rs:9:13
+ --> $DIR/suspicious_splitn.rs:10:13
|
LL | let _ = "a,b".splitn(0, ',');
| ^^^^^^^^^^^^^^^^^^^^
= note: the resulting iterator will always return `None`
error: `rsplitn` called with `0` splits
- --> $DIR/suspicious_splitn.rs:10:13
+ --> $DIR/suspicious_splitn.rs:11:13
|
LL | let _ = "a,b".rsplitn(0, ',');
| ^^^^^^^^^^^^^^^^^^^^^
= note: the resulting iterator will always return `None`
error: `splitn` called with `1` split
- --> $DIR/suspicious_splitn.rs:11:13
+ --> $DIR/suspicious_splitn.rs:12:13
|
LL | let _ = "a,b".splitn(1, ',');
| ^^^^^^^^^^^^^^^^^^^^
= note: the resulting iterator will always return the entire string followed by `None`
error: `splitn` called with `0` splits
- --> $DIR/suspicious_splitn.rs:12:13
+ --> $DIR/suspicious_splitn.rs:13:13
|
LL | let _ = [0, 1, 2].splitn(0, |&x| x == 1);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: the resulting iterator will always return `None`
error: `splitn_mut` called with `0` splits
- --> $DIR/suspicious_splitn.rs:13:13
+ --> $DIR/suspicious_splitn.rs:14:13
|
LL | let _ = [0, 1, 2].splitn_mut(0, |&x| x == 1);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: the resulting iterator will always return `None`
error: `splitn` called with `1` split
- --> $DIR/suspicious_splitn.rs:14:13
+ --> $DIR/suspicious_splitn.rs:15:13
|
LL | let _ = [0, 1, 2].splitn(1, |&x| x == 1);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: the resulting iterator will always return the entire slice followed by `None`
error: `rsplitn_mut` called with `1` split
- --> $DIR/suspicious_splitn.rs:15:13
+ --> $DIR/suspicious_splitn.rs:16:13
|
LL | let _ = [0, 1, 2].rsplitn_mut(1, |&x| x == 1);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: the resulting iterator will always return the entire slice followed by `None`
error: `splitn` called with `1` split
- --> $DIR/suspicious_splitn.rs:18:13
+ --> $DIR/suspicious_splitn.rs:19:13
|
LL | let _ = "a,b".splitn(X + 1, ',');
| ^^^^^^^^^^^^^^^^^^^^^^^^
= note: the resulting iterator will always return the entire string followed by `None`
error: `splitn` called with `0` splits
- --> $DIR/suspicious_splitn.rs:19:13
+ --> $DIR/suspicious_splitn.rs:20:13
|
LL | let _ = "a,b".splitn(X, ',');
| ^^^^^^^^^^^^^^^^^^^^
unsafe {};
}
+pub fn print_binary_tree() {
+ println!("{}", unsafe { String::from_utf8_unchecked(vec![]) });
+}
+
fn main() {}
LL ~ unsafe {};
|
-error: aborting due to 13 previous errors
+error: unsafe block missing a safety comment
+ --> $DIR/undocumented_unsafe_blocks.rs:288:20
+ |
+LL | println!("{}", unsafe { String::from_utf8_unchecked(vec![]) });
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: consider adding a safety comment
+ |
+LL ~ println!("{}", // Safety: ...
+LL ~ unsafe { String::from_utf8_unchecked(vec![]) });
+ |
+
+error: aborting due to 14 previous errors
print!("\u{DC}ben!"); // this is ok
}
+// issue 8013
+#[warn(clippy::non_ascii_literal)]
+fn single_quote() {
+ const _EMPTY_BLOCK: char = '▱';
+ const _FULL_BLOCK: char = '▰';
+}
+
fn main() {
zero();
uni();
canon();
+ single_quote();
}
|
= note: `-D clippy::non-ascii-literal` implied by `-D warnings`
-error: aborting due to 5 previous errors
+error: literal non-ASCII character detected
+ --> $DIR/unicode.rs:26:32
+ |
+LL | const _EMPTY_BLOCK: char = '▱';
+ | ^^^ help: consider replacing the string with: `'/u{25b1}'`
+
+error: literal non-ASCII character detected
+ --> $DIR/unicode.rs:27:31
+ |
+LL | const _FULL_BLOCK: char = '▰';
+ | ^^^ help: consider replacing the string with: `'/u{25b0}'`
+
+error: aborting due to 7 previous errors
]
[assign]
+
+# Allows shortcuts like `@rustbot ready`
+#
+# See https://github.com/rust-lang/triagebot/wiki/Shortcuts
+[shortcut]
background-color: #777777;
margin: auto 5px;
}
+
+ .label-version {
+ background-color: #777777;
+ margin: auto 5px;
+ font-family: monospace;
+ }
</style>
<style>
/* Expanding the mdBoom theme*/
</h2>
</header>
- <ul class="list-group lint-docs" ng-class="{collapse: true, in: open[lint.id]}">
+ <div class="list-group lint-docs" ng-class="{collapse: true, in: open[lint.id]}">
<div class="list-group-item lint-doc-md" ng-bind-html="lint.docs | markdown"></div>
<div class="lint-additional-info-container">
<!-- Applicability -->
<span class="label label-default label-applicability">{{lint.applicability.applicability}}</span>
<a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint_defs/enum.Applicability.html#variants">(?)</a>
</div>
- <!-- TODO xFrednet 2021-05-19: Somehow collect and show the version See rust-clippy#6492 -->
+ <!-- Clippy version -->
+ <div class="lint-additional-info-item">
+ <span>{{lint.group == "deprecated" ? "Deprecated" : "Added"}} in: </span>
+ <span class="label label-default label-version">{{lint.version}}</span>
+ </div>
<!-- Open related issues -->
<div class="lint-additional-info-item">
<a href="https://github.com/rust-lang/rust-clippy/issues?q=is%3Aissue+{{lint.id}}">Related Issues</a>
<a href="https://github.com/rust-lang/rust-clippy/blob/{{docVersion}}/clippy_lints/{{lint.id_span.path}}#L{{lint.id_span.line}}">View Source</a>
</div>
</div>
- </ul>
+ </div>
</article>
</div>
</div>
// patterns still match the raw compiler output.
if self.props.error_patterns.is_empty() {
rustc.args(&["--error-format", "json"]);
+ rustc.args(&["--json", "future-incompat"]);
}
rustc.arg("-Zui-testing");
rustc.arg("-Zdeduplicate-diagnostics=no");
Ui => {
if !self.props.compile_flags.iter().any(|s| s.starts_with("--error-format")) {
rustc.args(&["--error-format", "json"]);
+ rustc.args(&["--json", "future-incompat"]);
}
rustc.arg("-Ccodegen-units=1");
rustc.arg("-Zui-testing");
rustc.arg("-Zdeduplicate-diagnostics=no");
- rustc.arg("-Zemit-future-incompat-report");
}
MirOpt => {
rustc.args(&[
-Subproject commit 9983e0fc634e11717eb457a73fe84cfc9409ba8f
+Subproject commit 81e59e6b92cf1729aabbbbf09b81a81a03775d64
-Subproject commit d9b2291f546abc77d24499339a72a89127464b95
+Subproject commit 7d6fcbc0be2151bfa85ec146545b42d8be2fb28c
env_logger = "0.7.1"
[dependencies.mdbook]
-version = "0.4.12"
+version = "0.4.14"
default-features = false
features = ["search"]
}
files.sort();
+ if (no_headless) {
+ opts["jobs"] = 1;
+ console.log("`--no-headless` option is active, disabling concurrency for running tests.");
+ }
+
console.log(`Running ${files.length} rustdoc-gui (${opts["jobs"]} concurrently) ...`);
if (opts["jobs"] < 1) {
process.setMaxListeners(files.length + 1);
- } else {
+ } else if (!no_headless) {
process.setMaxListeners(opts["jobs"] + 1);
}
tests_queue.splice(tests_queue.indexOf(callback), 1);
});
tests_queue.push(callback);
- if (no_headless) {
- await tests_queue[i];
- } else if (opts["jobs"] > 0 && tests_queue.length >= opts["jobs"]) {
+ if (opts["jobs"] > 0 && tests_queue.length >= opts["jobs"]) {
await Promise.race(tests_queue);
}
}